feat: 初始化雄戈商城服务端代码与Webhook自动部署配置
这个提交包含在:
@@ -0,0 +1,479 @@
|
||||
<?php
|
||||
/**
|
||||
* 分销测试用户 & 配置初始化
|
||||
*
|
||||
* 用法(项目根目录):
|
||||
* php addons/shopro/tests/commission_setup_test_users.php
|
||||
* php addons/shopro/tests/commission_setup_test_users.php --goods-id=123
|
||||
* php addons/shopro/tests/commission_setup_test_users.php --addresses-only --goods-id=195
|
||||
* php addons/shopro/tests/commission_setup_test_users.php --addresses-only --money=10000
|
||||
*
|
||||
* 清理测试数据:
|
||||
* php addons/shopro/tests/commission_cleanup_test_users.php --dry-run
|
||||
* php addons/shopro/tests/commission_cleanup_test_users.php --confirm
|
||||
*/
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
http_response_code(403);
|
||||
exit("请通过命令行运行\n");
|
||||
}
|
||||
|
||||
define('APP_PATH', __DIR__ . '/../../../application/');
|
||||
define('ROOT_PATH', __DIR__ . '/../../../');
|
||||
|
||||
if (!is_file(APP_PATH . 'admin/command/Install/install.lock')) {
|
||||
fwrite(STDERR, "项目未安装。\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require ROOT_PATH . 'thinkphp/base.php';
|
||||
\think\App::initCommon();
|
||||
|
||||
use think\Db;
|
||||
|
||||
const TEST_PASSWORD = 'Test@123456';
|
||||
const TEST_SALT = 'fxtest01';
|
||||
const TEST_PASSWORD_HASH = '2e6b7166bb58caa2a84dd3eca3391f67';
|
||||
const TEST_USER_MONEY = 10000;
|
||||
|
||||
$argv = $GLOBALS['argv'] ?? [];
|
||||
$goodsId = 0;
|
||||
$userMoney = TEST_USER_MONEY;
|
||||
$addressesOnly = in_array('--addresses-only', $argv, true);
|
||||
foreach ($argv as $arg) {
|
||||
if (strpos($arg, '--goods-id=') === 0) {
|
||||
$goodsId = intval(substr($arg, 11));
|
||||
}
|
||||
if (strpos($arg, '--money=') === 0) {
|
||||
$userMoney = max(0, floatval(substr($arg, 8)));
|
||||
}
|
||||
}
|
||||
|
||||
echo "=== 分销测试数据初始化 ===\n\n";
|
||||
|
||||
if ($addressesOnly) {
|
||||
if (!$goodsId) {
|
||||
$goodsId = intval(Db::name('shopro_commission_goods')->where('status', 1)->value('goods_id')) ?: 195;
|
||||
}
|
||||
try {
|
||||
setupTestUserMoney($userMoney);
|
||||
$area = setupTestAddresses($goodsId);
|
||||
echo "\n配送区域内地址: {$area['province_name']}{$area['city_name']}{$area['area_name']}\n";
|
||||
echo "测试用户余额: {$userMoney} 元\n";
|
||||
echo "请重新进入下单页或刷新后再提交订单。\n";
|
||||
} catch (\Throwable $e) {
|
||||
fwrite(STDERR, "失败: " . $e->getMessage() . "\n");
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
setupCommissionConfig();
|
||||
setupAgentLevels();
|
||||
setupUsersAndAgents();
|
||||
setupTestUserMoney($userMoney);
|
||||
$goodsId = setupCommissionGoods($goodsId);
|
||||
if ($goodsId) {
|
||||
setupTestAddresses($goodsId);
|
||||
}
|
||||
Db::commit();
|
||||
} catch (\Throwable $e) {
|
||||
Db::rollback();
|
||||
fwrite(STDERR, "失败: " . $e->getMessage() . "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "\n========================================\n";
|
||||
echo " 初始化完成 · 登录信息如下\n";
|
||||
echo "========================================\n\n";
|
||||
echo "小程序登录:我的 → 账号登录\n";
|
||||
echo "统一密码:" . TEST_PASSWORD . "\n";
|
||||
echo "测试余额:{$userMoney} 元(可用 --money= 自定义)\n\n";
|
||||
|
||||
$users = getTestUserDefs();
|
||||
foreach ($users as $u) {
|
||||
echo sprintf(" [%s] %s\n", $u['role'], $u['nickname']);
|
||||
echo sprintf(" 手机号: %s(推荐,账号登录填这个)\n", $u['mobile']);
|
||||
echo sprintf(" 用户名: %s\n", $u['username']);
|
||||
echo sprintf(" 用户ID: %d\n\n", $u['id']);
|
||||
}
|
||||
|
||||
echo "推荐验证账号:\n";
|
||||
echo " 首购场景 → 用【测试D-买家】19900000004 下单\n";
|
||||
echo " 自购场景 → 用【测试C-直推】19900000003 自己下单\n";
|
||||
echo " 复购场景 → D 先下一单,再下一单\n\n";
|
||||
|
||||
if ($goodsId) {
|
||||
$goods = Db::name('shopro_goods')->where('id', $goodsId)->field('id,title,price')->find();
|
||||
echo "测试商品: ID={$goodsId} {$goods['title']} 价格={$goods['price']}元\n";
|
||||
echo "(请用该商品下单,单价1000左右最易对照预期佣金)\n\n";
|
||||
$addr = Db::name('shopro_user_address')->where('user_id', 900004)->where('is_default', '1')->find();
|
||||
if ($addr) {
|
||||
echo "默认收货地址(D): {$addr['province_name']}{$addr['city_name']}{$addr['area_name']}{$addr['address']}\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "支付后验证:\n";
|
||||
echo " php addons/shopro/tests/commission_verify.php verify-item {订单明细ID}\n\n";
|
||||
|
||||
echo "关系链: A(三级) ← B(二级) ← C(直推) ← D(买家)\n";
|
||||
|
||||
function getTestUserDefs()
|
||||
{
|
||||
return [
|
||||
['id' => 900001, 'username' => 'test_agent_a', 'nickname' => '测试A-三级', 'mobile' => '19900000001', 'parent' => 0, 'role' => '三级分销商A'],
|
||||
['id' => 900002, 'username' => 'test_agent_b', 'nickname' => '测试B-二级', 'mobile' => '19900000002', 'parent' => 900001, 'role' => '二级分销商B'],
|
||||
['id' => 900003, 'username' => 'test_agent_c', 'nickname' => '测试C-直推', 'mobile' => '19900000003', 'parent' => 900002, 'role' => '一级分销商C'],
|
||||
['id' => 900004, 'username' => 'test_buyer_d', 'nickname' => '测试D-买家', 'mobile' => '19900000004', 'parent' => 900003, 'role' => '买家D'],
|
||||
];
|
||||
}
|
||||
|
||||
function setupCommissionConfig()
|
||||
{
|
||||
$pairs = [
|
||||
30 => ['chain_commission_enable', '1', 'basic', '关系链分佣开关'],
|
||||
31 => ['differential_commission_enable', '1', 'basic', '级差奖开关'],
|
||||
];
|
||||
foreach ($pairs as $id => $row) {
|
||||
$exists = Db::name('shopro_commission_config')->where('id', $id)->find();
|
||||
if ($exists) {
|
||||
Db::name('shopro_commission_config')->where('id', $id)->update(['value' => $row[1]]);
|
||||
} else {
|
||||
Db::name('shopro_commission_config')->insert([
|
||||
'id' => $id, 'name' => $row[0], 'value' => $row[1], 'group' => $row[2], 'description' => $row[3],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$updates = [
|
||||
'management_award_enable' => '1',
|
||||
'repurchase_referrer_enable' => '1',
|
||||
'repurchase_self_enable' => '1',
|
||||
'repurchase_self_type' => 'fixed',
|
||||
'repurchase_self_amount' => '5',
|
||||
'chain_commission_enable' => '1',
|
||||
'differential_commission_enable' => '1',
|
||||
'commission_level' => '3',
|
||||
'self_buy' => '1',
|
||||
'commission_price_type' => 'goods_price',
|
||||
'commission_event' => 'payed',
|
||||
'agent_check' => '0',
|
||||
];
|
||||
foreach ($updates as $name => $value) {
|
||||
Db::name('shopro_commission_config')->where('name', $name)->update(['value' => $value]);
|
||||
}
|
||||
echo "[OK] 分销配置已更新(三级、内购开、支付后结算、商品价、免审核)\n";
|
||||
}
|
||||
|
||||
function setupAgentLevels()
|
||||
{
|
||||
$levels = [
|
||||
1 => '{"commission_1":"10.00","commission_2":"0.00","commission_3":"0.00","differential_amount":"40","management_rate":"5"}',
|
||||
2 => '{"commission_1":"10.00","commission_2":"5.00","commission_3":"0.00","differential_amount":"80","management_rate":"3"}',
|
||||
3 => '{"commission_1":"10.00","commission_2":"5.00","commission_3":"3.00","differential_amount":"120","management_rate":"2"}',
|
||||
];
|
||||
foreach ($levels as $level => $rules) {
|
||||
$row = Db::name('shopro_commission_agent_level')->where('level', $level)->find();
|
||||
if ($row) {
|
||||
Db::name('shopro_commission_agent_level')->where('level', $level)->update(['commission_rules' => $rules]);
|
||||
} else {
|
||||
Db::name('shopro_commission_agent_level')->insert([
|
||||
'level' => $level,
|
||||
'name' => '测试等级' . $level,
|
||||
'image' => '',
|
||||
'commission_rules' => $rules,
|
||||
'upgrade_type' => 0,
|
||||
'upgrade_rules' => '{}',
|
||||
]);
|
||||
}
|
||||
}
|
||||
echo "[OK] 分销商等级档位已设置(L1:40/5% L2:80/3% L3:120/2%)\n";
|
||||
}
|
||||
|
||||
function setupUsersAndAgents()
|
||||
{
|
||||
$ts = time();
|
||||
$host = 'test.local';
|
||||
$agents = [
|
||||
900001 => ['level' => 3, 'parent_agent_id' => 0],
|
||||
900002 => ['level' => 2, 'parent_agent_id' => 900001],
|
||||
900003 => ['level' => 1, 'parent_agent_id' => 900002],
|
||||
];
|
||||
|
||||
foreach (getTestUserDefs() as $u) {
|
||||
$conflict = Db::name('user')->where('mobile', $u['mobile'])->where('id', '<>', $u['id'])->find();
|
||||
if ($conflict) {
|
||||
Db::name('user')->where('id', $conflict['id'])->update([
|
||||
'mobile' => $u['mobile'] . '_old' . $conflict['id'],
|
||||
'updatetime' => $ts,
|
||||
]);
|
||||
echo "[WARN] 手机号 {$u['mobile']} 曾被用户 {$conflict['id']} 占用,已改号\n";
|
||||
}
|
||||
|
||||
$exists = Db::name('user')->where('id', $u['id'])->find();
|
||||
$base = [
|
||||
'username' => $u['username'],
|
||||
'nickname' => $u['nickname'],
|
||||
'password' => TEST_PASSWORD_HASH,
|
||||
'salt' => TEST_SALT,
|
||||
'mobile' => $u['mobile'],
|
||||
'email' => $u['mobile'] . '@' . $host,
|
||||
'parent_user_id' => $u['parent'],
|
||||
'status' => 'normal',
|
||||
'updatetime' => $ts,
|
||||
'level' => 1,
|
||||
'score' => 0,
|
||||
'total_consume' => 0,
|
||||
];
|
||||
if ($exists) {
|
||||
Db::name('user')->where('id', $u['id'])->update($base);
|
||||
echo "[UPD] 用户 {$u['nickname']} ({$u['id']})\n";
|
||||
} else {
|
||||
$base['id'] = $u['id'];
|
||||
$base['createtime'] = $ts;
|
||||
$base['jointime'] = $ts;
|
||||
$base['joinip'] = '127.0.0.1';
|
||||
$base['loginip'] = '127.0.0.1';
|
||||
$base['logintime'] = $ts;
|
||||
$base['prevtime'] = $ts;
|
||||
Db::name('user')->insert($base);
|
||||
echo "[NEW] 用户 {$u['nickname']} ({$u['id']})\n";
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($agents as $userId => $agent) {
|
||||
$exists = Db::name('shopro_commission_agent')->where('user_id', $userId)->find();
|
||||
$data = [
|
||||
'level' => $agent['level'],
|
||||
'parent_agent_id' => $agent['parent_agent_id'],
|
||||
'status' => 'normal',
|
||||
'info_status' => 1,
|
||||
'becometime' => $ts,
|
||||
'updatetime' => $ts,
|
||||
];
|
||||
if ($exists) {
|
||||
Db::name('shopro_commission_agent')->where('user_id', $userId)->update($data);
|
||||
} else {
|
||||
$data['user_id'] = $userId;
|
||||
$data['createtime'] = $ts;
|
||||
Db::name('shopro_commission_agent')->insert($data);
|
||||
}
|
||||
}
|
||||
echo "[OK] 分销商 A/B/C 已就绪,D 为普通买家\n";
|
||||
}
|
||||
|
||||
function setupTestUserMoney($money)
|
||||
{
|
||||
$money = number_format(floatval($money), 2, '.', '');
|
||||
$ts = time();
|
||||
foreach (getTestUserDefs() as $u) {
|
||||
$exists = Db::name('user')->where('id', $u['id'])->find();
|
||||
if (!$exists) {
|
||||
echo "[SKIP] 用户 {$u['id']} 不存在,跳过充值\n";
|
||||
continue;
|
||||
}
|
||||
Db::name('user')->where('id', $u['id'])->update([
|
||||
'money' => $money,
|
||||
'updatetime' => $ts,
|
||||
]);
|
||||
echo "[OK] 余额 {$u['nickname']} ({$u['id']}) = {$money} 元\n";
|
||||
}
|
||||
}
|
||||
|
||||
function setupCommissionGoods($goodsId)
|
||||
{
|
||||
if (!$goodsId) {
|
||||
$row = Db::name('shopro_commission_goods')
|
||||
->alias('cg')
|
||||
->join('shopro_goods g', 'g.id = cg.goods_id')
|
||||
->where('cg.status', 1)
|
||||
->where('cg.differential_enable', 1)
|
||||
->field('cg.goods_id, g.title')
|
||||
->find();
|
||||
if ($row) {
|
||||
$goodsId = intval($row['goods_id']);
|
||||
echo "[OK] 使用已有分销商品 ID={$goodsId} {$row['title']}\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!$goodsId) {
|
||||
$row = Db::name('shopro_goods')->where('status', 'up')->order('id desc')->field('id,title,price')->find();
|
||||
if ($row) {
|
||||
$goodsId = intval($row['id']);
|
||||
echo "[INFO] 自动选用上架商品 ID={$goodsId} {$row['title']}\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!$goodsId) {
|
||||
echo "[WARN] 未找到可用商品,请手动在后台配置分销商品,或执行: --goods-id=商品ID\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
$exists = Db::name('shopro_commission_goods')->where('goods_id', $goodsId)->find();
|
||||
$data = [
|
||||
'status' => 1,
|
||||
'self_rules' => 0,
|
||||
'commission_rules' => '{}',
|
||||
'commission_config' => '',
|
||||
'commission_order_status' => 1,
|
||||
'differential_enable' => 1,
|
||||
'management_enable' => 1,
|
||||
'repurchase_enable' => 1,
|
||||
];
|
||||
if ($exists) {
|
||||
Db::name('shopro_commission_goods')->where('goods_id', $goodsId)->update($data);
|
||||
} else {
|
||||
$data['goods_id'] = $goodsId;
|
||||
Db::name('shopro_commission_goods')->insert($data);
|
||||
}
|
||||
echo "[OK] 商品 {$goodsId} 已开启:参与分销 + 级差 + 管理奖 + 复购返现\n";
|
||||
return $goodsId;
|
||||
}
|
||||
|
||||
function setupTestAddresses($goodsId)
|
||||
{
|
||||
$area = resolveDeliveryAreaForGoods($goodsId);
|
||||
if (!$area) {
|
||||
throw new \Exception('无法解析商品配送区域,请检查运费模板');
|
||||
}
|
||||
|
||||
$ts = time();
|
||||
foreach (getTestUserDefs() as $u) {
|
||||
$exists = Db::name('shopro_user_address')
|
||||
->where('user_id', $u['id'])
|
||||
->where(function ($query) {
|
||||
$query->whereNull('deletetime')->whereOr('deletetime', 0);
|
||||
})
|
||||
->order('id asc')
|
||||
->find();
|
||||
|
||||
$data = [
|
||||
'is_default' => '1',
|
||||
'consignee' => str_replace('测试', '', $u['nickname']),
|
||||
'phone' => $u['mobile'],
|
||||
'province_name' => $area['province_name'],
|
||||
'city_name' => $area['city_name'],
|
||||
'area_name' => $area['area_name'],
|
||||
'address' => '分销测试地址1号',
|
||||
'province_id' => $area['province_id'],
|
||||
'city_id' => $area['city_id'],
|
||||
'area_id' => $area['area_id'],
|
||||
'updatetime' => $ts,
|
||||
];
|
||||
|
||||
if ($exists) {
|
||||
Db::name('shopro_user_address')->where('user_id', $u['id'])->update(['is_default' => '0']);
|
||||
Db::name('shopro_user_address')->where('id', $exists['id'])->update($data);
|
||||
echo "[UPD] 地址 {$u['nickname']} -> {$area['province_name']}{$area['city_name']}{$area['area_name']}\n";
|
||||
} else {
|
||||
$data['user_id'] = $u['id'];
|
||||
$data['createtime'] = $ts;
|
||||
Db::name('shopro_user_address')->insert($data);
|
||||
echo "[NEW] 地址 {$u['nickname']} -> {$area['province_name']}{$area['city_name']}{$area['area_name']}\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "[OK] 测试用户默认收货地址已写入(与商品运费模板配送范围一致)\n";
|
||||
return $area;
|
||||
}
|
||||
|
||||
function resolveDeliveryAreaForGoods($goodsId)
|
||||
{
|
||||
$goods = Db::name('shopro_goods')->where('id', $goodsId)->field('dispatch_ids,dispatch_type')->find();
|
||||
if ($goods && strpos($goods['dispatch_type'] ?? '', 'express') !== false && !empty($goods['dispatch_ids'])) {
|
||||
$dispatchIds = array_filter(explode(',', $goods['dispatch_ids']));
|
||||
$dispatch = Db::name('shopro_dispatch')->where('type', 'express')->where('id', 'in', $dispatchIds)->find();
|
||||
if ($dispatch && !empty($dispatch['type_ids'])) {
|
||||
$expressIds = array_filter(explode(',', $dispatch['type_ids']));
|
||||
$expressList = Db::name('shopro_dispatch_express')
|
||||
->where('id', 'in', $expressIds)
|
||||
->where(function ($query) {
|
||||
$query->whereNull('deletetime')->whereOr('deletetime', 0);
|
||||
})
|
||||
->order('weigh desc,id asc')
|
||||
->select();
|
||||
foreach ($expressList as $express) {
|
||||
$area = pickAreaFromExpress($express);
|
||||
if ($area) {
|
||||
return $area;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 默认:贵州省贵阳市南明区(多数本地店铺配送范围)
|
||||
return buildAreaInfo(520102);
|
||||
}
|
||||
|
||||
function pickAreaFromExpress($express)
|
||||
{
|
||||
foreach (['area_ids', 'city_ids', 'province_ids'] as $field) {
|
||||
if (empty($express[$field])) {
|
||||
continue;
|
||||
}
|
||||
$ids = array_filter(array_map('trim', explode(',', $express[$field])));
|
||||
foreach ($ids as $id) {
|
||||
$id = intval($id);
|
||||
if ($id <= 0) {
|
||||
continue;
|
||||
}
|
||||
$row = Db::name('shopro_area')->where('id', $id)->find();
|
||||
if (!$row) {
|
||||
continue;
|
||||
}
|
||||
if (intval($row['level']) === 3) {
|
||||
$area = buildAreaInfo($id);
|
||||
if ($area) {
|
||||
return $area;
|
||||
}
|
||||
}
|
||||
if (intval($row['level']) === 2) {
|
||||
$areaRow = Db::name('shopro_area')->where('pid', $id)->where('level', 3)->order('id asc')->find();
|
||||
if ($areaRow) {
|
||||
$area = buildAreaInfo($areaRow['id']);
|
||||
if ($area) {
|
||||
return $area;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (intval($row['level']) === 1) {
|
||||
$city = Db::name('shopro_area')->where('pid', $id)->where('level', 2)->order('id asc')->find();
|
||||
if ($city) {
|
||||
$areaRow = Db::name('shopro_area')->where('pid', $city['id'])->where('level', 3)->order('id asc')->find();
|
||||
if ($areaRow) {
|
||||
$area = buildAreaInfo($areaRow['id']);
|
||||
if ($area) {
|
||||
return $area;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function buildAreaInfo($areaId)
|
||||
{
|
||||
$area = Db::name('shopro_area')->where('id', $areaId)->find();
|
||||
if (!$area || intval($area['level']) !== 3) {
|
||||
return null;
|
||||
}
|
||||
$city = Db::name('shopro_area')->where('id', $area['pid'])->find();
|
||||
if (!$city) {
|
||||
return null;
|
||||
}
|
||||
$province = Db::name('shopro_area')->where('id', $city['pid'])->find();
|
||||
if (!$province) {
|
||||
return null;
|
||||
}
|
||||
return [
|
||||
'province_id' => intval($province['id']),
|
||||
'province_name' => $province['name'],
|
||||
'city_id' => intval($city['id']),
|
||||
'city_name' => $city['name'],
|
||||
'area_id' => intval($area['id']),
|
||||
'area_name' => $area['name'],
|
||||
];
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户