Initial commit of akmon project

This commit is contained in:
2026-01-20 08:04:15 +08:00
commit 77a2bab985
1309 changed files with 343305 additions and 0 deletions

976
zhipao.sql Normal file
View File

@@ -0,0 +1,976 @@
-- 运动训练监测与AI评估平台表结构设计Supabase/PostgreSQL规范含中文注释、索引、外键约束
-- 支持的语言表
CREATE TABLE public.ak_languages (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 语言ID
code VARCHAR(10) NOT NULL UNIQUE, -- 语言代码 (如 en-US, zh-CN)
name VARCHAR(50) NOT NULL, -- 语言名称
native_name VARCHAR(50) NOT NULL, -- 本地语言名称
is_active BOOLEAN DEFAULT true, -- 是否启用
is_default BOOLEAN DEFAULT false, -- 是否默认语言
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_languages IS '支持的语言表';
-- 地区表
CREATE TABLE public.ak_regions (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 地区ID
name VARCHAR(128) NOT NULL, -- 地区名称
parent_id uuid REFERENCES public.ak_regions(id), -- 上级地区ID
level INT, -- 地区级别 1省 2市 3区县 4街道
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_regions IS '地区表';
COMMENT ON COLUMN public.ak_regions.name IS '地区名称';
COMMENT ON COLUMN public.ak_regions.parent_id IS '上级地区ID';
COMMENT ON COLUMN public.ak_regions.level IS '地区级别';
-- 学校表
CREATE TABLE public.ak_schools (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 学校ID
name VARCHAR(128) NOT NULL, -- 学校名称
region_id uuid REFERENCES public.ak_regions(id), -- 所属地区
type VARCHAR(32), -- 学校类型
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_schools IS '学校表';
COMMENT ON COLUMN public.ak_schools.region_id IS '所属地区';
-- 年级表
CREATE TABLE public.ak_grades (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 年级ID
school_id uuid REFERENCES public.ak_schools(id) ON DELETE CASCADE, -- 所属学校
name VARCHAR(32) NOT NULL, -- 年级名称
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_grades IS '年级表';
COMMENT ON COLUMN public.ak_grades.school_id IS '所属学校';
-- 班级表
CREATE TABLE public.ak_classes (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 班级ID
grade_id uuid REFERENCES public.ak_grades(id) ON DELETE CASCADE, -- 所属年级
name VARCHAR(32) NOT NULL, -- 班级名称
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_classes IS '班级表';
COMMENT ON COLUMN public.ak_classes.grade_id IS '所属年级';
-- 用户表
CREATE TABLE public.ak_users (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 用户ID
username VARCHAR(64) UNIQUE NOT NULL, -- 用户名
email VARCHAR(128) UNIQUE NOT NULL, -- 邮箱
password_hash VARCHAR(256) NOT NULL, -- 密码哈希
gender VARCHAR(16) DEFAULT 'other', -- 性别
birthday DATE, -- 生日
height_cm INT, -- 身高
weight_kg INT, -- 体重
bio TEXT, -- biography
phone VARCHAR(32), -- 手机号码
avatar_url TEXT, -- 头像
region_id uuid REFERENCES public.ak_regions(id), -- 所属地区
school_id uuid REFERENCES public.ak_schools(id), -- 所属学校
grade_id uuid REFERENCES public.ak_grades(id), -- 所属年级
class_id uuid REFERENCES public.ak_classes(id), -- 所属班级
role VARCHAR(32) DEFAULT 'student', -- 角色
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(), -- 创建时间
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now(), -- 更新时间
auth_id UUID REFERENCES auth.users(id) ON DELETE CASCADE, -- Supabase Auth ID
preferred_language uuid REFERENCES public.ak_languages(id) -- 用户偏好语言
);
COMMENT ON TABLE public.ak_users IS '用户表';
COMMENT ON COLUMN public.ak_users.username IS '用户名';
COMMENT ON COLUMN public.ak_users.email IS '邮箱';
COMMENT ON COLUMN public.ak_users.gender IS '性别';
COMMENT ON COLUMN public.ak_users.region_id IS '所属地区';
COMMENT ON COLUMN public.ak_users.school_id IS '所属学校';
COMMENT ON COLUMN public.ak_users.grade_id IS '所属年级';
COMMENT ON COLUMN public.ak_users.class_id IS '所属班级';
COMMENT ON COLUMN public.ak_users.role IS '用户角色';
COMMENT ON COLUMN public.ak_users.preferred_language IS '用户偏好语言';
CREATE INDEX idx_users_region_id ON public.ak_users(region_id);
CREATE INDEX idx_users_school_id ON public.ak_users(school_id);
CREATE INDEX idx_users_grade_id ON public.ak_users(grade_id);
CREATE INDEX idx_users_class_id ON public.ak_users(class_id);
-- 用户联系方式表
CREATE TABLE public.ak_user_contacts (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 联系方式ID
user_id uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 用户ID
contact_type VARCHAR(32) NOT NULL, -- 联系方式类型
contact_value VARCHAR(128) NOT NULL, -- 联系方式内容
is_primary BOOLEAN DEFAULT false, -- 是否主联系方式
remark VARCHAR(128), -- 备注
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_user_contacts IS '用户联系方式表';
COMMENT ON COLUMN public.ak_user_contacts.contact_type IS '联系方式类型';
COMMENT ON COLUMN public.ak_user_contacts.contact_value IS '联系方式内容';
CREATE INDEX idx_user_contacts_user_id ON public.ak_user_contacts(user_id);
-- 用户个人展示设置表
CREATE TABLE public.ak_user_profiles (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 个人展示ID
user_id uuid UNIQUE REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 用户ID
avatar_url TEXT, -- 头像S3地址
background_url TEXT, -- 背景图/视频S3地址
declaration TEXT, -- 个人宣言
status_text TEXT, -- 近况
status_media_url TEXT, -- 近况配图/视频S3地址
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 更新时间
);
COMMENT ON TABLE public.ak_user_profiles IS '用户个人展示设置表';
-- 用户-班级多对多关系
CREATE TABLE public.ak_user_classes (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 关系ID
user_id uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 用户ID
class_id uuid REFERENCES public.ak_classes(id) ON DELETE CASCADE, -- 班级ID
role VARCHAR(32), -- 角色
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_user_classes IS '用户-班级多对多关系表';
CREATE INDEX idx_user_classes_user_id ON public.ak_user_classes(user_id);
CREATE INDEX idx_user_classes_class_id ON public.ak_user_classes(class_id);
-- 设备表
CREATE TABLE public.ak_devices (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 设备ID
user_id uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 用户ID
device_type VARCHAR(32) NOT NULL, -- 设备类型
device_name VARCHAR(64), -- 设备名称
device_mac VARCHAR(64), -- 设备MAC
bind_time TIMESTAMP WITH TIME ZONE DEFAULT now(), -- 绑定时间
status VARCHAR(16) DEFAULT 'active', -- 状态
extra JSONB -- 扩展信息
);
COMMENT ON TABLE public.ak_devices IS '设备表';
CREATE INDEX idx_devices_user_id ON public.ak_devices(user_id);
-- 训练计划表
CREATE TABLE public.ak_training_plans (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 计划ID
user_id uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 用户ID
plan_name VARCHAR(128) NOT NULL, -- 计划名称
description TEXT, -- 描述
start_date DATE, -- 开始日期
end_date DATE, -- 结束日期
status VARCHAR(16) DEFAULT 'active', -- 状态
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(), -- 创建时间
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 更新时间
);
COMMENT ON TABLE public.ak_training_plans IS '训练计划表';
CREATE INDEX idx_training_plans_user_id ON public.ak_training_plans(user_id);
-- 训练计划明细表
CREATE TABLE public.ak_training_plan_items (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 明细ID
plan_id uuid REFERENCES public.ak_training_plans(id) ON DELETE CASCADE, -- 计划ID
item_date DATE NOT NULL, -- 明细日期
activity_type VARCHAR(64), -- 活动类型
target_value FLOAT, -- 目标数值
unit VARCHAR(16), -- 单位
description TEXT, -- 描述
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_training_plan_items IS '训练计划明细表';
CREATE INDEX idx_training_plan_items_plan_id ON public.ak_training_plan_items(plan_id);
-- 训练记录表
CREATE TABLE public.ak_training_records (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 记录ID
user_id uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 用户ID
plan_item_id uuid REFERENCES public.ak_training_plan_items(id), -- 计划明细ID
device_id uuid REFERENCES public.ak_devices(id), -- 设备ID
activity_type VARCHAR(64), -- 活动类型
start_time TIMESTAMP WITH TIME ZONE, -- 开始时间
end_time TIMESTAMP WITH TIME ZONE, -- 结束时间
duration_sec INT, -- 时长
distance_km FLOAT, -- 距离
calories FLOAT, -- 卡路里
steps INT, -- 步数
avg_heart_rate INT, -- 平均心率
max_heart_rate INT, -- 最大心率
data JSONB, -- 详细原始数据
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_training_records IS '训练记录表';
CREATE INDEX idx_training_records_user_id ON public.ak_training_records(user_id);
CREATE INDEX idx_training_records_plan_item_id ON public.ak_training_records(plan_item_id);
-- 体征监测数据表
CREATE TABLE public.ak_biometric_data (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 数据ID
user_id uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 用户ID
device_id uuid REFERENCES public.ak_devices(id), -- 设备ID
data_type VARCHAR(32), -- 数据类型
value FLOAT, -- 数值
unit VARCHAR(16), -- 单位
measured_at TIMESTAMP WITH TIME ZONE, -- 测量时间
extra JSONB -- 扩展
);
COMMENT ON TABLE public.ak_biometric_data IS '体征监测数据表';
CREATE INDEX idx_biometric_data_user_id ON public.ak_biometric_data(user_id);
CREATE INDEX idx_biometric_data_device_id ON public.ak_biometric_data(device_id);
-- 视频分析结果表
CREATE TABLE public.ak_video_analysis (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 分析ID
user_id uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 用户ID
device_id uuid REFERENCES public.ak_devices(id), -- 设备ID
record_id uuid REFERENCES public.ak_training_records(id), -- 训练记录ID
video_url TEXT, -- 视频地址
analysis_type VARCHAR(32), -- 分析类型
result JSONB, -- AI分析结果
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_video_analysis IS '视频分析结果表';
-- AI 评估报告表
CREATE TABLE public.ak_ai_reports (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 报告ID
user_id uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 用户ID
record_id uuid REFERENCES public.ak_training_records(id), -- 训练记录ID
report_type VARCHAR(32), -- 报告类型
content JSONB, -- 评估内容
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_ai_reports IS 'AI评估报告表';
-- 消息/通知表
CREATE TABLE public.ak_notifications (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 通知ID
user_id uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 用户ID
title VARCHAR(128), -- 标题
content TEXT, -- 内容
status VARCHAR(16) DEFAULT 'unread', -- 状态
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_notifications IS '消息/通知表';
-- 设备事件日志表
CREATE TABLE public.ak_device_events (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 日志ID
device_id uuid REFERENCES public.ak_devices(id) ON DELETE CASCADE, -- 设备ID
event_type VARCHAR(32), -- 事件类型
event_time TIMESTAMP WITH TIME ZONE DEFAULT now(), -- 事件时间
data JSONB -- 事件数据
);
COMMENT ON TABLE public.ak_device_events IS '设备事件日志表';
-- 用户反馈表
CREATE TABLE public.ak_user_feedback (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 反馈ID
user_id uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 用户ID
content TEXT, -- 反馈内容
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_user_feedback IS '用户反馈表';
-- 运动报告表
CREATE TABLE public.ak_sport_reports (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 报告ID
user_id uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 用户ID
title VARCHAR(128) NOT NULL, -- 标题
content_md TEXT NOT NULL, -- Markdown内容
cover_url TEXT, -- 封面图片
visibility VARCHAR(16) DEFAULT 'private', -- 可见性
visible_until TIMESTAMP WITH TIME ZONE, -- 定时可见截止
allow_comment BOOLEAN DEFAULT true, -- 允许评论
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(), -- 创建时间
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 更新时间
);
COMMENT ON TABLE public.ak_sport_reports IS '运动报告表';
CREATE INDEX idx_sport_reports_user_id ON public.ak_sport_reports(user_id);
-- 运动报告分享记录表
CREATE TABLE public.ak_sport_report_shares (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 分享ID
report_id uuid REFERENCES public.ak_sport_reports(id) ON DELETE CASCADE, -- 报告ID
shared_by uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 分享人
shared_to uuid REFERENCES public.ak_users(id), -- 被分享人
share_type VARCHAR(16) DEFAULT 'link', -- 分享类型
share_token VARCHAR(64), -- 分享链接token
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(), -- 创建时间
expired_at TIMESTAMP WITH TIME ZONE -- 过期时间
);
COMMENT ON TABLE public.ak_sport_report_shares IS '运动报告分享记录表';
CREATE INDEX idx_sport_report_shares_report_id ON public.ak_sport_report_shares(report_id);
CREATE INDEX idx_sport_report_shares_shared_by ON public.ak_sport_report_shares(shared_by);
-- 积分账户表
CREATE TABLE public.ak_user_points (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 账户ID
user_id uuid UNIQUE REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 用户ID
total_points INT DEFAULT 0, -- 总积分
available_points INT DEFAULT 0, -- 可用积分
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 更新时间
);
COMMENT ON TABLE public.ak_user_points IS '积分账户表';
-- 积分变动明细表
CREATE TABLE public.ak_user_point_logs (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 明细ID
user_id uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 用户ID
change_type VARCHAR(32) NOT NULL, -- 变动类型
points INT NOT NULL, -- 积分数
reason TEXT, -- 变动原因
related_id uuid, -- 关联业务ID
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_user_point_logs IS '积分变动明细表';
CREATE INDEX idx_user_point_logs_user_id ON public.ak_user_point_logs(user_id);
-- 积分规则表
CREATE TABLE public.ak_point_rules (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 规则ID
rule_name VARCHAR(64) NOT NULL, -- 规则名称
event_code VARCHAR(64) NOT NULL, -- 事件标识
points INT NOT NULL, -- 积分数
limit_per_day INT, -- 每日上限
description TEXT, -- 描述
is_active BOOLEAN DEFAULT true, -- 是否启用
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(), -- 创建时间
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 更新时间
);
COMMENT ON TABLE public.ak_point_rules IS '积分规则表';
-- 积分交易记录表
CREATE TABLE public.ak_point_transactions (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 交易ID
user_id uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 用户ID
transaction_type VARCHAR(32) NOT NULL, -- 交易类型 (earn, spend, refund等)
points INT NOT NULL, -- 积分数量
description TEXT, -- 交易描述
reference_id uuid, -- 关联业务ID (如训练记录ID、商品ID等)
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_point_transactions IS '积分交易记录表';
COMMENT ON COLUMN public.ak_point_transactions.transaction_type IS '交易类型earn(获得), spend(消费), refund(退还)等';
COMMENT ON COLUMN public.ak_point_transactions.points IS '积分数量,正数表示获得,负数表示消费';
COMMENT ON COLUMN public.ak_point_transactions.reference_id IS '关联业务ID如训练记录ID、商品ID等';
CREATE INDEX idx_point_transactions_user_id ON public.ak_point_transactions(user_id);
CREATE INDEX idx_point_transactions_type ON public.ak_point_transactions(transaction_type);
CREATE INDEX idx_point_transactions_created_at ON public.ak_point_transactions(created_at);
-- 作业表
CREATE TABLE public.ak_assignments (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 作业ID
teacher_id uuid REFERENCES public.ak_users(id) ON DELETE SET NULL, -- 老师ID
title VARCHAR(128) NOT NULL, -- 作业标题
project_name VARCHAR(128), -- 作业所属项目/主题
status VARCHAR(32) DEFAULT 'active', -- 作业状态
start_time TIMESTAMP WITH TIME ZONE, -- 开始时间
end_time TIMESTAMP WITH TIME ZONE, -- 结束时间
description TEXT, -- 作业描述
class_id uuid REFERENCES public.ak_classes(id) ON DELETE CASCADE, -- 班级ID
due_date TIMESTAMP WITH TIME ZONE, -- 截止时间
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(), -- 创建时间
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 更新时间
);
COMMENT ON TABLE public.ak_assignments IS '作业表';
CREATE INDEX idx_assignments_teacher_id ON public.ak_assignments(teacher_id);
CREATE INDEX idx_assignments_class_id ON public.ak_assignments(class_id);
-- 作业提交表
CREATE TABLE public.ak_assignment_submissions (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 提交ID
assignment_id uuid REFERENCES public.ak_assignments(id) ON DELETE CASCADE, -- 作业ID
student_id uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 学生ID
submit_time TIMESTAMP WITH TIME ZONE DEFAULT now(), -- 提交时间
content_md TEXT, -- Markdown作业内容
attachment_url TEXT, -- 附件S3地址
score FLOAT, -- 分数
feedback TEXT, -- 反馈
status VARCHAR(16) DEFAULT 'submitted', -- 状态
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 更新时间
);
COMMENT ON TABLE public.ak_assignment_submissions IS '作业提交表';
CREATE INDEX idx_assignment_submissions_assignment_id ON public.ak_assignment_submissions(assignment_id);
CREATE INDEX idx_assignment_submissions_student_id ON public.ak_assignment_submissions(student_id);
-- 作业统计表
CREATE TABLE public.ak_assignment_stats (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 统计ID
assignment_id uuid REFERENCES public.ak_assignments(id) ON DELETE CASCADE, -- 作业ID
total_students INT, -- 总学生数
submitted_count INT, -- 已提交数
reviewed_count INT, -- 已批改数
avg_score FLOAT, -- 平均分
last_updated TIMESTAMP WITH TIME ZONE DEFAULT now() -- 更新时间
);
COMMENT ON TABLE public.ak_assignment_stats IS '作业统计表';
-- 教学资源表
CREATE TABLE public.ak_teaching_resources (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 资源ID
teacher_id uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 教师ID
type VARCHAR(32) NOT NULL, -- 资源类型video(视频), document(文档), plan(计划), image(图片)等
title VARCHAR(128) NOT NULL, -- 资源标题
description TEXT, -- 资源描述
content_url TEXT, -- 资源内容URL (S3地址等)
tags JSONB, -- 标签数组 (JSON格式)
is_public BOOLEAN DEFAULT false, -- 是否公开
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(), -- 创建时间
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 更新时间
);
COMMENT ON TABLE public.ak_teaching_resources IS '教学资源表';
COMMENT ON COLUMN public.ak_teaching_resources.type IS '资源类型video(视频), document(文档), plan(计划), image(图片)等';
COMMENT ON COLUMN public.ak_teaching_resources.tags IS '标签数组JSON格式存储';
COMMENT ON COLUMN public.ak_teaching_resources.is_public IS '是否公开public资源可被其他教师查看';
CREATE INDEX idx_teaching_resources_teacher_id ON public.ak_teaching_resources(teacher_id);
CREATE INDEX idx_teaching_resources_type ON public.ak_teaching_resources(type);
CREATE INDEX idx_teaching_resources_is_public ON public.ak_teaching_resources(is_public);
-- 课程安排表
CREATE TABLE public.ak_class_schedules (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 课程安排ID
teacher_id uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 教师ID
school_id uuid REFERENCES public.ak_schools(id) ON DELETE CASCADE, -- 学校ID
class_id uuid REFERENCES public.ak_classes(id) ON DELETE CASCADE, -- 班级ID
subject VARCHAR(64) NOT NULL, -- 科目名称
day_of_week INT NOT NULL CHECK (day_of_week >= 1 AND day_of_week <= 7), -- 星期几 (1=周一, 7=周日)
start_time TIME NOT NULL, -- 开始时间
end_time TIME NOT NULL, -- 结束时间
location VARCHAR(128), -- 上课地点
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(), -- 创建时间
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 更新时间
);
COMMENT ON TABLE public.ak_class_schedules IS '课程安排表';
COMMENT ON COLUMN public.ak_class_schedules.day_of_week IS '星期几1=周一2=周二...7=周日';
COMMENT ON COLUMN public.ak_class_schedules.subject IS '科目名称,如体育、数学等';
CREATE INDEX idx_class_schedules_teacher_id ON public.ak_class_schedules(teacher_id);
CREATE INDEX idx_class_schedules_school_id ON public.ak_class_schedules(school_id);
CREATE INDEX idx_class_schedules_class_id ON public.ak_class_schedules(class_id);
CREATE INDEX idx_class_schedules_day_of_week ON public.ak_class_schedules(day_of_week);
-- 互动记录表
CREATE TABLE public.ak_interactions (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 互动ID
user_id uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 用户ID (发起互动的用户)
target_type VARCHAR(32) NOT NULL, -- 目标类型training_record, assignment_submission, teaching_resource等
target_id uuid NOT NULL, -- 目标ID (被互动的对象ID)
interaction_type VARCHAR(32) NOT NULL, -- 互动类型comment(评论), like(点赞), share(分享)等
content TEXT, -- 互动内容 (如评论内容,点赞时可为空)
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(), -- 创建时间
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 更新时间
);
COMMENT ON TABLE public.ak_interactions IS '互动记录表';
COMMENT ON COLUMN public.ak_interactions.target_type IS '目标类型training_record(训练记录), assignment_submission(作业提交), teaching_resource(教学资源)等';
COMMENT ON COLUMN public.ak_interactions.interaction_type IS '互动类型comment(评论), like(点赞), share(分享)等';
CREATE INDEX idx_interactions_user_id ON public.ak_interactions(user_id);
CREATE INDEX idx_interactions_target ON public.ak_interactions(target_type, target_id);
CREATE INDEX idx_interactions_type ON public.ak_interactions(interaction_type);
-- 老师权限管理表
CREATE TABLE public.ak_teacher_roles (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 角色ID
user_id uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 用户ID
school_id uuid REFERENCES public.ak_schools(id), -- 学校ID
class_id uuid REFERENCES public.ak_classes(id), -- 班级ID
role VARCHAR(32) DEFAULT 'teacher', -- 角色
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_teacher_roles IS '老师权限管理表';
CREATE INDEX idx_teacher_roles_user_id ON public.ak_teacher_roles(user_id);
CREATE INDEX idx_teacher_roles_school_id ON public.ak_teacher_roles(school_id);
CREATE INDEX idx_teacher_roles_class_id ON public.ak_teacher_roles(class_id);
-- 角色定义表
CREATE TABLE public.ak_roles (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 角色ID
name VARCHAR(32) NOT NULL UNIQUE, -- 角色名称
description TEXT, -- 角色描述
level INT NOT NULL, -- 角色级别(数字越大权限越高)
is_system BOOLEAN DEFAULT false, -- 是否为系统角色
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_roles IS '角色定义表';
-- 插入基础角色
INSERT INTO public.ak_roles (name, description, level, is_system) VALUES
('student', '学生用户', 10, true),
('teacher', '教师用户', 20, true),
('school_admin', '学校管理员', 30, true),
('region_admin', '区域管理员', 40, true),
('system_admin', '系统管理员', 100, true);
-- 权限定义表
CREATE TABLE public.ak_permissions (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 权限ID
code VARCHAR(64) NOT NULL UNIQUE, -- 权限代码
name VARCHAR(128) NOT NULL, -- 权限名称
description TEXT, -- 权限描述
resource_type VARCHAR(32) NOT NULL, -- 资源类型
action VARCHAR(32) NOT NULL, -- 操作类型create, read, update, delete, manage
is_system BOOLEAN DEFAULT false, -- 是否为系统权限
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_permissions IS '权限定义表';
-- 角色-权限关联表
CREATE TABLE public.ak_role_permissions (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 关联ID
role_id uuid REFERENCES public.ak_roles(id) ON DELETE CASCADE, -- 角色ID
permission_id uuid REFERENCES public.ak_permissions(id) ON DELETE CASCADE, -- 权限ID
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_role_permissions IS '角色-权限关联表';
CREATE UNIQUE INDEX idx_role_permission_unique ON public.ak_role_permissions(role_id, permission_id);
CREATE INDEX idx_role_permissions_role_id ON public.ak_role_permissions(role_id);
CREATE INDEX idx_role_permissions_permission_id ON public.ak_role_permissions(permission_id);
-- 用户-角色关联表
CREATE TABLE public.ak_user_roles (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 关联ID
user_id uuid REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 用户ID
role_id uuid REFERENCES public.ak_roles(id) ON DELETE CASCADE, -- 角色ID
scope_type VARCHAR(32), -- 作用域类型global, region, school, grade, class
scope_id uuid, -- 作用域ID
created_by uuid REFERENCES public.ak_users(id), -- 创建人
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 创建时间
);
COMMENT ON TABLE public.ak_user_roles IS '用户-角色关联表';
CREATE INDEX idx_user_roles_user_id ON public.ak_user_roles(user_id);
CREATE INDEX idx_user_roles_role_id ON public.ak_user_roles(role_id);
COMMENT ON COLUMN public.ak_user_roles.scope_type IS '作用域类型,指定角色在哪个范围内有效';
COMMENT ON COLUMN public.ak_user_roles.scope_id IS '作用域ID对应scope_type的记录ID';
-- 辅助函数:检查用户是否拥有指定权限
CREATE OR REPLACE FUNCTION public.has_permission(user_id uuid, permission_code text)
RETURNS boolean AS $$
DECLARE
has_perm boolean;
BEGIN
SELECT EXISTS(
SELECT 1 FROM public.ak_user_roles ur
JOIN public.ak_role_permissions rp ON ur.role_id = rp.role_id
JOIN public.ak_permissions p ON rp.permission_id = p.id
WHERE ur.user_id = $1 AND p.code = $2
) INTO has_perm;
RETURN has_perm;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- 辅助函数:检查用户是否拥有指定作用域的权限
CREATE OR REPLACE FUNCTION public.has_scoped_permission(
user_id uuid,
permission_code text,
scope_type text,
scope_id uuid
)
RETURNS boolean AS $$
DECLARE
has_perm boolean;
BEGIN
-- 检查全局权限
SELECT EXISTS(
SELECT 1 FROM public.ak_user_roles ur
JOIN public.ak_role_permissions rp ON ur.role_id = rp.role_id
JOIN public.ak_permissions p ON rp.permission_id = p.id
WHERE ur.user_id = $1
AND p.code = $2
AND (ur.scope_type IS NULL OR ur.scope_type = 'global')
) INTO has_perm;
IF has_perm THEN
RETURN true;
END IF;
-- 检查特定作用域的权限
SELECT EXISTS(
SELECT 1 FROM public.ak_user_roles ur
JOIN public.ak_role_permissions rp ON ur.role_id = rp.role_id
JOIN public.ak_permissions p ON rp.permission_id = p.id
WHERE ur.user_id = $1
AND p.code = $2
AND ur.scope_type = $3
AND ur.scope_id = $4
) INTO has_perm;
RETURN has_perm;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- 修改用户表触发器,自动分配学生角色
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER AS $$
DECLARE
student_role_id uuid;
new_user_id uuid;
BEGIN
-- 创建基本用户记录
INSERT INTO public.ak_users (auth_id, username, email, role)
VALUES (new.id, new.email, new.email, 'student')
RETURNING id INTO new_user_id;
-- 获取学生角色ID
SELECT id INTO student_role_id FROM public.ak_roles WHERE name = 'student';
-- 分配学生角色
IF student_role_id IS NOT NULL THEN
INSERT INTO public.ak_user_roles (user_id, role_id, scope_type)
VALUES (new_user_id, student_role_id, 'global');
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- 示例权限数据
INSERT INTO public.ak_permissions (code, name, resource_type, action, is_system, description) VALUES
-- 学生权限
('student.profile.read', '查看个人资料', 'profile', 'read', true, '查看自己的个人资料'),
('student.profile.update', '更新个人资料', 'profile', 'update', true, '更新自己的个人资料'),
('student.training.read', '查看训练记录', 'training', 'read', true, '查看自己的训练记录'),
('student.training.create', '创建训练记录', 'training', 'create', true, '创建自己的训练记录'),
-- 教师权限
('teacher.student.read', '查看学生资料', 'student', 'read', true, '查看所教班级学生的资料'),
('teacher.class.read', '查看班级信息', 'class', 'read', true, '查看所教班级信息'),
('teacher.assignment.manage', '管理作业', 'assignment', 'manage', true, '创建、修改班级作业'),
('teacher.report.read', '查看学生报告', 'report', 'read', true, '查看学生训练报告'),
-- 学校管理员权限
('school_admin.teacher.manage', '管理教师', 'teacher', 'manage', true, '管理学校教师账号'),
('school_admin.class.manage', '管理班级', 'class', 'manage', true, '管理学校班级'),
('school_admin.grade.manage', '管理年级', 'grade', 'manage', true, '管理学校年级'),
('school_admin.report.school', '学校统计报表', 'report', 'read', true, '查看学校统计报表'),
-- 系统管理员权限
('admin.system.manage', '系统管理', 'system', 'manage', true, '系统管理权限'),
('admin.users.manage', '用户管理', 'users', 'manage', true, '管理所有用户'),
('admin.roles.manage', '角色权限管理', 'roles', 'manage', true, '管理角色和权限');
-- 为角色分配基础权限
INSERT INTO public.ak_role_permissions (role_id, permission_id)
SELECT r.id, p.id FROM public.ak_roles r, public.ak_permissions p
WHERE
(r.name = 'student' AND p.code LIKE 'student.%') OR
(r.name = 'teacher' AND (p.code LIKE 'teacher.%' OR p.code LIKE 'student.%')) OR
(r.name = 'school_admin' AND (p.code LIKE 'school_admin.%' OR p.code LIKE 'teacher.%' OR p.code LIKE 'student.%')) OR
(r.name = 'system_admin' AND p.code LIKE '%');
-- 为所有数据表添加行级安全策略的辅助函数
CREATE OR REPLACE FUNCTION public.current_user_has_permission(permission_code text)
RETURNS boolean AS $$
DECLARE
user_id uuid;
BEGIN
-- 获取当前用户的ID
SELECT id INTO user_id FROM public.ak_users WHERE auth_id = auth.uid();
IF user_id IS NULL THEN
RETURN false;
END IF;
RETURN public.has_permission(user_id, permission_code);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- 示例为班级表添加RLS策略
ALTER TABLE public.ak_classes ENABLE ROW LEVEL SECURITY;
-- 系统管理员和学校管理员可以管理所有班级
CREATE POLICY "管理员可以管理所有班级" ON public.ak_classes
USING (public.current_user_has_permission('admin.system.manage') OR
public.current_user_has_permission('school_admin.class.manage'));
-- 教师可以查看他们教授的班级
CREATE POLICY "教师可以查看所教授的班级" ON public.ak_classes
FOR SELECT
USING (EXISTS (
SELECT 1 FROM public.ak_teacher_roles tr
JOIN public.ak_users u ON tr.user_id = u.id
WHERE u.auth_id = auth.uid() AND tr.class_id = ak_classes.id
));
-- 添加i18n国际化支持
-- 插入基础语言
INSERT INTO public.ak_languages (code, name, native_name, is_active, is_default) VALUES
('zh-CN', 'Chinese (Simplified)', '简体中文', true, true),
('en-US', 'English (US)', 'English (US)', true, false);
-- 数据翻译基础表 - 用于继承
CREATE TABLE public.ak_translations (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- 翻译ID
record_id uuid NOT NULL, -- 关联记录ID
language_id uuid REFERENCES public.ak_languages(id) ON DELETE CASCADE, -- 语言ID
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(), -- 创建时间
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 更新时间
);
COMMENT ON TABLE public.ak_translations IS '数据翻译基础表(用于继承)';
CREATE UNIQUE INDEX idx_translations_record_language ON public.ak_translations (record_id, language_id);
CREATE INDEX idx_translations_language ON public.ak_translations (language_id);
-- 区域名称翻译表
CREATE TABLE public.ak_region_translations (
region_name VARCHAR(128) NOT NULL, -- 区域名称
CONSTRAINT ak_region_translations_pk PRIMARY KEY (id)
) INHERITS (public.ak_translations);
COMMENT ON TABLE public.ak_region_translations IS '区域名称翻译表';
ALTER TABLE public.ak_region_translations
ADD CONSTRAINT fk_region_translations_record
FOREIGN KEY (record_id)
REFERENCES public.ak_regions(id) ON DELETE CASCADE;
-- 学校名称翻译表
CREATE TABLE public.ak_school_translations (
school_name VARCHAR(128) NOT NULL, -- 学校名称
school_type VARCHAR(32), -- 学校类型
CONSTRAINT ak_school_translations_pk PRIMARY KEY (id)
) INHERITS (public.ak_translations);
COMMENT ON TABLE public.ak_school_translations IS '学校名称翻译表';
ALTER TABLE public.ak_school_translations
ADD CONSTRAINT fk_school_translations_record
FOREIGN KEY (record_id)
REFERENCES public.ak_schools(id) ON DELETE CASCADE;
-- 应用翻译表 (用于前端界面文本)
CREATE TABLE public.ak_app_translations (
key VARCHAR(255) NOT NULL, -- 翻译键
text TEXT NOT NULL, -- 翻译文本
language_id uuid REFERENCES public.ak_languages(id) ON DELETE CASCADE, -- 语言ID
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(), -- 创建时间
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() -- 更新时间
);
COMMENT ON TABLE public.ak_app_translations IS '应用翻译表(前端界面文本)';
ALTER TABLE public.ak_app_translations ADD PRIMARY KEY (key, language_id);
CREATE INDEX idx_app_translations_language ON public.ak_app_translations (language_id);
-- 获取当前用户语言的函数
CREATE OR REPLACE FUNCTION public.get_user_language()
RETURNS uuid AS $$
DECLARE
lang_id uuid;
default_lang_id uuid;
BEGIN
-- 查询用户的首选语言
SELECT u.preferred_language INTO lang_id
FROM public.ak_users u
WHERE u.auth_id = auth.uid();
-- 如果没有设置或用户未登录,返回默认语言
IF lang_id IS NULL THEN
SELECT id INTO default_lang_id
FROM public.ak_languages
WHERE is_default = true
LIMIT 1;
RETURN default_lang_id;
END IF;
RETURN lang_id;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- 获取翻译文本的函数 (通用版本)
CREATE OR REPLACE FUNCTION public.get_translation(
p_table_name text,
p_record_id uuid,
p_column_name text,
p_language_id uuid DEFAULT NULL
)
RETURNS text AS $$
DECLARE
result text;
lang_id uuid;
default_lang_id uuid;
query_text text;
BEGIN
-- 确定使用的语言ID
IF p_language_id IS NULL THEN
lang_id := public.get_user_language();
ELSE
lang_id := p_language_id;
END IF;
-- 构建查询语句动态获取翻译
query_text := 'SELECT t.' || quote_ident(p_column_name) ||
' FROM public.' || quote_ident('ak_' || p_table_name || '_translations') || ' t ' ||
'WHERE t.record_id = $1 AND t.language_id = $2';
-- 执行查询
EXECUTE query_text INTO result USING p_record_id, lang_id;
-- 如果没有找到翻译,尝试使用默认语言
IF result IS NULL THEN
SELECT id INTO default_lang_id
FROM public.ak_languages
WHERE is_default = true
LIMIT 1;
IF default_lang_id != lang_id THEN
EXECUTE query_text INTO result USING p_record_id, default_lang_id;
END IF;
END IF;
RETURN result;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- 获取区域翻译名称的便捷函数
CREATE OR REPLACE FUNCTION public.get_region_name(
p_region_id uuid,
p_language_id uuid DEFAULT NULL
)
RETURNS text AS $$
DECLARE
translated_name text;
original_name text;
BEGIN
-- 尝试获取翻译
translated_name := public.get_translation('region', p_region_id, 'region_name', p_language_id);
-- 如果没有翻译,返回原始名称
IF translated_name IS NULL THEN
SELECT name INTO original_name
FROM public.ak_regions
WHERE id = p_region_id;
RETURN original_name;
END IF;
RETURN translated_name;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- 获取学校翻译名称的便捷函数
CREATE OR REPLACE FUNCTION public.get_school_name(
p_school_id uuid,
p_language_id uuid DEFAULT NULL
)
RETURNS text AS $$
DECLARE
translated_name text;
original_name text;
BEGIN
-- 尝试获取翻译
translated_name := public.get_translation('school', p_school_id, 'school_name', p_language_id);
-- 如果没有翻译,返回原始名称
IF translated_name IS NULL THEN
SELECT name INTO original_name
FROM public.ak_schools
WHERE id = p_school_id;
RETURN original_name;
END IF;
RETURN translated_name;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- 获取前端界面文本翻译的函数
CREATE OR REPLACE FUNCTION public.get_ui_text(
p_key text,
p_language_id uuid DEFAULT NULL
)
RETURNS text AS $$
DECLARE
result text;
lang_id uuid;
default_lang_id uuid;
BEGIN
-- 确定使用的语言ID
IF p_language_id IS NULL THEN
lang_id := public.get_user_language();
ELSE
lang_id := p_language_id;
END IF;
-- 获取翻译
SELECT text INTO result
FROM public.ak_app_translations
WHERE key = p_key AND language_id = lang_id;
-- 如果没有找到翻译,尝试使用默认语言
IF result IS NULL THEN
SELECT id INTO default_lang_id
FROM public.ak_languages
WHERE is_default = true
LIMIT 1;
IF default_lang_id != lang_id THEN
SELECT text INTO result
FROM public.ak_app_translations
WHERE key = p_key AND language_id = default_lang_id;
END IF;
END IF;
-- 如果仍然没有找到翻译,返回键名
IF result IS NULL THEN
RETURN p_key;
END IF;
RETURN result;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- 创建翻译视图示例:区域多语言视图
CREATE OR REPLACE VIEW public.vw_regions_i18n AS
SELECT
r.id,
r.parent_id,
r.level,
r.created_at,
l.id as language_id,
l.code as language_code,
COALESCE(
rt.region_name,
r.name
) as name
FROM
public.ak_regions r
CROSS JOIN
public.ak_languages l
LEFT JOIN
public.ak_region_translations rt ON r.id = rt.record_id AND l.id = rt.language_id;
COMMENT ON VIEW public.vw_regions_i18n IS '区域多语言视图';
/*
i18n国际化系统说明
1. 核心设计:
- 通过ak_languages表定义支持的语言
- 主表存储默认语言(通常为中文)数据
- 翻译表以主表ID为外键存储其他语言的翻译
2. 翻译表结构:
- 基础翻译表ak_translations包含通用字段
- 特定内容的翻译表继承基础表并添加特定字段
- 组合索引确保每个记录的每种语言只有一个翻译
3. 用户语言偏好:
- 用户表增加preferred_language字段存储语言偏好
- get_user_language()函数获取当前用户的语言设置
4. 获取翻译的方式:
- 直接SQL查询翻译表
- 使用辅助函数如get_translation(), get_region_name()
- 通过多语言视图(如vw_regions_i18n)
5. 前端界面文本翻译:
- ak_app_translations表存储UI文本翻译
- get_ui_text()函数根据key获取对应语言的文本
*/