[色子游戏]玩家-新增玩家钱包操作
This commit is contained in:
@@ -80,5 +80,21 @@ export default {
|
||||
url: '/dice/player_wallet_record/DicePlayerWalletRecord/getPlayerWalletBefore',
|
||||
params: { player_id: playerId }
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 管理员钱包操作(加点/扣点):更新玩家平台币并创建流水记录
|
||||
* type 3=加点 4=扣点
|
||||
*/
|
||||
adminOperate(params: {
|
||||
player_id: number
|
||||
type: 3 | 4
|
||||
coin: number
|
||||
remark?: string
|
||||
}) {
|
||||
return request.post<any>({
|
||||
url: '/dice/player_wallet_record/DicePlayerWalletRecord/adminOperate',
|
||||
data: params
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,9 +51,16 @@
|
||||
@change="(v: string | number | boolean) => handleStatusChange(row, v ? 1 : 0)"
|
||||
/>
|
||||
</template>
|
||||
<!-- 平台币:tag 展示 -->
|
||||
<!-- 平台币:tag 可点击打开钱包操作弹窗 -->
|
||||
<template #coin="{ row }">
|
||||
<ElTag type="info" size="small">{{ row.coin ?? 0 }}</ElTag>
|
||||
<ElTag
|
||||
type="info"
|
||||
size="small"
|
||||
class="cursor-pointer hover:opacity-80"
|
||||
@click="openWalletOperate(row)"
|
||||
>
|
||||
{{ row.coin ?? 0 }}
|
||||
</ElTag>
|
||||
</template>
|
||||
<!-- 操作列 -->
|
||||
<template #operation="{ row }">
|
||||
@@ -80,6 +87,13 @@
|
||||
:data="dialogData"
|
||||
@success="refreshData"
|
||||
/>
|
||||
|
||||
<!-- 钱包操作弹窗(加点/扣点) -->
|
||||
<WalletOperateDialog
|
||||
v-model="walletDialogVisible"
|
||||
:player="walletOperatePlayer"
|
||||
@success="refreshData"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -89,6 +103,7 @@
|
||||
import api from '../../api/player/index'
|
||||
import TableSearch from './modules/table-search.vue'
|
||||
import EditDialog from './modules/edit-dialog.vue'
|
||||
import WalletOperateDialog from './modules/WalletOperateDialog.vue'
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = ref({
|
||||
@@ -188,4 +203,13 @@
|
||||
handleSelectionChange,
|
||||
selectedRows
|
||||
} = useSaiAdmin()
|
||||
|
||||
// 钱包操作弹窗(从平台币 tag 点击打开)
|
||||
const walletDialogVisible = ref(false)
|
||||
const walletOperatePlayer = ref<Record<string, any> | null>(null)
|
||||
|
||||
function openWalletOperate(row: Record<string, any>) {
|
||||
walletOperatePlayer.value = row
|
||||
walletDialogVisible.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="玩家钱包操作"
|
||||
width="480px"
|
||||
align-center
|
||||
:close-on-click-modal="false"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100px">
|
||||
<el-form-item label="玩家">
|
||||
<el-input :model-value="player?.username" disabled placeholder="-" />
|
||||
</el-form-item>
|
||||
<el-form-item label="钱包余额">
|
||||
<el-input-number
|
||||
:model-value="walletBalance"
|
||||
disabled
|
||||
:precision="2"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="操作类型" prop="type">
|
||||
<el-select v-model="formData.type" placeholder="请选择" clearable style="width: 100%">
|
||||
<el-option label="加点" :value="3" />
|
||||
<el-option label="扣点" :value="4" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="平台币变动" prop="coin">
|
||||
<el-input-number
|
||||
v-model="formData.coin"
|
||||
:min="0.01"
|
||||
:precision="2"
|
||||
placeholder="正数,扣点时不能超过余额"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
v-model="formData.remark"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="选填,不填则按类型自动填写"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="handleSubmit">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import walletRecordApi from '../../../api/player_wallet_record/index'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
|
||||
interface PlayerRow {
|
||||
id: number
|
||||
username?: string
|
||||
coin?: number
|
||||
}
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean
|
||||
player: PlayerRow | null
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
(e: 'success'): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
modelValue: false,
|
||||
player: null
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const submitting = ref(false)
|
||||
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (v) => emit('update:modelValue', v)
|
||||
})
|
||||
|
||||
const walletBalance = computed(() => {
|
||||
const c = props.player?.coin
|
||||
return c != null && c !== '' ? Number(c) : 0
|
||||
})
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
type: [{ required: true, message: '请选择操作类型', trigger: 'change' }],
|
||||
coin: [
|
||||
{ required: true, message: '请输入平台币变动', trigger: 'blur' },
|
||||
{
|
||||
validator: (_rule, value, callback) => {
|
||||
const n = Number(value)
|
||||
if (Number.isNaN(n) || n <= 0) {
|
||||
callback(new Error('平台币变动必须大于 0'))
|
||||
return
|
||||
}
|
||||
if (formData.type === 4 && n > walletBalance.value) {
|
||||
callback(new Error('扣点不能超过当前余额'))
|
||||
return
|
||||
}
|
||||
callback()
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const initialFormData = {
|
||||
type: null as 3 | 4 | null,
|
||||
coin: null as number | null,
|
||||
remark: ''
|
||||
}
|
||||
|
||||
const formData = reactive({ ...initialFormData })
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(open) => {
|
||||
if (open) {
|
||||
Object.assign(formData, initialFormData)
|
||||
formData.type = 3
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const handleClose = () => {
|
||||
visible.value = false
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value || !props.player?.id) return
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
const coin = Number(formData.coin) || 0
|
||||
if (coin <= 0) {
|
||||
ElMessage.warning('平台币变动必须大于 0')
|
||||
return
|
||||
}
|
||||
if (formData.type === 4 && coin > walletBalance.value) {
|
||||
ElMessage.warning('扣点不能超过当前余额')
|
||||
return
|
||||
}
|
||||
submitting.value = true
|
||||
await walletRecordApi.adminOperate({
|
||||
player_id: props.player.id,
|
||||
type: formData.type!,
|
||||
coin,
|
||||
remark: formData.remark?.trim() || undefined
|
||||
})
|
||||
ElMessage.success('操作成功')
|
||||
emit('success')
|
||||
handleClose()
|
||||
} catch (e) {
|
||||
if ((e as any)?.message) ElMessage.warning((e as any).message)
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -102,9 +102,23 @@
|
||||
getData()
|
||||
}
|
||||
|
||||
// 类型展示:0=充值 1=提现 2=购买抽奖次数
|
||||
const typeFormatter = (row: Record<string, unknown>) =>
|
||||
row.type === 0 ? '充值' : row.type === 1 ? '提现' : row.type === 2 ? '购买抽奖次数' : '-'
|
||||
// 类型展示:0=充值 1=提现 2=购买抽奖次数 3=管理员加点 4=管理员扣点
|
||||
const typeFormatter = (row: Record<string, unknown>) => {
|
||||
const t = row.type
|
||||
if (t === 0) return '充值'
|
||||
if (t === 1) return '提现'
|
||||
if (t === 2) return '购买抽奖次数'
|
||||
if (t === 3) return '管理员加点'
|
||||
if (t === 4) return '管理员扣点'
|
||||
return '-'
|
||||
}
|
||||
|
||||
// 操作人:关联管理员用户名
|
||||
const operatorFormatter = (row: Record<string, any>) => {
|
||||
const op = row.operator ?? row.operator_id
|
||||
if (op && typeof op === 'object' && op.username) return op.username
|
||||
return row.user_id ?? '-'
|
||||
}
|
||||
|
||||
// 用户列展示为 dicePlayer.username(兼容 dice_player)
|
||||
const usernameFormatter = (row: Record<string, any>) => {
|
||||
@@ -135,6 +149,12 @@
|
||||
{ prop: 'player_id', label: '用户', width: 120, formatter: usernameFormatter },
|
||||
{ prop: 'coin', label: '平台币变化', width: 110 },
|
||||
{ prop: 'type', label: '类型', width: 120, formatter: typeFormatter },
|
||||
{
|
||||
prop: 'user_id',
|
||||
label: '操作人',
|
||||
width: 100,
|
||||
formatter: operatorFormatter
|
||||
},
|
||||
{ prop: 'wallet_before', label: '钱包操作前', width: 110 },
|
||||
{ prop: 'wallet_after', label: '钱包操作后', width: 110 },
|
||||
{
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
<el-option label="充值" :value="0" />
|
||||
<el-option label="提现" :value="1" />
|
||||
<el-option label="购买抽奖次数" :value="2" />
|
||||
<el-option label="管理员加点" :value="3" />
|
||||
<el-option label="管理员扣点" :value="4" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="平台币变化" prop="coin">
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
<el-option label="充值" :value="0" />
|
||||
<el-option label="提现" :value="1" />
|
||||
<el-option label="购买抽奖次数" :value="2" />
|
||||
<el-option label="管理员加点" :value="3" />
|
||||
<el-option label="管理员扣点" :value="4" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
@@ -13,6 +13,7 @@ use app\dice\model\player\DicePlayer;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Tinywan\Jwt\JwtToken;
|
||||
|
||||
/**
|
||||
* 玩家钱包流水控制器
|
||||
@@ -48,6 +49,7 @@ class DicePlayerWalletRecordController extends BaseController
|
||||
$query = $this->logic->search($where);
|
||||
$query->with([
|
||||
'dicePlayer',
|
||||
'operator',
|
||||
]);
|
||||
$data = $this->logic->getList($query);
|
||||
return $this->success($data);
|
||||
@@ -106,6 +108,61 @@ class DicePlayerWalletRecordController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员钱包操作(加点/扣点):更新玩家平台币并创建流水记录
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('玩家钱包流水添加', 'dice:player_wallet_record:index:save')]
|
||||
public function adminOperate(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$playerId = $data['player_id'] ?? null;
|
||||
$type = isset($data['type']) ? (int) $data['type'] : null;
|
||||
$coin = isset($data['coin']) ? (float) $data['coin'] : null;
|
||||
|
||||
if ($playerId === null || $playerId === '') {
|
||||
return $this->fail('请选择玩家');
|
||||
}
|
||||
if (!in_array($type, [3, 4], true)) {
|
||||
return $this->fail('操作类型必须为 3=加点 或 4=扣点');
|
||||
}
|
||||
if ($coin === null || $coin <= 0) {
|
||||
return $this->fail('平台币变动必须大于 0');
|
||||
}
|
||||
|
||||
$data['player_id'] = $playerId;
|
||||
$data['type'] = $type;
|
||||
$data['coin'] = $coin;
|
||||
$data['remark'] = $data['remark'] ?? '';
|
||||
|
||||
$adminId = null;
|
||||
$checkAdmin = request()->header('check_admin');
|
||||
if (!empty($checkAdmin['id'])) {
|
||||
$adminId = (int) $checkAdmin['id'];
|
||||
}
|
||||
if (($adminId === null || $adminId <= 0)) {
|
||||
try {
|
||||
$token = JwtToken::getExtend();
|
||||
if (!empty($token['id']) && ($token['plat'] ?? '') === 'saiadmin') {
|
||||
$adminId = (int) $token['id'];
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// JWT 无效或未携带
|
||||
}
|
||||
}
|
||||
if ($adminId === null || $adminId <= 0) {
|
||||
return $this->fail('请先登录');
|
||||
}
|
||||
|
||||
try {
|
||||
$this->logic->adminOperate($data, $adminId);
|
||||
return $this->success('操作成功');
|
||||
} catch (\Throwable $e) {
|
||||
return $this->fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据
|
||||
* @param Request $request
|
||||
|
||||
@@ -8,8 +8,8 @@ namespace app\dice\logic\player_wallet_record;
|
||||
|
||||
use plugin\saiadmin\basic\think\BaseLogic;
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
use plugin\saiadmin\utils\Helper;
|
||||
use app\dice\model\player_wallet_record\DicePlayerWalletRecord;
|
||||
use app\dice\model\player\DicePlayer;
|
||||
|
||||
/**
|
||||
* 玩家钱包流水逻辑层
|
||||
@@ -35,4 +35,57 @@ class DicePlayerWalletRecordLogic extends BaseLogic
|
||||
return parent::add($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员钱包操作(加点/扣点):更新玩家平台币并创建流水记录
|
||||
* @param array $data player_id, type (3=加点 4=扣点), coin (正数), remark (可选)
|
||||
* @param int $adminId 当前管理员 id
|
||||
* @return mixed
|
||||
* @throws ApiException
|
||||
*/
|
||||
public function adminOperate(array $data, int $adminId): mixed
|
||||
{
|
||||
$playerId = (int) ($data['player_id'] ?? 0);
|
||||
$type = (int) ($data['type'] ?? 0);
|
||||
$coin = (float) ($data['coin'] ?? 0);
|
||||
|
||||
if ($playerId <= 0 || !in_array($type, [3, 4], true)) {
|
||||
throw new ApiException('参数错误:需要有效的 player_id 和 type(3=加点,4=扣点)');
|
||||
}
|
||||
if ($coin <= 0) {
|
||||
throw new ApiException('平台币变动必须大于 0');
|
||||
}
|
||||
|
||||
$player = DicePlayer::where('id', $playerId)->find();
|
||||
if (!$player) {
|
||||
throw new ApiException('玩家不存在');
|
||||
}
|
||||
|
||||
$walletBefore = (float) ($player['coin'] ?? 0);
|
||||
if ($type === 4 && $walletBefore < $coin) {
|
||||
throw new ApiException('扣点数量不能大于当前余额');
|
||||
}
|
||||
|
||||
$walletAfter = $type === 3 ? $walletBefore + $coin : $walletBefore - $coin;
|
||||
$remark = trim((string) ($data['remark'] ?? ''));
|
||||
if ($remark === '') {
|
||||
$remark = $type === 3 ? '管理员加点' : '管理员扣点';
|
||||
}
|
||||
|
||||
DicePlayer::where('id', $playerId)->update(['coin' => $walletAfter]);
|
||||
|
||||
$record = [
|
||||
'player_id' => $playerId,
|
||||
'coin' => $type === 3 ? $coin : -$coin,
|
||||
'type' => $type,
|
||||
'wallet_before' => $walletBefore,
|
||||
'wallet_after' => $walletAfter,
|
||||
'remark' => $remark,
|
||||
'user_id' => $adminId,
|
||||
'total_draw_count' => 0,
|
||||
'paid_draw_count' => 0,
|
||||
'free_draw_count' => 0,
|
||||
];
|
||||
|
||||
return $this->model->create($record);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace app\dice\model\player_wallet_record;
|
||||
|
||||
use app\dice\model\player\DicePlayer;
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
use plugin\saiadmin\app\model\system\SystemUser;
|
||||
use think\model\relation\BelongsTo;
|
||||
|
||||
/**
|
||||
* 玩家钱包流水模型
|
||||
@@ -24,6 +26,7 @@ use plugin\saiadmin\basic\think\BaseModel;
|
||||
* @property $paid_draw_count 购买抽奖次数
|
||||
* @property $free_draw_count 赠送抽奖次数
|
||||
* @property $remark 备注
|
||||
* @property $user_id 操作管理员id(type 3/4 时记录)
|
||||
* @property $create_time 创建时间
|
||||
* @property $update_time 修改时间
|
||||
*/
|
||||
@@ -44,11 +47,19 @@ class DicePlayerWalletRecord extends BaseModel
|
||||
/**
|
||||
* 关联模型 dicePlayer
|
||||
*/
|
||||
public function dicePlayer(): \think\model\relation\BelongsTo
|
||||
public function dicePlayer(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(DicePlayer::class, 'player_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联操作管理员(type 3/4 时有值)
|
||||
*/
|
||||
public function operator(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(SystemUser::class, 'user_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型 搜索
|
||||
*/
|
||||
|
||||
@@ -21,14 +21,14 @@ class BaseController extends OpenController
|
||||
protected $adminInfo;
|
||||
|
||||
/**
|
||||
* 当前登陆管理员ID
|
||||
* 当前登陆管理员ID(未登录时为 null)
|
||||
*/
|
||||
protected int $adminId;
|
||||
protected ?int $adminId = null;
|
||||
|
||||
/**
|
||||
* 当前登陆管理员账号
|
||||
* 当前登陆管理员账号(未登录时为空字符串)
|
||||
*/
|
||||
protected string $adminName;
|
||||
protected string $adminName = '';
|
||||
|
||||
/**
|
||||
* 逻辑层注入
|
||||
|
||||
Reference in New Issue
Block a user