```
feat(commission): 添加分账成功后佣金处理功能 添加了新的方法来处理分账成功后的佣金结算: - 新增 runCommissionRewardByOrderWithoutWallet 方法用于分账成功时标记已结算 - 新增 runCommissionRewardWithoutWallet 方法处理不入余额的佣金奖励 - 避免在分账成功时重复将佣金计入用户余额 feat(regional_agent): 添加区域代理分账处理逻辑 - 新增 settleByRegionalOrderWithoutWallet 方法处理区域代理分账 - 新增 settleRewardWithoutWallet 方法处理不入钱包的奖励结算 - 完善区域代理佣金处理流程 refactor(split): 优化分账执行器逻辑 - 修改 SplitExecutor 类以支持延迟钱包结算 - 添加异常处理和事务管理 - 支持根据支付网关类型决定是否延迟钱包结算 feat(split): 实现分账完成后钱包结算机制 - 添加 SplitWalletSettlement 类来管理分账与钱包结算的协调 - 支持在分账成功后延迟钱包入账,失败时回退到钱包 - 修复汇付分账与佣金结算的时间冲突问题 refactor(hook): 调整分账钩子事件处理 - 修改 SplitHook 类的事件参数传递 - 更新各个订单状态变更事件的分账触发逻辑 - 确保分账执行时能正确获取事件类型 docs(tests): 更新分账佣金验证说明 - 更新测试文件中的注释说明 - 明确汇付/微信分账成功后的佣金处理方式 ```
这个提交包含在:
@@ -91,6 +91,79 @@ class Reward
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分账成功:标记已结算,累计收益,不入余额
|
||||||
|
*/
|
||||||
|
public function runCommissionRewardByOrderWithoutWallet($event, $commissionOrder)
|
||||||
|
{
|
||||||
|
if (is_numeric($commissionOrder)) {
|
||||||
|
$commissionOrder = OrderModel::get($commissionOrder);
|
||||||
|
}
|
||||||
|
if (!$commissionOrder) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($commissionOrder['commission_reward_status'] !== self::COMMISSION_REWARD_STATUS_WAITING) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$commissionEvent = $commissionOrder['commission_event'];
|
||||||
|
if ($commissionEvent !== $event && $event !== 'admin') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$commissionOrder->commission_reward_status = self::COMMISSION_REWARD_STATUS_ACCOUNTED;
|
||||||
|
$commissionOrder->commission_time = time();
|
||||||
|
$commissionOrder->save();
|
||||||
|
|
||||||
|
$commissionRewards = RewardModel::all([
|
||||||
|
'commission_order_id' => $commissionOrder['id'],
|
||||||
|
'status' => self::COMMISSION_REWARD_STATUS_WAITING,
|
||||||
|
]);
|
||||||
|
if (!$commissionRewards) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
foreach ($commissionRewards as $commissionReward) {
|
||||||
|
$this->runCommissionRewardWithoutWallet($event, $commissionReward);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function runCommissionRewardWithoutWallet($event, $commissionReward)
|
||||||
|
{
|
||||||
|
if (is_numeric($commissionReward)) {
|
||||||
|
$commissionReward = RewardModel::get($commissionReward);
|
||||||
|
}
|
||||||
|
if (!$commissionReward || $commissionReward->status != self::COMMISSION_REWARD_STATUS_WAITING) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($this->isRepurchaseSelfReward($commissionReward)) {
|
||||||
|
$user = UserModel::get($commissionReward->agent_id);
|
||||||
|
if (!$user) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$commissionReward->status = self::COMMISSION_REWARD_STATUS_ACCOUNTED;
|
||||||
|
$commissionReward->save();
|
||||||
|
Log::write($commissionReward->agent_id, 'reward', [
|
||||||
|
'action' => $event,
|
||||||
|
'reward' => $commissionReward,
|
||||||
|
'wallet_skipped' => 'gateway_split',
|
||||||
|
], $this->oper);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$rewardAgent = new Agent($commissionReward->agent_id);
|
||||||
|
if ($rewardAgent && $rewardAgent->isAgentAvaliable()) {
|
||||||
|
$rewardAgent->agent->setInc('total_income', $commissionReward->commission);
|
||||||
|
$commissionReward->status = self::COMMISSION_REWARD_STATUS_ACCOUNTED;
|
||||||
|
$commissionReward->save();
|
||||||
|
Log::write($rewardAgent->user->id, 'reward', [
|
||||||
|
'action' => $event,
|
||||||
|
'reward' => $commissionReward,
|
||||||
|
'wallet_skipped' => 'gateway_split',
|
||||||
|
], $this->oper);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行奖金计划,直接派发佣金
|
* 执行奖金计划,直接派发佣金
|
||||||
*
|
*
|
||||||
@@ -140,7 +213,6 @@ class Reward
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 扣除/取消 分销订单
|
* 扣除/取消 分销订单
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ use addons\shopro\model\regional_agent\Agent as AgentModel;
|
|||||||
use addons\shopro\model\regional_agent\Level as LevelModel;
|
use addons\shopro\model\regional_agent\Level as LevelModel;
|
||||||
use addons\shopro\model\regional_agent\Order as RegionalOrderModel;
|
use addons\shopro\model\regional_agent\Order as RegionalOrderModel;
|
||||||
use addons\shopro\model\regional_agent\Reward as RewardModel;
|
use addons\shopro\model\regional_agent\Reward as RewardModel;
|
||||||
|
use addons\shopro\library\split\SplitWalletSettlement;
|
||||||
|
use addons\shopro\library\split\SplitWalletSettlement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 区域代理分佣
|
* 区域代理分佣
|
||||||
@@ -291,7 +293,9 @@ class Commission
|
|||||||
$agent->setInc('order_count', 1);
|
$agent->setInc('order_count', 1);
|
||||||
|
|
||||||
if ($this->_config->getCommissionEvent() === 'payed') {
|
if ($this->_config->getCommissionEvent() === 'payed') {
|
||||||
(new Reward())->settleByRegionalOrder($regionalOrder->id, 'payed');
|
if (!SplitWalletSettlement::shouldDeferRegionalWallet($order, 'payed')) {
|
||||||
|
(new Reward())->settleByRegionalOrder($regionalOrder->id, 'payed');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $regionalOrder;
|
return $regionalOrder;
|
||||||
|
|||||||
@@ -38,6 +38,28 @@ class Reward
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function settleByRegionalOrderWithoutWallet($regionalOrderId, $event)
|
||||||
|
{
|
||||||
|
$regionalOrder = RegionalOrderModel::get($regionalOrderId);
|
||||||
|
if (!$regionalOrder || $regionalOrder['commission_reward_status'] !== self::STATUS_WAITING) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$regionalOrder->commission_reward_status = self::STATUS_ACCOUNTED;
|
||||||
|
$regionalOrder->commission_time = time();
|
||||||
|
$regionalOrder->save();
|
||||||
|
|
||||||
|
$rewards = RewardModel::where('regional_order_id', $regionalOrderId)
|
||||||
|
->where('status', self::STATUS_WAITING)
|
||||||
|
->select();
|
||||||
|
|
||||||
|
foreach ($rewards as $reward) {
|
||||||
|
$this->settleRewardWithoutWallet($reward, $event);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public function settleReward($reward, $event)
|
public function settleReward($reward, $event)
|
||||||
{
|
{
|
||||||
if ($reward['status'] !== self::STATUS_WAITING) {
|
if ($reward['status'] !== self::STATUS_WAITING) {
|
||||||
@@ -68,6 +90,28 @@ class Reward
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function settleRewardWithoutWallet($reward, $event)
|
||||||
|
{
|
||||||
|
if ($reward['status'] !== self::STATUS_WAITING) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$agent = AgentModel::get($reward['agent_id']);
|
||||||
|
if (!$agent || $agent['status'] !== AgentModel::STATUS_NORMAL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$agentUser = UserModel::get($reward['agent_user_id']);
|
||||||
|
if (!$agentUser) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$agent->setInc('total_income', $reward['commission']);
|
||||||
|
$reward->status = self::STATUS_ACCOUNTED;
|
||||||
|
$reward->save();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public function backByRegionalOrder($regionalOrderId, $event)
|
public function backByRegionalOrder($regionalOrderId, $event)
|
||||||
{
|
{
|
||||||
$regionalOrder = RegionalOrderModel::get($regionalOrderId);
|
$regionalOrder = RegionalOrderModel::get($regionalOrderId);
|
||||||
|
|||||||
@@ -22,93 +22,106 @@ class SplitExecutor
|
|||||||
|
|
||||||
public function executeByOrderId($orderId, $event = 'payed')
|
public function executeByOrderId($orderId, $event = 'payed')
|
||||||
{
|
{
|
||||||
if (!$this->config->isEnable()) {
|
$order = Order::get($orderId);
|
||||||
return false;
|
$deferWallet = $order && SplitWalletSettlement::shouldDeferForGatewaySplit($order, $event);
|
||||||
}
|
$ok = false;
|
||||||
|
|
||||||
if (class_exists('\\addons\\shopro_huifu\\library\\Bridge')) {
|
try {
|
||||||
$hookParams = [
|
if (!$this->config->isEnable()) {
|
||||||
'order_id' => $orderId,
|
return false;
|
||||||
'event' => $event,
|
}
|
||||||
];
|
|
||||||
$handled = \think\Hook::listen('shopro_split_execute', $hookParams, null, true);
|
if (class_exists('\\addons\\shopro_huifu\\library\\Bridge')) {
|
||||||
if ($handled === true) {
|
$hookParams = [
|
||||||
|
'order_id' => $orderId,
|
||||||
|
'event' => $event,
|
||||||
|
];
|
||||||
|
$handled = \think\Hook::listen('shopro_split_execute', $hookParams, null, true);
|
||||||
|
if ($handled === true) {
|
||||||
|
$ok = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$orderCheck = Order::get($orderId);
|
||||||
|
if ($orderCheck && $orderCheck['pay_type'] === 'huifu_wechat') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$exists = SplitOrder::where('order_id', $orderId)->where('status', 'in', [1, 2])->find();
|
||||||
|
if ($exists) {
|
||||||
|
$ok = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$orderCheck = Order::get($orderId);
|
$order = Order::get($orderId);
|
||||||
if ($orderCheck && $orderCheck['pay_type'] === 'huifu_wechat') {
|
if (!$order || empty($order['transaction_id'])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
$exists = SplitOrder::where('order_id', $orderId)->where('status', 'in', [1, 2])->find();
|
|
||||||
if ($exists) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$order = Order::get($orderId);
|
|
||||||
if (!$order || empty($order['transaction_id'])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$items = $this->calculator->calculate($orderId);
|
|
||||||
if (empty($items)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$outOrderNo = 'PS' . date('YmdHis') . $orderId;
|
|
||||||
$splitAmount = array_sum(array_column($items, 'amount'));
|
|
||||||
|
|
||||||
Db::startTrans();
|
|
||||||
try {
|
|
||||||
$splitOrder = SplitOrder::create([
|
|
||||||
'order_id' => $orderId,
|
|
||||||
'order_sn' => $order['order_sn'],
|
|
||||||
'transaction_id' => $order['transaction_id'],
|
|
||||||
'out_order_no' => $outOrderNo,
|
|
||||||
'total_amount' => $order['pay_fee'],
|
|
||||||
'split_amount' => $splitAmount,
|
|
||||||
'status' => 1,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$receivers = [];
|
|
||||||
foreach ($items as $item) {
|
|
||||||
SplitOrderReceiver::create([
|
|
||||||
'split_order_id' => $splitOrder->id,
|
|
||||||
'receiver_id' => $item['receiver_id'],
|
|
||||||
'role_type' => $item['role_type'],
|
|
||||||
'amount' => $item['amount'],
|
|
||||||
'description' => $item['description'],
|
|
||||||
'status' => 0,
|
|
||||||
]);
|
|
||||||
$receivers[] = [
|
|
||||||
'type' => $item['receiver_type'],
|
|
||||||
'account' => $item['receiver_account'],
|
|
||||||
'name' => $item['receiver_name'] ?? '',
|
|
||||||
'amount' => intval(round($item['amount'] * 100)),
|
|
||||||
'description' => $item['description'],
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = $this->service->createOrder([
|
$items = $this->calculator->calculate($orderId);
|
||||||
'transaction_id' => $order['transaction_id'],
|
if (empty($items)) {
|
||||||
'out_order_no' => $outOrderNo,
|
return false;
|
||||||
'receivers' => $receivers,
|
}
|
||||||
'unfreeze_unsplit' => true,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$splitOrder->wx_result = is_array($result) ? json_encode($result, JSON_UNESCAPED_UNICODE) : json_encode($result);
|
$outOrderNo = 'PS' . date('YmdHis') . $orderId;
|
||||||
$splitOrder->status = 2;
|
$splitAmount = array_sum(array_column($items, 'amount'));
|
||||||
$splitOrder->save();
|
|
||||||
|
|
||||||
SplitOrderReceiver::where('split_order_id', $splitOrder->id)->update(['status' => 1]);
|
Db::startTrans();
|
||||||
Db::commit();
|
try {
|
||||||
return true;
|
$splitOrder = SplitOrder::create([
|
||||||
} catch (\Exception $e) {
|
'order_id' => $orderId,
|
||||||
Db::rollback();
|
'order_sn' => $order['order_sn'],
|
||||||
$this->markFailed($orderId, $outOrderNo ?? ('PS' . $orderId), $order, $items ?? [], $e->getMessage());
|
'transaction_id' => $order['transaction_id'],
|
||||||
return false;
|
'out_order_no' => $outOrderNo,
|
||||||
|
'total_amount' => $order['pay_fee'],
|
||||||
|
'split_amount' => $splitAmount,
|
||||||
|
'status' => 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$receivers = [];
|
||||||
|
foreach ($items as $item) {
|
||||||
|
SplitOrderReceiver::create([
|
||||||
|
'split_order_id' => $splitOrder->id,
|
||||||
|
'receiver_id' => $item['receiver_id'],
|
||||||
|
'role_type' => $item['role_type'],
|
||||||
|
'amount' => $item['amount'],
|
||||||
|
'description' => $item['description'],
|
||||||
|
'status' => 0,
|
||||||
|
]);
|
||||||
|
$receivers[] = [
|
||||||
|
'type' => $item['receiver_type'],
|
||||||
|
'account' => $item['receiver_account'],
|
||||||
|
'name' => $item['receiver_name'] ?? '',
|
||||||
|
'amount' => intval(round($item['amount'] * 100)),
|
||||||
|
'description' => $item['description'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->service->createOrder([
|
||||||
|
'transaction_id' => $order['transaction_id'],
|
||||||
|
'out_order_no' => $outOrderNo,
|
||||||
|
'receivers' => $receivers,
|
||||||
|
'unfreeze_unsplit' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$splitOrder->wx_result = is_array($result) ? json_encode($result, JSON_UNESCAPED_UNICODE) : json_encode($result);
|
||||||
|
$splitOrder->status = 2;
|
||||||
|
$splitOrder->save();
|
||||||
|
|
||||||
|
SplitOrderReceiver::where('split_order_id', $splitOrder->id)->update(['status' => 1]);
|
||||||
|
Db::commit();
|
||||||
|
$ok = true;
|
||||||
|
return true;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->markFailed($orderId, $outOrderNo ?? ('PS' . $orderId), $order, $items ?? [], $e->getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if ($deferWallet) {
|
||||||
|
SplitWalletSettlement::finalizeAfterSplitAttempt($orderId, $event, $ok);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace addons\shopro\library\split;
|
||||||
|
|
||||||
|
use addons\shopro\library\commission\Config as CommissionConfig;
|
||||||
|
use addons\shopro\library\commission\Reward as CommissionRewardLibrary;
|
||||||
|
use addons\shopro\library\regional_agent\Config as RegionalConfig;
|
||||||
|
use addons\shopro\library\regional_agent\Reward as RegionalRewardLibrary;
|
||||||
|
use addons\shopro\model\Order;
|
||||||
|
use addons\shopro\model\commission\Order as CommissionOrderModel;
|
||||||
|
use addons\shopro\model\regional_agent\Order as RegionalOrderModel;
|
||||||
|
use addons\shopro\model\split\SplitOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付通道分账与钱包佣金互斥:分账成功则不入余额,失败则回退入余额
|
||||||
|
*/
|
||||||
|
class SplitWalletSettlement
|
||||||
|
{
|
||||||
|
public static function shouldDeferCommissionWallet($order, $event)
|
||||||
|
{
|
||||||
|
if (!self::shouldDeferForGatewaySplit($order, $event)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$commissionEvent = (new CommissionConfig())->getCommissionEvent();
|
||||||
|
return $commissionEvent === $event;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function shouldDeferRegionalWallet($order, $event)
|
||||||
|
{
|
||||||
|
if (!self::shouldDeferForGatewaySplit($order, $event)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$regionalEvent = (new RegionalConfig())->getCommissionEvent();
|
||||||
|
return $regionalEvent === $event;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前订单是否应等分账结果再决定钱包入账
|
||||||
|
*/
|
||||||
|
public static function shouldDeferForGatewaySplit($order, $event)
|
||||||
|
{
|
||||||
|
if (is_numeric($order)) {
|
||||||
|
$order = Order::get($order);
|
||||||
|
}
|
||||||
|
if (!$order) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = new Config();
|
||||||
|
if (!$config->isEnable() || !$config->shouldSkipWallet()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($config->getTiming() !== $event) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (class_exists('\\addons\\shopro_huifu\\library\\Bridge')
|
||||||
|
&& \addons\shopro_huifu\library\Bridge::shouldHandleSplit($order)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array($config->getMode(), ['wechat', 'both'], true) && $order['pay_type'] === 'wechat') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isSplitSuccessful($orderId)
|
||||||
|
{
|
||||||
|
return SplitOrder::where('order_id', $orderId)
|
||||||
|
->where('status', 'in', [2, 3])
|
||||||
|
->count() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分账尝试结束后结算佣金(成功=仅记账不入钱包,失败=入钱包)
|
||||||
|
*/
|
||||||
|
public static function finalizeAfterSplitAttempt($orderId, $event, $splitExecutedOk = false)
|
||||||
|
{
|
||||||
|
if (!self::shouldDeferForGatewaySplit($orderId, $event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::isSplitSuccessful($orderId)) {
|
||||||
|
self::settleOrderWithoutWallet($orderId, $event);
|
||||||
|
} else {
|
||||||
|
self::settleOrderToWallet($orderId, $event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function settleOrderWithoutWallet($orderId, $event)
|
||||||
|
{
|
||||||
|
$commissionLib = new CommissionRewardLibrary();
|
||||||
|
$commissionOrders = CommissionOrderModel::where('order_id', $orderId)
|
||||||
|
->where('commission_reward_status', CommissionRewardLibrary::COMMISSION_REWARD_STATUS_WAITING)
|
||||||
|
->select();
|
||||||
|
foreach ($commissionOrders as $commissionOrder) {
|
||||||
|
if ($commissionOrder['commission_event'] === $event || $event === 'admin') {
|
||||||
|
$commissionLib->runCommissionRewardByOrderWithoutWallet($event, $commissionOrder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$regionalLib = new RegionalRewardLibrary();
|
||||||
|
$regionalOrders = RegionalOrderModel::where('order_id', $orderId)
|
||||||
|
->where('commission_reward_status', RegionalRewardLibrary::STATUS_WAITING)
|
||||||
|
->select();
|
||||||
|
foreach ($regionalOrders as $regionalOrder) {
|
||||||
|
if ($regionalOrder['commission_event'] === $event) {
|
||||||
|
$regionalLib->settleByRegionalOrderWithoutWallet($regionalOrder->id, $event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function settleOrderToWallet($orderId, $event)
|
||||||
|
{
|
||||||
|
$commissionLib = new CommissionRewardLibrary();
|
||||||
|
$commissionOrders = CommissionOrderModel::where('order_id', $orderId)
|
||||||
|
->where('commission_reward_status', CommissionRewardLibrary::COMMISSION_REWARD_STATUS_WAITING)
|
||||||
|
->select();
|
||||||
|
foreach ($commissionOrders as $commissionOrder) {
|
||||||
|
if ($commissionOrder['commission_event'] === $event || $event === 'admin') {
|
||||||
|
$commissionLib->runCommissionRewardByOrder($event, $commissionOrder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$regionalLib = new RegionalRewardLibrary();
|
||||||
|
$regionalOrders = RegionalOrderModel::where('order_id', $orderId)
|
||||||
|
->where('commission_reward_status', RegionalRewardLibrary::STATUS_WAITING)
|
||||||
|
->select();
|
||||||
|
foreach ($regionalOrders as $regionalOrder) {
|
||||||
|
if ($regionalOrder['commission_event'] === $event) {
|
||||||
|
$regionalLib->settleByRegionalOrder($regionalOrder->id, $event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ namespace addons\shopro\listener\commission;
|
|||||||
use addons\shopro\library\commission\Agent as AgentLibrary;
|
use addons\shopro\library\commission\Agent as AgentLibrary;
|
||||||
use addons\shopro\library\commission\Commission as CommissionLibrary;
|
use addons\shopro\library\commission\Commission as CommissionLibrary;
|
||||||
use addons\shopro\library\commission\Reward as RewardLibrary;
|
use addons\shopro\library\commission\Reward as RewardLibrary;
|
||||||
|
use addons\shopro\library\split\SplitWalletSettlement;
|
||||||
use addons\shopro\model\Order;
|
use addons\shopro\model\Order;
|
||||||
use addons\shopro\model\OrderItem;
|
use addons\shopro\model\OrderItem;
|
||||||
|
|
||||||
@@ -48,8 +49,10 @@ class CommissionHook
|
|||||||
$commissionOrder = $commission->addCommissionOrder();
|
$commissionOrder = $commission->addCommissionOrder();
|
||||||
// 执行奖金计划
|
// 执行奖金计划
|
||||||
$commission->runCommissionPlan($commissionOrder);
|
$commission->runCommissionPlan($commissionOrder);
|
||||||
// 支付后拨款
|
// 支付后拨款(汇付分账同事件时延至分账结果后再结算)
|
||||||
(new RewardLibrary())->runCommissionRewardByOrder('payed', $commissionOrder->id);
|
if (!SplitWalletSettlement::shouldDeferCommissionWallet($order, 'payed')) {
|
||||||
|
(new RewardLibrary())->runCommissionRewardByOrder('payed', $commissionOrder->id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ namespace addons\shopro\listener\split;
|
|||||||
|
|
||||||
use addons\shopro\library\split\Config;
|
use addons\shopro\library\split\Config;
|
||||||
use addons\shopro\library\split\SplitExecutor;
|
use addons\shopro\library\split\SplitExecutor;
|
||||||
|
|
||||||
class SplitHook
|
class SplitHook
|
||||||
{
|
{
|
||||||
protected function shouldRun($event)
|
protected function shouldRun($event)
|
||||||
@@ -21,7 +20,7 @@ class SplitHook
|
|||||||
if (!$this->shouldRun('payed')) {
|
if (!$this->shouldRun('payed')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->runSplit($params);
|
$this->runSplit($params, 'payed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function orderConfirmAfter(&$params)
|
public function orderConfirmAfter(&$params)
|
||||||
@@ -29,7 +28,7 @@ class SplitHook
|
|||||||
if (!$this->shouldRun('confirm')) {
|
if (!$this->shouldRun('confirm')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->runSplit($params);
|
$this->runSplit($params, 'confirm');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function orderFinish(&$params)
|
public function orderFinish(&$params)
|
||||||
@@ -37,7 +36,7 @@ class SplitHook
|
|||||||
if (!$this->shouldRun('finish')) {
|
if (!$this->shouldRun('finish')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->runSplit($params);
|
$this->runSplit($params, 'finish');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function orderRefundAfter(&$params)
|
public function orderRefundAfter(&$params)
|
||||||
@@ -52,12 +51,12 @@ class SplitHook
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function runSplit($params)
|
protected function runSplit($params, $event)
|
||||||
{
|
{
|
||||||
$order = $params['order'] ?? null;
|
$order = $params['order'] ?? null;
|
||||||
if (!$order) {
|
if (!$order) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
(new SplitExecutor())->executeByOrderId($order['id']);
|
(new SplitExecutor())->executeByOrderId($order['id'], $event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ function printSummary()
|
|||||||
echo " · 网关分账金额来自 commission/regional reward 或 split_rule\n";
|
echo " · 网关分账金额来自 commission/regional reward 或 split_rule\n";
|
||||||
echo " · 汇付:推广员/区域 huifu_id 收 div_amt,余款进 platform_huifu_id 或主商户 huifu_id\n";
|
echo " · 汇付:推广员/区域 huifu_id 收 div_amt,余款进 platform_huifu_id 或主商户 huifu_id\n";
|
||||||
echo " · 业务佣金结算仍写 shopro_user_wallet_log(commission_income / regional_agent_income)\n";
|
echo " · 业务佣金结算仍写 shopro_user_wallet_log(commission_income / regional_agent_income)\n";
|
||||||
echo " · Config::shouldSkipWallet() 未接入 Reward,分账开启时仍可能同时入账钱包 → 用 anomalies 命令排查\n";
|
echo " · 汇付/微信分账成功:佣金标记已结算且不入 user.money;分账失败则回退入钱包\n";
|
||||||
|
|
||||||
$since = time() - 90 * 86400;
|
$since = time() - 90 * 86400;
|
||||||
$stats = Db::name('shopro_split_order')->alias('so')
|
$stats = Db::name('shopro_split_order')->alias('so')
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户