/** * 团队作战中心全功能补齐与手机号脱敏专项验证测试 * 覆盖范围: * 1. 服务端:Agent.php 手机号脱敏与收益业绩统计 * 2. 服务端:Index.php 收益明细 buyer_id 穿透过滤与 buyer_info * 3. 移动端:team.vue 专属邀请码、团队收益看板、卡片脱敏、一键拨打/复制/查收益 * 4. 移动端:rewards.vue 穿透筛选提示、参数捕获与 API 传参 * 5. 移动端:index.vue 收益中心首页团队快捷入口看板 */ const fs = require('fs'); const path = require('path'); let passCount = 0; let failCount = 0; function assert(condition, testName, detail = '') { if (condition) { console.log(` \x1b[32m[PASS]\x1b[0m ${testName}`); passCount++; } else { console.error(` \x1b[31m[FAIL]\x1b[0m ${testName} - ${detail}`); failCount++; } } console.log('========================================================'); console.log(' 团队作战中心全功能闭环与手机号脱敏专项验证测试'); console.log('========================================================\n'); // 1. 服务端 Agent.php 校验 const agentPhpPath = path.resolve(__dirname, '../addons/shopro/controller/commission/Agent.php'); assert(fs.existsSync(agentPhpPath), 'Agent.php 控制器存在'); if (fs.existsSync(agentPhpPath)) { const content = fs.readFileSync(agentPhpPath, 'utf-8'); assert(content.includes("use app\\admin\\model\\shopro\\partner\\Reward as RewardModel;"), 'Agent.php 引入了 RewardModel'); assert(content.includes('$user->mobile = $maskedMobile;'), 'Agent.php 覆写了 $user->mobile 为脱敏号码'); assert(content.includes('$user->mobile_hide = $maskedMobile;'), 'Agent.php 提供了 $user->mobile_hide 字段'); assert(content.includes('$user->mobile_raw = $rawMobile;'), 'Agent.php 保留了 $user->mobile_raw 供客户端外呼'); assert(content.includes('$user->parentUser->mobile = $pMasked;'), 'Agent.php 对所属间推推荐人手机号执行了脱敏'); assert(content.includes("'team_total_consume'"), 'getTeamHealthSummary 返回团队自购总业绩'); assert(content.includes("'team_total_reward'"), 'getTeamHealthSummary 返回团队贡献总收益'); assert(content.includes("'team_month_reward'"), 'getTeamHealthSummary 返回本月团队贡献收益'); assert(content.includes("'team_order_count'"), 'getTeamHealthSummary 返回团队总订单数'); } // 2. 服务端 partner/Index.php 校验 const partnerIndexPhpPath = path.resolve(__dirname, '../addons/shopro/controller/partner/Index.php'); assert(fs.existsSync(partnerIndexPhpPath), 'partner/Index.php 控制器存在'); if (fs.existsSync(partnerIndexPhpPath)) { const content = fs.readFileSync(partnerIndexPhpPath, 'utf-8'); assert(content.includes("intval($this->request->param('buyer_id', 0))"), 'rewards 接口接收 buyer_id 参数'); assert(content.includes("$query->where('buyer_id', $buyerId);"), 'rewards 接口支持按 buyer_id 过滤明细'); assert(content.includes("'buyer_info' => $buyerInfo"), 'rewards 接口返回穿透成员信息 buyer_info'); } // 3. 移动端 team.vue 校验 const teamVuePath = path.resolve(__dirname, '../uniapp/pages/commission/team.vue'); assert(fs.existsSync(teamVuePath), 'team.vue 页面文件存在'); if (fs.existsSync(teamVuePath)) { const content = fs.readFileSync(teamVuePath, 'utf-8'); // 专属邀请码 assert(content.includes('专属邀请码:'), 'team.vue 包含专属邀请码标签'); assert(content.includes('HL{{ userInfo.id ||'), 'team.vue 包含 HL + 用户ID 的邀请码渲染'); assert(content.includes('onCopyInviteCode'), 'team.vue 包含 onCopyInviteCode 复制方法'); assert(content.includes('onCopyShareLink'), 'team.vue 包含 onCopyShareLink 复制链接方法'); // 团队收益看板 assert(content.includes('team-wealth-box'), 'team.vue 包含 team-wealth-box 团队收益看板'); assert(content.includes('state.summary.team_total_reward'), 'team.vue 绑定团队贡献总收益'); assert(content.includes('state.summary.team_total_consume'), 'team.vue 绑定团队自购总业绩'); assert(content.includes('state.summary.team_month_reward'), 'team.vue 绑定本月贡献收益'); assert(content.includes('state.summary.team_order_count'), 'team.vue 绑定团队订单数'); // 手机号脱敏展示与操作 assert(content.includes('item.mobile_hide'), 'team.vue 列表卡片展示脱敏手机号'); assert(content.includes('state.currentMember.mobile_hide'), 'team.vue 抽屉详情展示脱敏手机号'); assert(content.includes('btn-reward'), 'team.vue 列表卡片提供直达收益流水按钮'); assert(content.includes('onJumpRewards(item.id, item.nickname)'), 'team.vue 卡片支持一键穿透查收益流水'); assert(content.includes('onJumpRewards(state.currentMember.id, state.currentMember.nickname)'), 'team.vue 抽屉支持一键穿透查收益流水'); } // 4. 移动端 rewards.vue 校验 const rewardsVuePath = path.resolve(__dirname, '../uniapp/pages/commission/rewards.vue'); assert(fs.existsSync(rewardsVuePath), 'rewards.vue 页面文件存在'); if (fs.existsSync(rewardsVuePath)) { const content = fs.readFileSync(rewardsVuePath, 'utf-8'); assert(content.includes('buyer-filter-bar'), 'rewards.vue 包含 buyer-filter-bar 穿透筛选提示条'); assert(content.includes('onClearBuyerFilter'), 'rewards.vue 包含 onClearBuyerFilter 清除筛选方法'); assert(content.includes('options?.buyer_id'), 'rewards.vue onLoad 中捕获 buyer_id 参数'); assert(content.includes('params.buyer_id = state.buyerId;'), 'rewards.vue 请求 API 时传递 buyer_id'); } // 5. 移动端 index.vue 校验 const indexVuePath = path.resolve(__dirname, '../uniapp/pages/commission/index.vue'); assert(fs.existsSync(indexVuePath), 'index.vue 页面文件存在'); if (fs.existsSync(indexVuePath)) { const content = fs.readFileSync(indexVuePath, 'utf-8'); assert(content.includes('team-quick-card'), 'index.vue 包含 team-quick-card 快捷看板'); assert(content.includes('团队作战中心'), 'index.vue 明确展示团队作战中心标题'); assert(content.includes('/pages/commission/team'), 'index.vue 点击直达 /pages/commission/team'); } // 6. 算法测试:脱敏与邀请码逻辑 const testMobile = "13912345678"; const maskedMobile = testMobile.replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2'); assert(maskedMobile === "139****5678", `手机号脱敏算法验证正常: ${maskedMobile}`); const testUserId = 2048; const inviteCode = `HL${testUserId}`; assert(inviteCode === "HL2048", `邀请码生成格式符合规范: ${inviteCode}`); console.log('\n--------------------------------------------------------'); console.log(`测试结果统计: 通过 ${passCount} 项, 失败 ${failCount} 项`); console.log('========================================================'); process.exit(failCount === 0 ? 0 : 1);