1. 文档定位
本文说明 DGJ2.0 “轮胎保”小程序从客户认证、轮胎登记、生产周期占位、理赔申请、OPS 创建不良品采购退货单,到仓审通过后理赔完成的完整业务链。文档同时覆盖图片分类、轮胎属性、状态、数量、事务、通知、接口和排查方法。
核心结论:轮胎理赔不是独立结束于 t_tyre_claim。一笔理赔在待确认后会被采购退货业务引用,aftersale_order_id 将理赔记录与不良品采购退货单绑定;退货单审核、驳回和仓审结果反向推进理赔状态。
2. 业务目标
- 维修厂客户用客户编号和密码进入轮胎小程序。
- 按车辆、轮胎规格、生产周期和图片完成登记。
- 每条生产周期对应一条可发起理赔的占位记录。
- 用户对具体轮胎提交裂口或鼓包理赔。
- OPS 补充割胎凭证并创建不良品退货业务。
- 采购退货审核和仓审结果推进理赔。
- 达到登记数量后,登记单自动完成。
3. 业务边界
3.1 本文包含
- 客户认证、客户/服务站信息和修改密码。
- 登记创建、编辑、列表、详情、48 小时取消、1 年有效期。
- 规格、扁平比、尺寸、品牌、花纹和示例图属性。
- 登记照片、理赔照片和割胎照片。
- 理赔提交、编辑、取消、OPS 列表和详情。
- 理赔与不良品采购退货单的创建、审核、驳回和仓审完成。
- 站内通知 MQ、状态/数量口径、排查和回归。
3.2 本文不包含
- 普通采购退货和采购入库全链,见第 03、14 篇。
- SAAS/E站销售退货申请,见第 29 篇。
- 文件中心上传、OSS 鉴权和图片审核平台内部实现。
- 轮胎厂商线下鉴定、赔付结算和会计流程。
4. 参与角色
| 角色 | 身份数据 | 动作 | 责任边界 |
|---|---|---|---|
| 维修厂客户 | contact_id、客户编号 | 登录、登记、提交理赔 | 登记车辆/图片真实 |
| 所属服务站 | sid | 客户归属和数据隔离 | 只能访问本站客户 |
| OPS 人员 | PC 登录上下文 | 查看、补割胎图、取消、创建退货 | 审核业务资料 |
| 采购退货业务 | 采购退货单 ID | 接受理赔来源并流转 | 货物退回与仓审 |
| 仓库审核人 | 退货单状态 | 仓审通过/驳回 | 到货真实性 |
| 消息系统 | TYRE_CLAIM_SUBMIT | 通知 OPS | 不改变理赔状态 |
5. 系统分层
flowchart LR
M[轮胎小程序] --> I[inner/tyre API]
O[DGJ2 OPS PC] --> P[tyre/Claim]
I --> S[Tyre Services]
P --> S
S --> T[(Tyre Tables)]
S --> G[(BS_IMAGE)]
S --> Q[消息 MQ]
P --> R[采购退货 InvPo]
R --> S
6. 入口总表
6.1 小程序内部 API
| 模块 | 方法 | 请求核心 | Service/Model | 副作用 |
|---|---|---|---|---|
| 用户 | inner/tyre/User::auth | number、password | ContactModel 等 | 只读认证 |
| 用户 | User::info | contact_id | 客户/地址/站点模型 | 只读 |
| 用户 | User::modifyPassword | 联系人、原/新密码 | ContactModel | 更新密码 |
| 属性 | inner/tyre/Property::list | 无核心业务参数 | PropertyService | 只读 |
| 登记 | Registration::list | contact_id、分页、车牌 | RegistrationService::list | 只读并计算按钮 |
| 登记 | Registration::info | id、contact_id | RegistrationService::info | 只读 |
| 登记 | Registration::create | 车辆、轮胎、周期、图片 | RegistrationService::create | 新登记、占位理赔、图片 |
| 登记 | Registration::edit | 取消单 ID + 完整登记 | RegistrationService::edit | 重建占位理赔和图片 |
| 登记 | Registration::cancel | id、联系人上下文 | RegistrationService::cancel | 状态变 99 |
| 登记 | Registration::isMultiSubmit | 车牌、sid、客户 | 同名 Service | 只读计数 |
| 理赔 | inner/tyre/Claim::list | 客户、状态、分页 | ClaimService::list | 只读 |
| 理赔 | Claim::info | 理赔 ID、客户 | ClaimService::info | 只读 |
| 理赔 | Claim::submit | 理赔 ID、损坏类型、三组图片 | ClaimService::submit | 更新理赔、登记、图片、发 MQ |
| 理赔 | Claim::detail_info | id | ClaimService::detailInfo | OPS 组合详情 |
6.2 OPS PC 入口
| 方法 | 作用 | 上下文 |
|---|---|---|
tyre/Claim::initList | 加载理赔列表页 | PC 菜单权限 |
tyre/Claim::list | 全站或本站理赔查询 | JXCSID 注入 sid |
tyre/Claim::edit | 补充/替换割胎照片 | 仅待确认 |
tyre/Claim::cancel | 取消待确认理赔 | 理赔 ID |
tyre/Claim::info | 登记+理赔组合详情 | detail_info 校验 |
7. 用户认证
7.1 认证流程
sequenceDiagram
participant U as 小程序用户
participant C as inner/tyre/User
participant CT as ContactModel
participant A as Address/Admin Models
U->>C: number + password
C->>CT: 按客户编号查询
alt 客户无自定义密码
C->>C: 比较 DEFAULT_PASSWORD
else 已设置密码
C->>C: md5(password) 比较
end
C->>A: 默认地址联系人+服务站信息
C-->>U: 客户与服务站上下文
7.2 返回字段
| 字段 | 来源 |
|---|---|
contact_name/id/number | 客户主数据 |
contact_mobile、user_name | 客户默认地址 |
station_id/name/mobile | 服务站管理员数据 |
7.3 当前安全实现
- 旧密码为空时允许系统默认密码。
- 自定义密码使用
md5比对和保存。 - 当前实现未见现代密码哈希、盐、登录失败限流和会话 token 生成。
这是高风险历史兼容实现。改造需兼容旧 MD5 并在成功登录后渐进升级哈希,不能直接批量重置。
8. 用户请求示例
{
"number": "CUSTOMER_DEMO",
"password": "******"
}
修改密码:
{
"contact_id": 10001,
"old_password": "******",
"new_password": "********",
"new_password_confirm": "********"
}
生产日志不得记录密码、默认密码或完整手机号。
9. 轮胎属性
| 类型值 | 业务含义 | API 键 |
|---|---|---|
1 | 规格 | specification |
2 | 扁平比 | fineness_ratio |
3 | 尺寸 | thickness |
4 | 品牌 | brand |
15 | 花纹 | figure |
5-14 | 登记/理赔示例图片 | 对应 *_example_image |
PropertyService::list 只读取 status = 1 的属性。规格、扁平比按数值升序;尺寸用正则提取第一个正整数排序。
10. 属性快照
登记主表同时保存属性 ID 和属性名称:
property_brand_id/nameproperty_specification_id/nameproperty_fineness_ratio_id/nameproperty_thickness_id/name
RegistrationService::transForm 用 type-id 从实时属性集合取名称。保存名称快照能避免属性字典改名后历史登记展示变化。
风险:请求传入的 ID 不属于对应类型时,数组键不存在;目前依赖运行时异常,建议 Validator 显式校验组合。
11. 登记请求
{
"sid": 20001,
"contact_id": 10001,
"car_number": "浙A12345",
"mileage": "25600",
"registration_num": 2,
"property_brand_id": 101,
"property_specification_id": 201,
"property_fineness_ratio_id": 301,
"property_thickness_id": 401,
"figured": "PATTERN_DEMO",
"contact_details": "138****0000",
"production_dates": ["2420", "2421"],
"install_images": ["https://file.example/install.jpg"],
"new_images": ["https://file.example/new.jpg"],
"close_images": ["https://file.example/close.jpg"]
}
12. 登记校验
| 字段 | 规则 |
|---|---|
car_number | 必填、最长 15、车牌正则 |
mileage | 必填、最长 7 |
registration_num | 必填数字 |
| 四类属性 ID | 必填数字 |
figured | 必填 |
contact_details | 最长 11 |
production_dates | 数组数量必须等于登记数量;每项数字且最多 4 位 |
| 三组登记图片 | 每组 1-4 张 |
Validator 的 create/edit 场景中 property_fineness_ratio_id 重复列出,property_thickness_id 未进入场景白名单,尽管 rule 定义了该字段。Service 后续会读取厚度 ID,但校验缺口需修复并回归旧客户端。
13. 客户归属校验
transForm 按 contact_id 查询客户并要求 contact.sid == request.sid。这是登记创建的核心租户隔离。
但部分 info/cancel Controller 场景只校验 id,Service 仍依赖请求里的 contact_id 查询。API 网关是否统一注入 contact_id/sid 需环境确认;不得信任前端任意传入的站点身份。
14. 登记创建流程
sequenceDiagram
participant U as 小程序
participant C as Registration Controller
participant S as RegistrationService
participant DB as MySQL
U->>C: 完整登记请求
C->>C: RegistrationValidate
C->>S: create
S->>S: 客户归属+属性名称快照
S->>DB: BEGIN
S->>DB: 新增 registration
loop 每个production_date
S->>DB: 新增claim占位(status=0)
end
loop 三类登记图片
S->>DB: 新增BS_IMAGE
end
S->>DB: COMMIT
创建是一笔本地事务:登记、理赔占位和图片任一步异常都会回滚。
15. 为什么登记时就有理赔记录
登记数量和生产周期一一对应。创建时每个 production_dates 生成一条:
sid + contact_id + registration_id + production_date + claim_status=0
它不是已提交理赔,而是“该条轮胎未来可发起理赔”的占位。真正提交后才补:
claim_numberdamage_typeclaim_status = 1- 理赔图片
- 创建时间
flowchart LR
R[一张登记单 registration_num=3] --> C1[周期1 claim占位0]
R --> C2[周期2 claim占位0]
R --> C3[周期3 claim占位0]
C1 -->|提交| S1[真实理赔单1]
C2 -->|尚未提交| W2[继续占位]
C3 -->|尚未提交| W3[继续占位]
16. 登记图片
| ImageEnums 类型 | API 键 | 含义 |
|---|---|---|
TYPE_REGISTRATION_INSTALL | install_images | 装车图片 |
TYPE_REGISTRATION_NEW = 26 | new_images | 新胎整体照 |
TYPE_REGISTRATION_CLOSE_UP = 27 | close_images | 新胎特写照 |
图片统一写 t_bs_image:f_id = registration.id,type 区分业务。读取必须同时带 f_id + type,仅按 f_id 可能混入理赔或其他业务图片。
17. 登记状态机
| 值 | 状态 | 页面动作 |
|---|---|---|
0 | 已创建 | 可理赔;48 小时内可取消 |
1 | 处理中 | 未达到登记数量时仍可继续理赔 |
2 | 已完成 | 联系客服,不再新理赔 |
99 | 已作废 | 可编辑后恢复为已创建 |
stateDiagram-v2
[*] --> 已创建: 登记创建
已创建 --> 已作废: 48小时内取消
已作废 --> 已创建: 完整编辑重建
已创建 --> 处理中: 首条理赔提交
处理中 --> 处理中: 继续提交其他轮胎理赔
处理中 --> 已完成: 通过数量=登记数量
18. 页面动作计算
RegistrationService::list 计算:
| 条件 | can_cancel | can_claim | can_edit | can_contact |
|---|---|---|---|---|
| 非运营 + 已创建 + 48h 内 | 是 | 是 | 否 | 否 |
| 非运营 + 处理中且理赔数<登记数 | 否 | 是 | 否 | 否 |
| 已作废 | 否 | 否 | 是 | 否 |
| 已完成 | 否 | 否 | 否 | 是 |
| 运营车辆 | 否 | 否 | 否 | 是 |
| 创建超过 1 年 | 否 | 否 | 否 | 是 |
这些是展示辅助值,Service 提交时仍必须再次校验状态、运营车辆和数量,不能只靠按钮隐藏。
19. 48 小时和 1 年规则
- 创建超过 48 小时:不可取消。
- 创建超过 365 天:取消、理赔、编辑全部关闭,标记
is_expired = true,只允许联系客服。 - 1 年限制目前主要在列表按钮计算;
ClaimService::submit未见再次校验登记创建时间。直接调用 API 是否可绕过过期限制是高风险待补项。
20. 重复登记判断
isMultiSubmit(carNumber, sid, contactId) 当前返回:
同车牌 + 同服务站 + 同客户的登记记录数 >= 2
因此第 2 次提交前若只有 1 条,接口仍返回 false;达到 2 条后才为 true。若业务目标是“已有一条即提示重复”,阈值疑似应为 >= 1。该接口也不按取消/过期状态过滤,产品口径需确认。
21. 登记取消与编辑
取消仅允许已创建且 48 小时内,状态改为 99,不删除登记、占位理赔或图片。
编辑只允许状态 99,执行:
- 重新校验客户和属性。
- 开启事务。
- 更新完整登记并强制状态回 0。
- 删除该登记的全部理赔记录。
- 按新生产周期重建占位理赔。
- 删除三类登记图片并重建。
- 提交事务。
编辑不是局部更新,会删除原理赔占位。状态保护必须确保不存在已进入售后的真实理赔。
22. 理赔请求
{
"id": 30001,
"sid": 20001,
"contact_id": 10001,
"damage_type": 1,
"damage_images": ["https://file.example/damage.jpg"],
"location_images": ["https://file.example/location.jpg"],
"production_images": ["https://file.example/production.jpg"]
}
| 字段 | 规则 |
|---|---|
id | 登记时预建的理赔占位 ID |
damage_type | 1 裂口,2 鼓包 |
| 三组图片 | 各 1-4 张 |
sid/contact_id | 当前客户身份;Service 主要按 contact_id 查记录 |
23. 理赔图片
| ImageEnums 类型 | API 键 | 含义 |
|---|---|---|
4 | damage_images | 索赔胎整体/损坏照片 |
28 | location_images | 故障位置照片 |
29 | production_images | 索赔胎生产周期照片 |
5 | separate_image | OPS 补充的割胎照片 |
前三组提交时强制存在;割胎图在待确认阶段通过 OPS edit 单独维护。
24. 理赔提交
sequenceDiagram
participant U as 小程序
participant S as ClaimService
participant DB as DB
participant MQ as Message MQ
U->>S: submit(占位id,损坏类型,三组图片)
S->>DB: 按id+contact_id查占位
S->>S: status必须0、非运营、claim_num<registration_num
S->>DB: BEGIN
S->>DB: 生成claim_number,status=1
S->>DB: 写三组图片
S->>DB: registration status=1, claim_num+1
S->>MQ: sendMailToMq(TYRE_CLAIM_SUBMIT)
S->>DB: COMMIT
MQ 生产发生在数据库事务内。如果 MQ 成功但数据库提交失败,OPS 可能收到不存在的理赔通知;若数据库成功但 MQ 抛异常导致回滚,消息是否已发送取决于 Provider 行为。需要 Outbox 或补偿任务。
25. 理赔单号
当前 generateClaimNumber(contactId):
contactId + YYYYMMDD + 两位随机数
同一客户同一天理论上只有 100 个随机尾号,存在碰撞概率。数据库是否有唯一索引待确认。更稳妥方案是雪花 ID/序列并保留业务前缀。
26. 理赔状态机
| 值 | 状态 | 业务含义 |
|---|---|---|
0 | 草稿/占位 | 登记生成,尚未提交 |
1 | 待确认 | 用户已提交,OPS 可补凭证/建退货 |
2 | 待审核 | 已绑定不良品退货单,等待仓审 |
3 | 通过 | 仓审通过,理赔完成 |
99 | 不通过 | 用户取消或 OPS 拒绝的历史记录 |
stateDiagram-v2
[*] --> 占位0: 登记生产日期
占位0 --> 待确认1: 用户提交
待确认1 --> 待审核2: 退货单保存并auditClaim
待审核2 --> 待确认1: 退货单审核驳回/rejectClaim
待审核2 --> 通过3: 仓审通过/finishClaim
待确认1 --> 不通过99: 用户或OPS取消
不通过99 --> 新占位0: 新插入替代记录
27. OPS 列表查询
支持条件:
- 客户 ID/名称模糊。
- 理赔单号。
- 登记车牌/品牌模糊。
- 精确车牌。
- 创建时间。
- 登记 ID。
sid。- 理赔状态。
特殊筛选 claim_status = 1.5 表示待确认 1 + 待审核 2。数据库无 1.5 状态。
当前列表格式代码对该筛选有疑似错误:把 requestData['claim_status'] 赋为 DAMAGE_TYPE_MAP[STATUS_WAIT_AUDIT],状态 2 恰好也是鼓包类型 2,结果为“鼓包”。这不应改变返回理赔状态,但属于混用枚举的维护风险。
28. 组合详情
detailInfo 返回:
registration_info:登记、三类登记照片、过滤 99 后的生产周期。claim_info:理赔状态、三类理赔照片、割胎照片。
小程序 info 按 id + contact_id 做身份隔离,并把车辆/规格字段合并到理赔对象。
29. OPS 补割胎照片
仅待确认状态允许:
- 删除
f_id = claim.id,type=5的旧割胎图。 - 将逗号分隔
separate_image转为数组。 - 批量插入新图片。
- 整体在事务中完成。
空图片数组时 insert_batch([]) 的框架行为需测试;是否允许清空割胎图由产品确认。
30. 理赔取消
仅待确认状态可取消。当前实现不是把原记录恢复为占位,而是:
- 查登记并准备
claim_num - 1。 - 从原理赔复制
registration_id/sid/contact_id/production_date/create_time,新插一条占位。 - 原理赔状态改 99。
- 更新登记数量/状态。
flowchart LR
A[原理赔id=10,status=1] --> B[取消事务]
B --> C[原id=10,status=99保留历史]
B --> D[新id=11,status=0同生产周期]
B --> E[registration.claim_num减1]
这样保留失败/取消历史,同时恢复该生产周期再次理赔能力。
31. 取消实现疑点
cancel 和 OpsReject 判断:
if ($registrationInfo['claim_number'] == 1) {
$registrationSet['registration_status'] = ClaimEnums::STATUS_DRAFT;
}
登记数量字段在其他代码中是 claim_num,claim_number 是理赔单号字段。此处疑似字段名错误;并且赋值使用 ClaimEnums::STATUS_DRAFT,数值刚好也是 0,与 RegistrationEnums::STATUS_CREATED 相同,但枚举语义混用。文档把它标为当前代码风险,不把它解释为正确业务规则。
合理口径应是:取消后有效理赔数为 0 时,登记从处理中恢复已创建。
32. 与采购退货的绑定
轮胎理赔通过不良品采购退货单承载后续审核:
PoOrderEnums::SRC_ORDER_TYPE_TYRE = 11。- 退货保存扩展字段带
claim_id。 BadGoodsOrderSaveService要求理赔仍为待确认。- 保存后调用
ClaimService::auditClaim(claimId, returnOrderId)。 - 理赔记录写
aftersale_order_id并进入待审核。
sequenceDiagram
participant O as OPS
participant PO as 不良品退货保存
participant C as ClaimService
participant DB as DB
O->>PO: 选择claim_id创建退货单
PO->>DB: 查理赔必须status=1
PO->>PO: srcOrderType=11
PO->>DB: 保存退货主明细/坏件扩展
PO->>C: auditClaim(claimId, returnOrderId)
C->>DB: aftersale_order_id=退货单ID,status=2
33. 退货审核驳回
采购退货单审核被驳回时,InvPoService 对轮胎来源调用:
ClaimService::rejectClaim(afterSaleOrderId)
结果:
- 清空
aftersale_order_id。 - 理赔从待审核 2 回到待确认 1。
- 用户/OPS 可修正后重新建退货单。
34. OPS 单审拒绝
OpsReject(afterSaleOrderId) 与普通退货审核驳回不同:它要求理赔处于待审核,然后:
- 原理赔改 99。
- 插入新的占位理赔。
- 登记
claim_num - 1。 - 可能把登记恢复已创建。
这属于理赔业务最终拒绝,而 rejectClaim 只是退货单审核失败后的回退。
35. 仓审完成
finishClaim(afterSaleOrderId):
- 按退货单 ID 查理赔。
- 理赔状态更新为通过 3。
- 查询同登记所有理赔记录。
- 统计状态 3 的数量。
- 若通过数等于
registration_num,登记状态改为完成 2。
flowchart TD
A[退货仓审通过] --> B[理赔status=3]
B --> C[统计同登记通过理赔数]
C --> D{通过数 == registration_num?}
D -- 否 --> E[登记保持处理中]
D -- 是 --> F[登记status=2完成]
36. 数据关系
erDiagram
BS_CONTACT ||--o{ TYRE_REGISTRATION : owns
TYRE_REGISTRATION ||--o{ TYRE_CLAIM : creates
TYRE_REGISTRATION ||--o{ BS_IMAGE : registration_images
TYRE_CLAIM ||--o{ BS_IMAGE : claim_images
TYRE_CLAIM }o--o| SCM_PO_RETURN : aftersale_order_id
TYRE_PROPERTY ||--o{ TYRE_REGISTRATION : snapshot
关键关联键:
| 键 | 从哪里到哪里 |
|---|---|
contact_id | 客户 -> 登记/理赔 |
registration_id | 登记 -> 多个生产周期理赔 |
claim.id | 图片 f_id、退货扩展 claim_id |
aftersale_order_id | 理赔 -> 不良品采购退货单 |
sid | 服务站隔离和 OPS 查询 |
37. 数量守恒
对一张有效登记,在无历史脏数据时:
registration_num = 可理赔轮胎总数
claim_num = 已提交且未被取消/最终拒绝的理赔数
有效占位数 + 有效进行中数 + 已通过数 = registration_num
状态 99 是历史记录,不进入有效数量;取消/拒绝会新增替代占位,因此物理理赔行总数可以大于登记数量。
38. 事务矩阵
| 场景 | 同事务内容 | 外部副作用 | 风险 |
|---|---|---|---|
| 登记创建 | 登记、占位理赔、图片 | 无 | 本地原子 |
| 作废登记编辑 | 登记、删除重建理赔、图片 | 无 | 全量覆盖 |
| 理赔提交 | 理赔、图片、登记计数 | MQ 通知 | MQ 与 DB 不原子 |
| 补割胎图 | 删除旧图、插新图 | 无 | 空数组行为 |
| 理赔取消 | 新占位、原状态、登记计数 | 无 | 字段名疑点 |
| 退货绑定 | 采购退货 + 理赔状态 | 可能订单中心等 | 跨 Service 事务边界需确认 |
| 仓审完成 | 理赔、登记完成 | 下游退货流程 | 重复调用幂等 |
39. 幂等与并发
| 操作 | 当前保护 | 缺口 |
|---|---|---|
| 登记创建 | 本地事务 | 无请求幂等键;重复判断阈值可疑 |
| 理赔提交 | 必须状态 0 | 两并发读取 0 后是否都成功依赖行锁/条件更新 |
| 理赔单号 | 两位随机数 | 碰撞和唯一索引待确认 |
| 退货绑定 | 保存前检查状态 1 | 跨事务并发建两张退货单风险 |
| 取消 | 必须状态 1 | 非条件更新,重复请求可能双插占位 |
| 仓审完成 | 状态直接更新为 3 | 重复执行大体幂等,但缺原状态校验 |
建议将状态更新改为条件更新并检查影响行数:WHERE id=? AND claim_status=?。
40. 消息通知
理赔提交事务中调用:
MqSer::sendMailToMq({
contact_name,
sid,
type: MessageTypeEnums::TYRE_CLAIM_SUBMIT,
billNo: claim_number
})
消息类型 7,站内菜单定位 tyre-claim-list,URL /tyre/claim/initList。消息仅用于提醒,不是状态真值。
41. 图片安全
- 只保存可信文件中心 URL,禁止任意内网 URL。
- 限制 MIME、大小、像素和图片数量。
- 去除 EXIF 位置等隐私元数据。
- 图片读取需鉴权,不能因知道
f_id访问其他客户。 - 取消/拒绝历史图片是否保留需符合审计和隐私期限。
- 图片删除是数据库关系删除,不代表 OSS 物理文件已删除。
42. 故障树:登记创建失败
flowchart TD
A[登记失败] --> B{Validator通过?}
B -- 否 --> C[车牌/图片/周期/属性字段]
B -- 是 --> D{客户属于sid?}
D -- 否 --> E[客户归属错误]
D -- 是 --> F{属性type-id存在?}
F -- 否 --> G[属性停用或类型错]
F -- 是 --> H{主表是否写入?}
H -- 否 --> I[查DB异常]
H -- 是 --> J[占位数和图片数]
J --> K{事务完整?}
K -- 否 --> L[查异常提交/非同连接]
43. 故障树:不能提交理赔
按顺序检查:
- 理赔 ID 是否为该登记的状态 0 占位。
contact_id是否匹配。- 登记是否运营车辆。
claim_num < registration_num。- 三组图片是否各 1-4 张。
- 损坏类型是否 1/2。
- 登记是否超过一年;当前后端可能缺强校验。
- 是否存在并发请求已先改变状态。
44. 故障树:退货审核后理赔不变
| 检查点 | 预期 |
|---|---|
| 退货来源类型 | SRC_ORDER_TYPE_TYRE = 11 |
| 退货扩展 | claim_id 正确 |
| 保存前理赔状态 | 1 待确认 |
| 绑定结果 | aftersale_order_id = 退货单ID |
| 保存后理赔状态 | 2 待审核 |
| 审核驳回 | 回到 1 并清空绑定 |
| 仓审通过 | 状态 3 |
| 全部通过 | 登记状态 2 |
45. 只读 SQL:登记
字段需以环境 DDL 为准:
SELECT id, sid, contact_id, car_number, mileage, is_operate,
property_brand_id, property_brand_name,
property_specification_name, property_fineness_ratio_name,
property_thickness_name, figured, registration_num,
claim_num, registration_status, create_time, modify_time
FROM t_tyre_registration
WHERE id = 10001;
SELECT id, sid, contact_id, car_number, registration_status,
registration_num, claim_num, create_time
FROM t_tyre_registration
WHERE sid = 20001 AND contact_id = 30001 AND car_number = '浙A12345'
ORDER BY id DESC;
46. 只读 SQL:理赔
SELECT id, sid, contact_id, registration_id, production_date,
claim_number, claim_status, damage_type, aftersale_order_id,
create_time, modify_time
FROM t_tyre_claim
WHERE registration_id = 10001
ORDER BY id;
有效数量核对:
SELECT registration_id,
SUM(claim_status = 0) AS draft_count,
SUM(claim_status IN (1,2)) AS processing_count,
SUM(claim_status = 3) AS passed_count,
SUM(claim_status = 99) AS history_rejected_count
FROM t_tyre_claim
WHERE registration_id = 10001
GROUP BY registration_id;
单号碰撞:
SELECT claim_number, COUNT(*) AS cnt
FROM t_tyre_claim
WHERE claim_number IS NOT NULL AND claim_number <> ''
GROUP BY claim_number
HAVING cnt > 1;
47. 只读 SQL:图片
SELECT id, f_id, type, image, create_time
FROM t_bs_image
WHERE f_id IN (10001, 20001)
AND type IN (4,5,26,27,28,29)
ORDER BY f_id, type, id;
注意 registration.id 和 claim.id 来自不同表,数值可能相同;BS_IMAGE 只有 f_id + type 区分,因此图片类型不能复用冲突。
48. 数据巡检 SQL
登记数量与有效理赔数量不一致:
SELECT r.id, r.registration_num, r.claim_num,
SUM(c.claim_status <> 99) AS active_rows,
SUM(c.claim_status IN (1,2,3)) AS submitted_rows
FROM t_tyre_registration r
LEFT JOIN t_tyre_claim c ON c.registration_id = r.id
GROUP BY r.id, r.registration_num, r.claim_num
HAVING active_rows <> r.registration_num
OR submitted_rows <> r.claim_num;
理赔绑定不存在的退货单需按采购退货实际物理表补充 JOIN;先查:
SELECT id, claim_number, aftersale_order_id, claim_status
FROM t_tyre_claim
WHERE aftersale_order_id IS NOT NULL
AND aftersale_order_id <> '';
49. 日志与代码定位
rg -n "class Registration|function (create|edit|cancel|isMultiSubmit)" \
application/controllers/inner/tyre application/Services/Tyre/RegistrationService.php
rg -n "function (submit|auditClaim|rejectClaim|OpsReject|finishClaim)" \
application/Services/Tyre/ClaimService.php
rg -n "SRC_ORDER_TYPE_TYRE|claim_id|auditClaim|rejectClaim|finishClaim" \
application/service/scm/InvPoService.php application/Services/PoOrders
rg -n "TYPE_REGISTRATION|TYRE_CLAIM|IMAGE_TYPE_MAP" \
application/KzData/Enums/ImageEnums.php application/KzData/Enums/Tyre
rg -n "TYRE_CLAIM_SUBMIT|sendMailToMq" \
application/KzData/Enums/MessageTypeEnums.php application/Services/Tyre
50. 补偿 SOP
50.1 登记有主表但占位/图片不全
正常事务应全有或全无。先确认是否历史代码或人工数据:
- 导出登记、理赔、图片快照。
- 按
registration_num和生产周期确认缺失项。 - 未发生真实理赔时才能补占位。
- 图片只按原文件凭证补,不能伪造。
- 补后执行数量守恒巡检。
50.2 理赔已绑定退货但状态仍 1
- 查退货单是否真实创建成功。
- 查退货扩展
claim_id。 - 确认没有另一理赔绑定同一退货单。
- 在退货单仍处于对应前置状态时重放
auditClaim或业务补偿。 - 验证状态 2 和
aftersale_order_id同时存在。
50.3 仓审通过但登记未完成
- 查同登记全部状态 3 数量。
- 排除状态 99 历史行。
- 比较
registration_num。 - 若每条真实轮胎均已通过,可重放
finishClaim。 - 若数据缺失,先修复理赔关系,不能直接把登记改完成。
51. 禁止动作
- 直接把占位 0 改为通过 3。
- 为修复数量删除状态 99 的历史理赔。
- 未确认退货单时手填
aftersale_order_id。 - 只改
claim_num不修理赔明细。 - 用前端按钮状态代替后端状态校验。
- 在日志和文档公开客户密码、车主手机号和图片 URL 临时凭证。
52. 监控建议
| 指标 | 维度 | 告警 |
|---|---|---|
| 登记创建量/失败率 | sid、客户端版本 | 突增/突降 |
| 登记占位数量差 | registration_id | 非零 |
| 理赔提交事务失败 | 异常点 | 连续失败 |
| 理赔通知 MQ 失败 | routing/type | 超 SLA |
| 待确认停留时长 | 理赔单 | 超业务 SLA |
| 待审核停留时长 | 退货单 | 超仓审 SLA |
| 理赔单号重复 | 日期、客户 | 任一重复 |
| 已通过无退货绑定 | 理赔单 | 任一异常 |
| 全部通过未完成登记 | 登记单 | 任一异常 |
| 图片缺失 | 图片类型 | 必填类型为空 |
53. 回归清单:用户与属性
- [ ] 客户不存在、默认密码、自定义密码、错误密码。
- [ ] 修改密码原密码错误、确认不一致和成功。
- [ ] 旧 MD5 密码兼容。
- [ ] 客户默认地址为空。
- [ ] 服务站信息为空/停用。
- [ ] 属性只返回启用项。
- [ ] 规格、扁平比和尺寸排序。
- [ ] 属性停用后历史名称快照不变。
- [ ] 跨类型属性 ID 被拒绝。
54. 回归清单:登记
- [ ] 单条和多条轮胎登记。
- [ ] 生产周期数量必须等于登记数量。
- [ ] 生产周期非数字、超过 4 位。
- [ ] 三组图片各 1、4、0、5 张边界。
- [ ] 新能源/普通/特殊车牌。
- [ ] 客户不属于当前站点。
- [ ] 厚度字段 Validator 缺口修复后旧请求兼容。
- [ ] 登记、占位、图片同事务回滚。
- [ ] 48 小时取消边界。
- [ ] 作废后完整编辑和重建。
- [ ] 重复车牌第 1/2/3 条的提示口径。
- [ ] 365 天前后可操作性和直接 API 绕过测试。
- [ ] 运营车辆只能联系客服。
55. 回归清单:理赔
- [ ] 每个生产周期占位独立提交。
- [ ] 裂口和鼓包。
- [ ] 三组理赔图片边界。
- [ ] 占位状态以外拒绝提交。
- [ ] 理赔数量达到登记上限。
- [ ] 并发双提交同一占位。
- [ ] 理赔单号碰撞模拟。
- [ ] 提交 MQ 成功/失败与数据库事务。
- [ ] OPS 补割胎图、替换和清空。
- [ ] 用户取消保留 99 并补新占位。
- [ ] 取消后登记
claim_num和状态。 - [ ] 状态 1.5 组合筛选。
56. 回归清单:采购退货联动
- [ ] 状态 1 理赔创建不良品退货。
- [ ] 来源类型 11 和
claim_id正确。 - [ ] 同一理赔并发创建两张退货单。
- [ ] 绑定后状态 2 和退货 ID。
- [ ] 退货审核驳回回到状态 1。
- [ ] OPS 最终拒绝变 99、补占位、减计数。
- [ ] 仓审通过变 3。
- [ ] 部分轮胎通过登记保持处理中。
- [ ] 全部轮胎通过登记完成。
- [ ] 重复仓审回调幂等。
- [ ] 退货单取消/删除后的理赔恢复。
57. 改动风险
| 级别 | 代码 | 最低回归 |
|---|---|---|
| P0 | ClaimService 状态、计数、绑定 | 全理赔+采购退货联动 |
| P0 | InvPoService 轮胎来源分支 | 普通退货和轮胎退货隔离 |
| P1 | 登记编辑/取消/占位重建 | 全状态和数量守恒 |
| P1 | 图片类型枚举 | 登记、理赔、割胎和其他图片业务 |
| P1 | 用户认证密码升级 | 默认密码、MD5、登录迁移 |
| P1 | Validator | 新旧客户端请求兼容 |
| P2 | 属性排序和列表按钮 | 页面展示与后端保护 |
58. 已确认事实
- 登记创建按生产周期预建状态 0 理赔记录。
- 登记、占位和图片在同一本地事务。
- 客户必须属于请求
sid。 - 三类登记和三类理赔图片各要求 1-4 张。
- 登记取消仅限已创建且 48 小时内。
- 取消登记可完整编辑并重建占位和图片。
- 列表在一年后关闭所有业务动作。
- 理赔不支持运营车辆。
- 理赔提交增加
claim_num、登记进入处理中并发送消息。 - 取消/最终拒绝保留 99 历史并新建占位。
- 采购退货来源类型 11 绑定
aftersale_order_id。 - 退货驳回回到待确认,仓审通过变为理赔通过。
- 全部理赔通过后登记完成。
59. 待确认与已识别缺口
- 用户认证网关、token、限流和密码迁移方案。
RegistrationValidate厚度字段场景缺失。- 365 天限制在提交 Service 缺少强校验。
- 重复登记阈值
>=2是否符合产品口径。 claim_number与claim_num混用是否为线上缺陷。- 状态枚举与损坏类型枚举混用代码的实际影响。
- 理赔单号唯一索引和碰撞处理。
- 理赔状态更新的行锁/条件更新。
- MQ publisher confirm、重试和死信。
- 图片 OSS 生命周期、鉴权和物理删除。
- 采购退货与理赔更新是否处于同一数据库事务。
- OPS 最终拒绝和普通退货审核驳回的产品选择条件。
60. 证据来源
application/controllers/inner/tyre/User.phpapplication/controllers/inner/tyre/Registration.phpapplication/controllers/inner/tyre/Claim.phpapplication/controllers/inner/tyre/Property.phpapplication/controllers/tyre/Claim.phpapplication/Services/Tyre/RegistrationService.phpapplication/Services/Tyre/ClaimService.phpapplication/Services/Tyre/PropertyService.phpapplication/Services/PoOrders/OrderService/BadGoodsOrderSaveService.phpapplication/service/scm/InvPoService.phpapplication/models/tyre/RegistrationModel.phpapplication/models/tyre/ClaimModel.phpapplication/models/tyre/PropertyModel.phpapplication/models/bs/ImageModel.phpapplication/Validate/inner/Tyre/*application/KzData/Enums/Tyre/*application/KzData/Enums/ImageEnums.phpapplication/KzData/Enums/PoOrderEnums.phpapplication/KzData/Enums/MessageTypeEnums.phpapplication/config/tables.php
61. 一页式交接结论
- 一张登记对应多条生产周期,每条周期预建一条状态 0 理赔占位。
- 登记保存属性 ID 和名称快照,避免字典变化污染历史。
- 登记、占位、图片同事务;理赔提交还会在事务内发送消息,存在跨系统窗口。
- 状态 0 占位提交后才生成理赔单号并进入待确认 1。
- 待确认理赔通过
claim_id创建来源类型 11 的不良品采购退货单。 - 退货绑定后理赔状态 2;审核驳回回 1;仓审通过变 3。
- 取消/最终拒绝不删除历史,而是原记录置 99 并新建占位。
- 物理理赔行数可大于登记数量,数量核对必须排除 99。
- 全部有效轮胎理赔通过后登记才完成。
- 重复阈值、字段名混用、过期 API 校验、单号碰撞和并发状态更新是当前优先风险。
请求-日志-数据变更追踪卡
多入口请求链路
| 场景 | 调用方与入口 | 请求载荷/上下文 | Controller/Consumer | Service/Provider | 汇合点 | 最终业务事实 |
|---|---|---|---|---|---|---|
| 安装登记 | 轮胎小程序 /inner/tyre/Registration | 用户、车辆、轮胎、门店、安装时间、图片 | Registration Controller | RegistrationService | registration ID | 安装登记与图片关系落库 |
| 理赔提交 | /inner/tyre/Claim | 登记、故障、图片、理赔商品 | Claim Controller | ClaimService | claim ID | 理赔申请创建并进入审核 |
| OPS 审核 | PC /tyre/Claim | claim ID、审核结果、处理意见 | Tyre Claim Controller | ClaimService | claim ID | 审核状态推进,必要时生成坏品采购/退货 |
| 属性配置 | /inner/tyre/Property/管理页 | 品牌、规格、属性枚举 | Property Controller | PropertyService | property ID | 小程序可选属性配置更新 |
日志证据矩阵
| 链路段 | 日志来源 | 可检索锚点 | 成功信号 | 失败信号 | 与下一段关联方式 | | --- | --- | --- | --- | --- | --- | --- | | 登记 | Registration Controller/Service | request_id、registration no、用户/车辆/轮胎 | 主记录与图片关系 commit | 重复阈值、单号碰撞、字段校验失败 | registration ID 进入理赔 | | 理赔申请 | ClaimService | claim no、registration ID、图片 ID | claim pending 一次创建 | 重复申请、登记不可赔、图片缺失 | claim ID 查审核与订单关系 | | 审核/坏品单 | OPS Claim/BadGoodsOrderSaveService | claim ID、action、采购单号 | 状态推进且坏品单关系完整 | 并发审核、采购创建部分失败 | claim ID + PO billNo | | 属性查询 | PropertyService | property ID/code、版本/更新时间 | 返回当前有效配置 | 字段名混用、旧缓存 | property key 比 DB/响应 |
环节数据变更台账
| 步骤 | 代码位置 | 事务 | 读取事实 | 写入表/缓存/MQ | 字段或数量变化 | 回查证据 |
|---|---|---|---|---|---|---|
| 保存登记 | RegistrationService | 登记事务 | 用户车辆、轮胎唯一性、图片 | Registration、BS_IMAGE/关系 | insert;type=TYPE_REGISTRATION_INSTALL | 登记号、图片 ID、创建时间 |
| 提交理赔 | ClaimService | 理赔事务 | 登记状态、历史理赔数 | Claim/明细/图片关系 | insert;status=TYRE_CLAIM_SUBMIT | claim no、registration ID |
| 审核 | ClaimService | 审核事务 | 当前 claim 状态、action | Claim/审核记录 | submitted -> approved/rejected,旧状态不得覆盖 | 操作人、影响行数 |
| 生成坏品订单 | BadGoodsOrderSaveService/InvPoService | 跨理赔/采购边界需验证 | approved claim、商品数量 | 采购主明细、claim 关系 | PO insert;claim 写关联单号/处理态 | claim+PO 数量和类型 |
| 属性维护 | PropertyService | 配置事务 | 原属性值和使用情况 | Property 表/缓存 | value/status/order old -> new | property ID、更新时间、小程序返回 |
子模块追踪:tyre-user-property 轮胎用户与属性配置
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 用户查询 | 小程序用户登录/资料查询 | tyre user ID、mobile hash、sid | application/controllers/inner/tyre/User.php | 用户映射、站点、状态和权限 | 查询只读或用户关系本地事务 none/old -> active | request ID + user/sid | 个人信息脱敏;无权站点零业务写入 |
| 属性维护 | 运营新增/排序/停用轮胎属性 | property ID、type/value/order | application/controllers/inner/tyre/Property.php -> application/Services/Tyre/PropertyService.php | 原属性、重复值和使用情况 | 配置本地事务 value/status/order old -> new | request ID + property/type + operator | 停用不破坏历史登记;缓存 commit 后失效 |
子模块追踪:tyre-registration 轮胎安装登记
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 保存登记 | 小程序/Inner 提交车辆和轮胎 | registrationNo、user/vehicle、tyre code | application/controllers/inner/tyre/Registration.php -> application/Services/Tyre/RegistrationService.php | 用户、车辆、轮胎唯一性、属性和重复登记 | 登记本地事务 insert 主明细,status none -> registered | request ID + registrationNo + tyre code hash | 重复轮胎/字段非法零写入;超时按登记业务键回查 |
| 登记回查 | 登记成功但详情/图片缺失 | registration ID、image relation IDs | application/models/tyre/RegistrationModel.php | 主表、属性快照和图片关系 | 查询只读 不写 | registration + relation/image counts | 只有主表时用领域编辑补关系,不重复登记主体 |
子模块追踪:tyre-image 登记与理赔图片
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 上传关联 | 登记/理赔上传图片 | owner type/ID、image ID、type/order | application/models/bs/ImageModel.php -> application/KzData/Enums/ImageEnums.php | 文件可用、归属、图片类型和原关系 | 文件外部边界;本地关系事务 insert/update old -> new | request ID + owner/image ID + type | 文件失败不提交业务关系;敏感图 URL 权限校验 |
| 删除回查 | 替换/删除图片 | owner/image relation ID | application/Services/Tyre/ClaimService.php | 所有权、业务状态和引用数 | 本地事务软删/解绑;对象存储清理事务外 | owner/image ID + affected rows | 已审核证据按规则不可删;对象清理失败只补文件段 |
子模块追踪:tyre-claim 理赔申请
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 提交理赔 | 已登记轮胎发起理赔 | claimNo、registration ID、reason/images | application/controllers/inner/tyre/Claim.php -> application/Services/Tyre/ClaimService.php | 登记态、历史理赔数、窗口、图片和重复申请 | 理赔本地事务 insert 主明细/关系,status none -> TYRE_CLAIM_SUBMIT | request ID + claim/registration + image count | 不可赔/重复零写入;超时按登记+申请键回查 |
| 申请回查 | 理赔详情/数量异常 | claim ID、registration ID | application/models/tyre/ClaimModel.php | claim、登记、商品/图片明细 | 查询只读 不写 | claim/registration + row counts | 孤儿关系通过领域取消/重建,不手补采购关系 |
子模块追踪:tyre-claim-audit 理赔审核
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 审核 | 运营通过/拒绝理赔 | claimNo、action、operator、approved qty | application/controllers/tyre/Claim.php -> application/Services/Tyre/ClaimService.php | submitted、证据图片、登记和可赔规则 | 审核本地事务 submitted -> approved/rejected,写审核记录 | request ID + claim + action/operator | 旧状态请求零变化;通过后后置建单不在同一外部边界 |
| 审核后置 | approved 触发坏品处理 | claimNo、approved item/qty | application/Services/PoOrders/OrderService/BadGoodsOrderSaveService.php | 已审批、已有采购关系和商品数量 | 建单本地事务后回写关系;重复 claim 0 新单 | claim + po billNo + qty | 建单失败保持可补偿标记,不回退审核事实 |
子模块追踪:tyre-bad-po 坏品采购退货联动
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 创建坏品单 | approved claim 转采购退货/坏品单 | claimNo、po billNo、SKU、qty | application/Services/PoOrders/OrderService/BadGoodsOrderSaveService.php -> application/service/scm/InvPoService.php | 理赔终态、商品、来源唯一性和采购类型 | 采购本地事务 insert 主明细/claim 关系,none -> created | request ID + claim/po billNo + orderType | 来源守卫避免影响普通采购;部分失败整单回滚 |
| 履约回查 | 理赔通过但坏品退货未完成 | claim/po/inventory billNos | application/KzData/Enums/PoOrderEnums.php | 采购退货、库存负向和理赔处理态 | 查询只读;采购完成后才推进 claim 处理完成 | three billNos + SKU/transType | 采购成功只补理赔关系/状态,不重复出库 |
子模块追踪:tyre-compensate 理赔状态补偿
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 差异扫描 | claim 状态与采购/库存事实不一致 | batch、claim/po IDs | application/Services/Tyre/ClaimService.php | 审核、采购关系、履约和已有补偿 | 扫描查询只读;生成明确失败段 | batch + claim/po + diff type | 未证明外部/采购终态不执行写补偿 |
| 幂等补偿 | 小批修复 claim 关系/处理态 | claimNo、compensate key | application/Services/Tyre/ClaimService.php | 修复前快照、目标事实和当前状态 | 每 claim 本地事务只补缺失 old -> expected;重跑 0 变化 | batch + claim + before/after + rows | 不重做采购或库存成功段;生产写需授权 |