From ae8ced3d6fc623aa1a286fbe81ca1d82fd8de0f4 Mon Sep 17 00:00:00 2001 From: zhouyonggao <1971162852@qq.com> Date: Fri, 13 Mar 2026 15:37:10 +0800 Subject: [PATCH] =?UTF-8?q?feat(order):=20=E5=A2=9E=E5=8A=A0=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=B7=B2=E8=BF=87=E6=9C=9F=E7=8A=B6=E6=80=81=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=8F=8A=E7=9B=B8=E5=85=B3=E6=B5=81=E7=A8=8B=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加订单状态「服务已过期(500)」的处理逻辑和返回信息 - 更新幂等验证逻辑,支持已过期状态返回对应提示 - 扩展订单状态流转,允许200状态变更到500状态 - 新增通知任务表,支持服务完成、过期处理和取消补偿三种任务类型 - 实现券码状态同步流程及通知任务执行流程,包含任务重试和失败告警机制 - 订单取消流程支持已过期状态,返回明确错误提示 - 调整接口请求超时设置为5秒,增强调用稳定性 - 修改对账及监控指标,支持任务执行状态和失败告警 - 增加流程图说明,清晰展示状态同步及通知任务执行步骤 --- docs/需求文档-PRD.md | 205 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 178 insertions(+), 27 deletions(-) diff --git a/docs/需求文档-PRD.md b/docs/需求文档-PRD.md index be246ae..9fe8266 100644 --- a/docs/需求文档-PRD.md +++ b/docs/需求文档-PRD.md @@ -102,6 +102,7 @@ - 若订单状态为「服务未预约(100)」,继续执行后续流程(更新订单,不新增) - 若订单状态为「服务已取消(400)」,返回"服务已取消" - 若订单状态为「服务已完成(300)」,返回"服务已生效" + - 若订单状态为「服务已过期(500)」,返回"服务已过期" 5. 请求邮储服务开放平台接口验证权益码是否过期 如果过期: 1、CRM渠道返回 "二维码20分钟内有效,现已超时失效,请联系理财经理重新生成二维码! @@ -144,6 +145,7 @@ - [ ] 幂等验证:相同权益码(code)状态为「服务未预约(100)」时,继续执行后续流程 - [ ] 幂等验证:相同权益码(code)状态为「已取消(400)」时,返回"服务已取消" - [ ] 幂等验证:相同权益码(code)状态为「已完成(300)」时,返回"服务已生效" +- [ ] 幂等验证:相同权益码(code)状态为「已过期(500)」时,返回"服务已过期" - [ ] 权益码过期且渠道为CRM(41)时,返回"二维码20分钟内有效,现已超时失效,请联系理财经理重新生成二维码" - [ ] 权益码过期且渠道为手机银行(12)时,返回"当前页面停置时间过长,请您重新进入该页面" - [ ] 订单创建/更新时初始状态为「服务未预约(100)」 @@ -170,6 +172,7 @@ | TC-3.1.1-009 | 幂等-未预约状态 | 订单状态=100(未预约) | 相同权益码再次请求 | 继续执行流程,最终返回预约结果 | | TC-3.1.1-010 | 幂等-已完成状态 | 订单状态=300(已完成) | 相同权益码再次请求 | code=4, msg="服务已生效" | | TC-3.1.1-011 | 幂等-已取消状态 | 订单状态=400(已取消) | 相同权益码再次请求 | code=4, msg="服务已取消" | +| TC-3.1.1-011a | 幂等-已过期状态 | 订单状态=500(已过期) | 相同权益码再次请求 | code=4, msg="服务已过期" | | TC-3.1.1-012 | 权益码过期-CRM渠道 | 邮储返回权益码已过期 | channel=41 | code=4, msg含"二维码20分钟内有效" | | TC-3.1.1-013 | 权益码过期-手机银行 | 邮储返回权益码已过期 | channel=12 | code=4, msg含"当前页面停置时间过长" | | TC-3.1.1-014 | 权益码过期-电话银行 | 邮储返回权益码已过期 | channel=17 | code=4, msg含"当前页面停置时间过长" | @@ -202,6 +205,7 @@ - 获取锁失败:返回"请勿重复提交" 3. 检查订单状态必须是已预约状态才能取消: - 如果是已取消状态,直接返回取消成功 + - 如果是已过期状态,返回"服务已过期" - 其他非已预约状态,提示"服务已生效无法取消" 4. 调用高端客户权益积分预约服务状态查询接口查询状态: - 如果不是已预约状态且不是已取消状态,提示"服务已生效,无法取消" @@ -234,7 +238,8 @@ **验收标准**: - [ ] 正确解析AES加密的request参数,提取type、code、mac字段 - [ ] 本地订单状态为已取消时,直接返回取消成功 -- [ ] 本地订单状态非已预约且非已取消时,返回"服务已生效无法取消" +- [ ] 本地订单状态为已过期时,返回"服务已过期" +- [ ] 本地订单状态非已预约且非已取消且非已过期时,返回"服务已生效无法取消" - [ ] 调用邮储预约服务状态查询接口,非已预约且非已取消状态时返回"服务已生效,无法取消" - [ ] 调用邮储预约服务状态查询接口,已取消状态时直接返回取消成功 - [ ] 蓝色兄弟作废接口返回作废成功时正常处理 @@ -252,7 +257,8 @@ | TC-3.1.2-001 | 正常取消成功 | 订单状态=已预约(200),邮储状态=已预约,蓝色兄弟作废成功,邮储取消成功 | request=有效加密参数 | code=0, 订单状态=400 | | TC-3.1.2-002 | AES解密失败 | - | request=无效加密字符串 | code=2, msg="参数错误" | | TC-3.1.2-003 | 本地订单状态已取消 | 订单状态=已取消(400) | request=有效加密参数 | code=0, 直接返回取消成功 | -| TC-3.1.2-004 | 本地订单状态非已预约 | 订单状态=已完成(300) | request=有效加密参数 | code=1, msg="服务已生效无法取消" | +| TC-3.1.2-004 | 本地订单状态非已预约 | 订单状态=已完成(300) | request=有效加密参数 | code=4, msg="服务已生效无法取消" | +| TC-3.1.2-004a | 本地订单状态已过期 | 订单状态=已过期(500) | request=有效加密参数 | code=4, msg="服务已过期" | | TC-3.1.2-005 | 本地订单不存在 | 订单不存在 | request=有效加密参数 | code=1, msg="订单不存在" | | TC-3.1.2-006 | 邮储查询状态-已取消 | 订单状态=已预约(200),邮储状态=已取消 | request=有效加密参数 | code=0, 直接返回取消成功 | | TC-3.1.2-007 | 邮储查询状态-已生效 | 订单状态=已预约(200),邮储状态=已生效 | request=有效加密参数 | code=1, msg="服务已生效,无法取消" | @@ -655,8 +661,7 @@ | partner_tx_sri_no | varchar | 24 | M | 请求邮储服务平台的流水号 | | type | varchar | 6 | M | 服务类型 | | provider_code | varchar | 4 | M | 服务商编号(邮储分配的固定值) | -| status | int | 3 | M | 内部订单状态:0异常 100 未预约 200已预约 300已完成 400已取消 500已过期 | - +| status | int | 3 | M | 内部订单状态:100未预约 200已预约 300已完成 400已取消 500已过期 | | appointment_time | datetime | - | N | 预约成功时间 | | mac | varchar | 40 | N | 加密后的客户id | | channel | varchar | 4 | M | 渠道标识:12-手机银行,17-电话银行,41-CRM零售 | @@ -677,7 +682,32 @@ | create_time | datetime | - | M | 创建时间 | -#### 4.1.2 对账记录表(ycjfsc_reconciliation) +#### 4.1.3 通知任务表(ycjfsc_notify_task) + +| 字段名 | 类型 | 长度 | 必填 | 说明 | +|-------|------|-----|-----|------| +| id | bigint | 20 | M | 主键 | +| order_no | varchar | 32 | M | 关联订单号 | +| code | varchar | 32 | M | 权益码 | +| task_type | int | 1 | M | 任务类型:1-服务完成通知 2-过期处理 3-取消补偿 | +| trigger_source | int | 1 | M | 触发来源:1-回调通知 2-主动查询 | +| status | int | 1 | M | 任务状态:0-待执行 1-执行中 2-已完成 3-失败 | +| retry_count | int | 3 | M | 重试次数,默认0 | +| max_retry | int | 3 | M | 最大重试次数,默认8 | +| next_retry_time | datetime | - | N | 下次重试时间 | +| error_msg | varchar | 512 | N | 失败原因 | +| create_time | datetime | - | M | 创建时间 | +| update_time | datetime | - | M | 更新时间 | + +**任务类型说明:** + +| task_type | 说明 | 处理逻辑 | +|-----------|------|----------| +| 1 | 服务完成通知 | 调用邮储服务完成接口 → 订单状态改为300 | +| 2 | 过期处理 | 调用邮储预约取消接口 → 订单状态改为500 | +| 3 | 取消补偿 | 调用邮储预约取消接口 → 订单状态改为400 | + +#### 4.1.4 对账记录表(ycjfsc_reconciliation) | 字段名 | 类型 | 长度 | 必填 | 说明 | |-------|------|-----|-----|------| @@ -692,7 +722,7 @@ | create_time | datetime | - | M | 创建时间 | | update_time | datetime | - | M | 更新时间 | -#### 4.1.3 服务编码映射表(ycjfsc_service_activity_mapping) +#### 4.1.5 服务编码映射表(ycjfsc_service_activity_mapping) | 字段名 | 类型 | 长度 | 必填 | 说明 | |-------|------|-----|-----|------| @@ -710,7 +740,7 @@ 1. **订单号生成规则**:渠道号(2位) + 日期(8位) + 序列号(8位) 2. **外部业务号生成规则**:订单号 + 随机数(4位),保证唯一性和幂等性 -3. **状态流转**:只允许 200→300、200→400 的状态变更 +3. **状态流转**:只允许 200→300、200→400、200→500 的状态变更 4. **数据保留**:订单数据保留至少2年 --- @@ -823,9 +853,9 @@ ycjfsc:token:psbc | 任务 | 执行频率 | 处理逻辑 | |-----|---------|----------| -| 待通知订单扫描 | 每5分钟 | 扫描状态=250且超过1分钟未处理的订单,重新发起通知 | -| 异常订单告警 | 每10分钟 | 扫描状态=350的订单,发送告警通知 | -| 券码状态同步 | 每小时 | 查询蓝色兄弟券码状态,同步本地订单状态 | +| 券码状态同步 | 每小时 | 扫描状态=200(已预约)的订单,查询蓝色兄弟券码状态:已核销(2)→创建服务完成任务,已作废(3)→创建取消补偿任务,已过期(4)→创建过期处理任务 | +| 通知任务重试 | 每5分钟 | 扫描状态=3(失败)且未超过最大重试次数的任务,按退避策略重试 | +| 失败任务告警 | 每10分钟 | 扫描超过最大重试次数的任务,发送告警通知 | **机制四:对账兜底** @@ -833,17 +863,13 @@ ycjfsc:token:psbc - 发现不一致:自动重新发起服务完成通知 - 月度对账:与邮储对账文件核对,确保无遗漏 -#### 5.2.3 订单状态扩展 - -为支持一致性保障,扩展订单状态: +#### 5.2.3 订单状态说明 | 状态码 | 状态名称 | 说明 | |-------|---------|------| | 100 | 服务未预约 | 已兑换但未预约 | | 200 | 服务已预约 | 已预约待使用 | -| 250 | 待通知邮储 | 券码已核销,待通知邮储 | | 300 | 服务已完成 | 券码已核销且已通知邮储 | -| 350 | 通知异常 | 通知邮储多次失败,需人工处理 | | 400 | 服务已取消 | 用户主动取消 | | 500 | 服务已过期 | 超过有效期未使用 | @@ -863,9 +889,9 @@ ycjfsc:token:psbc | 指标 | 阈值 | 告警级别 | |-----|------|----------| -| 待通知订单积压 | > 10笔 | P2 | -| 通知异常订单 | > 0笔 | P1 | -| 通知失败率 | > 1% | P2 | +| 待执行任务积压 | > 10笔 | P2 | +| 失败任务超过最大重试 | > 0笔 | P1 | +| 任务执行失败率 | > 1% | P2 | | 对账差异 | > 0笔 | P1 | --- @@ -939,6 +965,9 @@ elseif (服务已取消 400) then elseif (服务已完成 300) then :返回"服务已生效"; stop +elseif (服务已过期 500) then + :返回"服务已过期"; + stop else (服务未预约 100 或 订单不存在) endif @@ -987,7 +1016,7 @@ endif | 2 | AES解密 | 使用数据库查询到的aes_key和aes_iv对request进行解密,解析出type、code、mac | | 3 | 获取分布式锁 | 基于权益码(code)获取锁,防止重复请求 | | 4 | defer释放锁 | 使用defer机制注册锁释放,确保流程结束时自动释放 | -| 5 | 幂等验证 | 根据订单状态决定后续流程:已预约→返回成功,已取消→返回"服务已取消",已完成→返回"服务已生效" | +| 5 | 幂等验证 | 根据订单状态决定后续流程:已预约→返回成功,已取消→返回"服务已取消",已完成→返回"服务已生效",已过期→返回"服务已过期" | | 6 | 验证权益码 | 调用邮储平台验证权益码有效性(前置校验,5s超时) | | 7 | 创建/更新订单 | 订单状态设为「服务未预约(100)」| | 8 | 发起预约 | 调用邮储平台发起预约(5s超时) | @@ -995,9 +1024,120 @@ endif --- -### 6.2 服务完成流程 +### 6.2 券码状态同步流程 -TODO +```plantuml +@startuml 券码状态同步流程 +start + +:定时任务触发(每小时); + +:扫描状态=200(已预约)的订单; + +while (遍历订单?) is (有) + :请求蓝色兄弟查询券码状态; + note right: 5s超时 + + if (券码状态?) then (已核销 2) + :检查是否已有通知任务; + if (任务存在?) then (否) + :创建通知任务\n(type=1 服务完成); + endif + elseif (已作废 3) then + :检查是否已有通知任务; + if (任务存在?) then (否) + :创建通知任务\n(type=3 取消补偿); + endif + elseif (已过期 4) then + :检查是否已有通知任务; + if (任务存在?) then (否) + :创建通知任务\n(type=2 过期处理); + endif + else (正常 1) + :跳过,等待用户使用; + endif +endwhile (无) + +stop + +@enduml +``` + +**流程步骤说明:** + +| 步骤 | 操作 | 说明 | +|-----|------|------| +| 1 | 定时触发 | 每小时执行一次 | +| 2 | 扫描订单 | 查询状态=200(已预约)的订单 | +| 3 | 查询券码状态 | 调用蓝色兄弟券码查询接口(5s超时) | +| 4 | 创建任务 | 根据券码状态创建对应类型的通知任务(幂等检查) | + +**券码状态与任务类型映射:** + +| 券码状态 | 任务类型 | 处理逻辑 | +|----------|----------|----------| +| 已核销(2) | 服务完成通知(1) | 调用邮储服务完成接口 → 订单状态改为300 | +| 已作废(3) | 取消补偿(3) | 调用邮储预约取消接口 → 订单状态改为400 | +| 已过期(4) | 过期处理(2) | 调用邮储预约取消接口 → 订单状态改为500 | +| 正常(1) | - | 跳过,等待用户使用 | + +### 6.2.1 通知任务执行流程 + +```plantuml +@startuml 通知任务执行流程 +start + +:定时任务触发(每5分钟); + +:扫描状态=0(待执行)或\n状态=3(失败)且未超过最大重试次数的任务; + +while (遍历任务?) is (有) + :更新任务状态为执行中(1); + + if (任务类型?) then (type=1 服务完成) + :调用邮储服务完成接口; + elseif (type=2 过期处理) then + :调用邮储预约取消接口; + else (type=3 取消补偿) + :调用邮储预约取消接口; + endif + note right: 5s超时 + + if (执行成功?) then (是) + :更新任务状态为已完成(2); + :更新订单状态; + note right: type=1→300, type=2→500, type=3→400 + else (否) + :retry_count++; + if (retry_count > max_retry?) then (是) + :任务状态保持失败(3); + :记录错误信息,等待告警; + else (否) + :更新任务状态为失败(3); + :设置下次重试时间(指数退避); + endif + endif +endwhile (无) + +stop + +@enduml +``` + +**重试策略(指数退避):** + +| 重试次数 | 间隔 | 累计时间 | +|----------|------|----------| +| 第1次 | 10秒 | 10秒 | +| 第2次 | 30秒 | 40秒 | +| 第3次 | 1分钟 | 1分40秒 | +| 第4次 | 5分钟 | 6分40秒 | +| 第5次 | 10分钟 | 16分40秒 | +| 第6次 | 30分钟 | 46分40秒 | +| 第7次 | 1小时 | 1小时46分40秒 | +| 第8次 | 2小时 | 3小时46分40秒 | + +超过8次仍失败,任务保持失败状态,等待告警通知后人工介入处理。 --- @@ -1024,6 +1164,9 @@ if (获取锁成功?) then (否) stop endif +:defer 释放分布式锁; +note right: 使用defer机制确保锁在流程结束时释放 + :查询本地订单; if (订单存在?) then (否) @@ -1036,12 +1179,18 @@ if (订单状态=已取消?) then (是) stop endif +if (订单状态=已过期?) then (是) + :返回"服务已过期"; + stop +endif + if (订单状态=已预约?) then (否) :返回"服务已生效无法取消"; stop endif :调用邮储预约服务状态查询接口; +note right: 5s超时 if (邮储状态=已取消?) then (是) :更新本地订单状态为已取消; @@ -1055,6 +1204,7 @@ if (邮储状态=已预约?) then (否) endif :调用蓝色兄弟作废接口; +note right: 5s超时 if (作废成功?) then (否) :调用蓝色兄弟券码查询接口; @@ -1065,6 +1215,7 @@ if (作废成功?) then (否) endif :调用邮储预约取消接口; +note right: 5s超时 if (取消成功?) then (否) :返回"服务取消失败"; @@ -1085,12 +1236,12 @@ stop |-----|------|------| | 1 | AES解密 | 对request进行AES解密,解析出type、code、mac | | 2 | 获取分布式锁 | 基于权益码(code)获取锁,防止重复请求 | -| 3 | 本地订单校验 | 查询订单是否存在,已取消直接返回成功,非已预约返回失败 | -| 4 | 邮储状态查询 | 调用邮储预约服务状态查询接口,确认可取消 | -| 5 | 蓝色兄弟作废 | 调用作废接口,失败时查询券码状态确认 | -| 6 | 邮储预约取消 | 调用邮储预约取消接口,确认取消成功 | -| 7 | 更新订单状态 | 更新本地订单状态为已取消(400) | -| 8 | 释放锁 | 释放分布式锁 | +| 3 | defer释放锁 | 使用defer机制注册锁释放,确保流程结束时自动释放 | +| 4 | 本地订单校验 | 查询订单是否存在,已取消直接返回成功,已过期返回"服务已过期",非已预约返回失败 | +| 5 | 邮储状态查询 | 调用邮储预约服务状态查询接口(5s超时),确认可取消 | +| 6 | 蓝色兄弟作废 | 调用作废接口(5s超时),失败时查询券码状态确认 | +| 7 | 邮储预约取消 | 调用邮储预约取消接口(5s超时),确认取消成功 | +| 8 | 更新订单状态 | 更新本地订单状态为已取消(400) | ---