feat(split): 完善分账接收方列表展示功能

- 添加用户信息查询和展示,包括昵称、手机号等用户详情
- 增加汇付进件状态显示和账号匹配校验功能
- 优化前端表格界面,新增用户关联信息、进件核对状态等列
- 改进数据格式化处理,提供更详细的分账接收方信息展示
- 更新表单提示文案,明确用户ID填写规则和要求
```
这个提交包含在:
可乐加冰
2026-10-05 12:56:57 +08:00
父节点 11e930f60d
当前提交 9181c7144f
共有 4 个文件被更改,包括 318 次插入 和 22 次删除

查看文件

@@ -5,6 +5,8 @@ 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\ProfitSharingService;
use app\admin\model\shopro\user\User as ShopUser;
use think\Db;
class Receiver extends Base
{
@@ -23,6 +25,7 @@ class Receiver extends Base
list($where, $sort, $order, $offset, $limit) = $this->custombuildparams();
$total = $this->model->where($where)->count();
$list = $this->model->where($where)->order($sort, $order)->limit($offset, $limit)->select();
$list = $this->formatReceiverList($list);
return $this->success('操作成功', null, ['total' => $total, 'rows' => $list]);
}
$this->assignconfig('splitHuifuOnly', (new SplitConfig())->isHuifuMode());
@@ -67,6 +70,142 @@ class Receiver extends Base
$this->success('操作成功');
}
protected function formatReceiverList($list)
{
$rows = [];
$userIds = [];
foreach ($list as $item) {
$data = is_array($item) ? $item : $item->toArray();
$roleId = intval($data['role_id'] ?? 0);
if ($roleId > 0) {
$userIds[] = $roleId;
}
}
$userIds = array_values(array_unique($userIds));
$users = [];
if ($userIds) {
$userRows = ShopUser::where('id', 'in', $userIds)->field('id,nickname,mobile')->select();
foreach ($userRows as $user) {
$u = is_array($user) ? $user : $user->toArray();
$users[intval($u['id'])] = $u;
}
}
$onboardMap = [];
if ($userIds) {
try {
$onboardRows = Db::name('shopro_huifu_onboard')
->where('user_id', 'in', $userIds)
->order('id desc')
->select();
foreach ($onboardRows as $ob) {
$uid = intval($ob['user_id']);
if (!isset($onboardMap[$uid])) {
$onboardMap[$uid] = $ob;
}
}
} catch (\Throwable $e) {
$onboardMap = [];
}
}
foreach ($list as $item) {
$data = is_array($item) ? $item : $item->toArray();
$roleId = intval($data['role_id'] ?? 0);
$user = $roleId > 0 && isset($users[$roleId]) ? $users[$roleId] : null;
$onboard = $roleId > 0 && isset($onboardMap[$roleId]) ? $onboardMap[$roleId] : null;
$data['user_id'] = $roleId;
$data['user_nickname'] = is_array($user) ? trim((string) ($user['nickname'] ?? '')) : '';
$data['user_mobile'] = is_array($user) ? trim((string) ($user['mobile'] ?? '')) : '';
$data['user_display'] = $this->formatUserDisplay($roleId, $user);
$data['role_id_text'] = $roleId > 0 ? (string) $roleId : '0(通用)';
$onboardHuifuId = trim((string) ($onboard['huifu_id'] ?? ''));
$receiverAccount = trim((string) ($data['receiver_account'] ?? ''));
$data['onboard_huifu_id'] = $onboardHuifuId;
$data['onboard_status'] = $onboard['onboard_status'] ?? '';
$data['onboard_status_text'] = $this->formatOnboardStatus($onboard['onboard_status'] ?? '');
$data['account_match'] = ($onboardHuifuId !== '' && $receiverAccount !== '' && $onboardHuifuId === $receiverAccount) ? 1 : 0;
$data['account_match_text'] = $this->formatAccountMatch($roleId, $onboardHuifuId, $receiverAccount);
$data['channel_text'] = ($data['channel'] ?? '') === 'huifu' ? '汇付' : '微信';
$data['role_type_text'] = $this->roleTypeLabel($data['role_type'] ?? '');
$data['status_text'] = ($data['status'] ?? '') === 'forbidden' ? '禁用' : '正常';
$data['createtime_text'] = !empty($data['createtime'])
? date('Y-m-d H:i:s', $data['createtime'])
: '-';
$rows[] = $data;
}
return $rows;
}
protected function formatUserDisplay($roleId, $user)
{
if ($roleId <= 0) {
return '—(规则模式通用接收方)';
}
if (!is_array($user)) {
return '用户#' . $roleId . '(未找到)';
}
$nickname = trim((string) ($user['nickname'] ?? ''));
$mobile = trim((string) ($user['mobile'] ?? ''));
if ($nickname !== '' && $mobile !== '') {
return $nickname . '(' . $mobile . ')';
}
if ($nickname !== '') {
return $nickname;
}
if ($mobile !== '') {
return $mobile;
}
return '用户#' . $roleId;
}
protected function formatOnboardStatus($status)
{
$map = [
'draft' => '草稿',
'submitting' => '提交中',
'basic_ok' => '基础进件完成',
'busi_pending' => '业务开通中',
'approved' => '已通过',
'rejected' => '已驳回',
'failed' => '失败',
];
$status = trim((string) $status);
return $status !== '' ? ($map[$status] ?? $status) : '—';
}
protected function formatAccountMatch($roleId, $onboardHuifuId, $receiverAccount)
{
if ($roleId <= 0) {
return '—';
}
if ($onboardHuifuId === '' && $receiverAccount === '') {
return '未配置';
}
if ($onboardHuifuId === '') {
return '未进件';
}
if ($receiverAccount === '') {
return '未填账号';
}
return $onboardHuifuId === $receiverAccount ? '与进件一致' : '与进件不一致';
}
protected function roleTypeLabel($type)
{
$map = [
'platform' => '平台',
'promoter' => '推广员',
'regional_agent' => '区域公司',
];
return $map[$type] ?? ($type ?: '-');
}
public function syncWx($ids = null)
{
$row = $this->model->get($ids);

查看文件

@@ -2,7 +2,7 @@
<div class="panel-body">
<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST">
<div class="form-group"><label class="control-label col-xs-12 col-sm-2">角色:</label><div class="col-xs-12 col-sm-8"><select class="form-control" name="row[role_type]"><option value="platform">平台</option><option value="promoter">推广员</option><option value="regional_agent">区域公司</option></select></div></div>
<div class="form-group"><label class="control-label col-xs-12 col-sm-2">关联ID:</label><div class="col-xs-12 col-sm-8"><input class="form-control" name="row[role_id]" type="number" value="0"></div></div>
<div class="form-group"><label class="control-label col-xs-12 col-sm-2">用户ID:</label><div class="col-xs-12 col-sm-8"><input class="form-control" name="row[role_id]" type="number" value="0" placeholder="推广员/区域公司的商城用户ID;规则模式填0"><p class="help-block">按佣金分账时必填具体 user_id,且 huifu_id 须与进件一致</p></div></div>
<div class="form-group"><label class="control-label col-xs-12 col-sm-2">通道:</label><div class="col-xs-12 col-sm-8"><select class="form-control" name="row[channel]"><option value="wechat">微信</option><option value="huifu">汇付</option></select></div></div>
<div class="form-group"><label class="control-label col-xs-12 col-sm-2">接收方类型:</label><div class="col-xs-12 col-sm-8"><select class="form-control" name="row[receiver_type]"><option value="PERSONAL_OPENID">个人openid</option><option value="MERCHANT_ID">商户号</option><option value="HUIFU_ID">汇付huifu_id</option></select></div></div>
<div class="form-group"><label class="control-label col-xs-12 col-sm-2">接收方账号:</label><div class="col-xs-12 col-sm-8"><input class="form-control" name="row[receiver_account]" required placeholder="openid/商户号/huifu_id"></div></div>

查看文件

@@ -1,25 +1,168 @@
<link rel="stylesheet" href="__CDN__/assets/addons/shopro/libs/element/element.css">
<div id="splitReceiverIndex" v-cloak style="padding:20px;">
<el-button type="primary" size="small" @click="openAdd" style="margin-bottom:12px;">添加接收方</el-button>
<el-table :data="listData" border v-loading="loading">
<el-table-column prop="id" label="ID" width="70"></el-table-column>
<el-table-column label="角色" width="120">
<template slot-scope="s">{{ roleTypeText(s.row.role_type) }}</template>
</el-table-column>
<el-table-column prop="channel" label="通道" width="90"></el-table-column>
<el-table-column prop="receiver_type" label="类型" width="140"></el-table-column>
<el-table-column prop="receiver_account" label="账号" min-width="180"></el-table-column>
<el-table-column prop="receiver_name" label="名称" width="120"></el-table-column>
<el-table-column v-if="!splitHuifuOnly" label="微信状态" width="100">
<template slot-scope="s">{{ wxStatusText(s.row.wx_status) }}</template>
</el-table-column>
<el-table-column label="操作" width="160">
<template slot-scope="s">
<el-button v-if="!splitHuifuOnly" type="text" @click="syncWx(s.row.id)">同步微信</el-button>
<span v-else style="color:#909399;font-size:12px;">汇付分账在控台绑定</span>
</template>
</el-table-column>
</el-table>
<link rel="stylesheet" href="__CDN__/assets/addons/shopro/libs/common.css">
<style>
#splitReceiverIndex {
padding: 0 20px 30px;
background: #f5f7fa;
min-height: 100%;
}
.page-card {
background: #fff;
border-radius: 8px;
padding: 16px 20px;
margin-bottom: 12px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
.page-intro {
font-size: 13px;
color: #606266;
line-height: 1.7;
margin: 0;
}
.page-intro strong { color: #303133; font-weight: 600; }
.cell-plain {
font-size: 13px;
color: #303133;
line-height: 1.5;
white-space: nowrap;
}
.cell-sub-plain {
font-size: 12px;
color: #909399;
line-height: 1.5;
margin-top: 3px;
white-space: nowrap;
}
.scroll-cell {
max-width: 100%;
overflow-x: hidden;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
scrollbar-width: thin;
}
.scroll-cell:hover {
overflow-x: auto;
cursor: ew-resize;
}
.scroll-cell::-webkit-scrollbar { height: 4px; }
.scroll-cell::-webkit-scrollbar-thumb {
background: rgba(116, 56, 213, 0.35);
border-radius: 2px;
}
.scroll-cell-inner {
display: inline-block;
white-space: nowrap;
min-width: min-content;
}
.mono-text {
font-family: Consolas, Monaco, "Courier New", monospace;
letter-spacing: 0.2px;
}
.role-tag {
display: inline-block;
padding: 0 8px;
border-radius: 4px;
font-size: 12px;
line-height: 20px;
background: #f0ebfa;
color: #7438D5;
}
.uid-text {
font-weight: 600;
color: #303133;
}
.match-tag {
display: inline-block;
padding: 0 6px;
border-radius: 4px;
font-size: 11px;
line-height: 18px;
white-space: nowrap;
}
.match-tag.ok { background: #f0f9eb; color: #67c23a; }
.match-tag.warn { background: #fdf6ec; color: #e6a23c; }
.match-tag.err { background: #fef0f0; color: #f56c6c; }
.match-tag.neutral { background: #f4f4f5; color: #909399; }
.status-tag {
display: inline-block;
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
}
.status-tag.normal { background: #f0f9eb; color: #67c23a; }
.status-tag.forbidden { background: #fef0f0; color: #f56c6c; }
#splitReceiverIndex .el-table .cell { line-height: 1.5; }
</style>
<div id="splitReceiverIndex" v-cloak>
<div class="page-card">
<p class="page-intro">
<strong>分账接收方</strong>用于网关分账时指定资金接收账户。
<strong>推广员/区域公司</strong>请填写对应商城 <strong>用户 ID(role_id)</strong>,并与汇付进件 <strong>huifu_id</strong> 保持一致;
<strong>role_id=0</strong> 表示规则模式下的通用接收方(不按具体用户分账)。
</p>
</div>
<div class="page-card">
<el-button type="primary" size="small" @click="openAdd">添加接收方</el-button>
</div>
<div class="page-card" style="padding-bottom: 8px;">
<el-table :data="listData" border stripe v-loading="loading" empty-text="暂无接收方,请先添加">
<el-table-column prop="id" label="记录ID" width="72" align="center"></el-table-column>
<el-table-column label="角色" width="96" align="center">
<template slot-scope="s">
<span class="role-tag">{{ s.row.role_type_text || roleTypeText(s.row.role_type) }}</span>
</template>
</el-table-column>
<el-table-column label="关联用户" min-width="200">
<template slot-scope="s">
<div class="cell-plain">
<span class="uid-text">ID {{ s.row.role_id_text }}</span>
</div>
<div class="scroll-cell" @wheel="scrollCellWheel" v-if="s.row.user_id > 0">
<div class="scroll-cell-inner cell-sub-plain">{{ s.row.user_display }}</div>
</div>
<div class="cell-sub-plain" v-else>{{ s.row.user_display }}</div>
</template>
</el-table-column>
<el-table-column label="通道 / 类型" width="130">
<template slot-scope="s">
<div class="cell-plain">{{ s.row.channel_text || channelText(s.row.channel) }}</div>
<div class="cell-sub-plain">{{ s.row.receiver_type }}</div>
</template>
</el-table-column>
<el-table-column label="汇付/接收账号" min-width="200">
<template slot-scope="s">
<div class="scroll-cell" @wheel="scrollCellWheel">
<div class="scroll-cell-inner cell-plain mono-text">{{ s.row.receiver_account || '—' }}</div>
</div>
<div class="cell-sub-plain" v-if="s.row.receiver_name">名称 {{ s.row.receiver_name }}</div>
<div class="cell-sub-plain" v-if="s.row.acct_id">子账户 {{ s.row.acct_id }}</div>
</template>
</el-table-column>
<el-table-column label="进件核对" min-width="150">
<template slot-scope="s">
<div class="cell-sub-plain" v-if="s.row.onboard_huifu_id">进件 {{ s.row.onboard_huifu_id }}</div>
<div class="cell-sub-plain">{{ s.row.onboard_status_text }}</div>
<span :class="matchTagClass(s.row.account_match_text)">{{ s.row.account_match_text }}</span>
</template>
</el-table-column>
<el-table-column label="状态" width="72" align="center">
<template slot-scope="s">
<span :class="'status-tag ' + (s.row.status === 'forbidden' ? 'forbidden' : 'normal')">
{{ s.row.status_text || (s.row.status === 'forbidden' ? '禁用' : '正常') }}
</span>
</template>
</el-table-column>
<el-table-column label="操作" width="140" fixed="right">
<template slot-scope="s">
<el-button v-if="!splitHuifuOnly" type="text" @click="syncWx(s.row.id)">同步微信</el-button>
<span v-else style="color:#909399;font-size:12px;">控台绑定分账关系</span>
</template>
</el-table-column>
</el-table>
<p style="margin-top:10px;font-size:12px;color:#909399;">
长账号字段<strong>鼠标悬浮可左右滑动</strong>查看完整内容。按佣金分账时,订单推广员的 user_id 必须在此有对应接收方。
</p>
</div>
</div>
<script src="__CDN__/assets/addons/shopro/libs/vue.js"></script>
<script src="__CDN__/assets/addons/shopro/libs/element/element.js"></script>

查看文件

@@ -29,6 +29,20 @@ define(['jquery', 'bootstrap', 'backend', 'form'], function ($, undefined, Backe
regional_agent: '区域公司',
};
return map[type] || type;
},
matchTagClass(text) {
if (!text || text === '—') return 'match-tag neutral';
if (text === '与进件一致') return 'match-tag ok';
if (text === '未进件' || text === '未填账号' || text === '未配置') return 'match-tag warn';
return 'match-tag err';
},
scrollCellWheel(e) {
var el = e.currentTarget;
if (!el || el.scrollWidth <= el.clientWidth) return;
if (Math.abs(e.deltaY) >= Math.abs(e.deltaX)) {
e.preventDefault();
el.scrollLeft += e.deltaY;
}
}
}
});