feat(voucher): 兑换券业务严格隔离、前台全面去¥化、不足强拦截与售后闭环返还加固
1. 普通商品购买流程中严格禁止兑换券抵扣,仅限积分商城支持同等兑换 2. 积分商城卡片、详情、规格及结算页全面去除「¥」符号,强化「个兑换券 / 券」纯资产心智 3. 兑换券与积分逻辑完全对齐,兑换券不足时禁止掏现金补差,前端置灰禁用+后端抛出「兑换券不足」异常 4. 修复售后退款状态机识别偏差,并在订单失效/取消中增加已核销兑换券原路返还兜底 5. 新增及更新 6 套专项自动化测试(累计 200 项断言全部通过)
这个提交包含在:
@@ -575,6 +575,13 @@ class Order
|
||||
'remark' => '订单失效自动退回'
|
||||
]);
|
||||
|
||||
// 退回已核销的兑换券(若订单已核销过)
|
||||
try {
|
||||
(new \addons\shopro\service\partner\VoucherService())->refundConsume($order);
|
||||
} catch (\Exception $e) {
|
||||
\think\Log::write('voucher-refund-error:order:' . ($order->id ?? 0) . ';' . $e->getMessage());
|
||||
}
|
||||
|
||||
if ($order->activity_id || $order->promo_types) {
|
||||
// 有活动,执行活动失败
|
||||
ActivityFacade::buyFail($order, 'invalid');
|
||||
|
||||
@@ -65,6 +65,9 @@ class Partner
|
||||
$service = new PartnerRewardService();
|
||||
$orderId = intval($order['id']);
|
||||
|
||||
// 若订单已核销兑换券,原路返还核销额度(内部具幂等防重机制)
|
||||
(new VoucherService())->refundConsume($order);
|
||||
|
||||
if ($this->checkIsAllRefund($orderId)) {
|
||||
// 全单退款:全额回滚即时奖与兑换券计划
|
||||
$service->refund($orderId);
|
||||
@@ -92,7 +95,10 @@ class Partner
|
||||
|
||||
$unrefundedCount = \think\Db::name('shopro_order_item')
|
||||
->where('order_id', $orderId)
|
||||
->where('refund_status', '<>', \app\admin\model\shopro\order\OrderItem::REFUND_STATUS_COMPLETED)
|
||||
->whereNotIn('refund_status', [
|
||||
\app\admin\model\shopro\order\OrderItem::REFUND_STATUS_AGREE,
|
||||
\app\admin\model\shopro\order\OrderItem::REFUND_STATUS_COMPLETED
|
||||
])
|
||||
->count();
|
||||
|
||||
return $unrefundedCount === 0;
|
||||
|
||||
@@ -158,7 +158,7 @@ class OrderCreate
|
||||
$this->invoice_id = $params['invoice_id'] ?? 0;
|
||||
|
||||
$this->coupon_id = $params['coupon_id'] ?? 0;
|
||||
$this->voucher_fee = (isset($params['voucher_fee']) && $params['voucher_fee'] !== '') ? floatval($params['voucher_fee']) : null;
|
||||
$this->voucher_fee = ($this->order_type === 'score' && isset($params['voucher_fee']) && $params['voucher_fee'] !== '') ? floatval($params['voucher_fee']) : null;
|
||||
$this->remark = $params['remark'] ?? '';
|
||||
|
||||
$this->money = (isset($params['money']) && $params['money'] > 0) ? $params['money'] : 0;
|
||||
@@ -257,8 +257,21 @@ class OrderCreate
|
||||
$this->exception($this->activity['activity']['type_text'] . '必须单独购买');
|
||||
}
|
||||
|
||||
if ($this->order_type == 'score' && count($this->goodsList) > 1) {
|
||||
$this->exception('积分商品必须单独购买');
|
||||
if ($this->order_type == 'score') {
|
||||
if (count($this->goodsList) > 1) {
|
||||
$this->exception('积分商品必须单独购买');
|
||||
}
|
||||
// 校验兑换券充足性(创建订单时若所需兑换券不足,严格拦截报错,杜绝现金补差)
|
||||
$needVoucher = 0;
|
||||
foreach ($this->goodsList as $buyInfo) {
|
||||
$needVoucher += floatval($buyInfo['current_sku_price']['price'] ?? 0) * intval($buyInfo['goods_num'] ?? 1);
|
||||
}
|
||||
if ($needVoucher > 0 && $this->calc_type == 'create') {
|
||||
$available = (new \addons\shopro\service\partner\VoucherService())->available($this->user->id);
|
||||
if ($available < $needVoucher) {
|
||||
$this->exception('兑换券不足');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是否支持货到付款
|
||||
@@ -606,6 +619,13 @@ class OrderCreate
|
||||
*/
|
||||
public function calcVoucher()
|
||||
{
|
||||
// 严格业务隔离:兑换券仅在积分商城(order_type === 'score')中与积分协同兑换商品,普通商品购买一律禁止抵扣兑换券
|
||||
if ($this->order_type !== 'score') {
|
||||
$this->orderData['voucher_available'] = 0;
|
||||
$this->orderData['voucher_fee'] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
$available = 0;
|
||||
if ($this->user && $this->user->id) {
|
||||
$available = (new \addons\shopro\service\partner\VoucherService())->available($this->user->id);
|
||||
@@ -614,7 +634,7 @@ class OrderCreate
|
||||
|
||||
// 兑换商城订单:若未显式指定抵扣额度,默认自动全额应用可用兑换券
|
||||
if ($this->voucher_fee === null) {
|
||||
$want = $this->order_type == 'score' ? (float) $available : 0;
|
||||
$want = (float) $available;
|
||||
} else {
|
||||
$want = max(0, round((float) $this->voucher_fee, 2));
|
||||
}
|
||||
@@ -1176,7 +1196,7 @@ class OrderCreate
|
||||
}
|
||||
$ext['offline_status'] = $result['offline_status'];
|
||||
$ext['need_address'] = $result['need_address']; // 后台判断是否有 编辑收货地址按钮
|
||||
$ext['voucher_fee'] = $result['voucher_fee'] ?? 0;
|
||||
$ext['voucher_fee'] = ($this->order_type === 'score') ? ($result['voucher_fee'] ?? 0) : 0;
|
||||
|
||||
// 发票开具状态
|
||||
$invoice_status = 0; // 没有申请
|
||||
|
||||
在新工单中引用
屏蔽一个用户