library-picturebook-activity/docs/project/03-database-design.md
En 7bc8c10d9a feat: 系统品牌更名为"智创未来"及相关配置调整
- 前后端所有"乐绘世界"统一更名为"智创未来"
- 生产环境乐读派API地址更新为公网地址
- 公众端登录页调整用户名/密码字段显示逻辑
- 同步更新文档、测试用例、主题样式中的品牌名称

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 19:30:26 +08:00

13 KiB
Raw Blame History

数据库设计

1. 变更概览

操作 表名 说明
修改 users 新增 phone、wx_openid、user_source、city 等字段
修改 tenants 新增 tenant_type 字段
修改 registrations 新增 participant_type、child_id 字段
修改 contests 新增 visibility 字段
新增 children 子女信息表
新增(二期) user_identities 用户身份表(一号多身份)
新增(二期) contest_co_organizers 活动协办机构表

2. 表结构详细设计

2.1 users 表(改造)

新增字段:

字段名 类型 必填 默认值 说明
phone VARCHAR(20) NULL 手机号,全局唯一,用于手机号登录
wx_openid VARCHAR(64) NULL 微信 OpenID全局唯一
wx_unionid VARCHAR(64) NULL 微信 UnionID跨应用统一标识
user_source ENUM 'admin_created' 用户来源admin_created / self_registered
city VARCHAR(50) NULL 所在城市
birthday DATE NULL 出生日期(自注册用户可填写)
-- 新增字段
ALTER TABLE users ADD COLUMN phone VARCHAR(20) UNIQUE DEFAULT NULL COMMENT '手机号';
ALTER TABLE users ADD COLUMN wx_openid VARCHAR(64) UNIQUE DEFAULT NULL COMMENT '微信OpenID';
ALTER TABLE users ADD COLUMN wx_unionid VARCHAR(64) DEFAULT NULL COMMENT '微信UnionID';
ALTER TABLE users ADD COLUMN user_source VARCHAR(20) NOT NULL DEFAULT 'admin_created' COMMENT '用户来源admin_created/self_registered';
ALTER TABLE users ADD COLUMN city VARCHAR(50) DEFAULT NULL COMMENT '所在城市';
ALTER TABLE users ADD COLUMN birthday DATE DEFAULT NULL COMMENT '出生日期';

-- 索引
CREATE INDEX idx_users_phone ON users(phone);
CREATE INDEX idx_users_wx_openid ON users(wx_openid);
CREATE INDEX idx_users_user_source ON users(user_source);

设计说明

  • phone 设为 UNIQUE 但允许 NULL因为管理员创建的机构用户可能没有手机号
  • wx_openidwx_unionid 为二期微信登录预留
  • user_source 区分用户是管理员创建的还是自主注册的,方便后台统计和管理
  • 自注册用户归属公众租户code='public', tenantId=8而非 tenant_id 为 NULL以兼容现有 JWT 认证链路

2.2 children 表(新增)

子女信息表,关联家长账号。

字段名 类型 必填 默认值 说明
id INT AUTO_INCREMENT 主键
parent_id INT - 家长用户 ID关联 users.id
name VARCHAR(50) - 子女姓名
gender VARCHAR(10) NULL 性别male / female
birthday DATE NULL 出生日期
grade VARCHAR(20) NULL 年级(如:大班、一年级)
city VARCHAR(50) NULL 所在城市
school_name VARCHAR(100) NULL 学校/幼儿园名称(手动填写)
avatar VARCHAR(500) NULL 头像 URL
is_deleted TINYINT(1) 0 软删除标记
create_time DATETIME CURRENT_TIMESTAMP 创建时间
modify_time DATETIME CURRENT_TIMESTAMP 修改时间
CREATE TABLE children (
  id INT AUTO_INCREMENT PRIMARY KEY,
  parent_id INT NOT NULL COMMENT '家长用户ID',
  name VARCHAR(50) NOT NULL COMMENT '子女姓名',
  gender VARCHAR(10) DEFAULT NULL COMMENT '性别male/female',
  birthday DATE DEFAULT NULL COMMENT '出生日期',
  grade VARCHAR(20) DEFAULT NULL COMMENT '年级',
  city VARCHAR(50) DEFAULT NULL COMMENT '所在城市',
  school_name VARCHAR(100) DEFAULT NULL COMMENT '学校/幼儿园名称',
  avatar VARCHAR(500) DEFAULT NULL COMMENT '头像URL',
  is_deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '软删除标记',
  create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  modify_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
  INDEX idx_children_parent_id (parent_id),
  CONSTRAINT fk_children_parent FOREIGN KEY (parent_id) REFERENCES users(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='子女信息表';

设计说明

  • parent_id 关联 users 表,一个家长可以有多个子女
  • school_name 是手动填写的文本字段,不关联租户表,因为公众用户的学校不一定是平台上的租户
  • grade 存储文本如"大班""一年级",不做枚举约束,因为不同教育体系的年级名称不同
  • 使用软删除(is_deleted),避免误删导致历史报名数据关联丢失

2.3 tenants 表(改造)

新增字段:

字段名 类型 必填 默认值 说明
tenant_type VARCHAR(20) 'other' 租户类型
ALTER TABLE tenants ADD COLUMN tenant_type VARCHAR(20) NOT NULL DEFAULT 'other'
  COMMENT '租户类型platform/library/kindergarten/school/institution/other';

CREATE INDEX idx_tenants_type ON tenants(tenant_type);

租户类型枚举值

说明
platform 平台租户(智创未来,全局唯一)
library 图书馆
kindergarten 幼儿园
school 学校
institution 社会机构
other 其他

2.4 registrations 表(改造)

新增字段:

字段名 类型 必填 默认值 说明
participant_type VARCHAR(10) 'self' 参与者类型self自己/ child子女
child_id INT NULL 子女 IDparticipant_type 为 child 时必填
ALTER TABLE registrations ADD COLUMN participant_type VARCHAR(10) NOT NULL DEFAULT 'self'
  COMMENT '参与者类型self-自己/child-代子女报名';
ALTER TABLE registrations ADD COLUMN child_id INT DEFAULT NULL
  COMMENT '子女ID代子女报名时填写';

CREATE INDEX idx_registrations_participant ON registrations(participant_type);
CREATE INDEX idx_registrations_child ON registrations(child_id);

设计说明

  • user_id(现有字段)始终记录操作人(谁提交的报名)
  • participant_type = 'self':用户以自己身份报名,child_id 为 NULL
  • participant_type = 'child':家长代子女报名,child_id 指向 children 表
  • 管理端查看报名记录时,能清晰区分"张妈妈帮小明报名"和"李同学自己报名"

2.5 contests 表(改造)

新增字段:

字段名 类型 必填 默认值 说明
visibility VARCHAR(20) 'designated' 活动可见范围
ALTER TABLE contests ADD COLUMN visibility VARCHAR(20) NOT NULL DEFAULT 'designated'
  COMMENT '可见范围public-公开/designated-指定机构/internal-仅内部';

可见范围说明

说明 数据过滤逻辑
public 全体公开 所有注册用户可见,公众端活动大厅展示
designated 指定机构 仅 contest_tenants 中列出的租户可见(现有逻辑)
internal 仅内部 仅创建该活动的机构内部可见

2.6 user_identities 表(二期新增)

一号多身份的身份关联表。

字段名 类型 必填 默认值 说明
id INT AUTO_INCREMENT 主键
user_id INT - 用户 ID
identity_type VARCHAR(20) - 身份类型
tenant_id INT NULL 关联租户(公众身份为 NULL
role_id INT NULL 关联角色
status VARCHAR(10) 'active' 状态active / inactive
create_time DATETIME CURRENT_TIMESTAMP 创建时间
-- 二期实现
CREATE TABLE user_identities (
  id INT AUTO_INCREMENT PRIMARY KEY,
  user_id INT NOT NULL COMMENT '用户ID',
  identity_type VARCHAR(20) NOT NULL COMMENT '身份类型public/tenant_admin/judge/staff',
  tenant_id INT DEFAULT NULL COMMENT '关联租户ID',
  role_id INT DEFAULT NULL COMMENT '关联角色ID',
  status VARCHAR(10) NOT NULL DEFAULT 'active' COMMENT '状态active/inactive',
  create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  INDEX idx_identity_user (user_id),
  INDEX idx_identity_tenant (tenant_id),
  UNIQUE KEY uk_user_tenant_type (user_id, tenant_id, identity_type),
  CONSTRAINT fk_identity_user FOREIGN KEY (user_id) REFERENCES users(id),
  CONSTRAINT fk_identity_tenant FOREIGN KEY (tenant_id) REFERENCES tenants(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户身份表';

2.7 contest_co_organizers 表(二期新增)

活动协办机构关联表。

-- 二期实现
CREATE TABLE contest_co_organizers (
  id INT AUTO_INCREMENT PRIMARY KEY,
  contest_id INT NOT NULL COMMENT '活动ID',
  tenant_id INT NOT NULL COMMENT '协办机构租户ID',
  permission_level VARCHAR(20) NOT NULL DEFAULT 'readonly' COMMENT '权限级别readonly/limited/full',
  create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  UNIQUE KEY uk_contest_tenant (contest_id, tenant_id),
  CONSTRAINT fk_co_org_contest FOREIGN KEY (contest_id) REFERENCES contests(id),
  CONSTRAINT fk_co_org_tenant FOREIGN KEY (tenant_id) REFERENCES tenants(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='活动协办机构表';

3. ER 关系图

                    ┌──────────┐
                    │  tenants │
                    │──────────│
                    │ id       │
                    │ name     │
                    │ tenant_  │
         ┌──────────│  type    │──────────┐
         │          └──────────┘          │
         │ tenant_id                      │ organizer_
         │                                │  tenant_id
    ┌────▼─────┐                   ┌──────▼──────┐
    │  users   │                   │  contests   │
    │──────────│                   │─────────────│
    │ id       │                   │ id          │
    │ username │                   │ contestName │
    │ phone    │                   │ visibility  │
    │ user_    │                   │ contestType │
    │  source  │                   └──────┬──────┘
    └──┬───┬───┘                          │
       │   │                              │ contest_id
       │   │ parent_id              ┌─────▼────────┐
       │   │                        │registrations │
       │   ├──────────┐             │──────────────│
       │   │          │             │ id           │
       │   │   ┌──────▼──────┐     │ user_id ◄────┼──── users.id
       │   │   │  children   │     │ participant_ │
       │   │   │─────────────│     │   type       │
       │   │   │ id          │     │ child_id ◄───┼──── children.id
       │   │   │ parent_id   │     │ contest_id   │
       │   │   │ name        │     │ status       │
       │   │   │ birthday    │     └──────────────┘
       │   │   └─────────────┘
       │   │
       │   └─── user_id ─────────── registrations.user_id
       │
       └─── tenant_id ──────────── tenants.id (公众用户归属 public 租户)

4. 数据迁移注意事项

4.1 现有数据兼容

  • users.user_source:现有用户默认值为 admin_created,无需迁移
  • users.phone:允许 NULL现有用户无需填写
  • registrations.participant_type:现有报名记录默认值为 self,无需迁移
  • tenants.tenant_type:现有租户默认值为 other,需要手动为已有租户设置正确类型
  • contests.visibility:现有活动默认值为 designated,保持现有行为不变

4.2 Prisma Schema 同步

所有数据库变更需要同步到 Prisma Schema 文件,通过 prisma migrate 管理迁移。

4.3 广东省图初始化数据

-- 创建广东省图租户
INSERT INTO tenants (name, code, tenant_type, description, valid_state)
VALUES ('广东省立中山图书馆', 'gdlib', 'library', '广东省图少儿绘本创作活动主办方', 1);

-- 创建广东省图管理员账号
-- (通过初始化脚本创建,关联对应的角色和权限)