fix: OCR 审查驱动的全库修复(安全/正确性/部署/前端) #64

Merged
wangziqi merged 43 commits from feat/ocr-review-fixes into main 2026-08-09 13:32:19 +00:00
Owner

概述

由 OCR(open-codereview.ai,deepseek-v4-flash)对 gongxue-base 全库审查后驱动的修复,分 6 个批次提交。

批次

  1. fix(ops):部署/代理/迁移脚本加固(serve-proxy、migrate.sh、deploy.sh/CI、PM2、oxlint)
  2. fix(security):认证/越权/注入/上传/凭据加固(JWT、越权 IDOR、LIKE 转义、上传 XSS、集成密钥加密)
  3. fix(correctness):并发/事务/实体/时区/状态一致性(double-spend、幂等、校验矛盾、china-time)
  4. fix(admin):前端功能与安全修复(角色勾选、月视图新建、附件预览、考勤 late 等)
  5. docs(ocr):OCR 审查报告与修复记录
  6. fix(security):classrooms 补充

验证

  • server:typecheck / lint 0 error / jest 152 套件 1178 用例
  • admin:typecheck / vitest 175
  • 多轮 OCR 定向复查:high/critical 归零

备注

  • 生产部署需在 .env 配置 JWT_SECRET(必填)、AI_CONFIG_ENCRYPTION_KEY(用过集成配置时)
  • 实体改动(class_student 外键 CASCADE 等)需接库后生成迁移:npm run migration:generate -- FixEntityMappings
  • 存量集成配置可运行:npm run encrypt:integration-secrets(一次性回填)

Reviewed-by: OCR (open-codereview.ai)

## 概述 由 OCR(open-codereview.ai,deepseek-v4-flash)对 gongxue-base 全库审查后驱动的修复,分 6 个批次提交。 ## 批次 1. fix(ops):部署/代理/迁移脚本加固(serve-proxy、migrate.sh、deploy.sh/CI、PM2、oxlint) 2. fix(security):认证/越权/注入/上传/凭据加固(JWT、越权 IDOR、LIKE 转义、上传 XSS、集成密钥加密) 3. fix(correctness):并发/事务/实体/时区/状态一致性(double-spend、幂等、校验矛盾、china-time) 4. fix(admin):前端功能与安全修复(角色勾选、月视图新建、附件预览、考勤 late 等) 5. docs(ocr):OCR 审查报告与修复记录 6. fix(security):classrooms 补充 ## 验证 - server:typecheck ✅ / lint 0 error ✅ / jest 152 套件 1178 用例 ✅ - admin:typecheck ✅ / vitest 175 ✅ - 多轮 OCR 定向复查:high/critical 归零 ## 备注 - 生产部署需在 .env 配置 JWT_SECRET(必填)、AI_CONFIG_ENCRYPTION_KEY(用过集成配置时) - 实体改动(class_student 外键 CASCADE 等)需接库后生成迁移:npm run migration:generate -- FixEntityMappings - 存量集成配置可运行:npm run encrypt:integration-secrets(一次性回填) Reviewed-by: OCR (open-codereview.ai)
wangziqi added 43 commits 2026-08-09 13:32:06 +00:00
UX 缺陷修复:
- 校验失败不再卡死弹窗按钮(Users/Roles/Bills)
- 押金收取/批量收取/添加分期防重复提交;切换房型重置勾选
- AI 表单/批量确认不再出现"假成功"
- Dashboard 各数据模块独立加载,单接口失败不再整页清零
- 房间可视化加载失败显示错误态而非永久转圈
- 学生编辑表单回填前重置,避免字段残留污染
- 覆盖式导入增加二次确认;恢复默认考勤时段确认并同步表单
- 金数据匹配关闭前确认,同步中禁止误关

体验提升:
- 新增统一 QueryErrorState/QueryEmpty,20+ 页面加载失败显示错误态与重试
- 全局 ErrorBoundary + RouteKeeper 逐页兜底
- 新增 usePageVisible/useVisibleRefetch,保活页面切回自动刷新数据
- 新增首次登录角色引导 RoleTour 与业务闭环 NextStepHint 引导卡
- 重构 A2UI:useSubmissionState/useXCardSurface 收敛状态与命令生命周期,
  ArtifactErrorBoundary 渲染降级,图表空数据占位
- AI 助手欢迎语与建议话术按角色定制,会话列表空态引导
- 更新 a2ui-contract.md 契约文档说明实现现状
- 通知中心改为游标分页 + 加载更多,历史通知不再被 50 条上限截断;
  加载失败显示错误态与重试
- AI 流式请求 401 被登出时,登录页提示"登录已过期",不再无声踢出
- AI 附件/引用来源打开失败时给出明确错误提示,不再"点了没反应"
- 新增 useDownload hook:统一导出/下载的防重复、loading 与成功/失败反馈;
  接入学生/账单/房间/入住/费用五个页面的模板下载与导出按钮
- 学生页移除不检查响应状态的私有下载实现,统一走 downloadBlob
- 编辑旧消息会删除其后全部消息时,先弹确认告知影响范围
- 有待发送附件时切换会话,先确认再丢弃(避免静默删除已上传文件)
- EditableCell 保存成功后提供 6 秒内"撤销"入口(把旧值重新保存),
  重新编辑时自动清除
- 导入向导/重新上传显示上传进度条与百分比,不再"假死"
- AI 附件上传透传进度给附件列表
- 租赁合同上传显示按钮 loading 与上传百分比
- 移除教师端 hero 的英文眉标(TEACHING DAY),日期并入正文描述
- 删除不再使用的 attendance-eyebrow 样式
- Dashboard 的 ══ 装饰注释改为普通注释

kill-ai-slop 扫描: 18 处命中 → 7 处(剩余均为合理用法:
链接 hover 下划线、头像圆形角)
- RoleTour: buildSteps 拆分为各角色的声明式步骤常量,函数从 91 行降至组装逻辑
- ImportWizardModal: 抽取 uploadRun 共享 helper,消除 handleUpload/handleReupload
  之间重复的上传进度与 loading 状态处理

aislop scan --changes: 99/100 → 100/100 (5 引擎 0 issues)
- 新增 useDirtyGuard hook:关闭弹窗时若表单被修改,先确认再关闭
- 接入 17 个弹窗:账号/密码/档案、角色、教室、宿舍/床位/柜子、
  考勤机、机构、班级、教师、考试、排课、入住/退宿/换房、费用
  (子组件弹窗用 useEffect 在回填后快照,时序正确)
- 学生列表空状态带「添加学生」引导动作
- 排课创建成功提示「同步钉钉」,考试创建成功提示「去详情录成绩」

aislop scan: 5 引擎 0 issues
admin:
- 17 个列表页空态统一为 QueryEmpty 引导:学生/账单/入住/费用/押金/
  教室/房间/班级/考试/排课/机构/考勤机/钱包/租赁/通知/角色等,
  有创建权限的页面附带主操作按钮,无权限时纯展示
- 教师端课堂点名新增「全部已打卡/全部未打卡」批量按钮:
  仅作用于状态不一致的记录,确认后调用批量接口,展示成功/失败数量

server:
- 新增 PUT /attendance-records/batch-status 批量改状态接口
  (ids ≤200,逐条权限校验与会话锁,部分失败返回 failedIds,
  审计日志记录批量结果;路由声明在 :id 之前避免被捕获)

aislop scan: 5 引擎 0 issues
- 原始考勤明细表支持行选择(跨页保留),选中后出现
  「标记正常/标记请假/标记缺勤」批量操作条
- 批量提交前确认,成功后按 updated/failed 数量反馈,
  失败记录(如已结算)提示原因;筛选条件变化自动清空选择
- 复用 PUT /attendance-records/batch-status 接口

aislop scan: 5 引擎 0 issues
- 全模块类型化: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 用例全部通过
- 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
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 配置/既有文件规模)
- 新增 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 用例通过
- uuid v7 替换手写 RFC9562 实现(uuid@11.1.1 转为直接依赖)
- node:timers/promises setTimeout 替换两处手写 sleep(钉钉限流、AI 上游重试退避)
- dayjs 替换 4 处零散 Date 格式化(imports/expenses/occupancies 导入模板、档案报告日期)
- 替换散落的 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 个文件
generateFromSchedules / getCalendar 原来用本地周一零点转 UTC 日期,
在上海时区会得到前一天的日期,导致默认范围变成周日~周日(8 天)。
改为 dayjs().utcOffset(8) 按中国日期计算,与 getTodayDateOnly 语义一致。

新增测试锁定:默认周 = 本周一~本周日,周日晚上仍归本周,显式传参原样透传。
joinDate/leaveDate/checkInDate/sync 起始日等业务日期默认值原来用
dayjs().utc()(UTC 今天),在国内 00:00-07:59 会取到昨天。
统一改为 dayjs().utcOffset(8)(固定 UTC+8 中国日期),与考勤周边界
修复保持一致;pending-tasks/controller-base 的本地 dayjs() 也显式化。
此前仅'今天'默认值统一为中国日期,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 测试断言
同步更新为北京时间语义。
- 新增 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 可命中缓存
由 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)
由 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)
由 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)
由 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)
由 OCR(open-codereview.ai,deepseek-v4-flash)审查产出:
- 审查汇总.md / high-severity-comments.md / all-comments.jsonl / 修复记录.md 等

Reviewed-by: OCR (open-codereview.ai)
fix(security): classrooms 上传限制/路由顺序/LIKE 转义补充
Some checks failed
CI / check (pull_request) Failing after 3m26s
db11a57e98
由 OCR(open-codereview.ai,deepseek-v4-flash)审查驱动修复:
- classrooms.controller:report 路由移到 :id 之前、上传 10MB + 判空
- classrooms.service:搜索关键词 LIKE 通配符转义

Reviewed-by: OCR (open-codereview.ai)
wangziqi merged commit fb9927e712 into main 2026-08-09 13:32:19 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: wangziqi/gongxue-base#64
No description provided.