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::authnumber、passwordContactModel 等只读认证
用户User::infocontact_id客户/地址/站点模型只读
用户User::modifyPassword联系人、原/新密码ContactModel更新密码
属性inner/tyre/Property::list无核心业务参数PropertyService只读
登记Registration::listcontact_id、分页、车牌RegistrationService::list只读并计算按钮
登记Registration::infoid、contact_idRegistrationService::info只读
登记Registration::create车辆、轮胎、周期、图片RegistrationService::create新登记、占位理赔、图片
登记Registration::edit取消单 ID + 完整登记RegistrationService::edit重建占位理赔和图片
登记Registration::cancelid、联系人上下文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_infoidClaimService::detailInfoOPS 组合详情

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/name
  • property_specification_id/name
  • property_fineness_ratio_id/name
  • property_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_number
  • damage_type
  • claim_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_INSTALLinstall_images装车图片
TYPE_REGISTRATION_NEW = 26new_images新胎整体照
TYPE_REGISTRATION_CLOSE_UP = 27close_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_cancelcan_claimcan_editcan_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,执行:

  1. 重新校验客户和属性。
  2. 开启事务。
  3. 更新完整登记并强制状态回 0。
  4. 删除该登记的全部理赔记录。
  5. 按新生产周期重建占位理赔。
  6. 删除三类登记图片并重建。
  7. 提交事务。

编辑不是局部更新,会删除原理赔占位。状态保护必须确保不存在已进入售后的真实理赔。

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_type1 裂口,2 鼓包
三组图片各 1-4 张
sid/contact_id当前客户身份;Service 主要按 contact_id 查记录

23. 理赔图片

ImageEnums 类型API 键含义
4damage_images索赔胎整体/损坏照片
28location_images故障位置照片
29production_images索赔胎生产周期照片
5separate_imageOPS 补充的割胎照片

前三组提交时强制存在;割胎图在待确认阶段通过 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 补割胎照片

仅待确认状态允许:

  1. 删除 f_id = claim.id,type=5 的旧割胎图。
  2. 将逗号分隔 separate_image 转为数组。
  3. 批量插入新图片。
  4. 整体在事务中完成。

空图片数组时 insert_batch([]) 的框架行为需测试;是否允许清空割胎图由产品确认。

30. 理赔取消

仅待确认状态可取消。当前实现不是把原记录恢复为占位,而是:

  1. 查登记并准备 claim_num - 1。
  2. 从原理赔复制 registration_id/sid/contact_id/production_date/create_time,新插一条占位。
  3. 原理赔状态改 99。
  4. 更新登记数量/状态。
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):

  1. 按退货单 ID 查理赔。
  2. 理赔状态更新为通过 3。
  3. 查询同登记所有理赔记录。
  4. 统计状态 3 的数量。
  5. 若通过数等于 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. 故障树:不能提交理赔

按顺序检查:

  1. 理赔 ID 是否为该登记的状态 0 占位。
  2. contact_id 是否匹配。
  3. 登记是否运营车辆。
  4. claim_num < registration_num。
  5. 三组图片是否各 1-4 张。
  6. 损坏类型是否 1/2。
  7. 登记是否超过一年;当前后端可能缺强校验。
  8. 是否存在并发请求已先改变状态。

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 登记有主表但占位/图片不全

正常事务应全有或全无。先确认是否历史代码或人工数据:

  1. 导出登记、理赔、图片快照。
  2. 按 registration_num 和生产周期确认缺失项。
  3. 未发生真实理赔时才能补占位。
  4. 图片只按原文件凭证补,不能伪造。
  5. 补后执行数量守恒巡检。

50.2 理赔已绑定退货但状态仍 1

  1. 查退货单是否真实创建成功。
  2. 查退货扩展 claim_id。
  3. 确认没有另一理赔绑定同一退货单。
  4. 在退货单仍处于对应前置状态时重放 auditClaim 或业务补偿。
  5. 验证状态 2 和 aftersale_order_id 同时存在。

50.3 仓审通过但登记未完成

  1. 查同登记全部状态 3 数量。
  2. 排除状态 99 历史行。
  3. 比较 registration_num。
  4. 若每条真实轮胎均已通过,可重放 finishClaim。
  5. 若数据缺失,先修复理赔关系,不能直接把登记改完成。

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. 改动风险

级别代码最低回归
P0ClaimService 状态、计数、绑定全理赔+采购退货联动
P0InvPoService 轮胎来源分支普通退货和轮胎退货隔离
P1登记编辑/取消/占位重建全状态和数量守恒
P1图片类型枚举登记、理赔、割胎和其他图片业务
P1用户认证密码升级默认密码、MD5、登录迁移
P1Validator新旧客户端请求兼容
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.php
  • application/controllers/inner/tyre/Registration.php
  • application/controllers/inner/tyre/Claim.php
  • application/controllers/inner/tyre/Property.php
  • application/controllers/tyre/Claim.php
  • application/Services/Tyre/RegistrationService.php
  • application/Services/Tyre/ClaimService.php
  • application/Services/Tyre/PropertyService.php
  • application/Services/PoOrders/OrderService/BadGoodsOrderSaveService.php
  • application/service/scm/InvPoService.php
  • application/models/tyre/RegistrationModel.php
  • application/models/tyre/ClaimModel.php
  • application/models/tyre/PropertyModel.php
  • application/models/bs/ImageModel.php
  • application/Validate/inner/Tyre/*
  • application/KzData/Enums/Tyre/*
  • application/KzData/Enums/ImageEnums.php
  • application/KzData/Enums/PoOrderEnums.php
  • application/KzData/Enums/MessageTypeEnums.php
  • application/config/tables.php

61. 一页式交接结论

  1. 一张登记对应多条生产周期,每条周期预建一条状态 0 理赔占位。
  2. 登记保存属性 ID 和名称快照,避免字典变化污染历史。
  3. 登记、占位、图片同事务;理赔提交还会在事务内发送消息,存在跨系统窗口。
  4. 状态 0 占位提交后才生成理赔单号并进入待确认 1。
  5. 待确认理赔通过 claim_id 创建来源类型 11 的不良品采购退货单。
  6. 退货绑定后理赔状态 2;审核驳回回 1;仓审通过变 3。
  7. 取消/最终拒绝不删除历史,而是原记录置 99 并新建占位。
  8. 物理理赔行数可大于登记数量,数量核对必须排除 99。
  9. 全部有效轮胎理赔通过后登记才完成。
  10. 重复阈值、字段名混用、过期 API 校验、单号碰撞和并发状态更新是当前优先风险。

请求-日志-数据变更追踪卡

多入口请求链路

场景调用方与入口请求载荷/上下文Controller/ConsumerService/Provider汇合点最终业务事实
安装登记轮胎小程序 /inner/tyre/Registration用户、车辆、轮胎、门店、安装时间、图片Registration ControllerRegistrationServiceregistration ID安装登记与图片关系落库
理赔提交/inner/tyre/Claim登记、故障、图片、理赔商品Claim ControllerClaimServiceclaim ID理赔申请创建并进入审核
OPS 审核PC /tyre/Claimclaim ID、审核结果、处理意见Tyre Claim ControllerClaimServiceclaim ID审核状态推进,必要时生成坏品采购/退货
属性配置/inner/tyre/Property/管理页品牌、规格、属性枚举Property ControllerPropertyServiceproperty 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_SUBMITclaim no、registration ID
审核ClaimService审核事务当前 claim 状态、actionClaim/审核记录submitted -> approved/rejected,旧状态不得覆盖操作人、影响行数
生成坏品订单BadGoodsOrderSaveService/InvPoService跨理赔/采购边界需验证approved claim、商品数量采购主明细、claim 关系PO insert;claim 写关联单号/处理态claim+PO 数量和类型
属性维护PropertyService配置事务原属性值和使用情况Property 表/缓存value/status/order old -> newproperty ID、更新时间、小程序返回

子模块追踪:tyre-user-property 轮胎用户与属性配置

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
用户查询小程序用户登录/资料查询tyre user ID、mobile hash、sidapplication/controllers/inner/tyre/User.php用户映射、站点、状态和权限查询只读或用户关系本地事务 none/old -> activerequest ID + user/sid个人信息脱敏;无权站点零业务写入
属性维护运营新增/排序/停用轮胎属性property ID、type/value/orderapplication/controllers/inner/tyre/Property.php -> application/Services/Tyre/PropertyService.php原属性、重复值和使用情况配置本地事务 value/status/order old -> newrequest ID + property/type + operator停用不破坏历史登记;缓存 commit 后失效

子模块追踪:tyre-registration 轮胎安装登记

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
保存登记小程序/Inner 提交车辆和轮胎registrationNo、user/vehicle、tyre codeapplication/controllers/inner/tyre/Registration.php -> application/Services/Tyre/RegistrationService.php用户、车辆、轮胎唯一性、属性和重复登记登记本地事务 insert 主明细,status none -> registeredrequest ID + registrationNo + tyre code hash重复轮胎/字段非法零写入;超时按登记业务键回查
登记回查登记成功但详情/图片缺失registration ID、image relation IDsapplication/models/tyre/RegistrationModel.php主表、属性快照和图片关系查询只读 不写registration + relation/image counts只有主表时用领域编辑补关系,不重复登记主体

子模块追踪:tyre-image 登记与理赔图片

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
上传关联登记/理赔上传图片owner type/ID、image ID、type/orderapplication/models/bs/ImageModel.php -> application/KzData/Enums/ImageEnums.php文件可用、归属、图片类型和原关系文件外部边界;本地关系事务 insert/update old -> newrequest ID + owner/image ID + type文件失败不提交业务关系;敏感图 URL 权限校验
删除回查替换/删除图片owner/image relation IDapplication/Services/Tyre/ClaimService.php所有权、业务状态和引用数本地事务软删/解绑;对象存储清理事务外owner/image ID + affected rows已审核证据按规则不可删;对象清理失败只补文件段

子模块追踪:tyre-claim 理赔申请

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
提交理赔已登记轮胎发起理赔claimNo、registration ID、reason/imagesapplication/controllers/inner/tyre/Claim.php -> application/Services/Tyre/ClaimService.php登记态、历史理赔数、窗口、图片和重复申请理赔本地事务 insert 主明细/关系,status none -> TYRE_CLAIM_SUBMITrequest ID + claim/registration + image count不可赔/重复零写入;超时按登记+申请键回查
申请回查理赔详情/数量异常claim ID、registration IDapplication/models/tyre/ClaimModel.phpclaim、登记、商品/图片明细查询只读 不写claim/registration + row counts孤儿关系通过领域取消/重建,不手补采购关系

子模块追踪:tyre-claim-audit 理赔审核

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
审核运营通过/拒绝理赔claimNo、action、operator、approved qtyapplication/controllers/tyre/Claim.php -> application/Services/Tyre/ClaimService.phpsubmitted、证据图片、登记和可赔规则审核本地事务 submitted -> approved/rejected,写审核记录request ID + claim + action/operator旧状态请求零变化;通过后后置建单不在同一外部边界
审核后置approved 触发坏品处理claimNo、approved item/qtyapplication/Services/PoOrders/OrderService/BadGoodsOrderSaveService.php已审批、已有采购关系和商品数量建单本地事务后回写关系;重复 claim 0 新单claim + po billNo + qty建单失败保持可补偿标记,不回退审核事实

子模块追踪:tyre-bad-po 坏品采购退货联动

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
创建坏品单approved claim 转采购退货/坏品单claimNo、po billNo、SKU、qtyapplication/Services/PoOrders/OrderService/BadGoodsOrderSaveService.php -> application/service/scm/InvPoService.php理赔终态、商品、来源唯一性和采购类型采购本地事务 insert 主明细/claim 关系,none -> createdrequest ID + claim/po billNo + orderType来源守卫避免影响普通采购;部分失败整单回滚
履约回查理赔通过但坏品退货未完成claim/po/inventory billNosapplication/KzData/Enums/PoOrderEnums.php采购退货、库存负向和理赔处理态查询只读;采购完成后才推进 claim 处理完成three billNos + SKU/transType采购成功只补理赔关系/状态,不重复出库

子模块追踪:tyre-compensate 理赔状态补偿

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
差异扫描claim 状态与采购/库存事实不一致batch、claim/po IDsapplication/Services/Tyre/ClaimService.php审核、采购关系、履约和已有补偿扫描查询只读;生成明确失败段batch + claim/po + diff type未证明外部/采购终态不执行写补偿
幂等补偿小批修复 claim 关系/处理态claimNo、compensate keyapplication/Services/Tyre/ClaimService.php修复前快照、目标事实和当前状态每 claim 本地事务只补缺失 old -> expected;重跑 0 变化batch + claim + before/after + rows不重做采购或库存成功段;生产写需授权