fix(agent): 规范分销商详情展示,明确合伙股东仅为会员进阶身份并杜绝误导管理员
- 修复非合伙股东会员(品酒师)被误判为自购考核已暂停的问题,明确赋值为免自购考核 - 分离基础会员信息与合伙股东进阶身份展示,对非合伙股东提供清晰的身份说明与免考核提示 - 纠正佣金规则提示文案为收益中心固定金额规则,收益现金统一规范为收益中心累计现金 - 列表页与详情页同步优化非合伙股东 30 日活跃状态呈现 - 增加 TDD 自动化验证测试套件 verify_agent_partner_identity_spec.js 并通过全部回归测试
这个提交包含在:
@@ -58,18 +58,43 @@ class Agent extends Common
|
||||
$last = intval($detail->last_self_order_time ?? 0);
|
||||
$days = \addons\shopro\service\partner\PartnerConfig::activeDays();
|
||||
$ago = $last ? intval(floor((time() - $last) / 86400)) : null;
|
||||
$cash = \app\admin\model\shopro\partner\Reward::where('user_id', $id)
|
||||
->where('status', 1)
|
||||
->where('amount', '>', 0)
|
||||
->sum('amount');
|
||||
|
||||
$identityInfo = \addons\shopro\service\partner\IdentityService::build($detail);
|
||||
$isPartner = !empty($identityInfo['is_partner']);
|
||||
$detail->is_partner = $isPartner ? 1 : 0;
|
||||
|
||||
$detail->next_push3_amount = number_format((float) $next, 2, '.', '');
|
||||
$detail->next_push3_text = '第 ' . ($seq + 1) . ' 单将返 ' . $detail->next_push3_amount;
|
||||
$detail->last_self_order_text = $last ? date('Y-m-d H:i:s', $last) : '从未下单';
|
||||
$detail->active_status_text = !empty($detail->active_30d)
|
||||
? '正常领取'
|
||||
: ('已暂停' . ($ago !== null ? '(距今 ' . $ago . ' 天)' : ''));
|
||||
$detail->partner_cash_income = number_format((float) $cash, 2, '.', '');
|
||||
|
||||
// 业务核心规范:合伙股东仅是会员专属进阶身份,普通会员与品酒师免自购考核
|
||||
if (!$isPartner) {
|
||||
$detail->active_status_text = '免自购考核 (基础会员/品酒师)';
|
||||
} else {
|
||||
$detail->active_status_text = !empty($detail->active_30d)
|
||||
? '正常领取'
|
||||
: ('已暂停' . ($ago !== null ? '(距今 ' . $ago . ' 天)' : ''));
|
||||
}
|
||||
|
||||
$totalCash = \app\admin\model\shopro\partner\Reward::where('user_id', $id)
|
||||
->where('status', 1)
|
||||
->where('amount', '>', 0)
|
||||
->sum('amount');
|
||||
$push3Cash = \app\admin\model\shopro\partner\Reward::where('user_id', $id)
|
||||
->where('status', 1)
|
||||
->where('amount', '>', 0)
|
||||
->where('reward_type', 'push3')
|
||||
->sum('amount');
|
||||
$partnerExclusiveCash = \app\admin\model\shopro\partner\Reward::where('user_id', $id)
|
||||
->where('status', 1)
|
||||
->where('amount', '>', 0)
|
||||
->where('reward_type', '<>', 'push3')
|
||||
->sum('amount');
|
||||
|
||||
$detail->income_total_cash = number_format((float) $totalCash, 2, '.', '');
|
||||
$detail->push3_cash_income = number_format((float) $push3Cash, 2, '.', '');
|
||||
$detail->partner_exclusive_cash = number_format((float) $partnerExclusiveCash, 2, '.', '');
|
||||
$detail->partner_cash_income = $detail->income_total_cash;
|
||||
$detail->partner_mode = \addons\shopro\service\partner\PartnerConfig::isPartnerMode() ? 1 : 0;
|
||||
$rate = (new \addons\shopro\service\partner\RateService())->currentRate();
|
||||
$detail->madow_value = \addons\shopro\service\partner\IdentityService::formatMoney((float) ($detail->madow ?? 0) * (float) $rate);
|
||||
|
||||
@@ -202,7 +202,10 @@
|
||||
: state.data.user_id
|
||||
}}
|
||||
</div>
|
||||
<el-form size="small" label-position="right" label-width="76px">
|
||||
<el-form size="small" label-position="right" label-width="116px" style="margin-top: 12px;">
|
||||
<div style="font-weight: 600; font-size: 13px; color: var(--sa-title); margin: 8px 0 12px; padding-bottom: 4px; border-bottom: 1px solid var(--sa-border);">
|
||||
基础分销信息
|
||||
</div>
|
||||
<el-form-item label="分销商状态:">
|
||||
<el-dropdown popper-class="agent-popover" trigger="click" @command="onChangeStatus">
|
||||
<span class="status" :style="{
|
||||
@@ -217,8 +220,8 @@
|
||||
{if $auth->check('shopro/commission/agent/edit')}
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item class="status" :style="{
|
||||
color: value.color,
|
||||
}" v-for="(value, key) in statusStyle" :key="key" :command="key">{{ value.label }}
|
||||
color: value.color,
|
||||
}" v-for="(value, key) in statusStyle" :key="key" :command="key">{{ value.label }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
{/if}
|
||||
@@ -228,7 +231,7 @@
|
||||
<el-form-item label="分销等级:">
|
||||
<template v-if="state.data.level_info">
|
||||
<div>{{ state.data.level_info.name }}</div>
|
||||
<div>(等级{{ state.data.level_info.level }})</div>
|
||||
<div class="ml-1" style="color: var(--sa-subfont);">(等级{{ state.data.level_info.level }})</div>
|
||||
</template>
|
||||
<template v-if="!state.data.level_info">
|
||||
{{ state.data.level }}
|
||||
@@ -254,8 +257,10 @@
|
||||
<el-button class="ml-2" type="primary" link @click="onChangeParentUser">更换
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="state.data.partner_mode" label="佣金比例:">
|
||||
<div class="tip">已切换为合伙股东固定金额,旧百分比不再生效。</div>
|
||||
<el-form-item v-if="state.data.partner_mode" label="佣金规则:">
|
||||
<div class="tip" style="color: var(--sa-subfont); line-height: 1.4;">
|
||||
已切换为收益中心固定金额规则,旧百分比不再生效。
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item v-else label="佣金比例:">
|
||||
<div class="commission-item">
|
||||
@@ -289,45 +294,87 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="允许升级:">
|
||||
<el-form-item label="允许自动升级:">
|
||||
{if $auth->check('shopro/commission/agent/edit')}
|
||||
<el-switch v-model="state.data.upgrade_lock" :active-value="0" :inactive-value="1"
|
||||
@change="state.data.user_id?onEdit({upgrade_lock: state.data.upgrade_lock}): ''">
|
||||
</el-switch>
|
||||
{/if}
|
||||
</el-form-item>
|
||||
<el-form-item label="酒道馆:">
|
||||
{{ state.data.is_venue ? '是' : '否' }}
|
||||
<el-form-item label="直推奖(推三返一):">
|
||||
<div>第 {{ state.data.direct_order_seq || 0 }} 单,{{ state.data.next_push3_text || '' }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="区县代理:">
|
||||
{{ state.data.is_county ? '是' : '否' }}{{ state.data.county_code ? '(' + state.data.county_code + ')' : '' }}
|
||||
<el-form-item label="累计直推开拓奖:">
|
||||
<div>{{ state.data.push3_cash_income || '0.00' }} 元</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
|
||||
<div style="font-weight: 600; font-size: 13px; color: var(--sa-title); margin: 16px 0 10px; padding-bottom: 4px; border-bottom: 1px solid var(--sa-border);" class="sa-flex sa-row-between">
|
||||
<span>合伙股东身份(会员进阶身份)</span>
|
||||
{if $auth->check('shopro/commission/agent/edit')}
|
||||
<el-button type="primary" @click="onOpenIdentity">开通 / 调整身份</el-button>
|
||||
<el-button type="primary" size="small" link @click="onOpenIdentity">开通 / 调整身份</el-button>
|
||||
{/if}
|
||||
</el-form-item>
|
||||
<el-form-item label="身份组合:">
|
||||
{{ state.data.identity_text || '-' }}
|
||||
</el-form-item>
|
||||
<el-form-item label="活跃状态:">
|
||||
{{ state.data.active_status_text || (state.data.active_30d ? '正常领取' : '已暂停') }}
|
||||
</el-form-item>
|
||||
<el-form-item label="直推序号:">
|
||||
第 {{ state.data.direct_order_seq || 0 }} 单,{{ state.data.next_push3_text || '' }}
|
||||
</el-form-item>
|
||||
<el-form-item label="最近自购:">
|
||||
{{ state.data.last_self_order_text || '从未下单' }}
|
||||
</el-form-item>
|
||||
<el-form-item label="马豆:">
|
||||
{{ state.data.madow || 0 }}(约合 ¥{{ state.data.madow_value || '0.00' }})
|
||||
</el-form-item>
|
||||
<el-form-item label="兑换券:">
|
||||
总额 {{ state.data.voucher_total || 0 }} / 已释放 {{ state.data.voucher_released || 0 }} /
|
||||
已使用 {{ state.data.voucher_used || 0 }} / 可用 {{ state.data.voucher_available || 0 }}
|
||||
</el-form-item>
|
||||
<el-form-item label="累计合伙股东现金:">
|
||||
{{ state.data.partner_cash_income || '0.00' }}
|
||||
</div>
|
||||
|
||||
<template v-if="state.data.is_partner">
|
||||
<el-form-item label="合伙股东身份:">
|
||||
<el-tag type="warning" effect="dark" size="small">{{ state.data.identity_text }}</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item label="酒道馆:">
|
||||
<el-tag size="small" :type="state.data.is_venue ? 'success' : 'info'">
|
||||
{{ state.data.is_venue ? '已开通' : '未开通' }}
|
||||
</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item label="区县代理:">
|
||||
<el-tag size="small" :type="state.data.is_county ? 'warning' : 'info'">
|
||||
{{ state.data.is_county ? '已开通' + (state.data.county_code ? ' (' + state.data.county_code + ')' : '') : '未开通' }}
|
||||
</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item label="30天自购考核:">
|
||||
<span v-if="state.data.active_30d" style="color: #52c41a; font-weight: 500;">
|
||||
🟢 正常领取
|
||||
</span>
|
||||
<span v-else style="color: #f56c6c; font-weight: 500;">
|
||||
🔴 {{ state.data.active_status_text || '已超期暂停' }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="最近自购时间:">
|
||||
{{ state.data.last_self_order_text || '从未下单' }}
|
||||
</el-form-item>
|
||||
<el-form-item label="进阶团队分润:">
|
||||
<strong>{{ state.data.partner_exclusive_cash || '0.00' }}</strong> 元
|
||||
</el-form-item>
|
||||
<el-form-item label="每日贡献马豆:">
|
||||
{{ state.data.madow || 0 }}(约合 ¥{{ state.data.madow_value || '0.00' }})
|
||||
</el-form-item>
|
||||
<el-form-item label="消费补贴兑换券:">
|
||||
总额 {{ state.data.voucher_total || 0 }} / 已释放 {{ state.data.voucher_released || 0 }} /
|
||||
已使用 {{ state.data.voucher_used || 0 }} / 可用 {{ state.data.voucher_available || 0 }}
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<el-form-item label="进阶身份:">
|
||||
<el-tag type="info" effect="plain" size="small">基础会员 · 品酒师(暂未开通合伙股东)</el-tag>
|
||||
</el-form-item>
|
||||
<div style="font-size: 12px; color: var(--sa-subfont); line-height: 1.6; background: #f8fafc; border: 1px dashed var(--sa-border); border-radius: 4px; padding: 8px 12px; margin: 4px 0 10px;">
|
||||
💡 <strong>「合伙股东」是会员专属进阶身份</strong>:当会员升级创客(V1~V6)或开通酒道馆、区县代理时自动附加合伙股东身份标签,并享有团队分润与平台日结分红。当前会员为基础会员,享有推三返一,<strong>免自购考核</strong>。
|
||||
</div>
|
||||
<el-form-item label="自购考核要求:">
|
||||
<el-tag size="small" type="info">免自购考核 (基础会员/品酒师)</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item label="合伙股东权益:">
|
||||
<span style="color: var(--sa-subfont);">晋升创客或开通馆/县后即可享有团队奖、极差奖与马豆分红</span>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<div style="font-weight: 600; font-size: 13px; color: var(--sa-title); margin: 16px 0 10px; padding-bottom: 4px; border-bottom: 1px solid var(--sa-border);">
|
||||
收益中心资产汇总
|
||||
</div>
|
||||
<el-form-item label="收益中心累计现金:">
|
||||
<span style="font-size: 16px; font-weight: 600; color: #f56c6c;">
|
||||
{{ state.data.income_total_cash || state.data.partner_cash_income || '0.00' }}
|
||||
</span>
|
||||
<span class="ml-1" style="font-size: 12px; color: var(--sa-subfont);">元</span>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" link
|
||||
|
||||
@@ -68,10 +68,15 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="30日活跃" min-width="100">
|
||||
<template #default="scope">
|
||||
<span class="sa-flex sa-col-center">
|
||||
<i class="active-dot" :class="scope.row.active_30d ? 'is-on' : 'is-off'"></i>
|
||||
{{ scope.row.active_30d ? '有自购' : '已暂停' }}
|
||||
</span>
|
||||
<template v-if="scope.row.is_partner">
|
||||
<span class="sa-flex sa-col-center">
|
||||
<i class="active-dot" :class="scope.row.active_30d ? 'is-on' : 'is-off'"></i>
|
||||
{{ scope.row.active_30d ? '有自购' : '已暂停' }}
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="sa-color--info" style="font-size:12px">免考核</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="马豆" min-width="130">
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
function assert(condition, testName, detail = '') {
|
||||
if (condition) {
|
||||
console.log(` [PASS] ${testName}`);
|
||||
passed++;
|
||||
} else {
|
||||
console.error(` [FAIL] ${testName} - ${detail}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("========================================================");
|
||||
console.log(" 合伙股东身份明确化 & 详情页避免误导管理员 规范测试");
|
||||
console.log("========================================================\n");
|
||||
|
||||
// 1. 验证后端 Agent.php 控制器逻辑
|
||||
const agentPhpPath = path.resolve(__dirname, '../application/admin/controller/shopro/commission/Agent.php');
|
||||
assert(fs.existsSync(agentPhpPath), 'Agent.php 文件存在');
|
||||
const agentPhpContent = fs.readFileSync(agentPhpPath, 'utf-8');
|
||||
|
||||
assert(agentPhpContent.includes("IdentityService::build($detail)"), 'Agent.php 中调用 IdentityService::build 识别会员身份');
|
||||
assert(agentPhpContent.includes('免自购考核'), 'Agent.php 中对非合伙股东会员明确赋值免自购考核状态文案');
|
||||
assert(agentPhpContent.includes('$detail->is_partner ='), 'Agent.php 中返回 is_partner 属性供前端区分展示');
|
||||
assert(agentPhpContent.includes('income_total_cash') || agentPhpContent.includes('total_reward_cash'), 'Agent.php 中提供清晰规范的收益中心累计现金字段');
|
||||
|
||||
// 2. 验证前端 detail.html 视图模板
|
||||
const agentDetailPath = path.resolve(__dirname, '../application/admin/view/shopro/commission/agent/detail.html');
|
||||
assert(fs.existsSync(agentDetailPath), 'agent/detail.html 文件存在');
|
||||
const agentDetailContent = fs.readFileSync(agentDetailPath, 'utf-8');
|
||||
|
||||
assert(!agentDetailContent.includes('已切换为合伙股东固定金额'), 'detail.html 移除误导性文案“已切换为合伙股东固定金额”');
|
||||
assert(agentDetailContent.includes('已切换为收益中心固定金额规则'), 'detail.html 规范为“已切换为收益中心固定金额规则”');
|
||||
assert(agentDetailContent.includes('state.data.is_partner'), 'detail.html 包含合伙股东进阶身份的分支条件判断 (is_partner)');
|
||||
assert(agentDetailContent.includes('免自购考核'), 'detail.html 包含对非合伙股东免自购考核的清晰呈现');
|
||||
assert(!agentDetailContent.includes('<el-form-item label="累计合伙股东现金:">'), 'detail.html 不再将所有会员现金收益笼统称为“累计合伙股东现金”');
|
||||
assert(agentDetailContent.includes('收益中心累计现金'), 'detail.html 采用规范的“收益中心累计现金”标签');
|
||||
|
||||
// 3. 验证列表页 index.html 视图模板
|
||||
const agentIndexPath = path.resolve(__dirname, '../application/admin/view/shopro/commission/agent/index.html');
|
||||
assert(fs.existsSync(agentIndexPath), 'agent/index.html 文件存在');
|
||||
const agentIndexContent = fs.readFileSync(agentIndexPath, 'utf-8');
|
||||
|
||||
assert(agentIndexContent.includes('is_partner') && agentIndexContent.includes('免考核'), 'agent/index.html 30日活跃列对非合伙股东显示免考核而非误报已暂停');
|
||||
|
||||
console.log("\n--------------------------------------------------------");
|
||||
console.log(`测试执行结果: 通过 ${passed} 项, 失败 ${failed} 项`);
|
||||
console.log("========================================================");
|
||||
|
||||
process.exit(failed === 0 ? 0 : 1);
|
||||
在新工单中引用
屏蔽一个用户