合同管理模块业务流程测试用例
生成日期:2026-03-26 模块名称:yudao-module-contract 文档用途:以完整业务场景串联各子功能的集成测试用例
一、测试环境说明
| 项 | 说明 |
|---|---|
| 后端地址 | http://localhost:48080/admin-api |
| 前端地址 | http://localhost:5800 |
| 测试账号 | admin / admin123 |
| 租户ID | 1 |
| 前置条件 | 数据库已初始化字典(contract_type/contract_nature/contract_status 等)、菜单数据 |
二、测试场景概览
本测试用例设计 3 个完整业务场景,每个场景贯穿合同全生命周期的不同路径:
| 场景 | 合同类型 | 覆盖流程 |
|---|---|---|
| 场景 A | 销售合同(ERP 系统) | 配置 → 分类 → 模板 → 创建 → 明细行 → 收款计划 → 提交审批 → 审批通过 → 履约 → 统计验证 |
| 场景 B | 采购合同(设备采购) | 创建 → 提交审批 → 审批通过 → 合同变更 → 变更提交 → 履约(交付+付款) → 统计验证 |
| 场景 C | 服务合同(咨询服务) | 创建草稿 → 修改 → 删除 → 重新创建 → 搜索 → 导出 → 关闭 |
三、场景 A:销售合同全生命周期
业务故事:宇擎科技向客户"深圳创新科技"销售 ERP 系统,签订 58 万销售合同。合同经审批生效后,按 3 期收款,客户分阶段付款。全程跟踪履约进度。
A-01 合同配置
| 项 | 内容 |
|---|---|
| 前置条件 | 系统已部署启动 |
| 操作入口 | 合同管理 → 合同配置 |
测试步骤
| # | 操作 | 请求 | 预期结果 |
|---|---|---|---|
| 1 | 获取当前配置 | GET /contract/config/get | 返回默认配置或已有配置 |
| 2 | 保存配置:编号前缀=HT、日期格式=yyyyMMdd、流水号位数=3、到期提醒=开启、提醒天数=30 | PUT /contract/config/save Body: {"codePrefix":"HT","codeDateFormat":"yyyyMMdd","codeSeqLength":3,"notifyEnabled":true,"notifyDays":30} | code=0,保存成功 |
| 3 | 再次获取配置验证 | GET /contract/config/get | 返回的字段值与步骤 2 一致 |
前端验证
| # | 操作 | 预期结果 |
|---|---|---|
| 4 | 浏览器打开合同配置页 /contract/contract-config | 表单回显:编号前缀=HT、日期格式=yyyyMMdd、流水号位数=3、到期提醒开关=开启、天数=30 |
| 5 | 修改提醒天数为 15,点击"保存配置" | 提示保存成功,刷新后回显 15 |
A-02 合同分类管理
| 项 | 内容 |
|---|---|
| 前置条件 | A-01 完成 |
| 操作入口 | 合同管理 → 合同分类 |
测试步骤
| # | 操作 | 请求 | 预期结果 |
|---|---|---|---|
| 1 | 创建顶级分类"销售合同" | POST /contract/category/create Body: {"name":"销售合同","code":"SALE","parentId":0,"sort":1,"status":0,"remark":"面向客户的销售类合同"} | code=0,返回分类 ID(记为 catSaleId) |
| 2 | 创建子分类"软件销售"挂在"销售合同"下 | POST /contract/category/create Body: {"name":"软件销售","code":"SALE_SW","parentId":${catSaleId},"sort":1,"status":0} | code=0,返回 catSaleSwId |
| 3 | 查询分类列表 | GET /contract/category/list | 返回列表含"销售合同"和"软件销售",parentId 关系正确 |
| 4 | 重复创建"销售合同" | POST /contract/category/create Body: {"name":"销售合同","code":"SALE2","parentId":0,"sort":2,"status":0} | code != 0,提示"合同分类名称已存在" |
| 5 | 获取精简列表 | GET /contract/category/simple-list | 返回 id/name/parentId 结构,可构建树 |
前端验证
| # | 操作 | 预期结果 |
|---|---|---|
| 6 | 打开合同分类页面 /contract/contract-category | 树形表格正确展示,"销售合同"展开后显示"软件销售" |
| 7 | 点击"销售合同"行的"新增下级"按钮 | 弹出表单,父分类自动回填为"销售合同" |
| 8 | 输入名称"实施服务"、编码"SALE_IMPL",点保存 | 新增成功,表格刷新后"销售合同"下多出"实施服务" |
| 9 | 编辑"软件销售",修改排序为 2 | 保存成功,排序变更 |
| 10 | 尝试删除"销售合同"(有子分类) | 提示"存在子分类,无法删除" |
A-03 合同模板管理
| 项 | 内容 |
|---|---|
| 前置条件 | A-02 完成,已有分类"软件销售" |
| 操作入口 | 合同管理 → 合同模板 |
测试步骤
| # | 操作 | 请求 | 预期结果 |
|---|---|---|---|
| 1 | 创建模板"标准销售合同模板" | POST /contract/template/create Body: {"name":"标准销售合同模板","categoryId":${catSaleSwId},"description":"适用于一般商品和软件销售场景","status":0,"sort":1} | code=0,返回模板 ID |
| 2 | 查询模板分页 | GET /contract/template/page?pageNo=1&pageSize=10 | 列表含"标准销售合同模板",分类显示正确 |
| 3 | 更新模板状态为停用 | PUT /contract/template/update Body: {"id":${templateId},"status":1} | code=0 |
| 4 | 再次查询,按 status=0 过滤 | GET /contract/template/page?pageNo=1&pageSize=10&status=0 | 不含被停用的模板 |
前端验证
| # | 操作 | 预期结果 |
|---|---|---|
| 5 | 打开合同模板页面 /contract/contract-template | 列表正确展示模板名称、合同分类、排序、状态、说明 |
| 6 | 使用搜索条件"模板名称=标准"进行搜索 | 列表过滤只显示含"标准"的模板 |
| 7 | 点击"新增模板",填写名称、选择分类、输入说明 | 保存成功,列表刷新 |
A-04 创建销售合同(含明细行)
| 项 | 内容 |
|---|---|
| 前置条件 | A-02、A-03 完成 |
| 操作入口 | 合同管理 → 合同台账 → 新增合同 |
测试步骤
| # | 操作 | 请求 | 预期结果 |
|---|---|---|---|
| 1 | 生成合同编号 | GET /contract/info/generate-code | 返回格式为 HT + 日期 + 随机数,如 HT202603261234 |
| 2 | 创建合同(含明细行) | POST /contract/info/create Body 如下 | code=0,返回合同 ID(记为 contractAId) |
步骤 2 请求体:
json
{
"contractCode": "HT20260326A001",
"contractName": "宇擎ERP系统销售合同",
"categoryId": "${catSaleSwId}",
"contractType": 1,
"contractNature": 1,
"partyACompanyName": "宇擎科技有限公司",
"partyBName": "深圳创新科技有限公司",
"partyBContact": "张三",
"partyBPhone": "13800138001",
"partyBAddress": "深圳市南山区科技园",
"totalAmount": 580000,
"currency": "CNY",
"signDate": "2026-03-26",
"startDate": "2026-04-01",
"endDate": "2027-03-31",
"ownerUserId": 1,
"ownerUserName": "宇擎源码",
"deptId": 103,
"deptName": "研发部门",
"remark": "含软件许可+首年维保",
"items": [
{"itemName": "ERP标准版软件许可", "spec": "V5.0", "unit": "套", "quantity": 1, "unitPrice": 400000, "amount": 400000, "remark": "含50用户授权"},
{"itemName": "首年维保服务", "spec": "12个月", "unit": "年", "quantity": 1, "unitPrice": 80000, "amount": 80000},
{"itemName": "数据迁移服务", "spec": "一次性", "unit": "项", "quantity": 1, "unitPrice": 100000, "amount": 100000}
]
}| # | 操作 | 请求 | 预期结果 |
|---|---|---|---|
| 3 | 查询合同详情 | GET /contract/info/get?id=${contractAId} | 基本信息完整,items 列表含 3 条明细行,金额合计 580000 |
| 4 | 验证合同状态 | — | status=0(草稿)、processStatus=-1(未发起) |
| 5 | 重复编号创建 | POST /contract/info/create 相同编号 | code != 0,提示"合同编号已存在" |
前端验证
| # | 操作 | 预期结果 |
|---|---|---|
| 6 | 打开合同台账列表 /contract/contract-info-list | 新合同显示在列表中,状态为"草稿" |
| 7 | 点击合同编号链接 | 跳转到合同详情页 /contract/contract-info-detail?id=xxx |
| 8 | 详情页检查 | 基本信息表单回显正确(合同名称、类型、性质、甲乙方、金额、日期等);合同明细表格显示 3 行 |
A-05 合同收款计划
| 项 | 内容 |
|---|---|
| 前置条件 | A-04 完成,合同 ID = contractAId |
| 操作入口 | API 调用 / 合同详情页 |
测试步骤
| # | 操作 | 请求 | 预期结果 |
|---|---|---|---|
| 1 | 创建第 1 期收款计划 | POST /contract/payment-plan/create Body: {"contractId":${contractAId},"period":1,"planAmount":174000,"planDate":"2026-04-20","status":0,"remark":"首付款30%"} | code=0,返回计划 ID |
| 2 | 创建第 2 期 | POST /contract/payment-plan/create Body: {"contractId":${contractAId},"period":2,"planAmount":232000,"planDate":"2026-07-31","status":0,"remark":"中期款40%"} | code=0 |
| 3 | 创建第 3 期 | POST /contract/payment-plan/create Body: {"contractId":${contractAId},"period":3,"planAmount":174000,"planDate":"2026-10-31","status":0,"remark":"尾款30%"} | code=0 |
| 4 | 查询该合同收款计划列表 | GET /contract/payment-plan/list?contractId=${contractAId} | 返回 3 条记录,期次 1/2/3,合计金额 = 580000 |
| 5 | 更新第 1 期为已收款 | PUT /contract/payment-plan/update Body: {"id":${plan1Id},"actualAmount":174000,"actualDate":"2026-04-18","status":2} | code=0 |
| 6 | 再次查询,验证状态 | GET /contract/payment-plan/list?contractId=${contractAId} | 第 1 期 status=2(已收付),actualAmount=174000 |
| 7 | 删除第 3 期计划 | DELETE /contract/payment-plan/delete?id=${plan3Id} | code=0 |
| 8 | 查询验证删除 | GET /contract/payment-plan/list?contractId=${contractAId} | 仅剩 2 条 |
A-06 提交合同审批
| 项 | 内容 |
|---|---|
| 前置条件 | A-04 完成,合同处于草稿状态 |
| 操作入口 | 合同详情页 → 提交按钮 |
测试步骤
| # | 操作 | 请求 | 预期结果 |
|---|---|---|---|
| 1 | 提交合同 | POST /contract/info/submit Body: 同 A-04 步骤 2 | code=0,返回合同 ID |
| 2 | 查询合同状态 | GET /contract/info/get?id=${contractAId} | status 变为审批中(1)或已生效(4,视流程配置),processInstanceId 不为空 |
| 3 | 再次提交同一合同 | POST /contract/info/submit 相同 body | 应被拒绝或幂等处理 |
前端验证
| # | 操作 | 预期结果 |
|---|---|---|
| 4 | 合同列表中查看该合同 | 状态标签变为"审批中"或"已生效"(取决于是否配置了审批流程) |
| 5 | 合同详情页 | 表单变为只读模式(已提交后不可编辑),显示审批状态 |
A-07 合同履约记录
| 项 | 内容 |
|---|---|
| 前置条件 | A-06 完成,合同已生效 |
| 操作入口 | 合同管理 → 合同履约 |
测试步骤
| # | 操作 | 请求 | 预期结果 |
|---|---|---|---|
| 1 | 添加收款履约记录 | POST /contract/performance/create Body: {"contractId":${contractAId},"performanceType":4,"performanceAmount":174000,"performanceDate":"2026-04-18 10:00:00","performanceContent":"客户支付首付款","remark":"银行转账到账"} | code=0,返回履约 ID |
| 2 | 添加交付履约记录 | POST /contract/performance/create Body: {"contractId":${contractAId},"performanceType":1,"performanceAmount":400000,"performanceDate":"2026-05-15 14:00:00","performanceContent":"ERP系统交付部署完成"} | code=0 |
| 3 | 查询合同履约列表 | GET /contract/performance/list-by-contract?contractId=${contractAId} | 返回 2 条记录 |
| 4 | 查询履约汇总 | GET /contract/performance/summary?contractId=${contractAId} | 返回汇总数据,已履约金额 = 574000 |
| 5 | 查询全局履约分页 | GET /contract/performance/page?pageNo=1&pageSize=10 | 列表含上述 2 条,显示合同编号和名称 |
| 6 | 更新一条履约记录 | PUT /contract/performance/update Body: {"id":${perfId},"remark":"更新备注"} | code=0 |
| 7 | 删除一条履约记录 | DELETE /contract/performance/delete?id=${perfId} | code=0 |
前端验证
| # | 操作 | 预期结果 |
|---|---|---|
| 8 | 打开合同履约页面 /contract/contract-performance | 列表正确展示,履约类型字典渲染(收款/交付/付款等) |
| 9 | 点击"新增履约记录"按钮 | 弹出表单,合同选择下拉可搜索、履约类型下拉、金额输入、日期选择 |
A-08 统计分析验证
| 项 | 内容 |
|---|---|
| 前置条件 | A-01 ~ A-07 完成 |
| 操作入口 | 合同管理 → 统计分析 |
测试步骤
| # | 操作 | 请求 | 预期结果 |
|---|---|---|---|
| 1 | 获取统计概览 | GET /contract/stats/overview | totalCount >= 1,totalAmount 含 A 合同的 580000,各状态计数正确 |
| 2 | 获取月度统计 | GET /contract/stats/monthly?year=2026 | 2026-03 月份有签约数据 |
| 3 | 获取分类统计 | GET /contract/stats/category | "销售合同"/"软件销售"分类有数据 |
前端验证
| # | 操作 | 预期结果 |
|---|---|---|
| 4 | 打开统计分析页面 /contract/contract-stats | 概览卡片显示合同总数、各状态数量、合同总金额、已履约金额、待收付金额 |
| 5 | 月度签约统计表格 | 2026 年各月份的合同数量和金额正确 |
四、场景 B:采购合同 + 合同变更
业务故事:宇擎科技向联想采购服务器设备,签订 32 万采购合同。合同生效后,因业务扩展需求追加采购交换机,发起合同金额变更。变更审批通过后继续履约。
B-01 创建采购合同
| # | 操作 | 请求 | 预期结果 |
|---|---|---|---|
| 1 | 创建采购分类(如已有则跳过) | POST /contract/category/create Body: {"name":"采购合同","code":"PURCHASE","parentId":0,"sort":2,"status":0} | 返回 catPurchaseId,或提示已存在 |
| 2 | 创建采购合同 | POST /contract/info/create Body: {"contractCode":"HT20260326B001","contractName":"服务器设备采购合同","categoryId":${catPurchaseId},"contractType":2,"contractNature":2,"partyACompanyName":"宇擎科技有限公司","partyBName":"联想集团","partyBContact":"王五","partyBPhone":"13700137003","totalAmount":320000,"currency":"CNY","signDate":"2026-03-20","startDate":"2026-03-25","endDate":"2026-06-25","ownerUserId":1,"ownerUserName":"宇擎源码","deptId":103,"deptName":"研发部门","remark":"采购8台服务器","items":[{"itemName":"机架式服务器","spec":"Dell R750","unit":"台","quantity":8,"unitPrice":35000,"amount":280000},{"itemName":"企业级交换机","spec":"H3C S6520","unit":"台","quantity":2,"unitPrice":20000,"amount":40000}]} | code=0,返回 contractBId |
| 3 | 查验合同详情 | GET /contract/info/get?id=${contractBId} | 合同类型=2(采购),性质=2(支出),明细 2 行,合计 320000 |
| 4 | 提交审批 | POST /contract/info/submit 同上 body | code=0 |
B-02 创建合同变更
| 项 | 内容 |
|---|---|
| 前置条件 | B-01 完成,合同已审批通过 |
| 操作入口 | 合同管理 → 合同变更 → 新增变更 |
测试步骤
| # | 操作 | 请求 | 预期结果 |
|---|---|---|---|
| 1 | 创建变更申请 | POST /contract/change/create Body: {"contractId":${contractBId},"changeType":1,"changeReason":"业务扩展需要追加采购交换机","changeContent":"增加4台交换机,合同金额增加8万元","originalValue":"合同总额32万","newValue":"合同总额40万","remark":"追加采购需求"} | code=0,返回变更 ID(changeId),自动生成 changeCode |
| 2 | 查询变更详情 | GET /contract/change/get?id=${changeId} | changeCode 格式正确(BG+日期+序号),changeType=1(金额变更),关联 contractId 正确 |
| 3 | 按合同查变更列表 | GET /contract/change/list-by-contract?contractId=${contractBId} | 返回 1 条变更记录 |
| 4 | 查询变更分页 | GET /contract/change/page?pageNo=1&pageSize=10 | 列表含该变更,显示合同编号和名称 |
| 5 | 提交变更审批 | POST /contract/change/submit?id=${changeId} | code=0 |
| 6 | 查询变更状态 | GET /contract/change/get?id=${changeId} | processStatus 不再是 -1(已发起流程) |
前端验证
| # | 操作 | 预期结果 |
|---|---|---|
| 7 | 打开合同变更页面 /contract/contract-change | 列表显示变更单号、合同编号、合同名称、变更类型(金额变更)、审批状态 |
| 8 | 点击"新增变更" | 弹出表单,合同选择下拉可搜索,变更类型下拉,变更原因/内容/原值/新值可输入 |
| 9 | 未提交的变更行操作列显示"提交"和"删除"按钮 | 已提交的不显示删除 |
B-03 采购合同履约
| # | 操作 | 请求 | 预期结果 |
|---|---|---|---|
| 1 | 交付履约:服务器到货 | POST /contract/performance/create Body: {"contractId":${contractBId},"performanceType":1,"performanceAmount":280000,"performanceDate":"2026-04-10 14:00:00","performanceContent":"8台服务器到货验收,全部通过"} | code=0 |
| 2 | 交付履约:交换机到货 | POST /contract/performance/create Body: {"contractId":${contractBId},"performanceType":1,"performanceAmount":40000,"performanceDate":"2026-04-12 09:30:00","performanceContent":"2台交换机到货验收"} | code=0 |
| 3 | 付款履约 | POST /contract/performance/create Body: {"contractId":${contractBId},"performanceType":5,"performanceAmount":320000,"performanceDate":"2026-04-15 16:00:00","performanceContent":"全款支付"} | code=0 |
| 4 | 查询合同履约汇总 | GET /contract/performance/summary?contractId=${contractBId} | 已履约金额合计正确(含交付 + 付款) |
五、场景 C:服务合同 + 边界测试
业务故事:创建一份咨询服务合同草稿,测试修改、删除、重建、搜索、导出等操作。
C-01 草稿生命周期
| # | 操作 | 请求 | 预期结果 |
|---|---|---|---|
| 1 | 保存草稿 | POST /contract/info/save Body: {"contractName":"年度技术咨询服务","contractType":3,"contractNature":2,"partyBName":"麦肯锡咨询","totalAmount":90000,"currency":"CNY"} | code=0,返回合同 ID,自动生成编号 |
| 2 | 查看草稿状态 | GET /contract/info/get?id=${contractCId} | status=0(草稿),编号已自动生成 |
| 3 | 修改草稿 | PUT /contract/info/update Body: {"id":${contractCId},"contractName":"2026年度技术咨询服务合同","totalAmount":120000,"startDate":"2026-04-01","endDate":"2026-12-31"} | code=0 |
| 4 | 查验修改结果 | GET /contract/info/get?id=${contractCId} | 名称、金额、日期均已更新 |
| 5 | 删除草稿 | DELETE /contract/info/delete?id=${contractCId} | code=0 |
| 6 | 查验删除 | GET /contract/info/get?id=${contractCId} | code != 0,提示合同不存在 |
C-02 列表搜索与导出
| # | 操作 | 请求 | 预期结果 |
|---|---|---|---|
| 1 | 无条件分页查询 | GET /contract/info/page?pageNo=1&pageSize=20 | 返回合同列表,total > 0 |
| 2 | 按合同编号搜索 | GET /contract/info/page?pageNo=1&pageSize=20&contractCode=HT20260326A | 仅返回编号匹配的合同 |
| 3 | 按合同名称搜索 | GET /contract/info/page?pageNo=1&pageSize=20&contractName=ERP | 返回含"ERP"的合同 |
| 4 | 按合同类型搜索 | GET /contract/info/page?pageNo=1&pageSize=20&contractType=1 | 仅返回销售合同 |
| 5 | 按合同状态搜索 | GET /contract/info/page?pageNo=1&pageSize=20&status=0 | 仅返回草稿状态合同 |
| 6 | 按乙方名称搜索 | GET /contract/info/page?pageNo=1&pageSize=20&partyBName=联想 | 返回乙方含"联想"的合同 |
| 7 | 组合条件搜索 | GET /contract/info/page?pageNo=1&pageSize=20&contractType=2&partyBName=联想 | 返回采购合同且乙方含"联想"的数据 |
| 8 | 导出 Excel | GET /contract/info/export-excel?contractType=1 | 返回 Excel 文件流(Content-Type 含 spreadsheet),文件可正常打开 |
| 9 | 获取精简列表 | GET /contract/info/simple-list | 返回 id/contractCode/contractName 列表,用于下拉选择 |
前端验证
| # | 操作 | 预期结果 |
|---|---|---|
| 10 | 合同台账页面,输入"合同编号"搜索条件,点击搜索 | 列表按编号过滤 |
| 11 | 选择"合同类型=采购合同",点击搜索 | 列表仅显示采购合同 |
| 12 | 点击"重置"按钮 | 搜索条件清空,列表恢复全部 |
| 13 | 点击"导出"按钮 | 浏览器下载 Excel 文件 |
六、场景 D:前端页面完整性检查
此场景不涉及具体业务数据,侧重于前端各页面的 UI 渲染、交互、路由跳转是否正常。
D-01 菜单与路由
| # | 检查项 | 路由 | 预期结果 |
|---|---|---|---|
| 1 | 合同管理一级菜单 | 侧边栏 | 左侧菜单出现"合同管理",点击展开子菜单 |
| 2 | 合同台账 | /contract/contract-info-list | 页面正常加载,显示搜索栏+表格+分页 |
| 3 | 合同分类 | /contract/contract-category | 页面正常加载,树形表格 |
| 4 | 合同模板 | /contract/contract-template | 页面正常加载,列表+搜索+分页 |
| 5 | 合同变更 | /contract/contract-change | 页面正常加载,列表+搜索+分页 |
| 6 | 合同履约 | /contract/contract-performance | 页面正常加载,列表+搜索+分页 |
| 7 | 合同配置 | /contract/contract-config | 页面正常加载,表单+保存按钮 |
| 8 | 统计分析 | /contract/contract-stats | 页面正常加载,概览卡片+月度统计表格 |
| 9 | 合同详情 | /contract/contract-info-detail?id=1 | 页面正常加载,显示表单+明细表格 |
D-02 字典渲染
| # | 页面 | 字段 | 预期渲染 |
|---|---|---|---|
| 1 | 合同台账列表 | 合同类型 | 彩色标签:销售合同/采购合同/服务合同/... |
| 2 | 合同台账列表 | 合同性质 | 彩色标签:收入类/支出类/无金额 |
| 3 | 合同台账列表 | 合同状态 | 彩色标签:草稿/审批中/已审批/已生效/... |
| 4 | 合同变更列表 | 变更类型 | 彩色标签:金额变更/期限变更/条款变更/... |
| 5 | 合同履约列表 | 履约类型 | 彩色标签:交付/验收/收款/付款/... |
| 6 | 合同详情表单 | 合同类型下拉 | 字典选项完整,与后端一致 |
| 7 | 合同详情表单 | 合同性质下拉 | 字典选项完整 |
D-03 表单校验
| # | 页面 | 操作 | 预期结果 |
|---|---|---|---|
| 1 | 合同分类新增 | 不填名称直接保存 | 提示"请输入分类名称" |
| 2 | 合同台账新增 | 不填合同名称直接保存 | 提示必填校验 |
| 3 | 合同台账新增 | 不选合同类型直接保存 | 提示"请选择合同类型" |
| 4 | 合同变更新增 | 不选关联合同直接保存 | 提示必填校验 |
| 5 | 合同配置保存 | 流水号位数输入 0 或负数 | 校验不通过 |
七、场景 E:跨功能关联验证
验证各子功能之间的数据关联和一致性。
E-01 合同与收付款计划关联
| # | 检查项 | 预期结果 |
|---|---|---|
| 1 | 创建收付款计划时,contractId 不存在 | 返回错误,提示合同不存在 |
| 2 | 查询计划传 contractId,验证只返回该合同的计划 | 不会返回其他合同的计划 |
| 3 | 计划金额合计 vs 合同总金额 | 无强制一致性约束,但数据应合理 |
E-02 合同与履约关联
| # | 检查项 | 预期结果 |
|---|---|---|
| 1 | 创建履约记录时,contractId 不存在 | 返回错误 |
| 2 | 履约汇总 summary 的已履约金额 = 该合同所有履约记录金额之和 | 数据一致 |
| 3 | 合同主表 performanceAmount 与履约汇总一致 | 创建履约记录后自动更新主表 |
E-03 合同与变更关联
| # | 检查项 | 预期结果 |
|---|---|---|
| 1 | 变更记录的 contractId 指向正确合同 | GET 变更详情后 contractId 与原合同匹配 |
| 2 | 按合同查变更列表 | 只返回该合同的变更,不混入其他合同 |
| 3 | 删除合同后查其变更列表 | 变更记录仍存在(软删除不影响变更数据) |
E-04 统计数据一致性
| # | 检查项 | 预期结果 |
|---|---|---|
| 1 | overview.totalCount = 合同台账 page 的 total | 数量一致 |
| 2 | overview.totalAmount = 所有非草稿合同金额之和 | 金额一致(±精度) |
| 3 | 月度统计各月合计 = overview 总量 | 数量和金额月度累加等于总计 |
| 4 | 分类统计各分类合计 = overview 总量 | 数量和金额分类累加等于总计 |
八、异常与边界测试
F-01 权限控制
| # | 操作 | 预期结果 |
|---|---|---|
| 1 | 不带 Authorization header 调用任意接口 | 返回 401 未授权 |
| 2 | 使用无 contract:info:create 权限的用户创建合同 | 返回 403 无权限 |
| 3 | 使用无 contract:info:delete 权限的用户删除合同 | 返回 403 无权限 |
F-02 数据完整性
| # | 操作 | 预期结果 |
|---|---|---|
| 1 | 创建合同时 contractType 传非法值(如 99999) | 创建成功但字典无法渲染,或后端校验拒绝 |
| 2 | 创建合同总金额传负数 | 合理的校验拒绝或允许(视业务设计) |
| 3 | 日期字段传空字符串 | 不报错,日期存为 null |
| 4 | 合同明细行 items 传空数组 | 创建成功,明细行为空 |
| 5 | contractCode 传超长字符串(>64字符) | 数据库层面截断或报错 |
F-03 并发与幂等
| # | 操作 | 预期结果 |
|---|---|---|
| 1 | 同时两个请求创建相同 contractCode | 仅一个成功,另一个返回编号已存在 |
| 2 | 同时两个请求更新同一合同 | 最后一次更新生效(乐观锁或覆盖写) |
九、测试数据清理
测试完成后,按以下顺序清理测试数据(或使用专用测试租户隔离):
- 删除履约记录:
DELETE /contract/performance/delete?id=xxx - 删除收付款计划:
DELETE /contract/payment-plan/delete?id=xxx - 删除合同变更:
DELETE /contract/change/delete?id=xxx - 删除合同(仅草稿状态可删):
DELETE /contract/info/delete?id=xxx - 删除合同模板:
DELETE /contract/template/delete?id=xxx - 删除合同分类(先删子分类再删父分类):
DELETE /contract/category/delete?id=xxx
注意:已提交/已生效的合同不可直接删除,需先终止或通过数据库操作清理。
十、测试检查清单(Checklist)
| # | 检查项 | 场景 | 状态 |
|---|---|---|---|
| 1 | 合同配置 CRUD | A-01 | ☐ |
| 2 | 合同分类树形 CRUD | A-02 | ☐ |
| 3 | 合同模板 CRUD + 搜索 | A-03 | ☐ |
| 4 | 合同创建(含明细行) | A-04 | ☐ |
| 5 | 合同编号唯一性 | A-04.5 | ☐ |
| 6 | 收付款计划 CRUD | A-05 | ☐ |
| 7 | 合同提交审批 | A-06 | ☐ |
| 8 | 合同履约 CRUD + 汇总 | A-07 | ☐ |
| 9 | 统计分析概览/月度/分类 | A-08 | ☐ |
| 10 | 采购合同创建 + 提交 | B-01 | ☐ |
| 11 | 合同变更创建 + 提交 | B-02 | ☐ |
| 12 | 变更列表按合同过滤 | B-02.3 | ☐ |
| 13 | 采购合同履约(交付+付款) | B-03 | ☐ |
| 14 | 草稿保存 / 修改 / 删除 | C-01 | ☐ |
| 15 | 列表搜索(编号/名称/类型/状态/乙方/组合) | C-02 | ☐ |
| 16 | Excel 导出 | C-02.8 | ☐ |
| 17 | 精简列表(下拉选择) | C-02.9 | ☐ |
| 18 | 前端各页面路由正常 | D-01 | ☐ |
| 19 | 字典标签渲染 | D-02 | ☐ |
| 20 | 表单必填校验 | D-03 | ☐ |
| 21 | 跨功能数据关联 | E-01~04 | ☐ |
| 22 | 权限控制 | F-01 | ☐ |
| 23 | 数据边界 | F-02 | ☐ |
