242 行
8.4 KiB
PHP
242 行
8.4 KiB
PHP
<?php
|
||
|
||
namespace tests\partner;
|
||
|
||
// 引导 ThinkPHP 环境
|
||
define('APP_PATH', __DIR__ . '/../../application/');
|
||
define('ROOT_PATH', dirname(realpath(APP_PATH)) . '/');
|
||
require_once __DIR__ . '/../../thinkphp/base.php';
|
||
|
||
\think\Config::set(\think\Config::load(APP_PATH . 'config.php'));
|
||
\think\Config::set(\think\Config::load(APP_PATH . 'database.php'));
|
||
|
||
use addons\shopro\service\partner\DailySettleService;
|
||
use addons\shopro\service\partner\PartnerConfig;
|
||
use addons\shopro\service\partner\VoucherService;
|
||
use app\admin\controller\shopro\partner\Reward;
|
||
use app\admin\controller\shopro\partner\Daily;
|
||
use think\Request;
|
||
use think\Db;
|
||
|
||
class PartnerFinanceTest
|
||
{
|
||
private $passed = 0;
|
||
private $failed = 0;
|
||
|
||
public function run()
|
||
{
|
||
echo "========================================================\n";
|
||
echo " 合伙股东/收益中心 财务优化专项测试 (Finance Tests) \n";
|
||
echo "========================================================\n\n";
|
||
|
||
$this->testOrderTreeParamError();
|
||
$this->testOrderTreeNonExist();
|
||
$this->testReissueRiskLimit();
|
||
$this->testReissueParamMissing();
|
||
$this->testDailyDryRunPreview();
|
||
$this->testVoucherDebtCalculation();
|
||
|
||
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++;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 测试 1:订单透视参数校验 (order_id <= 0 抛出异常或错误返回)
|
||
*/
|
||
public function testOrderTreeParamError()
|
||
{
|
||
$testName = "API: orderTree 参数非法拦截 (order_id=0)";
|
||
try {
|
||
$ctrl = new Reward();
|
||
Request::instance()->withGet(['order_id' => 0]);
|
||
$output = $this->captureResponse(function () use ($ctrl) {
|
||
$ctrl->orderTree();
|
||
});
|
||
$res = json_decode($output, true);
|
||
$this->assert(
|
||
isset($res['code']) && $res['code'] !== 0 && strpos($res['msg'] ?? '', '订单ID不合法') !== false,
|
||
$testName,
|
||
"返回内容: {$output}"
|
||
);
|
||
} catch (\think\exception\HttpResponseException $e) {
|
||
$res = $e->getResponse()->getData();
|
||
$this->assert(
|
||
isset($res['code']) && $res['code'] !== 0,
|
||
$testName,
|
||
"HttpResponse 拦截成功"
|
||
);
|
||
} catch (\Exception $e) {
|
||
$this->assert(true, $testName, "正确捕获参数异常: " . $e->getMessage());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 测试 2:订单透视订单不存在拦截
|
||
*/
|
||
public function testOrderTreeNonExist()
|
||
{
|
||
$testName = "API: orderTree 订单不存在拦截 (order_id=99999999)";
|
||
try {
|
||
$ctrl = new Reward();
|
||
Request::instance()->withGet(['order_id' => 99999999]);
|
||
$output = $this->captureResponse(function () use ($ctrl) {
|
||
$ctrl->orderTree();
|
||
});
|
||
$res = json_decode($output, true);
|
||
$this->assert(
|
||
isset($res['code']) && $res['code'] !== 0 && strpos($res['msg'] ?? '', '订单不存在') !== false,
|
||
$testName,
|
||
"返回内容: {$output}"
|
||
);
|
||
} catch (\think\exception\HttpResponseException $e) {
|
||
$res = $e->getResponse()->getData();
|
||
$this->assert(
|
||
isset($res['code']) && $res['code'] !== 0 && strpos($res['msg'] ?? '', '订单不存在') !== false,
|
||
$testName,
|
||
"正确拦截不存在订单"
|
||
);
|
||
} catch (\Exception $e) {
|
||
$this->assert(strpos($e->getMessage(), '订单不存在') !== false, $testName);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 测试 3:手工补发 10,000 元风控硬上限拦截
|
||
*/
|
||
public function testReissueRiskLimit()
|
||
{
|
||
$testName = "风控: reissue 单笔超 10,000 元硬上限拦截 (amount=10001)";
|
||
try {
|
||
$ctrl = new Reward();
|
||
Request::instance()->withPost([
|
||
'user_id' => 1,
|
||
'reward_type' => 'push3',
|
||
'amount' => 10001,
|
||
'memo' => '超额测试',
|
||
]);
|
||
$output = $this->captureResponse(function () use ($ctrl) {
|
||
$ctrl->reissue();
|
||
});
|
||
$res = json_decode($output, true);
|
||
$this->assert(
|
||
isset($res['code']) && $res['code'] !== 0 && strpos($res['msg'] ?? '', '10,000') !== false,
|
||
$testName,
|
||
"返回内容: {$output}"
|
||
);
|
||
} catch (\think\exception\HttpResponseException $e) {
|
||
$res = $e->getResponse()->getData();
|
||
$this->assert(
|
||
isset($res['code']) && $res['code'] !== 0 && strpos($res['msg'] ?? '', '10,000') !== false,
|
||
$testName,
|
||
"风控硬上限成功拦截"
|
||
);
|
||
} catch (\Exception $e) {
|
||
$this->assert(strpos($e->getMessage(), '10,000') !== false, $testName);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 测试 4:手工补发必填参数校验
|
||
*/
|
||
public function testReissueParamMissing()
|
||
{
|
||
$testName = "API: reissue 必填参数缺失拦截 (缺少 memo)";
|
||
try {
|
||
$ctrl = new Reward();
|
||
Request::instance()->withPost([
|
||
'user_id' => 1,
|
||
'reward_type' => 'push3',
|
||
'amount' => 100,
|
||
'memo' => '',
|
||
]);
|
||
$output = $this->captureResponse(function () use ($ctrl) {
|
||
$ctrl->reissue();
|
||
});
|
||
$res = json_decode($output, true);
|
||
$this->assert(
|
||
isset($res['code']) && $res['code'] !== 0 && strpos($res['msg'] ?? '', '必填') !== false,
|
||
$testName,
|
||
"返回内容: {$output}"
|
||
);
|
||
} catch (\think\exception\HttpResponseException $e) {
|
||
$res = $e->getResponse()->getData();
|
||
$this->assert(
|
||
isset($res['code']) && $res['code'] !== 0 && strpos($res['msg'] ?? '', '必填') !== false,
|
||
$testName,
|
||
"参数缺失成功拦截"
|
||
);
|
||
} catch (\Exception $e) {
|
||
$this->assert(strpos($e->getMessage(), '必填') !== false, $testName);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 测试 5:日结 Dry Run 预跑试算只读测试
|
||
*/
|
||
public function testDailyDryRunPreview()
|
||
{
|
||
$testName = "业务: DailySettleService::preview 只读试算结构完整性";
|
||
$service = new DailySettleService();
|
||
$testDate = '2026-09-01'; // 模拟日期
|
||
$preview = $service->preview($testDate);
|
||
|
||
$hasKeys = isset($preview['settle_date']) &&
|
||
isset($preview['order_count']) &&
|
||
isset($preview['gmv']) &&
|
||
isset($preview['madow']['pool']) &&
|
||
isset($preview['venue']['pool']) &&
|
||
isset($preview['county']['pool']) &&
|
||
isset($preview['financial']['payout_ratio']);
|
||
|
||
$this->assert(
|
||
$hasKeys && $preview['settle_date'] === $testDate,
|
||
$testName,
|
||
"预览结构字段校验完整,返回包含 GMV、均分池及综合拨比"
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 测试 6:兑换券待履约负债与资格判定
|
||
*/
|
||
public function testVoucherDebtCalculation()
|
||
{
|
||
$testName = "财务: 活跃资格判定周期配置 (30天)";
|
||
$days = PartnerConfig::activeDays();
|
||
$this->assert($days === 30, $testName, "活跃资格判定周期配置为 30 天");
|
||
}
|
||
|
||
private function captureResponse(callable $fn)
|
||
{
|
||
ob_start();
|
||
try {
|
||
$fn();
|
||
} catch (\think\exception\HttpResponseException $e) {
|
||
ob_end_clean();
|
||
throw $e;
|
||
} catch (\Exception $e) {
|
||
ob_end_clean();
|
||
throw $e;
|
||
}
|
||
return ob_get_clean();
|
||
}
|
||
}
|
||
|
||
// 执行测试
|
||
if (php_sapi_name() === 'cli') {
|
||
$tester = new PartnerFinanceTest();
|
||
exit($tester->run());
|
||
}
|