1.优化分红方式
This commit is contained in:
@@ -5,24 +5,32 @@ declare(strict_types=1);
|
||||
namespace app\admin\controller\auth;
|
||||
|
||||
use ba\Random;
|
||||
use ba\Tree;
|
||||
use Throwable;
|
||||
use support\think\Db;
|
||||
use support\validation\Validator;
|
||||
use support\validation\ValidationException;
|
||||
use app\common\controller\Backend;
|
||||
use app\common\service\AdminCommissionDistributionService;
|
||||
use app\admin\model\Admin as AdminModel;
|
||||
use support\Response;
|
||||
use Webman\Http\Request;
|
||||
|
||||
class Admin extends Backend
|
||||
{
|
||||
/**
|
||||
* 分红比例余量查询(表单提示用)
|
||||
*/
|
||||
protected array $noNeedPermission = ['commissionShareRemainder'];
|
||||
|
||||
protected ?object $model = null;
|
||||
|
||||
protected array|string $preExcludeFields = ['create_time', 'update_time', 'password', 'salt', 'login_failure', 'last_login_time', 'last_login_ip'];
|
||||
|
||||
protected array|string $quickSearchField = ['username', 'nickname'];
|
||||
|
||||
protected string|int|bool $dataLimit = 'parent';
|
||||
/** 使用 parent_admin_id 树过滤,不用角色组 dataLimit */
|
||||
protected string|int|bool $dataLimit = false;
|
||||
|
||||
protected string $dataLimitField = 'id';
|
||||
|
||||
@@ -35,41 +43,94 @@ class Admin extends Backend
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$response = $this->initializeBackend($request);
|
||||
if ($response !== null) return $response;
|
||||
if ($response !== null) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ($request->get('select') ?? $request->post('select')) {
|
||||
$selectRes = $this->select($request);
|
||||
if ($selectRes !== null) return $selectRes;
|
||||
if ($selectRes !== null) {
|
||||
return $selectRes;
|
||||
}
|
||||
}
|
||||
|
||||
list($where, $alias, $limit, $order) = $this->queryBuilder();
|
||||
$adminAlias = $alias[strtolower($this->model->getTable())] ?? 'admin';
|
||||
|
||||
$query = $this->model
|
||||
->withoutField('login_failure,password,salt')
|
||||
->withJoin($this->withJoinTable, $this->withJoinType)
|
||||
->alias($alias)
|
||||
->where($where);
|
||||
|
||||
// 仅返回“顶级角色组(pid=0)”下的管理员(用于远程下拉等场景)
|
||||
$visibleIds = AdminCommissionDistributionService::getVisibleAdminIdsForOperator(
|
||||
intval($this->auth->id),
|
||||
$this->auth->isSuperAdmin()
|
||||
);
|
||||
if ($visibleIds !== []) {
|
||||
$query->where($adminAlias . '.id', 'in', $visibleIds);
|
||||
}
|
||||
|
||||
$topGroup = $request->get('top_group') ?? $request->post('top_group');
|
||||
if ($topGroup === '1' || $topGroup === 1 || $topGroup === true) {
|
||||
$query = $query
|
||||
->join('admin_group_access aga', $alias['admin'] . '.id = aga.uid')
|
||||
->join('admin_group_access aga', $adminAlias . '.id = aga.uid')
|
||||
->join('admin_group ag', 'aga.group_id = ag.id')
|
||||
->where('ag.pid', 0)
|
||||
->distinct(true);
|
||||
}
|
||||
|
||||
$res = $query
|
||||
->order($order)
|
||||
->paginate($limit);
|
||||
$items = $res->items();
|
||||
$rows = $query->order($order)->select()->toArray();
|
||||
$rows = $this->enrichAdminRows($rows);
|
||||
foreach ($rows as $k => $row) {
|
||||
$parentId = intval($row['parent_admin_id'] ?? 0);
|
||||
$rows[$k]['pid'] = $parentId > 0 ? $parentId : 0;
|
||||
}
|
||||
|
||||
$tree = Tree::instance()->assembleChild($rows, 'pid', 'id');
|
||||
|
||||
return $this->success('', [
|
||||
'list' => $items,
|
||||
'total' => $res->total(),
|
||||
'list' => $tree,
|
||||
'total' => count($rows),
|
||||
'remark' => get_route_remark(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询同上级下剩余可分配分红比例(表单提示)
|
||||
*/
|
||||
public function commissionShareRemainder(Request $request): Response
|
||||
{
|
||||
$response = $this->initializeBackend($request);
|
||||
if ($response !== null) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$parentAdminId = intval($request->get('parent_admin_id', 0));
|
||||
$excludeId = intval($request->get('exclude_id', 0));
|
||||
if ($parentAdminId <= 0) {
|
||||
return $this->success('', [
|
||||
'used_rate' => '0.00',
|
||||
'remaining_rate' => '100.00',
|
||||
'parent_has_no_share' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
if (!$this->canManageAdminId($parentAdminId)) {
|
||||
return $this->error(__('You have no permission'));
|
||||
}
|
||||
|
||||
$stats = AdminCommissionDistributionService::getShareRemainder(
|
||||
$parentAdminId,
|
||||
$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,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 远程下拉(重写:支持 top_group=1 仅返回顶级组管理员)
|
||||
*/
|
||||
@@ -88,7 +149,9 @@ class Admin extends Backend
|
||||
$quickSearchArr = is_array($this->quickSearchField) ? $this->quickSearchField : explode(',', (string) $this->quickSearchField);
|
||||
foreach ($quickSearchArr as $f) {
|
||||
$f = trim((string) $f);
|
||||
if ($f === '') continue;
|
||||
if ($f === '') {
|
||||
continue;
|
||||
}
|
||||
$f = str_contains($f, '.') ? substr($f, strrpos($f, '.') + 1) : $f;
|
||||
if ($f !== '' && !in_array($f, $fields, true)) {
|
||||
$fields[] = $f;
|
||||
@@ -99,15 +162,15 @@ class Admin extends Backend
|
||||
$modelTable = strtolower($this->model->getTable());
|
||||
$mainAlias = ($alias[$modelTable] ?? $modelTable) . '.';
|
||||
|
||||
// 联表时避免字段歧义:主表字段统一 select 为 "admin.xxx as xxx"
|
||||
$selectFields = [];
|
||||
foreach ($fields as $f) {
|
||||
$f = trim((string) $f);
|
||||
if ($f === '') continue;
|
||||
if ($f === '') {
|
||||
continue;
|
||||
}
|
||||
$selectFields[] = $mainAlias . $f . ' as ' . $f;
|
||||
}
|
||||
|
||||
// 联表时避免排序字段歧义:无前缀的字段默认加主表前缀
|
||||
$qualifiedOrder = [];
|
||||
if (is_array($order)) {
|
||||
foreach ($order as $k => $v) {
|
||||
@@ -121,6 +184,14 @@ class Admin extends Backend
|
||||
->alias($alias)
|
||||
->where($where);
|
||||
|
||||
$visibleIds = AdminCommissionDistributionService::getVisibleAdminIdsForOperator(
|
||||
intval($this->auth->id),
|
||||
$this->auth->isSuperAdmin()
|
||||
);
|
||||
if ($visibleIds !== []) {
|
||||
$query->where($mainAlias . 'id', 'in', $visibleIds);
|
||||
}
|
||||
|
||||
$topGroup = $this->request ? ($this->request->get('top_group') ?? $this->request->post('top_group')) : null;
|
||||
if ($topGroup === '1' || $topGroup === 1 || $topGroup === true) {
|
||||
$query = $query
|
||||
@@ -143,7 +214,9 @@ class Admin extends Backend
|
||||
public function add(Request $request): Response
|
||||
{
|
||||
$response = $this->initializeBackend($request);
|
||||
if ($response !== null) return $response;
|
||||
if ($response !== null) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ($request->method() === 'POST') {
|
||||
$data = $request->post();
|
||||
@@ -179,6 +252,7 @@ class Admin extends Backend
|
||||
$data = $this->excludeFields($data);
|
||||
$creatorChannelId = $this->getCreatorChannelId();
|
||||
$groupChannelId = $this->resolveChannelIdFromPrimaryGroup($data['group_arr'] ?? []);
|
||||
|
||||
if (!$this->auth->isSuperAdmin()) {
|
||||
if ($creatorChannelId === null || $creatorChannelId === '') {
|
||||
return $this->error(__('You have no permission'));
|
||||
@@ -192,13 +266,24 @@ class Admin extends Backend
|
||||
$data['channel_id'] = $creatorChannelId;
|
||||
$data['parent_admin_id'] = $this->auth->id;
|
||||
} else {
|
||||
$data['channel_id'] = ($groupChannelId === null || $groupChannelId === '') ? null : $groupChannelId;
|
||||
$postedChannel = $data['channel_id'] ?? null;
|
||||
if ($postedChannel === null || $postedChannel === '') {
|
||||
$data['channel_id'] = ($groupChannelId === null || $groupChannelId === '') ? null : $groupChannelId;
|
||||
}
|
||||
}
|
||||
|
||||
$parentErr = $this->normalizeParentAndShareFields($data, null);
|
||||
if ($parentErr !== null) {
|
||||
return $this->error($parentErr);
|
||||
}
|
||||
|
||||
$data['invite_code'] = $this->generateUniqueInviteCode();
|
||||
$result = false;
|
||||
if (!empty($data['group_arr'])) {
|
||||
$authRes = $this->checkGroupAuth($data['group_arr']);
|
||||
if ($authRes !== null) return $authRes;
|
||||
if ($authRes !== null) {
|
||||
return $authRes;
|
||||
}
|
||||
}
|
||||
$this->model->startTrans();
|
||||
try {
|
||||
@@ -234,7 +319,9 @@ class Admin extends Backend
|
||||
public function edit(Request $request): Response
|
||||
{
|
||||
$response = $this->initializeBackend($request);
|
||||
if ($response !== null) return $response;
|
||||
if ($response !== null) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$pk = $this->model->getPk();
|
||||
$id = $request->get($pk) ?? $request->post($pk);
|
||||
@@ -243,8 +330,7 @@ class Admin extends Backend
|
||||
return $this->error(__('Record not found'));
|
||||
}
|
||||
|
||||
$dataLimitAdminIds = $this->getDataLimitAdminIds();
|
||||
if ($dataLimitAdminIds && !in_array($row[$this->dataLimitField], $dataLimitAdminIds)) {
|
||||
if (!$this->canManageAdminId(intval($row['id']))) {
|
||||
return $this->error(__('You have no permission'));
|
||||
}
|
||||
|
||||
@@ -255,7 +341,7 @@ class Admin extends Backend
|
||||
}
|
||||
$isSelfEdit = (int) $this->auth->id === (int) $id;
|
||||
if ($isSelfEdit) {
|
||||
unset($data['group_arr'], $data['group_name_arr']);
|
||||
unset($data['group_arr'], $data['group_name_arr'], $data['parent_admin_id'], $data['commission_share_rate']);
|
||||
}
|
||||
|
||||
$editGroupArr = null;
|
||||
@@ -313,7 +399,9 @@ class Admin extends Backend
|
||||
];
|
||||
}
|
||||
$authRes = $this->checkGroupAuth($checkGroups);
|
||||
if ($authRes !== null) return $authRes;
|
||||
if ($authRes !== null) {
|
||||
return $authRes;
|
||||
}
|
||||
}
|
||||
|
||||
$data = $this->excludeFields($data);
|
||||
@@ -336,6 +424,17 @@ class Admin extends Backend
|
||||
$data['channel_id'] = ($groupChannelId === null || $groupChannelId === '') ? null : $groupChannelId;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$isSelfEdit) {
|
||||
if (!$this->auth->isSuperAdmin()) {
|
||||
unset($data['parent_admin_id']);
|
||||
}
|
||||
$parentErr = $this->normalizeParentAndShareFields($data, intval($id));
|
||||
if ($parentErr !== null) {
|
||||
return $this->error($parentErr);
|
||||
}
|
||||
}
|
||||
|
||||
$result = false;
|
||||
$this->model->startTrans();
|
||||
try {
|
||||
@@ -361,37 +460,54 @@ class Admin extends Backend
|
||||
|
||||
unset($row['salt'], $row['login_failure']);
|
||||
$row['password'] = '';
|
||||
$rowData = $row->toArray();
|
||||
$enriched = $this->enrichAdminRows([$rowData]);
|
||||
$rowData = $enriched[0] ?? $rowData;
|
||||
$parentId = intval($rowData['parent_admin_id'] ?? 0);
|
||||
if ($parentId > 0) {
|
||||
$remainder = AdminCommissionDistributionService::getShareRemainder($parentId, intval($id));
|
||||
$rowData['share_remainder'] = $remainder;
|
||||
}
|
||||
|
||||
return $this->success('', [
|
||||
'row' => $row
|
||||
'row' => $rowData,
|
||||
]);
|
||||
}
|
||||
|
||||
public function del(Request $request): Response
|
||||
{
|
||||
$response = $this->initializeBackend($request);
|
||||
if ($response !== null) return $response;
|
||||
|
||||
$where = [];
|
||||
$dataLimitAdminIds = $this->getDataLimitAdminIds();
|
||||
if ($dataLimitAdminIds) {
|
||||
$where[] = [$this->dataLimitField, 'in', $dataLimitAdminIds];
|
||||
if ($response !== null) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$ids = $request->get('ids') ?? $request->post('ids') ?? [];
|
||||
$ids = is_array($ids) ? $ids : [];
|
||||
$where[] = [$this->model->getPk(), 'in', $ids];
|
||||
$data = $this->model->where($where)->select();
|
||||
if ($ids === []) {
|
||||
return $this->error(__('Parameter error'));
|
||||
}
|
||||
|
||||
$data = $this->model->where($this->model->getPk(), 'in', $ids)->select();
|
||||
|
||||
$count = 0;
|
||||
$this->model->startTrans();
|
||||
try {
|
||||
foreach ($data as $v) {
|
||||
if ($v->id != $this->auth->id) {
|
||||
$count += $v->delete();
|
||||
Db::name('admin_group_access')
|
||||
->where('uid', $v['id'])
|
||||
->delete();
|
||||
if ($v->id == $this->auth->id) {
|
||||
continue;
|
||||
}
|
||||
if (!$this->canManageAdminId(intval($v->id))) {
|
||||
continue;
|
||||
}
|
||||
$childCount = Db::name('admin')->where('parent_admin_id', $v->id)->count();
|
||||
if ($childCount > 0) {
|
||||
$this->model->rollback();
|
||||
return $this->error(__('Cannot delete administrator with sub-agents'));
|
||||
}
|
||||
$count += $v->delete();
|
||||
Db::name('admin_group_access')
|
||||
->where('uid', $v['id'])
|
||||
->delete();
|
||||
}
|
||||
$this->model->commit();
|
||||
} catch (Throwable $e) {
|
||||
@@ -404,9 +520,6 @@ class Admin extends Backend
|
||||
return $this->error(__('No rows were deleted'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 远程下拉(Admin 无自定义,走父类默认列表)
|
||||
*/
|
||||
public function select(Request $request): Response
|
||||
{
|
||||
return parent::select($request);
|
||||
@@ -465,6 +578,141 @@ class Admin extends Backend
|
||||
return Db::name('admin_group')->where('id', $gid)->value('channel_id');
|
||||
}
|
||||
|
||||
private function canManageAdminId(int $adminId): bool
|
||||
{
|
||||
if ($this->auth->isSuperAdmin()) {
|
||||
return true;
|
||||
}
|
||||
if ($adminId === intval($this->auth->id)) {
|
||||
return true;
|
||||
}
|
||||
$visible = AdminCommissionDistributionService::getVisibleAdminIdsForOperator(
|
||||
intval($this->auth->id),
|
||||
false
|
||||
);
|
||||
return in_array($adminId, $visible, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
private function normalizeParentAndShareFields(array &$data, ?int $editAdminId): ?string
|
||||
{
|
||||
$parentId = isset($data['parent_admin_id']) && $data['parent_admin_id'] !== '' && $data['parent_admin_id'] !== null
|
||||
? intval($data['parent_admin_id'])
|
||||
: 0;
|
||||
if ($parentId <= 0 && $editAdminId !== null && $editAdminId > 0) {
|
||||
$existingParent = Db::name('admin')->where('id', $editAdminId)->value('parent_admin_id');
|
||||
$parentId = intval($existingParent ?? 0);
|
||||
}
|
||||
if ($parentId <= 0) {
|
||||
$data['parent_admin_id'] = null;
|
||||
$data['commission_share_rate'] = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($editAdminId !== null && $parentId === $editAdminId) {
|
||||
return (string) __('Cannot set yourself as parent administrator');
|
||||
}
|
||||
|
||||
$parent = Db::name('admin')->where('id', $parentId)->find();
|
||||
if (!is_array($parent)) {
|
||||
return (string) __('Invalid parent administrator');
|
||||
}
|
||||
if (!$this->canManageAdminId($parentId) && !$this->auth->isSuperAdmin()) {
|
||||
return (string) __('You have no permission');
|
||||
}
|
||||
|
||||
$channelId = $data['channel_id'] ?? null;
|
||||
if ($channelId !== null && $channelId !== '' && (string) ($parent['channel_id'] ?? '') !== (string) $channelId) {
|
||||
return (string) __('Parent administrator must belong to the same channel');
|
||||
}
|
||||
if (($channelId === null || $channelId === '') && !empty($parent['channel_id'])) {
|
||||
$data['channel_id'] = $parent['channel_id'];
|
||||
}
|
||||
|
||||
$shareErr = AdminCommissionDistributionService::validateCommissionShareRate(
|
||||
$parentId,
|
||||
$data['commission_share_rate'] ?? null,
|
||||
$editAdminId
|
||||
);
|
||||
if ($shareErr !== null) {
|
||||
return $shareErr;
|
||||
}
|
||||
$data['commission_share_rate'] = bcadd(strval($data['commission_share_rate'] ?? '0'), '0', 2);
|
||||
$data['parent_admin_id'] = $parentId;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $rows
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function enrichAdminRows(array $rows): array
|
||||
{
|
||||
if ($rows === []) {
|
||||
return [];
|
||||
}
|
||||
$adminIds = [];
|
||||
$channelIds = [];
|
||||
$parentIds = [];
|
||||
foreach ($rows as $row) {
|
||||
$aid = intval($row['id'] ?? 0);
|
||||
if ($aid > 0) {
|
||||
$adminIds[] = $aid;
|
||||
}
|
||||
$cid = $row['channel_id'] ?? null;
|
||||
if ($cid !== null && $cid !== '') {
|
||||
$channelIds[] = intval($cid);
|
||||
}
|
||||
$pid = intval($row['parent_admin_id'] ?? 0);
|
||||
if ($pid > 0) {
|
||||
$parentIds[] = $pid;
|
||||
}
|
||||
}
|
||||
$channelNames = $channelIds !== []
|
||||
? Db::name('channel')->where('id', 'in', array_unique($channelIds))->column('name', 'id')
|
||||
: [];
|
||||
$parentNames = $parentIds !== []
|
||||
? Db::name('admin')->where('id', 'in', array_unique($parentIds))->column('username', 'id')
|
||||
: [];
|
||||
|
||||
$groupMap = [];
|
||||
if ($adminIds !== []) {
|
||||
$accessRows = Db::name('admin_group_access')->where('uid', 'in', $adminIds)->select()->toArray();
|
||||
$groupIds = array_unique(array_map(static fn(array $r): int => intval($r['group_id'] ?? 0), $accessRows));
|
||||
$groupNames = $groupIds !== []
|
||||
? Db::name('admin_group')->where('id', 'in', $groupIds)->column('name', 'id')
|
||||
: [];
|
||||
foreach ($accessRows as $access) {
|
||||
$uid = intval($access['uid'] ?? 0);
|
||||
$gid = intval($access['group_id'] ?? 0);
|
||||
if ($uid <= 0 || $gid <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (!isset($groupMap[$uid])) {
|
||||
$groupMap[$uid] = [];
|
||||
}
|
||||
$name = $groupNames[$gid] ?? '';
|
||||
if ($name !== '') {
|
||||
$groupMap[$uid][] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($rows as $k => $row) {
|
||||
$cid = $row['channel_id'] ?? null;
|
||||
$rows[$k]['channel_name'] = ($cid !== null && $cid !== '') ? strval($channelNames[intval($cid)] ?? '') : '';
|
||||
$pid = intval($row['parent_admin_id'] ?? 0);
|
||||
$rows[$k]['parent_admin_username'] = $pid > 0 ? strval($parentNames[$pid] ?? '') : '';
|
||||
$aid = intval($row['id'] ?? 0);
|
||||
$rows[$k]['group_name_arr'] = $groupMap[$aid] ?? [];
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
private function generateUniqueInviteCode(): string
|
||||
{
|
||||
$tries = 0;
|
||||
|
||||
Reference in New Issue
Block a user