Files
oil/企油通对接现状分析报告.md
T

83 KiB
Raw Blame History

企油通对接现状分析报告(第一阶段:项目理解与代码调研)

版本:v1 ・ 生成时间:2026-09-01 范围:仅调研,未修改任何代码 / 数据库 / 配置。 结论中凡无法从代码确证的,均标注 【待确认】。


0. 术语与角色对照(先统一叫法)

报告用词 含义 代码 / 数据中的体现
用户 终端加油司机 / 消费者 小程序用户、下单平台的 mobile
下单平台 调用我们接口的外部对接公司 yoshop_partners 表;样例数据只有 1 家「华集云慧」(orgNo=S99535450),回调地址 https://api.chenan.vip/hmb/oilCardChange
我们的系统 本项目 server-tp6 对外品牌名「慧明宝」(见 慧明宝供应链平台接口文档V1.0.16.docx),主体「广西慧明石化有限公司」
上游油品平台 / 四方平台 真正出油券、管油站、管核销的上游 yoshop_company 表;目前有两代对接实现,见第 3、4 节
企油通 本次要接入的新上游平台 企油通接口对接文档.md,域名 worknoya.vip
智慧油客 上一代上游(suoyoubao.com.cn) SybOil / Crypto;企油通文档里就是「passageway=3 已停用」的那个通道
定聚 当前在用上游(idingju.com,中石油 BP /api/cnpcbp/*) IdingjuOil / Idingju

关键判断: 业务链路 用户 → 下单平台 → 我们的系统 → 企油通 中,

  • 「用户 → 下单平台 → 我们的系统」这一段 = 慧明宝入站 API(app/api/controller/OilCard.php),是我们对下单平台的对外契约,本次仅做最小扩展(新增 name 字段),其余 method/URL/字段/加密不变。
  • 「我们的系统 → 上游」这一段 = 用企油通整体替换(智慧油客 + 定聚 全部废弃)。

0.5 决策记录(2026-09-01 与需求方确认)

# 议题 决策
1 废弃范围 智慧油客(SybOil) + 定聚(IdingjuOil) 两套上游全部废弃,统一换成企油通;不需要与定聚灰度并存
2 企油通企业 我们侧只有一个企业,business_license 为单一配置值;线下已开通账户(不需要在系统里做 7.2 创建 / 7.4 开户回调流程)
3 订单映射粒度 一张订单 ↔ 一次 createAndSendQuota(7.7),其中 1 个 batch → 1 条 waybill → 1 个 employee(该订单的司机)
4 分油用户来源 yoshop_account 用户池不保留;司机信息由下单平台提供
5 小程序油品链路 停用:mobile-uniapp 代码不动,但后端支付成功后不再触发任何油品发放/分油/核销逻辑(PaySuccess::shareOil、小程序 order/qrCode(Img)、Station.php 等油品分支停用或摘除)
6 入站契约变更 syb.oil.oilCard.share 新增 name(司机姓名)入参;不新增 id_card。其余字段与加密不变
7 支付通道 默认 = 支付宝企业码(passageway=1),需配置 institution_id;通道值做成后台/配置可切换
8 范围外 app/admin 模块、yoshop_merchant 支付聚合链路(submitOrder/Notify/乐刷)不在本次范围,不动

由决策派生的新约束(需在方案中处理):

  • 决策 6 + 7:passageway=1 时 employee_list 可不传 id_card,只需 mobile + name + quota_amount + voucher_name + institution_id,与「下单平台只加传 name」正好吻合。
  • 但 7.16 司机额度调整 / 7.17 获取支付凭证 均要求 id_card(必填) → 在没有 id_card 的前提下,「撤销分油 / 额度回收」能力受限(见风险 R-07)。
  • 决策 5:小程序历史油券订单(yoshop_order_goods 中大量 zsy_ticket 数据)后续只读展示,不再有新增。

1. 项目架构

1.1 server-tp6(后端,ThinkPHP 6)

多应用结构(app/ 下按模块分目录):

模块 作用 与本次相关度
app/api 对外 HTTP 接口:小程序端 + 下单平台入站 API + 上游回调接收 ★★★ 核心
app/store 后台管理端接口(admin-vue 实际对接的是这里,renderSuccess 风格) ★★★
app/admin yoshop 原生后台模块,仅 controller/{admin,setting,store},疑似未启用 【待确认】 ★
app/console 命令行 / 队列消费者 / 定时任务 ★★★
app/common 公共 model / service / crypto / enum ★★★
  • 路由:route/app.php 基本为空,全部走 TP6 默认「模块/控制器/操作」约定路由。
  • 多入口文件(public/*.php):除 index.php 外,为特定回调 / 场景硬编码了 PATH_INFO:
    入口文件 绑定路由 用途
    public/partners.php 透传到 api 应用 下单平台调用慧明宝 API 的入口(/partners.php/oilCard/share 等)
    public/oil.php /sybNotify/notify 上游「分油/发放结果」回调(小程序链路)
    public/oiluse.php /sybNotify/use 上游「加油/核销结果」回调(小程序链路)
    public/partnersoil.php /sybNotify/partnersNotify 上游「分油结果」回调(下单平台链路)
    public/partnersoiluse.php /sybNotify/partnersUse 上游「核销结果」回调(下单平台链路)
    public/leshua.php / public/notice.php / public/ocp.php — 乐刷支付 / 通知 / opcache 面板(与油品无关)
  • 队列:Redis(Predis),app/common/service/QueueService.php,支持普通队列 + ZSET 延迟队列。消费者在 config/console.php 注册:
    命令 队列 作用
    callback:queue callback_queue 处理上游回调(method: oil / use / oil_partners / use_partners)
    partners:queue partners_queue 下单平台订单入队后 → 调用上游发放
    notify:queue notify_queue 我们 → 下单平台的结果回调(带 3 次退避重试 60/300/1800s)
    card:queue / process:queue / timer — 兑换卡 / 其他 / 定时任务(与油品无关或次要)
  • 其他:GatewayWorker(WebSocket 推送,Gateway::sendToUid);app/common/lock/RedisLock(分油抢占锁)。
  • 日志:上游相关日志统一写 runtime/storelog/{Ym}/{Ymd}/{YmdH}.txt(saveLogStore())。

1.2 admin-vue(后台管理前端)

  • 技术栈:Vue 2 + Ant Design Vue(vue-antd-pro 脚手架),src/config/router.config.js 静态菜单 + src/router/generator-routers.js。
  • API 层:src/api/**,axios 封装(src/utils/request),基地址指向 server-tp6 的 store 应用。
  • 与本次相关的菜单 / 页面见第 6 节。

1.3 mobile-uniapp(小程序端,本次禁止修改)

  • 技术栈:uni-app + uView UI。

  • config.js:apiUrl = https://www.hmzkw.com/index.php?s=/api/,指向 server-tp6 api 应用。

  • 标准 yoshop 商城客户端(商品 / 购物车 / 结算 / 订单 / 微信支付 / 售后)。

  • 仅两处直接触达「油品」语义(其余全部是服务端在支付成功后自动处理):

    api 文件 接口 服务端 依赖上游?
    api/station.js station/list api/controller/Station.php → SybOil::baseGasStationList 是(且当前已坏,见风险)
    api/order.js order/qrCode、order/qrCodeImg api/controller/Order.php → IdingjuOil::payQrCodeRequest 是(核销码/图)
    api/order.js order/list、order/detail、order/cancel、order/pay … 标准商城订单 否(间接)

    order/qrCodeImg 出参是 { base64: "data:image/png;base64,..." },服务端通过 shell_exec("node public/myscript.js <url>") 把上游返回的二维码链接转成图片。这是企油通改造的强约束点(企油通给的是 H5 免登 URL,不是二维码字符串)。


2. 当前订单相关业务流程

2.1 流程 A:小程序商城消费链路(yoshop_order + yoshop_order_goods)

小程序 goods/list,detail
  → checkout/submit  ── 创建 yoshop_order + yoshop_order_goods(order_type='zsy_ticket',oilCardType='zsy_ticket')
  → order/pay(微信支付)
  → 支付回调 → app\api\service\order\PaySuccess::onPaySuccess
       ├─ updatePayStatus → shareOil($detail)   ★ 触发「分油」
       │    · getCompany($goods_no)  硬编码 goods_no→orgNo,取 yoshop_company(status=10)
       │    · processUserOrders($company_id)  RedisLock 抢一个「空闲」分油用户(yoshop_account,state=0 且 last_called_at<今天)
       │    · IdingjuOil::oilOilCardShare(...)  → 定聚 /api/cnpcbp/order
       │      入参 mobile / out_order_id=oilCardNo / shortname=goods_no / notify_url=oil.php / use_notify_url=oiluse.php
       │      出参 code==1,data.trade_no(=shareNo)、data.state(2=已发券)、data.useUrl
       │    · 回写 order_goods:account_id / company_id / shareNo / oil_status_zsy / oil_status
       └─ Event 'OrderPaySuccess'
  → 上游异步回调 public/oil.php (/sybNotify/notify)  → 塞 callback_queue(method='oil')
       → CallbackQueue case 'oil':按 shareNo 命中 order_goods,useStatus==1 → oil_status=1、qrCode=useUrl
  → 用户点核销 → order/qrCode | order/qrCodeImg
       → IdingjuOil::payQrCodeRequest(/api/cnpcbp/getUseCode)→ 写 yoshop_order_qrcode → 返回二维码 URL / base64 图
  → 加油完成 → 上游回调 public/oiluse.php (/sybNotify/use) → callback_queue(method='use')
       → CallbackQueue case 'use':写 yoshop_order_record,useStatus==2 → order_goods.use_status=1、use_time
         若该 order 下所有 order_goods 都 use_status=1 → yoshop_order.order_status=30(已完成)
         释放 yoshop_account(state=0)

订单状态枚举(app/common/enum/order/):

  • OrderStatus:10 进行中 / 20 已取消 / 21 待取消 / 30 已完成
  • PayStatus:10 待支付 / 20 成功 / 30 超时
  • PayType:10 余额 / 20 微信 / 30 转账
  • 油品自定义(在 order_goods / partners_order 字段上,非枚举类):
    • oil_status:0 未分油 / 1 已分油
    • oil_status_zsy:1 待处理 / 2 已发券 / 3 发券失败 / 5 已退单(注释写「中石油下单状态」)
    • use_status:0 未核销 / 1 已核销
    • oil_callback / use_callback:是否已回调下单平台

取消 / 退款:app/api/model/Order.php::cancel() 仅改订单状态(已付款→21 待取消,待后台审核)。app/common/service/order/Refund.php、order/Complete.php 均不触达上游——即小程序订单退款/取消不会撤销已发放的油券。属流程缺口(见风险 14)。

2.2 流程 B:下单平台对接链路(yoshop_partners_order)

入站(下单平台 → 我们,"慧明宝" API,全部 RSA+AES 信封),控制器 app/api/controller/OilCard.php,鉴权 app/common/crypto/RsaUtil.php(平台私钥签名下发 + 合作商公钥验签):

method 头 请求 URL 动作
syb.oil.oilCard.stockList /oilCard/stock 查合作商可下单数量 / 已用 / 未用(读 yoshop_partners_goods + yoshop_partners_order 计数)
syb.oil.oilCard.share /oilCard/share 下单:校验 appKey→yoshop_partners、requestId 幂等(yoshop_request_log 唯一索引)、商品/库存/合作商库存校验 → 创建 yoshop_partners_order(order_status=10) → 扣 goods_sku 库存 → push partners_queue → 返回 oilCardNo + amount
syb.pay.qrCode.request /oilCard/getPayQrCode 申请核销码:IdingjuOil::payQrCodeRequest → 写 yoshop_order_qrcode
syb.oil.oilCard.shareList /oilCard/shareList 分油记录分页查询
syb.oil.oilCard.orderStatus /oilCard/orderStatus 单笔订单状态查询

中段(我们 → 上游):app/console/command/PartnersQueue.php 消费 partners_queue:

  • 取 yoshop_partners_order,order_status==10 && oil_status==0 && oilCardType=='zsy_ticket'
  • getCompany($goods_no) 硬编码取 yoshop_company
  • IdingjuOil::oilOilCardShare(...),notify_url=partnersoil.php、use_notify_url=partnersoiluse.php
  • 回写 shareNo / qrCode / oil_status_zsy / oil_status;若 state==2 → push notify_queue(optType=share_oil)

上游回调(上游 → 我们):

  • public/partnersoil.php → /sybNotify/partnersNotify(method 置为 oil_partners)→ callback_queue
  • public/partnersoiluse.php → /sybNotify/partnersUse(method 置为 use_partners)→ callback_queue
  • CallbackQueue case oil_partners:按 shareNo 命中 yoshop_partners_order,useStatus=='1' → oil_status=1 / qrCode,push notify_queue(share_oil)
  • CallbackQueue case use_partners:写 yoshop_order_record,useStatus=='2' → use_status=1 / use_time,push notify_queue(add_oil)

出站回调(我们 → 下单平台):app/console/command/NotifyQueue.php 消费 notify_queue:

  • 取 yoshop_partners_order + yoshop_partners
  • PartnersOil::sendRequest($partners['callback'], ...),header method='syb.notify.oilCard.change',body {oilCardNo, amount, shareNo, type(add/sub), optType}
  • 期望响应 code=='00';成功回写 oil_callback / use_callback;失败按 1/3 次退避重试(延迟队列)

2.3 流程 C:后台手动运维(admin-vue → app/store)

见第 6 节。核心:手动分油 / 撤销分油 / 补单回调 / 核销码 / 合作商与公司管理 / 分油用户池管理 / 库存同步。

2.4 【待确认】另一条支付聚合链路

app/api/controller/Order.php::submitOrder / searchOrder、app/api/controller/Notify.php、yoshop_merchant(+balance_log+order)、public/leshua.php、config 中的 patformKey.php 等,构成一条「商户 / 代理商 + passageway(WX) + notifyUrl + 乐刷」的第四方支付聚合链路,与油券链路数据表有交叉(order_goods.merchant_id、Company::processUserOrders)。它与本次「企油通油品对接」的关系未确认——可能是历史遗留、可能仍在跑。建议本次不动,单独确认。


3. 当前上游(四方平台)对接流程

3.1 第一代:智慧油客 SybOil(suoyoubao.com.cn)—— 基本已废弃

  • 代码:app/common/service/SybOil.php(+ 早期 app/api/controller/syb/Syb*.php 一组「透传壳」控制器)
  • 加密:app/common/crypto/Crypto.php——RSA(平台公钥加密 AES key) + AES-256-ECB 加密 body + SHA256withRSA 签名,method 头 syb.*,接口路径 /api/1.0.0/...
  • 配置:config/crypto.php(单租户默认参数)或 按 yoshop_company 行(orgNo/appKey/privateKey/publicKey/platformPublicKey)
  • 现状:
    • CallbackQueue 里整段 syb.notify.* 处理逻辑全部被注释,替换为 oil / use / *_partners
    • PaySuccess::shareOil 的 SybOil 版本被注释,改用 IdingjuOil
    • yoshop_company 里 suoyoubao / 124.70.136.175 两行 status=20(停用)
    • 仍在引用但实际已不可用:app/api/controller/Station.php(油站列表,CompanyModel::detail('2') 指向停用公司)、app/store/controller/Goods.php(库存同步)
  • 结论:这是「已废弃的四方平台」最贴切的候选。

3.2 第二代:定聚 IdingjuOil(idingju.com,中石油 BP /api/cnpcbp/*)—— 当前在用

  • 代码:app/common/service/IdingjuOil.php + app/common/crypto/Idingju.php
  • 鉴权:/api/open/getToken(appid=orgNo, appsecret=appKey)拿 access_token,本地缓存到 public/temp/{orgNo}.txt(含 expires_time);业务请求把 access_token / timestamp / signature 拼到 URL query,body 为明文 JSON(不加密)
  • 签名:signature = sha1(api + token + implode(sort(params+token)) + timestamp)
  • 多租户:按 yoshop_company 行(status=10 的 3 行都是 idingju.com 域名)
  • 已实现的接口:
    方法 定聚接口 用途
    baseGasStationList /api/cnpcbp/stations 油站列表(当前 Station.php 用的是 SybOil,未切到这里)
    stockList /api/cnpcbp/queryStock 库存(data.quantity)
    oilOilCardSelect /api/cnpcbp/detail 订单详情(out_order_id 空,形同占位)
    oilOilCardShare /api/cnpcbp/order 下单 / 分油(data.trade_no / state / useUrl)
    payQrCodeRequest /api/cnpcbp/getUseCode 核销二维码(data.useQrCodeUrl)
  • 回调:上游 GET/POST 到 partnersoil.php / partnersoiluse.php / oil.php / oiluse.php(当前回调体未验签,sybNotify 里验签代码被注释,直接原样塞队列);回调关键字段 trade_no(=shareNo)、useStatus('1' 分油成功 / '2' 核销成功)、useUrl
  • 说明:定聚没有「分油成功」的独立推送语义——oilOilCardShare 同步返回 state,或靠 partnersoil.php 回调补。

3.3 出站客户端:PartnersOil(不是油品上游,是「回调下单平台」)

  • app/common/service/PartnersOil.php:方法名沿用 syb.* 风格,但实际是 POST 到 yoshop_partners.callback
  • 加密:RsaUtil(config/rsa.php 的平台私钥 + 合作商公钥),与入站 API 同一套信封

3.4 加密类总览

类 用途 算法 密钥来源
crypto/Crypto.php 智慧油客 SybOil RSA + AES-256-ECB + SHA256withRSA config/crypto.php 或 yoshop_company
crypto/Idingju.php 定聚 IdingjuOil token + sha1 签名,body 明文 yoshop_company
crypto/RsaUtil.php 慧明宝入站 API + 回调下单平台 RSA(加密 AES key) + AES-256-ECB + SHA256withRSA config/rsa.php(平台私钥文件)+ yoshop_partners(合作商公钥)
(企油通需新建) — appId+timestamp+sign(RSA),params 为 JSON 字符串;7.15 特殊 MD5→SHA1withRSA;7.16/7.17 强制 RSA 验签 线下索取

4. 旧四方平台代码位置(清单)

4.1 智慧油客 SybOil(第一代,基本死代码)

文件 说明
app/common/service/SybOil.php 上游客户端(油站/库存/分油/撤销/核销/发票/用户/优惠券等一整套 syb.*)
app/common/crypto/Crypto.php 配套加密
config/crypto.php 配套配置(含明文私钥)
app/api/controller/syb/SybBase.php SybOil.php SybOrder.php SybPay.php SybUser.php SybVoucher.php SybNotify.php 早期一组透传壳控制器(sendRequest 依赖 config('crypto.*'))
app/api/controller/SybNotify.php 仍在用(回调入口路由到这里),含 notify/use/partnersNotify/partnersUse + 被注释的 notifyOld
app/api/controller/Station.php 油站列表,SybOil + 停用公司,实际已坏
app/store/controller/Goods.php(stock/all 附近,~L155-211) 库存同步用 SybOil::stockList
app/console/command/CallbackQueue.php 顶部大段注释的 syb.notify.* 处理逻辑(历史实现)
app/api/service/order/PaySuccess.php(~L182-221 注释块) SybOil 版 shareOil

4.2 定聚 IdingjuOil(第二代,当前在用)

文件 说明
app/common/service/IdingjuOil.php 上游客户端
app/common/crypto/Idingju.php token + sha1
app/console/command/PartnersQueue.php 下单平台链路:消费 partners_queue → IdingjuOil::oilOilCardShare
app/api/service/order/PaySuccess.php(shareOil(),~L253-318) 小程序链路:IdingjuOil::oilOilCardShare
app/api/controller/Order.php(qrCode L310 / qrCodeImg L349) 小程序核销:IdingjuOil::payQrCodeRequest
app/api/controller/OilCard.php(getPayQrCode) 下单平台核销:IdingjuOil::payQrCodeRequest
app/store/controller/Order.php(qrCode / share) 后台手动核销 / 分油
app/store/controller/partners/Order.php(qrCode / share) 后台合作商订单手动核销 / 分油
app/store/controller/Company.php(stock()) 后台库存同步 IdingjuOil::stockList
app/console/command/CallbackQueue.php(case oil / use / oil_partners / use_partners) 回调处理(当前生效实现)

4.3 出站回调下单平台(本次不改)

文件 说明
app/common/service/PartnersOil.php + app/console/command/NotifyQueue.php + config/rsa.php + crypto/RsaUtil.php 我们 → 下单平台的结果回调

4.4 硬编码映射(迁移时必须统一收敛,现散落 7 处)

getCompany($goodsNo)(goods_no → orgNo)与 getGoodsNo($orgNo) / getGoodsNo 反向映射,重复出现在: app/api/controller/OilCard.php、app/api/controller/Order.php、app/api/service/order/PaySuccess.php、app/store/controller/Order.php、app/store/controller/partners/Order.php、app/store/controller/Company.php、app/console/command/PartnersQueue.php。


5. 订单创建 / 提交 / 支付 / 取消 / 退款 / 状态同步 / 回调 代码定位

环节 小程序链路 下单平台链路 后台
创建 / 提交 api/service/order/Checkout.php、api/service/order/source/**、api/model/Order.php::putInto api/controller/OilCard.php::share → api/model/partners/Order.php::saveOrder —
支付 api/controller/Order.php::pay → api/model/Order.php::onPay → 微信支付回调 → api/service/order/PaySuccess.php::onPaySuccess 无「支付」概念(下单即扣合作商库存 / 余额) —
发放 / 分油(触上游) PaySuccess::shareOil() → IdingjuOil::oilOilCardShare console/command/PartnersQueue.php → IdingjuOil::oilOilCardShare store/controller/Order.php::share、store/controller/partners/Order.php::share
核销 / 二维码(触上游) api/controller/Order.php::qrCode / qrCodeImg api/controller/OilCard.php::getPayQrCode store/controller/Order.php::qrCode、store/controller/partners/Order.php::qrCode
取消 api/controller/Order.php::cancel → api/model/Order.php::cancel(仅改状态,不触上游) 无 store/controller/Order.php::updatestatus
撤销分油 store/controller/Order.php::cancelShare —— 调用未定义方法 $this->oilOilCardCancelShare(),当前会致命错误 / 功能已损坏 — admin-vue CancelShareForm.vue → /order/cancelShare
退款 common/service/order/Refund.php(不触上游) 无 —
状态同步(我方←上游) 回调 oil.php / oiluse.php → SybNotify::notify/use → callback_queue → CallbackQueue case oil/use 回调 partnersoil.php / partnersoiluse.php → SybNotify::partnersNotify/partnersUse → callback_queue case oil_partners/use_partners 补单:store/controller/Order.php::callback / partners/Order.php::oilCallback / useCallback
结果回调(我方→下单平台) — console/command/NotifyQueue.php → PartnersOil::sendRequest($partners.callback) 手动触发同 oilCallback / useCallback
完成 api/model/Order.php::receipt / CallbackQueue 使所有券核销后置 order_status=30 CallbackQueue 更新 partners_order —

6. admin-vue 相关页面

菜单定义:admin-vue/src/config/router.config.js

菜单 路由 页面文件 对接 store 接口 与油品对接关系
分油公司 › 公司列表 /company/index views/company/Index.vue /company/index /company/all /company/detail 上游平台账号管理(url/orgNo/appKey/privateKey/publicKey/platformPublicKey)
分油公司 › 创建 / 编辑 /company/create /company/update views/company/Create.vue Update.vue /company/add /company/edit 同上;表单字段 = url, orgNo, appKey, privateKey, publicKey, platformPublicKey + 公司资料
— modal views/company/modules/ApiParaForm.vue — 只读展示对接参数
— modal views/company/modules/AccountForm.vue /company/addAccount 批量生成随机身份「分油用户」写入 yoshop_account
— modal views/company/modules/RechargeForm.vue /company/recharge 公司余额充值(yoshop_company_balance_log)
分油公司 › 余额明细 /company/balance/index views/company/balance/Index.vue /company.balance/* 公司余额流水
分油公司 › 分油用户 /account/index views/account/Index.vue /account/* yoshop_account 用户池(小程序分油借用的身份)
合作商管理 › 列表 /partners/index views/partners/Index.vue /partners/index /partners/all 下单平台账号管理(orgNo/appKey/privateKey/publicKey/callback/balance)
合作商管理 › 创建 / 编辑 /partners/create /partners/update views/partners/Create.vue Update.vue /partners/add /partners/edit 含勾选可售商品 + 库存分配(yoshop_partners_goods)
— modal views/partners/modules/ApiParaForm.vue — 只读展示对接参数(含回调地址)
合作商管理 › 订单列表 /partners/order/index views/partners/order/Index.vue /partners.order/list share qrCode oilCallback useCallback yoshop_partners_order 运维:手动分油 / 核销码 / 重推回调
— modal views/partners/order/modules/{OilForm,UseForm,ShareForm,PayForm}.vue 同上
合作商管理 › 余额明细 /partners/balance/index views/partners/balance/Index.vue /partners.balance/*
订单管理 › 全部 / 油券 / 优惠券 /order/list/all /oil /coupon views/order/Index.vue /order/list /order/detail yoshop_order + order_goods 运维
— modals views/order/modules/{ShareForm,CancelShareForm,FillUpForm,PayForm,CancelForm,DeliveryForm,PriceForm,UpdateStatusForm}.vue /order/share /order/cancelShare /order/callback /order/qrCode /order.event/* 手动分油 / 撤销分油(当前坏) / 补单 / 付款码
订单管理 › 售后 (路由已注释,页面文件仍在) views/order/refund/** — 当前未启用

企油通改造时预计需要动的 admin-vue 页面:views/company/**(改造为企业管理:营业执照 / 通道 / 企业状态 / 开户流程)、views/order/** 与 views/partners/order/**(补企油通字段、查发放结果、回单下载)。其余尽量不动。


7. 数据库相关表(oil.sql,表前缀 yoshop_)

7.1 下单平台侧

表 关键字段 说明
yoshop_partners orgNo, appKey, privateKey, publicKey, callback, balance 下单平台(对接公司)账号 & 回调地址 & 余额。样例仅 1 条「华集云慧」
yoshop_partners_goods partners_id, goods_id, stock_num, use_num 分配给下单平台的可售商品与库存
yoshop_partners_order province, mobile, oilCardType, oilCardNo, shareNo, partners_order_no, partners_id, goods_id/name/no, goods_price, total_price, order_status(10/20/30), qrCode, oil_status, oil_status_zsy, oil_time, oil_text, oil_callback(+time), use_status, use_time, use_text, use_callback(+time), expiration_time 下单平台订单主表。无 user_id(user_id=0)。样例数据 oil_status_zsy 多为 3(发券失败,测试环境 face003)
yoshop_partners_balance_log / yoshop_partners_stock_log scene, money / goods_id, num 余额 / 库存变动明细

7.2 上游平台侧

表 关键字段 说明
yoshop_company url, orgNo, appKey, privateKey, publicKey, platformPublicKey, canOrderNum, unUseNum, useNum, balance, status(10启用/20停用) 上游平台账号。样例:row 2/6 = 智慧油客(suoyoubao / 124.70...),status=20;row 7/8/9 = 定聚(idingju.com),status=10,privateKey/publicKey/platformPublicKey 存 '1'(定聚不用 RSA)
yoshop_company_balance_log scene(10充值/20消费/30管理员/40退款), money 公司余额流水
yoshop_account userNo, mobile, userName, userIdCard, userType, company_id, state(0空闲/1占用), last_called_at, is_delete 分油用户池——小程序订单分油时「借」一个身份去调上游

7.3 小程序商城订单侧

表 关键字段(仅列油品相关) 说明
yoshop_order order_no, order_type, order_status(10/20/21/30), total_price, pay_price, pay_type, pay_status, merchant_id, user_id, order_source, platform 商城订单主表(yoshop 标准 + merchant_id 代理)
yoshop_order_goods account_id, userNo, mobile, province, oilCardType, oilCardNo, shareNo, company_id, merchant_id, goods_id/name/no, goods_price, total_price, total_pay_price, qrCode, oil_status, oil_status_zsy, oil_time, oil_text, use_status, use_time, use_text, expiration_time 油品状态实际承载在这张表(一单可多券)
yoshop_order_qrcode oilCardNo, qrCode, amount, order_id, order_goods_id, user_id 每次申请核销码留痕(样例可见 URL 形态:https://t-h5.einrkv.com/...)
yoshop_order_record oilCardNo, shareNo, amount, type(add/sub), optType(share_oil/add_oil/cancel_share), order_id, order_goods_id, user_id 油卡变动通知落库
yoshop_request_log request_id(唯一), app_key, create_time 入站 API requestId 幂等去重

7.4 其他相关

表 说明
yoshop_goods / yoshop_goods_sku 商品(goods_no, oilCardType, province, stock_total)/ SKU(stock_num)。库存被「下单减」与「上游库存同步」两套逻辑写
yoshop_merchant / yoshop_merchant_balance_log / yoshop_merchant_order 「商户 / 代理商」体系 + 发券订单,与支付聚合链路相关,与企油通关系待确认
yoshop_order_refund 系列 售后单(当前后台未启用该菜单)

表结构注释在 oil.sql 中为 UTF-8;用 sed/编辑器读取正常,用 Windows PowerShell Get-Content 会乱码。


8. 企油通接口清单(据 企油通接口对接文档.md 第 7 章,共 23 个)

公共约定:

  • 全部 POST,路径 /enterprise/api/...,域名测试/正式不同,线下索取。
  • 外层信封:{ appId, timestamp, sign, params },其中 params 必须是 JSON 字符串(转义),不是对象。
  • 响应:{ status: ok|error, data, message, serialNo, sign }。
  • 业务接口不需要登录态 token(appId + business_license 即可)。
  • 金额单位不统一:分 → 7.7/7.8/7.9/7.10/7.11/7.12/7.13/7.14;元 → 7.16/7.17/7.19/7.20/7.21/7.23。
  • passageway:1 支付宝企业码 / 2 平安银行 / 3 智慧油客(🚫停用,勿传) / 4 企油通(仅出现在返回值)。
  • 7.16 / 7.17 必须真实 RSA 验签,否则 403。
  • batch_no / waybill_no / waybill_no_code 当前全平台唯一,需加企业前缀。
编号 名称 路径 用途 / 备注
7.1 企业登录(可选) /enterprise/api/auth/login 业务接口不需要,一般不用
7.2 企业申请创建 /enterprise/api/zhyk/openAnAccount 提交营业执照等,返回待审核;之后线下开户
7.3 企业账户信息推送(智慧油客) 回调 🚫 已停用
7.4 企业账户信息推送(平安银行) 回调 .../callback/ygzf/enterpriseAccountCallback 平台→我方,开户完成后推送银行账户
7.5 企业账户信息查询 /enterprise/api/zhyk/accountQuery 查企业账户号/开户行/状态
7.6 企业信息查询 /enterprise/api/zhyk/getEnterpriseInfo is_new / can_pay / password
7.7 运单油费下发 /enterprise/api/zhyk/createAndSendQuota 核心:给司机按运单发额度,单批≤100 人,异步,quota_amount 单位分,平安通道 id_card 必填
7.8 运单发放结果查询 /enterprise/api/zhyk/waybillBatch 按 batch_no 查发放明细与 handle_status
7.9 运单加油明细查询 /enterprise/api/zhyk/waybillQuotaDetail 返回 order_code(→7.19 回单入参)、加油站、升数、油品、实付
7.10 运单加油明细推送(异步) 回调 .../callback/ygzf/waybillConsumeCallback 平台→我方,消费推送;我方须返回 {"code":"100","msg":"Success"}
7.11 获取企业账户余额 /enterprise/api/zhyk/distributable 平安实时余额(勿高频轮询);支付宝通道不支持
7.12 司机钱包金额查询 /enterprise/api/zhyk/driverBalanceQuery 司机可用余额(分)
7.13 司机钱包明细查询 /enterprise/api/zhyk/driverWalletQuery 流水,默认只回溯近 1 个月
7.14 企业发票下载 /enterprise/api/zhyk/invoice 按月,返回发票地址集 + 加油明细
7.15 H5 跳转地址(司机免登加油) H5 URL 替代「核销二维码」;签名:ksort → http_build_query → urldecode → 大写MD5 → SHA1withRSA → base64;带 creditcode 时须一并入签且企业须 approved
7.16 司机额度调整 /enterprise/api/quota/adjustVoucher change_type 02 增 / 03 减(≈撤销分油),金额元,须 RSA 验签
7.17 获取支付凭证 /enterprise/api/quota/getVoucherList 返回 quota_id(→7.16 入参),金额元,须 RSA 验签
7.18 司机三证信息保存 /enterprise/api/zhyk/saveDriverDoc 身份证/驾照/行驶证,含 OCR + 实名二要素;仅更新已存在司机
7.19 支付回单获取(平安) /enterprise/api/receipt/getPaymentReceipt 按 order_code 取银行电子回单 PDF;generating 需重试(可能 T+1)
7.20 司机提现到银行卡(平安) /enterprise/api/driver-withdraw/withdrawToBank 异步,out_trade_no 幂等,金额元
7.21 提现结果查询(平安) /enterprise/api/driver-withdraw/queryWithdraw 轮询至 success/failed
7.22 企业列表查询 /enterprise/api/enterprise/list 枚举本 appId 名下企业与 business_license、已绑定 passageway;需开通权限
7.23 平安消费回调测试接口 /enterprise/api/zhyk/testPinganConsume 🚫 仅测试环境,模拟一笔消费联调 7.10

四阶段流程:① 企业创建(7.2→线下开户→7.4/7.5)② 发放油费(7.7→7.8;额度调整 7.17→7.16)③ 司机加油(7.15→7.9/7.10→7.19)④ 获取发票(7.14)。


9. 旧平台接口 ↔ 企油通接口 对应关系

⚠️ 前提:两者业务模型不同(详见风险 1)。下表是「功能意图」层面的映射,不是字段级等价。

现有能力(慧明宝入站 method / 上游调用) 现实现(定聚 / 智慧油客) 企油通对应 差异要点
下单 / 分油 syb.oil.oilCard.share → oilOilCardShare 定聚 /api/cnpcbp/order,同步返回 trade_no/state 7.7 createAndSendQuota(+ 7.8 查结果) 由「给手机号发一张固定面值油券」变为「按 waybill_list.employee_list 给司机发额度」;异步,需 7.8 轮询;平安通道 id_card 必填;金额单位分;batch_no/waybill_no/waybill_no_code 需加企业前缀
库存查询 syb.oil.oilCard.stockList → stockList 定聚 /api/cnpcbp/queryStock(quantity) 7.11 distributable(企业资金余额,平安) 企油通无「油券库存」概念,是企业账户可发放资金上限;支付宝通道不支持;勿高频轮询
分油记录查询 syb.oil.oilCard.shareList → — — 7.9 waybillQuotaDetail / 7.17 getVoucherList 7.17 须 RSA 验签;返回结构差异大(凭证 / 运单明细)
订单状态查询 syb.oil.oilCard.orderStatus → — 读本地 partners_order 7.8 waybillBatch(发放态)/ 7.9(消费态)/ 7.12(余额态) 状态分散在多个接口,无「单笔订单聚合状态」
核销二维码 syb.pay.qrCode.request → payQrCodeRequest 定聚 /api/cnpcbp/getUseCode(useQrCodeUrl 二维码链接) 7.15 H5 跳转地址(免登) 不再是二维码字符串,是 H5 URL;签名算法完全不同(大写 MD5 → SHA1withRSA);带 creditcode 时须企业 approved。小程序 order/qrCodeImg 出参 base64 图不能变 → 服务端需把 H5 URL 转二维码图
撤销分油(后台 cancelShare,当前坏) 曾经 SybOil::oilOilCardCancelShare 7.16 adjustVoucher(change_type=03 减额度) 须 RSA 验签;金额单位元;先 7.17 拿 quota_id
分油结果回调 partnersoil.php / oil.php(method oil/oil_partners,trade_no+useStatus=1) 定聚主动推 无对应推送 → 改为主动查 7.8 企油通发放无「成功推送」,须轮询
加油/核销结果回调 partnersoiluse.php / oiluse.php(method use/use_partners,useStatus=2) 定聚主动推 7.10 waybillConsumeCallback 回调地址由我方提供并配置;我方须返回 {"code":"100","msg":"Success"}(现有返回 OK / {"code":"00"},不匹配)
油站列表 syb.base.gasStation.list → baseGasStationList 定聚 /api/cnpcbp/stations(Station.php 却用 SybOil,已坏) 文档无公开油站接口(H5 内自带选站) 小程序 station/list 需重新评估:或保留定聚、或去掉、或改为静态/其它源 【待确认】
发票(SybOil 有 payInvoiceRequest/Status) 未接入下单平台链路 7.14 invoice 按月企业维度
我方 → 下单平台回调 syb.notify.oilCard.change(PartnersOil) 生效 保持不变,内部把企油通字段翻译回该结构 契约层,不改
企业开户 / 账户 旧系统无此概念(yoshop_company 直接填 orgNo/appKey) 7.2 / 7.4 / 7.5 / 7.6 / 7.22 全新流程:企业创建须审核 + 线下开户 + 异步回调
司机建档 / 三证 旧系统「借」yoshop_account 池身份 7.18 saveDriverDoc + 7.7 employee_list 建档 实名二要素核验;司机须属于企业
提现 无 7.20 / 7.21(新增能力,平安) 可选

10. 当前发现的问题与风险(已按 0.5 决策收敛)

图例:✅ 决策已消解 ・ 🔴 待处理阻塞项(需你拍板 / 需对接人补料) ・ 🟡 施工注意项

# 风险 级别 结论 / 处理方向
R-01 业务模型差异(油券库存 vs 企业运单额度) ✅ 决策 2/3/4:单企业 + 一张订单一次 createAndSendQuota(1 batch / 1 waybill / 1 employee);「商品面值」= quota_amount(元→分);不再有「平台油券库存」概念
R-02 「废弃四方平台」范围 ✅ 决策 1:SybOil + IdingjuOil 全部废弃,不灰度并存
R-03 企业开户不可即时(7.2→7.4) ✅ 决策 2:线下已开户,系统内不实现 7.2/7.4,business_license 直接配置;7.5/7.6/7.22 作可选核对
R-04 发放异步两段式(7.7 受理 → 7.8 轮询) 🟡 新增 qyt_poll_queue 延迟队列轮询 7.8;partners_order 增发放态字段
R-05 无「发放成功」推送 🟡 同 R-04,靠轮询 7.8 handle_status 驱动 notify_queue
R-06 核销形态:二维码字符串 → H5 免登 URL(7.15) 🟡 下单平台链路 getPayQrCode 出参 qrCode 直接放 H5 URL(本就是链接,下单平台侧生成二维码逻辑不受影响);7.15 特殊签名需新写。小程序 order/qrCodeImg 因链路停用(决策 5)不涉及
R-07 7.16/7.17 需 id_card,但下单平台不传(决策 6) 🟡→🔴 决策 9:撤销/退款做(企油通有 7.16)。但 7.16 的 quota_id 来自 7.17,而 7.17 需 id_card。落地方案:先尝试从 7.7 受理响应 / 7.8 结果 / 7.9 明细里拿到可用的凭证标识并落库(quota_id);若均拿不到 → 撤销请求要求下单平台补传 id_card。具体以联调时对接人确认的「7.16 定位方式」为准(R-19)
R-08 金额单位混用(分/元) 🟡 新增单位换算 helper,QytOil 内部统一
R-09 全平台唯一编号需企业前缀 🟡 定义前缀规则(如 HM + 环境标识),batch_no/waybill_no/waybill_no_code 由 saleOrderNo/order_id 派生并落库
R-10 库存查询语义(syb.oil.oilCard.stockList → /oilCard/stock) ✅ 决策:企油通没有就不接。接口壳保留(防下单平台报错),数据来源改为我方 yoshop_partners_goods.stock_num(后台仍可分配),不再调用上游;Company::stock / store/Goods.php 的上游库存同步移除。【待确认下单平台是否仍调用该接口】,如不调用后续可直接下线
R-11 签名/加密体系全新 🟡 新建 app/common/crypto/Qyt.php(业务信封 RSA 签名 + 7.15 H5 特殊签名 + 7.16/7.17 RSA 验签)
R-12 7.10 回调需返回 {"code":"100","msg":"Success"} 且需验签 🟡 新回调入口独立实现,不复用 SybNotify 的 OK 返回
R-13 store/controller/Order.php::cancelShare 调用未定义方法(已坏) 🟡 属小程序链路(决策 5 停用);本次移除该分支或改指向「按订单 7.16 撤销」(受 R-07 约束)
R-14 goods_no ↔ orgNo 硬编码散落 7 处 🟡 单企业后 orgNo 概念消失,goods_no → voucher_name/面值 收敛为 config/qyt.php 映射或读 yoshop_goods
R-15 退款/取消无上游回收 🟡 决策 9:做。新增入站 method syb.oil.oilCard.cancelShare(.docx 未定义,属慧明宝契约小扩展)→ 我方查凭证 → 7.16 change_type=03 减额度 → 回写 + 回调下单平台(optType=cancel_share, type=sub)
R-16 Station.php / store/Goods.php 依赖已停用 SybOil 🟡 决策 5:一并停用/摘除;store/Goods.php 库存同步改为不依赖上游
R-17 app/admin、yoshop_merchant 支付聚合 ✅ 决策 8:范围外,不动
R-18 敏感信息明文(config/DB 私钥) 🟡 企油通密钥放 config/qyt.php + .env,不写进 yoshop_* 表
R-19 环境参数需线下索取 🔴 需对接人给:测试/正式域名、appId、业务密钥/公私钥、institution_id、7.10 回调地址配置方式、7.15 落地页 appId
R-20 文档只覆盖第 7 章 🔴 7.15 落地页参数、institution_id 分配、拼签字段明细需补
R-21 小程序历史油券订单数据 🟡 yoshop_order_goods 大量 zsy_ticket 旧数据只读保留;后台订单页兼容旧/新字段展示

决策补充(2026-09-01 第二轮)

# 议题 决策
9 撤销 / 退款 做(企油通有 7.16 adjustVoucher)。新增入站 method syb.oil.oilCard.cancelShare → 我方定位凭证 → 7.16 change_type=03 减额度 → 回写 + 回调下单平台。凭证定位方式(是否需 id_card)以联调时对接人确认为准
10 /oilCard/stock 库存 企油通没有就不接。接口壳保留、数据取我方 yoshop_partners_goods,不再同步上游
11 回调下单平台字段 不硬凑。所有字段严格按企油通实际返回来填,企油通没有的(如旧 shareNo)就填空 / 不下发。发放阶段 shareNo 可留空或填 waybill_no_code;消费阶段填企油通 order_code
12 后台范围 「满足接口 + 满足运营」——凡企油通有、运营用得上的都进后台:发放结果查询(7.8)、加油明细(7.9)、H5 加油链接(7.15)、消费流水、企业余额(7.11)、企业信息(7.5/7.6)、发票下载(7.14)

决策补充(2026-09-01 第三轮 · 施工细节)

# 议题 决策
13 企油通订单字段落库 直接加到 yoshop_partners_order 主表(不建关联表);发放态复用 oil_status_zsy
14 name 缺失 兜底占位("司机"+手机尾4位),不阻断下单
15 qrCode 返回 直接把 7.15 H5 URL 放进 qrCode 字段,不转二维码图
16 发放结果获取 延迟队列退避轮询 7.8(新增 qyt_poll_queue + qyt:poll 命令)
17 7.10 消费回调入口 新建独立入口 public/qytconsume.php
18 小程序油品链路 后端停止触发,相关代码保留不删
19 旧上游代码(SybOil/IdingjuOil/…) 本期保留不删、只停止调用;全链路联调通过后在收尾阶段统一删除
20 后台「企业管理」 改造现有「分油公司」菜单为「企业管理」页(复用菜单/路由/权限)
21 施工节奏 先出 DB 迁移 + config/qyt.php + Qyt.php / QytOil.php 骨架审核,确认后再动队列/控制器

仍需对接人提供(R-19/R-20,阻塞联调,不阻塞脚手架开发)

测试 + 正式域名、appId、业务 RSA 公私钥与业务接口拼签字段顺序、institution_id(支付宝企业码通道必填)、7.10 回调地址如何配置、7.15 H5 落地页 appId 与签名字段清单、7.16 凭证定位方式。


附 B. 第二阶段 A-1 交付物(骨架,待审)

文件 状态 说明
server-tp6/qyt_migration.sql 新增 yoshop_partners_order 加 14 字段 + 2 索引;新建 yoshop_qyt_consume;含回滚脚本。执行前请审字段
server-tp6/config/qyt.php 新增 配置骨架;标 ★ 的值待对接人给(用 .env 覆盖)
server-tp6/app/common/crypto/Qyt.php 新增 业务信封 / 7.15 H5 签名 / 验签;bizSign() 的拼签规则标 TODO 待对接人确认(默认走 7.15 同族算法)
server-tp6/app/common/service/QytOil.php 新增 企油通客户端:7.7/7.8/7.9/7.11/7.14/7.15/7.16/7.17;元↔分、编号生成、日志工具

均为新增文件,未触碰任何现有文件。审核通过后再进入 A-2(改 PartnersQueue / 新增 QytPollQueue / QytNotify / 改 OilCard.php / 改 CallbackQueue)。


附 C. 第二阶段 A-2 交付物(下单平台链路打通,待审)

⚠️ 部署前必须先执行 qyt_migration.sql(yoshop_partners_order 会用到新列)。

新增文件

文件 说明
app/console/command/QytPollQueue.php qyt:poll 命令:退避轮询 7.8 拿发放结果,成功→写订单+投 notify_queue;超次数标记人工核对
app/api/controller/QytNotify.php /qytNotify/consume:接收 7.10,验签(TODO),落 callback_queue(method qyt_consume),返回 {"code":"100","msg":"Success"}
public/qytconsume.php 7.10 回调入口(配到企油通后台的地址)

改动文件(现有)

文件 改动
config/console.php 注册 qyt:poll
app/console/command/PartnersQueue.php 整个 processMessage 重写:IdingjuOil::oilOilCardShare → 生成企业前缀编号 + QytOil::createAndSendQuota(7.7 受理) → 受理成功投 qyt_poll_queue。删除硬编码 getCompany()
app/console/command/CallbackQueue.php 仅新增 case "qyt_consume"(写 yoshop_qyt_consume + 更新订单核销 + 投 notify_queue);旧 oil/use/oil_partners/use_partners 分支保留不动
app/api/controller/OilCard.php ①use QytOil ②share() 加 name 入参 + 兜底占位 + saveOrder 增 driver_name/passageway ③getPayQrCode() 改 QytOil::h5Url(出参 qrCode 结构不变)④新增 cancelShare()(method syb.oil.oilCard.cancelShare → 7.17 定位 quota_id → 7.16 减额度 → 投 notify_queue optType=cancel_share)。getCompany()/IdingjuOil import 保留(死代码,收尾清)
app/api/service/order/PaySuccess.php shareOil() 的 zsy_ticket 分支加 continue —— 小程序支付成功后不再触发上游(优惠券分支保留)

A-2 新增待确认

  • optType=cancel_share:NotifyQueue 把撤销通知发给下单平台时 type=sub / optType=cancel_share(.docx 回调 optType 枚举只有 share_volume/share_oil/add_oil)。需与下单平台确认能否识别,或改用其约定值。
  • 7.7 响应 data.status:文档写 1 成功 / 2 失败,此处「成功」按「受理成功」处理;如企油通实际语义不同需调 PartnersQueue。
  • 7.8 list[].handle_status:按 1待处理/2成功/3失败 匹配;如字段名/取值不同需调 QytPollQueue。

未动(留给 A-3)

小程序核销 app/api/controller/Order.php::qrCode/qrCodeImg、Station.php、store/controller/Order.php(小程序订单后台)、store/controller/Company.php::stock、store/controller/Goods.php、store/controller/partners/Order.php(下单平台订单后台运维)、admin-vue 页面。


附 D. 并发 / 原子性加固(A-2 补丁,已并入 A-2 交付物)

状态机(yoshop_partners_order)

oil_status_zsy 含义 迁入方 迁入条件
0 未提交 建单默认 —
1 已受理待发放 PartnersQueue WHERE oil_status_zsy=0(7.7 受理成功后)
2 发放成功 QytPollQueue WHERE oil_status=0 AND oil_status_zsy=1(7.8 handle_status=2)
3 发放失败 PartnersQueue / QytPollQueue WHERE oil_status_zsy IN (0,1)(7.7 拒绝 / 7.8=3 / 轮询超时)
5 已撤销 OilCard::cancelShare WHERE oil_status=1 AND oil_status_zsy=2 AND use_status=0(7.16 成功)

use_status 0→1:CallbackQueue WHERE use_status=0 AND oil_status=1(7.10 消费)。

每个状态迁移都是条件 UPDATE ... WHERE 期望值,affected_rows=1 的进程才推送下游队列 → 天然幂等,可多进程 / 重复消息安全。

具体措施

场景 风险 措施
OilCard::share 同一 saleOrderNo 并发 先查后插竞态 → 重复建单 / 库存超扣 DB 唯一键 uk_partner_saleorder(partners_id, partners_order_no);命中已存在订单 → 幂等返回原结果;saveOrder 捕获 Duplicate 再幂等返回
PartnersQueue 同一 order 被多 worker / 重复消息处理 重复调 7.7 RedisLock('qyt:send:{order_id}', 60s) 串行 + 锁内重读状态;batch_no 确定性派生,重试用同一值,依赖企油通按 batch_no 幂等(dup_batch_hints 命中即按已受理处理)
7.7 成功但 worker 崩溃(未及入轮询队列) 订单卡在「已受理」永不发放 oil_status_zsy 只在 7.7 结果确定后离开 0;QytPollQueue::reconcile()(空闲每 60s)补投:oil_status_zsy=0 超 2min → 重投 partners_queue;oil_status_zsy=1 且 15min 无进展 → 重投 qyt_poll_queue
QytPollQueue 多 worker / 延迟队列并发迁移 同一 order 重复 7.8 → 重复回调下单平台 QueueService::migrateDelayedToReady 改为先 zrem 抢占再入队;发放成功用条件 UPDATE 1→2,仅胜出者推 notify_queue
企油通重推 7.10 重复消费流水 / 重复回调 yoshop_qyt_consume 唯一键 uk_consume(waybill_no_code, order_code) + 插入捕获 Duplicate;use_status 条件跃迁 0→1,仅胜出者推 notify_queue
cancelShare 并发 / 与 7.10 消费竞争 重复调 7.16 减额度 / 撤销与加油冲突 RedisLock('qyt:cancel:{order_id}', 60s) + 条件占位 WHERE oil_status_zsy=2 AND use_status=0;7.16 失败回滚 oil_text;成功再条件跃迁 2→5(仍要求未核销),冲突则返回人工核对
callback_queue / notify_queue 单消费者假设 与旧代码一致 建议单进程;即使多进程,因全链路条件跃迁,最坏只是日志重复

遗留(联调时与对接人确认)

  • 7.7 幂等键:确认企油通是否严格按 batch_no 去重,重复提交返回什么(用于 dup_batch_hints 精确匹配)。
  • 7.16 幂等:文档无幂等键;当前靠 RedisLock + 条件占位防重,但跨进程锁过期(60s)后仍有极小窗口。若企油通提供幂等键(如 out_trade_no)应接入。
  • RedisLock 用 config('redis'),QueueService 用硬编码 127.0.0.1:6379 —— 生产需统一(本次未动 QueueService 连接配置)。

附 E. 对下单平台接口文档更新(慧明宝供应链平台接口文档V1.0.17.docx)

在原 V1.0.16.docx 基础上做增量修改,保留原文件为历史。改动:

# 章节 变更
1 3.2.1 用户分油 请求体 新增 name(司机姓名,可选,String V50,缺失平台兜底)
2 3.5.1 油卡变动通知 optType 新增取值 cancel_share(撤销分油,配 type=sub)
3 文末新增《附录:V1.0.17 变更说明》 ①share 增 name + 异步发放语义 + saleOrderNo 幂等;②新增接口 syb.oil.oilCard.cancelShare(撤销分油,/oilCard/cancelShare,入参 orgNo / oilCardNo 或 saleOrderNo / id_card,出参 oilCardNo amount);③qrCode.request 返回值由二维码串改为 H5 免登地址;④stockList 返回值改为平台内部额度统计;⑤notify 空值兼容说明;⑥修订记录

制作方式:unpack → merge_runs.py → 编辑 document.xml → 重新打包,validate.py XSD 校验通过。 注意:①本机无 LibreOffice,未能渲染 PDF 目视核对,建议用 Word 打开确认排版;②新增《附录》为普通标题,不在目录中,Word 打开时可「更新域」重建目录;③merge_runs 合并了冗余样式标签(内容与渲染不变),故 .docx 体积小于原文件属正常。


附 F. 范围收敛(2026-09-03 · 最终版)—— 支付宝企业码通道,仅 3 个上游接口

需求方最终确认:只对接支付宝企业码通道,上游实际只用 3 个接口: 7.7 运单油费下发 / 7.15 H5 跳转地址 / 7.10 运单加油明细推送(异步回调)。 司机姓名 + 手机号由下单平台必传。不做撤销(企油通仅 7.16 调整额度,本期不用)。 本节内容取代前文附 B / C / D / E 中与轮询、撤销、平安通道、7.8/7.9/7.11/7.14/7.16/7.17 相关的描述。

与之前版本的差异

项 之前(附 B/C/D) 现在(最终)
上游接口 7.7 + 7.8 轮询 + 7.15 + 7.10 + 7.16/7.17 撤销 + 可选 7.9/7.11/7.14 仅 7.7 + 7.15 + 7.10
发放结果 7.7 受理 → qyt_poll_queue 退避轮询 7.8 7.7 受理成功即视为发放成功(支付宝企业码通道),直接回调下单平台。删除 QytPollQueue / qyt:poll / qyt_poll_queue
通道 1/2 可切换 + institution_id 固定 passageway=1(支付宝企业码)+ institution_id 必填
司机姓名 name 可选,缺失兜底占位 必填,下单平台不传 → 返回 0001 缺少必填请求参数 name。(mobile 本就必填)
撤销分油 新增入站 syb.oil.oilCard.cancelShare → 7.17/7.16 不做。移除该 method、RedisLock('qyt:cancel:…')、QytOil::getVoucherList/adjustVoucher
7.7 入参 含 id_card、effective_*、institution_config、passageway 动态 精简为:batch_no / business_license / passageway="1" / institution_id / waybill_list:[{waybill_no, employee_list:[{mobile, name, quota_amount(分), voucher_name, effective_start:"", effective_end:"", waybill_no_code}]}]
DB 字段 含 passageway / quota_id / poll_times / last_poll_time 移除以上 4 个;保留 driver_name / batch_no / waybill_no / waybill_no_code / qyt_order_code / 消费明细 5 项
oil_status_zsy 取值 0/1/2/3/5 0 未提交 / 2 发放成功 / 3 发放失败(无 1 待处理、无 5 撤销)
yoshop_qyt_consume 含 quota_id / passageway 列 移除该 2 列

最终文件清单

文件 状态
config/qyt.php 精简(去通道切换 / 轮询配置)
app/common/crypto/Qyt.php 不变(信封 + 7.15 签名 + 验签)
app/common/service/QytOil.php 精简为 createAndSendQuota(7.7) + h5Url(7.15) + post/curlPost + 工具
app/console/command/PartnersQueue.php 7.7 受理成功=发放成功 → 条件跃迁 0→2 → 回调;含 reconcile() 兜底补投
app/console/command/QytPollQueue.php 已删除
config/console.php 移除 qyt:poll 注册
app/api/controller/QytNotify.php + public/qytconsume.php 不变(7.10 入口,返回 {"code":"100","msg":"Success"})
app/console/command/CallbackQueue.php qyt_consume 分支:use_status 条件跃迁 0→1 + yoshop_qyt_consume 去重;旧定聚分支保留
app/api/controller/OilCard.php share 加必填 name;getPayQrCode → QytOil::h5Url;移除 cancelShare;saveOrder 幂等
app/api/service/order/PaySuccess.php shareOil() 的 zsy_ticket 分支 continue(小程序停用)
app/common/service/QueueService.php migrateDelayedToReady 改 zrem-first(仅 notify_queue 用到延迟队列)
qyt_migration.sql 精简字段 + 保留 2 个唯一键
慧明宝供应链平台接口文档V1.0.17.docx name 必填;移除 cancelShare;发放语义改「受理即成功」;无 cancel_share

并发/原子性(最终,简化后)

  • share:uk_partner_saleorder(partners_id, partners_order_no) 唯一键 + 幂等返回原结果 + saveOrder 捕获 Duplicate。
  • PartnersQueue:RedisLock('qyt:send:{order_id}', 60s) 串行 + 锁内重读;发放态跃迁 UPDATE ... WHERE oil_status_zsy=0,affected=1 才回调下单平台;batch_no 确定性派生,重试用同一值,靠企油通按 batch_no 幂等(dup_batch_hints 命中即按已发放);空闲 reconcile() 每 60s 补投丢失的 partners_queue 消息。
  • CallbackQueue 7.10:yoshop_qyt_consume 唯一键去重 + use_status 跃迁 WHERE use_status=0 AND oil_status=1,affected=1 才回调;企油通重推安全。
  • NotifyQueue:原有 3 次退避重试不变;报文结构 syb.notify.oilCard.change 不变。

联调前仍需对接人提供

测试/正式域名、appId、业务接口 RSA 拼签字段顺序、institution_id、7.10 回调地址配置方式、7.15 H5 落地页 appId;确认 7.7 支付宝企业码通道响应 data.status 语义(是否 1=受理即发放成功)与 batch_no 重复提交的返回文案(校准 dup_batch_hints)。

11. 改造方案(基于 0.5 决策,待你对 §10「🔴 待拍板」项给结论后即可开工)

11.1 边界原则

  • 完全不动:mobile-uniapp 全部;NotifyQueue + PartnersOil 对下单平台回调的报文结构(syb.notify.oilCard.change);yoshop_partners / yoshop_partners_goods 表结构;app/admin;yoshop_merchant 相关。
  • 最小扩展:app/api/controller/OilCard.php 的 syb.oil.oilCard.share 仅新增可选入参 name。其余入站 method / URL / 加密不变。
  • 整体替换:「我们 → 上游」这一层——删除 SybOil / IdingjuOil 调用,换 QytOil。
  • 停用:小程序油品发放链路(PaySuccess::shareOil 油券分支、Order::qrCode / qrCodeImg、Station)。

11.2 新增 / 改动文件清单(预估)

类型 文件 说明
新增 config/qyt.php 域名、appId、公私钥、business_license、passageway 默认值、institution_id、编号前缀、7.10 回调地址、7.15 落地页地址
新增 app/common/crypto/Qyt.php 三种签名/验签:①业务信封 ②7.15 H5 签名 ③7.16/7.17 RSA 验签
新增 app/common/service/QytOil.php createAndSendQuota(7.7) / waybillBatch(7.8) / waybillQuotaDetail(7.9) / h5Url(7.15) / getVoucherList(7.17) / adjustVoucher(7.16) / (可选)distributable / invoice / enterpriseList
新增 public/qytconsume.php + app/api/controller/QytNotify.php 7.10 消费回调入口,验签,返回 {"code":"100","msg":"Success"},落 callback_queue(method qyt_consume)
新增 app/console/command/QytPollQueue.php(注册 qyt:poll) 退避轮询 7.8 发放结果
新增 app/common/library/Amount.php(或并入 helper) 元↔分
改 app/console/command/PartnersQueue.php 换 QytOil::createAndSendQuota;受理成功入 qyt_poll_queue
改 app/console/command/CallbackQueue.php 删定聚 oil/use/oil_partners/use_partners 分支,加 qyt_consume 分支
改 app/api/controller/OilCard.php share 加 name;getPayQrCode → QytOil::h5Url;stock 数据源改我方 partners_goods;新增 cancelShare method(7.16 减额度)
改 app/api/service/order/PaySuccess.php 移除 shareOil() 油券分支(保留优惠券分支)
改 app/store/controller/partners/Order.php share 改为「查发放结果(7.8)」;qrCode → h5Url;回调按钮走新队列
改 app/store/controller/Order.php 移除/改造小程序链路 cancelShare / qrCode / share
改 app/api/controller/Station.php、app/store/controller/Goods.php 停用/摘除上游依赖
删/弃 SybOil.php、IdingjuOil.php、crypto/{Crypto,Idingju}.php、config/crypto.php、app/api/controller/syb/*、public/{oil,oiluse,partnersoil,partnersoiluse}.php 确认无引用后移除(可先保留一版做回滚)
改 admin-vue src/views/partners/order/**、src/views/company/** 见 11.6

11.3 数据库改动(全部增量加字段,不改现有字段 / 类型)

yoshop_partners_order 增加:driver_name(司机姓名)、passageway(本单通道)、batch_no、waybill_no、waybill_no_code(唯一索引,回调匹配)、quota_id(7.17 凭证 ID)、qyt_order_code(7.9/7.19 订单号)、handle_status(7.8 单条发放态 1/2/3)、poll_times / last_poll_time、以及消费明细 station_name / litter / oil_name / pay_fee(分) / consume_time。

新增表(建议):yoshop_qyt_consume(7.10 每笔消费流水)。

yoshop_company / yoshop_account:单企业 + 取消用户池后闲置。不删表(保历史数据),改为不再写入;business_license 等改放 config/qyt.php。

11.4 下单平台链路 端到端流程(改造后)

下单平台 POST /partners.php/oilCard/share  (method=syb.oil.oilCard.share, RSA信封)
  body: orgNo, goodsNo, mobile, name(新增), saleOrderNo, oilCardType=zsy_ticket
    ↓ OilCard.php::share
  · 校验 appKey→yoshop_partners、requestId 幂等、库存(R-10)
  · 建 yoshop_partners_order(order_status=10, driver_name=name)
  · 生成 batch_no / waybill_no / waybill_no_code(企业前缀 + saleOrderNo/order_id)
  · push partners_queue → 返回 { oilCardNo, amount }(结构不变)
    ↓ PartnersQueue → QytOil::createAndSendQuota(7.7)
      params = { batch_no, business_license(配置), passageway(配置, 默认1),
                 institution_id(配置, 通道1必填),
                 waybill_list:[{ waybill_no, waybill_no_code,
                   employee_list:[{ mobile, name, quota_amount(元→分),
                                    voucher_name, effective_start:"", effective_end:"" }] }] }
  · status=1 受理成功 → 记 batch_no, handle_status=1 → push qyt_poll_queue(延迟)
  · status=2 → oil_status_zsy=3, oil_text=msg
    ↓ QytPollQueue → QytOil::waybillBatch(7.8) by batch_no(退避轮询)
  · list[0].handle_status==2 → oil_status=1, oil_status_zsy=2, quota_id → push notify_queue(share_oil)
  · ==3 → oil_status_zsy=3, oil_text=error_msg
  · ==1 且未超次数 → 再次延迟入队
    ↓ 核销:下单平台 POST /oilCard/getPayQrCode → QytOil::h5Url(7.15)
  · 返回 { qrCode: <H5免登URL> }(结构不变;下单平台侧原有「拿字符串生成二维码」逻辑不受影响)
    ↓ 司机加油后:企油通 POST public/qytconsume.php (7.10)
  · QytNotify 验签 → 按 waybill_no_code / order_code 命中 order
  · 写 yoshop_qyt_consume;use_status=1, use_time;返回 {"code":"100","msg":"Success"}
  · push notify_queue(add_oil)
    ↓ NotifyQueue(报文结构不变)→ PartnersOil::sendRequest($partners.callback,
      method=syb.notify.oilCard.change, body={ oilCardNo, amount, shareNo, type, optType })

name 缺失兜底:下单平台未传时用 "司机"+mobile后4位 占位(企业码通道不做姓名实名核验)—— 待你确认是否可接受。

11.5 对下单平台回调(syb.notify.oilCard.change)字段映射(决策 11:不硬凑,按企油通实际返回填)

回调字段 旧值(定聚) 新值(企油通)
oilCardNo partners_order.oilCardNo 不变(我方自生成)
amount total_price 不变
shareNo 定聚 trade_no 发放阶段:留空或填 waybill_no_code;消费阶段:填企油通回调的 order_code。企油通无对应值时填空字符串,不硬造
type / optType add/sub、share_oil/add_oil/cancel_share 不变;新增撤销场景 type=sub, optType=cancel_share

保持报文结构与字段名不变(下单平台既有解析不受影响),只是个别字段值可能为空。

11.6 admin-vue 改动(决策 12:满足接口 + 满足运营)

  • views/partners/order/Index.vue:列表增列「批次号 / 运单号 / 发放态 / 通道」;「手动分油」→「查询发放结果(7.8)」;「核销码」→「获取加油链接(7.15)」;增「撤销分油(7.16)」「加油明细(7.9)」。
  • 新增「企业管理」页:企业对接参数 + 通道切换(1/2) + institution_id + 企业信息(7.5/7.6) + 企业余额(7.11) + 发票下载(7.14) + 消费流水查询。
  • 订单详情页兼容「旧定聚字段 + 新企油通字段」两种展示。
  • 旧「分油公司 / 分油用户」菜单:保留只读(历史数据),不再新增。

11.7 建议实施顺序

A. 不依赖对接人环境料,可先做(脚手架 + 内部改造):

  1. DB 迁移脚本(yoshop_partners_order 加字段 + 新建 yoshop_qyt_consume)。
  2. config/qyt.php(占位)+ app/common/library 单位/编号 helper。
  3. app/common/crypto/Qyt.php(信封结构 + 7.15 算法已文档化可实现;业务接口拼签字段顺序留 TODO 待确认)。
  4. app/common/service/QytOil.php(各接口入参组装 + 调用骨架)。
  5. 改 app/api/controller/OilCard.php:share 加 name;新增 cancelShare method;stock 改内部数据源。
  6. 改 PartnersQueue + 新增 QytPollQueue(qyt:poll) + QytNotify + public/qytconsume.php + 改 CallbackQueue。
  7. 停用小程序油品链路(PaySuccess::shareOil 油券分支、Order::qrCode/qrCodeImg、Station、store/Goods::stock)。
  8. admin-vue:views/partners/order/** + 新增「企业管理」页。

B. 拿到对接人环境料后(R-19/R-20): 9. 填 config/qyt.php 真值 + 补 Qyt.php 拼签字段顺序 + 7.16 凭证定位方式。 10. 测试环境联调:7.7 → 7.8 → 7.15 → 7.10(用 7.23 模拟消费)→ 7.16 撤销。 11. 全链路回归(下单→发放→轮询→核销→消费回调→回调下单平台→撤销)+ 旧数据展示。

C. 收尾: 12. 确认无引用后清理 SybOil / IdingjuOil / crypto/{Crypto,Idingju} / config/crypto.php / app/api/controller/syb/* / 旧入口文件。


附:本阶段已核查的关键文件清单

  • 后端上游对接:app/common/service/{SybOil,IdingjuOil,PartnersOil}.php、app/common/crypto/{Crypto,Idingju,RsaUtil}.php、config/{crypto,rsa}.php
  • 入站 API:app/api/controller/OilCard.php、app/api/controller/SybNotify.php、app/api/controller/syb/*、app/api/controller/Station.php
  • 队列:app/console/command/{CallbackQueue,PartnersQueue,NotifyQueue}.php、config/console.php、app/common/service/QueueService.php
  • 订单:app/api/controller/Order.php、app/api/service/order/PaySuccess.php、app/api/service/main/order/PaySuccess.php、app/api/model/Order.php、app/api/model/partners/Order.php、app/common/model/partners/Order.php
  • 后台:app/store/controller/{Order,Company,Partners}.php、app/store/controller/partners/Order.php、app/store/model/{Company,Partners}.php、app/common/enum/order/*
  • 入口:public/{partners,partnersoil,partnersoiluse,oil,oiluse}.php
  • admin-vue:src/config/router.config.js、src/api/{partners,company,order}/*、src/views/{company,partners,order,account}/**
  • mobile-uniapp:config.js、api/{station,order,card,goods,checkout}.js
  • 数据库:oil.sql 中 yoshop_{account,company,company_balance_log,partners,partners_goods,partners_order,order,order_goods,order_qrcode,order_record,request_log,merchant} 建表与样例数据
  • 文档:企油通接口对接文档.md(第 7 章 23 接口)、慧明宝供应链平台接口文档V1.0.16.docx(= 我们对下单平台的入站契约)

附 G. 后台功能适配 + 接口文档就地修订(2026-09-04)

G1. 后台(app/store + admin-vue)适配

下单平台订单管理(运营日常用的「查看订单 / 处理订单」)

层 文件 改动
后端 app/store/controller/partners/Order.php 整体重写。移除 IdingjuOil / CompanyModel / getCompany() 硬编码;qrCode() 改调 QytOil::h5Url(7.15);share() 改为「重新发放」——仅允许 oil_status_zsy=3(发放失败) 条件重置为 0 后重投 partners_queue(避免与在途单冲突);新增 detail() 返回订单 + yoshop_qyt_consume 加油流水;oilCallback()/useCallback() 保留为「重推结果给下单平台」并加状态前置校验
后端 app/store/model/partners/Order.php getFilter() 新增检索:司机姓名 driver_name、单号 orderNo(油卡编号/销售单号/批次号/运单号 模糊)、企油通发放态 oil_status_zsy
前端 views/partners/order/Index.vue 搜索区加「司机姓名 / 单号 / 企油通发放态」,「用户电话」改「司机手机号」;列表新增「企油通单号(批次/运单)」「司机(姓名+手机)」「加油信息(油站/油品/升数/实付)」列,去掉旧「分油记录编号」列;状态列增加企油通发放态标签 + oil_text 失败原因;按钮文案改为 重新发放 / 加油链接 / 重推发放结果 / 重推加油结果
前端 views/partners/order/modules/PayForm.vue 重写:原来把二维码串塞 <iframe>,现改为用 vue-qr 渲染 7.15 H5 免登地址二维码 + 只读链接框 + 一键复制(运营可直接把链接发给司机)
前端 modules/{ShareForm,OilForm,UseForm}.vue、api/partners/order.js 标题与注释文案同步

views/partners/**(合作商列表/新增/编辑/对接参数/余额)维护的是下游接口账户(orgNo / appKey / RSA 公私钥 / 回调地址 / 可售商品与额度),与上游换成企油通无关,无需改动;其中「商品额度」正是 /oilCard/stock 现在的数据来源。

停用旧上游依赖的后台/小程序端点(上游定聚、智慧油客已下线,这些接口再调只会超时或报错,统一改为明确提示,页面仍可查历史数据)

文件 方法 处理
app/api/controller/Order.php qrCode() / qrCodeImg() 返回「该功能已停用」(小程序不改代码,只是拿到提示)
app/api/controller/Station.php list() 返回空列表(企油通无油站查询能力),小程序页面不报错
app/store/controller/Order.php share() / cancelShare() / qrCode() 返回「小程序油品链路已停用」(顺带修掉了原先调用未定义方法的致命错误)
app/store/controller/Company.php stock() 返回「上游库存同步已停用」
app/store/controller/Goods.php onReloadStock() 同上
app/store/controller/User.php add() / oil() 分油用户池已停用,不再向上游注册用户 / 手动分油

其余 new SybOil() 残留经核对均只用于写日志(saveLogStore),无业务副作用;PaySuccess::shareOil 内的 IdingjuOil 调用位于已 continue 的分支,不可达。

G2. 接口文档就地修订(不加变更说明章节)

慧明宝供应链平台接口文档V1.0.17.docx 直接在原文覆盖修改,未新增任何「变更说明 / 附录」章节,并删除本期无用的接口与参数。

版本 / 接口层面

# 位置 改动
1 1.2 版本号 V1.0.16 → V1.0.17
2 3.4 基础数据查询 / 油站列表查询 整章删除(企油通无油站查询能力;含请求应答表与报文示例,约 277KB)
3 2.7 接口清单列表 删「油站查询」行;序号 5→4;付款码申请用途改为「获取司机免登加油地址(H5)」
4 目录 删除对应两条 TOC 条目

最终对外接口收敛为 4 个:syb.oil.oilCard.share / syb.oil.oilCard.shareList / syb.pay.qrCode.request / syb.notify.oilCard.change(另有正文中的 stockList、orderStatus 两个查询接口)。

参数层面(去掉本期无用的传参)

接口 删除 / 修改
syb.oil.oilCard.share 删请求体 amount(金额由 goodsNo 面额决定,本期不传);新增必填 name(司机姓名 V50);goodsNo 约束 ?→1;使用场景⑵ 改为「同步返回受理结果,发放结果由油卡变动通知推送(optType=share_oil)或用订单状态查询主动查」;请求报文示例同步删 amount 与注释行、补 "name"
syb.pay.qrCode.request 删请求体 longitude / latitude / oilCardType 三个参数(企油通 H5 免登不需要);API 描述改为「获取司机免登加油地址(H5)」;使用场景改写;返回值 qrCode 描述由「二维码链接」改为「H5免登加油地址」并去掉「2分钟有效/扫一次失效」旧口径;请求与应答报文示例同步更新(应答示例换成企油通 H5 地址样例)
syb.oil.oilCard.stockList 使用场景改为「查询自身可下单额度与用量统计(平台内部额度,非上游实时库存)」
syb.oil.oilCard.shareList 返回字段 num 口径由「分油数量(升)」修正为「购买数量(张)」,与实现一致
syb.notify.oilCard.change optType 删除未使用的 发券:share_volume,仅保留 share_oil / add_oil

校验:XML 良构 ✓、XSD validate.py 全部通过 ✓、段落 1717 → 1392、残留检查 gasStation/油站/share_volume/二维码链接/longitude/latitude/「变更说明」均为 0。 仍未做:本机无 LibreOffice,无法渲染 PDF 目视核对排版;删章后小节序号由 Word 样式自动重排,打开时建议「更新域」刷新目录。


附 H. 下单改为按金额直传(2026-09-06)

下单平台不再传 goodsNo(商品编码),改为直接传 amount(金额,元)。商品/面额概念从下单链路整体移除。

后端

文件 改动
app/api/controller/OilCard.php share() 入参 goodsNo → amount(必填,正则校验:元、≤2 位小数、>0);删除商品查找、goods_sku 平台库存校验、partners_goods 合作商额度校验、下单扣库存;落单金额三字段(goods_price/total_price/total_pay_price)统一取 amount;返回 amount 用传入值
同上 删除整个 stock() 方法(syb.oil.oilCard.stockList 完全建立在 goodsNo 之上,已无意义);删除已失效的 updateGoodsStockNum()、getCompany();清理 6 个不再使用的 use
同上 shareList 响应去 productName;orderStatus 响应去 province / goods_name / goods_no(这些字段现在恒为空)
app/api/model/partners/Order.php saveOrder() 去掉 PartnersGoods 的 setIncUseNum / setDecStockNum,事务包装随之取消(单条写入无需事务)
app/store/model/partners/Order.php 列表查询去掉 goods.images.file 关联与 goods_no 检索项

前端 admin-vue

views/partners/order/Index.vue:删除「商品编码」搜索项、「商品信息」列与 GoodsItem 组件引用;列名「油卡数量/油卡金额」→「数量/发放金额」,汇总文案→「发放总金额」。eslint 0 error(warning 数与改动前一致)。

接口文档(就地修订,无变更说明章节)

位置 改动
3.2.1 用户分油 请求体 删 goodsNo,新增 amount(必填,String V13,「本单发放给司机的金额,单位元,最多两位小数且必须大于 0」);请求示例同步
油券库存查询 syb.oil.oilCard.stockList 整节删除 + 目录条目删除
分油记录查询 响应删 productName 字段与示例行
订单状态查询 响应删 province / goods_name 字段行;两处 JSON 示例删对应 3 行;oil_status_zsy 描述由「中石油下单状态 1待处理/2已发券/3发券失败」改为「发放状态:0未提交 / 2发放成功 / 3发放失败」

校验:XML 良构 ✓、XSD 全过 ✓、段落 1717 → 1234;残留检查 goodsNo/stockList/油券库存/productName/goods_name/goods_no/province/中石油 均为 0。

最终 share 请求体仅剩 6 个参数:orgNo / amount / mobile / name / saleOrderNo / oilCardType。

额度管控:改用合作商余额(已实现)

去掉 goodsNo 后,原先由 yoshop_partners_goods.stock_num 承担的下单额度管控,改由 yoshop_partners.balance(合作商余额) 承担。

新增 app/common/service/PartnersBalance.php:

方法 说明
deduct() 原子扣减:UPDATE yoshop_partners SET balance = balance - amt WHERE partners_id=? AND balance >= amt,affected=1 才算成功 —— 天然防并发超扣。成功返回流水ID
refund() 回补余额并写退款流水
bindOrder() 建单后把 order_id 回填到扣款流水(deduct 时还没有订单ID)
balance() 查询当前余额

流水写入 yoshop_partners_balance_log,场景沿用现有枚举:扣减 = CONSUME(20),回补 = REFUND(40);金额拼接前用 number_format 规范化,避免注入。

接入点

位置 行为
OilCard::share 幂等检查通过后才扣款(重复 saleOrderNo 直接返回原单,不重复扣);扣款失败 → 2028 账户余额不足;建单抛异常或未成功 → 立即回补;建单成功 → bindOrder 回填订单ID
PartnersQueue 三条发放失败路径(缺司机信息 / 7.7 调用失败 / data.status=2)在状态条件跃迁 affected=1 时回补余额,天然幂等
store/controller/partners/Order::share(后台重新发放) 失败时余额已回补,重发前重新扣减;若状态被其它进程改变导致重置失败 → 回补刚扣的金额

资金一致性:先扣后建单、失败即回补;所有回补都挂在「条件跃迁胜出」之后,重复消息 / 多进程不会重复回补。

文档:amount 字段取值说明补充「下单时平台校验并扣减对接公司账户余额,余额不足返回错误码 2028;发放失败时该笔金额原路回补」。错误码 2028 账户余额不足 原本就在附录 4.2 中,无需新增。

运营入口:后台「合作商管理 → 充值」已可维护余额,「合作商 → 余额明细」可查流水,无需新增页面。

合作商「可售商品 + 库存」下线(已清理)

额度改由余额承担后,yoshop_partners_goods / yoshop_partners_stock_log 已无任何读写方,整体移除。

删除的文件(5 个 PHP + 1 个 Vue) app/api/model/PartnersGoods.php、app/common/model/PartnersGoods.php、app/store/model/PartnersGoods.php、app/common/model/partners/StockLog.php、app/store/model/partners/StockLog.php、admin-vue/src/views/partners/modules/MultiGoods.vue

改动的文件

文件 改动
app/store/model/Partners.php add() / edit() 去掉 checkGoodsData、PartnersGoodsModel::increased/updates、StockLogModel::increased 及两个 import
app/store/controller/Partners.php detail() 不再返回 goodsList(连同 allot_num 填充逻辑),去 import
admin-vue/views/partners/{Create,Update}.vue 移除 <MultiGoods> 组件、goodsList data、提交时的 values.goodsList、handleGoodsChange、import 与 components 注册
admin-vue/views/partners/modules/index.js 移除 MultiGoods 导入导出
admin-vue/common/model/partners/Index.js 移除 GoodsApi import、formData.goods、getGoodsList() 方法及其在 getFromData 中的调用

数据库:qyt_migration.sql 第 4 节把两张表注释标记为「已下线,仅历史数据」,并附上 DROP TABLE 语句(已注释)。两表仍有历史数据,是否物理删除由你决定,确认后取消注释执行即可。

校验:PHP 端 PartnersGoods / StockLog / partners_goods / partners_stock_log 全仓残留 0;admin-vue MultiGoods / goodsList 残留 0,eslint 0 problems。


附 I. 后台「分油公司 / 分油用户」菜单下线(2026-09-06)

企油通改为单企业配置(config/qyt.php)、分油用户池取消后,这两个菜单对应的 yoshop_company / yoshop_account 已无业务意义,后端接口也早已返回「已停用」。本次把前端入口整体收掉。

删除

类型 路径
页面 views/company/(Index / Create / Update / balance / modules 共 8 个文件 + style.less)、views/account/Index.vue
API api/company/{index,balance,recharge}.js、api/account/index.js
模型 / 枚举 common/model/company/Index.js、common/enum/company/**(3 个)
用户分油弹窗 views/user/modules/OilForm.vue(依赖已停用的 /user/oil)

改动

文件 改动
config/router.config.js 移除整个 /company 路由块(含嵌套的 /account),菜单「分油公司」「分油用户」「公司余额明细」一并消失
common/model/user/Index.js 去 CompanyApi import、formData.companyList、getCompanyList() 及其在 getFromData 的调用
views/user/{Create,Update}.vue 移除表单里的「分油公司」下拉(数据源已不存在)
views/user/Index.vue 移除 OilForm 组件、import、注册、handleOil() 与那行已注释的「分油」按钮
views/user/modules/index.js 移除 OilForm 导入导出
api/user/index.js 移除 oil: '/user/oil' 与 oil() 方法

校验

  • 残留引用检查:views/company / api/company / model/company / enum/company / views/account/Index / api/account 均为 0;OilForm 仅剩合作商订单页那个(不同文件,需保留)。
  • eslint:本次改动引入 0 新错误。改动过程中在 views/partners/{Create,Update}.vue 留下的 3 处 comma-dangle 已修复。当前全量仅剩 2 个改动前就存在的错误:config/router.config.js 的 comma-dangle、views/user/grade/modules/index.js 的 eol-last(两者均未被本次改动触碰)。

后端未动(说明)

app/store/controller/{Company,Account,User}.php 及 yoshop_company / yoshop_account 的模型仍在仓库中,相关方法已返回「已停用」提示,现在没有任何前端调用方。保留是为了不影响历史数据查询与可能的权限表(yoshop_store_api / yoshop_store_menu)记录;如需彻底清除,可在确认权限表无关联后单独处理。