'待分账', 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; } }