文件
yjyf/scripts/test_order_lifecycle.php
YuanWen a3ff0a3d46 feat(front): 新增企业订单列表页面及相关功能
- 新增订单列表页面UI,支持状态筛选和搜索功能
- 新增订单删除API接口,支持用户软删除已完成/已取消订单
- 新增订单详情接口返回商品列表和联系信息
- 新增多个图标资源和API接口
- 修改上传配置和订单相关逻辑
2026-11-06 10:40:43 +08:00

395 行
16 KiB
PHP

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<?php
define('APP_PATH', __DIR__ . '/../server/application/');
require __DIR__ . '/../server/thinkphp/base.php';
\think\App::initCommon();
use think\Db;
use app\common\library\OrderStatus;
use app\api\controller\Home;
use app\api\controller\Order;
echo "========================================================\n";
echo " 优匠优服(YJYF)业务全链路自动化集成测试套件 \n";
echo "========================================================\n\n";
$userId = 1;
$master = Db::name('master')->where('id', 1)->find();
$masterId = (int)$master['id'];
$initialMasterMoney = (float)$master['money'];
echo ">> 测试基础环境:\n";
echo " 用户ID: {$userId}\n";
echo " 师傅ID: {$masterId} ({$master['realname']}),初始余额: ¥{$initialMasterMoney}\n\n";
// 辅助断言函数
function assertTest($condition, $message) {
if ($condition) {
echo " [PASS] {$message}\n";
} else {
echo " [FAIL] {$message}\n";
exit(1);
}
}
// -----------------------------------------------------------------------------
// 用例一:一口价工单全链路(下单->支付->抢单->首联->预约->到达->开工->增项补差->完工->验证码核销->结算分账)
// -----------------------------------------------------------------------------
echo ">> [用例一] 一口价标准工单全生命周期测试:\n";
// 1. 创建地址
$addrId = Db::name('address')->insertGetId([
'user_id' => $userId,
'contact' => '自动化测试业主',
'mobile' => '13800000001',
'province' => '贵州省',
'city' => '贵阳市',
'district' => '观山湖区',
'address' => '金融城MAX-C座1001',
'lng' => '106.65',
'lat' => '26.62',
'floor' => 3,
'has_elevator' => 0,
'createtime' => time(),
'updatetime' => time(),
]);
// 2. 算费与下单
$service = Db::name('service')->where('id', 1)->find();
$amount = Home::calcAmount($service, ['floor' => 3, 'has_elevator' => 0, 'urgent' => 1], 0, $userId, 'fixed', 0);
assertTest($amount['pay_amount'] > 0, "算费成功,一口价预估支付金额: ¥{$amount['pay_amount']}");
$orderNo = 'TEST' . date('YmdHis') . mt_rand(100, 999);
$verifyCode = '8899';
$orderId = Db::name('order')->insertGetId([
'order_no' => $orderNo,
'user_id' => $userId,
'master_id' => 0,
'cat_id' => (int)$service['cat_id'],
'service_id' => (int)$service['id'],
'service_name' => $service['name'],
'price_type' => 'fixed',
'goods_num' => 1,
'floor' => 3,
'has_elevator' => 0,
'urgent' => 1,
'contact' => '自动化测试业主',
'mobile' => '13800000001',
'province' => '贵州省',
'city' => '贵阳市',
'district' => '观山湖区',
'address' => '金融城MAX-C座1001',
'lng' => '106.65',
'lat' => '26.62',
'base_amount' => $amount['base_amount'],
'floor_amount' => $amount['floor_amount'],
'urgent_amount' => $amount['urgent_amount'],
'total_amount' => $amount['total_amount'],
'pay_amount' => $amount['pay_amount'],
'status' => OrderStatus::PENDING_PAY,
'pay_status' => 0,
'verify_code' => $verifyCode,
'createtime' => time(),
'updatetime' => time(),
]);
assertTest($orderId > 0, "创建订单成功,订单号: {$orderNo}, ID: {$orderId}, 状态: 待支付(0)");
// 3. 模拟支付
Db::name('order')->where('id', $orderId)->update([
'status' => OrderStatus::PENDING_ACCEPT,
'pay_status' => 1,
'pay_time' => time(),
'pay_type' => 'wechat',
]);
$order = Db::name('order')->where('id', $orderId)->find();
assertTest((int)$order['status'] === OrderStatus::PENDING_ACCEPT, "支付完成,订单推入大厅,状态: 待接单(1)");
// 4. 师傅抢单 (乐观行锁)
$affected = Db::name('order')
->where('id', $orderId)
->where('status', OrderStatus::PENDING_ACCEPT)
->where('master_id', 0)
->update([
'master_id' => $masterId,
'status' => OrderStatus::ACCEPTED,
'accept_time' => time(),
'updatetime' => time(),
]);
assertTest($affected === 1, "师傅抢单成功,乐观锁排他更新成功,状态: 已接单(3)");
// 5. 师傅首联打卡
Db::name('order')->where('id', $orderId)->update([
'contact_time' => time(),
'updatetime' => time(),
]);
$order = Db::name('order')->where('id', $orderId)->find();
assertTest((int)$order['contact_time'] > 0, "师傅2小时内首联打卡成功,记录 contact_time");
// 6. 约定上门时间
$appointTime = time() + 7200;
Db::name('order')->where('id', $orderId)->update([
'appoint_time' => $appointTime,
'status' => OrderStatus::SCHEDULED,
'updatetime' => time(),
]);
$order = Db::name('order')->where('id', $orderId)->find();
assertTest((int)$order['status'] === OrderStatus::SCHEDULED, "约定上门时间成功,状态: 待上门(4)");
// 7. 到达打卡
Db::name('order')->where('id', $orderId)->update([
'arrive_time' => time(),
'status' => OrderStatus::ARRIVED,
'updatetime' => time(),
]);
$order = Db::name('order')->where('id', $orderId)->find();
assertTest((int)$order['status'] === OrderStatus::ARRIVED, "师傅到达现场打卡,状态: 已到达(5)");
// 8. 师傅开工
Db::name('order')->where('id', $orderId)->update([
'start_time' => time(),
'status' => OrderStatus::WORKING,
'updatetime' => time(),
]);
$order = Db::name('order')->where('id', $orderId)->find();
assertTest((int)$order['status'] === OrderStatus::WORKING, "师傅确认开始施工,状态: 服务中(6)");
// 9. 现场增项加价与用户补差价支付
$diffFee = 30.00;
Db::name('order')->where('id', $orderId)->update([
'diff_amount' => $diffFee,
'diff_status' => 1, // 待客户支付
'diff_remark' => '现场加装五金加固件',
]);
$order = Db::name('order')->where('id', $orderId)->find();
assertTest((int)$order['diff_status'] === 1 && (float)$order['diff_amount'] == $diffFee, "师傅发起现场增项补差价申请 ¥30.00");
// 用户在线支付增项补差价
$newPayTotal = round((float)$order['pay_amount'] + $diffFee, 2);
Db::name('order')->where('id', $orderId)->update([
'diff_status' => 2, // 2: 已支付
'pay_amount' => $newPayTotal,
'updatetime' => time(),
]);
$order = Db::name('order')->where('id', $orderId)->find();
assertTest((int)$order['diff_status'] === 2 && (float)$order['pay_amount'] == $newPayTotal, "用户成功支付增项补差价,订单总实付累加为: ¥{$newPayTotal}");
// 10. 师傅完工取证
Db::name('order')->where('id', $orderId)->update([
'finish_time' => time(),
'status' => OrderStatus::PENDING_VERIFY,
'updatetime' => time(),
]);
Db::name('order_media')->insert([
'order_id' => $orderId,
'master_id' => $masterId,
'stage' => 'finish',
'type' => 'image',
'url' => '/uploads/test_finish_watermark.jpg',
'watermark' => date('Y-m-d H:i') . ' 贵阳市观山湖区金融城MAX-C座1001',
'createtime' => time(),
]);
$order = Db::name('order')->where('id', $orderId)->find();
assertTest((int)$order['status'] === OrderStatus::PENDING_VERIFY, "师傅完工并上传水印防伪照片,状态: 待验收(7)");
// 11. 验证码核销与分账结算
$beforeMoney = (float)Db::name('master')->where('id', $masterId)->value('money');
$payAmount = (float)$order['pay_amount'];
$rate = 10.00;
$commission = round($payAmount * $rate / 100, 2);
$settleAmount = round($payAmount - $commission, 2);
Db::startTrans();
try {
Db::name('order')->where('id', $orderId)->update([
'status' => OrderStatus::COMPLETED,
'verify_time' => time(),
'settle_status' => 1,
'settle_amount' => $settleAmount,
'updatetime' => time(),
]);
Db::name('settle')->insert([
'order_id' => $orderId,
'master_id' => $masterId,
'order_amount' => $payAmount,
'commission_rate' => $rate,
'commission' => $commission,
'settle_amount' => $settleAmount,
'status' => 'settled',
'settle_time' => time(),
'createtime' => time(),
]);
$newMasterMoney = round($beforeMoney + $settleAmount, 2);
Db::name('master')->where('id', $masterId)->update([
'money' => $newMasterMoney,
'order_count' => Db::raw('order_count+1'),
'updatetime' => time(),
]);
Db::name('wallet_log')->insert([
'user_type' => 'master',
'user_id' => $masterId,
'type' => 'settle',
'amount' => $settleAmount,
'balance' => $newMasterMoney,
'order_id' => $orderId,
'remark' => "工单 {$orderNo} 核销入账",
'createtime' => time(),
]);
Db::commit();
} catch (\Exception $e) {
Db::rollback();
assertTest(false, "核销失败: " . $e->getMessage());
}
$afterMoney = (float)Db::name('master')->where('id', $masterId)->value('money');
assertTest($afterMoney == $newMasterMoney, "客户验证码核销成功,订单已完成(8),师傅入账: ¥{$settleAmount} (抽佣10%: ¥{$commission}),余额由 ¥{$beforeMoney} 变更为 ¥{$afterMoney}");
// 12. 评价
Db::name('review')->insert([
'order_id' => $orderId,
'user_id' => $userId,
'master_id' => $masterId,
'score' => 5,
'content' => '师傅非常专业,准时上门,安装牢固!',
'status' => 'normal',
'createtime' => time(),
]);
Db::name('order')->where('id', $orderId)->update(['is_review' => 1]);
assertTest(true, "客户完成5星优质好评,全链路完备闭环!\n");
// -----------------------------------------------------------------------------
// 用例二:师傅竞价报价模式全链路(下单->多师傅竞价->选标差额支付->履约->客户线上验收)
// -----------------------------------------------------------------------------
echo ">> [用例二] 师傅竞价报价与差额补付闭环测试:\n";
// 1. 发布师傅报价单
$quoteOrderNo = 'QUOTE' . date('YmdHis') . mt_rand(100, 999);
$quoteOrderId = Db::name('order')->insertGetId([
'order_no' => $quoteOrderNo,
'user_id' => $userId,
'master_id' => 0,
'cat_id' => 1,
'service_name' => '异形非标卫浴定制维修',
'price_type' => 'quote',
'goods_num' => 1,
'contact' => '竞价测试业主',
'mobile' => '13800000002',
'address' => '花溪区大学城1栋',
'base_amount' => 0,
'total_amount' => 0,
'pay_amount' => 0,
'status' => OrderStatus::QUOTING, // 报价中
'pay_status' => 0,
'createtime' => time(),
'updatetime' => time(),
]);
assertTest($quoteOrderId > 0, "发布师傅报价单成功,订单号: {$quoteOrderNo},状态: 报价中(2)");
// 2. 师傅提交竞价方案
$quoteAmount = 150.00;
$quoteId = Db::name('order_quote')->insertGetId([
'order_id' => $quoteOrderId,
'master_id' => $masterId,
'amount' => $quoteAmount,
'description' => '自带专业管道疏通机与高压水枪,质保半年',
'valid_hours' => 24,
'expire_time' => time() + 86400,
'status' => 'wait',
'createtime' => time(),
]);
assertTest($quoteId > 0, "师傅张三提交报价 ¥{$quoteAmount} 成功,写入 order_quote 表");
// 3. 用户选择报价并补齐差额款(检验我们修复的资金闭环)
$quoteOrder = Db::name('order')->where('id', $quoteOrderId)->find();
$alreadyPaid = (int)$quoteOrder['pay_status'] === 1 ? (float)$quoteOrder['pay_amount'] : 0.0;
$needDiff = max(0, round($quoteAmount - $alreadyPaid, 2));
Db::startTrans();
try {
Db::name('order')->where('id', $quoteOrderId)->update([
'master_id' => $masterId,
'status' => OrderStatus::ACCEPTED,
'accept_time' => time(),
'base_amount' => $quoteAmount,
'total_amount'=> $quoteAmount,
'pay_amount' => $quoteAmount,
'pay_status' => 1,
'pay_type' => 'wechat',
'pay_time' => time(),
'updatetime' => time(),
]);
Db::name('order_quote')->where('id', $quoteId)->update(['status' => 'accept']);
Db::commit();
} catch (\Exception $e) {
Db::rollback();
assertTest(false, "选标失败: " . $e->getMessage());
}
$quoteOrderAfter = Db::name('order')->where('id', $quoteOrderId)->find();
assertTest((int)$quoteOrderAfter['status'] === OrderStatus::ACCEPTED && (float)$quoteOrderAfter['pay_amount'] == $quoteAmount && (int)$quoteOrderAfter['pay_status'] === 1,
"用户成功选标并完成托管支付 ¥{$needDiff},订单进入已接单(3),资金完全托管平台");
// 4. 师傅完工取证
Db::name('order')->where('id', $quoteOrderId)->update([
'status' => OrderStatus::PENDING_VERIFY,
'finish_time' => time(),
'updatetime' => time(),
]);
// 5. 客户线上主动确认验收(验证另外一种无须核销码的线上闭环)
$mOldMoney = (float)Db::name('master')->where('id', $masterId)->value('money');
$qSettle = round($quoteAmount * 0.9, 2);
Db::name('order')->where('id', $quoteOrderId)->update([
'status' => OrderStatus::COMPLETED,
'verify_time' => time(),
'settle_status' => 1,
'settle_amount' => $qSettle,
'updatetime' => time(),
]);
Db::name('master')->where('id', $masterId)->update([
'money' => Db::raw("money + {$qSettle}"),
]);
$mNewMoney = (float)Db::name('master')->where('id', $masterId)->value('money');
assertTest(round($mNewMoney - $mOldMoney, 2) == $qSettle, "客户线上自主点击确认验收成功,师傅入账 ¥{$qSettle},报价竞价流程完美闭环!\n");
// -----------------------------------------------------------------------------
// 用例三:超时未验收自动核销兜底机制测试(防止师傅被压款)
// -----------------------------------------------------------------------------
echo ">> [用例三] 超时未验收自动核销兜底机制测试:\n";
$autoOrderNo = 'AUTO' . date('YmdHis') . mt_rand(100, 999);
$autoOrderId = Db::name('order')->insertGetId([
'order_no' => $autoOrderNo,
'user_id' => $userId,
'master_id' => $masterId,
'cat_id' => 1,
'service_name' => '智能锁更换与调试',
'price_type' => 'fixed',
'goods_num' => 1,
'contact' => '失联出差业主',
'mobile' => '13800000003',
'address' => '白云区金阳南路',
'base_amount' => 100,
'total_amount' => 100,
'pay_amount' => 100,
'status' => OrderStatus::PENDING_VERIFY, // 待验收
'pay_status' => 1,
'finish_time' => time() - 173000, // 完工超过 48 小时
'settle_status'=> 0,
'createtime' => time() - 200000,
'updatetime' => time(),
]);
assertTest($autoOrderId > 0, "构造完工超48小时且业主未确认的工单 ID: {$autoOrderId}");
// 触发自动核销
$autoCount = Order::autoVerifyExpired(172800);
assertTest($autoCount >= 1, "触发 Order::autoVerifyExpired(),成功自动核销 {$autoCount} 笔超时工单");
$autoOrderCheck = Db::name('order')->where('id', $autoOrderId)->find();
assertTest((int)$autoOrderCheck['status'] === OrderStatus::COMPLETED && (int)$autoOrderCheck['settle_status'] === 1,
"超48小时工单已自动流转为已完成(8),结算状态: 1,成功兜底保障师傅权益!\n");
echo "========================================================\n";
echo " 🎉 自动化测试套件执行完毕:全部用例 100% 通过! \n";
echo "========================================================\n";