游戏-渠道管理-优化样式增强验证,新增关联删除
This commit is contained in:
@@ -36,7 +36,7 @@ class Channel extends Backend
|
||||
* adminTree 为辅助接口,默认权限节点名 game/channel/admintree 往往未在后台录入;
|
||||
* 与列表权限 game/channel/index 对齐,避免子管理员已勾「渠道管理」仍 401。
|
||||
*/
|
||||
protected array $noNeedPermission = ['adminTree'];
|
||||
protected array $noNeedPermission = ['adminTree', 'deleteRelatedCounts'];
|
||||
|
||||
protected function initController(WebmanRequest $request): ?Response
|
||||
{
|
||||
@@ -44,6 +44,25 @@ class Channel extends Backend
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表;附带 delete_related_counts=1 时返回删除前关联数据统计(走与 index 相同的路由入口,避免单独 URL 在部分环境下 404)
|
||||
*
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index(WebmanRequest $request): Response
|
||||
{
|
||||
$response = $this->initializeBackend($request);
|
||||
if ($response !== null) {
|
||||
return $response;
|
||||
}
|
||||
$delPreview = $request->get('delete_related_counts');
|
||||
if ($delPreview === '1' || $delPreview === 1 || $delPreview === true) {
|
||||
return $this->deleteRelatedCountsResponse($request);
|
||||
}
|
||||
|
||||
return $this->_index();
|
||||
}
|
||||
|
||||
/**
|
||||
* 渠道-管理员树(父级=渠道,子级=管理员,仅可选择子级)
|
||||
*/
|
||||
@@ -200,6 +219,20 @@ class Channel extends Backend
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
if ($result !== false) {
|
||||
$newChannelId = $this->resolveNewChannelIdAfterInsert($data);
|
||||
if (!$this->isPositiveChannelId($newChannelId)) {
|
||||
$code = $data['code'] ?? null;
|
||||
if (is_string($code) && trim($code) !== '') {
|
||||
$newChannelId = Db::name('game_channel')->where('code', trim($code))->order('id', 'desc')->value('id');
|
||||
}
|
||||
}
|
||||
if ($this->isPositiveChannelId($newChannelId)) {
|
||||
try {
|
||||
$this->copyGameConfigFromChannelZero($newChannelId);
|
||||
} catch (Throwable $e) {
|
||||
return $this->error(__('Game channel copy default config failed') . ': ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
return $this->success(__('Added successfully'));
|
||||
}
|
||||
return $this->error(__('No rows were added'));
|
||||
@@ -316,13 +349,282 @@ class Channel extends Backend
|
||||
->order($order)
|
||||
->paginate($limit);
|
||||
|
||||
$list = $this->buildChannelListWithRealtimeUserCounts($res->items());
|
||||
|
||||
return $this->success('', [
|
||||
'list' => $res->items(),
|
||||
'list' => $list,
|
||||
'total' => $res->total(),
|
||||
'remark' => get_route_remark(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表 user_count 按 game_user.game_channel_id 实时 COUNT,与库字段无关(用户增删改时会回写 game_channel.user_count)
|
||||
*
|
||||
* @param iterable<int|string, mixed> $items
|
||||
* @return list<array<string, mixed>>
|
||||
*/
|
||||
private function buildChannelListWithRealtimeUserCounts(iterable $items): array
|
||||
{
|
||||
$rows = [];
|
||||
foreach ($items as $item) {
|
||||
$rows[] = is_array($item) ? $item : $item->toArray();
|
||||
}
|
||||
if ($rows === []) {
|
||||
return [];
|
||||
}
|
||||
$ids = [];
|
||||
foreach ($rows as $r) {
|
||||
if (isset($r['id'])) {
|
||||
$ids[] = $r['id'];
|
||||
}
|
||||
}
|
||||
if ($ids === []) {
|
||||
return $rows;
|
||||
}
|
||||
$agg = Db::name('game_user')
|
||||
->where('game_channel_id', 'in', $ids)
|
||||
->field('game_channel_id, count(*) as cnt')
|
||||
->group('game_channel_id')
|
||||
->select()
|
||||
->toArray();
|
||||
$countMap = [];
|
||||
foreach ($agg as $a) {
|
||||
$countMap[$a['game_channel_id']] = (int) $a['cnt'];
|
||||
}
|
||||
foreach ($rows as &$r) {
|
||||
$cid = $r['id'] ?? null;
|
||||
$r['user_count'] = ($cid !== null && $cid !== '') ? ($countMap[$cid] ?? 0) : 0;
|
||||
}
|
||||
unset($r);
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除前统计:与当前选中渠道相关的游戏配置条数、游戏用户条数(须具备 game/channel/del)
|
||||
*
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function deleteRelatedCounts(WebmanRequest $request): Response
|
||||
{
|
||||
$response = $this->initializeBackend($request);
|
||||
if ($response !== null) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
return $this->deleteRelatedCountsResponse($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
private function deleteRelatedCountsResponse(WebmanRequest $request): Response
|
||||
{
|
||||
if (!$this->auth->check('game/channel/del')) {
|
||||
return $this->error(__('You have no permission'));
|
||||
}
|
||||
|
||||
$channelIds = $this->getAuthorizedChannelIdsForIncomingIds($request);
|
||||
if ($channelIds === []) {
|
||||
return $this->success('', [
|
||||
'game_config_count' => 0,
|
||||
'game_user_count' => 0,
|
||||
]);
|
||||
}
|
||||
|
||||
// 实时统计:game_config.channel_id、game_user.game_channel_id(与渠道 id 一致)
|
||||
$configCount = Db::name('game_config')->where('channel_id', 'in', $channelIds)->count();
|
||||
$userCount = Db::name('game_user')->where('game_channel_id', 'in', $channelIds)->count();
|
||||
|
||||
return $this->success('', [
|
||||
'game_config_count' => $configCount,
|
||||
'game_user_count' => $userCount,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除渠道:若存在关联的游戏配置或用户,须带 confirm_cascade=1;同时级联删除关联数据
|
||||
*
|
||||
* @throws Throwable
|
||||
*/
|
||||
protected function _del(): Response
|
||||
{
|
||||
$where = [];
|
||||
$dataLimitAdminIds = $this->getDataLimitAdminIds();
|
||||
if ($dataLimitAdminIds) {
|
||||
$where[] = [$this->dataLimitField, 'in', $dataLimitAdminIds];
|
||||
}
|
||||
|
||||
$ids = $this->request ? ($this->request->post('ids') ?? $this->request->get('ids') ?? []) : [];
|
||||
if (!is_array($ids)) {
|
||||
$ids = $ids !== null && $ids !== '' ? [$ids] : [];
|
||||
}
|
||||
if ($ids === []) {
|
||||
return $this->error(__('Parameter error'));
|
||||
}
|
||||
|
||||
$pk = $this->model->getPk();
|
||||
$where[] = [$pk, 'in', $ids];
|
||||
|
||||
$data = $this->model->where($where)->select();
|
||||
if (count($data) === 0) {
|
||||
return $this->error(__('No rows were deleted'));
|
||||
}
|
||||
|
||||
$channelIds = [];
|
||||
foreach ($data as $v) {
|
||||
$channelIds[] = $v[$pk];
|
||||
}
|
||||
|
||||
// 删除确认用实时条数:game_config.channel_id、game_user.game_channel_id
|
||||
$configCount = Db::name('game_config')->where('channel_id', 'in', $channelIds)->count();
|
||||
$userCount = Db::name('game_user')->where('game_channel_id', 'in', $channelIds)->count();
|
||||
|
||||
$confirmCascade = $this->request->get('confirm_cascade');
|
||||
if ($confirmCascade === null || $confirmCascade === '') {
|
||||
$confirmCascade = $this->request->post('confirm_cascade');
|
||||
}
|
||||
$confirmed = $confirmCascade === 1 || $confirmCascade === '1' || $confirmCascade === true;
|
||||
|
||||
if (($configCount > 0 || $userCount > 0) && !$confirmed) {
|
||||
return $this->error(__('Game channel delete need confirm related'), [
|
||||
'need_confirm' => true,
|
||||
'game_config_count' => $configCount,
|
||||
'game_user_count' => $userCount,
|
||||
]);
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
$this->model->startTrans();
|
||||
try {
|
||||
Db::name('game_config')->where('channel_id', 'in', $channelIds)->delete();
|
||||
Db::name('game_user')->where('game_channel_id', 'in', $channelIds)->delete();
|
||||
foreach ($data as $v) {
|
||||
$count += $v->delete();
|
||||
}
|
||||
$this->model->commit();
|
||||
} catch (Throwable $e) {
|
||||
$this->model->rollback();
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
|
||||
if ($count) {
|
||||
return $this->success(__('Deleted successfully'));
|
||||
}
|
||||
|
||||
return $this->error(__('No rows were deleted'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<string|int> $ids
|
||||
* @return list<int|string>
|
||||
*/
|
||||
private function getAuthorizedChannelIdsForIncomingIds(WebmanRequest $request): array
|
||||
{
|
||||
$where = [];
|
||||
$dataLimitAdminIds = $this->getDataLimitAdminIds();
|
||||
if ($dataLimitAdminIds) {
|
||||
$where[] = [$this->dataLimitField, 'in', $dataLimitAdminIds];
|
||||
}
|
||||
|
||||
$ids = $request->post('ids') ?? $request->get('ids') ?? [];
|
||||
if (!is_array($ids)) {
|
||||
$ids = $ids !== null && $ids !== '' ? [$ids] : [];
|
||||
}
|
||||
if ($ids === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$pk = $this->model->getPk();
|
||||
$where[] = [$pk, 'in', $ids];
|
||||
|
||||
return $this->model->where($where)->column($pk);
|
||||
}
|
||||
|
||||
/**
|
||||
* ThinkORM 在连接池/部分驱动下,insert 后 getKey() 可能未及时带上自增 id,这里多路兜底
|
||||
*
|
||||
* @param array<string, mixed> $postedChannelData 已过滤后的入库数据(含 code 等)
|
||||
*/
|
||||
private function resolveNewChannelIdAfterInsert(array $postedChannelData): int|string|null
|
||||
{
|
||||
$pk = $this->model->getPk();
|
||||
$id = $this->model->getKey();
|
||||
if ($this->isPositiveChannelId($id)) {
|
||||
return $id;
|
||||
}
|
||||
$rowData = $this->model->getData();
|
||||
if (is_array($rowData)) {
|
||||
if (isset($rowData[$pk]) && $this->isPositiveChannelId($rowData[$pk])) {
|
||||
return $rowData[$pk];
|
||||
}
|
||||
if (isset($rowData['id']) && $this->isPositiveChannelId($rowData['id'])) {
|
||||
return $rowData['id'];
|
||||
}
|
||||
}
|
||||
$lastInsId = $this->model->db()->getLastInsID();
|
||||
if ($this->isPositiveChannelId($lastInsId)) {
|
||||
return $lastInsId;
|
||||
}
|
||||
$lastInsId2 = Db::name('game_channel')->getLastInsID();
|
||||
if ($this->isPositiveChannelId($lastInsId2)) {
|
||||
return $lastInsId2;
|
||||
}
|
||||
$code = $postedChannelData['code'] ?? null;
|
||||
if (is_string($code) && trim($code) !== '') {
|
||||
$found = Db::name('game_channel')->where('code', trim($code))->order('id', 'desc')->value('id');
|
||||
if ($this->isPositiveChannelId($found)) {
|
||||
return $found;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function isPositiveChannelId(mixed $id): bool
|
||||
{
|
||||
if ($id === null || $id === '') {
|
||||
return false;
|
||||
}
|
||||
if (is_numeric($id)) {
|
||||
return $id > 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建渠道后:将 channel_id=0 的全局默认游戏配置复制一份,channel_id 指向新渠道主键
|
||||
*
|
||||
* @param int|string $newChannelId 新建 game_channel.id
|
||||
*/
|
||||
private function copyGameConfigFromChannelZero(int|string $newChannelId): void
|
||||
{
|
||||
$exists = Db::name('game_config')->where('channel_id', $newChannelId)->count();
|
||||
if ($exists > 0) {
|
||||
return;
|
||||
}
|
||||
// 全局模板:channel_id 为 0 或字符串 '0'(与业务约定一致)
|
||||
$rows = Db::name('game_config')
|
||||
->whereIn('channel_id', [0, '0'])
|
||||
->select()
|
||||
->toArray();
|
||||
if ($rows === []) {
|
||||
return;
|
||||
}
|
||||
$now = time();
|
||||
foreach ($rows as $row) {
|
||||
foreach (['ID', 'id', 'Id'] as $pkField) {
|
||||
unset($row[$pkField]);
|
||||
}
|
||||
$row['channel_id'] = $newChannelId;
|
||||
$row['create_time'] = $now;
|
||||
$row['update_time'] = $now;
|
||||
Db::name('game_config')->strict(false)->insert($row);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 若需重写查看、编辑、删除等方法,请复制 @see \app\admin\library\traits\Backend 中对应的方法至此进行重写
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user