- v3 修正通知依赖:F30 依赖 F14(不再被邮件阻塞) - 提升关键项 Test_Method 为可复现 curl 步骤 - 新增回归脚本条目:F40/F41
12 KiB
12 KiB
| 1 | ID | Title | Description | Acceptance | Test_Method | Tools | Dev_Status | Review1_Status | Regression_Status | Files | Dependencies | Notes |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2 | F1 | CSV快照收口(P5蓝图) | 创建P5蓝图执行合同快照,覆盖功能补齐+平台演进待办项 | CSV通过校验;依赖关系正确;旧快照不回改 | python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-02-09_11-17-07-p5-blueprint.csv | manual | DONE | DONE | DONE | issues/2026-02-09_11-17-07-p5-blueprint.csv | [] | none |
| 3 | F2 | 费用类型只读API(expense-types) | 补齐费用类型查询接口,供报销明细下拉使用 | GET /api/expense-types?enabled=true 返回37启用项;无权限用户阻断符合约定 | cd server && node --check src/routes/expenseTypes.js | manual | TODO | TODO | TODO | server/src/routes/expenseTypes.js | server/src/app.js | server/README.md | ["F1"] | 优先复用既有 ExpenseType seed,不引入新依赖 |
| 4 | F3 | 费用类型管理API(system.settings) | 新增费用类型增改启停接口,并写入 audit_logs | finance_mgr/admin 可启停费用类型;禁用后不出现在 enabled 查询;审计日志包含变更摘要 | manual: curl 调用创建/禁用/启用并查询验证;同时查询 /api/audit-logs 筛选 action | manual | TODO | TODO | TODO | server/src/routes/expenseTypes.js | server/src/routes/audit.js | server/src/constants/permissions.js | ["F2"] | 只做最小可运营;复杂排序/导入后置 |
| 5 | F4 | 报销单编辑与删除(补齐CRUD) | 补齐 PATCH/DELETE 报销单能力(仅 draft/rejected 可编辑;仅 draft 可删除);删除 draft 必须自动解绑其绑定票据并回到 available | 状态/权限校验正确;删除不影响其他实体;删除 draft 时 invoice_pool 自动解绑;workflow/audit 证据一致 | cd server && ./scripts/regression-workflow-actions.sh | manual | TODO | TODO | TODO | server/src/routes/reimbursements.js | server/scripts/regression-workflow-actions.sh | ["F1"] | 复用现有 ensureOwnOrCanViewAll 与 AppError 体系 |
| 6 | F5 | 报销明细CRUD与总金额重算 | 新增报销明细 items 的增改删;每次变更事务内重算 reimbursement_forms.total_amount | items CRUD 只允许 draft/rejected;total_amount=items.sum;submit 按 total_amount 匹配 approval_rule | cd server && ./scripts/regression-workflow-actions.sh | manual | TODO | TODO | TODO | server/src/routes/reimbursements.js | server/scripts/regression-workflow-actions.sh | ["F2","F4"] | 金额字段统一 toMoneyString;避免浮点误差 |
| 7 | F6 | 前端报销编辑器(明细+费用类型) | 在报销详情页补齐明细编辑能力(费用类型下拉+日期+金额+说明),并保持现有提交/撤回/支付闭环 | employee 可完整填写明细并提交;列表/详情金额一致;typecheck/build/e2e 通过 | cd client && npm run typecheck && npm run build && npm run test:e2e | manual | TODO | TODO | TODO | client/src/pages/ReimbursementDetailPage.tsx | client/src/api/reimbursements.ts | client/src/api/expenseTypes.ts | ["F3","F5"] | UI继续复用现有 shadcn 风格组件;加 data-testid |
| 8 | F7 | 代提交(expense.proxy_submit) | 允许 administration 为员工 owner 创建/提交报销(submitter=行政,owner=员工),强制 delegation_grants 有效期校验;workflow 解析以 owner 为基准 | 无授权时 403;有授权可 create/submit 且审批人按 owner.direct_manager 解析;audit_logs 记录 proxy 行为 | cd server && ./scripts/regression-workflow-actions.sh | manual | TODO | TODO | TODO | server/src/routes/reimbursements.js | server/src/utils/delegation.js | server/src/routes/workflow.js | ["F5"] | 先做 API+后端校验;前端可先用最小入口(owner选择) |
| 9 | F8 | 行政代上传(owner≠uploaded_by) | 放开 invoice-pool/upload 跨 owner 上传:需 delegation_grants 或 system.users;并写 audit_logs | 行政可为已授权员工上传;未授权拒绝;发票归属与可见性规则不被破坏;审计可追踪 | cd server && ./scripts/regression-ocr.sh && ./scripts/regression-audit.sh | manual | TODO | TODO | TODO | server/src/routes/invoicePool.js | server/src/utils/delegation.js | server/src/routes/audit.js | ["F7"] | 保持上传不阻塞 OCR;ownerScope 查询语义不变 |
| 10 | F9 | 票据解绑/重绑(bound->available) | 新增 unbind 接口:仅当报销单处于 draft/rejected 时允许解绑;解绑后票据回到 available 并清理 linked_* | 解绑权限与状态校验正确;解绑后可重新绑定其他报销草稿;不破坏判重约束 | cd server && ./scripts/regression-workflow-actions.sh | manual | TODO | TODO | TODO | server/src/routes/invoicePool.js | server/scripts/regression-workflow-actions.sh | ["F1"] | 与 invoice_pool_status_invariants_chk 保持一致 |
| 11 | F10 | 判重例外(override duplicate) | 新增 finance_mgr 判重例外接口:标记 invoice_pool.is_duplicate_override=true 并记录引用原单号+原因;写 audit_logs | 命中判重时普通用户仍 VALID_003;finance_mgr 可 override 后成功 bind/submit;若原单 status=approved/paid 则 VALID_004 | cd server && ./scripts/regression-dedup.sh && ./scripts/regression-audit.sh | manual | TODO | TODO | TODO | server/src/routes/invoicePool.js | server/src/routes/reimbursements.js | server/scripts/regression-dedup.sh | server/src/routes/audit.js | ["F9"] | 严格遵守规格书:例外必须可审计可追溯 |
| 12 | F11 | OCR失败重试API(次数/频控) | 新增 POST /api/invoice-pool/:id/retry-ocr:attempt<=3 且 60 秒频控;超限返回 VALID_006/VALID_007;写 audit_logs | ocr_failed 可重试回到 pending_ocr 并由 job 处理;频控与次数校验正确;mock 模式可回归 | cd server && ./scripts/regression-ocr.sh && ./scripts/regression-audit.sh | manual | TODO | TODO | TODO | server/src/routes/invoicePool.js | server/src/jobs/ocrJob.js | server/scripts/regression-ocr.sh | ["F1"] | 错误码新增需同步 client 错误文案映射 |
| 13 | F12 | 90天未绑定自动归档(unused_timeout) | 新增作业:available/ocr_failed 超过 90 天未绑定自动归档为 archived_reason=unused_timeout;并保证不参与判重 | 作业可重复运行幂等;归档后仍可查询/预览;不影响全局判重历史口径 | cd server && ./scripts/regression-ocr.sh && manual: 调整 created_at 模拟超期后跑 job 断言 | manual | TODO | TODO | TODO | server/src/jobs/invoiceArchiveJob.js | server/src/app.js | server/README.md | ["F11"] | 后续迁移到 pg-boss;当前可先 node-cron 实现 |
| 14 | F13 | signed-url审计与下载行为记录 | 对 signed-url 签发与下载两类事件写 audit_logs(不记录敏感 token 明文) | 无权限仍 403;有权限获取 signed-url 与下载时均可在 audit_logs 检索到对应记录 | cd server && ./scripts/regression-audit.sh | manual | TODO | TODO | TODO | server/src/routes/invoicePool.js | server/src/routes/audit.js | ["F1"] | extra 仅保存 invoiceId/actorId/expireAt 等脱敏信息 |
| 15 | F14 | 站内通知基础设施(DB+API) | 新增 notifications 表与 API(list/markRead),用于工作流与系统事件通知 | 可分页读取;可标记已读;权限隔离正确;最小索引满足试运行 | cd server && npm run prisma:migrate:dev && ./scripts/regression-all.sh | manual | TODO | TODO | TODO | server/prisma/schema.prisma | server/src/routes/notifications.js | server/src/app.js | ["F1"] | 只做最小字段:title/body/type/targetId/readAt |
| 16 | F15 | 邮件通知(SMTP/Nodemailer) | 引入 nodemailer 发送邮件:新待办/密码重置等;失败降级不阻塞主流程;可配置开关 | EMAIL_ENABLED=false 时不发送;开启后可发出测试邮件;失败仅记录 audit/日志不影响业务 | manual: 配置 SMTP 并触发一次待办通知/重置密码邮件 | manual | TODO | TODO | TODO | server/src/services/email/* | server/src/config.js | server/package.json | ["F14"] | 优先复用成熟 nodemailer;写回滚说明 |
| 17 | F16 | 前端通知入口(Bell) | 新增通知列表页/弹层与未读角标;支持标记已读 | 四角色均可见自己的通知;读写后角标正确;e2e 覆盖核心路径 | cd client && npm run test:e2e | manual | TODO | TODO | TODO | client/src/components/navigation/Topbar.tsx | client/src/pages/NotificationsPage.tsx | client/src/api/notifications.ts | ["F14"] | 保持 UI 轻量,不做复杂订阅推送 |
| 18 | F17 | 报表导出(CSV/Excel) | 新增报销/票据/审计的导出接口(权限控制),前端提供下载入口 | 导出字段与权限正确;大数据量时可分页/流式输出;审计记录导出操作 | manual: 导出后核对字段;并在 audit_logs 中检索 EXPORT 动作 | manual | TODO | TODO | TODO | server/src/routes/exports.js | client/src/pages/* | server/package.json | ["F6","F16"] | 推荐 exceljs 或 csv-stringify(二选一并写 Notes:用途/版本/回滚) |
| 19 | F18 | 审批规则维护(system.settings) | 补齐 approval_rules 维护 API 与最小前端页(finance_mgr/admin),支持启停与优先级/金额段维护 | 规则变更可立即影响新 submit;历史 instance 不回写;审计记录变更 | cd server && ./scripts/regression-workflow-actions.sh | manual | TODO | TODO | TODO | server/src/routes/approvalRules.js | client/src/pages/ApprovalRulesPage.tsx | server/src/app.js | ["F5"] | 只做最小可运营;复杂可视化后置 |
| 20 | F19 | 作业系统升级(pg-boss,Postgres队列) | 引入 pg-boss 替代 node-cron:迁移 OCR/委派回收/超期归档,统一重试、并发、单机/多实例语义 | 同一作业在多进程下不重复处理;作业可观测(claimed/updated/failed);现有回归全绿 | cd server && ./scripts/regression-all.sh && ./scripts/regression-ocr.sh | manual | TODO | TODO | TODO | server/src/jobs/* | server/src/app.js | server/package.json | deploy/trialrun/* | ["F12"] | 选择 pg-boss(成熟/无 Redis);保留回滚:可切回 node-cron |
| 21 | F20 | Worker进程拆分(可选) | 提供 API 进程与 Worker 进程拆分的部署方式(compose 开关),保持单机也可跑 | 开启后:API 不跑作业;Worker 只跑作业;两者共享 DATABASE_URL | manual: docker compose 起两个进程并跑回归脚本 | manual | TODO | TODO | TODO | deploy/trialrun/docker-compose.yml | server/src/index.js | server/README.md | ["F19"] | 仍保持单机;为未来横向扩展铺路 |
| 22 | F21 | 对象存储(MinIO/S3兼容) | 存储从本地目录演进到 S3 兼容(先 MinIO),保持 signed-url 契约不变或提供兼容层 | 上传/预览/签名URL 在 MinIO 下可用;回归与 E2E 通过;迁移路径清晰可回滚 | manual: trialrun 环境启用 MinIO 并完成一次上传预览 | manual | TODO | TODO | TODO | server/src/services/storage/* | deploy/trialrun/* | server/README.md | ["F20"] | 优先用 @aws-sdk/client-s3;单机优先 MinIO |
| 23 | F22 | 可观测性增强(metrics/日志) | 新增 /metrics(Prometheus)与更完整的结构化日志字段规范(不落敏感) | metrics 可抓取关键指标(HTTP/作业/DB);日志具备 requestId/actorId/targetId 等字段 | manual: curl /metrics 并核对字段;触发作业观察指标变化 | manual | TODO | TODO | TODO | server/src/app.js | server/src/observability/* | deploy/trialrun/* | ["F19"] | 优先用 prom-client;OTel 放更后 |
| 24 | F23 | SSO/OIDC(可选,P7+) | 支持 OIDC 登录(企业 SSO),保留现有密码登录作为 fallback | 可通过 OIDC 登录并绑定员工;权限模型不变;可配置开关 | manual: 配置 OIDC provider 后走一次登录回调 | manual | TODO | TODO | TODO | server/src/routes/auth.js | server/src/services/oidc/* | server/README.md | ["F1"] | 优先复用 openid-client/passport 策略 |
| 25 | F24 | 工作流扩展到其他实体(P7+) | 抽象 entity_type 扩展到请假/采购等(先骨架:definition+instance+task 复用) | 新实体可创建 instance 并走同审批动作;不影响报销既有链路 | manual: seed 一个 leave 定义并跑一条最小链路 | manual | TODO | TODO | TODO | server/src/routes/workflow.js | server/src/routes/leave.js | server/prisma/schema.prisma | ["F19"] | 只做骨架与复用点,不做完整业务表单 |
| 26 | F25 | 敏感字段加密与脱敏(P6+) | 落实员工银行卡/身份证等敏感字段的可逆加密与脱敏输出,并对查看/变更写审计 | 对外响应仅脱敏;解密仅限特权权限并写 audit;不在日志/错误中泄露明文 | manual: 创建员工并查看返回脱敏;用特权接口取明文并在 audit 检索记录 | manual | TODO | TODO | TODO | server/src/routes/users.js | server/src/services/crypto/* | server/src/routes/audit.js | ["F1"] | 优先用成熟库(libsodium/crypto),并明确密钥轮换策略 |