feat(partner): 调整酒道馆与区县代理日结均分池为1分钟增量结算

这个提交包含在:
chen
2026-09-18 11:30:39 +08:00
父节点 195a098101
当前提交 85947e43ae
共有 6 个文件被更改,包括 377 次插入 和 43 次删除

查看文件

@@ -108,10 +108,8 @@ class DailySettleService
// 标记订单已结算马豆
foreach ($unsettledOrders as $o) {
$ext = $o['ext'] ?? [];
if (is_string($ext)) {
$ext = json_decode($ext, true) ?: [];
}
$freshExt = \think\Db::name('shopro_order')->where('id', $o['id'])->value('ext');
$ext = is_array($freshExt) ? $freshExt : (json_decode($freshExt ?? '', true) ?: []);
$ext['madow_settled'] = 1;
$ext['madow_settle_time'] = time();
\think\Db::name('shopro_order')->where('id', $o['id'])->update([
@@ -123,7 +121,79 @@ class DailySettleService
RateService::clearCache();
}
// 3. 统计今日最新大盘并同步/创建今日 DailyReport
// 3. 酒道馆 1 分钟均分结算(提取未结算酒道馆的合格订单)
$unsettledVenueOrders = [];
$venuePool = 0;
foreach ($allQualifiedOrders as $o) {
$freshExt = \think\Db::name('shopro_order')->where('id', $o['id'])->value('ext');
$ext = is_array($freshExt) ? $freshExt : (json_decode($freshExt ?? '', true) ?: []);
if (empty($ext['venue_settled'])) {
$unsettledVenueOrders[] = $o;
$rules = PartnerConfig::getOrderEffectiveRules($o);
$vShare = isset($rules['venue_share']) && is_numeric($rules['venue_share'])
? (float) $rules['venue_share']
: (float) PartnerConfig::get('partner_venue_share_per_order', 100);
$venuePool = bcadd((string) $venuePool, (string) $vShare, 2);
}
}
$venueRes = ['pool' => 0, 'count' => 0, 'each' => '0.00'];
$settledVenueOrderIds = [];
if ((float) $venuePool > 0 && !empty($unsettledVenueOrders)) {
$settledVenueOrderIds = array_column($unsettledVenueOrders, 'id');
$windowStart = time() - (PartnerConfig::activeDays() - 1) * 86400;
$venueIds = $activeService->activeUserIds($windowStart, time(), ['is_venue' => 1]);
$venueRes = $this->cashShareMinute($venueIds, $venuePool, 'venue_share', 'venue', '酒道馆', $settledVenueOrderIds);
foreach ($unsettledVenueOrders as $o) {
$freshExt = \think\Db::name('shopro_order')->where('id', $o['id'])->value('ext');
$ext = is_array($freshExt) ? $freshExt : (json_decode($freshExt ?? '', true) ?: []);
$ext['venue_settled'] = 1;
$ext['venue_settle_time'] = time();
\think\Db::name('shopro_order')->where('id', $o['id'])->update([
'ext' => json_encode($ext, JSON_UNESCAPED_UNICODE),
]);
}
}
// 4. 区县代理 1 分钟均分结算(提取未结算区县的合格订单)
$unsettledCountyOrders = [];
$countyPool = 0;
foreach ($allQualifiedOrders as $o) {
$freshExt = \think\Db::name('shopro_order')->where('id', $o['id'])->value('ext');
$ext = is_array($freshExt) ? $freshExt : (json_decode($freshExt ?? '', true) ?: []);
if (empty($ext['county_settled'])) {
$unsettledCountyOrders[] = $o;
$rules = PartnerConfig::getOrderEffectiveRules($o);
$cShare = isset($rules['county_share']) && is_numeric($rules['county_share'])
? (float) $rules['county_share']
: (float) PartnerConfig::get('partner_county_share_per_order', 50);
$countyPool = bcadd((string) $countyPool, (string) $cShare, 2);
}
}
$countyRes = ['pool' => 0, 'count' => 0, 'each' => '0.00'];
$settledCountyOrderIds = [];
if ((float) $countyPool > 0 && !empty($unsettledCountyOrders)) {
$settledCountyOrderIds = array_column($unsettledCountyOrders, 'id');
$windowStart = time() - (PartnerConfig::activeDays() - 1) * 86400;
$countyIds = $activeService->activeUserIds($windowStart, time(), ['is_county' => 1]);
$countyRes = $this->cashShareMinute($countyIds, $countyPool, 'county_share', 'county', '区县', $settledCountyOrderIds);
foreach ($unsettledCountyOrders as $o) {
$freshExt = \think\Db::name('shopro_order')->where('id', $o['id'])->value('ext');
$ext = is_array($freshExt) ? $freshExt : (json_decode($freshExt ?? '', true) ?: []);
$ext['county_settled'] = 1;
$ext['county_settle_time'] = time();
\think\Db::name('shopro_order')->where('id', $o['id'])->update([
'ext' => json_encode($ext, JSON_UNESCAPED_UNICODE),
]);
}
}
// 5. 统计今日最新大盘并同步/创建今日 DailyReport
$todayStart = strtotime($today . ' 00:00:00');
$todayEnd = strtotime($today . ' 23:59:59');
$todayGmv = $rateService->qualifiedGmv($todayStart, $todayEnd);
@@ -145,6 +215,24 @@ class DailySettleService
->where('status', 1)
->sum('madow');
// 当日累计发放酒道馆均分总额(含直接分给酒道馆与尾差/进平台金额)
$todayVenuePool = (float) \think\Db::name('shopro_partner_reward')
->whereBetween('createtime', [$todayStart, $todayEnd])
->where('reward_type', 'venue_share')
->where('status', 1)
->sum('amount');
// 当日累计发放区县均分总额(含直接分给区县与尾差/进平台金额)
$todayCountyPool = (float) \think\Db::name('shopro_partner_reward')
->whereBetween('createtime', [$todayStart, $todayEnd])
->where('reward_type', 'county_share')
->where('status', 1)
->sum('amount');
$windowStart = time() - (PartnerConfig::activeDays() - 1) * 86400;
$venueUserCount = count($activeService->activeUserIds($windowStart, time(), ['is_venue' => 1]));
$countyUserCount = count($activeService->activeUserIds($windowStart, time(), ['is_county' => 1]));
$existingTodayReport = DailyReport::where('settle_date', $today)->find();
if ($existingTodayReport) {
$existingTodayReport->order_count = $todayOrderCount;
@@ -152,6 +240,10 @@ class DailySettleService
$existingTodayReport->gmv = $todayGmv;
$existingTodayReport->rate = $currentRate;
$existingTodayReport->madow_pool = $todayMadowPool;
$existingTodayReport->venue_pool = $todayVenuePool;
$existingTodayReport->venue_user_count = $venueUserCount;
$existingTodayReport->county_pool = $todayCountyPool;
$existingTodayReport->county_user_count = $countyUserCount;
$existingTodayReport->voucher_release_total = $todayVoucherReleased;
$existingTodayReport->updatetime = time();
$existingTodayReport->save();
@@ -164,10 +256,10 @@ class DailySettleService
'rate' => $currentRate,
'madow_pool' => $todayMadowPool,
'share_user_count' => $madowRes['count'] ?? 0,
'venue_pool' => 0,
'venue_user_count' => 0,
'county_pool' => 0,
'county_user_count' => 0,
'venue_pool' => $todayVenuePool,
'venue_user_count' => $venueUserCount,
'county_pool' => $todayCountyPool,
'county_user_count' => $countyUserCount,
'voucher_release_total' => $todayVoucherReleased,
'status' => 1,
'error_msg' => '',
@@ -180,6 +272,8 @@ class DailySettleService
$rateService->snapshot($today, $todayGmv, [
'madow_pool' => $todayMadowPool,
'share_user_count' => $madowRes['count'] ?? 0,
'venue_pool' => $todayVenuePool,
'county_pool' => $todayCountyPool,
]);
return [
@@ -189,7 +283,11 @@ class DailySettleService
'madow_settled_gmv' => (float) $madowGmv,
'madow_pool' => $madowRes['pool'],
'madow_each' => $madowRes['each'],
'settled_orders' => $settledOrderIds,
'venue_pool' => $venueRes['pool'],
'venue_each' => $venueRes['each'],
'county_pool' => $countyRes['pool'],
'county_each' => $countyRes['each'],
'settled_orders' => array_values(array_unique(array_merge($settledOrderIds, $settledVenueOrderIds, $settledCountyOrderIds))),
];
} finally {
flock($fp, LOCK_UN);
@@ -288,7 +386,8 @@ class DailySettleService
'status' => 1,
]);
if ((float) $each > 0) {
Wallet::change($userId, 'commission', $each, $type, [
$walletType = PartnerConfig::rewardWalletType();
Wallet::change($userId, $walletType, $each, $type, [
'settle_date' => $settleDate,
]);
AgentModel::where('user_id', $userId)->setInc('total_income', $each);
@@ -311,6 +410,82 @@ class DailySettleService
}
}
/**
* 临时分钟级均分分润(酒道馆 / 区县代理)
*/
public function cashShareMinute($userIds, $pool, $type, $role, $label, $orderIds = [])
{
$count = count($userIds);
$pool = round((float) $pool, 2);
if ($pool <= 0) {
return ['pool' => 0, 'count' => $count, 'each' => '0.00'];
}
$minId = (int) \think\Db::name('shopro_partner_reward')->where('order_id', '<', 0)->min('order_id');
$virtualOrderId = min(-100000, $minId - 1);
if ($count <= 0) {
PartnerRewardService::write([
'order_id' => $virtualOrderId,
'buyer_id' => 0,
'user_id' => 0,
'role' => 'system',
'reward_type' => $type,
'amount' => $pool,
'base_amount' => $pool,
'settle_date' => null,
'ext' => ['formula' => "{$label}分母为0,奖池 {$pool} 进平台", 'orders' => $orderIds],
'status' => 1,
]);
return ['pool' => $pool, 'count' => 0, 'each' => '0.00'];
}
$each = function_exists('bcdiv') ? bcdiv((string) $pool, (string) $count, 2) : number_format($pool / $count, 2, '.', '');
$distributed = 0;
foreach ($userIds as $userId) {
PartnerRewardService::write([
'order_id' => $virtualOrderId,
'buyer_id' => 0,
'user_id' => $userId,
'role' => $role,
'reward_type' => $type,
'amount' => $each,
'base_amount' => $pool,
'settle_date' => null,
'ext' => ['formula' => "{$label}奖池 {$pool} / {$count} = {$each}", 'share_user_count' => $count, 'pool' => $pool, 'orders' => $orderIds],
'status' => 1,
]);
if ((float) $each > 0) {
$walletType = PartnerConfig::rewardWalletType();
Wallet::change($userId, $walletType, $each, $type, [
'minute_settle' => 1,
'orders' => $orderIds,
]);
AgentModel::where('user_id', $userId)->setInc('total_income', $each);
}
$distributed = bcadd((string) $distributed, (string) $each, 2);
}
$tail = bcsub((string) $pool, (string) $distributed, 2);
if ((float) $tail > 0) {
PartnerRewardService::write([
'order_id' => $virtualOrderId,
'buyer_id' => 0,
'user_id' => 0,
'role' => 'system',
'reward_type' => $type,
'amount' => $tail,
'base_amount' => $pool,
'settle_date' => null,
'ext' => ['formula' => "{$label}尾差 {$tail} 进平台", 'orders' => $orderIds],
'status' => 1,
]);
}
return ['pool' => $pool, 'count' => $count, 'each' => $each];
}
public function clearDaily($settleDate)
{
(new VoucherService())->rollbackRelease($settleDate);
@@ -323,7 +498,13 @@ class DailySettleService
->select();
foreach ($cashRows as $row) {
if ((float) $row->amount > 0) {
Wallet::change($row->user_id, 'commission', -$row->amount, 'reward_back', [
$walletLog = \app\admin\model\shopro\user\WalletLog::where('user_id', $row->user_id)
->where('ext', 'like', '%"settle_date":"' . $settleDate . '"%')
->where('event', $row->reward_type)
->order('id', 'desc')
->find();
$targetWallet = ($walletLog && $walletLog->type === 'commission') ? 'commission' : PartnerConfig::rewardWalletType();
Wallet::change($row->user_id, $targetWallet, -$row->amount, 'reward_back', [
'settle_date' => $settleDate,
'rerun' => 1,
], '日结重跑扣回');

查看文件

@@ -28,6 +28,12 @@ class PartnerConfig
return (bool) self::get('partner_mode', 0);
}
public static function rewardWalletType()
{
$type = (string) self::get('partner_reward_wallet', 'money');
return in_array($type, ['money', 'commission']) ? $type : 'money';
}
public static function isEnabled()
{
$enable = self::get('partner_enable', 1);

查看文件

@@ -78,7 +78,12 @@ class PartnerRewardService
foreach ($rewards as $reward) {
if (in_array($reward->reward_type, PartnerConfig::instantTypes()) && (float) $reward->amount > 0 && $reward->user_id > 0) {
Wallet::change($reward->user_id, 'commission', -$reward->amount, 'reward_back', [
$walletLog = \app\admin\model\shopro\user\WalletLog::where('user_id', $reward->user_id)
->where('ext', 'like', '%"partner_reward_id":' . intval($reward->id) . '%')
->order('id', 'desc')
->find();
$targetWallet = ($walletLog && $walletLog->type === 'commission') ? 'commission' : PartnerConfig::rewardWalletType();
Wallet::change($reward->user_id, $targetWallet, -$reward->amount, 'reward_back', [
'partner_reward_id' => $reward->id,
'order_id' => $orderId,
], '收益中心奖励退回');
@@ -200,15 +205,45 @@ class PartnerRewardService
return $qualifiedPayFee > 0;
}
public function touchSelfOrder($userId, $time = null)
/**
* 刷新有效订单时间(自购及直推订单双向保活)
* 1. 买家本人(自购)
* 2. 买家直属推荐人(直推,严格限制一级上级,二级及以上不触发)
*/
public function touchOrderTimes($buyerId, $time = null)
{
$timestamp = $time ?: time();
if (!$buyerId) {
return;
}
// 1. 买家本人自购保活
$this->updateAgentOrderTime($buyerId, $timestamp);
// 2. 直推一级上级保活(限定自购以及直推订单都算有效)
$parentUserId = \think\Db::name('user')->where('id', $buyerId)->value('parent_user_id');
if ($parentUserId && intval($parentUserId) !== intval($buyerId)) {
$this->updateAgentOrderTime($parentUserId, $timestamp);
}
}
protected function updateAgentOrderTime($userId, $timestamp)
{
$agent = AgentModel::where('user_id', $userId)->find();
if ($agent) {
$agent->last_self_order_time = $time ?: time();
$agent->save();
$current = intval($agent->last_self_order_time ?? 0);
if ($timestamp >= $current) {
$agent->last_self_order_time = $timestamp;
$agent->save();
}
}
}
public function touchSelfOrder($userId, $time = null)
{
$this->updateAgentOrderTime($userId, $time ?: time());
}
public static function dailyOrderId($settleDate)
{
return -intval(date('Ymd', strtotime($settleDate)));
@@ -287,7 +322,8 @@ class PartnerRewardService
'status' => 1,
]);
if ($row && self::$lastWriteCreated && (float) $amount > 0 && $userId > 0 && $row->status == 1 && in_array($type, PartnerConfig::instantTypes())) {
Wallet::change($userId, 'commission', $amount, $type, [
$walletType = PartnerConfig::rewardWalletType();
Wallet::change($userId, $walletType, $amount, $type, [
'partner_reward_id' => $row->id,
'order_id' => $orderId,
]);