PayrollManagementSystem/backend/tests/README.md
liuwenxin b86eaf2b00 docs: 记录 2026-10-20 全量回归与 e2e 实跑结果
全量 862 passed(11:32);e2e role_home 4/4、report_flow 12/12(按业务编号下钻断言)、calc_settlement 8/8;审批开关已还原、无测试账号残留。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-20 10:10:32 +08:00

51 KiB
Raw Permalink Blame History

后端测试

AI摘要:本项目后端的测试用例清单与运行记录。按「基础设施层随实现即时测、业务模块整模块完成时统一测」分层组织。当前 697 个用例全绿(2026-10-16,M3 增量批次后)。

一、测试约定

编写时机(分层对待)

  • core/ 等基础设施随实现即时补测 —— 它们是被业务复用多次的地基,越早锁定越省事
  • 业务模块(系统管理、基础数据管理、业务数据管理、绩效工资核算、报表统计)在整个模块功能完成时统一编写用例并执行,不在模块做到一半时零散补测

目录二分

目录 依赖 用途
tests/unit/ 无(不碰 Docker、不连库) 纯函数与元数据验证,秒级跑完,开发中可高频执行
tests/integration/ 完整应用实例或 MySQL/Redis 容器 接口级、跨产物一致性验证

单元测试里的 Redis 依赖通过 fake_redis 夹具(fakeredis)替换,因此验证码 GETDEL、限流计数、黑名单 TTL 这些逻辑无需起容器即可覆盖。

注释要求

  • 测试类用中文 docstring 说明「这组用例守的是什么」——即如果不测会出什么问题
  • 用例名描述场景与预期(test_中文按字节而非字符计算边界)
  • 关键断言补注释说明为何该断言重要,尤其是防回归的用例

运行方式

cd backend
uv run pytest tests/unit -q          # 纯单元,无需容器,秒级
uv run pytest -q                     # 全部(集成部分需容器在跑)
uv run pytest tests/integration -q   # 仅集成

二、用例清单

基础设施层(core)

测试文件 守什么 用例数 关键覆盖点
unit/test_models.py 建表规范红线 49 表名小写蛇形复数;每列中文注释且确实输出到 DDL;uk_/idx_/fk_ 命名前缀;ENGINE/CHARSET 显式指定;ON UPDATE CURRENT_TIMESTAMP 存在(server_onupdate 不会生成该子句,是 SQLAlchemy 的行为陷阱);日志表反向断言不含该子句
unit/test_logging.py 日志脱敏与格式化 16 密码/令牌/Bearer/手机号/身份证五类脱敏;脱敏必须后置于 %-格式化(早期实现在 filter 阶段改写 record.msg,抹掉 %s 占位符导致 logging 抛 TypeError、整条日志丢失);setup_logging 幂等
unit/test_config.py 配置加载与安全边界 14 密钥强度 fail-fast;JWT ≤120 分钟、验证码 ≤300 秒的硬约束;密码含 @ : / # 时 URL 构造不破坏;渲染 URL 默认隐藏密码
unit/test_security.py 密码哈希 11 BCrypt 格式与定长 60;72 字节边界(24 个汉字正好、25 个超限);超长密码校验时返回 False 而非抛异常(防侧信道);哈希串损坏不炸
unit/test_response.py 统一响应契约 11 字段固定为 code/message/data;分页序列化为规范要求的驼峰 list/total/pageNum/pageSize
unit/test_exception_handlers.py 异常转统一信封 5 校验错误 422→400 并压成单行中文;HTTP 状态码与 body 的 code 必须一致

系统管理 · 登录鉴权

测试文件 守什么 用例数 关键覆盖点
unit/test_jwt.py 令牌有效期语义与版本机制 12 exp 基于 UTC 而非本地时间(混算会产生 8 小时偏差,表现为「刚登录就过期」或「过期了还能用」);过期/篡改签名/alg=none 攻击/畸形字符串一律收敛为 401 而非 500;jti 唯一
unit/test_captcha.py 验证码一次性语义 12 GETDEL 后二次使用必然失败(防重放);校验失败也消费(否则可用一个验证码反复试密码);字符集剔除 0O1Il;每次渲染含随机干扰不可比对;TTL 300 秒
unit/test_login_limiter.py 阈值与滑动窗口 9 达阈值即锁;每次失败刷新 TTL(否则「4 次失败 + 等 14 分钟 + 1 次失败」可绕过锁定);按用户名隔离,一个账号被攻击不牵连他人;登录成功清除计数
unit/test_token_blacklist.py 黑名单 TTL 语义 7 TTL 等于令牌剩余有效期(写死固定值会导致「登出 N 分钟后又活了」,不设过期则 Redis 无限增长);已过期令牌不写入;jti 互不串号
unit/test_auth_schemas.py 密码按字节校验 9 24 个汉字 = 72 字节正好、25 个被拒 —— 按字符数校验(如 max_length=20)根本拦不住,会让登录接口 500
integration/test_auth_flow.py 整条链路串联正确性 16 验证码→登录→持令牌访问→登出失效;验证码重放被拒;失败原因如实落库且日志表无任何密码字段;账号不存在与密码错误文案完全相同(防枚举);禁用账号与 token_version 提升均使旧令牌立即失效

系统管理 · 用户与角色管理

测试文件 守什么 用例数 关键覆盖点
unit/test_audit_service.py 审计上下文的语义 16 正常退出记成功、抛异常记失败;失败时先 rollback 再写日志且原异常原样重抛(顺序颠倒时数据库异常会让失败记录丢成 PendingRollbackError);操作人标量在进入时固化(失败路径不再触碰会过期的 ORM 对象);进入时脏会话告警(防嵌套半提交);审计自身失败不外抛、不替换业务异常
unit/test_audit_serialization.py 入库前截断与脱敏不丢数据、不泄密 16 各列按模型常量截断;60KB 请求参数按 UTF-8 字节边界截断后是原串的干净前缀(中文不切半);结构化抹除(按敏感键名整值替换):前导空白/空格/逗号/引号密码全被抹除且 JSON 结构完好、嵌套 dict/list 与键名大小写、非敏感键不误伤;手机号仍由 scrub 文本兜底;孤立代理字符不再抛错丢审计
unit/test_role_schemas.py 角色编码格式红线 16 ^[a-z][a-z0-9_]{1,31}$ 全边界(大写/中文/数字或下划线开头/单字符/超长被拒);编辑模型不接受 role_code(多余字段忽略);状态只能 0/1
integration/test_user_management.py 用户 CRUD 串联正确性 + 审计留痕 23 分页以 pageNum/pageSize 生效(钉死别名契约,改蛇形会静默失效);自删/自禁/移除自己 admin 角色 403;禁用与重置密码使旧令牌立即失效;每个写操作(含失败的)在 operation_logs 有对应记录、request_data 全文扫不到明文密码;账号删除后审计行 user_id 置空而 username 保留;编辑时保留既有角色不撞唯一键(1062 回归);编辑未修改字段不把 NULL 污染成空串;含下划线用户名不误配(LIKE 转义回归)
integration/test_role_management.py 角色保护规则(系统自举与授权安全的最低防线) 13 admin 角色禁删禁停 403;有绑定用户的角色禁删 400 且消息含用户数、解绑后可删;编码创建后不可改;停用角色不可再分配(接口层拦截);停用后不在 /users/meta/roles 但仍在管理列表;user_count 正确;普通用户 403

系统管理 · 日志查询

测试文件 守什么 用例数 关键覆盖点
unit/test_log_schemas.py 时间范围校验与时区归一 13 起始晚于结束拒绝(两类日志共用基类,放过去则 SQL 条件互斥、查询恒空且无提示);带时区输入转本地 naive(Z 后缀 → 东八区 08:00;混用 aware/naive 不再抛 TypeError 穿透 500);边界相等通过;仅传一端不校验
integration/test_log_query.py 写入与查询的口径一致 13 写操作产生的审计可按 username/module/action/success/时间范围检索到,摘要含目标用户名;失败记录带中文 error_message;列表不含 request_data、详情端点返回已脱敏参数(两段式,含 404/403);起始晚于结束 400;登录日志直插造数后可按 ip 精确筛选;两类日志普通用户 403

应用骨架与交付产物

测试文件 守什么 用例数 关键覆盖点
integration/test_health.py 统一信封与全局异常 7 /api/health 始终 200 且依赖状态如实上报;404/405 返回统一信封而非框架默认 detail 数组;/api/docs 可访问
integration/test_schema_sync.py DDL 不漂移 6 db/migration/V1__*.sql 与 db/init/init-dev.sql 的表集合与列集合逐表比对——两份 DDL 手工同步迟早漂移,且漂移后果隐蔽(开发库结构与生产不一致,上线才发现)

M1 计提参数与岗位角色(2026-10-13 完成)

文件 守什么 用例数
tests/integration/test_business_roles.py 岗位 CRUD 链路、内置岗位禁删禁停(种子 3 个)、模板编码校验、编辑换模板、权限 403、模板下拉 5 项 8
tests/integration/test_param_management.py 目录 16 项与七分组、企业户 2‰→3‰ 按到账日回溯、斜率 4/360 分数原样存储、变更生命周期(立即/未来生效不泄漏/同日取最新/撤回回退/已生效禁撤)、值校验(上限/枚举/除零分数/过去日期)、预览金标准(27 天=97/10000、折算 15000、绩效 145.5、可付 52.38;361 天=4‰)、经理聚合口径拒绝、权限 403 18
tests/unit/test_formula.py、tests/unit/test_param_registry.py 八个公式纯函数精确分数断言(第 1 天=899/90000、360=6‰、361=4‰、27 天、防除零、负绩效不归零、R×R)、注册表 16 项与迁移种子同源、模板声明参数闭环 43
tests/unit/test_param_snapshot.py 快照覆盖链(角色>产品类别>默认)、注册表默认值兜底、同日取最新、冻结不可变 12

运行:uv run pytest -q → 406 passed(2026-10-13 全量含回归;T5 冒烟另验证提交/生效/撤回/对比/导出/预览全链路)。

基础数据管理(2026-10-13 完成)

文件 守什么 用例数
tests/integration/test_personnel.py 未登录 401、新增默认在职、工资标准 str 直出、经理自引用/不存在或已离职/岗位停用 400、删除保护(团队成员与客户归属引用→引导改离职)、辐射区域与拓展计划全量替换(含清空与详情回显)、在职/离职切换、审计成功与失败双记录 20
tests/integration/test_products.py 未登录 401、类别值域 400、名称+规格组合唯一(同名称不同规格放行)、单价负数/非数字 400、启停切换、改类别、审计留痕 14
tests/integration/test_customers.py 未登录 401、客户分类值域 400、「公司名称+负责人」组合唯一(同公司不同负责人放行)、归属变更(主表更新+留痕字段断言)、归属人员离职 400、新旧相同含双方为空 400、留痕分页 join 姓名、编辑端点不触归属、审计留痕 14

运行:uv run pytest tests/integration/test_personnel.py tests/integration/test_products.py tests/integration/test_customers.py -q → 48 passed(2026-10-13,连跑两次幂等)。

业务数据管理(2026-10-13 完成)

文件 守什么 用例数
tests/integration/test_businesses.py 未登录 401;编号 YW{YYYYMMDD}-{4位} 与当日序号递增;空明细/数量 0/非法类别/单价非数字 400;销售额服务层固化(schema 无 sale_amount 入参,塞了忽略);特殊业务必填说明;编辑全量替换后三处冗余一致(明细 SUM == 业务冗余列 == DB SUM);状态机(终止无原因 400 → 留痕 old=1/new=3 → 终态再变更 400);归属变更主表+留痕同生(old/new owner 与 assistant、owner_type=2)、归属未变化 400、创建时归属人员已离职 400;有发货/回款禁换客户禁删;空业务删除连带清理明细/状态留痕/业务归属留痕;毛利润视图金标准(销售 10000/成本 30 → 率 997/1000、市政 30% → 折算 6970;销售额 0 全 0 防除零;成本缺失行派生为 null 不计类别汇总;asOf 按业务日期回溯旧阈值);审计成功/失败双记录 25
tests/integration/test_shipments.py 未登录 401;编号 SH{YYYYMMDD}-{4位};业务不存在 404;终态业务禁新发货 400;空明细/数量 0/运费负数/运费三位小数 400;行金额与发货单总额服务层固化;业务 total_ship/total_freight 回写一致;编辑后两处汇总同步;被回款指定禁删(解除后可删);删除后业务级汇总回退;终态业务发货单禁改禁删;审计双记录 15
tests/integration/test_receipts.py 未登录 401;批次 HK{YYYYMMDD}-{4位};未指定批次可登记且 payment_days 为 null;指定批次派生「发货日首日计第 1 天」(2026-10-05 → 2026-11-02 = 29 天,编辑清指定后回 null);业务不存在 404 / 终态禁新增 400;金额负数/三位小数 400;跨业务指定批次 400(「发货批次不属于该业务,无法指定」,编辑路同校验);合计回写与删除回退;已核算(改库 settlement_status=1)禁改禁删;settlement_status 筛选 0/1 两路;审计双记录 16
tests/integration/test_ratings.py 未登录 401;种子四等级全量按 sort_order 升序(A 90-100/B 75-89/C 60-74/D 0-59);区间倒挂/超上限/负下限 → 400「分值区间不合法」;名称唯一 → 400「该等级名称已存在」;新增与编辑两路重叠 400 点名冲突等级;边界相邻不算重叠正例(删 C 后新增 60-74 / 把 D 编辑为 0-74,74 与 75 相邻放行);编辑改名改区间回显;删除后判定表更新、二次删除 404;审计双记录。种子删改由 conftest 兜底恢复 14

运行:uv run pytest tests/integration/test_{businesses,shipments,receipts,ratings}.py -q → 70 passed(2026-10-13,连跑两次幂等);全量回归 uv run pytest -q → 603 passed(单元 381 / 集成 222)。

M3-core ④ 核算引擎 + 月度结算(2026-10-16 完成)

文件 守什么 用例数
tests/unit/test_formula.py(扩) R 钳制(向下取整/[0,100] 封顶封底/销售额 0 防御)、月工资三口径(INCLUDE 加/DEDUCT 减可负/EXCLUDE 仅绩效/基本缺失按 0/未知口径报错)、试用期年累计说明(未超无 note/超额精确差额)、经理 12.5%+5% 聚合(#12 暂口径) +12
tests/integration/test_calc.py 金标准双类别复算到分(市政 4000 + 轨交 0 = 折算 4000;88/9000 约分原文落 rate_text;绩效 39.11;R=100 应付 39.11,DB 锚定);业务级一次性计提 #25(同业务第二笔回款 repeated=True 仅登记进度不新增行);已核算再核算 400;未指定批次 400;成本缺失行毛利按 0 计(折算保守 −3000、missing_cost=1);负绩效照算不归零(折算 −2000);准入(normal 403/marketing_ops 可用);列表筛选;重算(finance 授权、v2 生效 v1 保留、reason 留痕、原因缺失 400、未核算重算 400、越权 403);批量逐笔成败清单 13
tests/integration/test_settlements.py 生成按人汇总+台账回填 settlement_id;无核算记录 400;已审定月份重生成 400;开关关闭提交即自动通过锁定(AP 单号/locked_at 回填);锁定后回款禁改 400(M2 已核算禁改防线先于锁定拦截);开关开启两节点流转(finance→leader)至锁定;#12 暂口径聚合不实现(items 按人汇总不聚合验证) 6

运行:uv run pytest tests/integration/test_{calc,settlements}.py tests/unit/test_formula.py -q → 32 passed(2026-10-16);全量回归见运行记录。

M3 增量批次(2026-10-16 完成:公司信息 / 二次分配录入 / 审批框架)

文件 守什么 用例数
tests/integration/test_settings.py 公司信息单行语义(PUT 只改名称不增行);public 端点无令牌 200 且字段白名单仅 2 项(复用 admin schema 即泄漏面);名称空 400;非 admin 403;logo 三重校验(伪造扩展名被 magic bytes 拦 / gif 拒 / 超 2MB 拒);合法 png 上传回读 content-type;匿名上传 401;修改留审计日志 11
tests/unit/test_secondary_allocation_schema.py 比例边界矩阵(0 拒 / 0.01-100 过 / 100.01 拒 / 负数拒 / 三位小数拒绝不静默舍入 / 非数字拒);人员 ID 非正拒;创建/编辑两入参的参与人去重(uk 前置友好提示);不同参与人合法 13
tests/integration/test_secondary_allocation.py 带方案创建详情回显(personnel_name 冗余固化:离职后回显有名);不建方案详情空且无 hint;合计满额 hint 只报合计无「余额」噪音;离职人员参与 400「分摊参与人员不存在或已离职」;终态业务禁改方案 400;编辑全量替换与空数组清空;被方案引用人员禁删(RESTRICT→400)且方案回显有名;删除业务后方案 CASCADE 随删、人员可删 9
tests/integration/test_approval_flow.py 开关关闭(种子默认 0):提单即两级「自动通过」留痕(status=3、steps 两条 action=3、operator 记「系统」——留痕不断链);方案为空不提单;开关开启两节点流转(1→财务通过→2→领导通过→3);财务驳回整体 status=4 且意见留痕;驳回缺意见 400;节点准入:领导/admin 不可代审财务节点(403);终态单重复操作 400;再编辑撤旧建新(旧单 status=5 + node=0/action=4 撤回留痕);模块准入(normal/marketing_ops 403,finance/leader 200)。开关切换经参数变更批次端到端,不直改库;用后 conftest 恢复种子基线 11
tests/unit/test_param_registry.py(改) 注册表 16→18 项(V6 首例 BOOL 两个审批开关);BOOL 校验边界专项(0/1 过、true/2 拒) +1
tests/integration/test_schema_sync.py(改) 角色种子断言 2→5 角色(marketing_ops/finance/leader 入列) 参数化

运行:uv run pytest tests/integration/test_{settings,secondary_allocation,approval_flow}.py tests/unit/test_secondary_allocation_schema.py -q → 44 passed(2026-10-16);全量回归 uv run pytest -q → 697 passed(基线 625 + 新增 72,5:10)。

报表统计(M4 ⑤,2026-10-19 后端完成)

文件 守什么 用例数
tests/integration/test_reports.py 个人明细与台账逐笔一致 + pageSize=1 时 summary 仍为全筛选合计;部门汇总团队基数=成员绩效合计、分成按参数、无成员经理出 0 值行;无台账经理挂上级不 500、仅有调整单人员并入团队/未分配、经理个人户开关两侧口径、按经理筛选不误判其他团队下属、对账恒等式;应发与月结 total_payable 对拍(两位经理各计一次 5%)、有调整单时等于月结+调整净额、筛选态只汇总展示团队;导出页眉 ID→名称/中文标签、回款维度 sheet 透传筛选、导出超限与取数异常的失败审计、非法月份查询/导出均 400;回款统计 8 档桶恒在、空桶为 0、二次分摊拆行时回款金额只计一次;业务统计排除经理聚合行;普通用户 403 27
tests/unit/test_report_helpers.py 区间分桶边界与 8 档编码唯一;标签覆盖全部枚举;Excel 模板页眉/签字栏/空数据/金额格式/合计/公式注入/维度 sheet/状态中文;部门汇总签字稿五块顺序、零成员团队与未分配行、5% 按计入次数累计、应发合计取对账口径、聚合行舍入差单列、比例标题参数化 17
tests/unit/test_month_validation.py 非法月份(2026-13/2026-00/0000-01 等)在报表、月结、调整单 schema 入口即拒绝;可选月份空串规范为 None 17

运行:uv run pytest tests/integration/test_reports.py -q → 27 passed(2026-10-20 复核);uv run pytest tests/unit/test_report_helpers.py tests/unit/test_month_validation.py -q → 34 passed;全量 862 passed(2026-10-20,11:32)。

e2e 实跑(2026-10-20):frontend/e2e/report_flow.py 12/12 通过。回款统计改为展开目标桶后按本次业务编号定位下钻行(不依赖桶累计值,可重复运行);另覆盖多桶同时展开、部门下钻筛选、打印截断提示、部门汇总打印五块结构。

工作台(M4 ⑧,2026-10-19 完成)

文件 守什么 用例数
tests/integration/test_dashboard.py 权限 403 且文案「无权访问工作台」;总览绩效合计与台账一致 + 非法月份 400;待办(月结提交→待审批命中、回款未指定批次命中、同步未建显式降级);风险预警(审批超时命中、参数关闭后不产生条目);最近动态来源操作日志;待办按角色过滤(财务/领导/多角色/admin)+ 真实流转;回款逾期命中与阈值参数生效;终止业务排除;warnings.total 不受展示上限截断;回款趋势跨月归属 11
tests/unit/test_param_registry.py(改) 注册表 19→22 项(V10 预警规则三项);编码集合与迁移种子一致 改

运行:uv run pytest tests/integration/test_dashboard.py -q → 11 passed(2026-10-19 code-review 修复回归)。

实施中发现并修复:alembic upgrade --sql 在 Windows 控制台按 GBK 输出,重定向生成的 V10__*.sql 被 test_schema_sync 按 UTF-8 读取时抛 UnicodeDecodeError——导出命令须加 PYTHONIOENCODING=utf-8 PYTHONUTF8=1(已写入 backend/README)。

三、运行记录

日期 范围 用例数 结果 备注
2026-10-09 全部 120 ✅ 通过(4.99s) 阶段 1 骨架 + 阶段 2 数据库完成后首次全量运行
2026-10-09 全部 119 ✅ 通过(5.01s) ORM 由异步改回同步后回归;净减 1 例(sync_database_url 独立配置已合并)
2026-10-09 全部 184 ✅ 通过(25.46s) 登录鉴权模块完成后统一编写并执行该模块测试套件(新增 65 例)。集成用例需容器在跑
2026-10-10 全部 282 ✅ 通过(103.81s) 系统管理模块(用户/角色/日志)完成后统一编写该模块套件(新增 91 例:单元 48 / 集成 43)。含操作日志审计机制专项;e2e 发现并修复 replace_roles 唯一键缺陷(见四、缺陷表);集成用例需容器在跑
2026-10-10 全部 301 ✅ 通过(87.40s) code-review 缺陷修复后全量回归(新增/改造 19 例)。密码结构化脱敏、时区归一、日志详情两段式各有专项;集成套件因密码哈希模块级预计算提速(63s→42s)
2026-10-13 基础数据集成三文件 48 ✅ 通过(27.60s) 基础数据管理模块完成后统一编写(人员 20/产品 14/客户 14)。审计上下文内外的校验位置差异为本次新发现的缺陷类别(见下)
2026-10-13 业务数据集成四文件 70 ✅ 通过(54.73s,连跑两次幂等) 业务数据管理(后端接口)完成后统一编写(业务 25/发货 15/回款 16/评级 14)。全量回归 603 例通过
2026-10-13 全量回归 + 两个 e2e 605 ✅ 605 passed(208s);e2e 基础数据 13 步 + 业务全链路 16 步全过 M2 收尾:删除保护补齐(+2 用例)后最终回归 605;e2e 期间修复三处缺陷(见下表),均已用例锁定
2026-10-13 全部 603 ✅ 通过(5:17) T11 全量回归(单元 381 / 集成 222),含本模块 70 例
2026-10-15 全量回归 + e2e 639 ✅ 全部通过 code-review 32 条修复后全量回归(新增 34 例:跨位宽 3 / 参数预览与维度 3 / 审计重试 2 / 终态编辑与 quantize 2 / 产品保护 2+6 / 评级锁 1 / formula 钳制 1 等)。e2e 冒烟:登录 8 项 + 系统管理 13 项 + 基础数据 13 步 + 业务全链路 16 步全过(拦截器/正则收口/params 分页改造后复跑)
2026-10-17 M3-core 定点 32 ✅ 通过 ④ 核算引擎+月结完成(calc 13 / settlements 6 / formula 扩 12 / schemas 等)。金标准复算到分;重算版本链;月结锁定与审批联动
2026-10-17 全量回归 701+N ✅ 全部通过 M3-core 收尾回归(T1-T6:V7 四表+核算+月结+调整单;前端台账/月结两页 order17/18)。e2e 补 calc_settlement_flow 待下批
2026-10-16 全量回归 + 7 个 e2e 697 ✅ 697 passed(5:10);e2e 七脚本全过 M3 增量批次完成(+72 例:settings 11 / 分配 schema 13+集成 9 / 审批 11 / registry 与 schema_sync 断言扩充等)。e2e:登录 8 + 系统管理 13 + 基础数据 12 + 业务 16 + 参数 7 步回归全过;新增 settings_flow 4 步 / secondary_allocation_flow 6 步(首跑即抓出 public 缓存与 logo 清理两处缺陷,见下表)
2026-10-19 M3 定点(calc/settlements) 34 ✅ 通过(55s) code-review P0+P1 修复中:经理聚合比例/调整单即生效/进度登记重算 400/财务节点不提前锁定/锁定拦截三路径/回填与聚合落库/月末取参(+新增 test_settlement_helpers 5 例、test_calc_helpers 4 例锁定 _q2 HALF_UP)
2026-10-19 全量回归 780 ✅ 780 passed(8:53) M3 code-review 21 项修复后全量回归(P0 算错钱/不可用 6 项 → P1 锁定不变量与双轨漂移 7 项 → P2 健壮性 6 项 → calc_receipt 重构;新增回归约 20 例,含跨会话并入的 #12 聚合基数开关用例)。type-check 绿;e2e calc_settlement_flow 仍待补
2026-10-19 拆行收口批次 全量 788 ✅ 788 passed(9:20) 2026-10-19 业务答复落实完成:#14 首日取整 10‰(公式段/锚点不变,用例反转);#12 单笔侧开关消费(1=业务员递减计提);二次分摊拆行(参与人份额+负责人余额守恒,uk 三列版);权限分层(params 查看放开/写仅 admin);V8 迁移(审批开关默认开/撤试用期参数/增基数开关)。定点:calc 20 / settlements 13(含 #12 开关两档金标准 6.84→24.34)/ businesses 30(保存侧+3)/ formula 35。e2e calc_settlement_flow 交接并行会话
2026-10-19 报表统计修复回归 全量 847 ✅ 847 passed(10:13) M4 报表 code-review 15 项修复后全量回归(后端 8 + 前端 7):归属三集合/调整单全量并入/经理个人户拆分/对账出参、分块签字稿导出、导出审计覆盖取数、筛选页眉 ID→名称、月份校验 400、岗位只读选项。报表集成 24 / 报表单元 16 / 月份校验 17。type-check + build 绿;e2e report_flow.py 扩至 12 步,待 E2E_ADMIN_PASSWORD 实跑
2026-10-19 全部 852 ✅ 852 passed(9:17) M4 ⑧ 工作台完成(四区块实时聚合 + ECharts 5.6 前端 + V10 预警参数三项;新增 5 例;参数注册表 19→22;同步模块显式降级)。期间修复:alembic --sql 导出 GBK 导致 schema 同步误报
2026-10-19 code-review 修复回归 全量 862 ✅ 862 passed(9:36) M4 工作台 + 报表 3 必须 + 8 建议修复:普通用户落地页/守卫(报表集成 27、工作台集成 11、报表单元 17,新增 10 例);部门汇总 5% 按计入次数累计并与月结 total_payable 对拍、聚合行舍入差;工作台待办按角色过滤、逾期命中与阈值生效、终止业务排除、warnings.total 截断、趋势跨月归属。ruff 绿;type-check + build 绿
2026-10-20 e2e 全链路 — ✅ login 8 / system 13 / role_home 4 / report 12 / calc_settlement 8 重启后端加载新代码后实跑:普通用户落地页与受限页回退、报表四页/导出/打印、核算与月结链路全过。report_flow 回款统计改为展开目标桶后按本次业务编号定位明细,不依赖桶累计值,可重复运行
2026-10-20 全量回归 862 ✅ 862 passed(11:32) 工作台/报表修复提交后的全量复核;e2e 三脚本复跑:role_home 4/4、report 12/12、calc_settlement 8/8;calc 审批开关已还原为 1,无 e2e_role_* 残留
2026-10-19 单元全量 + 报表定点 484 ✅ 484 passed(45.94s);报表单元 11 例 0.70s M4 报表模块收尾(前端四页/打印视图/导出与单元测试补齐)。ruff 绿;前端 pnpm type-check 绿。报表集成 7 例记录见上节
2026-10-19 全部 806 ✅ 806 passed(7:59) M4 ⑤ 报表统计全量回归(报表新增 18 例:集成 7 + 单元 11;另含并行会话并入的 M3 前端修复与字体合规)。ruff 绿。e2e:calc_settlement_flow 8 步由并行会话跑通;report_flow.py 待 E2E_ADMIN_PASSWORD 运行

四、测试发现并修复的缺陷

日期 缺陷 影响 处理
2026-10-19 部门汇总导出/打印只展平成员行 团队分成、全公司 5% 块、未分配人员、零成员团队全部缺失——签字稿合计小于应发,财务按签字稿复核会漏发经理分成与未分配绩效 导出改为单 sheet 分块签字稿(团队/公司/对账/成员/未分配)+ 打印五块;应发公式与对账块由后端 Decimal 计算
2026-10-19 _member_rows 直接 member_map[pid] 索引 无台账行的营销经理自身挂上级时 KeyError → 部门汇总接口与导出 500(当月完全无台账也会触发) 归属只从 member_map/adjustments 生成;manager_role 覆盖所有成员引用的上级
2026-10-19 生效调整单只并入有台账人员 只有调整单、当月无回款的人被静默丢弃——补发/扣减金额不出现,按报表发放会少发 调整单人员补零值成员后参与团队/未分配归属;每人调整净额只计一次
2026-10-19 经理个人户成员行取该人全部绩效 团队基数只含个人户行,成员行却含普通行——基数≠成员合计,同一笔业绩在成员行与未分配重复 普通行与个人户分行记账;经理成员行只取个人户金额,普通行留在上级团队/未分配
2026-10-19 导出取数与超限校验在审计上下文之外 超限 400 与取数异常不留失败日志,与 M4 计划 T12「导出 400/失败审计」不符 _export_payload 移入 with audit;静态标题映射(AuditRecord frozen)
2026-10-19 月份只校验长度 2026-13 传到 calendar.monthrange 抛 ValueError → 500;导出手工构造模型抛裸 ValidationError 同样 500 新增 core/month.py(格式+01-12+年份);四类 query、月结、调整单接入;导出 ValidationError 转 400
2026-10-19 前端报表五处状态缺陷 打印超限判定恒假(静默截断)、部门下钻 query 未读取、回款多桶共用一个 drill 状态(面板串行)、summary 在请求序号守卫前写入(合计串号)、e2e 全页行数选择器必失败 分别修正判定/读取 query/按桶独立状态+数组参数/onData 回调/限定主桶表;补 e2e 多桶展开、下钻、截断、部门打印用例
2026-10-19 工作台权限收紧后守卫仍回退 /dashboard 普通用户登录/直连受限页被反复重定向到同一受限页 → 无法进入系统(导航被判定无限重定向) router/access.ts 统一准入与落地页解析(工作台 → 业务数据 → 回款);登录回跳经 canAccessPath 校验;菜单/面包屑复用同一判定
2026-10-19 部门汇总应发只计一次全公司 5% 月结每位有聚合行的经理各计一次 5%,签字稿少计 N-1 份;导出/打印金额小于月结应发 团队行增 has_aggregate/aggregate_amount(优先冻结聚合行);对账改按聚合额求和,新增「聚合行舍入差」吸收分列 q2 尾差;补「与 total_payable 对拍」用例
2026-10-19 工作台待办未按角色过滤 财务看得到「待领导审核」、领导看得到「待财务复核」,与 FR-034「仅看到与自己职责相关」不符 pending_statuses_for_roles 按角色映射在途节点;repository 参数化 statuses;admin 保留全部在途视角
2026-10-09 日志脱敏在 logging.Filter 阶段改写 record.msg 抹掉 %s 占位符 → logging 内部 msg % args 抛 TypeError → 整条日志丢失(比不脱敏更危险) 改为 SensitiveFormatter 在格式化之后脱敏;补回归用例
2026-10-09 PasswordHash.recommended() 在 pwdlib 0.3.x 推荐 Argon2 而非 BCrypt 密码算法与规范不符,且随库版本变化——同一批密码可能混用两种算法 改为显式 PasswordHash((BcryptHasher(),)) 钉死算法
2026-10-09 模型未设置表级 comment,update_time 用 server_onupdate 违反规范红线;server_onupdate 只是 ORM 标记,不生成 ON UPDATE CURRENT_TIMESTAMP 子句,更新时间永不刷新 补表注释;改用 text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
2026-10-09 pymysql 未带 [rsa] extra,cryptography 缺失 MySQL 8.0 默认 caching_sha2_password,容器重启后首次连接走全量认证时抛 RuntimeError,且报错在建连阶段易被误读为「密码错误」 依赖改为 pymysql[rsa]
2026-10-09 setex 在 redis-py 8 已弃用 规范明确禁用弃用 API;弃用接口在未来大版本会被移除 验证码与黑名单改用 set(key, value, ex=…)
2026-10-09 测试中 TTL 断言写死等值 属测试自身缺陷:Redis 的 TTL 以毫秒精度存绝对过期时刻、返回时向下取整,刚写入就查询必然可能少 1 秒——会变成偶发失败的闪烁测试 断言改为容忍 1 秒偏差,并注明原因
2026-10-09 验证码沿用 captcha 库默认渲染 随机彩色字符 + 重噪点,与界面视觉体系冲突;该库最后更新于 2025-03,内部还引用了 Pillow 的旧别名 改为自绘(core/captcha_image.py):配色与界面统一,难度由逐字旋转、抖动、干扰曲线与噪点保证;仅沿用其内置字体文件
2026-10-09 噪点用 ellipse 绘制 3×3 包围盒画出的椭圆恰好填充中心与四个边中点,形状就是「+」,形成规则的十字纹理——既显眼又容易被整体滤波抹掉,起不到干扰作用 改用单像素点与 2×2 方块
2026-10-09 tests/unit/test_jwt.py 两处赋值了未使用的 settings(ruff F841) 变量未被引用,属无效代码 删除赋值;该文件的 get_settings 导入仍被其他用例使用
2026-10-10 replace_roles 先清后建但中间未落库 SQLAlchemy 在同一 flush 中先处理 INSERT 再处理 DELETE,编辑用户保留既有角色时重插同 (user_id, role_id) 撞唯一键(1062)——编辑用户接口 500。由前端 e2e「编辑用户」步骤暴露:集成测试当时只覆盖了「换掉全部角色」(无交集)的路径 clear() 后立即 session.flush() 把删除落库,再追加新集合;补集成回归用例(编辑时保留既有角色)
2026-10-10 审计脱敏依赖文本正则 三种逃逸形态:密码以空白开头时完全不脱敏、含空格/逗号只抹首段、含引号既残留明文又破坏 JSON——明文/残段写入 operation_logs.request_data 且日志页可直接展开查看(由 code-review 发现并逐条实证复现;既有测试所用密码均不含空格,恰好掩盖该路径) 改为按键名的结构化抹除(递归 dict/list 整值替换 ***),scrub 降级为文本兜底;补前导空白/空格/引号/嵌套/大小写五类边界用例
2026-10-10 .gitignore 的 logs/ 规则吞掉日志查询页目录 规则无前导斜杠、匹配任意层级:frontend/src/views/system/logs/ 整体被忽略,文件进不了版本库——干净克隆构建失败(本地因文件已在磁盘上而一切"正常",隐蔽性极强) 收窄为 /backend/logs/(该规则本意即后端运行日志);以 git check-ignore 实证不再命中
2026-10-10 时间筛选未做时区归一 混用 aware/naive 抛 TypeError 穿透成 500;带时区输入被驱动静默丢弃偏移(查询窗口偏 8 小时且无提示) LogQueryBase 加 field_validator:aware → astimezone() 本地 naive、naive 原样;补 4 例专项
2026-10-10 审计时序两处缺陷 ① 失败路径 rollback 后读 operator 触发额外查询,账号并发被删时 ObjectDeletedError 被兜底吞掉、失败审计静默丢失;② 成功/失败两分支 8 个实参重复展开,且 role/user service 在 commit 之后仍做查询与响应构造——后置查询一旦抛错,「已提交成功」会被记成失败 operator 标量进入时固化;审计链 DTO 化(audit 4 参 / _safe_write 2 参 / add 2 参);service 的 commit 后查询与响应构造全部前置
2026-10-13 基础数据三 service 的前置校验在审计上下文之外 校验失败(岗位停用/经理自引用/分类不合法等 400)完全不留操作日志,违背「成功与失败都记」的既定原则——与 2026-10-10 审计时序缺陷同源,新写的 service 又犯了(各自独立实现时口径漂移) personnel/product/customer 三 service 的值域与引用校验统一移入 audit_service.audit 上下文内;集成用例锁定(审计成功与失败双记录)
2026-10-13 change_status 状态留痕的 old_status 取到赋值后的 ORM 属性 时间线记成 old==new(如 1→3 的变更记成 3→3)——「变更前状态」无法回溯,状态流转追溯失效。由集成用例断言 (old,new)==(1,3) 抓住 赋值前固化 old_status = business.status 再流转;应用日志一并修正;回归用例锁定
2026-10-13 客户/人员删除保护漏检业务引用(T4 占位注释「待业务表就位后补充」在 T7 交付后无人补齐) 被业务引用的客户、被业务归属(负责/协助)引用的人员删除 → FK RESTRICT 穿透成 500。由 e2e 清理步骤暴露(集成测试只覆盖了 T2/T4 当时的引用面,业务表就位后引用面扩大未回看) customer 删除加 count_business_refs 检查 + IntegrityError 兜底 400;personnel 删除加 count_business_roles_refs(负责/协助合并统计);补 2 个集成用例锁定;文案更新「客户归属或业务归属引用」
2026-10-13 单价列(DECIMAL(14,4))被两位小数校验拒收(前后端同源) 编辑任何带精度的历史单价(回显 "5000.0000")被前端 AMOUNT_PATTERN 与后端 _check_amount 双双拒绝——创建后单价带小数位的记录永远无法编辑。由 e2e「编辑业务」步骤暴露(创建用整数、编辑回显带 4 位小数,恰好只在这条路径相遇) 后端 _check_amount 加 scale 参数(金额/数量 2 位、单价类 4 位);前端业务/发货明细行单价类放行 4 位;集成用例锁定
2026-10-13 发货 freight_amount 漏做金额校验 负数/三位小数/超容量的运费直接落库(schema 对 quantity/sale_price 都复用了 _check_amount,唯独运费没套用),业务运费合计与报表随之错乱 shipments.py schema 的 Create/Update 加 field_validator("freight_amount")(同套口径,文案「运费金额不能为负数 / 最多两位小数」);集成用例锁定
2026-10-15 计算预览把 ‰ 存储值当纯比例相乘(企业户/信息员分支) enterprise.rate=3(3‰)、info.rate=1(1‰)未经 ÷1000 直接乘——绩效金额放大 1000 倍(15000×3=45000)。既有测试只覆盖业务员路径(salesperson_rate 内部有 ÷1000),两分支零覆盖使缺陷存活;该预览是 M3 核算共用契约 两分支显式 /1000 并注释声明换算;补企业户/信息员精确数值用例锁定
2026-10-15 编号撞唯一键的重试写在 audit 上下文内 return _retry_create(业务/发货/回款三处同构) 吞异常后正常退出被记 success=True(假成功审计,业务实际已回滚);重入整个 create_* 违反「不支持嵌套」约定;_NO_RETRY=1 死常量致持续冲突时无界递归至 RecursionError 改为 audit 上下文内 for 循环(上限 _NO_RETRY),rollback 后重新锁行与取号;新增集成用例锁定「成功/最终失败各恰好一条日志」
2026-10-15 单号 MAX 检索用 VARCHAR 字典序(no_generator) 序号破 4 位宽度(9999→10000)后字典序反转("-10000"<"-9999"),MAX 恒取旧值 → seq 恒重算同一号码 → 当日该前缀所有建单持续撞唯一键 排序改「长度优先、同级字典序」(同前缀下长度大即数值大);跨位宽/同宽/空表三用例锁定;param 版本号一并复用
2026-10-15 编辑业务全程无锁读 + 无终态门槛 ① 「有发货禁换客户」走无锁快照读,与发货/回款写路径(FOR UPDATE)不互斥——并发新增发货可旁路保护;② 已完成/已终止业务仍可全量改明细并重算 M3 核算基线(发货/回款服务禁写终态,父单据反而放行) get_with_names 加 for_update;update 补终态门槛;前端编辑按钮终态禁用;has_docs 单点收口(update/delete 共用)
2026-10-15 删除产品无引用保护(docstring「待 V5 补充」在 V5 就位后未兑现) 被业务/发货明细引用的产品删除 → FK RESTRICT 穿透成 500(与 2026-10-13 客户/人员删除保护漏检同源——「占位注释交付后无人回看」第二次发生) count_refs(两明细表)+ IntegrityError 兜底 400;全局注册 IntegrityError handler 兜底未来遗漏(漏网的引用冲突收敛 400 而非 500)
2026-10-15 产品 schema 私写校验不查小数位与容量 sale_price 用 _parse_non_negative(非负/有限),5 位小数被 Numeric(14,4) 静默截断(舍入即算错钱)、超容量 500——与 fba8cfc 修掉的「schema 小数位口径」同源残留 复用 _check_amount(PRICE_MAX+scale=4 / AMOUNT_MAX);6 个秒级 schema 单测锁定
2026-10-15 行金额乘积(数量2位×单价4位=6位小数)直写 DECIMAL(14,2) MySQL 静默舍入且回显漂移:创建响应读内存值 "5.166615"、复查读库值 "5.17"——同一行金额前后显示不一致 显式 quantize(HALF_UP) 固化(与 MySQL 舍入方向一致);回显/库值一致性用例锁定
2026-10-15 先款后货(payment_days 为负)计提比例线性外插 比例 = 起始 + |天数|×斜率,高于起始值甚至超 100%——M3 接入真实回款后直接多算绩效工资(预览被 schema 拦住、回款登记不校验日期先后,缺陷在 M3 前不可见) salesperson_rate 钳制 days≥0(按起始比例计提;payment_days 出参仍照实输出负值,口径经用户 2026-10-15 裁决)
2026-10-15 评级区间重叠先查后写无锁、无唯一键兜底 两管理员并发提交重叠区间互判不重叠而双双入库——同一总分命中多等级且结果取决于插入顺序 判重叠的 list_all 加 FOR UPDATE 锁全表行串行化;只读直出不加锁
2026-10-15 岗位维度值大小写校验(ci)与快照匹配(精确)不对称 API 提交 "SalesPerson" 校验通过入库,快照字典按精确键取值不命中——静默按错误参数算钱且无报错;param_code 变形大写则 get_spec 抛 ValueError 成 500 校验命中后一律以库里规范值落库(role_code/definition.param_code);批量预载字典键 casefold 归一保持等价命中
2026-10-16 logo 上传同扩展名覆盖时先写后删 save_logo 在写入新文件之后清理旧扩展名——同扩展名重传时把刚落盘的新文件一并删掉,回读 404(首次单跑通过、集成连跑即暴露,依赖「库里已有同扩展名旧记录」的前置状态) 清理旧扩展名移到写新文件之前且仅异扩展名执行;集成连跑两遍幂等锁定
2026-10-16 public 端点 300s 响应缓存让登录页拿旧公司名 公司名保存后退出登录,登录页命中浏览器缓存副本仍显示旧品牌名(设置页「保存后生效」承诺失效)——由 settings_flow e2e 首跑暴露(新标签页直接打开正常、退出回跳的登录页异常) 登录页改用带时间戳的原生 fetch + cache: no-store 绕过磁盘缓存(axios 对响应 max-age 无能为力);手动解包统一信封
2026-10-16 V6 迁移 downgrade 保留 autogenerate 的逐个 drop_index 且漏删行级种子 外键唯一支撑索引单独删除报 MySQL 1553 回滚中断;roles/param_definitions/param_values 的行级种子(表由 V1/V3 建)drop_table 不会带走——重放 upgrade 时唯一键冲突 downgrade 只保留 drop_table(V1/V3 先例)+ 三条 DELETE 反向清理行级种子;downgrade→upgrade 往返实测通过
2026-10-19 经理团队聚合分成放大 100 倍(settlement_service) 参数按百分数存储(12.5=12.5%),parse_fraction 后调用方又 ×100、manager_team_share 内部再 ÷100——净效应「绩效×12.5」;单元测试直传 Fraction 未覆盖调用方换算使缺陷存活 去掉调用方 ×100;rate_text 改用实际参数原文(原硬编码默认值失真);集成用例两档金标准(6.84/9.78 vs 错误值 684.43)锁定
2026-10-19 审批联动不判终审(approval_service.act) 财务节点通过(1→2)即 mark_locked 月结/调整单置生效——领导驳回后 locked_at/locked_by 残留、两级审批形同虚设(锁定后源数据写路径立即被拦) 生效类联动收敛到 final_approved(终态才触发);驳回任意节点即时;mark_rejected 补清锁定字段;用例断言财务通过后仍 status=2 且无锁定信息
2026-10-19 调整单创建完全不可用(两处叠加) ① ENABLE_CODE_BY_TYPE 漏登记 type=3 → is_enabled 恒 400「未知的审批对象类型」;② 修复①后暴露:自动生效分支手工插 ApprovalStep(order_id=调整单id),而该外键指向 approval_orders,必然 errno 1452 补登记 approval.adjustment.enabled;自动通过留痕统一交 submit_order(不再手工插步);新建调整单集成用例锁定
2026-10-19 重算绕过业务级一次性计提防线 「进度登记」回款(#25 第二笔及以后)无独立台账行,重算会凭空落一行生效行——同业务两行生效行、绩效按两倍计入工资 无旧版本行一律 400「进度登记无可重算」;用例锁定「重算后同业务仍只有一行生效行」
2026-10-19 前端重算传错 ID(台账页) 把台账行主键当 receipt_id 传给重算接口——会作废并重算无关回款的台账(若目标回款已核算),或报错无法重算目标行 改传 row.receipt_id(聚合行 receipt_id 空值防御);LedgerRow 本含该字段
2026-10-19 quantize 缺省银行家舍入(9 处金额固化) docstring 声称 HALF_UP,实际未传 rounding 走 ROUND_HALF_EVEN——x.xx5 舍成偶数尾数,与业务四舍五入口径差 1 分(台账固化金额比应发少 1 分) 9 处显式 rounding=ROUND_HALF_UP;_q2 提为模块级并单测锁定(2.345→2.35)
2026-10-19 月结锁定拦截只覆盖 1/4 写路径 仅新增回款被拦;回款编辑(可跨月搬数)/删除、发货增/改/删、核算与重算全部放行——锁定月份快照与源数据脱钩且无留痕 五处写路径补齐 ensure_month_not_locked(编辑按原月+新月双查);拦截先于「被引用/已核算」判定;三用例锁定
2026-10-19 台账 settlement_id 回填从未发生(死代码) update_ledger_settlement_id 全库零调用——「未入结」判据恒真,重生成会把已固化行反复纳入(或补上回填却不先解绑导致漏算整月) generate 回填 + 重生成前解绑(新增 clear_ledger_settlement_id);用例断言回填与重生成金额不翻倍
2026-10-19 经理聚合行从不落库 _build_manager_aggregates 只构造内存对象参与 items 汇总——台账表永远无 calc_type=2 行,报表下钻(business_id 锚点)落空 聚合行经 repository 落库并回填 settlement_id;重生成先作废旧聚合行(deactivate_month_aggregates);_confirm_scope 限定 calc_type=1 防聚合行混入单笔池
2026-10-19 月结取参用生成日(date.today) 补生成历史月份时经理分成/月工资口径/试用期阈值按生成日参数计算——同一月份两次生成金额不同,违背「参数按生效日期时点化」红线 新增 _month_end(所属月月末)为聚合与月工资口径取参锚点;单测 5 例 + 集成用例(10-25 预约变更影响 10 月聚合)
2026-10-19 迁移双轨三处漂移 ① V6 就地修改加入 uk 致旧库补不上(V8 也未补)→ V9 条件式补齐(幂等裸 SQL,临时库验证两分支);② V7 留存 SQL 约束/索引名单数(fk_performance_ledger_*)与 alembic op.f/init-dev 漂移;③ V8 迁移 revision 缺类型注解,test_schema_sync 的 head 推断静默漏检 V8、init-dev 版本号落后未被发现 V9 迁移 + V7 SQL 名修正 + V8 注解修正 + init-dev 版本推进;schema 同步测试恢复全量断言
2026-10-19 运维脚本两处(restore_drill/health_check) ① 行数核对把查询失败回显 '?' 当数据比较('?'=='?' 判 ✓)且 TABLES 缺 V7 四表等 16 张——备份漏表时演练全绿误报「可恢复」;② redis-cli -a "$REDIS_PASSWORD" 密码进宿主进程命令行 TABLES 补全 32 张业务表 + '?' 一律判 ✗;Redis 探活改容器内环境变量展开(compose 补 REDIS_PASSWORD),实测 PONG
2026-10-19 调整单金额无校验 / INT 参数原文 int() / logo 清理裸抛 ① amount 负数反转补发-扣减语义、'abc' 直落库 500;② int('360.0') 使整月核算 500(INT 参数合法入库原文);③ 旧 logo 清理 OSError 裸抛把成功更新记成失败审计并 500 schema 正则 + 服务层 Decimal>0;int(parse_fraction(...));清理包 try 仅 warning;各补回归用例

五、待补用例

模块 状态 说明
基础数据管理 ✅ 已完成 2026-10-13 完成(48 例,见上表);「被业务数据引用」的删除保护已随 T11 的业务/发货禁删用例覆盖
业务数据管理 ✅ 后端已完成 2026-10-13 完成(70 例,见上表);前端页面与绩效核算联动(M3 台账核销回写 settlement_status)待 M3 补测
M3 增量(设置/分配/审批) ✅ 已完成 2026-10-16 完成(44 例,见上表);审批流的月结审批对象(order_type=2)随 ④ 月结子块接入时补测
绩效工资核算(M3-core) ✅ 已完成 2026-10-17 完成(核算/月结/调整单,见运行记录);2026-10-19 code-review 21 项修复并补回归约 20 例;2026-10-20 e2e calc_settlement_flow 8/8 实跑通过(台账/月结真实浏览器链路)
报表统计 ✅ 已完成 2026-10-19 完成并补齐回归;2026-10-20 e2e report_flow 12/12 实跑通过,全量 862 passed

系统管理模块(用户/角色/日志查询)已于 2026-10-10 完成并补齐用例,见上表。