feat(dmepa): 全局统一规范DMEPA命名并优化游客与品酒师身份显示

这个提交包含在:
chen
2026-09-19 15:41:39 +08:00
父节点 3868becc63
当前提交 fd8c1d09cc
共有 65 个文件被更改,包括 1068 次插入 和 390 次删除

查看文件

@@ -104,7 +104,7 @@ class DashboardWealthTest
'total() 返回完整的 wealth_data 资金盘宏观指标');
$this->assert(!empty($madow) && isset($madow['total_madow'], $madow['current_rate'], $madow['rate_per_wan'], $madow['total_madow_cny']),
'total() 返回完整的 madow_data 马豆资产与折算人民币指标');
'total() 返回完整的 madow_data dmepa资产与折算人民币指标');
$this->assert(!empty($voucher) && isset($voucher['total_voucher'], $voucher['released_voucher'], $voucher['pending_voucher']),
'total() 返回完整的 voucher_data 消费兑换券杠杆资金池指标');
@@ -120,7 +120,7 @@ class DashboardWealthTest
}
/**
* 测试 2:每一个角色身份的会员数、马豆数量、分佣数量与马豆价值精算校验
* 测试 2:每一个角色身份的会员数、dmepa数量、分佣数量与dmepa价值精算校验
*/
public function testAllRolesStatisticsIntegrity()
{
@@ -134,7 +134,7 @@ class DashboardWealthTest
$this->assert(isset($roleStats[$roleKey]), "矩阵包含角色身份: {$roleKey}");
$item = $roleStats[$roleKey] ?? [];
$this->assert(isset($item['user_count'], $item['madow_total'], $item['madow_cny'], $item['reward_amount'], $item['reward_count'], $item['active_rate']),
"角色 {$roleKey} 完整包含会员数、持豆量、马豆市值、分佣总额、分佣笔数与活跃率");
"角色 {$roleKey} 完整包含会员数、持豆量、dmepa市值、分佣总额、分佣笔数与活跃率");
}
// 普通会员无需自购考核标识

查看文件

@@ -67,7 +67,7 @@ class MultiAssetRechargeTest
public function run()
{
echo "========================================================\n";
echo " 会员多资产(余额/积分/马豆/兑换券)充值业务测试 \n";
echo " 会员多资产(余额/积分/dmepa/兑换券)充值业务测试 \n";
echo "========================================================\n\n";
$this->setUp();
@@ -291,41 +291,41 @@ class MultiAssetRechargeTest
public function testMadowRechargeAndDeduct()
{
// 充值 5.12345678 颗马豆
// 充值 5.12345678 颗dmepa
$res1 = $this->invokeRecharge([
'id' => $this->testUserId,
'type' => 'madow',
'amount' => '5.12345678',
'memo' => '充值马豆',
'memo' => '充值dmepa',
]);
$agent1 = AgentModel::where('user_id', $this->testUserId)->find();
$this->assert($res1['code'] === 1 && $agent1->madow == '15.12345678', '马豆8位高精度充值到账', "当前马豆: {$agent1->madow}");
$this->assert($res1['code'] === 1 && $agent1->madow == '15.12345678', 'dmepa8位高精度充值到账', "当前dmepa: {$agent1->madow}");
// 扣除 3.00000000 颗马豆
// 扣除 3.00000000 颗dmepa
$res2 = $this->invokeRecharge([
'id' => $this->testUserId,
'type' => 'madow',
'amount' => '-3.00000000',
'memo' => '扣除马豆',
'memo' => '扣除dmepa',
]);
$agent2 = AgentModel::where('user_id', $this->testUserId)->find();
$this->assert($res2['code'] === 1 && $agent2->madow == '12.12345678', '马豆高精度扣除成功', "当前马豆: {$agent2->madow}");
$this->assert($res2['code'] === 1 && $agent2->madow == '12.12345678', 'dmepa高精度扣除成功', "当前dmepa: {$agent2->madow}");
// 验证流水表记录
$log = RewardModel::where('user_id', $this->testUserId)->where('reward_type', 'admin_madow')->order('id', 'desc')->find();
$this->assert($log && $log->status == 1, '马豆充值写入收益流水明细');
$this->assert($log && $log->status == 1, 'dmepa充值写入收益流水明细');
}
public function testMadowOverdrawn()
{
// 尝试超额扣除马豆
// 尝试超额扣除dmepa
$res = $this->invokeRecharge([
'id' => $this->testUserId,
'type' => 'madow',
'amount' => '-99.00000000',
'memo' => '超额扣除马豆',
'memo' => '超额扣除dmepa',
]);
$this->assert($res['code'] === 0 && strpos($res['msg'], '不足') !== false, '马豆持仓不足拦截超扣', $res['msg'] ?? '');
$this->assert($res['code'] === 0 && strpos($res['msg'], '不足') !== false, 'dmepa持仓不足拦截超扣', $res['msg'] ?? '');
}
public function testVoucherRechargeAndDeduct()

查看文件

@@ -71,15 +71,15 @@ function doRecharge($type, $amount, $memo)
echo "=== 开始对会员 10 (尚善) 真实链路充值验证 ===\n";
// 1. 马豆充值 +8 颗
$r1 = doRecharge('madow', '8.00000000', '真实环境测试充值马豆');
// 1. dmepa充值 +8 颗
$r1 = doRecharge('madow', '8.00000000', '真实环境测试充值dmepa');
$agent1 = Db::name('shopro_commission_agent')->where('user_id', 10)->find();
echo "1. 充值马豆+8结果: " . json_encode($r1, JSON_UNESCAPED_UNICODE) . " 当前持仓: " . ($agent1['madow'] ?? '0') . "\n";
echo "1. 充值dmepa+8结果: " . json_encode($r1, JSON_UNESCAPED_UNICODE) . " 当前持仓: " . ($agent1['madow'] ?? '0') . "\n";
// 2. 马豆扣除 -8 颗
$r2 = doRecharge('madow', '-8.00000000', '真实环境测试扣除马豆');
// 2. dmepa扣除 -8 颗
$r2 = doRecharge('madow', '-8.00000000', '真实环境测试扣除dmepa');
$agent2 = Db::name('shopro_commission_agent')->where('user_id', 10)->find();
echo "2. 扣除马豆-8结果: " . json_encode($r2, JSON_UNESCAPED_UNICODE) . " 恢复持仓: " . ($agent2['madow'] ?? '0') . "\n";
echo "2. 扣除dmepa-8结果: " . json_encode($r2, JSON_UNESCAPED_UNICODE) . " 恢复持仓: " . ($agent2['madow'] ?? '0') . "\n";
// 3. 兑换券充值 +20 元
$r3 = doRecharge('voucher', '20.00', '真实环境测试充值兑换券');
@@ -105,7 +105,7 @@ echo "6. 扣除积分-10结果: " . json_encode($r6, JSON_UNESCAPED_UNICODE) . "
$rewards = Db::name('shopro_partner_reward')->where('user_id', 10)->whereIn('reward_type', ['admin_madow', 'admin_voucher'])->order('id', 'desc')->limit(4)->select();
echo "\n=== 会员 10 收益流水落库检查 (共 " . count($rewards) . " 笔充值/扣除流水) ===\n";
foreach ($rewards as $idx => $item) {
echo " [" . ($idx + 1) . "] 类型: {$item['reward_type']} | 金额: {$item['amount']} | 马豆: {$item['madow']} | 备注: " . $item['ext'] . "\n";
echo " [" . ($idx + 1) . "] 类型: {$item['reward_type']} | 金额: {$item['amount']} | dmepa: {$item['madow']} | 备注: " . $item['ext'] . "\n";
}
echo "\n=== 会员 10 真实测试全流程 100% 成功完毕 ===\n";

查看文件

@@ -32,7 +32,7 @@ assert(rewardModelContent.includes('function getWalletTypeTextAttr'), '包含 ge
assert(rewardModelContent.includes("PartnerConfig::rewardWalletType()"), '现金分润缺省联动 PartnerConfig::rewardWalletType()');
assert(rewardModelContent.includes("'商城余额'"), 'wallet_type_text 映射包含 商城余额');
assert(rewardModelContent.includes("'佣金钱包'"), 'wallet_type_text 映射包含 佣金钱包');
assert(rewardModelContent.includes("'分红马豆'"), 'wallet_type_text 映射包含 分红马豆');
assert(rewardModelContent.includes("'分红dmepa'"), 'wallet_type_text 映射包含 分红dmepa');
// --- 2. Order.php 订单分成构建与入账账户对齐验证 ---
console.log('\n--- 2. Order.php 订单分成构建与入账账户对齐验证 ---');

查看文件

@@ -0,0 +1,94 @@
const fs = require('fs');
const path = require('path');
const assert = require('assert');
console.log('========================================================');
console.log(' 现金收益明细隔离虚拟资产与清除0元记录 专项测试');
console.log('========================================================\n');
const rootDir = path.resolve(__dirname, '../../');
// 1. 验证 partner/Index.php
const indexCtrlPath = path.join(rootDir, 'addons/shopro/controller/partner/Index.php');
assert(fs.existsSync(indexCtrlPath), 'addons/shopro/controller/partner/Index.php 文件存在');
const indexContent = fs.readFileSync(indexCtrlPath, 'utf8');
// 验证现金白名单与排他性
assert(indexContent.includes('$cashTypes = array_merge(PartnerConfig::instantTypes(), [\'venue_share\', \'county_share\'])'), 'rewards 接口定义了专属现金类型白名单');
assert(!indexContent.includes("'share' => ['venue_share', 'county_share', 'madow_share']"), '加权奖 Tab 中已彻底移除 madow_share (马豆分红)');
assert(indexContent.includes("'share' => ['venue_share', 'county_share']"), '加权奖 Tab 仅保留纯现金均分奖励');
assert(indexContent.includes("$query->whereIn('reward_type', $cashTypes)"), '全部 Tab 默认仅查询纯现金奖励白名单');
// 验证 0 元记录过滤
assert(indexContent.includes("$q->where('amount', '>', 0)"), 'rewards 强制要求已入账记录金额大于0');
assert(indexContent.includes("->whereOr('status', -1)"), '保留未达成的拦截记录');
// 验证来源文本规范化
assert(indexContent.includes("'source_text' => $sourceText"), 'rewards 接口返回结构化 source_text');
assert(indexContent.includes('全网加权日结分红'), '日结均分奖励明确来源文本为全网加权日结分红');
console.log(' [✓ PASS] 服务端 partner/Index.php 现金类型白名单与0元过滤已就绪');
// 2. 验证前端 rewards.vue
const rewardsVuePath = path.join(rootDir, 'uniapp/pages/commission/rewards.vue');
assert(fs.existsSync(rewardsVuePath), 'rewards.vue 页面文件存在');
const rewardsContent = fs.readFileSync(rewardsVuePath, 'utf8');
assert(rewardsContent.includes('item.source_text'), 'rewards.vue 优先展示 source_text');
assert(!rewardsContent.includes("来自 {{ item.buyer_nickname || '好友' }}"), 'rewards.vue 彻底移除生硬的“来自 好友”兜底');
assert(rewardsContent.includes('state.current.source_text'), 'rewards.vue 详情弹窗优先展示 source_text');
console.log(' [✓ PASS] 前端 rewards.vue 来源展示优化与兜底修复已就绪');
// 3. 模拟逻辑单元测试
const instantTypes = ['push3', 'maker_diff', 'maker_peer', 'maker_overtaken', 'team_venue', 'team_county', 'venue_peer', 'county_peer'];
const cashTypes = [...instantTypes, 'venue_share', 'county_share'];
const mockRows = [
{ id: 1, reward_type: 'push3', amount: '398.00', madow: '0', status: 1, buyer: { nickname: '张三' } },
{ id: 2, reward_type: 'madow_share', amount: '0.00', madow: '120.00', status: 1, buyer: null }, // 每日贡献马豆
{ id: 3, reward_type: 'venue_share', amount: '50.00', madow: '0', status: 1, buyer: null }, // 酒道馆均分现金
{ id: 4, reward_type: 'voucher_release', amount: '0.00', madow: '0', status: 1, buyer: null }, // 券释放
{ id: 5, reward_type: 'push3', amount: '0.00', madow: '0', status: -1, skip_reason: 'inactive_30d', buyer: { nickname: '李四' } }, // 未自购拦截
{ id: 6, reward_type: 'push3', amount: '0.00', madow: '0', status: 1, buyer: null }, // 异常 0 元已入账记录
];
// 模拟查询逻辑
function filterRewards(tab) {
return mockRows.filter(row => {
// 1. 类型过滤
if (tab === 'all') {
if (!cashTypes.includes(row.reward_type)) return false;
} else if (tab === 'share') {
if (!['venue_share', 'county_share'].includes(row.reward_type)) return false;
} else if (tab === 'push3') {
if (row.reward_type !== 'push3') return false;
}
// 2. 金额过滤: amount > 0 或者 status === -1
const amountNum = parseFloat(row.amount);
if (!(amountNum > 0 || row.status === -1)) {
return false;
}
return true;
});
}
// 验证 全部 Tab
const allList = filterRewards('all');
assert(allList.some(r => r.id === 1), '全部 Tab 应包含正常推三返一');
assert(allList.some(r => r.id === 3), '全部 Tab 应包含酒道馆日结现金均分');
assert(allList.some(r => r.id === 5), '全部 Tab 应包含被拦截的跳过记录');
assert(!allList.some(r => r.id === 2), '全部 Tab 绝不能包含每日贡献马豆 (madow_share)');
assert(!allList.some(r => r.id === 4), '全部 Tab 绝不能包含券释放 (voucher_release)');
assert(!allList.some(r => r.id === 6), '全部 Tab 绝不能包含已入账但金额为0的废弃记录');
console.log(' [✓ PASS] 全部 Tab 纯现金过滤验证成功 (过滤掉所有 madow_share 与 0 元记录)');
// 验证 加权奖 Tab
const shareList = filterRewards('share');
assert(shareList.some(r => r.id === 3), '加权奖 Tab 包含酒道馆日结现金均分');
assert(!shareList.some(r => r.id === 2), '加权奖 Tab 彻底排除马豆分红');
console.log(' [✓ PASS] 加权奖 Tab 纯现金过滤验证成功');
console.log('\n========================================================');
console.log(' 全部修复项与业务隔离测试 100% 通过!');
console.log('========================================================');

查看文件

@@ -0,0 +1,156 @@
const fs = require('fs');
const path = require('path');
const assert = require('assert');
console.log('========================================================');
console.log(' 未买单用户显示“游客”、购买1单后显示“品酒师”专项测试');
console.log('========================================================\n');
const rootDir = path.resolve(__dirname, '../../');
// 1. 验证 IdentityService.php
const identityServicePath = path.join(rootDir, 'addons/shopro/service/partner/IdentityService.php');
assert(fs.existsSync(identityServicePath), 'IdentityService.php 文件存在');
const idServiceContent = fs.readFileSync(identityServicePath, 'utf8');
assert(idServiceContent.includes('function hasPurchased('), 'IdentityService 包含 hasPurchased 方法');
assert(idServiceContent.includes("'total_consume'"), 'hasPurchased 优先使用 total_consume 快速命中');
assert(idServiceContent.includes("'shopro_order'"), 'hasPurchased 校验有效订单');
assert(idServiceContent.includes("'apply_refund_status', '<>', 2"), 'hasPurchased 排除全退订单');
assert(idServiceContent.includes("identities[] = 'guest'"), '未买单时 identities 包含 guest');
assert(idServiceContent.includes("identities[] = 'taster'"), '已买单时 identities 包含 taster');
assert(idServiceContent.includes("$parts[] = $hasPurchased ? $tasterName : '游客'"), '无进阶身份时按买单状态显示品酒师或游客');
assert(idServiceContent.includes("'is_guest' => $isGuest"), 'IdentityService::build 返回 is_guest');
assert(idServiceContent.includes("'has_purchased' => $hasPurchased"), 'IdentityService::build 返回 has_purchased');
assert(idServiceContent.includes("'is_guest' => $full['is_guest']"), 'IdentityService::attachProfile 返回 is_guest');
console.log(' [✓ PASS] IdentityService.php 服务层买单检测与身份映射逻辑已就绪');
// 2. 验证 Agent.php 模型
const agentModelPath = path.join(rootDir, 'application/admin/model/shopro/commission/Agent.php');
assert(fs.existsSync(agentModelPath), 'Agent.php 模型文件存在');
const agentModelContent = fs.readFileSync(agentModelPath, 'utf8');
assert(agentModelContent.includes("'is_guest'"), 'Agent 模型 $append 数组包含 is_guest');
assert(agentModelContent.includes("'has_purchased'"), 'Agent 模型 $append 数组包含 has_purchased');
assert(agentModelContent.includes('function getIsGuestAttr'), 'Agent 模型包含 getIsGuestAttr 获取器');
assert(agentModelContent.includes('function getHasPurchasedAttr'), 'Agent 模型包含 getHasPurchasedAttr 获取器');
console.log(' [✓ PASS] Agent.php 模型 append 属性与获取器已就绪');
// 3. 验证 User.php 控制器 (API 与 Admin)
const apiUserCtrlPath = path.join(rootDir, 'addons/shopro/controller/user/User.php');
const apiUserContent = fs.readFileSync(apiUserCtrlPath, 'utf8');
assert(apiUserContent.includes('attachProfile($agent, null, $user)'), 'api user/profile 传递 user 对象至 attachProfile');
const adminUserCtrlPath = path.join(rootDir, 'application/admin/controller/shopro/user/User.php');
const adminUserContent = fs.readFileSync(adminUserCtrlPath, 'utf8');
assert(adminUserContent.includes('attachProfile($agent, $rate, $user)'), '后台 User::index 传递 user 对象至 attachProfile');
assert(adminUserContent.includes('attachProfile($agent, null, $user)'), '后台 User::detail 传递 user 对象至 attachProfile');
console.log(' [✓ PASS] 后端控制器已打通 user 对象与 profile 联动');
// 4. 验证 Uniapp 前端组件
const userCardPath = path.join(rootDir, 'uniapp/sheep/components/s-user-card/s-user-card.vue');
assert(fs.existsSync(userCardPath), 's-user-card.vue 组件存在');
const userCardContent = fs.readFileSync(userCardPath, 'utf8');
assert(userCardContent.includes('v-if="isGuest" class="identity-tag guest-tag">游客</view>'), 's-user-card 包含游客标签模板渲染');
assert(userCardContent.includes('const isGuest = computed('), 's-user-card 包含 isGuest 计算属性');
assert(userCardContent.includes('.guest-tag {'), 's-user-card 包含 guest-tag 视觉样式');
console.log(' [✓ PASS] s-user-card.vue 游客标签与品酒师徽章动态切换已就绪');
const commInfoPath = path.join(rootDir, 'uniapp/pages/commission/components/commission-info.vue');
const commInfoContent = fs.readFileSync(commInfoPath, 'utf8');
assert(commInfoContent.includes('userInfo.value?.identity_text'), 'commission-info.vue 优先使用 identity_text 展示游客/品酒师');
console.log(' [✓ PASS] commission-info.vue 收益中心顶部身份文案已就绪');
const teamPath = path.join(rootDir, 'uniapp/pages/commission/team.vue');
const teamContent = fs.readFileSync(teamPath, 'utf8');
assert(teamContent.includes('item?.agent?.identity_text'), 'team.vue 格式化下级身份优先使用 identity_text');
console.log(' [✓ PASS] team.vue 团队作战中心下级身份显示已就绪');
// 5. 模拟逻辑单元测试
function simulateBuild(agent, user, orders) {
const level = agent ? (agent.level || 0) : 0;
const isVenue = agent ? (agent.is_venue || 0) : 0;
const isCounty = agent ? (agent.is_county || 0) : 0;
const isPartner = agent ? (agent.is_partner || 0) : 0;
let hasPurchased = false;
if (user && parseFloat(user.total_consume || 0) > 0) {
hasPurchased = true;
} else if (orders && orders.length > 0) {
hasPurchased = orders.some(o => ['paid', 'completed'].includes(o.status) && o.apply_refund_status !== 2);
}
const isAdvanced = (level >= 2) || (isVenue === 1) || (isCounty === 1) || (isPartner === 1);
const isGuest = (level === 1 && !hasPurchased && !isAdvanced);
const identities = [];
const parts = [];
if (level === 1) {
if (hasPurchased) {
identities.push('taster');
} else if (!isAdvanced) {
identities.push('guest');
}
} else if (level >= 2) {
identities.push('maker');
parts.push('创客V' + (level - 1));
}
if (isVenue) {
identities.push('venue');
parts.push('酒道馆');
}
if (isCounty) {
identities.push('county');
parts.push('区县');
}
if (isPartner) {
identities.push('partner');
parts.push('合伙股东');
}
if (parts.length === 0 && level === 1) {
parts.push(hasPurchased ? '品酒师' : '游客');
}
return {
identities,
identity_text: parts.join(' · ') || (isGuest ? '游客' : ''),
is_guest: isGuest,
has_purchased: hasPurchased,
};
}
// 模拟场景测试
// 场景 1: 新用户注册,未买单 (0单,0消费)
const res1 = simulateBuild({ level: 1, user_id: 101 }, { id: 101, total_consume: 0 }, []);
assert.strictEqual(res1.identity_text, '游客', '场景1:新用户未买单 identity_text 应为游客');
assert.strictEqual(res1.is_guest, true, '场景1:新用户未买单 is_guest 应为 true');
assert(res1.identities.includes('guest'), '场景1:identities 包含 guest');
assert(!res1.identities.includes('taster'), '场景1:identities 不含 taster');
// 场景 2: 购买 1 单已支付 (消费 1398)
const res2 = simulateBuild({ level: 1, user_id: 102 }, { id: 102, total_consume: 1398 }, [{ status: 'paid', apply_refund_status: 0 }]);
assert.strictEqual(res2.identity_text, '品酒师', '场景2:购买1单后 identity_text 应升级为品酒师');
assert.strictEqual(res2.is_guest, false, '场景2:购买1单后 is_guest 应为 false');
assert(res2.identities.includes('taster'), '场景2:identities 包含 taster');
assert(!res2.identities.includes('guest'), '场景2:identities 不含 guest');
// 场景 3: 购买后发生全单退款完成 (apply_refund_status = 2)
const res3 = simulateBuild({ level: 1, user_id: 103 }, { id: 103, total_consume: 0 }, [{ status: 'paid', apply_refund_status: 2 }]);
assert.strictEqual(res3.identity_text, '游客', '场景3:全额退款后身份应回退为游客');
assert.strictEqual(res3.is_guest, true, '场景3:全额退款后 is_guest 应为 true');
// 场景 4: 进阶创客角色不受影响 (level = 3 创客V2,未下单)
const res4 = simulateBuild({ level: 3, user_id: 104 }, { id: 104, total_consume: 0 }, []);
assert.strictEqual(res4.identity_text, '创客V2', '场景4:进阶创客未买单仍正常展示创客V2');
assert.strictEqual(res4.is_guest, false, '场景4:进阶创客 is_guest 应为 false');
// 场景 5: 进阶酒道馆+区县角色不受影响
const res5 = simulateBuild({ level: 1, is_venue: 1, is_county: 1, user_id: 105 }, { id: 105, total_consume: 0 }, []);
assert.strictEqual(res5.identity_text, '酒道馆 · 区县', '场景5:酒道馆与区县正常展示');
assert.strictEqual(res5.is_guest, false, '场景5:复合身份 is_guest 应为 false');
console.log(' [✓ PASS] 所有业务场景逻辑模拟测试 100% 通过!\n');
console.log('========================================================');
console.log(' 所有专项检查与验证均已完美通过!');
console.log('========================================================');

查看文件

@@ -1,7 +1,7 @@
/**
* tests/partner/verify_madow_switch_spec.js
*
* 验证“我的马豆”页面马币市值指示胶囊后台开关:
* 验证“我的dmepa”页面马币市值指示胶囊后台开关:
* 1. 后端配置读取与默认值向后兼容逻辑验证
* 2. 移动端前台显隐条件判定逻辑(开启、关闭、未配置)验证
* 3. 接口数据契约与统一返回结构校验
@@ -13,7 +13,7 @@ const path = require('path');
const assert = require('assert');
console.log('====================================================');
console.log(' 开始验证:“我的马豆”马币市值指示胶囊后台显隐开关');
console.log(' 开始验证:“我的dmepa”马币市值指示胶囊后台显隐开关');
console.log('====================================================\n');
let passCount = 0;

查看文件

@@ -0,0 +1,174 @@
/**
* tests/partner/verify_remove_mabi_wording_spec.js
*
* 验证“我的dmepa”全链路移除“马币”二字:
* 1. 移动端 madow.vue 指示胶囊文案对齐为「市值 1 dmepa ≈ xx 元」
* 2. 移动端 madow.vue 抽屉弹窗默认文案与数据净化逻辑(不含“马币”)
* 3. 服务端 PartnerConfig::madowRuleTitle 与 madowRules 彻底去除“马币”并具备脱敏自愈能力
* 4. 后台 Config 控制器、后台视图 index.html、config.js 默认值与提示语彻底移除“马币”
*/
const fs = require('fs');
const path = require('path');
const assert = require('assert');
console.log('====================================================');
console.log(' 验证:“我的dmepa”全链路彻底移除「马币」二字');
console.log('====================================================\n');
let passCount = 0;
let failCount = 0;
function runTest(title, fn) {
try {
fn();
console.log(`[PASS] ${title}`);
passCount++;
} catch (err) {
console.error(`[FAIL] ${title}`);
console.error(` 错误原因: ${err.message}`);
failCount++;
}
}
const rootDir = path.resolve(__dirname, '../../');
// 1. 验证 uniapp/pages/commission/madow.vue
runTest('uniapp/pages/commission/madow.vue 胶囊文案由「马币市值」调整为「市值」', () => {
const file = path.join(rootDir, 'uniapp/pages/commission/madow.vue');
assert.ok(fs.existsSync(file), 'madow.vue 存在');
const content = fs.readFileSync(file, 'utf-8');
// 核心指示胶囊
assert.ok(/市值\s*1\s*DMEPA\s*≈/i.test(content), '胶囊必须使用「市值 1 DMEPA ≈」');
assert.ok(!content.includes('马币市值'), '胶囊中严禁出现「马币市值」');
// 顶栏红框文字去除验证
assert.ok(content.includes('生态资产'), '必须保留「生态资产」标签');
assert.ok(!content.includes('持有dmepa总量') && !content.includes('持有DMEPA总量'), '顶栏「持有DMEPA总量」文字已按需求彻底去掉');
// 默认规则弹窗标题
assert.ok(content.includes("ruleTitle: '市值与增值规则'"), '默认弹窗标题必须为「市值与增值规则」');
// 默认规则内容
assert.ok(content.includes("title: '市值动态增值'"), '默认规则条目必须为「市值动态增值」');
// 数据接收净化
assert.ok(content.includes('.replace(/马币/g, \'\')'), 'getMadow 必须对动态规则标题和条目做去除「马币」净化');
});
// 2. 验证 addons/shopro/service/partner/PartnerConfig.php
runTest('addons/shopro/service/partner/PartnerConfig.php 规则方法彻底移除「马币」并脱敏净化', () => {
const file = path.join(rootDir, 'addons/shopro/service/partner/PartnerConfig.php');
assert.ok(fs.existsSync(file), 'PartnerConfig.php 存在');
const content = fs.readFileSync(file, 'utf-8');
assert.ok(content.includes("self::get('partner_madow_rule_title', '市值与增值规则')"), '默认标题为「市值与增值规则」');
assert.ok(content.includes("return str_replace('马币', '', $title);"), '标题输出时执行 str_replace 清除「马币」');
assert.ok(content.includes("'title' => '市值动态增值'"), '默认规则条目包含「市值动态增值」');
assert.ok(content.includes("str_replace('马币', '', $rule['title'])"), '条目输出时执行 str_replace 清除「马币」');
});
// 3. 验证 application/admin/controller/shopro/Config.php
runTest('application/admin/controller/shopro/Config.php 后台自愈默认值去除「马币」', () => {
const file = path.join(rootDir, 'application/admin/controller/shopro/Config.php');
assert.ok(fs.existsSync(file), 'Config.php 存在');
const content = fs.readFileSync(file, 'utf-8');
assert.ok(content.includes("'partner_madow_rule_title' => '市值与增值规则'"), 'ruleDefaults 默认标题去除「马币」');
assert.ok(content.includes("['title' => '市值动态增值'"), 'ruleDefaults 默认条目去除「马币」');
});
// 4. 验证 application/admin/view/shopro/config/index.html
runTest('application/admin/view/shopro/config/index.html 视图提示与占位符去除「马币」', () => {
const file = path.join(rootDir, 'application/admin/view/shopro/config/index.html');
assert.ok(fs.existsSync(file), 'index.html 存在');
const content = fs.readFileSync(file, 'utf-8');
assert.ok(content.includes('市值 1 dmepa ≈ xxxx 元'), 'tip 提示更新为「市值 1 dmepa ≈ xxxx 元」');
assert.ok(content.includes('placeholder="市值与增值规则"'), '输入框占位符更新为「市值与增值规则」');
});
// 5. 验证 public/assets/js/backend/shopro/config.js
runTest('public/assets/js/backend/shopro/config.js 默认值去除「马币」', () => {
const file = path.join(rootDir, 'public/assets/js/backend/shopro/config.js');
assert.ok(fs.existsSync(file), 'config.js 存在');
const content = fs.readFileSync(file, 'utf-8');
assert.ok(content.includes("form.model.partner_madow_rule_title = '市值与增值规则'"), 'config.js 默认标题更新为「市值与增值规则」');
assert.ok(content.includes("{ title: '市值动态增值'"), 'config.js 默认规则条目更新为「市值动态增值」');
});
// 6. API 模拟测试:涵盖正常场景、业务失败场景、参数错误场景
runTest('API 模拟测试:正常请求 partner/madow 接口数据结构与文案校验', () => {
const mockSuccessResponse = {
code: 1,
msg: 'ok',
data: {
madow: '3993.0000',
madow_rate: '0.00000858',
madow_value: '0.03',
show_madow_rate: 1,
rule_title: '市值与增值规则',
rules: [
{ title: 'DMEPA获取与核算', desc: '平台合伙人与创客达成考核或完成订单后,系统将于次日 00:30 起统一参与每日DMEPA加权核算并自动分发。' },
{ title: '市值动态增值', desc: '市值随全平台订单增加而上浮增值。全网完成订单总量越多,DMEPA单价所对应的市值越高,共享生态红利。' },
{ title: '价值说明与权益', desc: '市值仅作为平台数字生态资产价值参考展示,不可直接兑换现金提现。具体权益按平台股东与合伙人章程执行。' }
],
list: []
}
};
assert.strictEqual(mockSuccessResponse.code, 1);
assert.strictEqual(mockSuccessResponse.data.rule_title.includes('马币'), false, '返回的 rule_title 严禁包含马币');
assert.strictEqual(mockSuccessResponse.data.rules[1].title.includes('马币'), false, '返回的 rules[1].title 严禁包含马币');
assert.strictEqual(mockSuccessResponse.data.rules[1].desc.includes('马币'), false, '返回的 rules[1].desc 严禁包含马币');
});
runTest('API 模拟测试:当服务端数据包含历史「马币」文案时,前端净化脱敏逻辑生效', () => {
const dirtyData = {
rule_title: '马币市值与增值规则',
rules: [
{ title: '马币市值动态增值', desc: '马币市值随全平台订单增加而上浮增值。' }
]
};
const sanitizedTitle = String(dirtyData.rule_title).replace(/马币/g, '');
const sanitizedRules = dirtyData.rules.map(item => ({
...item,
title: String(item.title || '').replace(/马币/g, ''),
desc: String(item.desc || '').replace(/马币/g, '')
}));
assert.strictEqual(sanitizedTitle, '市值与增值规则');
assert.strictEqual(sanitizedRules[0].title, '市值动态增值');
assert.strictEqual(sanitizedRules[0].desc, '市值随全平台订单增加而上浮增值。');
});
runTest('API 模拟测试:业务拒绝场景(未登录/无权限)统一响应封装', () => {
const mockAuthFailResponse = {
code: 0,
msg: '请先登录后查看DMEPA资产',
data: null
};
assert.strictEqual(mockAuthFailResponse.code, 0);
assert.strictEqual(typeof mockAuthFailResponse.msg, 'string');
});
runTest('API 模拟测试:参数错误场景(page为非法字符串)边界处理', () => {
const sanitizePage = (inputPage) => {
const p = parseInt(inputPage, 10);
return isNaN(p) || p < 1 ? 1 : p;
};
assert.strictEqual(sanitizePage('invalid'), 1);
assert.strictEqual(sanitizePage('-5'), 1);
assert.strictEqual(sanitizePage('3'), 3);
});
console.log('\n====================================================');
console.log(`测试完成: 成功 ${passCount} 项, 失败 ${failCount} 项`);
console.log('====================================================');
if (failCount > 0) {
process.exit(1);
}

查看文件

@@ -105,7 +105,9 @@ assert(fs.existsSync(moneyVuePath), 'money.vue 文件存在');
if (fs.existsSync(moneyVuePath)) {
const content = fs.readFileSync(moneyVuePath, 'utf-8');
assert(content.includes("name: '收益分成'") && content.includes("value: 'reward'"), 'money.vue 的 tabMaps 增加「收益分成」筛选');
assert(content.includes('收益分成全额入账'), '动态指示胶囊说明收益分成全额入账可直接抵扣');
assert(content.includes('tag-badge') && content.includes('现金资产'), '保留现金资产专属标签');
assert(!content.includes('账户钱包余额'), '已成功移除「账户钱包余额」文案');
assert(!content.includes('线上充值与收益分成全额入账'), '已成功移除「线上充值与收益分成全额入账」胶囊文案');
}
// ----------------------------------------------------

查看文件

@@ -3,7 +3,7 @@ const path = require('path');
const assert = require('assert');
console.log('========================================================');
console.log(' 全网奖池(酒道馆/区县代/马豆/兑换券)1分钟结算规范验证');
console.log(' 全网奖池(酒道馆/区县代/dmepa/兑换券)1分钟结算规范验证');
console.log('========================================================\n');
let passCount = 0;

查看文件

@@ -15,7 +15,7 @@ function assert(condition, testName, detail = '') {
}
console.log("========================================================");
console.log(" 订单/分润数据清理与马豆汇率回归初始值规范自动化测试");
console.log(" 订单/分润数据清理与dmepa汇率回归初始值规范自动化测试");
console.log("========================================================\n");
// 1. 验证控制台清理命令 PartnerClean.php 存在且逻辑完整
@@ -37,15 +37,15 @@ assert(
);
assert(
cleanCmdContent.includes("shopro_partner_rate_log") && cleanCmdContent.includes("shopro_partner_daily_report") && cleanCmdContent.includes("shopro_partner_voucher_plan"),
'涵盖清理马豆汇率大盘快照、日结对账报表与兑换券释放计划'
'涵盖清理dmepa汇率大盘快照、日结对账报表与兑换券释放计划'
);
assert(
cleanCmdContent.includes("`madow` = 0.00000000") && cleanCmdContent.includes("`direct_order_seq` = 0") && cleanCmdContent.includes("`voucher_total` = 0.00"),
'涵盖重置分销商持仓马豆、推三返一序号、兑换券资产归零'
'涵盖重置分销商持仓dmepa、推三返一序号、兑换券资产归零'
);
assert(
cleanCmdContent.includes("cache('partner_madow_current_rate', null)"),
'命令执行后清除马豆汇率缓存 partner_madow_current_rate'
'命令执行后清除dmepa汇率缓存 partner_madow_current_rate'
);
assert(
cleanCmdContent.includes("Db::startTrans()") && cleanCmdContent.includes("Db::commit()"),
@@ -76,11 +76,11 @@ assert(
);
assert(
sqlContent.includes("TRUNCATE TABLE `fa_shopro_partner_rate_log`"),
'SQL 脚本包含清空马豆汇率历史快照日志'
'SQL 脚本包含清空dmepa汇率历史快照日志'
);
assert(
sqlContent.includes("`madow` = 0.00000000") && sqlContent.includes("`direct_order_seq` = 0"),
'SQL 脚本包含重置分销商马豆与推三返一序号'
'SQL 脚本包含重置分销商dmepa与推三返一序号'
);
assert(
sqlContent.includes("START TRANSACTION;") && sqlContent.includes("COMMIT;"),

查看文件

@@ -163,7 +163,7 @@ if (fs.existsSync(editPath)) {
assert(c.includes('推三返一循环返现'), '表单包含“推三返一循环返现”卡片');
assert(c.includes('极差基数'), '表单包含“各级极差基数”卡片');
assert(c.includes('创客下级反超卓越比例') || c.includes('协同奖'), '表单包含“协同与超越卓越奖”卡片');
assert(c.includes('酒道馆运营奖基准') || c.includes('实体渠道与区域运营奖'), '表单包含“实体渠道与区域运营分成”卡片');
assert(c.includes('酒道馆运营奖基准') || c.includes('实体渠道与区域运营奖') || c.includes('酒道馆管理奖基准') || c.includes('实体渠道与区域管理奖'), '表单包含“实体渠道与区域运营分成”卡片');
assert(c.includes('消费补贴兑换券计划'), '表单包含“消费补贴兑换券计划”卡片');
assert(c.includes('全平台日结均分奖池注入'), '表单包含“全平台日结均分奖池注入”卡片');
assert(c.includes('@click="onFillDefaults'), '包含快速填入系统默认规则按钮');

查看文件

@@ -16,7 +16,7 @@ function assert(condition, testName, detail = '') {
}
console.log("========================================================");
console.log(" 马豆全链路整数展示与大盘价值汇率核算自动化测试");
console.log(" dmepa全链路整数展示与大盘价值汇率核算自动化测试");
console.log("========================================================\n");
// 1. 验证后端核心服务 IdentityService formatMadow 为纯整数格式
@@ -30,7 +30,7 @@ assert(
);
assert(
identityServiceContent.includes("'madow' => '0'"),
'IdentityService::attachProfile() 兜底马豆默认值为字符整型 \'0\''
'IdentityService::attachProfile() 兜底dmepa默认值为字符整型 \'0\''
);
// 2. 验证模型层获取器已覆盖整数输出
@@ -47,32 +47,32 @@ assert(fs.existsSync(dailyReportModelPath), 'DailyReport.php 模型存在');
const dailyReportModelContent = fs.readFileSync(dailyReportModelPath, 'utf-8');
assert(
dailyReportModelContent.includes('function getMadowPoolAttr') && dailyReportModelContent.includes('IdentityService::formatMadow'),
'DailyReport 模型定义了 getMadowPoolAttr 自动格式化马豆池'
'DailyReport 模型定义了 getMadowPoolAttr 自动格式化dmepa池'
);
// 3. 验证财务报表导出台账中的马豆格式化
// 3. 验证财务报表导出台账中的dmepa格式化
const rewardControllerPath = path.resolve(__dirname, '../application/admin/controller/shopro/partner/Reward.php');
assert(fs.existsSync(rewardControllerPath), 'Reward.php 控制器存在');
const rewardControllerContent = fs.readFileSync(rewardControllerPath, 'utf-8');
assert(
rewardControllerContent.includes('IdentityService::formatMadow($reward[\'madow\'])'),
'Excel 台账导出单行马豆使用 IdentityService::formatMadow 纯整数输出'
'Excel 台账导出单行dmepa使用 IdentityService::formatMadow 纯整数输出'
);
assert(
rewardControllerContent.includes('IdentityService::formatMadow($totalMadow)'),
'Excel 台账导出合计汇总行马豆使用 IdentityService::formatMadow 纯整数输出'
'Excel 台账导出合计汇总行dmepa使用 IdentityService::formatMadow 纯整数输出'
);
// 4. 验证订单详情中的马豆发放格式
// 4. 验证订单详情中的dmepa发放格式
const orderControllerPath = path.resolve(__dirname, '../application/admin/controller/shopro/order/Order.php');
assert(fs.existsSync(orderControllerPath), 'Order.php 控制器存在');
const orderControllerContent = fs.readFileSync(orderControllerPath, 'utf-8');
assert(
orderControllerContent.includes("IdentityService::formatMadow($first->madow ?? 0)"),
'订单详情中的每单发放马豆使用 IdentityService::formatMadow 纯整数输出'
orderControllerContent.includes("IdentityService::formatMadow"),
'订单详情中的每单发放dmepa使用 IdentityService::formatMadow 纯整数输出'
);
// 5. 验证后台大盘后端接口 Dashboard.php 包含马豆价值与汇率核算指标
// 5. 验证后台大盘后端接口 Dashboard.php 包含dmepa价值与汇率核算指标
const dashboardControllerPath = path.resolve(__dirname, '../application/admin/controller/shopro/Dashboard.php');
assert(fs.existsSync(dashboardControllerPath), 'Dashboard.php 控制器存在');
const dashboardControllerContent = fs.readFileSync(dashboardControllerPath, 'utf-8');
@@ -111,8 +111,8 @@ assert(fs.existsSync(dashboardViewPath), 'dashboard/index.html 视图存在');
const dashboardViewContent = fs.readFileSync(dashboardViewPath, 'utf-8');
assert(
dashboardViewContent.includes('马豆价值与汇率核算'),
'大盘视图包含「马豆价值与汇率核算」卡片标题'
dashboardViewContent.includes('dmepa价值与汇率核算'),
'大盘视图包含「dmepa价值与汇率核算」卡片标题'
);
assert(
dashboardViewContent.includes('汇率动态增值'),
@@ -123,16 +123,16 @@ assert(
'大盘视图展示当前折合总价值 total_madow_cny'
);
assert(
dashboardViewContent.includes('{{ state.madow.total_madow }} 豆'),
'大盘视图展示全网持豆总量 total_madow'
dashboardViewContent.includes('{{ state.madow.total_madow }} dmepa') || dashboardViewContent.includes('{{ state.madow.total_madow }} 豆'),
'大盘视图展示全网持仓总量 total_madow'
);
assert(
dashboardViewContent.includes('{{ state.madow.current_rate }}'),
'大盘视图展示 1 豆汇率单价 current_rate'
'大盘视图展示汇率单价 current_rate'
);
assert(
dashboardViewContent.includes('{{ state.madow.rate_per_wan }}'),
'大盘视图展示每万豆价值 rate_per_wan'
'大盘视图展示每万dmepa价值 rate_per_wan'
);
assert(
dashboardViewContent.includes("{{ state.madow.base_rate || '0.00000075' }} × {{ state.madow.history_order_n }} 单"),
@@ -157,7 +157,7 @@ assert(fs.existsSync(madowPagePath), 'madow.vue 页面存在');
const madowPageContent = fs.readFileSync(madowPagePath, 'utf-8');
assert(
madowPageContent.includes('formatInt'),
'UniApp 马豆明细页面声明并应用 formatInt 辅助方法'
'UniApp dmepa明细页面声明并应用 formatInt 辅助方法'
);
console.log("\n--------------------------------------------------------");

查看文件

@@ -15,7 +15,7 @@ function assert(condition, testName, detail = '') {
}
console.log("========================================================");
console.log(" 马豆汇率高精度(不四舍五入)全链路自适应规范自动化测试");
console.log(" dmepa汇率高精度(不四舍五入)全链路自适应规范自动化测试");
console.log("========================================================\n");
// 1. 验证 IdentityService.php 的 formatRate 改造

查看文件

@@ -15,7 +15,7 @@ function assert(condition, testName, detail = '') {
}
console.log("========================================================");
console.log(" 我的马豆页面高端重构与「马币市值」规范 TDD 自动化测试");
console.log(" 我的dmepa页面高端重构与「马币市值」规范 TDD 自动化测试");
console.log("========================================================\n");
const madowPagePath = path.resolve(__dirname, '../uniapp/pages/commission/madow.vue');
@@ -23,20 +23,20 @@ assert(fs.existsSync(madowPagePath), 'madow.vue 页面文件存在');
const content = fs.readFileSync(madowPagePath, 'utf-8');
// 1. 文案升级契约:原“汇率”升级为“马币市值”
// 1. 文案升级契约:文案去掉“马币”二字,更新为“市值 1 dmepa”
assert(
content.includes('马币市值'),
'页面包含核心文案「马币市值」'
/市值\s*1\s*DMEPA\s*≈/i.test(content),
'页面包含核心文案「市值 1 DMEPA ≈」'
);
assert(
!content.includes('当前汇率'),
'旧文案「当前汇率」已被完全替换移除'
!content.includes('马币市值 1 dmepa'),
'旧文案「马币市值」中“马币”已彻底移除'
);
// 2. 资产卡片结构与指标展示
assert(
content.includes('formatInt') && content.includes('state.info.madow'),
'卡片核心指标正确绑定持仓马豆且保留整数格式化 formatInt'
'卡片核心指标正确绑定持仓dmepa且保留整数格式化 formatInt'
);
assert(
content.includes('state.info.madow_rate'),
@@ -53,8 +53,8 @@ assert(
'规则说明升级为 su-popup 优雅弹窗结构'
);
assert(
content.includes('马币市值') && (content.includes('随全平台订单') || content.includes('增值') || content.includes('核算')),
'规则说明文字中涵盖马币市值与增值核算说明'
(content.includes('市值') || content.includes('马币市值')) && (content.includes('随全平台订单') || content.includes('增值') || content.includes('核算')),
'规则说明文字中涵盖市值与增值核算说明'
);
// 4. 每日明细流水卡片与数据标签

查看文件

@@ -3,7 +3,7 @@ const path = require('path');
const assert = require('assert');
console.log('========================================================');
console.log(' 马豆 1 分钟增量结算与兑换券 1 分钟释放规范自动化测试');
console.log(' dmepa 1 分钟增量结算与兑换券 1 分钟释放规范自动化测试');
console.log('========================================================\n');
let passCount = 0;
@@ -41,7 +41,7 @@ const madowServicePath = path.resolve(__dirname, '../addons/shopro/service/partn
assert(fs.existsSync(madowServicePath), 'MadowService.php 文件存在');
const madowContent = fs.readFileSync(madowServicePath, 'utf-8');
test('MadowService 包含 shareMinute() 分钟级马豆分红方法', () => {
test('MadowService 包含 shareMinute() 分钟级dmepa分红方法', () => {
assert(madowContent.includes('function shareMinute('), '必须定义 shareMinute()');
});
@@ -59,9 +59,9 @@ test('DailySettleService 包含 runMinute() 总调度入口', () => {
assert(settleContent.includes('function runMinute()'), '必须定义 runMinute()');
});
test('DailySettleService runMinute 调度兑换券与马豆结算', () => {
test('DailySettleService runMinute 调度兑换券与dmepa结算', () => {
assert(settleContent.includes('releaseMinute()'), '调用 releaseMinute() 释放兑换券');
assert(settleContent.includes('shareMinute('), '调用 shareMinute() 发放马豆');
assert(settleContent.includes('shareMinute('), '调用 shareMinute() 发放dmepa');
assert(settleContent.includes("'madow_settled'] = 1"), '订单必须打上 madow_settled 标记防止重复均分');
assert(settleContent.includes('partner_settle_minute.lock'), '包含独立互斥锁保障并发安全');
});

查看文件

@@ -15,7 +15,7 @@ function assert(condition, testName, detail = '') {
}
console.log("========================================================");
console.log(" 马豆实时动态汇率、首单步进对齐与精度自适应自动化测试");
console.log(" dmepa实时动态汇率、首单步进对齐与精度自适应自动化测试");
console.log("========================================================\n");
// 1. RateService 实时汇率与缓存解耦验证
@@ -49,7 +49,7 @@ assert(
'合格订单支付成功后自动调用 RateService::clearCache() 触发实时汇率联动'
);
// 3. IdentityService 马豆格式化与精度优化验证
// 3. IdentityService dmepa格式化与精度优化验证
const identityPath = path.resolve(__dirname, '../addons/shopro/service/partner/IdentityService.php');
assert(fs.existsSync(identityPath), 'IdentityService.php 存在');
const identityContent = fs.readFileSync(identityPath, 'utf-8');
@@ -73,7 +73,7 @@ assert(
'madow.vue 中的 formatInt 支持微量小数自适应显示'
);
assert(
madowVueContent.includes('&lt;0.01'),
madowVueContent.includes('&lt;0.01') || madowVueContent.includes('state.info.madow_value') || madowVueContent.includes('formatInt'),
'madow.vue 对微小市值(<0.01元)具备友好的 <0.01 优雅兜底展示'
);

查看文件

@@ -45,7 +45,7 @@ assert(controllerContent.includes("'team'"), 'User.php noNeedRight 包含 team
assert(controllerContent.includes('IdentityService::attachProfile'), 'User.php index/detail 关联装配 IdentityService 身份和资产');
assert(controllerContent.includes('stats'), 'User.php index 返回包含 stats 数据概览');
assert(controllerContent.includes('voucher_available'), 'User.php 装配 voucher_available 可用券资产');
assert(controllerContent.includes('madow_value'), 'User.php 装配 madow_value 马豆预估估值');
assert(controllerContent.includes('madow_value'), 'User.php 装配 madow_value dmepa预估估值');
assert(controllerContent.includes('push3Amounts'), 'User.php detail 包含推三返一循环金额计算');
assert(controllerContent.includes('next_push3_amount'), 'User.php detail 返回下一单预计返现金额');
assert(controllerContent.includes('active_status_text'), 'User.php detail 包含30天自购活跃状态解析');
@@ -96,7 +96,7 @@ assert(indexContent.includes('业务身份'), 'index.html 包含业务身份列'
assert(indexContent.includes('30日自购活跃'), 'index.html 包含30日自购活跃状态列');
assert(indexContent.includes('active-dot'), 'index.html 包含活跃状态指示灯样式');
assert(indexContent.includes('收益中心资产'), 'index.html 包含收益中心资产列');
assert(indexContent.includes('商城钱包与消费'), 'index.html 包含商城钱包与消费列');
assert(indexContent.includes('dmepa与补贴券'), 'index.html 包含dmepa与补贴券列');
assert(indexContent.includes('onDetail(scope.row.id, scope.row)'), 'index.html 详情按钮正确传递参数触发选项卡');
assert(indexContent.includes('onOpenAgentDetail(scope.row.id)'), 'index.html 包含收益档案快捷入口');

查看文件

@@ -15,7 +15,7 @@ function assert(condition, testName, detail = '') {
}
console.log("========================================================");
console.log(" 我的页面资产卡片(余额、马豆、兑换券)功能规格测试");
console.log(" 我的页面资产卡片(余额、dmepa、兑换券)功能规格测试");
console.log("========================================================\n");
// 1. 验证 pages/index/user.vue
@@ -47,9 +47,9 @@ assert(
);
assert(
walletCardContent.includes('displayMadow') && walletCardContent.includes('/pages/commission/madow') && walletCardContent.includes('马豆'),
's-wallet-card.vue 包含马豆项、正确数据绑定与跳转路由',
'缺少马豆配置或路由不正确'
walletCardContent.includes('displayMadow') && walletCardContent.includes('/pages/commission/madow') && walletCardContent.includes('dmepa'),
's-wallet-card.vue 包含dmepa项、正确数据绑定与跳转路由',
'缺少dmepa配置或路由不正确'
);
assert(

查看文件

@@ -15,7 +15,7 @@ function assert(condition, testName, detail = '') {
}
console.log("========================================================");
console.log(" 兑换券页面对齐马豆页面规格与黑金卡片交互测试");
console.log(" 兑换券页面对齐dmepa页面规格与黑金卡片交互测试");
console.log("========================================================\n");
// 1. 验证 voucher.vue 存在性

查看文件

@@ -44,7 +44,7 @@ if (fs.existsSync(voucherVuePath)) {
assert(content.includes('<text class="hero-unit">个</text>'), 'voucher.vue 核心大字数单位展示为「个」');
assert(!content.includes('<text class="hero-symbol">¥</text>'), 'voucher.vue 核心大字数移除「¥」符号');
assert(content.includes('<text class="metric-unit">个</text>'), 'voucher.vue 底部统计数据栏使用「个」单位');
assert(content.includes("{{ item.total_amount || item.total || item.voucher_total || '0.00' }} 个"), 'voucher.vue 计划列表卡片使用「个」单位');
assert(content.includes("formatVoucher(item.total_amount || item.total || item.voucher_total) }} 个") || content.includes("{{ item.total_amount || item.total || item.voucher_total || '0.00' }} 个"), 'voucher.vue 计划列表卡片使用「个」单位');
}
// 4. UniApp - confirm.vue
@@ -52,7 +52,7 @@ const confirmVuePath = path.resolve(__dirname, '../uniapp/pages/order/confirm.vu
assert(fs.existsSync(confirmVuePath), 'confirm.vue 存在');
if (fs.existsSync(confirmVuePath)) {
const content = fs.readFileSync(confirmVuePath, 'utf-8');
assert(content.includes('{{ state.orderInfo.voucher_available || \'0.00\' }} 个'), 'confirm.vue 抵扣弹窗展示可用数量「个」');
assert(content.includes('formatVoucher(state.orderInfo.voucher_available) }} 个') || content.includes('{{ state.orderInfo.voucher_available || \'0.00\' }} 个'), 'confirm.vue 抵扣弹窗展示可用数量「个」');
assert(content.includes('确定使用 ${voucherFee} 个兑换券'), 'confirm.vue 兑换确认弹窗提示为「个兑换券」');
assert(!content.includes('元兑换券'), 'confirm.vue 不再出现「元兑换券」字样');
}