1.优化分红方式
This commit is contained in:
@@ -21,7 +21,7 @@ class Admin extends Backend
|
||||
/**
|
||||
* 分红比例余量查询(表单提示用)
|
||||
*/
|
||||
protected array $noNeedPermission = ['commissionShareRemainder'];
|
||||
protected array $noNeedPermission = ['commissionShareRemainder', 'groupMeta'];
|
||||
|
||||
protected ?object $model = null;
|
||||
|
||||
@@ -107,11 +107,38 @@ class Admin extends Backend
|
||||
|
||||
$parentAdminId = intval($request->get('parent_admin_id', 0));
|
||||
$excludeId = intval($request->get('exclude_id', 0));
|
||||
$isTopLevelGroup = ($request->get('is_top_level') ?? $request->post('is_top_level')) === '1'
|
||||
|| ($request->get('is_top_level') ?? $request->post('is_top_level')) === 1
|
||||
|| ($request->get('is_top_level') ?? $request->post('is_top_level')) === true;
|
||||
$channelId = intval($request->get('channel_id', 0));
|
||||
|
||||
if ($isTopLevelGroup) {
|
||||
if ($channelId <= 0) {
|
||||
return $this->success('', [
|
||||
'used_rate' => '0.00',
|
||||
'remaining_rate' => '100.00',
|
||||
'parent_has_no_share' => false,
|
||||
'is_top_level' => true,
|
||||
]);
|
||||
}
|
||||
$stats = AdminCommissionDistributionService::getChannelRootShareRemainder(
|
||||
$channelId,
|
||||
$excludeId > 0 ? $excludeId : null
|
||||
);
|
||||
return $this->success('', [
|
||||
'used_rate' => $stats['used_rate'],
|
||||
'remaining_rate' => $stats['remaining_rate'],
|
||||
'parent_has_no_share' => bccomp($stats['remaining_rate'], '0', 2) <= 0,
|
||||
'is_top_level' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
if ($parentAdminId <= 0) {
|
||||
return $this->success('', [
|
||||
'used_rate' => '0.00',
|
||||
'remaining_rate' => '100.00',
|
||||
'parent_has_no_share' => false,
|
||||
'is_top_level' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -128,6 +155,43 @@ class Admin extends Backend
|
||||
'used_rate' => $stats['used_rate'],
|
||||
'remaining_rate' => $stats['remaining_rate'],
|
||||
'parent_has_no_share' => bccomp($stats['remaining_rate'], '0', 2) <= 0,
|
||||
'is_top_level' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询角色组是否为顶级(pid=0),供管理员表单联动
|
||||
*/
|
||||
public function groupMeta(Request $request): Response
|
||||
{
|
||||
$response = $this->initializeBackend($request);
|
||||
if ($response !== null) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$groupId = intval($request->get('group_id', 0));
|
||||
if ($groupId <= 0) {
|
||||
return $this->error(__('Invalid parameters'));
|
||||
}
|
||||
|
||||
$group = Db::name('admin_group')->where('id', $groupId)->field(['id', 'pid', 'channel_id'])->find();
|
||||
if (!is_array($group)) {
|
||||
return $this->error(__('Record not found'));
|
||||
}
|
||||
|
||||
if (!$this->auth->isSuperAdmin()) {
|
||||
$authGroups = $this->getManageableGroupIds();
|
||||
if (!in_array($groupId, $authGroups, true)) {
|
||||
return $this->error(__('You have no permission'));
|
||||
}
|
||||
}
|
||||
|
||||
$pid = intval($group['pid'] ?? 0);
|
||||
|
||||
return $this->success('', [
|
||||
'is_top_level' => $pid === 0,
|
||||
'pid' => $pid,
|
||||
'channel_id' => $group['channel_id'] ?? null,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -272,7 +336,7 @@ class Admin extends Backend
|
||||
}
|
||||
}
|
||||
|
||||
$parentErr = $this->normalizeParentAndShareFields($data, null);
|
||||
$parentErr = $this->normalizeParentAndShareFields($data, null, $data['group_arr'] ?? []);
|
||||
if ($parentErr !== null) {
|
||||
return $this->error($parentErr);
|
||||
}
|
||||
@@ -429,7 +493,7 @@ class Admin extends Backend
|
||||
if (!$this->auth->isSuperAdmin()) {
|
||||
unset($data['parent_admin_id']);
|
||||
}
|
||||
$parentErr = $this->normalizeParentAndShareFields($data, intval($id));
|
||||
$parentErr = $this->normalizeParentAndShareFields($data, intval($id), $editGroupArr ?? []);
|
||||
if ($parentErr !== null) {
|
||||
return $this->error($parentErr);
|
||||
}
|
||||
@@ -463,8 +527,18 @@ class Admin extends Backend
|
||||
$rowData = $row->toArray();
|
||||
$enriched = $this->enrichAdminRows([$rowData]);
|
||||
$rowData = $enriched[0] ?? $rowData;
|
||||
$groupArr = is_array($rowData['group_arr'] ?? null) ? $rowData['group_arr'] : [];
|
||||
$rowData['primary_group_is_top_level'] = $this->isPrimaryGroupTopLevel($groupArr);
|
||||
$parentId = intval($rowData['parent_admin_id'] ?? 0);
|
||||
if ($parentId > 0) {
|
||||
if ($rowData['primary_group_is_top_level']) {
|
||||
$channelId = intval($rowData['channel_id'] ?? 0);
|
||||
if ($channelId > 0) {
|
||||
$rowData['root_share_remainder'] = AdminCommissionDistributionService::getChannelRootShareRemainder(
|
||||
$channelId,
|
||||
intval($id)
|
||||
);
|
||||
}
|
||||
} elseif ($parentId > 0) {
|
||||
$remainder = AdminCommissionDistributionService::getShareRemainder($parentId, intval($id));
|
||||
$rowData['share_remainder'] = $remainder;
|
||||
}
|
||||
@@ -594,10 +668,51 @@ class Admin extends Backend
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
* @param array<int|string> $groupIds
|
||||
*/
|
||||
private function normalizeParentAndShareFields(array &$data, ?int $editAdminId): ?string
|
||||
private function isPrimaryGroupTopLevel(array $groupIds): bool
|
||||
{
|
||||
if ($groupIds === []) {
|
||||
return false;
|
||||
}
|
||||
$gid = intval($groupIds[0]);
|
||||
if ($gid <= 0) {
|
||||
return false;
|
||||
}
|
||||
$pid = Db::name('admin_group')->where('id', $gid)->value('pid');
|
||||
|
||||
return intval($pid ?? 0) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
* @param array<int|string> $groupIds
|
||||
*/
|
||||
private function normalizeParentAndShareFields(array &$data, ?int $editAdminId, array $groupIds = []): ?string
|
||||
{
|
||||
if ($this->isPrimaryGroupTopLevel($groupIds)) {
|
||||
$data['parent_admin_id'] = null;
|
||||
$channelId = $data['channel_id'] ?? null;
|
||||
if ($channelId === null || $channelId === '') {
|
||||
$channelId = $this->resolveChannelIdFromPrimaryGroup($groupIds);
|
||||
if ($channelId !== null && $channelId !== '') {
|
||||
$data['channel_id'] = $channelId;
|
||||
}
|
||||
}
|
||||
$channelIdInt = intval($channelId ?? 0);
|
||||
$shareErr = AdminCommissionDistributionService::validateChannelRootCommissionShareRate(
|
||||
$channelIdInt,
|
||||
$data['commission_share_rate'] ?? null,
|
||||
$editAdminId
|
||||
);
|
||||
if ($shareErr !== null) {
|
||||
return $shareErr;
|
||||
}
|
||||
$data['commission_share_rate'] = bcadd(strval($data['commission_share_rate'] ?? '0'), '0', 2);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$parentId = isset($data['parent_admin_id']) && $data['parent_admin_id'] !== '' && $data['parent_admin_id'] !== null
|
||||
? intval($data['parent_admin_id'])
|
||||
: 0;
|
||||
@@ -608,6 +723,7 @@ class Admin extends Backend
|
||||
if ($parentId <= 0) {
|
||||
$data['parent_admin_id'] = null;
|
||||
$data['commission_share_rate'] = null;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,9 @@ return [
|
||||
'Sub-agent commission share rate is required' => 'Commission share rate from parent is required for sub-agents',
|
||||
'Commission share rate must be between 0 and 100' => 'Commission share rate must be between 0 and 100',
|
||||
'Sum of sibling commission share rates cannot exceed 100%' => 'Sum of sibling commission share rates cannot exceed 100%',
|
||||
'Sum of channel top-level commission share rates cannot exceed 100%' => 'Sum of channel top-level commission share rates cannot exceed 100%',
|
||||
'Top-level agent commission share rate is required' => 'Commission share rate from channel is required for top-level role group administrators',
|
||||
'Channel is required for top-level agent commission share' => 'Channel must be set before configuring top-level commission share rate',
|
||||
'Parent administrator is required for sub-agent' => 'Parent administrator is required for sub-agent',
|
||||
'Invalid parent administrator' => 'Invalid parent administrator',
|
||||
'Parent administrator must belong to the same channel' => 'Parent administrator must belong to the same channel',
|
||||
|
||||
@@ -10,6 +10,9 @@ return [
|
||||
'Sub-agent commission share rate is required' => '子代理须填写从上级分红中抽取的比例',
|
||||
'Commission share rate must be between 0 and 100' => '分红比例须在0到100之间',
|
||||
'Sum of sibling commission share rates cannot exceed 100%' => '同上级下各子代理分红比例合计不能超过100%',
|
||||
'Sum of channel top-level commission share rates cannot exceed 100%' => '同渠道顶级代理分红比例合计不能超过100%',
|
||||
'Top-level agent commission share rate is required' => '顶级角色组管理员须填写从渠道分红中分得的比例',
|
||||
'Channel is required for top-level agent commission share' => '顶级角色组管理员须先绑定渠道后再设置分红比例',
|
||||
'Parent administrator is required for sub-agent' => '子代理须绑定上级管理员',
|
||||
'Invalid parent administrator' => '上级管理员无效',
|
||||
'Parent administrator must belong to the same channel' => '上级管理员须与当前渠道一致',
|
||||
|
||||
@@ -81,6 +81,57 @@ class AdminCommissionDistributionService
|
||||
return ['used_rate' => $used, 'remaining_rate' => $remaining];
|
||||
}
|
||||
|
||||
/**
|
||||
* 同渠道下顶级代理(无上级)已占用的渠道分红比例
|
||||
*
|
||||
* @return array{used_rate:string,remaining_rate:string}
|
||||
*/
|
||||
public static function getChannelRootShareRemainder(int $channelId, ?int $excludeAdminId = null): array
|
||||
{
|
||||
$used = '0.00';
|
||||
if ($channelId <= 0) {
|
||||
return ['used_rate' => $used, 'remaining_rate' => '100.00'];
|
||||
}
|
||||
$query = Db::name('admin')
|
||||
->where('channel_id', $channelId)
|
||||
->where('status', 'enable')
|
||||
->whereRaw('(parent_admin_id IS NULL OR parent_admin_id = 0)');
|
||||
if ($excludeAdminId !== null && $excludeAdminId > 0) {
|
||||
$query->where('id', '<>', $excludeAdminId);
|
||||
}
|
||||
$rows = $query->column('commission_share_rate');
|
||||
foreach ($rows as $rate) {
|
||||
if ($rate === null || $rate === '') {
|
||||
continue;
|
||||
}
|
||||
$used = bcadd($used, bcadd(strval($rate), '0', 2), 2);
|
||||
}
|
||||
$remaining = bcsub('100.00', $used, 2);
|
||||
if (bccomp($remaining, '0', 2) < 0) {
|
||||
$remaining = '0.00';
|
||||
}
|
||||
return ['used_rate' => $used, 'remaining_rate' => $remaining];
|
||||
}
|
||||
|
||||
public static function validateChannelRootCommissionShareRate(int $channelId, mixed $rateRaw, ?int $excludeAdminId = null): ?string
|
||||
{
|
||||
if ($channelId <= 0) {
|
||||
return (string) __('Channel is required for top-level agent commission share');
|
||||
}
|
||||
if ($rateRaw === null || $rateRaw === '') {
|
||||
return (string) __('Top-level agent commission share rate is required');
|
||||
}
|
||||
$rate = bcadd(strval($rateRaw), '0', 2);
|
||||
if (bccomp($rate, '0', 2) <= 0 || bccomp($rate, '100', 2) > 0) {
|
||||
return (string) __('Commission share rate must be between 0 and 100');
|
||||
}
|
||||
$remainder = self::getChannelRootShareRemainder($channelId, $excludeAdminId);
|
||||
if (bccomp(bcadd($remainder['used_rate'], $rate, 2), '100.00', 2) > 0) {
|
||||
return (string) __('Sum of channel top-level commission share rates cannot exceed 100%');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function validateCommissionShareRate(?int $parentAdminId, mixed $rateRaw, ?int $excludeAdminId = null): ?string
|
||||
{
|
||||
if ($parentAdminId === null || $parentAdminId <= 0) {
|
||||
@@ -110,35 +161,66 @@ class AdminCommissionDistributionService
|
||||
if ($channelId <= 0 || bccomp($totalCommission, '0', 2) <= 0) {
|
||||
return [];
|
||||
}
|
||||
$roots = Db::name('admin')
|
||||
$rootRows = Db::name('admin')
|
||||
->where('channel_id', $channelId)
|
||||
->where('status', 'enable')
|
||||
->whereRaw('(parent_admin_id IS NULL OR parent_admin_id = 0)')
|
||||
->order('id', 'asc')
|
||||
->column('id');
|
||||
if ($roots === []) {
|
||||
->field(['id', 'commission_share_rate'])
|
||||
->select()
|
||||
->toArray();
|
||||
if ($rootRows === []) {
|
||||
return [];
|
||||
}
|
||||
$rootCount = count($roots);
|
||||
$perRoot = bcdiv($totalCommission, strval($rootCount), 2);
|
||||
$assigned = '0.00';
|
||||
$useRateSplit = true;
|
||||
foreach ($rootRows as $rootRow) {
|
||||
$rate = bcadd(strval($rootRow['commission_share_rate'] ?? '0'), '0', 2);
|
||||
if (bccomp($rate, '0', 2) <= 0) {
|
||||
$useRateSplit = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$merged = [];
|
||||
foreach ($roots as $index => $rootId) {
|
||||
$rootId = intval($rootId);
|
||||
if ($rootId <= 0) {
|
||||
continue;
|
||||
}
|
||||
$isLast = $index === $rootCount - 1;
|
||||
$rootAmount = $isLast ? bcsub($totalCommission, $assigned, 2) : $perRoot;
|
||||
if (!$isLast) {
|
||||
$assigned = bcadd($assigned, $rootAmount, 2);
|
||||
}
|
||||
$parts = self::distributeFromAdmin($rootId, $rootAmount, $calcBaseAmount);
|
||||
foreach ($parts as $adminId => $amount) {
|
||||
if (!isset($merged[$adminId])) {
|
||||
$merged[$adminId] = '0.00';
|
||||
if ($useRateSplit) {
|
||||
foreach ($rootRows as $rootRow) {
|
||||
$rootId = intval($rootRow['id'] ?? 0);
|
||||
if ($rootId <= 0) {
|
||||
continue;
|
||||
}
|
||||
$rate = bcadd(strval($rootRow['commission_share_rate'] ?? '0'), '0', 2);
|
||||
$rootAmount = bcmul($totalCommission, bcdiv($rate, '100', 4), 2);
|
||||
if (bccomp($rootAmount, '0', 2) <= 0) {
|
||||
continue;
|
||||
}
|
||||
$parts = self::distributeFromAdmin($rootId, $rootAmount, $calcBaseAmount);
|
||||
foreach ($parts as $adminId => $amount) {
|
||||
if (!isset($merged[$adminId])) {
|
||||
$merged[$adminId] = '0.00';
|
||||
}
|
||||
$merged[$adminId] = bcadd($merged[$adminId], $amount, 2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$rootCount = count($rootRows);
|
||||
$perRoot = bcdiv($totalCommission, strval($rootCount), 2);
|
||||
$assigned = '0.00';
|
||||
foreach ($rootRows as $index => $rootRow) {
|
||||
$rootId = intval($rootRow['id'] ?? 0);
|
||||
if ($rootId <= 0) {
|
||||
continue;
|
||||
}
|
||||
$isLast = $index === $rootCount - 1;
|
||||
$rootAmount = $isLast ? bcsub($totalCommission, $assigned, 2) : $perRoot;
|
||||
if (!$isLast) {
|
||||
$assigned = bcadd($assigned, $rootAmount, 2);
|
||||
}
|
||||
$parts = self::distributeFromAdmin($rootId, $rootAmount, $calcBaseAmount);
|
||||
foreach ($parts as $adminId => $amount) {
|
||||
if (!isset($merged[$adminId])) {
|
||||
$merged[$adminId] = '0.00';
|
||||
}
|
||||
$merged[$adminId] = bcadd($merged[$adminId], $amount, 2);
|
||||
}
|
||||
$merged[$adminId] = bcadd($merged[$adminId], $amount, 2);
|
||||
}
|
||||
}
|
||||
$out = [];
|
||||
|
||||
Reference in New Issue
Block a user