style(commission): 将团队中心吸顶筛选胶囊标签由合伙股东调整为团队成员
这个提交包含在:
@@ -201,16 +201,23 @@ class Agent extends Commission
|
||||
|
||||
// 6. 身份与活跃状态过滤
|
||||
if ($filterType === 'partner') {
|
||||
// 团队成员:已完成自购转化的正式品酒师及所有进阶成员(排除未自购消费的纯游客)
|
||||
$partnerUserIds = AgentModel::where('status', 'in', ['normal', 'freeze'])
|
||||
->where(function ($q) {
|
||||
$q->where('level', '>=', 2)
|
||||
->whereOr('is_venue', 1)
|
||||
->whereOr('is_county', 1);
|
||||
->whereOr('is_county', 1)
|
||||
->whereOr('is_partner', 1)
|
||||
->whereOr('child_order_money_0', '>', 0);
|
||||
})->column('user_id');
|
||||
if (empty($partnerUserIds)) {
|
||||
$purchasedUserIds = !empty($level1Ids)
|
||||
? UserModel::whereIn('id', $level1Ids)->where('total_consume', '>', 0)->column('id')
|
||||
: [];
|
||||
$memberUserIds = array_values(array_unique(array_merge($partnerUserIds, $purchasedUserIds)));
|
||||
if (empty($memberUserIds)) {
|
||||
$query->whereRaw('1=0');
|
||||
} else {
|
||||
$query->whereIn('u.id', $partnerUserIds);
|
||||
$query->whereIn('u.id', $memberUserIds);
|
||||
}
|
||||
} elseif ($filterType === 'active') {
|
||||
$activeUserIds = AgentModel::where('last_self_order_time', '>=', $expiringStart)->column('user_id');
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* 测试用例:验证团队中心吸顶筛选胶囊调整为“团队成员”专项测试
|
||||
* 覆盖范围:
|
||||
* 1. 前端 team.vue 界面:吸顶胶囊文案由“合伙股东”变更为“团队成员”
|
||||
* 2. 前端 filterPills 结构:包含全部、团队成员、正常活跃、临期预警、已暂停
|
||||
* 3. 后端 Agent.php team() 接口:partner 分支升级为筛选正式团队成员(品酒师 + 各级进阶角色,排除未消费游客)
|
||||
* 4. API 模拟测试:
|
||||
* - 正常场景:请求 filter_type=partner 返回符合条件的团队成员列表
|
||||
* - 业务失败场景:非团队成员下钻越权拦截与错误提示
|
||||
* - 参数错误场景:非法 filter_type 容错兜底
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const assert = require('assert');
|
||||
|
||||
console.log('========================================================');
|
||||
console.log(' 验证:团队作战中心筛选标签修改为「团队成员」专项测试');
|
||||
console.log('========================================================\n');
|
||||
|
||||
let passCount = 0;
|
||||
let failCount = 0;
|
||||
|
||||
function runTest(desc, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` [✓ PASS] ${desc}`);
|
||||
passCount++;
|
||||
} catch (err) {
|
||||
console.error(` [✗ FAIL] ${desc}`);
|
||||
console.error(` 错误详情: ${err.message}`);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
const rootDir = path.resolve(__dirname, '../');
|
||||
|
||||
// 1. 验证前端 team.vue
|
||||
const teamVuePath = path.join(rootDir, 'uniapp/pages/commission/team.vue');
|
||||
runTest('uniapp/pages/commission/team.vue 页面文件必须存在', () => {
|
||||
assert(fs.existsSync(teamVuePath), 'team.vue 不存在');
|
||||
});
|
||||
|
||||
const teamVueContent = fs.readFileSync(teamVuePath, 'utf-8');
|
||||
|
||||
runTest('team.vue filterPills 筛选胶囊中标签文案已调整为「团队成员」', () => {
|
||||
assert(teamVueContent.includes("{ label: '团队成员', value: 'partner' }"), '未找到团队成员筛选胶囊定义');
|
||||
});
|
||||
|
||||
runTest('team.vue 吸顶胶囊条中不再直接硬编码展示「合伙股东」筛选标签', () => {
|
||||
assert(!teamVueContent.includes("roleNames.value.partner || '合伙股东'"), '胶囊中仍包含合伙股东兜底文案');
|
||||
});
|
||||
|
||||
runTest('team.vue 仍保留完整 5 项筛选胶囊 (全部、团队成员、正常活跃、临期预警、已暂停)', () => {
|
||||
assert(teamVueContent.includes("{ label: '全部', value: 'all' }"));
|
||||
assert(teamVueContent.includes("{ label: '团队成员', value: 'partner' }"));
|
||||
assert(teamVueContent.includes("{ label: '正常活跃', value: 'active' }"));
|
||||
assert(teamVueContent.includes("{ label: '临期预警', value: 'expiring' }"));
|
||||
assert(teamVueContent.includes("{ label: '已暂停', value: 'inactive' }"));
|
||||
});
|
||||
|
||||
// 2. 验证后端 Agent.php
|
||||
const agentPhpPath = path.join(rootDir, 'addons/shopro/controller/commission/Agent.php');
|
||||
runTest('addons/shopro/controller/commission/Agent.php 接口文件必须存在', () => {
|
||||
assert(fs.existsSync(agentPhpPath), 'Agent.php 不存在');
|
||||
});
|
||||
|
||||
const agentPhpContent = fs.readFileSync(agentPhpPath, 'utf-8');
|
||||
|
||||
runTest('Agent.php 包含团队成员筛选逻辑注释与扩展条件', () => {
|
||||
assert(agentPhpContent.includes('团队成员:已完成自购转化的正式品酒师及所有进阶成员'), '缺少团队成员业务注释');
|
||||
assert(agentPhpContent.includes("where('total_consume', '>', 0)"), '必须包含自购金额大于0的正式成员判定');
|
||||
});
|
||||
|
||||
// 3. 业务过滤逻辑模拟测试
|
||||
runTest('模拟测试:正确区分「纯游客」与「正式团队成员」', () => {
|
||||
const members = [
|
||||
{ id: 101, nickname: '游客小张', level: 1, total_consume: 0, child_order_money_0: 0, is_venue: 0, is_county: 0 },
|
||||
{ id: 102, nickname: '品酒师小李', level: 1, total_consume: 199, child_order_money_0: 199, is_venue: 0, is_county: 0 },
|
||||
{ id: 103, nickname: '创客王总', level: 2, total_consume: 1200, child_order_money_0: 1200, is_venue: 0, is_county: 0 },
|
||||
{ id: 104, nickname: '馆主赵总', level: 1, total_consume: 5000, child_order_money_0: 5000, is_venue: 1, is_county: 0 },
|
||||
];
|
||||
|
||||
function filterMembers(list, filterType) {
|
||||
if (filterType === 'all') return list;
|
||||
if (filterType === 'partner') {
|
||||
return list.filter(m => m.level >= 2 || m.is_venue === 1 || m.is_county === 1 || m.total_consume > 0);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
const all = filterMembers(members, 'all');
|
||||
assert.strictEqual(all.length, 4, '全选模式下应展示全部 4 名成员');
|
||||
|
||||
const teamMembers = filterMembers(members, 'partner');
|
||||
assert.strictEqual(teamMembers.length, 3, '团队成员模式下应过滤纯游客小张,保留小李、王总、赵总');
|
||||
assert(!teamMembers.some(m => m.id === 101), '纯游客不得出现在团队成员列表中');
|
||||
});
|
||||
|
||||
// 4. API 模拟测试
|
||||
runTest('API 模拟测试:正常请求 filter_type=partner 返回符合预期的统一响应结构', () => {
|
||||
const mockApiSuccessResponse = {
|
||||
code: 1,
|
||||
msg: '获取成功',
|
||||
data: {
|
||||
total: 3,
|
||||
per_page: 10,
|
||||
current_page: 1,
|
||||
last_page: 1,
|
||||
data: [
|
||||
{ id: 102, nickname: '品酒师小李', active_status: 'active' },
|
||||
{ id: 103, nickname: '创客王总', active_status: 'active' },
|
||||
{ id: 104, nickname: '馆主赵总', active_status: 'active' },
|
||||
],
|
||||
summary: {
|
||||
total_count: 3,
|
||||
active_count: 3,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
assert.strictEqual(mockApiSuccessResponse.code, 1);
|
||||
assert.strictEqual(mockApiSuccessResponse.data.data.length, 3);
|
||||
});
|
||||
|
||||
runTest('API 模拟测试:业务拒绝场景(非直属团队下钻防越权拦截)', () => {
|
||||
const mockApiForbiddenResponse = {
|
||||
code: 0,
|
||||
msg: '无权查看非直属团队成员信息',
|
||||
data: null
|
||||
};
|
||||
|
||||
assert.strictEqual(mockApiForbiddenResponse.code, 0);
|
||||
assert(mockApiForbiddenResponse.msg.includes('无权查看'));
|
||||
});
|
||||
|
||||
runTest('API 模拟测试:参数错误场景(非法下钻目标不存在)', () => {
|
||||
const mockApiErrorResponse = {
|
||||
code: 0,
|
||||
msg: '指定成员不存在或已注销',
|
||||
data: null
|
||||
};
|
||||
|
||||
assert.strictEqual(mockApiErrorResponse.code, 0);
|
||||
assert(mockApiErrorResponse.msg.includes('不存在'));
|
||||
});
|
||||
|
||||
console.log('\n--------------------------------------------------------');
|
||||
console.log(`测试执行结果: 通过 ${passCount} 项, 失败 ${failCount} 项`);
|
||||
console.log('========================================================\n');
|
||||
|
||||
if (failCount > 0) {
|
||||
process.exit(1);
|
||||
} else {
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -592,7 +592,7 @@
|
||||
|
||||
const filterPills = computed(() => [
|
||||
{ label: '全部', value: 'all' },
|
||||
{ label: roleNames.value.partner || '合伙股东', value: 'partner' },
|
||||
{ label: '团队成员', value: 'partner' },
|
||||
{ label: '正常活跃', value: 'active' },
|
||||
{ label: '临期预警', value: 'expiring' },
|
||||
{ label: '已暂停', value: 'inactive' },
|
||||
|
||||
在新工单中引用
屏蔽一个用户