文件
xionggeshop/application/admin/controller/shopro/split/Order.php
可乐加冰 702154d68f ```
feat(shopro/split): 优化汇付分账查询功能

- 重构汇付渠道状态查询逻辑,新增 queryHuifuChannelStatus 方法处理支付查单和分账查询
- 支持未 confirm 时使用支付查单,有 confirm_hf_seq_id 时查询分账结果
- 优化异常处理机制,区分 HttpResponseException 和其他异常类型
- 改进前端显示逻辑,根据返回数据类型展示更详细的查询结果信息
- 增强错误提示和用户体验,提供更精确的反馈信息
```
2026-10-05 10:18:35 +08:00

269 行
10 KiB
PHP

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<?php
namespace app\admin\controller\shopro\split;
use app\admin\controller\shopro\Base;
use addons\shopro\library\split\Config as SplitConfig;
use addons\shopro\library\split\SplitExecutor;
use addons\shopro\library\split\ProfitSharingService;
use app\admin\model\shopro\order\Order as ShopOrder;
use app\admin\model\shopro\user\User as ShopUser;
class Order extends Base
{
protected $model = null;
protected $statusMap = [
0 => '待分账',
1 => '分账中',
2 => '成功',
3 => '部分成功',
4 => '失败',
];
public function _initialize()
{
parent::_initialize();
$this->model = new \addons\shopro\model\split\SplitOrder;
}
public function index()
{
$this->request->filter(['strip_tags']);
if ($this->request->isAjax()) {
$nobuildfields = ['buyer_keyword', 'order_sn', 'status', 'channel', 'createtime'];
list($where, $sort, $order, $offset, $limit) = $this->custombuildparams(null, $nobuildfields);
$query = $this->buildSearch();
$total = $query->where($where)->count();
$list = $this->buildSearch()
->with([
'receivers',
'order' => function ($q) {
$q->field('id,order_sn,user_id,pay_fee,paytime,status')
->with(['user' => function ($uq) {
$uq->field('id,nickname,mobile');
}]);
},
])
->where($where)
->order($sort, $order)
->limit($offset, $limit)
->select();
$list = $this->formatSplitOrderList($list);
return $this->success('操作成功', null, ['total' => $total, 'rows' => $list]);
}
$this->assignconfig('splitHuifuOnly', (new SplitConfig())->isHuifuMode());
return $this->view->fetch();
}
protected function buildSearch()
{
$filter = (array) json_decode($this->request->get('filter', ''), true);
$query = $this->model;
if (!empty($filter['order_sn'])) {
$orderIds = ShopOrder::where('order_sn', 'like', '%' . trim($filter['order_sn']) . '%')->column('id');
$query = $query->where('order_id', 'in', $orderIds ?: [0]);
}
if (!empty($filter['buyer_keyword'])) {
$kw = trim($filter['buyer_keyword']);
$userIds = ShopUser::where('nickname|mobile', 'like', '%' . $kw . '%')->column('id');
$orderIds = $userIds
? ShopOrder::where('user_id', 'in', $userIds)->column('id')
: [];
$query = $query->where('order_id', 'in', $orderIds ?: [0]);
}
if (isset($filter['status']) && $filter['status'] !== '') {
$query = $query->where('status', intval($filter['status']));
}
if (!empty($filter['channel'])) {
$query = $query->where('channel', $filter['channel']);
}
if (!empty($filter['createtime'])) {
$parts = explode(' - ', $filter['createtime']);
if (count($parts) === 2) {
$start = strtotime($parts[0]);
$end = strtotime($parts[1] . ' 23:59:59');
if ($start && $end) {
$query = $query->where('createtime', 'between', [$start, $end]);
}
}
}
return $query;
}
protected function formatSplitOrderList($list)
{
$rows = [];
foreach ($list as $item) {
$data = is_array($item) ? $item : $item->toArray();
$order = $data['order'] ?? null;
$user = is_array($order) ? ($order['user'] ?? null) : null;
$status = intval($data['status'] ?? 0);
$channel = $data['channel'] ?? 'wechat';
$buyerNickname = $user['nickname'] ?? '';
$buyerMobile = $user['mobile'] ?? '';
$receivers = $data['receivers'] ?? [];
$lines = [];
foreach ($receivers as $recv) {
$r = is_array($recv) ? $recv : (method_exists($recv, 'toArray') ? $recv->toArray() : []);
$lines[] = sprintf(
'%s ¥%s',
$this->roleTypeLabel($r['role_type'] ?? ''),
number_format(floatval($r['amount'] ?? 0), 2, '.', '')
);
}
$data['buyer_nickname'] = $buyerNickname;
$data['buyer_mobile'] = $buyerMobile;
$data['buyer_display'] = $this->formatBuyerDisplay(
$buyerNickname,
$buyerMobile,
is_array($order) ? intval($order['user_id'] ?? 0) : 0
);
$data['status_text'] = $this->statusMap[$status] ?? '-';
$data['channel_text'] = $channel === 'huifu' ? '汇付' : '微信';
$data['createtime_text'] = !empty($data['createtime'])
? date('Y-m-d H:i:s', $data['createtime'])
: '-';
$data['order_paytime_text'] = (is_array($order) && !empty($order['paytime']))
? date('Y-m-d H:i:s', $order['paytime'])
: '-';
$data['receiver_summary'] = $lines ? implode(';', $lines) : '—';
unset($data['order']);
$rows[] = $data;
}
return $rows;
}
protected function formatBuyerDisplay($nickname, $mobile, $userId)
{
$nickname = trim((string) $nickname);
$mobile = trim((string) $mobile);
if ($nickname !== '') {
return $mobile !== '' ? ($nickname . '(' . $mobile . ')') : $nickname;
}
if ($mobile !== '') {
return $mobile;
}
return $userId ? ('用户#' . $userId) : '—';
}
protected function roleTypeLabel($type)
{
$map = [
'platform' => '平台',
'promoter' => '推广员',
'regional_agent' => '区域公司',
];
return $map[$type] ?? ($type ?: '接收方');
}
public function retry($ids = null)
{
$id = $ids ?: $this->request->post('id');
$row = $this->model->get($id);
if (!$row) {
$this->error('分账记录不存在,可能已被上次重试替换。请刷新列表后,对最新一条失败记录重试');
}
$orderId = (int) $row['order_id'];
$channel = $row['channel'] ?? 'wechat';
try {
$result = (new SplitExecutor())->retry($id);
} catch (\Throwable $e) {
$this->error('重试失败:' . $e->getMessage());
}
if ($result) {
$this->success('重试成功');
}
$hint = $this->formatSplitWxResultHint($orderId, $channel);
if ($hint === '') {
$hint = ':请确认已部署最新 HuifuSplitService.php;或执行 php addons/shopro/tests/split_commission_verify.php try-split ' . $row['order_sn'] . ' --execute 查看详情';
}
$this->error('重试失败' . $hint);
}
protected function formatSplitWxResultHint($orderId, $channel)
{
$latest = $this->model->where('order_id', $orderId)
->where('channel', $channel)
->order('id', 'desc')
->find();
if (!$latest || empty($latest['wx_result'])) {
return '';
}
$raw = is_string($latest['wx_result']) ? $latest['wx_result'] : json_encode($latest['wx_result'], JSON_UNESCAPED_UNICODE);
if ($raw !== '' && strpos($raw, 'fund_freeze_stat=FREEZE') !== false) {
return ':' . mb_substr($raw, 0, 500);
}
$decoded = json_decode($raw, true);
if (is_array($decoded)) {
$parts = array_filter([
$decoded['resp_desc'] ?? '',
$decoded['resp_code'] ?? '',
$decoded['bank_message'] ?? '',
]);
return $parts ? (':' . implode(' ', $parts)) : (':' . mb_substr($raw, 0, 300));
}
return ':' . mb_substr($raw, 0, 300);
}
public function query($ids = null)
{
$row = $this->model->get($ids);
if (!$row) {
$this->error('记录不存在');
}
try {
if (($row['channel'] ?? 'wechat') === 'huifu' && class_exists('\\addons\\shopro_huifu\\library\\HuifuSplitService')) {
$result = $this->queryHuifuChannelStatus($row);
} elseif ((new SplitConfig())->isHuifuMode()) {
$this->error('当前为汇付分账,请对 channel=huifu 的分账记录查询,或到汇付控台查看分账结果');
} else {
$service = new ProfitSharingService();
$result = $service->queryOrder([
'out_order_no' => $row['out_order_no'],
'transaction_id' => $row['transaction_id'],
]);
}
} catch (\think\exception\HttpResponseException $e) {
throw $e;
} catch (\Throwable $e) {
$this->error('查询失败:' . ($e->getMessage() ?: '未知错误'));
}
$row->wx_result = json_encode($result, JSON_UNESCAPED_UNICODE);
$row->save();
$this->success('查询成功', null, $result);
}
/**
* 汇付:未 confirm 成功时用支付查单;已有 confirm_hf_seq_id 时再查分账结果
*/
protected function queryHuifuChannelStatus($row)
{
$payload = [];
if (class_exists('\\addons\\shopro_huifu\\library\\HuifuQueryService')) {
$payload['pay_query'] = (new \addons\shopro_huifu\library\HuifuQueryService())->queryByOrderSn($row['order_sn']);
}
$confirmSeq = trim((string) ($row['confirm_hf_seq_id'] ?? ''));
if ($confirmSeq !== '') {
$service = new \addons\shopro_huifu\library\HuifuSplitService();
$payload['split_query'] = $service->querySplit($confirmSeq);
}
if (empty($payload)) {
throw new \RuntimeException('汇付查询组件不可用');
}
return $payload;
}
}