/** * 自购与直推订单双向保活(收益有效期)专项测试 * 覆盖场景: * 1. 静态代码结构检查:PartnerRewardService::touchOrderTimes, ActiveService::hasQualifiedOrder & syncActiveTimes, Partner.php 监听调用 * 2. 逻辑模拟:自购订单激活本人保活 * 3. 逻辑模拟:直推订单激活直属上级保活(二级及以上间接团队隔离,不激活) * 4. 逻辑模拟:防时间倒退(较旧订单不覆盖较新订单时间戳) * 5. 状态转换模拟:直推订单使 30 天未自购的上级从 inactive 转为 active */ const fs = require('fs'); const path = require('path'); const assert = require('assert'); let passCount = 0; let failCount = 0; function it(desc, fn) { try { fn(); console.log(` [PASS] ${desc}`); passCount++; } catch (err) { console.error(` [FAIL] ${desc}`); console.error(` Error: ${err.message}`); failCount++; } } console.log('========================================================'); console.log(' 自购与直推订单双向保活(收益有效期)专项测试'); console.log('========================================================\n'); const rewardServicePath = path.resolve(__dirname, '../../addons/shopro/service/partner/PartnerRewardService.php'); const activeServicePath = path.resolve(__dirname, '../../addons/shopro/service/partner/ActiveService.php'); const listenerPath = path.resolve(__dirname, '../../addons/shopro/listener/Partner.php'); const rewardServiceContent = fs.readFileSync(rewardServicePath, 'utf8'); const activeServiceContent = fs.readFileSync(activeServicePath, 'utf8'); const listenerContent = fs.readFileSync(listenerPath, 'utf8'); // 1. 代码规范与方法存在性检查 it('PartnerRewardService 包含 touchOrderTimes 方法且更新买家本人与一级推荐人', () => { assert(rewardServiceContent.includes('function touchOrderTimes'), '缺少 touchOrderTimes 方法'); assert(rewardServiceContent.includes('parent_user_id'), 'touchOrderTimes 未查询 parent_user_id'); assert(rewardServiceContent.includes('$this->updateAgentOrderTime($parentUserId'), '未更新直推上级订单时间'); }); it('PartnerRewardService 防时间倒退逻辑(新时间戳不被旧时间戳覆盖)', () => { assert(rewardServiceContent.includes('$timestamp >= $current'), 'updateAgentOrderTime 缺少防时间倒退判断'); }); it('ActiveService::hasQualifiedOrder 支持自购与直推订单双向判定', () => { assert(activeServiceContent.includes('parent_user_id'), 'hasQualifiedOrder 未关联直推下级 parent_user_id'); assert(activeServiceContent.includes('$candidateUserIds = array_merge([$userId], $directChildIds)'), '缺少买家本人与直推下级候选集合并'); }); it('ActiveService 包含 syncActiveTimes 全量校准同步方法', () => { assert(activeServiceContent.includes('function syncActiveTimes'), '缺少 syncActiveTimes 方法'); assert(activeServiceContent.includes('max($selfTime, $directTime)'), 'syncActiveTimes 缺少自购与直推最大值聚合'); }); it('Partner 监听器 orderPaidAfter 调用 touchOrderTimes 触发双向保活', () => { assert(listenerContent.includes('touchOrderTimes'), 'Partner.php 未调用 touchOrderTimes'); }); // 2. 模拟订单与层级保活流转模拟器 class MockAgentSystem { constructor() { this.users = {}; // id -> { id, parent_user_id } this.agents = {}; // user_id -> { last_self_order_time, level } } addUser(id, parentUserId, level = 1) { this.users[id] = { id, parent_user_id: parentUserId }; this.agents[id] = { last_self_order_time: 0, level }; } updateAgentOrderTime(userId, timestamp) { const agent = this.agents[userId]; if (agent) { if (timestamp >= agent.last_self_order_time) { agent.last_self_order_time = timestamp; } } } touchOrderTimes(buyerId, time) { const timestamp = time || Math.floor(Date.now() / 1000); // 1. 买家本人(自购) this.updateAgentOrderTime(buyerId, timestamp); // 2. 直推一级上级(直推) const user = this.users[buyerId]; if (user && user.parent_user_id && user.parent_user_id !== buyerId) { this.updateAgentOrderTime(user.parent_user_id, timestamp); } } isActive(userId, nowSec, windowDays = 30) { const agent = this.agents[userId]; if (!agent || !agent.last_self_order_time) return false; const windowStart = nowSec - windowDays * 86400; return agent.last_self_order_time >= windowStart; } } // 场景一:买家 A 自购,激活本人 30 天保活 it('场景一:买家 A 自购 -> 激活本人保活,状态为 active', () => { const sys = new MockAgentSystem(); sys.addUser(101, 0); // 无上级 const orderTime = 1700000000; sys.touchOrderTimes(101, orderTime); assert.strictEqual(sys.agents[101].last_self_order_time, orderTime, '买家 A 订单时间未更新'); assert.strictEqual(sys.isActive(101, orderTime + 86400), true, '自购后第 1 天应为 active'); assert.strictEqual(sys.isActive(101, orderTime + 31 * 86400), false, '自购后第 31 天应为 inactive'); }); // 场景二:直推下级 B 下单 -> 激活直推上级 A 保活 it('场景二:直推下级 B 下单 -> 同时激活直推上级 A,A 即使无自购也变为 active', () => { const sys = new MockAgentSystem(); sys.addUser(100, 0); // 上级 A(从未自购) sys.addUser(200, 100); // 直推下级 B(推荐人是 A) const orderTime = 1700000000; sys.touchOrderTimes(200, orderTime); // B 本人自购更新 assert.strictEqual(sys.agents[200].last_self_order_time, orderTime, '买家 B 未更新'); // A 直推获得保活更新 assert.strictEqual(sys.agents[100].last_self_order_time, orderTime, '直推上级 A 未被直推订单激活'); assert.strictEqual(sys.isActive(100, orderTime + 10 * 86400), true, '直推上级 A 获得 30 天有效分红资格'); }); // 场景三:间接下级 C(二级)下单 -> 仅激活 B,不激活上上级 A(严格限定直推与自购) it('场景三:二级下级 C 下单 -> 仅激活一级直推人 B,上上级 A 不受影响(层级隔离)', () => { const sys = new MockAgentSystem(); sys.addUser(10, 0); // 上上级 A sys.addUser(20, 10); // 上级 B sys.addUser(30, 20); // 下级 C(买家) const orderTime = 1700000000; sys.touchOrderTimes(30, orderTime); assert.strictEqual(sys.agents[30].last_self_order_time, orderTime, '买家 C 应更新'); assert.strictEqual(sys.agents[20].last_self_order_time, orderTime, '直属上级 B 应更新'); assert.strictEqual(sys.agents[10].last_self_order_time, 0, '上上级 A 绝不应被二级订单更新(严格限定自购与直推)'); }); // 场景四:防时间倒退机制(乱序异步事件不能覆盖更新的时间戳) it('场景四:防时间倒退 -> 乱序处理时,较旧的订单不会覆盖较新的保活时间戳', () => { const sys = new MockAgentSystem(); sys.addUser(1, 0); sys.addUser(2, 1); const tNew = 1700050000; const tOld = 1700010000; sys.touchOrderTimes(2, tNew); assert.strictEqual(sys.agents[1].last_self_order_time, tNew); // 此时来了一单较早的补偿结算 sys.touchOrderTimes(2, tOld); assert.strictEqual(sys.agents[1].last_self_order_time, tNew, '较旧的时间戳被错误覆盖了'); }); console.log(`\n--------------------------------------------------------`); console.log(`测试结果统计: 通过 ${passCount} 项, 失败 ${failCount} 项`); console.log(`========================================================\n`); if (failCount > 0) { process.exit(1); } else { process.exit(0); }