主题
商户入驻至平台审核激活全链路测试规范 (Spec)
本文档定义了携岛项目(XieLi)商户入驻申请、平台端审核、保证金支付以及激活状态控制的自动化测试用例规范。
一、 测试设计原则与数据准备
- 测试环境要求:
- 后端服务基于 Spring Boot 运行,直连远程数据库
xieli_dev(8.134.105.163) 和本地 Redis 缓存服务。
- 后端服务基于 Spring Boot 运行,直连远程数据库
- 唯一性测试数据动态生成:
- 由于测试数据不需要回滚且会真实落库,为了支持用例被多次重复运行而不触发唯一索引冲突(如手机号重复注册),所有用例中的关键标识均需动态生成:
- 手机号:使用
13加上随机 9 位数字(例如:13+System.currentTimeMillis() % 1000000000)。 - 商户名称:拼接时间戳,例如
自动化测试商户_1719500000。 - 订单号:按系统规则生成唯一前缀
MCH+ 毫秒级时间戳。
- 手机号:使用
- 由于测试数据不需要回滚且会真实落库,为了支持用例被多次重复运行而不触发唯一索引冲突(如手机号重复注册),所有用例中的关键标识均需动态生成:
二、 自动化测试用例设计
Case 1: 商户入驻申请字段合法性校验 (Verification & Validation)
- 目的:验证入驻表单字段完整性校验及个人与企业商户的差异化拦截。
- 输入/测试数据:
- 用例 1A (公共必填校验):缺少联系电话/密码/业务类型。
- 用例 1B (企业入驻差异化校验):缺少开户支行
bankBranch/ 营业执照等。 - 用例 1C (个人入驻差异化校验):个人入驻(如专车司机)缺少身份证号
idCard等,但无需校验营业执照。
- 预期结果:接口应正确返回状态码
500,并提示具体缺少的字段信息(例如:“开户支行不能为空”)。
Case 2: 手机号唯一性冲突拦截
- 目的:验证相同手机号不可重复申请商户。
- 步骤:
- 产生一个随机手机号
phoneA并完成第一次合法入驻申请。 - 使用相同的
phoneA再次发起入驻申请。
- 产生一个随机手机号
- 预期结果:第二次申请时接口拦截并提示
该手机号已注册商户。
Case 3: 短信验证码强制拦截
- 目的:防止绕过短信验证接口直接提交入驻表单。
- 步骤:
- 产生一个随机手机号
phoneB。 - 不向 Redis 写入验证状态,直接调用入驻申请
/api/merchant/apply。
- 产生一个随机手机号
- 预期结果:接口应被拒绝,返回提示:
请先完成手机号验证码校验。
Case 4: 敏感数据 AES 加密落库
- 目的:保证法人身份证、手机号和对公银行卡号的隐私安全。
- 步骤:
- 发起一次合法的企业商户入驻,提交明文法人身份证
110101199001011234和银行卡号6222020202020202020。 - 直接从商户表
xl_merchant查询刚才插入的记录。
- 发起一次合法的企业商户入驻,提交明文法人身份证
- 预期结果:
- 数据库中的
legal_person_id_card和bank_account_number字段必须为 AES 密文(长度显著变长,非明文)。 - 调用商户端详情接口时,能解密出正常明文供校验。
- 数据库中的
Case 5: OCR 识别与优雅降级
- 目的:当阿里云 OCR 识别发生故障时,不影响正常入驻。
- 步骤:
- 模拟调用
upload-and-ocr,但由于某种原因(或人工 Mock)第三方 OCR 服务返回识别结果为null。 - 验证系统能成功上传图片到 OSS 并不报错。
- 模拟调用
- 预期结果:返回上传成功的
ossUrl,且ocrResult字段为null(允许降级)。
Case 6: 平台端审核越权拦截
- 目的:确保非管理员用户无法修改商户状态。
- 步骤:
- 生成测试商户并提交,状态为
1(待审核)。 - 不携带任何管理员 Token,或携带普通商户的 Token 访问审核接口
/api/admin/merchants/audit。
- 生成测试商户并提交,状态为
- 预期结果:接口应返回
403或被 Spring Security 拦截。
Case 7: 平台审核状态机边界控制
- 目的:防止通过接口非法更改商户状态。
- 步骤:
- 已经通过审核(或已经是注销状态)的商户。
- 管理员再次对其调用
/audit接口尝试通过/驳回。
- 预期结果:系统抛出异常,提示:
只有待平台审核商户可以审核。
Case 8: 审核意见与驳回重提全流转
- 目的:验证审核驳回、补料重提交的完整闭环。
- 步骤:
- 管理员调用审核接口驳回该商户,状态设为
2,记录rejectReason: "营业执照模糊"。 - 商户端调用
/resubmit重提资料(包含更新后的营业执照路径)。 - 再次查询商户状态。
- 管理员调用审核接口驳回该商户,状态设为
- 预期结果:
- 重提后商户状态回滚为
1(待审核)。 - 原驳回意见字段
rejectReason被重置清空(null)。 - 敏感字段保持加密。
- 重提后商户状态回滚为
Case 9: 支付回调幂等性校验
- 目的:防止因微信重复回调导致多次处理订单或重复激活。
- 步骤:
- 商户通过审核进入
3(待支付) 状态,生成保证金订单。 - 模拟微信发起第一次支付成功通知(回调
/api/merchant/pay/notify)。 - 紧接着使用完全相同的报文和单号发起第二次回调。
- 商户通过审核进入
- 预期结果:
- 第一次回调后,商户状态变为
4(激活),paidAt被记录。 - 第二次回调接口成功返回,但不再执行更新商户表的操作,数据库内状态和时间保持一致。
- 第一次回调后,商户状态变为
Case 10: 回调丢失的主动查单兜底
- 目的:保证微信回调丢包时商户端仍能正常激活。
- 步骤:
- 模拟商户已支付完成,但后端接口因为网络中断没有接收到支付成功回调。
- 商户端轮询查询状态,即调用
/api/merchant/status/{merchantId}。 - 后端触发向微信查单并返回成功。
- 预期结果:在查询后,数据库中的商户状态自动更新为
4(激活),且写入paidAt。
Case 11: 未激活商户的 403 路由拦截 (White-list Enforcement)
- 目的:保护核心商户接口,只有激活商户能开展业务。
- 步骤:
- 创建待审核 (状态 1) 或待支付 (状态 3) 的商户,并生成对应的商户端 JWT 登录 Token。
- 使用此 Token 访问商户端非白名单接口(如
/api/merchant/wallet/info或商品发布等)。 - 访问白名单接口
/api/merchant/status。
- 预期结果:
- 访问非白名单接口被拦截,返回状态码
403或报错提示商户审核未通过,暂无权访问该功能。 - 访问
/status接口正常返回。
- 访问非白名单接口被拦截,返回状态码
Case 12: 注销与保证金退款流(含手动降级)
- 目的:验证商户注销时的退保证金逻辑。
- 步骤:
- 激活状态商户(已付保证金)发起注销。
- 校验商户状态变为
5(注销中),并且创建了xl_refund_record退款记录。 - 若微信退款接口异常,管理员在微信平台手工退款后,调用平台端
/confirmDepositRefund。
- 预期结果:商户状态成功变更为
6(已注销)。
Case 13: 电商商城商户入驻流程
- 目的:验证 businessType 为
mall时能够成功提交申请且 entryType 被自动判定为merchant。 - 步骤:
- 准备商户分类为
clothing(服装商城),对应业务类型mall。 - 发起商户入驻申请。
- 准备商户分类为
- 预期结果:申请成功,返回 entryType 为
"merchant"。
Case 14: 本地生活商户入驻流程
- 目的:验证 businessType 为
locallife时能够成功提交申请且 entryType 判定为merchant。 - 步骤:
- 准备商户分类为
food(餐饮美食),对应业务类型locallife。 - 发起商户入驻申请。
- 准备商户分类为
- 预期结果:申请成功,返回 entryType 为
"merchant"。
Case 15: 合作摄影师个人入驻流程
- 目的:验证 businessType 为
delivery且 categoryCode 为photographer时,系统判定为personal(个人入驻),仅校验个人必填字段并成功插入。 - 步骤:
- 准备分类为
photographer,业务类型为delivery。 - 发起个人入驻申请,仅提交身份证件信息,跳过营业执照与银行对公信息。
- 准备分类为
- 预期结果:申请成功,返回 entryType 为
"personal"。
Case 16: 合作司机个人入驻流程
- 目的:验证 businessType 为
delivery且 categoryCode 为driver时,系统判定为personal(个人入驻),仅校验个人必填字段并成功插入。 - 步骤:
- 准备分类为
driver,业务类型为delivery。 - 发起个人入驻申请,仅提交身份证件信息,跳过营业执照与银行对公信息。
- 准备分类为
- 预期结果:申请成功,返回 entryType 为
"personal"。