Files
webman-buildadmin/app/admin/controller/config/DepositChannel.php
2026-04-23 10:15:01 +08:00

374 lines
14 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace app\admin\controller\config;
use app\common\controller\Backend;
use app\common\library\game\DepositChannel as DepositChannelLib;
use app\common\library\game\FinanceCashierConfig as FinanceCashierConfigLib;
use app\common\service\GameHotDataCoordinator;
use app\common\service\GameHotDataLock;
use InvalidArgumentException;
use support\think\Db;
use support\Response;
use Throwable;
use Webman\Http\Request as WebmanRequest;
/**
* 充值支付渠道game_config.deposit_channel
*/
class DepositChannel extends Backend
{
protected bool $modelValidate = false;
protected array $noNeedPermission = ['index', 'save'];
private function hasNodePermission(WebmanRequest $request, string $action): bool
{
if (!$this->auth) {
return false;
}
$controllerPath = get_controller_path($request);
if (!$controllerPath) {
return false;
}
$paths = [];
$paths[] = $controllerPath . '/' . $action;
$parts = explode('/', $controllerPath);
foreach ($parts as &$part) {
if (str_contains($part, '_')) {
$part = lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $part))));
}
}
$paths[] = implode('/', $parts) . '/' . $action;
foreach (array_values(array_unique($paths)) as $path) {
if ($this->auth->check($path)) {
return true;
}
}
return false;
}
protected function initController(WebmanRequest $request): ?Response
{
return null;
}
/**
* 列表baTablelist / total / remark+ registry弹窗展示名
*/
public function index(WebmanRequest $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) {
return $response;
}
if (!$this->hasNodePermission($request, 'index')) {
return $this->error(__('You have no permission'), [], 401);
}
if ($request->method() !== 'GET') {
return $this->error(__('Parameter error'));
}
$registryOut = $this->buildRegistryOut();
$parsed = DepositChannelLib::parseStoredOverridesFromDb();
$items = DepositChannelLib::expandRowsForAdmin($parsed);
$quickSearch = $request->get('quickSearch', '');
if (is_string($quickSearch) && trim($quickSearch) !== '') {
$q = mb_strtolower(trim($quickSearch));
$items = array_values(array_filter($items, static function (array $it) use ($q, $registryOut): bool {
$code = isset($it['code']) && is_string($it['code']) ? mb_strtolower($it['code']) : '';
$name = '';
if (isset($it['code']) && is_string($it['code']) && isset($registryOut[$it['code']])) {
$meta = $registryOut[$it['code']];
$name = isset($meta['name']) && is_string($meta['name']) ? mb_strtolower($meta['name']) : '';
}
return $q === '' || str_contains($code, $q) || str_contains($name, $q);
}));
}
$orderRaw = $request->get('order', '');
if (is_string($orderRaw) && str_contains($orderRaw, ',')) {
$parts = explode(',', $orderRaw, 2);
$field = trim($parts[0]);
$dir = strtolower(trim($parts[1] ?? 'asc'));
if ($field === 'sort') {
usort($items, static function (array $a, array $b) use ($dir): int {
$sa = isset($a['sort']) && is_numeric($a['sort']) ? intval($a['sort']) : 0;
$sb = isset($b['sort']) && is_numeric($b['sort']) ? intval($b['sort']) : 0;
if ($sa !== $sb) {
return $dir === 'desc' ? ($sb <=> $sa) : ($sa <=> $sb);
}
$ca = isset($a['code']) && is_string($a['code']) ? $a['code'] : '';
$cb = isset($b['code']) && is_string($b['code']) ? $b['code'] : '';
return strcmp($ca, $cb);
});
}
}
$total = count($items);
$pageRaw = $request->get('page', '1');
$limitRaw = $request->get('limit', '20');
$page = is_numeric($pageRaw) ? max(1, intval($pageRaw)) : 1;
$limit = is_numeric($limitRaw) ? max(1, min(500, intval($limitRaw))) : 20;
$offset = ($page - 1) * $limit;
$pageRows = array_slice($items, $offset, $limit);
foreach ($pageRows as &$pr) {
if (!is_array($pr)) {
continue;
}
$code = isset($pr['code']) && is_string($pr['code']) ? $pr['code'] : '';
$meta = $code !== '' && isset($registryOut[$code]) ? $registryOut[$code] : null;
$pr['display_name'] = is_array($meta) && isset($meta['name']) && is_string($meta['name']) ? $meta['name'] : $code;
$pr['name_en'] = is_array($meta) && isset($meta['name_en']) && is_string($meta['name_en']) ? $meta['name_en'] : '';
}
unset($pr);
return $this->success('', [
'list' => $pageRows,
'total' => $total,
'remark' => '',
'registry' => $registryOut,
'items' => $pageRows,
]);
}
/**
* 单条读取 / 单条更新(弹窗)
*/
public function edit(WebmanRequest $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) {
return $response;
}
if (!$this->hasNodePermission($request, 'edit')) {
return $this->error(__('You have no permission'), [], 401);
}
if ($request->method() === 'GET') {
$code = $request->get('code', '');
if (!is_string($code) || trim($code) === '') {
$code = $request->get('id', '');
}
if (!is_string($code) || trim($code) === '') {
return $this->error(__('Parameter error'));
}
$code = strtolower(trim($code));
$parsed = DepositChannelLib::parseStoredOverridesFromDb();
$items = DepositChannelLib::expandRowsForAdmin($parsed);
$found = null;
foreach ($items as $it) {
if (!is_array($it)) {
continue;
}
$c = isset($it['code']) && is_string($it['code']) ? strtolower(trim($it['code'])) : '';
if ($c === $code) {
$found = $it;
break;
}
}
if ($found === null) {
return $this->error(__('Record not found'));
}
return $this->success('', ['row' => $found]);
}
if ($request->method() === 'POST') {
$payload = $request->post();
if (!is_array($payload)) {
return $this->error(__('Parameter %s can not be empty', ['']));
}
$code = isset($payload['code']) && is_string($payload['code']) ? strtolower(trim($payload['code'])) : '';
if ($code === '') {
return $this->error(__('Parameter error'));
}
$parsed = DepositChannelLib::parseStoredOverridesFromDb();
$items = DepositChannelLib::expandRowsForAdmin($parsed);
$foundIdx = -1;
$foundRow = null;
foreach ($items as $k => $it) {
if (!is_array($it)) {
continue;
}
$c = isset($it['code']) && is_string($it['code']) ? strtolower(trim($it['code'])) : '';
if ($c === $code) {
$foundIdx = $k;
$foundRow = $it;
break;
}
}
if ($foundIdx < 0 || $foundRow === null) {
return $this->error(__('Record not found'));
}
$items[$foundIdx] = $this->mergeDepositChannelEditPayload($foundRow, $payload, $code);
return $this->persistChannelList($items);
}
return $this->error(__('Parameter error'));
}
public function save(WebmanRequest $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) {
return $response;
}
if (!$this->hasNodePermission($request, 'save')) {
return $this->error(__('You have no permission'), [], 401);
}
if ($request->method() !== 'POST') {
return $this->error(__('Parameter error'));
}
$payload = $request->post();
if (!is_array($payload)) {
return $this->error(__('Parameter %s can not be empty', ['']));
}
$items = $payload['items'] ?? null;
if (!is_array($items)) {
return $this->error('items 必须为数组');
}
return $this->persistChannelList($items);
}
/**
* @param list<array<string, mixed>> $items
*/
private function persistChannelList(array $items): Response
{
try {
$registry = DepositChannelLib::codeRegistry();
$clean = DepositChannelLib::prepareOverridesForSave(array_values($items));
$expectedCodes = array_keys($registry);
sort($expectedCodes);
$got = array_column($clean, 'code');
sort($got);
if ($expectedCodes !== $got) {
return $this->error('请保存全部已注册渠道行(不可缺行)');
}
$json = DepositChannelLib::encodeForDb($clean);
} catch (InvalidArgumentException $e) {
return $this->error($e->getMessage());
}
$now = time();
$resourceKey = GameHotDataLock::safeResourceKeyForConfig(DepositChannelLib::CONFIG_KEY);
$lock = GameHotDataLock::tryAcquire(GameHotDataLock::TYPE_GAME_CONFIG, $resourceKey);
if (!$lock['acquired']) {
return $this->error('该配置正在被其他操作占用,请稍后再试');
}
try {
try {
$exists = Db::name('game_config')->where('config_key', DepositChannelLib::CONFIG_KEY)->find();
if ($exists) {
Db::name('game_config')->where('config_key', DepositChannelLib::CONFIG_KEY)->update([
'config_value' => $json,
'value_type' => 'json',
'update_time' => $now,
]);
} else {
Db::name('game_config')->insert([
'config_key' => DepositChannelLib::CONFIG_KEY,
'config_value' => $json,
'value_type' => 'json',
'remark' => '充值支付渠道 JSON与 finance_cashier.channels 同步)',
'create_time' => $now,
'update_time' => $now,
]);
}
$fcExists = Db::name('game_config')->where('config_key', FinanceCashierConfigLib::CONFIG_KEY)->find();
if ($fcExists) {
$fcPayload = FinanceCashierConfigLib::parseFromConfigValue($fcExists['config_value'] ?? null);
$fcPayload['channels'] = $clean;
$fcJson = FinanceCashierConfigLib::encodeForDb($fcPayload);
Db::name('game_config')->where('config_key', FinanceCashierConfigLib::CONFIG_KEY)->update([
'config_value' => $fcJson,
'value_type' => 'json',
'update_time' => $now,
]);
GameHotDataCoordinator::afterGameConfigKeyCommitted(FinanceCashierConfigLib::CONFIG_KEY);
}
} catch (Throwable $e) {
return $this->error($e->getMessage());
}
GameHotDataCoordinator::afterGameConfigKeyCommitted(DepositChannelLib::CONFIG_KEY);
return $this->success(__('Saved successfully'));
} finally {
GameHotDataLock::release(GameHotDataLock::TYPE_GAME_CONFIG, $resourceKey, $lock['token'], $lock['redis_lock']);
}
}
/**
* @return array<string, array{name: string, name_en: string, sort: int}>
*/
private function buildRegistryOut(): array
{
$registry = DepositChannelLib::codeRegistry();
$registryOut = [];
foreach ($registry as $code => $meta) {
if (!is_array($meta)) {
continue;
}
$registryOut[$code] = [
'name' => isset($meta['name']) && is_string($meta['name']) ? $meta['name'] : '',
'name_en' => isset($meta['name_en']) && is_string($meta['name_en']) ? $meta['name_en'] : '',
'sort' => isset($meta['sort']) && is_numeric($meta['sort']) ? intval($meta['sort']) : 10,
];
}
return $registryOut;
}
/**
* 弹窗整表提交与列表内开关均走此合并:未出现在 payload 中的字段沿用 $current。
*
* @param array<string, mixed> $current
* @param array<string, mixed> $payload
*
* @return array{code: string, sort: int, status: int, tier_ids: list<string>}
*/
private function mergeDepositChannelEditPayload(array $current, array $payload, string $code): array
{
$form = [];
if (array_key_exists('sort', $payload)) {
$form['sort'] = $payload['sort'];
} else {
$form['sort'] = $current['sort'] ?? 0;
}
if (array_key_exists('status', $payload)) {
$form['status'] = $payload['status'];
} else {
$form['status'] = $current['status'] ?? 0;
}
return $this->normalizeChannelFormRow($form, $code);
}
/**
* @param array<string, mixed> $payload
*
* @return array{code: string, sort: int, status: int, tier_ids: list<string>}
*/
private function normalizeChannelFormRow(array $payload, string $code): array
{
$sort = isset($payload['sort']) && is_numeric($payload['sort']) ? intval($payload['sort']) : 0;
$status = 0;
$st = $payload['status'] ?? 1;
if ($st === true || $st === 1 || $st === '1') {
$status = 1;
}
return [
'code' => $code,
'sort' => $sort,
'status' => $status,
'tier_ids' => [],
];
}
}