mirror of
https://github.com/crmeb/CRMEB.git
synced 2026-10-28 21:40:50 +00:00
feat(积分): 收货发放购买赠送积分并按来源分族退款冲正
- 下单快照 extra_info.items 写入商品级 give_integral,收货按数量聚合发放,旧订单缺快照回查商品配置兜底 - 赠送流水区分下单族(order_give_integral)/购买族(pay_give_integral),退款冲正按两族占比拆分落库 order_gain_refund/product_gain_refund,幂等标记分族查重
This commit is contained in:
parent
6da86d4e6d
commit
4668f0a1ea
@ -2101,6 +2101,8 @@ class CenterOrderController
|
||||
'store_name' => $item['productInfo']['store_name'] ?? '',
|
||||
'price' => $item['productInfo']['price'] ?? '0.00',
|
||||
'true_price' => $item['truePrice'] ?? '0.00',
|
||||
// 商品级购买送积分快照(eb_product_center.give_integral),收货时按数量聚合发放
|
||||
'give_integral' => (float)($item['productInfo']['give_integral'] ?? 0),
|
||||
'cart_num' => $item['cart_num'] ?? 1,
|
||||
'product_type' => $item['productInfo']['product_type'] ?? '',
|
||||
'attr_info' => $item['productInfo']['attrInfo'] ?? [],
|
||||
|
||||
@ -32,7 +32,7 @@ class UserBillDao extends BaseDao
|
||||
{
|
||||
return $this->getModel()->where('link_id', $orderId)->where('uid', $uid)
|
||||
->where('category', 'integral')->where('status', 1)
|
||||
->whereIn('type', ['gain', 'product_gain', 'order_give_integral', 'pay_give_integral', 'order_deduction', 'integral_refund'])
|
||||
->whereIn('type', ['gain', 'product_gain', 'order_give_integral', 'pay_give_integral', 'order_deduction', 'integral_refund', 'order_gain_refund', 'product_gain_refund'])
|
||||
->field('type, pm, SUM(number) AS number')->group('type, pm')->select()->toArray();
|
||||
}
|
||||
|
||||
|
||||
@ -68,10 +68,16 @@ class OrderRefundRewardServices extends BaseService
|
||||
if (!$user) {
|
||||
throw new \RuntimeException('退款权益关联用户不存在');
|
||||
}
|
||||
$earned = $returned = '0';
|
||||
$earned = $returned = $earnedOrder = $earnedProduct = '0';
|
||||
foreach ($this->billDao->getOrderIntegralRewards((int)$order['id'], $uid) as $bill) {
|
||||
if ((int)$bill['pm'] === 1) {
|
||||
$earned = bcadd($earned, (string)$bill['number'], 0);
|
||||
// 下单赠送族 gain/order_give_integral,购买赠送族 product_gain/pay_give_integral
|
||||
if (in_array($bill['type'], ['gain', 'order_give_integral'], true)) {
|
||||
$earnedOrder = bcadd($earnedOrder, (string)$bill['number'], 0);
|
||||
} else {
|
||||
$earnedProduct = bcadd($earnedProduct, (string)$bill['number'], 0);
|
||||
}
|
||||
} else {
|
||||
$returned = bcadd($returned, (string)$bill['number'], 0);
|
||||
}
|
||||
@ -79,9 +85,22 @@ class OrderRefundRewardServices extends BaseService
|
||||
$integralTarget = $this->target($earned, $total, $paid, 0);
|
||||
$processed = max((int)$returned, (int)($accounted['integral'] ?? 0));
|
||||
$due = max(0, (int)bcsub($integralTarget, (string)$processed, 0));
|
||||
$deduct = min($due, max(0, (int)$user['integral']));
|
||||
if ($deduct > 0 && !$this->app->make(UserBillServices::class)->billIntegralIncome(
|
||||
'integral_refund', $uid, $deduct, (int)$order['id'], '订单退款回收赠积分:' . $order['order_no']
|
||||
// 按两族赠送占比拆分本次冲正额,分族落库(order_gain_refund / product_gain_refund)
|
||||
$dueOrder = $earned > 0 ? (int)bcdiv(bcmul((string)$due, $earnedOrder, 2), $earned, 0) : 0;
|
||||
$dueProduct = max(0, $due - $dueOrder);
|
||||
$balance = max(0, (int)$user['integral']);
|
||||
$deductOrder = min($dueOrder, $balance);
|
||||
$balance -= $deductOrder;
|
||||
$deductProduct = min($dueProduct, $balance);
|
||||
$deduct = $deductOrder + $deductProduct;
|
||||
$userBillServices = $this->app->make(UserBillServices::class);
|
||||
if ($deductOrder > 0 && !$userBillServices->billIntegralIncome(
|
||||
'order_gain_refund', $uid, $deductOrder, (int)$order['id'], '订单退款回收下单赠送积分:' . $order['order_no']
|
||||
)) {
|
||||
throw new \RuntimeException('退款赠积分冲正失败');
|
||||
}
|
||||
if ($deductProduct > 0 && !$userBillServices->billIntegralIncome(
|
||||
'product_gain_refund', $uid, $deductProduct, (int)$order['id'], '订单退款回收购买商品赠送积分:' . $order['order_no']
|
||||
)) {
|
||||
throw new \RuntimeException('退款赠积分冲正失败');
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ namespace app\services\order;
|
||||
|
||||
|
||||
use app\dao\order\OrderCenterDao;
|
||||
use app\dao\product\CenterProductDao;
|
||||
use crmeb\basic\BaseService;
|
||||
use app\services\user\member\MemberCardServices;
|
||||
use app\services\user\UserBillServices;
|
||||
@ -266,10 +267,39 @@ class OrderTakeServices extends BaseService
|
||||
}
|
||||
/** @var UserBillServices $userBillServices */
|
||||
$userBillServices = app()->make(UserBillServices::class);
|
||||
// 检查是否有赠送积分可送(中台订单无 gain_integral 实体列,缺省 0——商品级赠送快照聚合待后续补全)
|
||||
if ((int)($order['gain_integral'] ?? 0) > 0) {
|
||||
// 购买商品送积分:中台订单无 gain_integral 实体列,从 extra_info.items 的商品级
|
||||
// give_integral 快照按数量聚合(下单时由 buildOrderItemsForExtra 写入)
|
||||
$extraInfo = $order['extra_info'] ?? [];
|
||||
if (is_string($extraInfo)) {
|
||||
$extraInfo = json_decode($extraInfo, true) ?: [];
|
||||
}
|
||||
$items = $extraInfo['items'] ?? [];
|
||||
// 修复前下的旧订单快照无 give_integral 字段,回查中台商品当前配置兜底
|
||||
$missingIds = [];
|
||||
foreach ($items as $item) {
|
||||
if (!array_key_exists('give_integral', $item) && (int)($item['product_id'] ?? 0) > 0) {
|
||||
$missingIds[$item['product_id']] = true;
|
||||
}
|
||||
}
|
||||
$productGiveMap = [];
|
||||
if ($missingIds) {
|
||||
/** @var CenterProductDao $centerProductDao */
|
||||
$centerProductDao = app()->make(CenterProductDao::class);
|
||||
$productGiveMap = $centerProductDao->getColumn([['id', 'in', array_keys($missingIds)]], 'give_integral', 'id') ?: [];
|
||||
}
|
||||
$gainIntegral = '0';
|
||||
foreach ($items as $item) {
|
||||
$give = array_key_exists('give_integral', $item)
|
||||
? (float)($item['give_integral'] ?? 0)
|
||||
: (float)($productGiveMap[$item['product_id'] ?? 0] ?? 0);
|
||||
if ($give > 0) {
|
||||
$num = max(1, (int)($item['cart_num'] ?? 1));
|
||||
$gainIntegral = bcadd($gainIntegral, bcmul((string)$give, (string)$num, 0), 0);
|
||||
}
|
||||
}
|
||||
if ((float)$gainIntegral > 0) {
|
||||
// 购买商品送积分
|
||||
$res2 = $userBillServices->billIntegralIncome('pay_give_integral', $order['uid'], $order['gain_integral'], $order['id']);
|
||||
$res2 = $userBillServices->billIntegralIncome('pay_give_integral', $order['uid'], $gainIntegral, $order['id']);
|
||||
} else {
|
||||
$res2 = true;
|
||||
}
|
||||
@ -293,7 +323,7 @@ class OrderTakeServices extends BaseService
|
||||
} else {
|
||||
$res3 = true;
|
||||
}
|
||||
$give_integral = $order_integral + (int)($order['gain_integral'] ?? 0);
|
||||
$give_integral = (int)$order_integral + (int)$gainIntegral;
|
||||
if ($give_integral > 0 && $res2 && $res3) {
|
||||
//触发积分变动事件(消息发送已移至 UserIntegralChangeListener;中台无 gain_integral 列,累计值以 bill 流水为准不再回写)
|
||||
event('UserIntegralChange', [(int)$order['uid'], $order, $storeTitle, $give_integral, $order_integral]);
|
||||
|
||||
@ -137,35 +137,61 @@ class RefundExecutorServices extends BaseService
|
||||
/** @var UserBillServices $userBillServices */
|
||||
$userBillServices = app()->make(UserBillServices::class);
|
||||
|
||||
// 操作1:收回订单赠送积分(下单赠送 gain + 商品赠送 product_gain,integral_refund 已回退过则幂等跳过)
|
||||
// 操作1:分族收回订单赠送积分(下单赠送族 gain/order_give_integral、购买赠送族 product_gain/pay_give_integral)
|
||||
// 发放链路经 billIntegralIncome 落库类型为 order_give_integral / pay_give_integral,须按实际落库类型统计
|
||||
$orderGain = (int)$userBillServices->sum([
|
||||
'category' => 'integral',
|
||||
'type' => 'order_give_integral',
|
||||
'link_id' => $orderId,
|
||||
'uid' => $uid,
|
||||
], 'number') + (int)$userBillServices->sum([
|
||||
'category' => 'integral',
|
||||
'type' => 'gain',
|
||||
'link_id' => $orderId,
|
||||
'uid' => $uid,
|
||||
], 'number');
|
||||
$productGain = (int)$userBillServices->sum([
|
||||
'category' => 'integral',
|
||||
'type' => 'pay_give_integral',
|
||||
'link_id' => $orderId,
|
||||
'uid' => $uid,
|
||||
], 'number') + (int)$userBillServices->sum([
|
||||
'category' => 'integral',
|
||||
'type' => 'product_gain',
|
||||
'link_id' => $orderId,
|
||||
'uid' => $uid,
|
||||
], 'number');
|
||||
$giveIntegral = $orderGain + $productGain;
|
||||
if ($giveIntegral > 0) {
|
||||
// 幂等标记查实际落库 type:模板 'integral_refund' 落库 type 为 order_deduction
|
||||
// (type=integral_refund 是模板 'order_integral_refund' 返还使用积分的流水,语义不同)
|
||||
$count = (int)$userBillServices->count([
|
||||
if ($orderGain > 0 || $productGain > 0) {
|
||||
// 幂等标记按分族落库类型分别查重
|
||||
$orderRefunded = (int)$userBillServices->count([
|
||||
'category' => 'integral',
|
||||
'type' => 'order_deduction',
|
||||
'type' => 'order_gain_refund',
|
||||
'link_id' => $orderId,
|
||||
]);
|
||||
if (!$count) {
|
||||
// 当前积分不足时扣减全部,差额不追回
|
||||
$deductAmount = min($currentIntegral, $giveIntegral);
|
||||
$productRefunded = (int)$userBillServices->count([
|
||||
'category' => 'integral',
|
||||
'type' => 'product_gain_refund',
|
||||
'link_id' => $orderId,
|
||||
]);
|
||||
$hasRefund = false;
|
||||
// 顺序扣减:当前积分不足时扣减全部,差额不追回(总量与原合并扣减口径一致)
|
||||
if (!$orderRefunded && $orderGain > 0) {
|
||||
$deductAmount = min($currentIntegral, $orderGain);
|
||||
if ($deductAmount > 0) {
|
||||
$userBillServices->billIntegralIncome('integral_refund', $uid, $deductAmount, $orderId);
|
||||
$currentIntegral = bcsub((string)$currentIntegral, (string)$deductAmount, 2);
|
||||
$userBillServices->billIntegralIncome('order_gain_refund', $uid, $deductAmount, $orderId);
|
||||
$currentIntegral = (int)bcsub((string)$currentIntegral, (string)$deductAmount, 0);
|
||||
$hasRefund = true;
|
||||
}
|
||||
}
|
||||
if (!$productRefunded && $productGain > 0) {
|
||||
$deductAmount = min($currentIntegral, $productGain);
|
||||
if ($deductAmount > 0) {
|
||||
$userBillServices->billIntegralIncome('product_gain_refund', $uid, $deductAmount, $orderId);
|
||||
$currentIntegral = (int)bcsub((string)$currentIntegral, (string)$deductAmount, 0);
|
||||
$hasRefund = true;
|
||||
}
|
||||
}
|
||||
if ($hasRefund) {
|
||||
// 清除积分冻结
|
||||
$userBillServices->update(['link_id' => $orderId], ['frozen_time' => 0]);
|
||||
}
|
||||
|
||||
@ -97,6 +97,22 @@ class UserBillServices extends BaseService
|
||||
'status' => 1,
|
||||
'pm' => 1
|
||||
],
|
||||
'order_gain_refund' => [
|
||||
'title' => '退款返还下单赠送积分',
|
||||
'category' => self::TYPE_INTEGRAL,
|
||||
'type' => 'order_gain_refund',
|
||||
'mark' => '订单退款,返还下单赠送积分{%num%}',
|
||||
'status' => 1,
|
||||
'pm' => 0
|
||||
],
|
||||
'product_gain_refund' => [
|
||||
'title' => '退款返还购买商品赠送积分',
|
||||
'category' => self::TYPE_INTEGRAL,
|
||||
'type' => 'product_gain_refund',
|
||||
'mark' => '订单退款,返还购买商品赠送积分{%num%}',
|
||||
'status' => 1,
|
||||
'pm' => 0
|
||||
],
|
||||
'pay_product_integral_back' => [
|
||||
'title' => '商品退积分',
|
||||
'category' => self::TYPE_INTEGRAL,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user