mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-04-20 11:05:18 +08:00
9.7 KiB
9.7 KiB
万车充小程序未分账补分账 SOP(adapayMemberId=0)
1. 目标与背景
- 目标:将未分账订单批量向汇付发起
PaymentConfirm请求,分账对象固定为adapayMemberId=0(平台默认账户)。 - 数据来源:
doc/万车充小程序-未分账明细.xlsx。 - 编写日期:2026-03-11(Asia/Shanghai)。
说明:本 SOP 面向“拿到一份未分账明细文件后,批量补分账”的场景。文档中的流程可复用于后续同类任务。
2. 本次文件分析结论
对 doc/万车充小程序-未分账明细.xlsx 的 Sheet1 统计结果如下:
- 总记录数:
42,225 paymentId去重后重复数:0app_id:全部为app_d0c80cb1-ffc8-48cb-a030-fe9bec823aaa- 支付时间范围:
2024-09-07 19:29:00~2026-03-06 00:11:20 - 未分账金额(列“剩余未分账金额”)
- 合计:
171,710.73 - 最小:
0.01 - 中位数:
0.50 - P90:
8.45 - 最大:
268.39
- 合计:
- 金额分布:
0.01~1:34,3671~10:4,01810~50:3,06150~100:507>=100:272
- 交易订单号格式:
42,223/42,225包含下划线(通常为orderCode_时间戳)
关键结论:
- 量大(4.2 万+),必须批处理+可重试+可追踪。
- 文件最晚数据到
2026-03-06,执行时必须先查汇付最新状态,不能盲目按文件金额直发。 - 小额单非常多(0.01~1 占比高),要考虑接口速率与批次执行时长。
3. 现有代码落点(可复用)
- 发起汇付分账请求:
jsowell-pile/src/main/java/com/jsowell/adapay/service/AdapayService.java- 方法:
createPaymentConfirmRequest(PaymentConfirmParam param)
- 查询支付确认列表:
- 同上
queryPaymentConfirmList(QueryPaymentConfirmDTO dto)
- 同上
- 分账对象模型:
DivMember.memberId设为0,feeFlag设为Y
- 已有“未分账处理”参考(测试代码):
jsowell-admin/src/test/java/PaymentTestController.java- 方法:
processUnSettledOrder()
注意:
jsowell-quartz里的processUnSettledOrder()当前版本(V2)主要是“查汇付确认信息”,不是生产可直接执行的补分账任务。
4. 推荐执行方案(生产可落地)
4.1 方案总览
分三阶段执行:
- 任务清单准备
- 批量补分账执行
- 结果复核与失败重试
建议新增一个“一次性批处理工具(仅本次任务分支)”,不要直接依赖手工接口逐条处理。
4.2 任务清单准备
从 Excel 抽取以下字段形成任务清单(CSV/临时表均可):
payment_id← 交易流水号source_order_no← 交易订单号order_code←source_order_no按_分割取前半段;若无_则回退原值pay_amt← 交易订单金额file_confirmed_amt← 已确认分账金额file_reserved_amt← 支付确认撤销金额file_remaining_amt← 剩余未分账金额pay_time← 支付时间
过滤条件:
file_remaining_amt > 0payment_id非空
4.3 批量补分账执行(核心)
单条任务处理逻辑(必须幂等):
- 用
payment_id调queryPaymentConfirmList查询汇付最新确认信息。 - 计算“执行时最新可分账金额”
latest_remaining_amt:- 基线金额使用文件中的
file_remaining_amt。 - 若汇付返回了确认信息,使用
pay_amt - confirmed_amt - reserved_amt再算一遍,取两者较小值(且不小于 0)。
- 基线金额使用文件中的
- 若
latest_remaining_amt <= 0:跳过(记为SKIPPED_NO_REMAINING)。 - 构造分账请求:
paymentId = payment_idconfirmAmt = latest_remaining_amtdivMemberList = [{ memberId: "0", amount: latest_remaining_amt, feeFlag: "Y" }]orderCode = order_codewechatAppId = 对应 appId
- 调用
createPaymentConfirmRequest(param)。 - 按响应记录结果:
- 成功:记录
payment_confirm_id、confirm_amt、fee_amt - 失败:记录
error_code、error_msg
- 成功:记录
建议执行参数:
- 批次:每批
500(或200起步) - QPS:
3~5(每笔 sleep200~300ms) - 失败重试:对可重试错误最多
2次
建议错误处理:
confirm_amt_over_limit:立即重查汇付最新金额,按新金额重试一次。payment_over_time_doing/refund_repeate_request:放入重试队列,延迟重跑。- 其他错误:记录失败,人工复核。
4.4 结果复核与验收
必须输出三类结果文件(CSV):
success.csv:成功明细(含 paymentConfirmId)skipped.csv:跳过明细(无剩余可分账)failed.csv:失败明细(含错误码)
验收标准:
success + skipped + failed = 总任务数- 对
success样本抽查queryPaymentConfirmList,确认div_members包含memberId=0 failed有明确可重试策略或人工处理结论
4.5 回滚预案(必要时)
如果误分账到错误对象或金额错误:
- 从
success.csv取payment_confirm_id - 调用
createConfirmReverse(paymentConfirmId, wechatAppId)撤销支付确认 - 撤销后重新按正确参数执行补分账
代码参考:
AdapayService.createConfirmReverse(...)
5. 下次同类任务复用清单
执行前 Checklist:
- 明细文件是否包含
payment_id、remaining_amt、pay_amt - 是否确认目标账户(本 SOP 为
memberId=0) - 是否先做 100 条小批次试跑
- 是否有完整执行日志与
success/skipped/failed三份结果 - 是否准备了
payment_confirm_id级别回滚能力
执行后 Checklist:
- 失败项是否重试完毕
- 关键样本是否验证到汇付侧最新状态
- 本次任务结果文件是否归档到
doc/或运维归档目录
6. 当前已落地入口
已新增任务方法(JsowellTask):
importAdapayUnsplitRecordAndCompleteFields()importAdapayUnsplitRecordAndCompleteFields(String filePath)
实现位置:
jsowell-quartz/src/main/java/com/jsowell/quartz/task/JsowellTask.java
方法职责:
- 从 Excel 导入
adapay_unsplit_record(按insertOrUpdateSelective,支持重复执行) - 自动补齐
order_code / due_refund_amount / settle_amount / pile_type等缺失字段 - 输出导入与补齐统计日志
执行示例:
- 使用默认路径:
jsowellTask.importAdapayUnsplitRecordAndCompleteFields() - 指定路径:
jsowellTask.importAdapayUnsplitRecordAndCompleteFields('doc/万车充小程序-未分账明细.xlsx')
7. 完整执行流程(落地版)
以下流程是当前代码已支持、可直接按顺序执行的标准操作。
7.1 执行前检查
- 使用 JDK8。
- 编译通过:
mvn -pl jsowell-quartz -am -DskipTests compile
- 确认 Excel 文件就绪:
- 默认路径:
doc/万车充小程序-未分账明细.xlsx - 或你自己的绝对路径/相对路径
7.2 第一步:导入并补齐缺失字段
执行任务方法(任选其一):
- 默认路径:
jsowellTask.importAdapayUnsplitRecordAndCompleteFields()
- 指定路径:
jsowellTask.importAdapayUnsplitRecordAndCompleteFields('doc/万车充小程序-未分账明细.xlsx')
本步骤会自动完成:
- Excel ->
adapay_unsplit_record导入(insertOrUpdateSelective,可重复执行) - 自动补齐
order_code / due_refund_amount / settle_amount / pile_type - 输出导入统计与补齐统计日志
7.3 第二步:导入后校验
建议执行以下 SQL:
-- 总量
SELECT COUNT(*) AS total_cnt
FROM adapay_unsplit_record;
-- 核心补齐字段缺失情况
SELECT COUNT(*) AS missing_cnt
FROM adapay_unsplit_record
WHERE order_code IS NULL
OR settle_amount IS NULL
OR due_refund_amount IS NULL
OR pile_type IS NULL;
missing_cnt 理想结果应接近 0(少量异常数据可人工排查)。
7.4 第三步:执行未分账处理(分账到 memberId=0)
执行任务方法(任选其一):
- 使用默认 appId(
Constants.DEFAULT_APP_ID)+ pageSize=500:
jsowellTask.processUnsplitRecordToDefaultMember()
- 指定 appId 和分页大小:
jsowellTask.processUnsplitRecordToDefaultMember('你的wechatAppId', 200)
建议先用 200 小批量试跑,再提升到 500。
本步骤会自动完成:
- 分页读取
queryList()的待分账记录 - 先查汇付最新确认信息,计算实时剩余可分账金额
- 取
min(数据库待分账金额, 汇付实时剩余金额)作为本次confirmAmt - 调
PaymentConfirm分账,分账对象固定memberId=0 - 回写结果:
- 成功:更新
confirmed_split_amount,split_flag=SUCCESS - 失败:
split_flag=FAILED
- 成功:更新
7.5 第四步:循环执行直到待分账清零
每轮执行后,建议跑以下 SQL:
-- 按当前业务口径,仍待处理数量
SELECT COUNT(*) AS remain_cnt
FROM adapay_unsplit_record
WHERE (settle_amount > confirmed_split_amount - payment_revoke_amount)
OR (due_refund_amount > refund_amount);
-- 分账结果分布
SELECT split_flag, COUNT(*) AS cnt
FROM adapay_unsplit_record
GROUP BY split_flag;
当 remain_cnt = 0 可视为本次补分账完成。
7.6 失败重试与回滚
- 失败重试:重新执行
processUnsplitRecordToDefaultMember(...)即可。 - 回滚(如金额或目标账户异常):
- 按
paymentId查询对应payment_confirm_id - 调
createConfirmReverse(paymentConfirmId, wechatAppId)撤销 - 重新执行正确参数的分账流程
- 按
8. 一键操作建议(便于下次复用)
按下面顺序执行,基本可覆盖同类任务:
jsowellTask.importAdapayUnsplitRecordAndCompleteFields('文件路径')- 校验
missing_cnt jsowellTask.processUnsplitRecordToDefaultMember('appId', 200)- 查看
remain_cnt - 若
remain_cnt > 0,重复第 3-4 步,最后切pageSize=500提速