- 新增电工APP独立鉴权中间件与登录注册接口 - 新增定时任务管理模块,支持任务配置、执行日志与手动触发 - 新增运维费定价与电工派单成本价目管理 - 新增电工资质证照管理与到期提醒定时任务 - 完善数据库实体与DTO校验,优化权限控制
11 KiB
11 KiB
Checklist
change-id:
add-om-pricing-and-worker-registry逐条核对,通过后勾选。 验证方式说明:
- 「API 实测」= 在本地开发库(127.0.0.1:5432/xintong)起
MIDWAY_SERVER_ENV=local实例,用 admin 真实 JWT 逐条打接口 + 直连数据库比对;- 「浏览器实测」=
vite+ 本地后端,用浏览器实机登录后走查;- 「代码核对」= 无法在运行时构造的场景,以代码 + 结构(约束/索引)核对。
数据库与实体
database/migrations/20261001_om_worker_registry.sql已创建,覆盖worker/worker_cert/service_price/dispatch_price/sys_notice五处变更,且可重复执行无报错 —— API 实测:本地库重跑迁移无报错,worker18 列、dispatch_price11 列不变,sys_menu34-46 仍为 13 行无重复database/db_seed.sql已同步菜单(id 34-46)、字典(5 个 dict_type)、ADMIN 角色绑定,新库初始化与迁移后结构一致 —— 代码核对(Task 1 已完成并经迁移对照)worker实体含password_hash/audit_status/audit_remark/audit_by/audit_at/last_login_at—— 代码核对 + 库表列核对(6 列均在)dispatch_price实体含price_mode/unit/status/remark/updated_at—— 代码核对 + 库表列核对(5 列均在)worker_cert/service_price/sys_notice实体已创建,索引与迁移脚本一致 —— API 实测uk_worker_cert_type_no/uk_service_price_code/uk_sys_notice_biz均生效
客户运维价目(FR-1)
/api/service-prices支持分页、关键词(编码 / 名称)、price_mode/service_type/status筛选 —— API 实测四种筛选均只返回命中记录price_code自动生成为FW+YYYYMMDD+3 位序号且不可编辑 —— API 实测连建三条得到FW20261001001~003,priceCode在白名单外故不可写price_mode为 1 按次 / 2 按工时 / 3 材料 三种取值均可新增、编辑、筛选 —— API 实测- 支持启用 / 停用与硬删除两个独立操作 —— API 实测
PUT /:id/status与DELETE /:id(删除后查库确认为物理删除,重复删返回 404)
电工派单成本价(FR-2)
- 存量
dispatch_price行迁移后price_mode=1、status=1无 NULL —— API 实测存量 2 行均为priceMode=1 / status=1 /api/dispatch-pricesCRUD + 筛选可用,price_code冲突返回 400 且 message 明确 —— CRUD/筛选 API 实测通过;UniqueConstraintError→「价格编码已存在」分支为代码核对(生成器保证编码不重复,无法从接口构造冲突)
电工登记与审核(FR-3 / FR-4)
/api/workers支持分页、关键词、status/auditStatus/certStatus筛选 —— API 实测 + 浏览器实测(已过期 1 条 / 无证书 3 条,筛选结果与库一致)- 创建时手机号唯一校验生效;默认密码规则为手机号后 6 位(不足 6 位用
123456)并 bcrypt 存储 —— API 实测:重复手机号返回手机号已存在;password_hash以$2a$10$开头,用手机号后 6 位可成功登录电工 APP - 审核通过写入
audit_status=1/audit_by/audit_at,清空audit_remark,并置cert_verified=true—— API 实测(库中字段逐项比对) - 审核驳回未填理由返回 400;填写后
audit_status=2且理由、审核人、时间落库 —— API 实测:不填理由 →code:400「请填写驳回理由」;填理由 →audit_status=2 / audit_remark / audit_by / audit_at落库 - 删除电工级联删除其
worker_cert;存在派单引用时返回 400 并提示 —— API 实测:删除后worker_cert1→0;自造survey_order引用后返回code:400「该电工存在派单记录,无法删除」 /api/workers/options与/recommend仅返回audit_status=1 AND status=1的电工 —— 代码核对(查询条件已收紧)+ API 实测两接口正常返回
资质证书与到期状态(FR-5 / FR-6)
worker_cert支持一电工多证,(worker_id, cert_type, cert_no)唯一约束生效 —— API 实测一电工挂多证;重复插入触发uk_worker_cert_type_no唯一冲突- 资质状态计算正确:无证书 / 正常 / 临期(≤30 天)/ 已过期(
expire_date < today),expire_date为 NULL 视为长期有效 —— API 实测:构造 +10 天 / -5 天 / +200 天 / NULL 四种证书,certStatus四种筛选各自只返回对应记录 - 电工列表「资质状态」列标签颜色正确(绿 / 橙 / 红 / 灰)—— 浏览器实测:列表渲染出「已过期」彩色标签;配色取自
a-tag状态映射(代码核对) - 列表 KPI 卡统计「电工总数 / 待审核 / 临期 / 已过期」数值正确 —— API 实测
stats与独立 SQL 计算一致;浏览器实测 KPI 显示 5 / 4 / 0 / 1,与接口一致 - 仪表盘「电工资质到期提醒」区块展示临期与已过期证书清单并可跳转 —— 浏览器实测:区块展示「验收测试电工 | 高压电工证 | 2026-09-26 | 已过期 5 天」,且可跳转电工登记管理页
站内通知与定时任务(FR-7)
sys_noticeUNIQUE(notice_type,biz_id,receiver_id) 生效,重复执行定时任务不产生重复通知 —— API 实测:直接重复 INSERT 报uk_sys_notice_biz唯一冲突;真实调用scanCertExpireAndNotify()三次,created依次为 1 / 0 / 0- 定时任务每日 08:00 扫描
expire_date <= today + 30 天的证书,为每个 ADMIN 用户生成未读通知 —— 代码核对(@Job({ cronTime: "0 0 8 * * *", timeZone: "Asia/Shanghai" }))+ 实测扫描逻辑生成 ADMIN 未读通知 - 证书续期或被删除时,该证书既有未读通知被清理 —— API 实测:续期(改
expireDate)后该证书通知 1→0;删除证书后亦为 0 /api/notices系列接口仅返回receiver_id = 当前用户的数据,未读数接口正确 —— API 实测:列表仅返回本人;读他人通知返回 404;unread-count与库中计数一致- 顶栏铃铛红点角标随未读数变化;单条已读 / 全部已读生效;点击通知跳转电工登记页 —— 浏览器实测:角标显示 2,抽屉内 2 条「验收测试」通知
电工 APP 接口(FR-8)
/api/worker-app/auth/register与/login可匿名访问,注册手机号唯一,登录返回 worker token(payload 含workerId/phone/userType=worker)—— API 实测注册/登录成功;重复手机号被拒WorkerAuthMiddleware仅作用于/api/worker-app/*,且放行/api/worker-app/auth/*—— API 实测:匿名可访问auth/*,未带 token 访问/profile返回 401- 电工 APP 无法通过
:id越权读取 / 修改他人资料与证书(返回 403)—— API 实测:电工 X 的 token 改/删电工 Y 的证书 →code:403「无权操作该证书」,Y 数据未被改动 POST /api/worker-app/audit/resubmit仅在audit_status=2时成功,audit_status=1时返回 400 —— API 实测auth.middleware.ts与rbac.middleware.ts均已忽略/api/worker-app/*—— 代码核对 + API 实测(电工 token 不会被管理端鉴权拦截)
菜单、权限与路由(FR-9 / FR-10)
运营派单(id=3)已改为目录,管理端侧边栏展开可见 3 个子菜单 —— API 实测登录菜单树中 id=3 为menuType=1且含 34/35/36;浏览器实测展开可见三个子菜单- ADMIN 可见「运维费定价」「电工派单成本价」「电工登记管理」;SALESMAN 不可见「运营派单」及其子菜单 —— API 实测:自签 SALESMAN token 调
/auth/currentUser,dispatch:list/dispatch:price/dispatch:cost/dispatch:worker均为 false - 前端
/dispatch重定向到/dispatch/price,3 条子路由meta.permission正确 —— 代码核对(router 已配置redirect与meta.permission)+ 浏览器实测(首次因后端瞬时抖动显示报错,复测页面正常渲染) /api/service-prices、/api/dispatch-prices、/api/workers已纳入 RBAC 权限映射 —— API 实测:SALESMAN 调/api/workers、/api/service-prices均 HTTP 403- SALESMAN 调用
/api/workers/options、/api/workers/recommend正常返回(白名单生效),调用/api/workers列表返回 403 —— API 实测 HTTP 200 / 403
安全与规范(NFR)
GET /api/workers与详情接口不返回password_hash—— API 实测:列表 / 详情 /certStatus各分支返回体均无password_hash/passwordHash- 所有查询走 Sequelize 参数化 API,无字符串拼接 SQL 与模板字符串 LIKE —— 代码核对:模糊查询统一
Op.like+ 绑定值,无原始 SQL 拼接 - 前端页面未使用
v-html,表单输入含长度与特殊字符校验 —— 代码核对:三个新页面无v-html(grep 0 命中),输入框均带maxlength,手机号等有正则校验 - 列表接口均走
limit + offset分页,证书聚合无 N+1 查询 —— 代码核对:worker_cert用worker_id IN (...)批量加载后按电工分组 - Controller 参数使用 DTO +
class-validator,无滥用any(除既有约定处)——worker/worker-app用 DTO 类型;service-price/dispatch-price/notice沿用工程既有约定(@Body() body: any,业务校验在 service 层)。注:工程未注册@midwayjs/validate组件,DTO 装饰器全站不生效,属既有约定
端到端
- 后端
yarn dev启动无编译错误 —— API 实测:MIDWAY_SERVER_ENV=local实例启动成功;npx tsc --noEmit退出码 0 - 前端
yarn dev启动,三个新页面 CRUD 全链路可用 —— 前端vite启动成功、vue-tsc --noEmit退出码 0;浏览器实测三个页面均正常渲染、弹窗字段齐备、KPI/筛选/铃铛/仪表盘联动正常;CRUD 接口链路已在 API 层全量实测(浏览器内「填表→点提交」这一步受自动化工具步骤预算限制未跑通) - AC-1 ~ AC-11 逐条走通并留痕 —— 见上方各项实测证据;验证数据已清理,数据库恢复至验证前基线(
worker=4 / worker_cert=0 / sys_notice=0 / service_price=0 / dispatch_price=2)
验证阶段修复
- 修复
WorkerAuthMiddleware同时声明match与ignore:Midway 抛MIDWAY_10001: options.match and options.ignore can not both present,导致全站所有/api/*返回 500;已改为仅match,/api/worker-app/auth/*在resolve()内放行 —— API 实测修复后匿名访问新接口由 500 恢复为 401