$val) { $name = 'PHP_' . strtoupper($key); if (is_array($val)) { foreach ($val as $k => $v) { $item = $name . '_' . strtoupper($k); putenv("$item=$v"); } } else { putenv("$name=$val"); } } } require_once __DIR__ . '/../../application/common.php'; \think\Config::set(\think\Config::load(APP_PATH . 'config.php')); \think\Config::set(include APP_PATH . 'database.php', 'database'); \think\Loader::addNamespace('app', APP_PATH); require_once __DIR__ . '/../../addons/shopro/helper/helper.php'; // 初始化 CLI 下的 Session 并注入合法的 Admin 账号及 Auth 登录状态 \think\Session::init(); $auth = \app\admin\library\Auth::instance(); $refAuth = new \ReflectionClass($auth); if ($refAuth->hasProperty('logined')) { $p = $refAuth->getProperty('logined'); $p->setAccessible(true); $p->setValue($auth, true); } if ($refAuth->hasProperty('rules')) { $p = $refAuth->getProperty('rules'); $p->setAccessible(true); $p->setValue($auth, ['*']); } use app\admin\controller\shopro\user\User; use app\admin\model\shopro\user\User as UserModel; use app\admin\model\shopro\commission\Agent as AgentModel; use app\admin\model\shopro\partner\Reward as RewardModel; use think\Request; use think\Db; class TestUserController extends User { public function _initialize() { $this->model = new UserModel(); } } class MultiAssetRechargeTest { private $passed = 0; private $failed = 0; private $testUserId = 0; public function run() { echo "========================================================\n"; echo " 会员多资产(余额/积分/dmepa/兑换券)充值业务测试 \n"; echo "========================================================\n\n"; $this->setUp(); try { $this->testInvalidAssetType(); $this->testMoneyRechargeAndDeduct(); $this->testMoneyOverdrawn(); $this->testScoreRechargeAndDeduct(); $this->testScoreOverdrawn(); $this->testMadowRechargeAndDeduct(); $this->testMadowOverdrawn(); $this->testVoucherRechargeAndDeduct(); $this->testVoucherOverdrawn(); } finally { $this->tearDown(); } echo "\n--------------------------------------------------------\n"; echo "测试执行结果: 通过 {$this->passed} 项, 失败 {$this->failed} 项\n"; echo "========================================================\n"; return $this->failed === 0 ? 0 : 1; } private function assert($condition, $testName, $detail = '') { if ($condition) { echo " [PASS] {$testName}\n"; $this->passed++; } else { echo " [FAIL] {$testName} - {$detail}\n"; $this->failed++; } } private function setUp() { // 查找或创建一个测试用户 $user = UserModel::where('username', 'test_recharge_user')->find(); if (!$user) { $now = time(); $id = Db::name('user')->insertGetId([ 'username' => 'test_recharge_user', 'nickname' => '充值测试员', 'password' => md5('123456'), 'salt' => '123456', 'mobile' => '13988889999', 'money' => 100.00, 'score' => 500, 'status' => 'normal', 'createtime' => $now, 'updatetime' => $now, ]); $this->testUserId = $id; } else { $this->testUserId = $user->id; Db::name('user')->where('id', $this->testUserId)->update([ 'money' => 100.00, 'score' => 500, ]); } // 初始化 agent 记录 $agent = Db::name('shopro_commission_agent')->where('user_id', $this->testUserId)->find(); if (!$agent) { Db::name('shopro_commission_agent')->insert([ 'user_id' => $this->testUserId, 'level' => 1, 'status' => 'normal', 'madow' => '10.00000000', 'voucher_total' => '200.00', 'voucher_released' => '200.00', 'voucher_used' => '50.00', 'createtime' => time(), 'updatetime' => time(), ]); } else { Db::name('shopro_commission_agent')->where('user_id', $this->testUserId)->update([ 'madow' => '10.00000000', 'voucher_total' => '200.00', 'voucher_released' => '200.00', 'voucher_used' => '50.00', ]); } } private function tearDown() { if ($this->testUserId) { try { Db::name('user')->where('id', $this->testUserId)->delete(); Db::name('shopro_commission_agent')->where('user_id', $this->testUserId)->delete(); Db::name('shopro_partner_reward')->where('user_id', $this->testUserId)->delete(); Db::name('shopro_user_wallet_log')->where('user_id', $this->testUserId)->delete(); Db::name('user_money_log')->where('user_id', $this->testUserId)->delete(); Db::name('user_score_log')->where('user_id', $this->testUserId)->delete(); } catch (\Exception $e) { // 忽略非关键日志表删除错误 } } } private function invokeRecharge($params) { $controller = new TestUserController(); $request = Request::create('shopro/user/user/recharge', 'POST', $params, [], [], [ 'HTTP_X_REQUESTED_WITH' => 'xmlhttprequest', 'HTTP_ACCEPT' => 'application/json', ]); // 绑定 request 到 控制器 $ref = new \ReflectionClass($controller); $reqProp = $ref->getProperty('request'); $reqProp->setAccessible(true); $reqProp->setValue($controller, $request); // 设置 Request 全局单例 $refInst = new \ReflectionClass(Request::class); $instProp = $refInst->getProperty('instance'); $instProp->setAccessible(true); $instProp->setValue(null, $request); try { $controller->recharge(); } catch (\think\exception\HttpResponseException $e) { $data = $e->getResponse()->getData(); if (is_string($data)) { $decoded = json_decode($data, true); if (is_array($decoded)) { return $decoded; } } if (!is_array($data)) { echo "[DEBUG RAW DATA] " . substr((string)$data, 0, 200) . "\n"; } return is_array($data) ? $data : ['code' => 0, 'msg' => (string) $data]; } return ['code' => 0, 'msg' => '未知响应']; } public function testInvalidAssetType() { $res = $this->invokeRecharge([ 'id' => $this->testUserId, 'type' => 'invalid_coin', 'amount' => 10, 'memo' => '测试非法资产类型', ]); $this->assert($res['code'] === 0 && strpos($res['msg'], '不支持') !== false, '非法资产类型校验拦截', $res['msg'] ?? ''); } public function testMoneyRechargeAndDeduct() { // 充值 50 元 $res1 = $this->invokeRecharge([ 'id' => $this->testUserId, 'type' => 'money', 'amount' => 50, 'memo' => '充值50元', ]); $u1 = UserModel::get($this->testUserId); $this->assert($res1['code'] === 1 && (float)$u1->money == 150.00, '余额充值成功且到账', "当前余额: {$u1->money}"); // 扣除 30 元 $res2 = $this->invokeRecharge([ 'id' => $this->testUserId, 'type' => 'money', 'amount' => -30, 'memo' => '扣除30元', ]); $u2 = UserModel::get($this->testUserId); $this->assert($res2['code'] === 1 && (float)$u2->money == 120.00, '余额扣除成功且准确', "当前余额: {$u2->money}"); } public function testMoneyOverdrawn() { // 尝试扣除超过当前余额 (120元) $res = $this->invokeRecharge([ 'id' => $this->testUserId, 'type' => 'money', 'amount' => -200, 'memo' => '超额扣除余额', ]); $this->assert($res['code'] === 0 && strpos($res['msg'], '不足') !== false, '余额不足拦截超扣', $res['msg'] ?? ''); } public function testScoreRechargeAndDeduct() { // 充值 100 积分 $res1 = $this->invokeRecharge([ 'id' => $this->testUserId, 'type' => 'score', 'amount' => 100, 'memo' => '充值100积分', ]); $u1 = UserModel::get($this->testUserId); $this->assert($res1['code'] === 1 && $u1->score == 600, '积分充值成功且到账', "当前积分: {$u1->score}"); // 扣除 200 积分 $res2 = $this->invokeRecharge([ 'id' => $this->testUserId, 'type' => 'score', 'amount' => -200, 'memo' => '扣除200积分', ]); $u2 = UserModel::get($this->testUserId); $this->assert($res2['code'] === 1 && $u2->score == 400, '积分扣除成功且准确', "当前积分: {$u2->score}"); } public function testScoreOverdrawn() { // 尝试超额扣除积分 $res = $this->invokeRecharge([ 'id' => $this->testUserId, 'type' => 'score', 'amount' => -1000, 'memo' => '超额扣除积分', ]); $this->assert($res['code'] === 0 && strpos($res['msg'], '不足') !== false, '积分不足拦截超扣', $res['msg'] ?? ''); } public function testMadowRechargeAndDeduct() { // 充值 5.12345678 枚dmepa $res1 = $this->invokeRecharge([ 'id' => $this->testUserId, 'type' => 'madow', 'amount' => '5.12345678', 'memo' => '充值dmepa', ]); $agent1 = AgentModel::where('user_id', $this->testUserId)->find(); $this->assert($res1['code'] === 1 && $agent1->madow == '15.12345678', 'dmepa8位高精度充值到账', "当前dmepa: {$agent1->madow}"); // 扣除 3.00000000 枚dmepa $res2 = $this->invokeRecharge([ 'id' => $this->testUserId, 'type' => 'madow', 'amount' => '-3.00000000', 'memo' => '扣除dmepa', ]); $agent2 = AgentModel::where('user_id', $this->testUserId)->find(); $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, 'dmepa充值写入收益流水明细'); } public function testMadowOverdrawn() { // 尝试超额扣除dmepa $res = $this->invokeRecharge([ 'id' => $this->testUserId, 'type' => 'madow', 'amount' => '-99.00000000', 'memo' => '超额扣除dmepa', ]); $this->assert($res['code'] === 0 && strpos($res['msg'], '不足') !== false, 'dmepa持仓不足拦截超扣', $res['msg'] ?? ''); } public function testVoucherRechargeAndDeduct() { // 当前 released=200, used=50, available=150 // 充值 100 元兑换券 $res1 = $this->invokeRecharge([ 'id' => $this->testUserId, 'type' => 'voucher', 'amount' => 100, 'memo' => '充值兑换券', ]); $agent1 = AgentModel::where('user_id', $this->testUserId)->find(); $avail1 = $agent1->voucher_released - $agent1->voucher_used; $this->assert($res1['code'] === 1 && $avail1 == 250.00, '兑换券充值成功可用额度增加', "可用券: {$avail1}"); // 扣除 80 元兑换券 $res2 = $this->invokeRecharge([ 'id' => $this->testUserId, 'type' => 'voucher', 'amount' => -80, 'memo' => '扣除兑换券', ]); $agent2 = AgentModel::where('user_id', $this->testUserId)->find(); $avail2 = $agent2->voucher_released - $agent2->voucher_used; $this->assert($res2['code'] === 1 && $avail2 == 170.00, '兑换券扣除成功可用额度准确减少', "可用券: {$avail2}"); // 验证流水表记录 $log = RewardModel::where('user_id', $this->testUserId)->where('reward_type', 'admin_voucher')->order('id', 'desc')->find(); $this->assert($log && $log->amount == -80.00, '兑换券变动写入收益流水明细'); } public function testVoucherOverdrawn() { // 尝试超额扣除兑换券 (当前可用 170) $res = $this->invokeRecharge([ 'id' => $this->testUserId, 'type' => 'voucher', 'amount' => -300, 'memo' => '超额扣除兑换券', ]); $this->assert($res['code'] === 0 && strpos($res['msg'], '不足') !== false, '兑换券可用额度不足拦截超扣', $res['msg'] ?? ''); } } $test = new MultiAssetRechargeTest(); exit($test->run());