wangziqi
|
74fb09bdba
|
fix(admin): 报读操作统一失效档案与班级查询缓存
CI / check (pull_request) Waiting to run
Dependency Check / check (pull_request) Waiting to run
报读增删改/归档都会经 syncRosterMembership 变更班级花名册,4 个 mutation
统一失效 ['archive', studentId] + queryKeys.classes.all(抽 ENROLLMENT_INVALIDATE
常量),避免班级详情/花名册读到陈旧数据;操作列拍平嵌套三元。
|
2026-08-12 16:18:45 +08:00 |
|
wangziqi
|
0ea31b8bc3
|
feat(server): 报读表索引迁移与存量重复清洗
- 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 后新建报读),
未恢复行保留备份表供人工排查
|
2026-08-12 16:18:33 +08:00 |
|
wangziqi
|
37b1ba45e1
|
fix(server): 花名册报读同步并发幂等加固
- 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
|
2026-08-12 16:18:18 +08:00 |
|
wangziqi
|
bce076ddaf
|
feat: 班级花名册与档案报读双向同步 (#83)
|
2026-08-12 06:04:13 +00:00 |
|
wangziqi
|
dfc3ffb78c
|
refactor(admin): 清理 lint 警告(no-explicit-any / exhaustive-deps)
CI / check (pull_request) Successful in 3m36s
Dependency Check / check (pull_request) Successful in 2m37s
|
2026-08-12 11:04:06 +08:00 |
|
wangziqi
|
fee23219ce
|
fix(server): 清理 lint 未使用变量警告
|
2026-08-12 11:04:06 +08:00 |
|
wangziqi
|
6f805faf97
|
fix(server): 押金 decimal 列补 transformer,接口金额返回数字
CI / check (pull_request) Successful in 4m6s
Dependency Check / check (pull_request) Successful in 1m18s
|
2026-08-12 10:31:45 +08:00 |
|
wangziqi
|
68e5b9ac61
|
fix(admin): 修正考试列表 schema 校验(接口无 isArchived 字段,改用 status)
CI / check (pull_request) Successful in 4m5s
Dependency Check / check (pull_request) Successful in 2m47s
|
2026-08-12 10:25:15 +08:00 |
|
wangziqi
|
31d1fe908a
|
fix(admin): 修复工作台 KPI 卡片在窄屏下换行
CI / check (pull_request) Successful in 2m26s
Dependency Check / check (pull_request) Successful in 2m36s
|
2026-08-12 10:12:10 +08:00 |
|
wangziqi
|
b0e66f81ad
|
fix(server): 考勤趋势横坐标日期改为 YYYY-MM-DD 字符串返回
|
2026-08-12 10:12:04 +08:00 |
|
wangziqi
|
7987941a3c
|
docs: 补充仓库开发指南(AGENTS.md)
CI / check (pull_request) Successful in 2m59s
Dependency Check / check (pull_request) Successful in 1m21s
|
2026-08-12 09:36:10 +08:00 |
|
wangziqi
|
d9cbaf0eda
|
feat(admin): 用户菜单新增修改密码弹窗
|
2026-08-12 09:33:57 +08:00 |
|
wangziqi
|
a4210a5373
|
feat(server): 新增用户自助修改密码接口(免原密码校验)
|
2026-08-12 09:33:47 +08:00 |
|
wangziqi
|
1f0b377a7f
|
feat(admin): 入住记录新增编辑弹窗与操作列
CI / check (pull_request) Successful in 2m58s
Dependency Check / check (pull_request) Successful in 2m15s
|
2026-08-11 17:28:16 +08:00 |
|
wangziqi
|
353a6c72e1
|
feat(server): 入住记录新增编辑接口与权限迁移
|
2026-08-11 17:28:16 +08:00 |
|
wangziqi
|
f858d90b76
|
fix(admin): 修复排课周视图周区间错位一天(zh-cn 下 weekday(1) 是周二)
CI / check (pull_request) Successful in 5m16s
Dependency Check / check (pull_request) Successful in 2m48s
|
2026-08-11 16:36:01 +08:00 |
|
wangziqi
|
a0611fda25
|
ci(deploy): runs-on 对齐 runner 标签,部署目录改为生产实际路径
CI / check (pull_request) Successful in 5m8s
Dependency Check / check (pull_request) Successful in 3m37s
runner(nonlocal-runner,个人级)注册标签为 ubuntu-latest 等,原 self-hosted
标签不匹配导致 job 无 runner 可派发。部署目录默认值 /opt/gongxue 改为生产
实际路径 /www/wwwroot/jidi.gongxue100.com(仍可用仓库变量 REMOTE_DIR 覆盖)
|
2026-08-11 16:01:34 +08:00 |
|
wangziqi
|
30d289875b
|
refactor(server): 迁移改官方 migrationsRun 模式,移除自定义启动 runner
CI / check (pull_request) Successful in 3m41s
Dependency Check / check (pull_request) Successful in 2m22s
TypeOrmModule 连接后自动执行未跑迁移(glob 自动发现,ts-node/dist 均匹配),
删除 main.ts 启动钩子与独立 DataSource。部署脚本显式 migration:run 保留:
迁移失败在 PM2 重载前中止,不中断在跑服务;与 migrationsRun 幂等共存
|
2026-08-11 15:30:13 +08:00 |
|
wangziqi
|
d1c320b1f5
|
refactor(server): 移除 app.module 失效迁移清单
migrations 配置未设 migrationsRun 从不执行,手写 12 类清单已过期(缺 2 个迁移),
删除后迁移统一由 runner glob 自动发现与 datasource.ts CLI 管理
|
2026-08-11 15:18:36 +08:00 |
|
wangziqi
|
01ca0c6f4b
|
fix(server,admin): 成绩/班级均分/排名超列宽写入 MySQL 报 500
score/class_avg 为 decimal(5,2)(上限 999.99)、rank 为 int,越界值直写库抛
ER_WARN_DATA_OUT_OF_RANGE。DTO 加 @Max 与列宽对齐返回 400 清晰报错,
前端单元格与表单 InputNumber 同步加 max 阻止输入
|
2026-08-11 15:18:30 +08:00 |
|
wangziqi
|
0a685c8ea6
|
feat(admin): 报读记录班级选择、已报读禁用与班级跳转
- 班级名称列改为班级下拉(选项禁用已报读班级),保存时映射 className+classId 双字段
- 已有关联的班级名渲染为可点击链接跳转班级详情
|
2026-08-11 15:18:25 +08:00 |
|
wangziqi
|
3e3c05b8bc
|
fix(server): 档案子记录编辑修复 ER_BAD_FIELD_ERROR
findOne+select+eager join 时 TypeORM 0.3.x 生成缺 id 列的 distinctAlias 子查询
(Unknown column 'distinctAlias.xxx_id'),所有档案子记录编辑/删除均 500。
改回全列读取(单行主键查询成本可忽略)
|
2026-08-11 15:18:12 +08:00 |
|
wangziqi
|
2499f90231
|
feat(server): 报读记录关联班级 classId 与花名册单向同步
- student_enrollments 新增 class_id 列(迁移幂等),报读/换班/归档/删除时同步 class_student 成员状态
- 班级名以班级表为准快照,防止前后端漂移;班级不存在直接 400
- 迁移 runner 改 glob 自动发现(ts-node/dist 均可),新增迁移文件免手改清单
|
2026-08-11 15:17:58 +08:00 |
|
wangziqi
|
2e5e3fb7e5
|
fix(server): 钉钉导入配置错误改抛 BadRequestException,使 create() 补偿删除真正生效
CI / check (pull_request) Successful in 2m46s
Dependency Check / check (pull_request) Successful in 2m18s
|
2026-08-11 12:53:56 +08:00 |
|
wangziqi
|
2871407f76
|
fix(server): keepPlus 排除括号开头、钉钉导入补偿删除收窄并记录日志、rels 大小不可读保守拒绝
CI / check (pull_request) Successful in 4m40s
Dependency Check / check (pull_request) Successful in 3m17s
|
2026-08-11 12:50:59 +08:00 |
|
wangziqi
|
257c2065b8
|
fix(server): 迁移超长科目备份移到截断前、keepPlus/rels 解析加固、钉钉导入失败补偿
CI / check (pull_request) Successful in 4m26s
Dependency Check / check (pull_request) Successful in 2m48s
- 迁移:超长科目原值备份移到 step 2 拆分 UPDATE 之前(原位置截断后按长度查不到原值,备份无效)
- keepPlus:'+' 后紧跟公式触发符(- + = @ 等)不保留前缀,写回 Excel 不会被当公式
- workbook rels:逐元素解析任意属性顺序/引号,声明大小超限拒绝,防文件名绕过与内存放大
- create():钉钉导入失败时补偿删除刚建班级(成员外键 CASCADE),避免孤儿班级
|
2026-08-11 12:46:12 +08:00 |
|
wangziqi
|
66c039ee87
|
fix(admin): 排课提交保留 teacherId、同名新生导入提示、冲突检测排除 cancelled
CI / check (pull_request) Successful in 4m0s
Dependency Check / check (pull_request) Successful in 3m7s
- teacherId 用隐藏 Form.Item 注册:validateFields 返回、resetFields 清理,
修复提交丢失老师选择与新建弹窗残留上次编辑老师(重构引入的回归)
- 教师添加返回空数组(全部科目已存在)时提示「未新增」而非「已添加」
- 冲突检测排除所有非 active 排课(与后端 cancelled 等状态语义一致)
- 创建弹窗打开时复位教师加载 loading,防过期请求导致永久 spinner
|
2026-08-11 12:38:31 +08:00 |
|
wangziqi
|
122f3843a2
|
fix(server): 班级创建事务化、花名册同名新生兜底修正、迁移超长科目备份
- create() 班级/学生/教师写入整体包事务,避免教师失败留下孤儿班级
- 花名册提交:行内带标识的同名新生不再复用本批姓名兜底(避免被误判冲突丢弃,与预览一致)
- 迁移:超长科目截断前备份原值,且备份表确保存在(全新部署不因缺表启动失败)
- workbook rels 解析真实工作表路径,防自定义文件名绕过 sheet/行数预扫描
- keepPlus 完整正则对齐注释意图(+ 86 前缀保留);formatDate 用 UTC getter
- main.ts 大 JSON 路由前缀与限制提取为常量并注明耦合点
|
2026-08-11 12:38:24 +08:00 |
|
wangziqi
|
0b3edb88aa
|
fix(admin): 密码按字节数校验,对齐 bcrypt 72 字节上限
CI / check (pull_request) Successful in 2m51s
Dependency Check / check (pull_request) Successful in 1m49s
- 共享 PASSWORD_RULES:新增账号与重置密码弹窗统一
- 原 max:72 按字符数校验,多字节密码(如中文 3 字节)会通过前端校验却被服务端
@MaxByteLength(72) 拒绝;改为 TextEncoder 字节数校验
|
2026-08-11 11:54:14 +08:00 |
|
wangziqi
|
48d06b29c1
|
feat(admin): 班级花名册导入确认流程与教师候选人
- 提交体条件化:空数组不发送,修复全匹配/全新建两种场景被后端 @ArrayMinSize 拒 400
- 无论是否新建学生都先确认,避免已匹配学生被一键加入无提示
- 学员下拉保留学号/ID 兜底区分同名;科目去重键小写对齐后端唯一索引
- 任课老师空科目前端拦截;教师候选人相关调整
|
2026-08-11 11:54:09 +08:00 |
|
wangziqi
|
f19e72ca40
|
feat(admin): 排课冲突检测与教师选择状态收敛
- findTimeConflicts:半选值/无效日期防护、日期未选不误报、坏时间按必定冲突、10 分钟缓冲命名常量
- 教师下拉完全由表单驱动,移除冗余 teacherKey 状态(消除整类双源 desync 问题)
- 教师加载加请求序号防过期覆盖 + loading 禁用下拉 + 失败提示不清空表单
- 编辑模式 teacherKey 按实际选项推导,fallback 优先任课老师行并同步科目
- 切班/切老师自动带出:仅真匹配时自动选中,显式科目无人教授时清空防错配
- 选项按复合 key 去重,冲突提示标注基于当前视图
|
2026-08-11 11:53:58 +08:00 |
|
wangziqi
|
5f03f7a164
|
feat(server): class_teacher 科目列迁移——一行一科目 + 4 列唯一索引
- 迁移确保 subject NOT NULL DEFAULT '' 并建 (class_id,user_id,role_type,subject) 唯一索引
- 历史多科目值按内容拆分(不依赖列类型),首科目截断与 TS 侧 trim 归一化一致
- 破坏性清理前备份到审计表;暂存表自包含身份列,恢复不依赖原行存活
- 4 列兜底去重防 CREATE UNIQUE INDEX 撞 ER_DUP_ENTRY;GET_LOCK 串行化多实例迁移
- 收敛后启动为纯 no-op,滚动部署不再反复清理
- init.sql 与实体对齐:subject NOT NULL DEFAULT '',唯一索引显式命名
|
2026-08-11 11:53:47 +08:00 |
|
wangziqi
|
bfc8175277
|
fix(server): 花名册预览与提交匹配归一化一致,并发导入计数修正
- 预览二次校验改 LOWER(TRIM) 归一化,与主查询/commit 路径一致,避免预览判可新建而提交判冲突
- 并发创建同标识学生重查改用锁定读刷新可见性(REPEATABLE READ 旧快照问题)
- 批量成员保存撞唯一索引后的恢复:复活行不计入并发跳过,仅新增行且已 active 才算已在本班
- 重试保存逐行执行,避免多行 INSERT 原子失败静默丢行
|
2026-08-11 11:53:40 +08:00 |
|
wangziqi
|
545d087b3d
|
feat(server): 教师分配 DTO 校验与实体对齐,重复分配由唯一索引兜底
- 科目长度上限收敛到 dto MAX_SUBJECT_LENGTH(30),subjects/subject 加 @MaxLength
- ValidateSubjectTeacherHasSubject 对非字符串元素做类型守卫,避免 500
- normalizeTeacherSubjects 去重键小写对齐 MySQL 大小写不敏感 collation
- 任课老师逐行保存并包事务:并发 1062 跳过、非重复错误整体回滚
- 非科目角色去掉无事务悲观锁,改查重 + save 捕获 1062 转 400
- 实体 subject 列对齐迁移后 NOT NULL DEFAULT '',唯一索引显式命名
- 提交 DTO 跨字段校验:合计行数不超过文件解析上限,id 需为正整数
|
2026-08-11 11:52:09 +08:00 |
|
wangziqi
|
386c8dd5b3
|
fix(server): 班级花名册导入安全加固(zip 炸弹防护/公式注入/解析错误透出)
- assertRosterZipSafe 流式解压限流:单条目 8MB、总量 32MB 硬上限,超限即时中止
- 解压流超时兜底,防畸形 zip 挂起请求
- 工作表数量与行数在 ExcelJS 整体加载前流式预扫描拦截
- sanitizeCellText 补空格/换行公式触发符,keepPlus 支持括号且至少含一位数字
- 解析错误改 RosterParseError(400) 透出可操作提示,不再被 500 吞掉
- main.ts 仅对 /api/classes 路由放宽 JSON body 上限至 2mb
|
2026-08-11 11:51:58 +08:00 |
|
wangziqi
|
6f421c7bd6
|
feat(admin): 附件上传改为拖拽并增加大小/格式提示
|
2026-08-10 17:29:48 +08:00 |
|
wangziqi
|
7df28f46e0
|
feat(admin): 学生基本信息枚举化并按区块分组展示
|
2026-08-10 17:29:48 +08:00 |
|
wangziqi
|
de5843475f
|
feat(admin): 课堂回访默认当天日期、跟进方式改为下拉选择
|
2026-08-10 17:29:48 +08:00 |
|
wangziqi
|
98768341e0
|
feat(admin): 考试成绩科目联想、单报读自动关联与考试名称自动生成
|
2026-08-10 17:29:48 +08:00 |
|
wangziqi
|
b946713b61
|
feat(admin): 报读班型支持班级选择、自动带出与已报读识别
|
2026-08-10 17:29:48 +08:00 |
|
wangziqi
|
d7d1bfaab1
|
feat(admin): 学生档案共享枚举常量(科目/跟进方式/性别/年级/选科方向)及单测
|
2026-08-10 17:29:47 +08:00 |
|
wangziqi
|
e092ede8d8
|
Merge pull request 'fix: 修复 CI 时区测试失败 + 依赖安全升级 + 新增依赖检查 workflow' (#68) from fix/ci-deps-security into main
Dependency Check / check (push) Successful in 48s
|
2026-08-10 02:13:41 +00:00 |
|
wangziqi
|
c6e1d94510
|
fix: 修复 CI 时区测试失败 + 依赖安全升级 + 新增依赖检查 workflow
CI / check (pull_request) Successful in 4m11s
Dependency Check / check (pull_request) Successful in 2m30s
- fix(server): getCourseClock 固定 UTC+8(Asia/Shanghai),与 attendance-settlement 保持一致,
修复 CI(UTC 容器)下 attendance.lesson-session.spec.ts 失败
- chore(deps): @ant-design/x 2.9.0 / @ant-design/x-markdown 2.9.0 / mermaid 11.16.1;
直接依赖 dompurify 3.4.13(修复生产依赖 audit 漏洞,mermaid/x-markdown 复用同一份)
- ci: 新增 dependency-check workflow(生产依赖 moderate+ / 全局 critical 失败,outdated 报告)
|
2026-08-10 10:05:05 +08:00 |
|
wangziqi
|
8c437cd082
|
Merge pull request 'fix: 迁移读取根 .env + 部署脚本本地直跑 + 健康检查读 PORT' (#66) from fix/datasource-root-env into main
|
2026-08-10 01:45:19 +00:00 |
|
wangziqi
|
4c95de2e56
|
Merge pull request 'fix(admin): 修复首次进入系统后点击任意导航被拉回 dashboard' (#67) from fix/route-redirect-keepalive into main
|
2026-08-10 01:44:02 +00:00 |
|
wangziqi
|
ce9bde35eb
|
fix(admin): 修复首次进入系统后点击任意导航被拉回 dashboard
CI / check (pull_request) Failing after 2m14s
根因:RouteKeeper 保活缓存了首页 index 节点,DefaultRoute 中的声明式
<Navigate> 会随缓存节点在后续路由变化时反复触发 replace,导致所有导航
被强制拉回落地页(dashboard),刷新后因不再经过 / 而恢复正常。
修复:DefaultRoute 改为 effect 导航,仅在确实处于 / 且已算出落地页时
跳转一次;navigate 通过 ref 持有避免 effect 空转。新增集成测试覆盖
「首次进入 / → 跳转一次 → 后续导航不被劫持 → 回到 / 仍能重定向」。
|
2026-08-10 09:42:46 +08:00 |
|
wangziqi
|
9d74bd2d51
|
ci(deploy): deploy.sh 支持本地直跑模式 + workflow 健康检查读取 .env PORT
CI / check (pull_request) Failing after 5m3s
- deploy.sh 默认本地直跑(./deploy.sh 或 ./deploy.sh local),不再需要 SSH;传 ssh_host 才走远程版
- workflow 健康检查从 .env 读取 PORT(默认 3000),适配 PORT=3002 等自定义端口
|
2026-08-09 22:04:21 +08:00 |
|
wangziqi
|
f4072ce93a
|
fix(db): 迁移 CLI 默认读取仓库根目录 .env
- datasource.ts 优先加载根目录 .env,再回退 apps/server/.env
- 生产只需维护根目录一份 .env,迁移脚本不再报 root 空密码
|
2026-08-09 22:02:00 +08:00 |
|
wangziqi
|
29d987ab74
|
Merge pull request 'ci: 部署工作流改为本地 runner 直跑版(免 SSH/Secrets)' (#65) from ci/local-runner-deploy into main
|
2026-08-09 13:42:36 +00:00 |
|
wangziqi
|
f0321b5bdd
|
ci(deploy): 部署工作流改为本地 runner 直跑版(免 SSH/Secrets)
CI / check (pull_request) Failing after 3m9s
- 移除 SSH/rsync 步骤与 SSH 相关 Secrets 依赖,runs-on: self-hosted 本机直跑
- 步骤:git pull → npm ci → build → migration:run → pm2 startOrReload --update-env → 健康检查
- 新增可选输入 seed_roles:首次部署/新增权限码时强制播种 RBAC,跑完自动去掉
- .env 无需配置到 Gitea,仅放服务器部署目录
- 注释说明 runner 在 docker 容器时的挂载与宿主 pm2 要求
|
2026-08-09 21:41:48 +08:00 |
|
wangziqi
|
fb9927e712
|
Merge pull request 'fix: OCR 审查驱动的全库修复(安全/正确性/部署/前端)' (#64) from feat/ocr-review-fixes into main
|
2026-08-09 13:32:19 +00:00 |
|
wangziqi
|
db11a57e98
|
fix(security): classrooms 上传限制/路由顺序/LIKE 转义补充
CI / check (pull_request) Failing after 3m26s
由 OCR(open-codereview.ai,deepseek-v4-flash)审查驱动修复:
- classrooms.controller:report 路由移到 :id 之前、上传 10MB + 判空
- classrooms.service:搜索关键词 LIKE 通配符转义
Reviewed-by: OCR (open-codereview.ai)
|
2026-08-09 21:30:42 +08:00 |
|
wangziqi
|
1289aea7ce
|
docs(ocr): 全库 OCR 审查与修复记录
由 OCR(open-codereview.ai,deepseek-v4-flash)审查产出:
- 审查汇总.md / high-severity-comments.md / all-comments.jsonl / 修复记录.md 等
Reviewed-by: OCR (open-codereview.ai)
|
2026-08-09 21:29:54 +08:00 |
|
wangziqi
|
a32c0a0731
|
fix(admin): 前端功能与安全修复(角色勾选/月视图新建/附件预览/考勤 late 等)
由 OCR(open-codereview.ai,deepseek-v4-flash)审查驱动修复:
- Roles 多组勾选、Schedules 月视图新建、useDirtyGuard 稳定引用
- JinshujuMatchModal success:false、Attendance store 订阅、EditableCell Enter、DynamicChart 空数据、渲染期 ref
- AttachmentsTab 附件预览不再 window.open(防存储型 XSS)+ 请求乱序防护
- 考勤 late 计入出勤率与主状态
Reviewed-by: OCR (open-codereview.ai)
|
2026-08-09 21:29:54 +08:00 |
|
wangziqi
|
f50301148d
|
fix(correctness): 并发/事务/实体/时区/状态一致性修复
由 OCR(open-codereview.ai,deepseek-v4-flash)审查驱动修复:
- wallets 原子扣款防 double-spend;refund 条件更新幂等;findTransactions 分页
- financial/imports/occupancies/attendance 事务与 advisory lock;重复生成/提交幂等
- 矛盾校验器、日期区间、实体双映射/DECIMAL/nullable、时区统一(china-time)
- rbac-seed 防重激活、exam 权限恢复、状态一致性、路由顺序、N+1/IN 分块等性能项
Reviewed-by: OCR (open-codereview.ai)
|
2026-08-09 21:29:54 +08:00 |
|
wangziqi
|
99ea931409
|
fix(security): 认证/越权/注入/上传/凭据全链路加固
由 OCR(open-codereview.ai,deepseek-v4-flash)审查驱动修复:
- JWT 生产必填、密码 8-72 字节、防枚举;全局 ValidationPipe
- classes/dashboard/schedules/students/attendance/archive/exams 越权与 IDOR 修复
- LIKE 通配符转义(14 处);上传 10MB 上限 + MIME 白名单 + 附件 XSS
- 集成配置 appSecret AES 加密 + 回填脚本;审计 best-effort;IP 来源防伪造
Reviewed-by: OCR (open-codereview.ai)
|
2026-08-09 21:29:22 +08:00 |
|
wangziqi
|
3bcad138a1
|
fix(ops): 加固部署/代理/迁移脚本,生产环境安全默认
由 OCR(open-codereview.ai,deepseek-v4-flash)审查驱动修复:
- serve-proxy:API_TARGET 生效、SPA 404 语义、流式静态文件、hop-by-hop/超时/断连/穿越防护
- migrate.sh:密码不进 argv/不泄漏 pm2、原子锁防重入、DML-only 事务说明、就绪诊断
- deploy.sh/CI:保护 .env、总是 npm ci(迁移依赖 devDeps)、健康检查、并发锁
- ecosystem/oxlint/.env.example:优雅停机、React 版本对齐、TRUST_PROXY 说明
Reviewed-by: OCR (open-codereview.ai)
|
2026-08-09 21:29:22 +08:00 |
|
wangziqi
|
9565a0f23c
|
feat(admin): 深化 TanStack Query 数据层
- 新增 queryKeys.ts 统一 QueryKey 工厂(覆盖全部模块,invalidate/refetch/prefetch 同源)
- 新增 queryClient.ts 全局配置:retry 1、staleTime 30s、gcTime 5min、refetchOnWindowFocus false
- 新增 useApiQuery:zod schema 校验 + 类型收敛 + select 转换,消除页面重复 validateResponse 样板
- useApiMutation 支持 onMutate/onSettled/context(乐观更新)
- 示范迁移:Organizations(useApiQuery + 乐观更新归档/恢复)、Students(useApiQuery + queryKeys + 打开抽屉前 prefetch 档案聚合)
- StudentProfileContent queryKey 统一为 queryKeys.archive.detail,prefetch 可命中缓存
|
2026-08-08 18:37:55 +08:00 |
|
wangziqi
|
4dd7b5a0b1
|
fix(server): 时间戳→业务日期/时间全部统一为中国时区(UTC+8)
此前仅'今天'默认值统一为中国日期,Date→字符串的转换仍是 UTC:
- sync 考勤同步窗口 lastSyncAt 时间戳按 UTC 取日期,凌晨会偏一天
- 考勤导出 punchTime/createdAt 显示 UTC 时间
- normalizeDateOnly 把 ISO datetime 按 UTC 归一化,业务日期少一天
- getCourseClock/getChinaDateParts 用 dayjs.utc(date).add(8h) 隐式转换
统一为 dayjs(date).utcOffset(8),纯日期字符串运算(shiftDate/addDays/
daysInMonth/nextMonth)保留 dayjs.utc;date-normalization 测试断言
同步更新为北京时间语义。
|
2026-08-08 18:01:35 +08:00 |
|
wangziqi
|
7905f1ee23
|
fix(server): 业务日期'今天'统一为中国日期,避免凌晨 UTC 偏一天
joinDate/leaveDate/checkInDate/sync 起始日等业务日期默认值原来用
dayjs().utc()(UTC 今天),在国内 00:00-07:59 会取到昨天。
统一改为 dayjs().utcOffset(8)(固定 UTC+8 中国日期),与考勤周边界
修复保持一致;pending-tasks/controller-base 的本地 dayjs() 也显式化。
|
2026-08-08 17:58:16 +08:00 |
|
wangziqi
|
782b43ef0a
|
fix(attendance): 默认周范围改为中国日期周一起点
generateFromSchedules / getCalendar 原来用本地周一零点转 UTC 日期,
在上海时区会得到前一天的日期,导致默认范围变成周日~周日(8 天)。
改为 dayjs().utcOffset(8) 按中国日期计算,与 getTodayDateOnly 语义一致。
新增测试锁定:默认周 = 本周一~本周日,周日晚上仍归本周,显式传参原样透传。
|
2026-08-08 17:56:00 +08:00 |
|
wangziqi
|
8936453e84
|
refactor(server): 日期/月份格式化统一改用 dayjs
- 替换散落的 toISOString().slice(0,10) / split('T')[0] / replace('T',' ')(UTC 语义用 dayjs(...).utc() 保持完全一致)
- Asia/Shanghai 固定时区日期(Intl.DateTimeFormat en-CA)改用 dayjs().utcOffset(8)
- 月份边界 first/last、daysInMonth、nextMonth、shiftDate/addDays 等改 dayjs 简洁实现
- 涉及 attendance/classes/classrooms/room/dashboard/bills/expenses/occupancies/sync/ai-chat/rental 等 28 个文件
|
2026-08-08 17:52:32 +08:00 |
|
wangziqi
|
92ba2e779f
|
refactor(server): 用已装库替换手写工具
- uuid v7 替换手写 RFC9562 实现(uuid@11.1.1 转为直接依赖)
- node:timers/promises setTimeout 替换两处手写 sleep(钉钉限流、AI 上游重试退避)
- dayjs 替换 4 处零散 Date 格式化(imports/expenses/occupancies 导入模板、档案报告日期)
|
2026-08-08 17:46:31 +08:00 |
|
wangziqi
|
8ddc3ea690
|
refactor(admin): LiteMermaid 用 usehooks-ts useIsMounted 替代手写 cancelled 标志
|
2026-08-08 17:27:14 +08:00 |
|
wangziqi
|
d324b2fb0a
|
refactor(server): 日期工具统一迁入 dayjs(utc 插件),移除手写 Intl/padStart 重复实现
|
2026-08-08 17:27:14 +08:00 |
|
wangziqi
|
74d5b90faa
|
refactor(admin): 用 usehooks-ts useEventListener/useTimeout 与 dayjs 替代手写监听、撤销计时与相对时间
|
2026-08-08 17:21:33 +08:00 |
|
wangziqi
|
dab28f85e8
|
refactor(admin): 用已安装的 file-saver/fast-deep-equal 替代手写下载与 JSON 比较
|
2026-08-08 17:11:10 +08:00 |
|
wangziqi
|
01cceea237
|
refactor(admin): 钉钉集成配置缓存迁入 zustand 页面级 store,移除模块级单例
|
2026-08-08 17:07:13 +08:00 |
|
wangziqi
|
9cc9ed09df
|
refactor(admin): EditableCell 跨单元格协调状态迁入 zustand,替代模块级单例并补单测
|
2026-08-08 17:04:42 +08:00 |
|
wangziqi
|
260a7517d2
|
feat(admin): RouteDock 页签增强——pathname 唯一 key、20 上限 LRU、关闭其他/左/右/全部、登出清理、持久化校验收紧
|
2026-08-08 17:00:49 +08:00 |
|
wangziqi
|
d27f10eb84
|
chore: admin 构建稳定 vendor 分包,消除 chunk 告警;更新 .env.example 品牌文案与端口
|
2026-08-08 16:46:16 +08:00 |
|
wangziqi
|
00cdb322d2
|
fix: 修复 QA 巡检发现的 console 告警——useForm 未连接、RangePicker 空值禁用、dashboard 占用率类型
|
2026-08-08 16:36:58 +08:00 |
|
wangziqi
|
6c11fd7e16
|
refactor(server): 待办统计日期懒计算,aislop 豁免注释与文档纠错
|
2026-08-08 16:03:05 +08:00 |
|
wangziqi
|
e50f660811
|
chore: 移除 aislop 质量门禁工作流
|
2026-08-08 15:29:13 +08:00 |
|
wangziqi
|
bb8228bd0f
|
docs: 同步 A2UI 契约与 AI 工作流文档至统一 artifact/向导流程
|
2026-08-08 15:06:03 +08:00 |
|
wangziqi
|
f03f22c605
|
docs: 更新项目结构与部署说明(apps/* 布局、monorepo 命令)
|
2026-08-08 15:06:03 +08:00 |
|
wangziqi
|
b73ac5e3e4
|
ci: 新增 aislop 质量门禁工作流
|
2026-08-08 15:06:03 +08:00 |
|
wangziqi
|
f160256865
|
chore(server): 移除 e2e 测试配置与 supertest 依赖
|
2026-08-08 15:05:52 +08:00 |
|
wangziqi
|
f96c1c26c3
|
fix(server): 考勤自动结算支持空班级直接完成,补充自动匹配测试
|
2026-08-08 15:05:42 +08:00 |
|
wangziqi
|
a0829f17ce
|
feat(admin): 管理端 UI/UX 深度优化——错误/加载/空状态、表单快捷键、移动端适配与无障碍
|
2026-08-08 15:05:33 +08:00 |
|
wangziqi
|
57639a3869
|
refactor(admin): AI 对话收敛为统一 ui.artifact 协议,移除 legacy 事件处理
|
2026-08-08 15:05:20 +08:00 |
|
wangziqi
|
b37ae9471e
|
refactor(server): A2UI 收敛——移除 legacy SSE 双发与 render_review 工具,提取公共校验
|
2026-08-08 15:05:10 +08:00 |
|
wangziqi
|
1e3d6ee20f
|
refactor(server): 收敛类型边界工具,aislop AI Slop 10 → 1
- 新增 common/buffer.ts bufferToArrayBuffer:9 处
'as unknown as ArrayBuffer' 收敛为精确切片(含 byteOffset),
消除潜在 Buffer 池偏移隐患,类型断言集中到单一实现
- 新增 common/stringify.ts:4 处重复的 stringify 助手收敛为共享实现
- aislop: AI Slop 10→1(仅剩 1 处有理由的 stringify 薄包装,
eslint no-base-to-string 绕过所需);Code Quality 剩余
4 重复块(声明式 SQL 配置)+ 2 文件过大(既有规模)均保留
- 测试 142 套件/1065 用例通过
|
2026-08-08 09:48:03 +08:00 |
|
wangziqi
|
4b60e0c018
|
fix: code-review 审查问题修复 + A2UI 测试补齐
Standards 轴:
- 移除 uiArtifacts.ts 的 payloadOf 死代码残留
- AttendanceDevices 残留 any 类型化(补 ClassroomOption.status 字段)
- 批量考勤纠错区分业务失败(已结算/无权限)与系统错误,
前端提示精确到两类数量
- Dashboard queryFn 六段重复校验块收敛为 safeValidate 助手
Spec 轴:
- 补齐阶段 3.4 A2UI 测试:图表空数据占位、ArtifactErrorBoundary
降级隔离、useSubmissionState/useXCardSurface 单测(7 用例)
- 阶段 2.2 补两处引导:教师工作台区分「今日无课」与「未分配班级」、
班级花名册空态带「添加学员」动作
- 契约文档修正 DynamicReview 状态管理描述(多提交点如实说明)
aislop 剩余 16 警告均为必要豁免(类型边界/声明式 SQL 配置/既有文件规模)
|
2026-08-08 09:43:48 +08:00 |
|
wangziqi
|
bcd2d1a559
|
refactor(admin): A2UI 双轨收敛,统一 artifact 协议
- mergeArtifactIntoMessage 不再派发 legacy 列表,只维护 uiArtifacts
- 渲染层从 uiArtifacts 派生 forms/reviews/charts,为空时回退 legacy
(历史消息兼容,老 metadata 仅 a2uiForm/a2uiReview/a2uiChart)
- sseReducer 删除 ui.form/ui.review/ui.chart 旧事件分支,只消费
ui.artifact(后端过渡期双发,旧事件将被忽略)
- types.ts legacy 字段标注 deprecated;契约文档同步现状
- 测试更新:旧事件忽略 + uiArtifacts 合并/恢复断言
aislop scan: 5 引擎 0 issues
|
2026-08-08 09:33:53 +08:00 |
|
wangziqi
|
a644a8de42
|
refactor(server): 清理全量 any 类型安全警告 (692 → 0)
- 全模块类型化:controller 的 req: any → AuthenticatedRequest/RequestUser,
聚合查询 getRawMany 泛型标注、导入行/响应体定义具体 interface、
catch (e: any) → unknown + 收窄、no-base-to-string 用 String() 显式转换
- 第三方无类型库边界(pdfkit/exceljs)文件级或单行 disable 并注明理由
- 顺带修复:get-business-context.tool 两个 require-await error、
bills.controller 参数顺序隐患、main.ts compression 调用
- 运行时逻辑零改动;测试 142 套件 / 1065 用例全部通过
|
2026-08-08 09:28:23 +08:00 |
|
wangziqi
|
8b5fa98028
|
feat(admin): 管理端考勤明细批量纠错
- 原始考勤明细表支持行选择(跨页保留),选中后出现
「标记正常/标记请假/标记缺勤」批量操作条
- 批量提交前确认,成功后按 updated/failed 数量反馈,
失败记录(如已结算)提示原因;筛选条件变化自动清空选择
- 复用 PUT /attendance-records/batch-status 接口
aislop scan: 5 引擎 0 issues
|
2026-08-07 18:03:22 +08:00 |
|
wangziqi
|
1b4ba893fd
|
feat: 空状态引导全面应用与考勤批量标记
admin:
- 17 个列表页空态统一为 QueryEmpty 引导:学生/账单/入住/费用/押金/
教室/房间/班级/考试/排课/机构/考勤机/钱包/租赁/通知/角色等,
有创建权限的页面附带主操作按钮,无权限时纯展示
- 教师端课堂点名新增「全部已打卡/全部未打卡」批量按钮:
仅作用于状态不一致的记录,确认后调用批量接口,展示成功/失败数量
server:
- 新增 PUT /attendance-records/batch-status 批量改状态接口
(ids ≤200,逐条权限校验与会话锁,部分失败返回 failedIds,
审计日志记录批量结果;路由声明在 :id 之前避免被捕获)
aislop scan: 5 引擎 0 issues
|
2026-08-07 18:00:29 +08:00 |
|
wangziqi
|
c10476d203
|
feat(admin): 弹窗未保存内容保护与空状态引导扩展
- 新增 useDirtyGuard hook:关闭弹窗时若表单被修改,先确认再关闭
- 接入 17 个弹窗:账号/密码/档案、角色、教室、宿舍/床位/柜子、
考勤机、机构、班级、教师、考试、排课、入住/退宿/换房、费用
(子组件弹窗用 useEffect 在回填后快照,时序正确)
- 学生列表空状态带「添加学生」引导动作
- 排课创建成功提示「同步钉钉」,考试创建成功提示「去详情录成绩」
aislop scan: 5 引擎 0 issues
|
2026-08-07 17:56:13 +08:00 |
|
wangziqi
|
bcbf3c8b2e
|
refactor(admin): 按 aislop 质量门禁修复两个代码质量警告
- RoleTour: buildSteps 拆分为各角色的声明式步骤常量,函数从 91 行降至组装逻辑
- ImportWizardModal: 抽取 uploadRun 共享 helper,消除 handleUpload/handleReupload
之间重复的上传进度与 loading 状态处理
aislop scan --changes: 99/100 → 100/100 (5 引擎 0 issues)
|
2026-08-07 17:43:00 +08:00 |
|
wangziqi
|
ff3b6cdfde
|
style(admin): 清理 AI slop 痕迹
- 移除教师端 hero 的英文眉标(TEACHING DAY),日期并入正文描述
- 删除不再使用的 attendance-eyebrow 样式
- Dashboard 的 ══ 装饰注释改为普通注释
kill-ai-slop 扫描: 18 处命中 → 7 处(剩余均为合理用法:
链接 hover 下划线、头像圆形角)
|
2026-08-07 17:41:01 +08:00 |
|
wangziqi
|
8dc72d6e1b
|
fix(admin): 编辑消息/切换会话保护、行内编辑撤销与上传进度反馈
- 编辑旧消息会删除其后全部消息时,先弹确认告知影响范围
- 有待发送附件时切换会话,先确认再丢弃(避免静默删除已上传文件)
- EditableCell 保存成功后提供 6 秒内"撤销"入口(把旧值重新保存),
重新编辑时自动清除
- 导入向导/重新上传显示上传进度条与百分比,不再"假死"
- AI 附件上传透传进度给附件列表
- 租赁合同上传显示按钮 loading 与上传百分比
|
2026-08-07 17:38:21 +08:00 |
|
wangziqi
|
00e2bc5acf
|
fix(admin): 通知分页、登录过期提示、附件打开反馈与导出统一 loading
- 通知中心改为游标分页 + 加载更多,历史通知不再被 50 条上限截断;
加载失败显示错误态与重试
- AI 流式请求 401 被登出时,登录页提示"登录已过期",不再无声踢出
- AI 附件/引用来源打开失败时给出明确错误提示,不再"点了没反应"
- 新增 useDownload hook:统一导出/下载的防重复、loading 与成功/失败反馈;
接入学生/账单/房间/入住/费用五个页面的模板下载与导出按钮
- 学生页移除不检查响应状态的私有下载实现,统一走 downloadBlob
|
2026-08-07 17:33:58 +08:00 |
|
wangziqi
|
67435e46ca
|
feat(admin): 用户体验体系化提升与高危缺陷修复
UX 缺陷修复:
- 校验失败不再卡死弹窗按钮(Users/Roles/Bills)
- 押金收取/批量收取/添加分期防重复提交;切换房型重置勾选
- AI 表单/批量确认不再出现"假成功"
- Dashboard 各数据模块独立加载,单接口失败不再整页清零
- 房间可视化加载失败显示错误态而非永久转圈
- 学生编辑表单回填前重置,避免字段残留污染
- 覆盖式导入增加二次确认;恢复默认考勤时段确认并同步表单
- 金数据匹配关闭前确认,同步中禁止误关
体验提升:
- 新增统一 QueryErrorState/QueryEmpty,20+ 页面加载失败显示错误态与重试
- 全局 ErrorBoundary + RouteKeeper 逐页兜底
- 新增 usePageVisible/useVisibleRefetch,保活页面切回自动刷新数据
- 新增首次登录角色引导 RoleTour 与业务闭环 NextStepHint 引导卡
- 重构 A2UI:useSubmissionState/useXCardSurface 收敛状态与命令生命周期,
ArtifactErrorBoundary 渲染降级,图表空数据占位
- AI 助手欢迎语与建议话术按角色定制,会话列表空态引导
- 更新 a2ui-contract.md 契约文档说明实现现状
|
2026-08-07 17:23:23 +08:00 |
|
wangziqi
|
db0d972633
|
Merge pull request 'fix: 上传与路由校验加固、甘特图时间线修复' (#63) from fix/quick-wins-and-gantt into main
|
2026-08-07 08:38:45 +00:00 |
|
wangziqi
|
8d4ebcf9c0
|
fix(admin,server): 修复 Dashboard 甘特图时间线与数据校验
CI / check (pull_request) Failing after 1m54s
|
2026-08-07 16:38:16 +08:00 |
|
wangziqi
|
5f9566e26d
|
fix(server): 修正档案成绩趋势图 y 轴坐标字符串拼接
|
2026-08-07 16:38:09 +08:00 |
|
wangziqi
|
5b5ffb5b9e
|
fix(server): 路由 id 使用 ParseIntPipe,限制分页与批量数组上限
|
2026-08-07 16:38:03 +08:00 |
|
wangziqi
|
899d2dde5b
|
fix(admin,server): 上传改用 file.buffer 并移除手写 multipart 头
|
2026-08-07 16:37:55 +08:00 |
|
wangziqi
|
54b002455f
|
chore(admin): 声明 react-syntax-highlighter 运行时依赖
|
2026-08-07 16:37:47 +08:00 |
|