[游戏管理]用户管理-优化钱包操作
This commit is contained in:
@@ -20,7 +20,7 @@ class User extends Backend
|
||||
*/
|
||||
protected ?object $model = null;
|
||||
|
||||
protected array|string $preExcludeFields = ['id', 'uuid', 'create_time', 'update_time', 'invite_code'];
|
||||
protected array|string $preExcludeFields = ['id', 'uuid', 'create_time', 'update_time', 'invite_code', 'coin', 'total_deposit_coin', 'total_valid_bet_coin'];
|
||||
|
||||
protected array $withJoinTable = ['channel', 'admin'];
|
||||
|
||||
@@ -200,6 +200,159 @@ class User extends Backend
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台钱包加减点(不允许在用户编辑表单直接改余额)
|
||||
*/
|
||||
public function walletAdjust(WebmanRequest $request): Response
|
||||
{
|
||||
$response = $this->initializeBackend($request);
|
||||
if ($response !== null) {
|
||||
return $response;
|
||||
}
|
||||
if ($request->method() !== 'POST') {
|
||||
return $this->error(__('Parameter error'));
|
||||
}
|
||||
|
||||
$userIdRaw = $request->post('user_id');
|
||||
$userId = is_numeric(strval($userIdRaw)) ? intval(strval($userIdRaw)) : 0;
|
||||
if ($userId <= 0) {
|
||||
return $this->error(__('Parameter error'));
|
||||
}
|
||||
|
||||
$opRaw = $request->post('op');
|
||||
$op = is_string($opRaw) ? trim($opRaw) : '';
|
||||
if (!in_array($op, ['credit', 'deduct'], true)) {
|
||||
return $this->error('操作类型不正确');
|
||||
}
|
||||
|
||||
$amountRaw = $request->post('amount');
|
||||
$amountText = is_string($amountRaw) || is_numeric($amountRaw) ? trim(strval($amountRaw)) : '';
|
||||
if ($amountText === '' || !is_numeric($amountText)) {
|
||||
return $this->error('金额格式不正确');
|
||||
}
|
||||
if (bccomp($amountText, '0', 4) <= 0) {
|
||||
return $this->error('金额必须大于0');
|
||||
}
|
||||
|
||||
$remarkRaw = $request->post('remark');
|
||||
$remark = is_string($remarkRaw) ? trim($remarkRaw) : '';
|
||||
$adminName = is_string($this->auth->username ?? null) ? $this->auth->username : ('#' . strval($this->auth->id));
|
||||
$amountForRemark = self::formatAmountForDisplay($amountText);
|
||||
if ($remark === '') {
|
||||
$actionText = $op === 'credit' ? '加点' : '扣点';
|
||||
$remark = '后台管理员(' . $adminName . ')' . $actionText . $amountForRemark . '(值)';
|
||||
}
|
||||
|
||||
$user = $this->model->where('id', $userId)->find();
|
||||
if (!$user) {
|
||||
return $this->error(__('Record not found'));
|
||||
}
|
||||
$dataLimitAdminIds = $this->getDataLimitAdminIds();
|
||||
if ($dataLimitAdminIds && !in_array($user[$this->dataLimitField], $dataLimitAdminIds)) {
|
||||
return $this->error(__('You have no permission'));
|
||||
}
|
||||
|
||||
$channelIdRaw = $user['channel_id'] ?? null;
|
||||
$channelId = is_numeric(strval($channelIdRaw)) ? intval(strval($channelIdRaw)) : null;
|
||||
$before = strval($user['coin'] ?? '0');
|
||||
$delta = self::normalizeAmountScale($amountText, 4);
|
||||
if ($op === 'credit') {
|
||||
$after = bcadd($before, $delta, 4);
|
||||
$bizType = 'admin_credit';
|
||||
$direction = 1;
|
||||
} else {
|
||||
if (bccomp($before, $delta, 4) < 0) {
|
||||
return $this->error('余额不足,扣点失败');
|
||||
}
|
||||
$after = bcsub($before, $delta, 4);
|
||||
$bizType = 'admin_deduct';
|
||||
$direction = 2;
|
||||
}
|
||||
|
||||
$now = time();
|
||||
$idem = 'admin_adjust_' . $userId . '_' . $this->auth->id . '_' . $now . '_' . random_int(1000, 9999);
|
||||
Db::startTrans();
|
||||
try {
|
||||
Db::name('user')->where('id', $userId)->update([
|
||||
'coin' => $after,
|
||||
'update_time' => $now,
|
||||
]);
|
||||
|
||||
Db::name('user_wallet_record')->insert([
|
||||
'user_id' => $userId,
|
||||
'channel_id' => $channelId,
|
||||
'biz_type' => $bizType,
|
||||
'direction' => $direction,
|
||||
'amount' => $delta,
|
||||
'balance_before' => $before,
|
||||
'balance_after' => $after,
|
||||
'ref_type' => 'admin_user_wallet_adjust',
|
||||
'ref_id' => null,
|
||||
'idempotency_key' => $idem,
|
||||
'operator_admin_id' => intval(strval($this->auth->id)),
|
||||
'remark' => substr($remark, 0, 500),
|
||||
'create_time' => $now,
|
||||
]);
|
||||
Db::commit();
|
||||
} catch (Throwable $e) {
|
||||
Db::rollback();
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
|
||||
return $this->success('钱包调整成功', [
|
||||
'user_id' => $userId,
|
||||
'coin_before' => self::formatAmountForDisplay($before),
|
||||
'coin_after' => self::formatAmountForDisplay($after),
|
||||
'amount' => self::formatAmountForDisplay($delta),
|
||||
'op' => $op,
|
||||
]);
|
||||
}
|
||||
|
||||
private static function normalizeAmountScale(string $amount, int $scale): string
|
||||
{
|
||||
$raw = trim(str_replace(',', '.', $amount));
|
||||
if ($raw === '') {
|
||||
return '0';
|
||||
}
|
||||
$negative = false;
|
||||
if (str_starts_with($raw, '-')) {
|
||||
$negative = true;
|
||||
$raw = ltrim(substr($raw, 1));
|
||||
}
|
||||
if (!str_contains($raw, '.')) {
|
||||
$v = ltrim($raw, '0');
|
||||
$v = $v === '' ? '0' : $v;
|
||||
return $negative ? ('-' . $v) : $v;
|
||||
}
|
||||
[$intPart, $fracPart] = explode('.', $raw, 2);
|
||||
$intPart = ltrim($intPart, '0');
|
||||
$intPart = $intPart === '' ? '0' : $intPart;
|
||||
$fracPart = preg_replace('/\D+/', '', $fracPart) ?? '';
|
||||
if (strlen($fracPart) > $scale) {
|
||||
$fracPart = substr($fracPart, 0, $scale);
|
||||
} else {
|
||||
$fracPart = str_pad($fracPart, $scale, '0');
|
||||
}
|
||||
$v = $intPart . '.' . $fracPart;
|
||||
return $negative ? ('-' . $v) : $v;
|
||||
}
|
||||
|
||||
private static function formatAmountForDisplay(string $amount): string
|
||||
{
|
||||
$normalized = self::normalizeAmountScale($amount, 4);
|
||||
$negative = false;
|
||||
if (str_starts_with($normalized, '-')) {
|
||||
$negative = true;
|
||||
$normalized = substr($normalized, 1);
|
||||
}
|
||||
$parts = explode('.', $normalized, 2);
|
||||
$intPart = $parts[0] ?? '0';
|
||||
$fracPart = $parts[1] ?? '0000';
|
||||
$displayFrac = substr($fracPart, 0, 2);
|
||||
$v = $intPart . '.' . str_pad($displayFrac, 2, '0');
|
||||
return $negative ? ('-' . $v) : $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色组 → 管理员树(仅当前账号可管理的角色组及其下管理员;用于游戏用户归属)
|
||||
* 同一管理员若属于多个组,只挂在 id 最小的所属组下,避免树中重复 value
|
||||
|
||||
Reference in New Issue
Block a user