Commit Graph

65 Commits

Author SHA1 Message Date
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
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
782b43ef0a fix(attendance): 默认周范围改为中国日期周一起点
generateFromSchedules / getCalendar 原来用本地周一零点转 UTC 日期,
在上海时区会得到前一天的日期,导致默认范围变成周日~周日(8 天)。
改为 dayjs().utcOffset(8) 按中国日期计算,与 getTodayDateOnly 语义一致。

新增测试锁定:默认周 = 本周一~本周日,周日晚上仍归本周,显式传参原样透传。
2026-08-08 17:56:00 +08:00
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
d324b2fb0a refactor(server): 日期工具统一迁入 dayjs(utc 插件),移除手写 Intl/padStart 重复实现 2026-08-08 17:27:14 +08:00
f96c1c26c3 fix(server): 考勤自动结算支持空班级直接完成,补充自动匹配测试 2026-08-08 15:05:42 +08:00
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
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
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
5b5ffb5b9e fix(server): 路由 id 使用 ParseIntPipe,限制分页与批量数组上限 2026-08-07 16:38:03 +08:00
d1c933f032 refactor(server): 清理无用导入与类型转换
- 移除测试/控制器中未使用的导入与实例
- bills/classroom-rentals 改用 ESM 导入替代 require
- bills.service 去掉多余断言、backfill 显式转字符串、main.ts void bootstrap
2026-08-05 21:14:17 +08:00
b882411f42 refactor: 拆分考勤服务文件并通过 aislop 全项目扫描
Some checks failed
CI / check (pull_request) Failing after 5m24s
2026-08-05 18:55:36 +08:00
6ba7f4e3d0 refactor: 抽取课程考勤记录构建 helper,消除重复代码 2026-08-05 18:51:06 +08:00
c8316e9e8e feat: 结算时同步钉钉已审批请假并标记为请假 2026-08-05 18:43:02 +08:00
1e1e24f0f0 fix: 修复考勤控制器 DI 解析与班级日期迁移空 SQL 2026-08-05 17:46:11 +08:00
47720a8fcc refactor: 移除 SQLite 支持,仅保留 MySQL 2026-08-05 17:43:13 +08:00
e9c8a1085d feat: 考勤模块重构与钉钉考勤同步 2026-08-05 17:11:23 +08:00
6f29220257 test: 跳过外部考勤工作流集成测试
All checks were successful
CI / check (pull_request) Successful in 4m0s
2026-07-23 14:35:38 +08:00
9f54fec972 Merge remote-tracking branch 'origin/main'
Some checks failed
CI / check (pull_request) Failing after 4m23s
2026-07-23 14:25:15 +08:00
f3b59935d6 feat: 集成 AI 对话与只读查询工具 2026-07-23 14:24:40 +08:00
0bdc8a067b fix: satisfy attendance workflow lint
Some checks failed
CI / check (pull_request) Failing after 3m33s
2026-07-23 11:48:50 +08:00
099cff7f00 test: cover end-to-end attendance workflow
Some checks failed
CI / check (pull_request) Failing after 1m29s
2026-07-23 10:31:23 +08:00
0ce8f58adb chore: remove dead code and tune frontend chunks
Remove unused production symbols, starter assets, and stale browser tooling; reduce frontend chunk fragmentation.
2026-07-23 01:26:41 +00:00
90cc321221 fix: UI improvements and DingTalk attendance refresh for completed sessions
All checks were successful
CI / check (pull_request) Successful in 1m33s
- DatePicker editors open immediately on double-click
- Student archive drawer hides empty studentNo parentheses
- Student add/edit form uses 2-column grid layout
- Table horizontal scrollbars show only on hover/focus
- Remove fake 近7日趋势 stats from attendance student detail
- Allow DingTalk refresh to update completed attendance sessions
  (late-arriving punches can now change absent → present)
- Switch dev command from concurrently to turbo run dev
- Remove concurrently/wait-on dependencies
2026-07-22 17:50:59 +08:00
2c0be0f033 test: 为边界条件修复添加测试用例
- rooms: parseRoomNumber 未知格式、楼层0、capacity ?? 测试
- occupancies: capacity undefined/null/0 fail-closed 防守测试
- schedules: startTime > endTime 拒绝测试
- attendance: 重叠检查按时间排序、raw[index] 移除测试
- expenses: 金额零/负/NaN 拒绝、period反转校验测试

5 modules, 40 tests, all passing
2026-07-20 15:30:41 +08:00
00e9c5e45a fix: 修复多处边界条件问题
- rooms: parseRoomNumber 未知格式返回默认 capacity=4,防止 undefined 绕过入住容量检查
- rooms: 修复 parseInt() || undefined 导致楼层 0 被吞掉
- rooms: batchImport 中 capacity 使用 ?? 代替 ||,显式 0 不被覆盖
- occupancies: 所有 capacity 比较加 ?? 0 防守兜底,fail closed
- schedules: assertValidScheduleRange 增加 startTime > endTime 校验
- attendance: 时段重叠检查改为按 startTime 排序后再比较,消除漏检
- attendance: 移除 getScheduleOptionsForAttendance 中不可靠的 raw[index] fallback
- expenses: 个人附加费批量导入增加 assertPositiveAmount 校验
- expenses: 水电费导入增加 periodEnd >= periodStart 校验
2026-07-20 14:45:53 +08:00
c7ba2799b9 feat: 拆分学号/身份证字段 + 考勤教师展示 + 代码优化
- 入住导入模板:学号和身份证号拆为独立字段,前后端对齐
- 排课查询关联教师,考勤归档页展示教师姓名
- 抽查时段增加 IsIn 校验
- 抽取 withPessimisticWriteLock 去重悲观锁查询
- import 增加文件空 buffer 校验
- 测试 mock 补全,适配事务 manager
- MySQL init.sql VALUES() 语法兼容修复
2026-07-20 11:54:01 +08:00
375c7ec60b feat: refine admin forms, attendance and finance workflows
Squash merge PR #23.

Included changes:
- complete occupancy check-in required fields/default payload
- improve responsive admin management pages
- fix attendance edge cases and attendance period config
- refine wallet/finance-related workflow handling

Checks:
- npm run typecheck -w apps/admin
- npm run typecheck -w apps/server
2026-07-18 12:54:10 +00:00
b3d0bafc22 feat: refine deposit room type workflows 2026-07-17 17:36:04 +08:00
f7328d670d fix: 归档删除改为软删除 2026-07-17 14:13:17 +08:00
4fbcde48b1 test: update attendance service mocks 2026-07-16 11:01:24 +08:00
559b8a56e9 feat: add attendance device SN classroom bindings 2026-07-16 10:22:23 +08:00
fcde6caaaa fix: defer attendance settlement until lesson end 2026-07-15 09:39:21 +08:00
b1f35f9d1a test: harden business boundary conditions 2026-07-15 00:03:55 +08:00
e45da7f998 feat: improve attendance scheduling and API validation 2026-07-14 23:12:14 +08:00
811e7ce826 feat: show DingTalk punch device details 2026-07-14 11:20:41 +08:00
7860703672 test: update DingTalk token cache fixtures 2026-07-13 16:04:05 +08:00
1c8bd08be4 feat: settle course attendance automatically 2026-07-13 10:26:38 +08:00
a266d450e1 chore: apply eslint --fix autofixes (remove unnecessary as never casts in tests) 2026-07-12 23:01:37 +08:00
cc4f4dae4e fix: audit remediation — SSE user scoping, FK transactional safety, UI error handling
- H4: scoped SSE import progress to exact userId match; non-HTTP events excluded from all subscribers
- H2: moved PRAGMA foreign_key_check inside SQLite transaction before COMMIT; violations rollback preserving old tables
- M1: removed dead axios-style error branch from extractErrorMessage (interceptor already unwraps)
- M2: split handleSave try/catch — save errors vs reload errors shown distinctly
- M3: added provider field validation before AI config test request
- Added SSE scoping regression tests (import service + controller)
- Added FK check failure rollback test (database-migrations.spec)
- Updated controller spec expectations for userId parameter

Co-authored-by: Code Review <branch-review>
2026-07-12 22:59:03 +08:00
1e1c476bc3 feat: add CASL authorization and AI configuration 2026-07-11 14:25:34 +08:00
8ed1682b90 fix permissions and teacher attendance workflows 2026-07-10 20:40:52 +08:00
44105c1689 refactor: remove obsolete scaffolding and dead modules
Drop the unused root hello-world controller, empty CommonModule imports, development seeding code, legacy student report entity, stale DTOs, notification hook, and their placeholder tests.
2026-07-10 14:12:56 +08:00
e26a8e816e fix(attendance): normalize DingTalk match statuses
Store and validate stable English status codes for unmatched, pending, and matched attendance records so import, filtering, and auto-match use the same values.
2026-07-10 14:11:55 +08:00
6029d8e2fd refactor(server): remove Department/UserDepartment entities, CampusScope, and departmentId from all entities
- Delete department.entity.ts, user-department.entity.ts
- Remove Department/UserDepartment from entities/index.ts
- Remove departmentId column from 18 entities (AttendanceRecord, ArchiveAttachment, Bill, ClassSchedule, Classroom, ClassroomRental, Deposit, DepositInstallment, ExamScore, LearningRecord, Occupancy, PersonalExpense, ResultArchive, Room, RoomExpense, Student, StudentEnrollment, StudentProfile, StudentReport)
- Remove departments/ module entirely
- Delete campus-scope.ts, campus-scope.middleware.ts (request-utils.ts kept — it's just IP extraction)
- Simplify common.module.ts to empty module
- Remove CampusScopeMiddleware from app.module.ts
- Remove all CampusScope injections and filter calls across all services
- Remove departmentId from all DTOs and controllers
- Simplify dingtalk/wecom sync to only sync users (no dept table)
- Update seed module to remove department seeding
- Clean frontend compilation
2026-07-09 17:51:32 +08:00
22bc1876d8 refactor: remove departmentId from Class entity 2026-07-09 17:32:37 +08:00
86f126671c fix: repair 5 sites where StudentDingMapping.studentId was misused as User FK
- dingtalk.service.ts syncOneUser: query studentRepo (not userRepo) by mapping.studentId
- sync.service.ts importDingTalkUsers: skip User role lookup for existing mappings
- attendance.service.ts autoMatchDingRecords: use studentId directly, remove second-hop query
- schedule-sync.service.ts syncAll: skip teacher mapping block (deprecated)
- rbac.service.ts getUnboundUsers: return [] (method deleted in Task 4)
2026-07-09 17:00:00 +08:00
ef1b46b9f4 refactor: replace UserDingMapping with StudentDingMapping entity 2026-07-09 16:53:25 +08:00
4d4db7626c chore: remove dead CampusScope references from attendance spec test 2026-07-09 15:35:59 +08:00
3240c7cfdd fix(server): restore @InjectRepository(UserDingMapping) accidentally removed with scope 2026-07-09 15:31:35 +08:00