diff --git a/src/main/java/com/yunhai/homework/common/listener/HomeworkAuditListener.java b/src/main/java/com/yunhai/homework/common/listener/HomeworkAuditListener.java index 67cbf7c..7a8d83b 100644 --- a/src/main/java/com/yunhai/homework/common/listener/HomeworkAuditListener.java +++ b/src/main/java/com/yunhai/homework/common/listener/HomeworkAuditListener.java @@ -11,7 +11,7 @@ import java.time.LocalDateTime; /** * JPA 审计监听器:在实体持久化/更新前自动填充创建人/更新人(平台当前登录人)与时间戳。 * 对应 {@link BaseEntity} 的 createBy / updateBy / createTime / updateTime 字段。 - * 当前登录人取自 {@link UserContext}(用户中心数字ID,字符串形式落审计列)。 + * 当前登录人取自 {@link UserContext}(用户中心数字ID,直接以 INT 落审计列)。 */ @Component public class HomeworkAuditListener { @@ -24,9 +24,9 @@ public class HomeworkAuditListener { Integer userId = UserContext.getUserId(); if (userId != null) { if (entity.getCreateBy() == null) { - entity.setCreateBy(String.valueOf(userId)); + entity.setCreateBy(userId); } - entity.setUpdateBy(String.valueOf(userId)); + entity.setUpdateBy(userId); } } @@ -35,7 +35,7 @@ public class HomeworkAuditListener { entity.setUpdateTime(LocalDateTime.now()); Integer userId = UserContext.getUserId(); if (userId != null) { - entity.setUpdateBy(String.valueOf(userId)); + entity.setUpdateBy(userId); } } diff --git a/src/main/java/com/yunhai/homework/entity/BaseEntity.java b/src/main/java/com/yunhai/homework/entity/BaseEntity.java index 5f98e8c..c4aa39b 100644 --- a/src/main/java/com/yunhai/homework/entity/BaseEntity.java +++ b/src/main/java/com/yunhai/homework/entity/BaseEntity.java @@ -24,10 +24,10 @@ public abstract class BaseEntity implements Serializable { private Integer id; /** - * 创建人ID(平台用户ID) + * 创建人ID(用户中心数字ID,与业务人员标识同一 ID 空间) */ @Column(name = "create_by", updatable = false) - private String createBy; + private Integer createBy; /** * 创建时间 @@ -36,10 +36,10 @@ public abstract class BaseEntity implements Serializable { private LocalDateTime createTime; /** - * 更新人ID(平台用户ID) + * 更新人ID(用户中心数字ID) */ @Column(name = "update_by") - private String updateBy; + private Integer updateBy; /** * 更新时间 diff --git a/src/main/java/com/yunhai/homework/entity/SysDict.java b/src/main/java/com/yunhai/homework/entity/SysDict.java index 3091084..56eec09 100644 --- a/src/main/java/com/yunhai/homework/entity/SysDict.java +++ b/src/main/java/com/yunhai/homework/entity/SysDict.java @@ -18,6 +18,13 @@ import javax.persistence.Table; @EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false) public class SysDict extends BaseEntity { + /** + * 学校ID(多租户隔离:新增时从当前用户角色填充,查询时按校过滤; + * 存量数据为 NULL,视为全局字典,全校可见)。 + */ + @Column(name = "sch_id") + private Integer schId; + /** 字典类型(如 "dept"=发文部门) */ @Column(name = "dict_type", length = 50) private String dictType; diff --git a/src/main/java/com/yunhai/homework/model/homework/HomeworkVo.java b/src/main/java/com/yunhai/homework/model/homework/HomeworkVo.java index 333d49e..a7597c4 100644 --- a/src/main/java/com/yunhai/homework/model/homework/HomeworkVo.java +++ b/src/main/java/com/yunhai/homework/model/homework/HomeworkVo.java @@ -73,9 +73,11 @@ public class HomeworkVo { */ private com.yunhai.homework.model.v2.ProgressVo progress; - private String createBy; + /** 创建人ID(用户中心数字ID) */ + private Integer createBy; private LocalDateTime createTime; - private String updateBy; + /** 更新人ID(用户中心数字ID) */ + private Integer updateBy; private LocalDateTime updateTime; /** diff --git a/src/main/java/com/yunhai/homework/repository/SysDictRepository.java b/src/main/java/com/yunhai/homework/repository/SysDictRepository.java index c083840..5d024b8 100644 --- a/src/main/java/com/yunhai/homework/repository/SysDictRepository.java +++ b/src/main/java/com/yunhai/homework/repository/SysDictRepository.java @@ -2,14 +2,44 @@ package com.yunhai.homework.repository; import com.yunhai.homework.entity.SysDict; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import java.util.List; import java.util.Optional; public interface SysDictRepository extends JpaRepository { + + // ---------------- 存量/无学校归属(schId 为空时不过滤,同 homework 多租户口径) ---------------- + + /** 全量(含禁用),用于管理列表与重名/序号计算 */ List findByDictTypeOrderBySortOrderAsc(String dictType); + + /** 启用项(下拉框用) */ List findByDictTypeAndEnableFlagOrderBySortOrderAsc(String dictType, Integer enableFlag); - /** 按类型+key 取单条字典(发文部门名称解析用) */ + /** 按类型+key 取单条字典 */ Optional findByDictTypeAndDictKey(String dictType, String dictKey); + + // ---------------- 多租户:本校 + 全局(sch_id 为 NULL 的存量视为全局,全校可见) ---------------- + + /** 可见全量(含禁用):本校 + 全局 */ + @Query("select d from SysDict d where d.dictType = :dictType " + + "and (d.schId = :schId or d.schId is null) order by d.sortOrder asc") + List findVisibleBySchIdAndDictType(@Param("schId") Integer schId, @Param("dictType") String dictType); + + /** 可见启用项:本校 + 全局(下拉框用) */ + @Query("select d from SysDict d where d.dictType = :dictType and d.enableFlag = :enableFlag " + + "and (d.schId = :schId or d.schId is null) order by d.sortOrder asc") + List findVisibleEnabledBySchIdAndDictType(@Param("schId") Integer schId, + @Param("dictType") String dictType, + @Param("enableFlag") Integer enableFlag); + + /** 可见单条:本校优先,其次全局;同 key 多条时按本校优先 + id 升序,服务侧取首条 */ + @Query("select d from SysDict d where d.dictType = :dictType and d.dictKey = :dictKey " + + "and (d.schId = :schId or d.schId is null) " + + "order by case when d.schId = :schId then 0 else 1 end asc, d.id asc") + List findVisibleBySchIdAndDictTypeAndDictKey(@Param("schId") Integer schId, + @Param("dictType") String dictType, + @Param("dictKey") String dictKey); } diff --git a/src/main/java/com/yunhai/homework/service/IDictService.java b/src/main/java/com/yunhai/homework/service/IDictService.java index 31ef515..fae44eb 100644 --- a/src/main/java/com/yunhai/homework/service/IDictService.java +++ b/src/main/java/com/yunhai/homework/service/IDictService.java @@ -69,7 +69,10 @@ public interface IDictService { * 用于作业列表/详情展示 DeptName(对应 C# GetDeptName)。 * * @param deptId 作业 deptId(存的是 sys_dict.dict_key) + * @param schId 作业所属学校ID(展示场景传 homework.schId,写入场景传当前登录人学校; + * 多租户下优先本校字典、其次全局字典,不会命中其他学校同名 key; + * 为 null 时退化为全局匹配,兼容存量数据) * @return 部门名称;无匹配时返回 null */ - String resolveDeptName(String deptId); + String resolveDeptName(String deptId, Integer schId); } diff --git a/src/main/java/com/yunhai/homework/service/impl/DictService.java b/src/main/java/com/yunhai/homework/service/impl/DictService.java index 23df1c6..026f34e 100644 --- a/src/main/java/com/yunhai/homework/service/impl/DictService.java +++ b/src/main/java/com/yunhai/homework/service/impl/DictService.java @@ -9,6 +9,7 @@ import com.yunhai.homework.repository.SmsModuleRepository; import com.yunhai.homework.repository.SmsSchoolConfigRepository; import com.yunhai.homework.repository.SysDictRepository; import com.yunhai.homework.service.IDictService; +import com.yunhai.homework.utils.UserContext; import com.yunhai.platform.apistd.BizException; import com.yunhai.platform.utils.exception.ErrorCode; import org.slf4j.Logger; @@ -19,6 +20,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; +import javax.persistence.criteria.Predicate; import java.util.*; import java.util.stream.Collectors; @@ -64,7 +66,7 @@ public class DictService implements IDictService { if (dictType == null) { throw new BizException(ErrorCode.PARAM_ERROR, "参数错误"); } - List list = sysDictRepository.findByDictTypeAndEnableFlagOrderBySortOrderAsc(dictType, 1); + List list = visibleEnabledList(dictType); if (list.isEmpty()) { throw new BizException(ErrorCode.FORBIDDEN, "尚未添加发文部门,请联系管理员添加!"); } @@ -90,7 +92,7 @@ public class DictService implements IDictService { // 非 DEPT 类型需超管权限,本系统暂只支持 DEPT throw new BizException(ErrorCode.FORBIDDEN, "非超管不得操作!"); } - List list = sysDictRepository.findByDictTypeOrderBySortOrderAsc(dictType); + List list = visibleList(dictType); return list.stream() .sorted(Comparator.comparingInt(d -> d.getSortOrder() == null ? 999 : d.getSortOrder())) .map(d -> { @@ -113,21 +115,24 @@ public class DictService implements IDictService { if (!StringUtils.hasText(name)) { throw new BizException(ErrorCode.PARAM_ERROR, "输入字符串格式不正确!"); } - // 校验重名 - List existing = sysDictRepository.findByDictTypeOrderBySortOrderAsc(DICT_TYPE_DEPT); + Integer schId = UserContext.getSchId(); + // 重名校验范围 = 本校 + 全局可见项,避免与存量全局字典重名 + List existing = visibleList(DICT_TYPE_DEPT); boolean duplicate = existing.stream().anyMatch(d -> DICT_TYPE_DEPT.equals(d.getDictType()) && name.equals(d.getDictValue()) && d.getEnableFlag() != 3); if (duplicate) { throw new BizException(ErrorCode.FORBIDDEN, "已存在该分类,请重新填写!"); } - // 计算 sortOrder = max + 1 + // 计算 sortOrder = max + 1(同样基于本校 + 全局可见项) int maxSort = existing.stream() .filter(d -> d.getEnableFlag() != 3) .mapToInt(d -> d.getSortOrder() != null ? d.getSortOrder() : 0) .max().orElse(0); SysDict dict = new SysDict(); + // 多租户:归属当前登录人学校;无学校归属(区域级)时为 NULL,即全局字典 + dict.setSchId(schId); dict.setDictType(DICT_TYPE_DEPT); dict.setDictValue(name); dict.setSortOrder(maxSort + 1); @@ -144,16 +149,18 @@ public class DictService implements IDictService { if (serID == null) { throw new BizException(ErrorCode.PARAM_ERROR, "参数错误!"); } - // 校验重名(排除自身) - List existing = sysDictRepository.findByDictTypeOrderBySortOrderAsc(DICT_TYPE_DEPT); + SysDict dict = sysDictRepository.findById(serID) + .orElseThrow(() -> new BizException(ErrorCode.FORBIDDEN, "字典记录不存在")); + checkSchoolScope(dict); + + // 校验重名(排除自身,范围 = 本校 + 全局可见项) + List existing = visibleList(DICT_TYPE_DEPT); boolean duplicate = existing.stream().anyMatch(d -> !d.getId().equals(serID) && name.equals(d.getDictValue()) && d.getEnableFlag() != 3); if (duplicate) { throw new BizException(ErrorCode.FORBIDDEN, "已存在该分类,请重新填写!"); } - SysDict dict = sysDictRepository.findById(serID) - .orElseThrow(() -> new BizException(ErrorCode.FORBIDDEN, "字典记录不存在")); if (StringUtils.hasText(name)) { dict.setDictValue(name); } @@ -170,11 +177,20 @@ public class DictService implements IDictService { public void delDept(Integer serID) { SysDict dict = sysDictRepository.findById(serID) .orElseThrow(() -> new BizException(ErrorCode.FORBIDDEN, "字典记录不存在")); + checkSchoolScope(dict); // 校验是否被作业引用(按 deptId = dictKey 匹配) // C# 中检查 Notify_Message.DeptID == dict.CodeID - // Java 中 Homework.deptId 存的是 dictKey - List referenced = homeworkRepository.findAll((root, query, cb) -> - cb.equal(root.get("deptId"), dict.getDictKey())); + // Java 中 Homework.deptId 存的是 dictKey;多租户下仅本校作业算引用, + // 其他学校同名 key(如各校自建的 dept1)不受影响 + Integer refSchId = dict.getSchId() != null ? dict.getSchId() : UserContext.getSchId(); + List referenced = homeworkRepository.findAll((root, query, cb) -> { + List predicates = new ArrayList<>(); + predicates.add(cb.equal(root.get("deptId"), dict.getDictKey())); + if (refSchId != null) { + predicates.add(cb.equal(root.get("schId"), refSchId)); + } + return cb.and(predicates.toArray(new Predicate[0])); + }); if (!referenced.isEmpty()) { throw new BizException(ErrorCode.FORBIDDEN, "已引用该部门,无法删除!"); } @@ -189,7 +205,7 @@ public class DictService implements IDictService { if (!StringUtils.hasText(ids)) { throw new BizException(ErrorCode.PARAM_ERROR, "参数错误"); } - List list = sysDictRepository.findByDictTypeOrderBySortOrderAsc(DICT_TYPE_DEPT); + List list = visibleList(DICT_TYPE_DEPT); if (list.isEmpty()) { throw new BizException(ErrorCode.FORBIDDEN, "字典类型错误,未找到该字典类型"); } @@ -205,12 +221,18 @@ public class DictService implements IDictService { .mapToInt(d -> d.getSortOrder() != null ? d.getSortOrder() : idArr.length) .max().orElse(idArr.length); + // 仅本校可见字典可参与排序(前端 ids 即来自本校管理列表) + Set visibleIds = list.stream().map(SysDict::getId).collect(Collectors.toSet()); + // lambda 中不能修改局部变量,用数组包装 final int[] indexOrd = { maxSort }; // 逆序遍历,从最大序号递减 for (int i = idArr.length - 1; i >= 0; i--) { int id = idArr[i]; + if (!visibleIds.contains(id)) { + continue; + } sysDictRepository.findById(id).ifPresent(dict -> { dict.setSortOrder(indexOrd[0]); sysDictRepository.save(dict); @@ -244,17 +266,51 @@ public class DictService implements IDictService { /** * 按发文部门字典 key 解析部门名称(对应 C# GetDeptName)。 + * 多租户:schId 非空时优先本校字典、其次全局(sch_id 为 NULL)字典, + * 不会命中其他学校的同名 key;schId 为空时退化为全局匹配(兼容存量调用)。 */ @Override - public String resolveDeptName(String deptId) { + public String resolveDeptName(String deptId, Integer schId) { if (!StringUtils.hasText(deptId)) { return null; } - return sysDictRepository.findByDictTypeAndDictKey(DICT_TYPE_DEPT, deptId.trim()) + String key = deptId.trim(); + if (schId != null) { + List scoped = sysDictRepository.findVisibleBySchIdAndDictTypeAndDictKey(schId, DICT_TYPE_DEPT, key); + if (!scoped.isEmpty()) { + return scoped.get(0).getDictValue(); + } + return null; + } + return sysDictRepository.findByDictTypeAndDictKey(DICT_TYPE_DEPT, key) .map(SysDict::getDictValue) .orElse(null); } + /** 当前登录人可见的全量字典(本校 + 全局;无学校归属时不过滤) */ + private List visibleList(String dictType) { + Integer schId = UserContext.getSchId(); + return schId != null + ? sysDictRepository.findVisibleBySchIdAndDictType(schId, dictType) + : sysDictRepository.findByDictTypeOrderBySortOrderAsc(dictType); + } + + /** 当前登录人可见的启用字典(本校 + 全局;无学校归属时不过滤) */ + private List visibleEnabledList(String dictType) { + Integer schId = UserContext.getSchId(); + return schId != null + ? sysDictRepository.findVisibleEnabledBySchIdAndDictType(schId, dictType, 1) + : sysDictRepository.findByDictTypeAndEnableFlagOrderBySortOrderAsc(dictType, 1); + } + + /** 多租户写保护:禁止操作其他学校的字典(全局字典与无学校归属用户不受限) */ + private void checkSchoolScope(SysDict dict) { + Integer schId = UserContext.getSchId(); + if (dict.getSchId() != null && schId != null && !dict.getSchId().equals(schId)) { + throw new BizException(ErrorCode.FORBIDDEN, "无权操作其他学校的字典!"); + } + } + /** * 将前端 Type 参数映射到字典类型字符串。 * C# 中使用枚举,Java 版仅支持 DEPT(1)。 diff --git a/src/main/java/com/yunhai/homework/service/impl/HomeworkService.java b/src/main/java/com/yunhai/homework/service/impl/HomeworkService.java index 34d5622..669dd8b 100644 --- a/src/main/java/com/yunhai/homework/service/impl/HomeworkService.java +++ b/src/main/java/com/yunhai/homework/service/impl/HomeworkService.java @@ -1021,9 +1021,9 @@ public class HomeworkService implements IHomeworkService { String legacy = legacyHwStatus(entity); vo.setHwStatus(legacy); vo.setHwStatusDesc(legacyHwStatusDesc(legacy)); - // 存量数据 dept_name 为空时按 deptId 从字典兜底解析(对应 C# GetDeptName) + // 存量数据 dept_name 为空时按 deptId 从字典兜底解析(对应 C# GetDeptName;按作业所属学校匹配) if (!StringUtils.hasText(vo.getDeptName())) { - vo.setDeptName(dictService.resolveDeptName(vo.getDeptId())); + vo.setDeptName(dictService.resolveDeptName(vo.getDeptId(), entity.getSchId())); } vo.setRead(read); vo.setReadCount(homeworkReadRepository.countByHomeworkId(entity.getId())); @@ -1031,10 +1031,10 @@ public class HomeworkService implements IHomeworkService { } /** - * 写入时解析部门名称:优先按 deptId 查字典,字典缺失时回退前端传值。 + * 写入时解析部门名称:优先按 deptId 查本校字典,字典缺失时回退前端传值。 */ private String resolveDeptName(HomeworkRequest request) { - String resolved = dictService.resolveDeptName(request.getDeptId()); + String resolved = dictService.resolveDeptName(request.getDeptId(), UserContext.getSchId()); return StringUtils.hasText(resolved) ? resolved : request.getDeptName(); } diff --git a/src/main/resources/sql/homework_receiver_create.sql b/src/main/resources/sql/homework_receiver_create.sql index b414f22..5f140a6 100644 --- a/src/main/resources/sql/homework_receiver_create.sql +++ b/src/main/resources/sql/homework_receiver_create.sql @@ -16,9 +16,9 @@ CREATE TABLE IF NOT EXISTS `homework_receiver` ( `receiver_id` INT NOT NULL COMMENT '接收人ID(用户中心数字ID)', `receiver_name` VARCHAR(64) NULL COMMENT '接收人姓名快照', `receive_type` INT NOT NULL DEFAULT 1 COMMENT '接收类型:1 接收 / 2 抄送', - `create_by` VARCHAR(64) NULL COMMENT '创建人ID(审计填充)', + `create_by` INT NULL COMMENT '创建人ID(用户中心数字ID)', `create_time` DATETIME NULL COMMENT '创建时间(审计填充)', - `update_by` VARCHAR(64) NULL COMMENT '更新人ID(审计填充)', + `update_by` INT NULL COMMENT '更新人ID(用户中心数字ID)', `update_time` DATETIME NULL COMMENT '更新时间(审计填充)', PRIMARY KEY (`id`), KEY `idx_homework_id` (`homework_id`), diff --git a/src/main/resources/sql/v3_schema.sql b/src/main/resources/sql/v3_schema.sql index 571f162..2cc55ad 100644 --- a/src/main/resources/sql/v3_schema.sql +++ b/src/main/resources/sql/v3_schema.sql @@ -6,7 +6,9 @@ -- 2) MySQL 5.7 不支持 ADD COLUMN IF NOT EXISTS,重复执行前请先核对列是否存在; -- 3) 定时发布时间复用 homework.timing_send_time(PRD scheduled_at 同义),不新增列。 -- 4) 人员标识口径:教师/学生一律为用户中心数字ID(INT),不存登录账号 --- (与 v3_account_to_id_refactor.sql / v4_person_id_to_int.sql 收敛后的最终形态一致)。 +-- (与 v3_account_to_id_refactor.sql / v4_person_id_to_int.sql 收敛后的最终形态一致); +-- 审计列 create_by / update_by 亦为用户中心数字ID(INT), +-- 与 v5_audit_by_to_int.sql 收敛后的最终形态一致。 -- ===================================================================== -- --------------------------------------------------------------------- @@ -32,9 +34,9 @@ CREATE TABLE IF NOT EXISTS `homework_class` ( `homework_id` INT NOT NULL, `class_id` VARCHAR(64) NOT NULL, `class_name` VARCHAR(50) NULL COMMENT '班级名快照', - `create_by` VARCHAR(64) NULL, + `create_by` INT NULL COMMENT '创建人ID(用户中心数字ID)', `create_time` DATETIME NULL, - `update_by` VARCHAR(64) NULL, + `update_by` INT NULL COMMENT '更新人ID(用户中心数字ID)', `update_time` DATETIME NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_hw_class` (`homework_id`, `class_id`) @@ -51,9 +53,9 @@ CREATE TABLE IF NOT EXISTS `homework_attachment` ( `file_type` VARCHAR(20) NULL COMMENT 'image/file/...', `file_name` VARCHAR(200) NULL, `sort` INT NOT NULL DEFAULT 0, - `create_by` VARCHAR(64) NULL, + `create_by` INT NULL COMMENT '创建人ID(用户中心数字ID)', `create_time` DATETIME NULL, - `update_by` VARCHAR(64) NULL, + `update_by` INT NULL COMMENT '更新人ID(用户中心数字ID)', `update_time` DATETIME NULL, KEY `idx_hw` (`homework_id`), PRIMARY KEY (`id`) @@ -72,9 +74,9 @@ CREATE TABLE IF NOT EXISTS `homework_read_record` ( `first_read_at` DATETIME NULL, `last_read_at` DATETIME NULL, `read_count` INT NOT NULL DEFAULT 0, - `create_by` VARCHAR(64) NULL, + `create_by` INT NULL COMMENT '创建人ID(用户中心数字ID)', `create_time` DATETIME NULL, - `update_by` VARCHAR(64) NULL, + `update_by` INT NULL COMMENT '更新人ID(用户中心数字ID)', `update_time` DATETIME NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_hw_student` (`homework_id`, `student_id`) @@ -103,9 +105,9 @@ CREATE TABLE IF NOT EXISTS `homework_submission` ( `reviewed_by` INT NULL COMMENT '批阅人ID(用户中心数字ID,同教师ID空间)', `reviewed_by_name` VARCHAR(64) NULL COMMENT '批阅人姓名快照', `reviewed_at` DATETIME NULL, - `create_by` VARCHAR(64) NULL, + `create_by` INT NULL COMMENT '创建人ID(用户中心数字ID)', `create_time` DATETIME NULL, - `update_by` VARCHAR(64) NULL, + `update_by` INT NULL COMMENT '更新人ID(用户中心数字ID)', `update_time` DATETIME NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_hw_student_ver` (`homework_id`, `student_id`, `version`), @@ -126,9 +128,9 @@ CREATE TABLE IF NOT EXISTS `submission_attachment` ( `file_type` VARCHAR(20) NULL COMMENT 'image/file', `file_name` VARCHAR(200) NULL, `sort` INT NOT NULL DEFAULT 0, - `create_by` VARCHAR(64) NULL, + `create_by` INT NULL COMMENT '创建人ID(用户中心数字ID)', `create_time` DATETIME NULL, - `update_by` VARCHAR(64) NULL, + `update_by` INT NULL COMMENT '更新人ID(用户中心数字ID)', `update_time` DATETIME NULL, KEY `idx_sub` (`submission_id`), PRIMARY KEY (`id`) @@ -147,9 +149,9 @@ CREATE TABLE IF NOT EXISTS `class_student` ( `student_name` VARCHAR(50) NOT NULL, `status` TINYINT NOT NULL DEFAULT 1 COMMENT '1 在班 0 已转出/退课', `leave_at` DATETIME NULL, - `create_by` VARCHAR(64) NULL, + `create_by` INT NULL COMMENT '创建人ID(用户中心数字ID)', `create_time` DATETIME NULL, - `update_by` VARCHAR(64) NULL, + `update_by` INT NULL COMMENT '更新人ID(用户中心数字ID)', `update_time` DATETIME NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_class_student` (`class_id`, `student_id`) @@ -172,9 +174,9 @@ CREATE TABLE IF NOT EXISTS `notification` ( `is_read` TINYINT NOT NULL DEFAULT 0, `read_at` DATETIME NULL, `send_channel` VARCHAR(20) NOT NULL DEFAULT 'inapp' COMMENT 'inapp/sms', - `create_by` VARCHAR(64) NULL, + `create_by` INT NULL COMMENT '创建人ID(用户中心数字ID)', `create_time` DATETIME NULL, - `update_by` VARCHAR(64) NULL, + `update_by` INT NULL COMMENT '更新人ID(用户中心数字ID)', `update_time` DATETIME NULL, KEY `idx_receiver` (`receiver_id`, `is_read`), KEY `idx_hw_type` (`homework_id`, `type`),