1.将部门修改为渠道,并且所有dice_表关联渠道表
2.将所有配置表,记录表设置关联渠道 3.优化后台页面设置
This commit is contained in:
497
server/app/dice/service/DiceChannelConfigService.php
Normal file
497
server/app/dice/service/DiceChannelConfigService.php
Normal file
@@ -0,0 +1,497 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\dice\service;
|
||||
|
||||
use app\dice\helper\AdminScopeHelper;
|
||||
use app\dice\logic\reward\DiceRewardLogic;
|
||||
use app\dice\model\reward\DiceReward;
|
||||
use app\dice\model\reward_config\DiceRewardConfig;
|
||||
use plugin\saiadmin\app\model\system\SystemDept;
|
||||
use plugin\saiadmin\app\model\system\SystemUser;
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
use support\think\Db;
|
||||
|
||||
/**
|
||||
* 渠道默认配置复制、补齐与关联删除
|
||||
* 默认配置:dept_id = 0(与超管「默认配置模板」一致)
|
||||
*/
|
||||
class DiceChannelConfigService
|
||||
{
|
||||
/** 需 (dept_id, id) 复合唯一的配置表 */
|
||||
private const COMPOSITE_KEY_TABLES = [
|
||||
'dice_config',
|
||||
'dice_reward_config',
|
||||
];
|
||||
/** 从默认模板复制的配置表 */
|
||||
private const CONFIG_TABLES = [
|
||||
'dice_config',
|
||||
'dice_ante_config',
|
||||
'dice_lottery_pool_config',
|
||||
'dice_reward_config',
|
||||
'dice_game',
|
||||
];
|
||||
|
||||
/** 复制时必须保留主键 id(非自增或固定 0-25) */
|
||||
private const TABLES_KEEP_ID = [
|
||||
'dice_config',
|
||||
'dice_reward_config',
|
||||
];
|
||||
|
||||
/** 可关联删除的业务表 */
|
||||
private const RELATION_TABLES = [
|
||||
'dice_config' => ['label' => '游戏键值配置', 'group' => 'configs'],
|
||||
'dice_ante_config' => ['label' => '底注配置', 'group' => 'configs'],
|
||||
'dice_lottery_pool_config' => ['label' => '彩金池配置', 'group' => 'configs'],
|
||||
'dice_reward_config' => ['label' => '奖励索引配置', 'group' => 'configs'],
|
||||
'dice_reward' => ['label' => '中奖概率(奖励对照)', 'group' => 'configs'],
|
||||
'dice_game' => ['label' => '游戏管理', 'group' => 'configs'],
|
||||
'dice_player' => ['label' => '玩家', 'group' => 'players'],
|
||||
'dice_play_record' => ['label' => '抽奖记录', 'group' => 'records'],
|
||||
'dice_play_record_test' => ['label' => '测试抽奖记录', 'group' => 'records'],
|
||||
'dice_player_wallet_record' => ['label' => '钱包流水', 'group' => 'records'],
|
||||
'dice_player_ticket_record' => ['label' => '票券记录', 'group' => 'records'],
|
||||
'dice_reward_config_record' => ['label' => '权重测试记录', 'group' => 'records'],
|
||||
];
|
||||
|
||||
/**
|
||||
* 默认模板 dept_id 统一为 0,并为固定 id 的配置表建立 (dept_id, id) 唯一约束
|
||||
*/
|
||||
public function ensureConfigCompositeKeys(): void
|
||||
{
|
||||
foreach (array_merge(self::CONFIG_TABLES, ['dice_reward']) as $table) {
|
||||
if ($this->tableHasColumn($table, 'dept_id')) {
|
||||
Db::table($table)->whereNull('dept_id')->update(['dept_id' => AdminScopeHelper::DEFAULT_TEMPLATE_DEPT]);
|
||||
}
|
||||
}
|
||||
foreach (self::COMPOSITE_KEY_TABLES as $table) {
|
||||
if (!$this->tableHasColumn($table, 'dept_id')) {
|
||||
continue;
|
||||
}
|
||||
if (!$this->tableHasColumn($table, 'row_id')) {
|
||||
if ($table === 'dice_reward_config') {
|
||||
Db::execute(
|
||||
'ALTER TABLE `dice_reward_config`'
|
||||
. ' MODIFY `id` int(11) NOT NULL COMMENT \'ID\','
|
||||
. ' ADD COLUMN `row_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT FIRST,'
|
||||
. ' DROP PRIMARY KEY,'
|
||||
. ' ADD PRIMARY KEY (`row_id`)'
|
||||
);
|
||||
} else {
|
||||
Db::execute(
|
||||
'ALTER TABLE `dice_config`'
|
||||
. ' ADD COLUMN `row_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT FIRST,'
|
||||
. ' DROP PRIMARY KEY,'
|
||||
. ' ADD PRIMARY KEY (`row_id`)'
|
||||
);
|
||||
}
|
||||
}
|
||||
$indexes = Db::query("SHOW INDEX FROM `{$table}` WHERE Key_name = 'uk_dept_config'");
|
||||
if (empty($indexes)) {
|
||||
Db::execute("ALTER TABLE `{$table}` ADD UNIQUE KEY `uk_dept_config` (`dept_id`, `id`)");
|
||||
}
|
||||
}
|
||||
$this->ensureDeptScopedUniqueIndexes();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将全局唯一键改为按渠道 (dept_id, 业务键) 唯一,便于复制默认模板
|
||||
*/
|
||||
private function ensureDeptScopedUniqueIndexes(): void
|
||||
{
|
||||
if ($this->tableHasColumn('dice_lottery_pool_config', 'dept_id')) {
|
||||
$old = Db::query("SHOW INDEX FROM `dice_lottery_pool_config` WHERE Key_name = 'dice_lottery_poll_config_unique'");
|
||||
if (!empty($old)) {
|
||||
Db::execute('ALTER TABLE `dice_lottery_pool_config` DROP INDEX `dice_lottery_poll_config_unique`');
|
||||
}
|
||||
$uk = Db::query("SHOW INDEX FROM `dice_lottery_pool_config` WHERE Key_name = 'uk_dept_name'");
|
||||
if (empty($uk)) {
|
||||
Db::execute('ALTER TABLE `dice_lottery_pool_config` ADD UNIQUE KEY `uk_dept_name` (`dept_id`, `name`)');
|
||||
}
|
||||
}
|
||||
if ($this->tableHasColumn('dice_game', 'dept_id')) {
|
||||
foreach (['uk_dice_game_code', 'uk_dice_game_key'] as $idx) {
|
||||
$exists = Db::query("SHOW INDEX FROM `dice_game` WHERE Key_name = '{$idx}'");
|
||||
if (!empty($exists)) {
|
||||
Db::execute("ALTER TABLE `dice_game` DROP INDEX `{$idx}`");
|
||||
}
|
||||
}
|
||||
$ukCode = Db::query("SHOW INDEX FROM `dice_game` WHERE Key_name = 'uk_dept_game_code'");
|
||||
if (empty($ukCode)) {
|
||||
Db::execute('ALTER TABLE `dice_game` ADD UNIQUE KEY `uk_dept_game_code` (`dept_id`, `game_code`)');
|
||||
}
|
||||
$ukKey = Db::query("SHOW INDEX FROM `dice_game` WHERE Key_name = 'uk_dept_game_key'");
|
||||
if (empty($ukKey)) {
|
||||
Db::execute('ALTER TABLE `dice_game` ADD UNIQUE KEY `uk_dept_game_key` (`dept_id`, `game_key`)');
|
||||
}
|
||||
}
|
||||
if ($this->tableHasColumn('dice_reward', 'dept_id')) {
|
||||
$old = Db::query("SHOW INDEX FROM `dice_reward` WHERE Key_name = 'uk_direction_grid_number'");
|
||||
if (!empty($old)) {
|
||||
Db::execute('ALTER TABLE `dice_reward` DROP INDEX `uk_direction_grid_number`');
|
||||
}
|
||||
$uk = Db::query("SHOW INDEX FROM `dice_reward` WHERE Key_name = 'uk_dept_direction_grid'");
|
||||
if (empty($uk)) {
|
||||
Db::execute('ALTER TABLE `dice_reward` ADD UNIQUE KEY `uk_dept_direction_grid` (`dept_id`, `direction`, `grid_number`)');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前无 dept_id 的配置标记为默认模板(仅执行一次迁移)
|
||||
*/
|
||||
public function markLegacyConfigAsDefault(): int
|
||||
{
|
||||
$this->ensureConfigCompositeKeys();
|
||||
$total = 0;
|
||||
foreach (self::CONFIG_TABLES as $table) {
|
||||
if (!$this->tableHasColumn($table, 'dept_id')) {
|
||||
continue;
|
||||
}
|
||||
$total += $this->countByDept($table, AdminScopeHelper::DEFAULT_TEMPLATE_DEPT);
|
||||
}
|
||||
return $total;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为单个渠道从默认模板复制配置(已存在则跳过)
|
||||
*/
|
||||
public function copyDefaultConfigToDept(int $deptId): array
|
||||
{
|
||||
if ($deptId <= 0) {
|
||||
throw new ApiException('Invalid channel id');
|
||||
}
|
||||
$result = ['dept_id' => $deptId, 'copied' => [], 'skipped' => [], 'merged' => []];
|
||||
foreach (self::CONFIG_TABLES as $table) {
|
||||
if (!$this->tableHasColumn($table, 'dept_id')) {
|
||||
continue;
|
||||
}
|
||||
if (in_array($table, self::TABLES_KEEP_ID, true)) {
|
||||
$merged = $this->syncCompositeIdTableFromDefault($table, $deptId);
|
||||
if ($merged > 0) {
|
||||
$result['merged'][$table] = $merged;
|
||||
} elseif ($this->countByDept($table, $deptId) > 0) {
|
||||
$result['skipped'][] = $table;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ($this->countByDept($table, $deptId) > 0) {
|
||||
$result['skipped'][] = $table;
|
||||
continue;
|
||||
}
|
||||
$rows = $this->defaultTemplateRows($table);
|
||||
if (empty($rows)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($rows as $row) {
|
||||
$row = (array) $row;
|
||||
unset($row['id'], $row['row_id'], $row['create_time'], $row['update_time'], $row['delete_time']);
|
||||
$row['dept_id'] = $deptId;
|
||||
Db::table($table)->insert($row);
|
||||
}
|
||||
$result['copied'][] = $table;
|
||||
}
|
||||
$this->ensureRewardReferenceForDept($deptId);
|
||||
DiceRewardConfig::refreshCache($deptId);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按业务 id 从默认模板补齐配置(dice_config / dice_reward_config)
|
||||
*/
|
||||
private function syncCompositeIdTableFromDefault(string $table, int $deptId): int
|
||||
{
|
||||
$templateRows = $this->defaultTemplateRows($table);
|
||||
if (empty($templateRows)) {
|
||||
return 0;
|
||||
}
|
||||
$inserted = 0;
|
||||
foreach ($templateRows as $row) {
|
||||
$row = (array) $row;
|
||||
if (!isset($row['id'])) {
|
||||
continue;
|
||||
}
|
||||
$businessId = $row['id'];
|
||||
$exists = Db::table($table)->where('dept_id', $deptId)->where('id', $businessId)->count();
|
||||
if ($exists > 0) {
|
||||
continue;
|
||||
}
|
||||
unset($row['row_id'], $row['create_time'], $row['update_time'], $row['delete_time']);
|
||||
$row['dept_id'] = $deptId;
|
||||
Db::table($table)->insert($row);
|
||||
$inserted++;
|
||||
}
|
||||
return $inserted;
|
||||
}
|
||||
|
||||
/**
|
||||
* 渠道已有奖励索引时,自动生成 dice_reward 对照表
|
||||
*/
|
||||
public function ensureRewardReferenceForDept(int $deptId): void
|
||||
{
|
||||
if ($deptId <= 0 || !$this->tableHasColumn('dice_reward', 'dept_id')) {
|
||||
return;
|
||||
}
|
||||
if ($this->countByDept('dice_reward_config', $deptId) <= 0) {
|
||||
return;
|
||||
}
|
||||
if ($this->countByDept('dice_reward', $deptId) > 0) {
|
||||
return;
|
||||
}
|
||||
$logic = new DiceRewardLogic();
|
||||
$logic->createRewardReferenceFromConfig($deptId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制默认 dice_reward 到渠道
|
||||
*/
|
||||
public function copyDefaultRewardsToDept(int $deptId): void
|
||||
{
|
||||
$this->ensureRewardReferenceForDept($deptId);
|
||||
if (!$this->tableHasColumn('dice_reward', 'dept_id')) {
|
||||
return;
|
||||
}
|
||||
if ($this->countByDept('dice_reward', $deptId) > 0) {
|
||||
return;
|
||||
}
|
||||
if ($this->countByDept('dice_reward_config', $deptId) > 0) {
|
||||
return;
|
||||
}
|
||||
$rows = $this->defaultTemplateRows('dice_reward');
|
||||
foreach ($rows as $row) {
|
||||
$row = (array) $row;
|
||||
unset($row['id'], $row['row_id']);
|
||||
unset($row['create_time'], $row['update_time'], $row['delete_time']);
|
||||
$row['dept_id'] = $deptId;
|
||||
Db::table('dice_reward')->insert($row);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为所有已有渠道补齐缺失配置
|
||||
*/
|
||||
public function syncAllChannelsFromDefault(): array
|
||||
{
|
||||
$deptIds = SystemDept::column('id');
|
||||
$summary = [];
|
||||
foreach ($deptIds as $deptId) {
|
||||
$deptId = (int) $deptId;
|
||||
if ($deptId <= 0) {
|
||||
continue;
|
||||
}
|
||||
$summary[$deptId] = $this->copyDefaultConfigToDept($deptId);
|
||||
}
|
||||
return $summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修复已删除渠道 ID、无管理员关联的遗留数据,归并到首个顶级渠道
|
||||
*/
|
||||
public function repairOrphanDeptReferences(): array
|
||||
{
|
||||
$validDeptIds = array_map('intval', SystemDept::column('id') ?: []);
|
||||
if (empty($validDeptIds)) {
|
||||
return [];
|
||||
}
|
||||
$rootDeptId = min($validDeptIds);
|
||||
$stats = [];
|
||||
$inList = implode(',', $validDeptIds);
|
||||
|
||||
$stats['sa_system_user'] = Db::execute(
|
||||
"UPDATE sa_system_user SET dept_id = {$rootDeptId}
|
||||
WHERE dept_id IS NOT NULL AND dept_id > 0 AND dept_id NOT IN ({$inList})"
|
||||
);
|
||||
|
||||
$bizTables = [
|
||||
'dice_player',
|
||||
'dice_play_record',
|
||||
'dice_play_record_test',
|
||||
'dice_player_wallet_record',
|
||||
'dice_player_ticket_record',
|
||||
'dice_reward_config_record',
|
||||
];
|
||||
foreach ($bizTables as $table) {
|
||||
if (!$this->tableHasColumn($table, 'dept_id')) {
|
||||
continue;
|
||||
}
|
||||
$stats[$table . '_invalid_dept'] = Db::execute(
|
||||
"UPDATE `{$table}` SET dept_id = {$rootDeptId}
|
||||
WHERE dept_id IS NOT NULL AND dept_id > 0 AND dept_id NOT IN ({$inList})"
|
||||
);
|
||||
}
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据管理员/玩家回填 dept_id
|
||||
*/
|
||||
public function backfillDataDeptId(): array
|
||||
{
|
||||
$stats = $this->repairOrphanDeptReferences();
|
||||
if ($this->tableHasColumn('dice_player', 'dept_id') && $this->tableHasColumn('dice_player', 'admin_id')) {
|
||||
$stats['dice_player'] = Db::execute(
|
||||
'UPDATE dice_player p INNER JOIN sa_system_user u ON p.admin_id = u.id
|
||||
SET p.dept_id = u.dept_id WHERE (p.dept_id IS NULL OR p.dept_id = 0) AND u.dept_id IS NOT NULL AND u.dept_id > 0'
|
||||
);
|
||||
}
|
||||
$validDeptIds = SystemDept::column('id') ?: [];
|
||||
if (!empty($validDeptIds) && $this->tableHasColumn('dice_player', 'dept_id')) {
|
||||
$rootDeptId = (int) min($validDeptIds);
|
||||
$stats['dice_player_legacy'] = Db::table('dice_player')
|
||||
->where(function ($q) {
|
||||
$q->whereNull('dept_id')->whereOr('dept_id', 0);
|
||||
})
|
||||
->update(['dept_id' => $rootDeptId]);
|
||||
}
|
||||
$stats = array_merge($stats, $this->backfillRecordDeptIdByPlayer('dice_play_record'));
|
||||
$stats = array_merge($stats, $this->backfillRecordDeptIdByAdmin('dice_play_record'));
|
||||
$stats = array_merge($stats, $this->backfillRecordDeptIdByPlayer('dice_player_wallet_record'));
|
||||
$stats = array_merge($stats, $this->backfillRecordDeptIdByPlayer('dice_player_ticket_record'));
|
||||
$stats = array_merge($stats, $this->backfillRecordDeptIdByAdmin('dice_play_record_test'));
|
||||
if (!empty($validDeptIds) && $this->tableHasColumn('dice_play_record_test', 'dept_id')) {
|
||||
$rootDeptId = (int) min($validDeptIds);
|
||||
$stats['dice_play_record_test_legacy'] = Db::table('dice_play_record_test')
|
||||
->where(function ($q) {
|
||||
$q->whereNull('dept_id')->whereOr('dept_id', 0);
|
||||
})
|
||||
->update(['dept_id' => $rootDeptId]);
|
||||
}
|
||||
if ($this->tableHasColumn('dice_reward_config_record', 'dept_id')) {
|
||||
$stats['dice_reward_config_record'] = Db::execute(
|
||||
'UPDATE dice_reward_config_record r INNER JOIN sa_system_user u ON r.admin_id = u.id
|
||||
SET r.dept_id = u.dept_id WHERE (r.dept_id IS NULL OR r.dept_id = 0) AND u.dept_id IS NOT NULL AND u.dept_id > 0'
|
||||
);
|
||||
}
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除渠道前关联数据统计
|
||||
*/
|
||||
public function getDestroyPreview(array $deptIds): array
|
||||
{
|
||||
$items = [];
|
||||
foreach ($deptIds as $deptId) {
|
||||
$deptId = (int) $deptId;
|
||||
if ($deptId <= 0) {
|
||||
continue;
|
||||
}
|
||||
$dept = SystemDept::find($deptId);
|
||||
$row = [
|
||||
'dept_id' => $deptId,
|
||||
'dept_name' => $dept ? $dept->name : '',
|
||||
'user_count' => SystemUser::where('dept_id', $deptId)->count(),
|
||||
'relations' => [],
|
||||
];
|
||||
foreach (self::RELATION_TABLES as $table => $meta) {
|
||||
if (!$this->tableHasColumn($table, 'dept_id')) {
|
||||
continue;
|
||||
}
|
||||
$count = $this->countByDept($table, $deptId);
|
||||
if ($count > 0) {
|
||||
$row['relations'][] = [
|
||||
'table' => $table,
|
||||
'label' => $meta['label'],
|
||||
'group' => $meta['group'],
|
||||
'count' => $count,
|
||||
];
|
||||
}
|
||||
}
|
||||
$items[] = $row;
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除渠道及勾选的关联数据
|
||||
*
|
||||
* @param array $deleteTables 要删除的表名列表
|
||||
*/
|
||||
public function destroyDeptWithRelations(int $deptId, array $deleteTables): void
|
||||
{
|
||||
if ($deptId <= 0) {
|
||||
throw new ApiException('Invalid channel id');
|
||||
}
|
||||
$userCount = SystemUser::where('dept_id', $deptId)->count();
|
||||
if ($userCount > 0) {
|
||||
throw new ApiException('This channel has users, please delete or transfer them first');
|
||||
}
|
||||
$allowed = array_keys(self::RELATION_TABLES);
|
||||
foreach ($deleteTables as $table) {
|
||||
if (!in_array($table, $allowed, true)) {
|
||||
continue;
|
||||
}
|
||||
if (!$this->tableHasColumn($table, 'dept_id')) {
|
||||
continue;
|
||||
}
|
||||
Db::table($table)->where('dept_id', $deptId)->delete();
|
||||
}
|
||||
SystemDept::destroy($deptId, true);
|
||||
DiceRewardConfig::refreshCache($deptId);
|
||||
DiceReward::refreshCache($deptId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function defaultTemplateRows(string $table): array
|
||||
{
|
||||
$templateId = AdminScopeHelper::DEFAULT_TEMPLATE_DEPT;
|
||||
$rows = Db::table($table)->where('dept_id', $templateId)->select()->toArray();
|
||||
if (!empty($rows)) {
|
||||
return $rows;
|
||||
}
|
||||
return Db::table($table)->whereNull('dept_id')->select()->toArray();
|
||||
}
|
||||
|
||||
private function backfillRecordDeptIdByPlayer(string $table): array
|
||||
{
|
||||
if (!$this->tableHasColumn($table, 'dept_id') || !$this->tableHasColumn($table, 'player_id')) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
$table => Db::execute(
|
||||
"UPDATE `{$table}` r INNER JOIN dice_player p ON r.player_id = p.id
|
||||
SET r.dept_id = p.dept_id WHERE (r.dept_id IS NULL OR r.dept_id = 0) AND p.dept_id IS NOT NULL AND p.dept_id > 0"
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
private function backfillRecordDeptIdByAdmin(string $table): array
|
||||
{
|
||||
if (!$this->tableHasColumn($table, 'dept_id') || !$this->tableHasColumn($table, 'admin_id')) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
$table => Db::execute(
|
||||
"UPDATE `{$table}` r INNER JOIN sa_system_user u ON r.admin_id = u.id
|
||||
SET r.dept_id = u.dept_id WHERE (r.dept_id IS NULL OR r.dept_id = 0) AND u.dept_id IS NOT NULL AND u.dept_id > 0"
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
private function countByDept(string $table, ?int $deptId): int
|
||||
{
|
||||
$query = Db::table($table);
|
||||
if ($deptId === null || $deptId === AdminScopeHelper::DEFAULT_TEMPLATE_DEPT) {
|
||||
$templateId = AdminScopeHelper::DEFAULT_TEMPLATE_DEPT;
|
||||
$query->where(function ($q) use ($templateId) {
|
||||
$q->where('dept_id', $templateId)->whereOr('dept_id', 'null');
|
||||
});
|
||||
} else {
|
||||
$query->where('dept_id', $deptId);
|
||||
}
|
||||
return $query->count();
|
||||
}
|
||||
|
||||
private function tableHasColumn(string $table, string $column): bool
|
||||
{
|
||||
try {
|
||||
$fields = Db::getFields($table);
|
||||
return isset($fields[$column]);
|
||||
} catch (\Throwable $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user