fix/roster-enrollment-sync-hardening
main
承接 #83(班级花名册与档案报读双向同步)。对合并后的代码做多轮 OCR review + 修复循环(12 轮迭代,最后一轮本任务范围 0 评论),主要修复并发幂等/死锁/数据一致性问题。
ensureEnrollmentsForRoster
missing
courseCategory
TeacherRoleType
archive.addEnrollment/updateEnrollment
saveEnrollmentWithDupGuard
isDuplicateEntryError
common/db-errors.ts
classes.membership.spec
addStudents
enrollmentsCreated: 0
1786515492285-AddIndexToStudentEnrollments
(class_id, student_id)
1786517000000-AddActiveUniqueToStudentEnrollments
keep_enrollment_id
student_enrollments_active_cleanup
['archive', studentId] + queryKeys.classes.all
ENROLLMENT_INVALIDATE
- ensureEnrollmentsForRoster:普通读 + 分块批量插入,批内撞 active 部分唯一索引 时逐行回退(1062 跳过),missing 去重并按 studentId 排序防死锁; courseCategory 仅映射 culture/professional/comprehensive,角色类型用枚举 - 导入路径恢复全量同步(不剔除 alreadyAdded,避免钉钉导入/建班等不同步 路径导致漏建报读) - archive add/updateEnrollment 保存 1062 转 400(saveEnrollmentWithDupGuard), 并补 2 个单测 - isDuplicateEntryError 提取到 common/db-errors.ts 单一来源(queries re-export) - 修复 classes.membership.spec:mock dataSource.transaction + manager, 断言更新为新返回结构;addStudents 空输入早返回补 enrollmentsCreated: 0
- AddIndexToStudentEnrollments:student_enrollments 补 (class_id, student_id) 普通索引,加速幂等查找(NON_UNIQUE=1 守卫 + 1061/1091 并发幂等) - AddActiveUniqueToStudentEnrollments:active 部分唯一函数索引(MySQL 8.0.13+ 版本守卫),闭合并发重复报读;up 前 GET_LOCK 串行化 + 清洗存量重复 active (保留最早、其余置 archived,keep_enrollment_id 备份可回滚),有界重试; down 三级恢复守卫(archived + 花名册 active + 无 up 后新建报读), 未恢复行保留备份表供人工排查
报读增删改/归档都会经 syncRosterMembership 变更班级花名册,4 个 mutation 统一失效 ['archive', studentId] + queryKeys.classes.all(抽 ENROLLMENT_INVALIDATE 常量),避免班级详情/花名册读到陈旧数据;操作列拍平嵌套三元。
No dependencies set.
The note is not visible to the blocked user.
背景
承接 #83(班级花名册与档案报读双向同步)。对合并后的代码做多轮 OCR review + 修复循环(12 轮迭代,最后一轮本任务范围 0 评论),主要修复并发幂等/死锁/数据一致性问题。
改动
fix(server): 花名册报读同步并发幂等加固
ensureEnrollmentsForRoster:普通读 + 分块批量插入(50/块),批内撞 active 部分唯一索引时逐行回退(1062 跳过);missing去重并按 studentId 排序,避免多行 INSERT 行序不同互相等待死锁courseCategory仅映射 culture/professional/comprehensive(与前端 handleClassChange 一致,bootcamp/sprint 不再写入非法值);角色类型改用TeacherRoleTypearchive.addEnrollment/updateEnrollment保存撞唯一索引转 400(新增saveEnrollmentWithDupGuard),并补 2 个单测isDuplicateEntryError提取到common/db-errors.ts单一来源(queries re-export 保持兼容)classes.membership.spec(mock dataSource.transaction + manager,断言新返回结构);addStudents空输入早返回补enrollmentsCreated: 0feat(server): 报读表索引迁移与存量重复清洗
1786515492285-AddIndexToStudentEnrollments:(class_id, student_id)普通索引(NON_UNIQUE=1 守卫 + 1061/1091 并发幂等)1786517000000-AddActiveUniqueToStudentEnrollments:active 部分唯一函数索引(MySQL 8.0.13+ 版本守卫;仅 active 且 class_id 非空的行唯一,archived 多条/无班报读不受影响)keep_enrollment_id备份到student_enrollments_active_cleanup可回滚)→ 有界 3 次重试fix(admin): 报读操作统一失效档案与班级查询缓存
['archive', studentId] + queryKeys.classes.all(抽ENROLLMENT_INVALIDATE常量),避免班级花名册读到陈旧数据;操作列拍平嵌套三元验证