1.所有接口需要根据agent_id绑定渠道

2.移除所有记录页面的更新按钮,只能查看数据
3.将所有软删除修改为硬删除
This commit is contained in:
2026-05-19 12:04:34 +08:00
parent b089f302de
commit 1f25280dfd
30 changed files with 325 additions and 592 deletions

View File

@@ -427,6 +427,9 @@
"rewardConfigRecord": "Dice Weight Test Records", "rewardConfigRecord": "Dice Weight Test Records",
"playRecordTest": "Draw Records (Test Weight)", "playRecordTest": "Draw Records (Test Weight)",
"config": "Game Config" "config": "Game Config"
},
"game": {
"title": "Game Management"
} }
}, },
"table": { "table": {

View File

@@ -38,8 +38,31 @@
"ruleGameNameRequired": "Chinese name is required", "ruleGameNameRequired": "Chinese name is required",
"ruleGameTypeRequired": "Game type is required" "ruleGameTypeRequired": "Game type is required"
}, },
"table": { "search": {
"providerCode": "Provider Code",
"placeholderProviderCode": "Enter provider code",
"gameCode": "Game Code",
"placeholderGameCode": "Enter game code",
"gameType": "Game Type",
"placeholderGameType": "Enter game type",
"status": "Status",
"placeholderStatus": "Select status",
"statusEnabled": "Enabled", "statusEnabled": "Enabled",
"statusDisabled": "Disabled" "statusDisabled": "Disabled"
},
"table": {
"id": "ID",
"provider": "Provider",
"providerCode": "Provider Code",
"gameCode": "Game Code",
"gameKey": "Game Key",
"gameName": "Name (ZH)",
"gameNameEn": "Name (EN)",
"gameType": "Type",
"sort": "Sort",
"status": "Status",
"statusEnabled": "Enabled",
"statusDisabled": "Disabled",
"updateTime": "Update Time"
} }
} }

View File

@@ -423,6 +423,9 @@
"rewardConfigRecord": "权重测试记录", "rewardConfigRecord": "权重测试记录",
"playRecordTest": "抽奖记录(测试权重)", "playRecordTest": "抽奖记录(测试权重)",
"config": "游戏配置" "config": "游戏配置"
},
"game": {
"title": "游戏管理"
} }
}, },
"table": { "table": {

View File

@@ -38,8 +38,31 @@
"ruleGameNameRequired": "请输入中文名称", "ruleGameNameRequired": "请输入中文名称",
"ruleGameTypeRequired": "请输入游戏类型" "ruleGameTypeRequired": "请输入游戏类型"
}, },
"table": { "search": {
"providerCode": "供应商编码",
"placeholderProviderCode": "请输入供应商编码",
"gameCode": "游戏编号",
"placeholderGameCode": "请输入游戏编号",
"gameType": "游戏类型",
"placeholderGameType": "请输入游戏类型",
"status": "状态",
"placeholderStatus": "请选择状态",
"statusEnabled": "启用", "statusEnabled": "启用",
"statusDisabled": "禁用" "statusDisabled": "禁用"
},
"table": {
"id": "ID",
"provider": "供应商",
"providerCode": "供应商编码",
"gameCode": "游戏编号",
"gameKey": "游戏唯一值",
"gameName": "中文名",
"gameNameEn": "英文名",
"gameType": "类型",
"sort": "排序",
"status": "状态",
"statusEnabled": "启用",
"statusDisabled": "禁用",
"updateTime": "更新时间"
} }
} }

View File

@@ -46,12 +46,21 @@ export async function loadPageLocale(routePath: string): Promise<void> {
const modules = locale === LanguageEnum.EN ? enModules : zhModules const modules = locale === LanguageEnum.EN ? enModules : zhModules
const tryPaths: string[] = [path] const tryPaths: string[] = [path]
// 兼容别名路由:例如 /user 实际页面为 /system/user // 兼容别名路由:菜单 path 为短名但 locale 文件位于模块子目录
// 例如:/user -> system/user/game -> dice/game
if (!path.includes('/')) { if (!path.includes('/')) {
tryPaths.push(`system/${path}`) tryPaths.push(`system/${path}`)
} // 兜底:在任意一级子目录下查找同名文件
if (path === 'user') { const suffix = `/${path}.json`
tryPaths.push('system/user') const localePrefix = `./langs/${locale}/`
for (const key of Object.keys(modules)) {
if (key.startsWith(localePrefix) && key.endsWith(suffix)) {
const candidate = key.slice(localePrefix.length, -'.json'.length)
if (!tryPaths.includes(candidate)) {
tryPaths.push(candidate)
}
}
}
} }
let matchedPath: string | null = null let matchedPath: string | null = null

View File

@@ -61,6 +61,10 @@ export const MAP_PATH_TO_MENU_I18N_KEY: Record<string, string> = {
'/dice/play_record_test/index': 'menus.dice.playRecordTest', '/dice/play_record_test/index': 'menus.dice.playRecordTest',
'/dice/config': 'menus.dice.config', '/dice/config': 'menus.dice.config',
'/dice/config/index': 'menus.dice.config', '/dice/config/index': 'menus.dice.config',
'game': 'menus.game.title',
'game/index': 'menus.game.title',
'/game': 'menus.game.title',
'/game/index': 'menus.game.title',
'/result/success': 'menus.result.success', '/result/success': 'menus.result.success',
'/result/fail': 'menus.result.fail', '/result/fail': 'menus.result.fail',
'/exception/403': 'menus.exception.forbidden', '/exception/403': 'menus.exception.forbidden',

View File

@@ -39,18 +39,6 @@ export default {
}) })
}, },
/**
* 更新数据
* @param params 数据参数
* @returns 执行结果
*/
update(params: Record<string, any>) {
return request.put<any>({
url: '/core/dice/play_record/DicePlayRecord/update',
data: params
})
},
/** /**
* 删除数据 * 删除数据
* @param id 数据ID * @param id 数据ID

View File

@@ -39,18 +39,6 @@ export default {
}) })
}, },
/**
* 更新数据
* @param params 数据参数
* @returns 执行结果
*/
update(params: Record<string, any>) {
return request.put<any>({
url: '/core/dice/play_record_test/DicePlayRecordTest/update',
data: params
})
},
/** /**
* 删除数据 * 删除数据
* @param id 数据ID * @param id 数据ID

View File

@@ -39,18 +39,6 @@ export default {
}) })
}, },
/**
* 更新数据
* @param params 数据参数
* @returns 执行结果
*/
update(params: Record<string, any>) {
return request.put<any>({
url: '/core/dice/player_ticket_record/DicePlayerTicketRecord/update',
data: params
})
},
/** /**
* 删除数据 * 删除数据
* @param id 数据ID * @param id 数据ID

View File

@@ -39,18 +39,6 @@ export default {
}) })
}, },
/**
* 更新数据
* @param params 数据参数
* @returns 执行结果
*/
update(params: Record<string, any>) {
return request.put<any>({
url: '/core/dice/player_wallet_record/DicePlayerWalletRecord/update',
data: params
})
},
/** /**
* 删除数据 * 删除数据
* @param id 数据ID * @param id 数据ID

View File

@@ -39,18 +39,6 @@ export default {
}) })
}, },
/**
* 更新数据
* @param params 数据参数
* @returns 执行结果
*/
update(params: Record<string, any>) {
return request.put<any>({
url: '/core/dice/reward_config_record/DiceRewardConfigRecord/update',
data: params
})
},
/** /**
* 删除数据 * 删除数据
* @param id 数据ID * @param id 数据ID

View File

@@ -10,7 +10,7 @@
<template #icon> <template #icon>
<ArtSvgIcon icon="ri:add-fill" /> <ArtSvgIcon icon="ri:add-fill" />
</template> </template>
新增 {{ $t('table.actions.add') }}
</ElButton> </ElButton>
<ElButton <ElButton
v-permission="'dice:game:index:destroy'" v-permission="'dice:game:index:destroy'"
@@ -21,7 +21,7 @@
<template #icon> <template #icon>
<ArtSvgIcon icon="ri:delete-bin-5-line" /> <ArtSvgIcon icon="ri:delete-bin-5-line" />
</template> </template>
删除 {{ $t('table.actions.delete') }}
</ElButton> </ElButton>
</ElSpace> </ElSpace>
</template> </template>
@@ -108,20 +108,20 @@
apiParams: { limit: 100 }, apiParams: { limit: 100 },
columnsFactory: () => [ columnsFactory: () => [
{ type: 'selection', align: 'center' }, { type: 'selection', align: 'center' },
{ prop: 'id', label: 'ID', width: 80, align: 'center' }, { prop: 'id', label: 'page.table.id', width: 80, align: 'center' },
{ prop: 'provider', label: '供应商', minWidth: 120, align: 'center' }, { prop: 'provider', label: 'page.table.provider', minWidth: 120, align: 'center' },
{ prop: 'provider_code', label: '供应商编码', minWidth: 120, align: 'center' }, { prop: 'provider_code', label: 'page.table.providerCode', minWidth: 120, align: 'center' },
{ prop: 'game_code', label: '游戏编号', minWidth: 120, align: 'center' }, { prop: 'game_code', label: 'page.table.gameCode', minWidth: 120, align: 'center' },
{ prop: 'game_key', label: '游戏唯一值', minWidth: 120, align: 'center' }, { prop: 'game_key', label: 'page.table.gameKey', minWidth: 120, align: 'center' },
{ prop: 'game_name', label: '中文名', minWidth: 120, align: 'center' }, { prop: 'game_name', label: 'page.table.gameName', minWidth: 120, align: 'center' },
{ prop: 'game_name_en', label: '英文名', minWidth: 120, align: 'center' }, { prop: 'game_name_en', label: 'page.table.gameNameEn', minWidth: 120, align: 'center' },
{ prop: 'game_type', label: '类型', minWidth: 90, align: 'center' }, { prop: 'game_type', label: 'page.table.gameType', minWidth: 90, align: 'center' },
{ prop: 'sort', label: '排序', width: 80, align: 'center' }, { prop: 'sort', label: 'page.table.sort', width: 80, align: 'center' },
{ prop: 'status', label: '状态', width: 90, align: 'center', useSlot: true }, { prop: 'status', label: 'page.table.status', width: 90, align: 'center', useSlot: true },
{ prop: 'update_time', label: '更新时间', minWidth: 160, align: 'center' }, { prop: 'update_time', label: 'page.table.updateTime', minWidth: 160, align: 'center' },
{ {
prop: 'operation', prop: 'operation',
label: '操作', label: 'table.actions.operation',
width: 110, width: 110,
align: 'center', align: 'center',
fixed: 'right', fixed: 'right',

View File

@@ -8,25 +8,41 @@
@search="handleSearch" @search="handleSearch"
> >
<el-col v-bind="setSpan(6)"> <el-col v-bind="setSpan(6)">
<el-form-item label="供应商编码" prop="provider_code"> <el-form-item :label="$t('page.search.providerCode')" prop="provider_code">
<el-input v-model="formData.provider_code" placeholder="请输入供应商编码" clearable /> <el-input
v-model="formData.provider_code"
:placeholder="$t('page.search.placeholderProviderCode')"
clearable
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col v-bind="setSpan(6)"> <el-col v-bind="setSpan(6)">
<el-form-item label="游戏编号" prop="game_code"> <el-form-item :label="$t('page.search.gameCode')" prop="game_code">
<el-input v-model="formData.game_code" placeholder="请输入游戏编号" clearable /> <el-input
v-model="formData.game_code"
:placeholder="$t('page.search.placeholderGameCode')"
clearable
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col v-bind="setSpan(6)"> <el-col v-bind="setSpan(6)">
<el-form-item label="游戏类型" prop="game_type"> <el-form-item :label="$t('page.search.gameType')" prop="game_type">
<el-input v-model="formData.game_type" placeholder="请输入游戏类型" clearable /> <el-input
v-model="formData.game_type"
:placeholder="$t('page.search.placeholderGameType')"
clearable
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col v-bind="setSpan(6)"> <el-col v-bind="setSpan(6)">
<el-form-item label="状态" prop="status"> <el-form-item :label="$t('page.search.status')" prop="status">
<el-select v-model="formData.status" placeholder="请选择状态" clearable> <el-select
<el-option label="启用" :value="1" /> v-model="formData.status"
<el-option label="禁用" :value="0" /> :placeholder="$t('page.search.placeholderStatus')"
clearable
>
<el-option :label="$t('page.search.statusEnabled')" :value="1" />
<el-option :label="$t('page.search.statusDisabled')" :value="0" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>

View File

@@ -1,13 +1,13 @@
<template> <template>
<el-dialog <el-dialog
v-model="visible" v-model="visible"
:title="dialogType === 'add' ? $t('page.form.dialogTitleAdd') : $t('page.form.dialogTitleEdit')" :title="$t('page.form.dialogTitleEdit')"
width="600px" width="600px"
align-center align-center
:close-on-click-modal="false" :close-on-click-modal="false"
@close="handleClose" @close="handleClose"
> >
<el-form ref="formRef" :model="formData" :rules="rules" label-width="120px"> <el-form ref="formRef" :model="formData" label-width="120px">
<el-form-item :label="$t('page.form.player')" prop="player_id"> <el-form-item :label="$t('page.form.player')" prop="player_id">
<el-select <el-select
v-model="formData.player_id" v-model="formData.player_id"
@@ -15,7 +15,7 @@
clearable clearable
filterable filterable
style="width: 100%" style="width: 100%"
:disabled="dialogType === 'edit'" disabled
> >
<el-option <el-option
v-for="item in playerOptions" v-for="item in playerOptions"
@@ -32,7 +32,7 @@
clearable clearable
filterable filterable
style="width: 100%" style="width: 100%"
:disabled="dialogType === 'edit'" disabled
> >
<el-option <el-option
v-for="item in lotteryConfigOptions" v-for="item in lotteryConfigOptions"
@@ -48,7 +48,7 @@
:placeholder="$t('form.placeholderSelect')" :placeholder="$t('form.placeholderSelect')"
clearable clearable
style="width: 100%" style="width: 100%"
:disabled="dialogType === 'edit'" disabled
> >
<el-option :label="$t('page.form.paid')" :value="0" /> <el-option :label="$t('page.form.paid')" :value="0" />
<el-option :label="$t('page.form.free')" :value="1" /> <el-option :label="$t('page.form.free')" :value="1" />
@@ -60,7 +60,7 @@
:placeholder="$t('form.placeholderSelect')" :placeholder="$t('form.placeholderSelect')"
clearable clearable
style="width: 100%" style="width: 100%"
:disabled="dialogType === 'edit'" disabled
> >
<el-option :label="$t('page.form.noBigWin')" :value="0" /> <el-option :label="$t('page.form.noBigWin')" :value="0" />
<el-option :label="$t('page.form.bigWin')" :value="1" /> <el-option :label="$t('page.form.bigWin')" :value="1" />
@@ -72,7 +72,7 @@
:placeholder="$t('page.form.placeholderWinCoin')" :placeholder="$t('page.form.placeholderWinCoin')"
:precision="0" :precision="0"
style="width: 100%" style="width: 100%"
:disabled="dialogType === 'edit'" disabled
/> />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.form.superWinCoin')" prop="super_win_coin"> <el-form-item :label="$t('page.form.superWinCoin')" prop="super_win_coin">
@@ -82,7 +82,7 @@
:precision="0" :precision="0"
:min="0" :min="0"
style="width: 100%" style="width: 100%"
:disabled="dialogType === 'edit'" disabled
/> />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.form.rewardWinCoin')" prop="reward_win_coin"> <el-form-item :label="$t('page.form.rewardWinCoin')" prop="reward_win_coin">
@@ -92,7 +92,7 @@
:precision="0" :precision="0"
:min="0" :min="0"
style="width: 100%" style="width: 100%"
:disabled="dialogType === 'edit'" disabled
/> />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.form.direction')" prop="direction"> <el-form-item :label="$t('page.form.direction')" prop="direction">
@@ -101,7 +101,7 @@
:placeholder="$t('page.form.placeholderDirection')" :placeholder="$t('page.form.placeholderDirection')"
clearable clearable
style="width: 100%" style="width: 100%"
:disabled="dialogType === 'edit'" disabled
> >
<el-option :label="$t('page.form.clockwise')" :value="0" /> <el-option :label="$t('page.form.clockwise')" :value="0" />
<el-option :label="$t('page.form.anticlockwise')" :value="1" /> <el-option :label="$t('page.form.anticlockwise')" :value="1" />
@@ -113,7 +113,7 @@
:placeholder="$t('page.form.placeholderStartIndex')" :placeholder="$t('page.form.placeholderStartIndex')"
:min="0" :min="0"
style="width: 100%" style="width: 100%"
:disabled="dialogType === 'edit'" disabled
/> />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.form.targetIndex')" prop="target_index"> <el-form-item :label="$t('page.form.targetIndex')" prop="target_index">
@@ -122,7 +122,7 @@
:placeholder="$t('page.form.placeholderTargetIndex')" :placeholder="$t('page.form.placeholderTargetIndex')"
:min="0" :min="0"
style="width: 100%" style="width: 100%"
:disabled="dialogType === 'edit'" disabled
/> />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.form.rollArray')" prop="rollArrayItems"> <el-form-item :label="$t('page.form.rollArray')" prop="rollArrayItems">
@@ -137,7 +137,7 @@
controls-position="right" controls-position="right"
placeholder="" placeholder=""
class="roll-array-input" class="roll-array-input"
:disabled="dialogType === 'edit'" disabled
/> />
</div> </div>
<div class="roll-array-hint">{{ $t('page.form.rollArrayHint') }}</div> <div class="roll-array-hint">{{ $t('page.form.rollArrayHint') }}</div>
@@ -150,7 +150,7 @@
:max="30" :max="30"
:precision="0" :precision="0"
style="width: 100%" style="width: 100%"
:disabled="dialogType === 'edit'" disabled
/> />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.form.rewardTier')" prop="reward_tier"> <el-form-item :label="$t('page.form.rewardTier')" prop="reward_tier">
@@ -160,7 +160,7 @@
clearable clearable
filterable filterable
style="width: 100%" style="width: 100%"
:disabled="true" disabled
> >
<el-option label="T1" value="T1" /> <el-option label="T1" value="T1" />
<el-option label="T2" value="T2" /> <el-option label="T2" value="T2" />
@@ -172,20 +172,15 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="handleClose">{{ dialogType === 'edit' ? $t('form.close') : $t('common.cancel') }}</el-button> <el-button @click="handleClose">{{ $t('form.close') }}</el-button>
<el-button v-if="dialogType === 'add'" type="primary" @click="handleSubmit">{{ $t('table.form.submit') }}</el-button>
</template> </template>
</el-dialog> </el-dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import api from '../../../api/play_record/index' import api from '../../../api/play_record/index'
import { useI18n } from 'vue-i18n' import { getChannelDeptRequestParams } from '@/composables/useChannelDeptScope'
import { getChannelDeptRequestParams, withChannelDeptParams } from '@/composables/useChannelDeptScope' import type { FormInstance } from 'element-plus'
import { ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
const { t } = useI18n()
interface Props { interface Props {
modelValue: boolean modelValue: boolean
@@ -200,7 +195,7 @@
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
modelValue: false, modelValue: false,
dialogType: 'add', dialogType: 'edit',
data: undefined data: undefined
}) })
@@ -213,32 +208,6 @@
set: (value) => emit('update:modelValue', value) set: (value) => emit('update:modelValue', value)
}) })
const rules = computed<FormRules>(() => ({
player_id: [{ required: true, message: t('page.form.rulePlayerRequired'), trigger: 'change' }],
lottery_config_id: [{ required: true, message: t('page.form.ruleLotteryConfigRequired'), trigger: 'change' }],
lottery_type: [{ required: true, message: t('page.form.ruleLotteryTypeRequired'), trigger: 'change' }],
is_win: [{ required: true, message: t('page.form.ruleIsWinRequired'), trigger: 'change' }],
win_coin: [{ required: true, message: t('page.form.ruleWinCoinRequired'), trigger: 'blur' }],
rollArrayItems: [
{
validator: (_rule: any, value: (number | null)[], callback: (e?: Error) => void) => {
if (!value || value.length !== 5) {
callback(new Error(t('page.form.ruleRollArrayLength')))
return
}
const ok = value.every((n) => n != null && n >= 1 && n <= 6)
if (!ok) {
callback(new Error(t('page.form.ruleRollArrayValues')))
return
}
callback()
},
trigger: 'change'
}
],
reward_tier: [{ required: true, message: t('page.form.ruleRewardTierRequired'), trigger: 'change' }]
}))
const playerOptions = ref<Array<{ id: number; username: string }>>([]) const playerOptions = ref<Array<{ id: number; username: string }>>([])
const lotteryConfigOptions = ref<Array<{ id: number; name: string }>>([]) const lotteryConfigOptions = ref<Array<{ id: number; name: string }>>([])
@@ -376,46 +345,6 @@
visible.value = false visible.value = false
formRef.value?.resetFields() formRef.value?.resetFields()
} }
const handleSubmit = async () => {
if (!formRef.value) return
try {
await formRef.value.validate()
const payload = { ...formData } as Record<string, unknown>
// 将 5 个输入值拼成 [1,2,3,4,5] 格式,确保每项为 16 的整数
const items = formData.rollArrayItems
const rollArray = items.map((n) => {
const v = n != null ? Number(n) : 1
return Math.min(6, Math.max(1, Number.isNaN(v) ? 1 : Math.floor(v)))
})
payload.roll_array = rollArray
payload.roll_number = formData.roll_number ?? rollArray.reduce((s, n) => s + n, 0)
delete payload.rollArrayItems
if (props.dialogType === 'add') {
delete payload.id
await api.save(withChannelDeptParams(payload))
ElMessage.success(t('page.form.addSuccess'))
} else {
await api.update(withChannelDeptParams(payload))
ElMessage.success(t('page.form.editSuccess'))
}
emit('success')
handleClose()
} catch (error: any) {
let msg = t('page.form.validateFailed')
if (error?.message) {
msg = error.message
} else if (typeof error === 'string') {
msg = error
} else if (error && typeof error === 'object') {
const first = Object.values(error).find((v: any) => v?.[0]?.message)
if (first && Array.isArray(first)) {
msg = (first[0] as any).message || msg
}
}
ElMessage.warning(msg)
}
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -1,15 +1,15 @@
<template> <template>
<el-dialog <el-dialog
v-model="visible" v-model="visible"
:title="dialogType === 'add' ? $t('page.form.titleAdd') : $t('page.form.titleEdit')" :title="$t('page.form.titleEdit')"
width="600px" width="600px"
align-center align-center
:close-on-click-modal="false" :close-on-click-modal="false"
@close="handleClose" @close="handleClose"
> >
<el-form ref="formRef" :model="formData" :rules="rules" label-width="120px"> <el-form ref="formRef" :model="formData" label-width="120px">
<el-form-item :label="$t('page.form.labelLotteryConfigId')" prop="lottery_config_id"> <el-form-item :label="$t('page.form.labelLotteryConfigId')" prop="lottery_config_id">
<el-input v-model="formData.lottery_config_id" :placeholder="$t('page.form.placeholderLotteryConfigId')" /> <el-input v-model="formData.lottery_config_id" :placeholder="$t('page.form.placeholderLotteryConfigId')" disabled />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.form.drawType')" prop="lottery_type"> <el-form-item :label="$t('page.form.drawType')" prop="lottery_type">
<el-select <el-select
@@ -17,13 +17,20 @@
:placeholder="$t('form.placeholderSelect')" :placeholder="$t('form.placeholderSelect')"
clearable clearable
style="width: 100%" style="width: 100%"
disabled
> >
<el-option :label="$t('page.search.paid')" :value="0" /> <el-option :label="$t('page.search.paid')" :value="0" />
<el-option :label="$t('page.search.free')" :value="1" /> <el-option :label="$t('page.search.free')" :value="1" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.search.direction')" prop="direction"> <el-form-item :label="$t('page.search.direction')" prop="direction">
<el-select v-model="formData.direction" :placeholder="$t('form.placeholderSelect')" clearable style="width: 100%"> <el-select
v-model="formData.direction"
:placeholder="$t('form.placeholderSelect')"
clearable
style="width: 100%"
disabled
>
<el-option :label="$t('page.search.clockwise')" :value="0" /> <el-option :label="$t('page.search.clockwise')" :value="0" />
<el-option :label="$t('page.search.anticlockwise')" :value="1" /> <el-option :label="$t('page.search.anticlockwise')" :value="1" />
</el-select> </el-select>
@@ -35,6 +42,7 @@
:precision="0" :precision="0"
controls-position="right" controls-position="right"
style="width: 100%" style="width: 100%"
disabled
/> />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.search.paidAmount')" prop="paid_amount"> <el-form-item :label="$t('page.search.paidAmount')" prop="paid_amount">
@@ -44,10 +52,11 @@
:precision="0" :precision="0"
controls-position="right" controls-position="right"
style="width: 100%" style="width: 100%"
disabled
/> />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.search.isBigWin')" prop="is_win"> <el-form-item :label="$t('page.search.isBigWin')" prop="is_win">
<el-select v-model="formData.is_win" :placeholder="$t('form.placeholderSelect')" clearable style="width: 100%"> <el-select v-model="formData.is_win" :placeholder="$t('form.placeholderSelect')" clearable style="width: 100%" disabled>
<el-option :label="$t('page.search.noBigWin')" :value="0" /> <el-option :label="$t('page.search.noBigWin')" :value="0" />
<el-option :label="$t('page.search.bigWin')" :value="1" /> <el-option :label="$t('page.search.bigWin')" :value="1" />
</el-select> </el-select>
@@ -59,6 +68,7 @@
:precision="0" :precision="0"
controls-position="right" controls-position="right"
style="width: 100%" style="width: 100%"
disabled
/> />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.search.rewardTier')" prop="reward_tier"> <el-form-item :label="$t('page.search.rewardTier')" prop="reward_tier">
@@ -67,6 +77,7 @@
:placeholder="$t('page.form.placeholderRewardTier')" :placeholder="$t('page.form.placeholderRewardTier')"
clearable clearable
style="width: 100%" style="width: 100%"
disabled
> >
<el-option label="T1" value="T1" /> <el-option label="T1" value="T1" />
<el-option label="T2" value="T2" /> <el-option label="T2" value="T2" />
@@ -77,19 +88,19 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.table.startIndex')" prop="start_index"> <el-form-item :label="$t('page.table.startIndex')" prop="start_index">
<el-input v-model="formData.start_index" :placeholder="$t('page.form.placeholderStartIndex')" /> <el-input v-model="formData.start_index" :placeholder="$t('page.form.placeholderStartIndex')" disabled />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.form.labelTargetIndex')" prop="target_index"> <el-form-item :label="$t('page.form.labelTargetIndex')" prop="target_index">
<el-input v-model="formData.target_index" :placeholder="$t('page.form.placeholderTargetIndex')" /> <el-input v-model="formData.target_index" :placeholder="$t('page.form.placeholderTargetIndex')" disabled />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.search.rollNumber')" prop="roll_number"> <el-form-item :label="$t('page.search.rollNumber')" prop="roll_number">
<el-input v-model="formData.roll_number" :placeholder="$t('page.form.placeholderRollNumber')" /> <el-input v-model="formData.roll_number" :placeholder="$t('page.form.placeholderRollNumber')" disabled />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.form.labelRollArray')" prop="roll_array"> <el-form-item :label="$t('page.form.labelRollArray')" prop="roll_array">
<el-input v-model="formData.roll_array" :placeholder="$t('page.form.placeholderRollArray')" /> <el-input v-model="formData.roll_array" :placeholder="$t('page.form.placeholderRollArray')" disabled />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.form.labelStatus')" prop="status"> <el-form-item :label="$t('page.form.labelStatus')" prop="status">
<sa-radio v-model="formData.status" dict="data_status" /> <sa-radio v-model="formData.status" dict="data_status" disabled />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.table.superWinCoin')" prop="super_win_coin"> <el-form-item :label="$t('page.table.superWinCoin')" prop="super_win_coin">
<el-input-number <el-input-number
@@ -98,6 +109,7 @@
:precision="0" :precision="0"
controls-position="right" controls-position="right"
style="width: 100%" style="width: 100%"
disabled
/> />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.table.rewardWinCoin')" prop="reward_win_coin"> <el-form-item :label="$t('page.table.rewardWinCoin')" prop="reward_win_coin">
@@ -107,25 +119,21 @@
:precision="0" :precision="0"
controls-position="right" controls-position="right"
style="width: 100%" style="width: 100%"
disabled
/> />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.form.labelAdminId')" prop="admin_id"> <el-form-item :label="$t('page.form.labelAdminId')" prop="admin_id">
<el-input v-model="formData.admin_id" :placeholder="$t('page.form.placeholderAdminId')" /> <el-input v-model="formData.admin_id" :placeholder="$t('page.form.placeholderAdminId')" disabled />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="handleClose">{{ $t('common.cancel') }}</el-button> <el-button @click="handleClose">{{ $t('form.close') }}</el-button>
<el-button type="primary" @click="handleSubmit">{{ $t('table.form.submit') }}</el-button>
</template> </template>
</el-dialog> </el-dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import api from '../../../api/play_record_test/index' import type { FormInstance } from 'element-plus'
import { ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { useI18n } from 'vue-i18n'
import { withChannelDeptParams } from '@/composables/useChannelDeptScope'
interface Props { interface Props {
modelValue: boolean modelValue: boolean
@@ -140,12 +148,11 @@
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
modelValue: false, modelValue: false,
dialogType: 'add', dialogType: 'edit',
data: undefined data: undefined
}) })
const emit = defineEmits<Emits>() const emit = defineEmits<Emits>()
const { t } = useI18n()
const formRef = ref<FormInstance>() const formRef = ref<FormInstance>()
@@ -157,18 +164,6 @@
set: (value) => emit('update:modelValue', value) set: (value) => emit('update:modelValue', value)
}) })
/**
* 表单验证规则
*/
const rules = computed<FormRules>(() => ({
lottery_config_id: [{ required: true, message: t('page.form.ruleLotteryConfigIdRequired'), trigger: 'blur' }],
lottery_type: [{ required: true, message: t('page.form.ruleDrawTypeRequired'), trigger: 'blur' }],
is_win: [{ required: true, message: t('page.form.ruleIsBigWinRequired'), trigger: 'blur' }],
direction: [{ required: true, message: t('page.form.ruleDirectionRequired'), trigger: 'blur' }],
reward_tier: [{ required: true, message: t('page.form.ruleRewardTierRequired'), trigger: 'blur' }],
status: [{ required: true, message: t('page.form.ruleStatusRequired'), trigger: 'blur' }]
}))
/** /**
* 初始数据 * 初始数据
*/ */
@@ -198,7 +193,7 @@
const formData = reactive({ ...initialFormData }) const formData = reactive({ ...initialFormData })
/** /**
* 监听弹窗打开,初始化表单数据 * 监听弹窗打开,初始化表单数据(仅查看)
*/ */
watch( watch(
() => props.modelValue, () => props.modelValue,
@@ -213,9 +208,7 @@
* 初始化页面数据 * 初始化页面数据
*/ */
const initPage = async () => { const initPage = async () => {
// 先重置为初始值
Object.assign(formData, initialFormData) Object.assign(formData, initialFormData)
// 如果有数据,则填充数据
if (props.data) { if (props.data) {
await nextTick() await nextTick()
initForm() initForm()
@@ -223,7 +216,7 @@
} }
/** /**
* 初始化表单数据 * 回填表单数据
*/ */
function normalizePlatformCoin(val: unknown): number { function normalizePlatformCoin(val: unknown): number {
if (val === '' || val === null || val === undefined) return 0 if (val === '' || val === null || val === undefined) return 0
@@ -245,34 +238,11 @@
} }
} }
/** /**
* 关闭弹窗并重置表单 * 关闭弹窗
*/ */
const handleClose = () => { const handleClose = () => {
visible.value = false visible.value = false
formRef.value?.resetFields() formRef.value?.resetFields()
} }
/**
* 提交表单
*/
const handleSubmit = async () => {
if (!formRef.value) return
try {
await formRef.value.validate()
const payload = { ...formData }
if (props.dialogType === 'add') {
await api.save(withChannelDeptParams(payload))
ElMessage.success(t('page.form.addSuccess'))
} else {
await api.update(withChannelDeptParams(payload))
ElMessage.success(t('page.form.editSuccess'))
}
emit('success')
handleClose()
} catch (error) {
console.log('表单验证失败:', error)
}
}
</script> </script>

View File

@@ -1,13 +1,13 @@
<template> <template>
<el-dialog <el-dialog
v-model="visible" v-model="visible"
:title="dialogType === 'add' ? $t('page.form.dialogTitleAdd') : $t('page.form.dialogTitleEdit')" :title="$t('page.form.dialogTitleEdit')"
width="600px" width="600px"
align-center align-center
:close-on-click-modal="false" :close-on-click-modal="false"
@close="handleClose" @close="handleClose"
> >
<el-form ref="formRef" :model="formData" :rules="rules" label-width="120px"> <el-form ref="formRef" :model="formData" label-width="120px">
<el-form-item :label="$t('page.form.player')" prop="player_id"> <el-form-item :label="$t('page.form.player')" prop="player_id">
<el-select <el-select
v-model="formData.player_id" v-model="formData.player_id"
@@ -15,7 +15,7 @@
clearable clearable
filterable filterable
style="width: 100%" style="width: 100%"
:disabled="dialogType === 'edit'" disabled
> >
<el-option <el-option
v-for="item in playerOptions" v-for="item in playerOptions"
@@ -30,7 +30,7 @@
v-model="formData.use_coins" v-model="formData.use_coins"
:placeholder="$t('page.form.placeholderUseCoins')" :placeholder="$t('page.form.placeholderUseCoins')"
:min="0" :min="0"
:disabled="dialogType === 'edit'" disabled
/> />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.form.paidDrawCount')" prop="paid_ticket_count"> <el-form-item :label="$t('page.form.paidDrawCount')" prop="paid_ticket_count">
@@ -38,8 +38,7 @@
v-model="formData.paid_ticket_count" v-model="formData.paid_ticket_count"
:placeholder="$t('page.form.placeholderPaidDrawCount')" :placeholder="$t('page.form.placeholderPaidDrawCount')"
:min="0" :min="0"
@change="onTicketCountChange" disabled
:disabled="dialogType === 'edit'"
/> />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.form.freeDrawCount')" prop="free_ticket_count"> <el-form-item :label="$t('page.form.freeDrawCount')" prop="free_ticket_count">
@@ -47,8 +46,7 @@
v-model="formData.free_ticket_count" v-model="formData.free_ticket_count"
:placeholder="$t('page.form.placeholderFreeDrawCount')" :placeholder="$t('page.form.placeholderFreeDrawCount')"
:min="0" :min="0"
@change="onTicketCountChange" disabled
:disabled="dialogType === 'edit'"
/> />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.form.totalDrawCount')" prop="total_ticket_count"> <el-form-item :label="$t('page.form.totalDrawCount')" prop="total_ticket_count">
@@ -68,25 +66,20 @@
maxlength="500" maxlength="500"
show-word-limit show-word-limit
style="width: 100%" style="width: 100%"
:disabled="dialogType === 'edit'" disabled
/> />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="handleClose">{{ $t('common.cancel') }}</el-button> <el-button @click="handleClose">{{ $t('form.close') }}</el-button>
<el-button type="primary" @click="handleSubmit">{{ $t('table.form.submit') }}</el-button>
</template> </template>
</el-dialog> </el-dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import api from '../../../api/player_ticket_record/index' import api from '../../../api/player_ticket_record/index'
import { useI18n } from 'vue-i18n' import { getChannelDeptRequestParams } from '@/composables/useChannelDeptScope'
import { getChannelDeptRequestParams, withChannelDeptParams } from '@/composables/useChannelDeptScope' import type { FormInstance } from 'element-plus'
import { ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
const { t } = useI18n()
interface Props { interface Props {
modelValue: boolean modelValue: boolean
@@ -101,7 +94,7 @@
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
modelValue: false, modelValue: false,
dialogType: 'add', dialogType: 'edit',
data: undefined data: undefined
}) })
@@ -117,17 +110,6 @@
set: (value) => emit('update:modelValue', value) set: (value) => emit('update:modelValue', value)
}) })
/**
* 表单验证规则
*/
const rules = reactive<FormRules>({
player_id: [{ required: true, message: t('page.form.rulePlayerRequired'), trigger: 'change' }],
use_coins: [{ required: true, message: t('page.form.ruleUseCoinsRequired'), trigger: 'blur' }],
paid_ticket_count: [{ required: true, message: t('page.form.rulePaidDrawRequired'), trigger: 'blur' }],
free_ticket_count: [{ required: true, message: t('page.form.ruleFreeDrawRequired'), trigger: 'blur' }],
remark: [{ required: true, message: t('page.form.ruleRemarkRequired'), trigger: 'blur' }]
})
/** 玩家下拉选项id、username */ /** 玩家下拉选项id、username */
const playerOptions = ref<Array<{ id: number; username: string }>>([]) const playerOptions = ref<Array<{ id: number; username: string }>>([])
@@ -138,10 +120,6 @@
return paid + free return paid + free
}) })
function onTicketCountChange() {
formData.total_ticket_count = totalTicketCountComputed.value
}
/** /**
* 初始数据 * 初始数据
*/ */
@@ -161,7 +139,7 @@
const formData = reactive({ ...initialFormData }) const formData = reactive({ ...initialFormData })
/** /**
* 监听弹窗打开,初始化表单并拉取玩家选项(与 player_wallet_record 一致) * 监听弹窗打开,初始化表单并拉取玩家选项
*/ */
watch( watch(
() => props.modelValue, () => props.modelValue,
@@ -182,7 +160,7 @@
) )
/** /**
* 初始化页面数据(仅重置表单、回填编辑数据,不在此处请求玩家列表) * 初始化页面数据
*/ */
const initPage = async () => { const initPage = async () => {
Object.assign(formData, { ...initialFormData }) Object.assign(formData, { ...initialFormData })
@@ -193,7 +171,7 @@
} }
/** /**
* 初始化表单数据 * 回填表单数据
*/ */
const initForm = () => { const initForm = () => {
if (!props.data) return if (!props.data) return
@@ -215,34 +193,10 @@
} }
/** /**
* 关闭弹窗并重置表单 * 关闭弹窗
*/ */
const handleClose = () => { const handleClose = () => {
visible.value = false visible.value = false
formRef.value?.resetFields() formRef.value?.resetFields()
} }
/**
* 提交表单total_ticket_count 由 paid_ticket_count + free_ticket_count 自动求和,提交前写入)
*/
const handleSubmit = async () => {
if (!formRef.value) return
try {
formData.total_ticket_count = totalTicketCountComputed.value
await formRef.value.validate()
if (props.dialogType === 'add') {
const rest = { ...formData } as Record<string, unknown>
delete rest.id
await api.save(withChannelDeptParams(rest))
ElMessage.success(t('page.form.addSuccess'))
} else {
await api.update(withChannelDeptParams(formData))
ElMessage.success(t('page.form.editSuccess'))
}
emit('success')
handleClose()
} catch (error) {
console.log('表单验证失败:', error)
}
}
</script> </script>

View File

@@ -1,13 +1,13 @@
<template> <template>
<el-dialog <el-dialog
v-model="visible" v-model="visible"
:title="dialogType === 'add' ? $t('page.form.dialogTitleAdd') : $t('page.form.dialogTitleEdit')" :title="$t('page.form.dialogTitleEdit')"
width="600px" width="600px"
align-center align-center
:close-on-click-modal="false" :close-on-click-modal="false"
@close="handleClose" @close="handleClose"
> >
<el-form ref="formRef" :model="formData" :rules="rules" label-width="120px"> <el-form ref="formRef" :model="formData" label-width="120px">
<el-form-item :label="$t('page.form.user')" prop="player_id"> <el-form-item :label="$t('page.form.user')" prop="player_id">
<el-select <el-select
v-model="formData.player_id" v-model="formData.player_id"
@@ -15,8 +15,7 @@
clearable clearable
filterable filterable
style="width: 100%" style="width: 100%"
:disabled="dialogType === 'edit'" disabled
@change="onPlayerChange"
> >
<el-option <el-option
v-for="item in playerOptions" v-for="item in playerOptions"
@@ -32,7 +31,7 @@
:placeholder="$t('page.form.placeholderType')" :placeholder="$t('page.form.placeholderType')"
clearable clearable
style="width: 100%" style="width: 100%"
:disabled="dialogType === 'edit'" disabled
> >
<el-option :label="$t('page.form.typeRecharge')" :value="0" /> <el-option :label="$t('page.form.typeRecharge')" :value="0" />
<el-option :label="$t('page.form.typeWithdraw')" :value="1" /> <el-option :label="$t('page.form.typeWithdraw')" :value="1" />
@@ -48,8 +47,7 @@
:precision="2" :precision="2"
:step="1" :step="1"
style="width: 100%" style="width: 100%"
@change="onCoinChange" disabled
:disabled="dialogType === 'edit'"
/> />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.form.walletBefore')" prop="wallet_before"> <el-form-item :label="$t('page.form.walletBefore')" prop="wallet_before">
@@ -78,25 +76,20 @@
:placeholder="$t('page.form.placeholderRemark')" :placeholder="$t('page.form.placeholderRemark')"
maxlength="500" maxlength="500"
show-word-limit show-word-limit
:disabled="dialogType === 'edit'" disabled
/> />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="handleClose">{{ $t('common.cancel') }}</el-button> <el-button @click="handleClose">{{ $t('form.close') }}</el-button>
<el-button type="primary" @click="handleSubmit">{{ $t('table.form.submit') }}</el-button>
</template> </template>
</el-dialog> </el-dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import api from '../../../api/player_wallet_record/index' import api from '../../../api/player_wallet_record/index'
import { useI18n } from 'vue-i18n' import { getChannelDeptRequestParams } from '@/composables/useChannelDeptScope'
import { getChannelDeptRequestParams, withChannelDeptParams } from '@/composables/useChannelDeptScope' import type { FormInstance } from 'element-plus'
import { ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
const { t } = useI18n()
interface Props { interface Props {
modelValue: boolean modelValue: boolean
@@ -111,7 +104,7 @@
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
modelValue: false, modelValue: false,
dialogType: 'add', dialogType: 'edit',
data: undefined data: undefined
}) })
@@ -127,12 +120,6 @@
set: (value) => emit('update:modelValue', value) set: (value) => emit('update:modelValue', value)
}) })
const rules = reactive<FormRules>({
player_id: [{ required: true, message: t('page.form.ruleUserRequired'), trigger: 'change' }],
coin: [{ required: true, message: t('page.form.ruleCoinRequired'), trigger: 'blur' }],
type: [{ required: true, message: t('page.form.ruleTypeRequired'), trigger: 'change' }]
})
const initialFormData: { const initialFormData: {
id: number | null id: number | null
player_id: number | null player_id: number | null
@@ -153,36 +140,6 @@
const formData = reactive({ ...initialFormData }) const formData = reactive({ ...initialFormData })
/** 选择用户后拉取当前平台币作为钱包操作前 */
async function onPlayerChange(playerId: number | null) {
if (playerId == null) {
formData.wallet_before = 0
calcWalletAfter()
return
}
try {
const res = await api.getPlayerWalletBefore(playerId)
const before = res?.wallet_before ?? 0
formData.wallet_before = Number(before)
calcWalletAfter()
} catch {
formData.wallet_before = 0
calcWalletAfter()
}
}
/** 平台币变化时重算钱包操作后 */
function onCoinChange() {
calcWalletAfter()
}
/** 钱包操作后 = 钱包操作前 + 平台币变化 */
function calcWalletAfter() {
const before = Number(formData.wallet_before) || 0
const coin = Number(formData.coin) || 0
formData.wallet_after = Number((before + coin).toFixed(2))
}
watch( watch(
() => props.modelValue, () => props.modelValue,
async (open) => { async (open) => {
@@ -230,24 +187,4 @@
visible.value = false visible.value = false
formRef.value?.resetFields() formRef.value?.resetFields()
} }
const handleSubmit = async () => {
if (!formRef.value) return
try {
await formRef.value.validate()
calcWalletAfter()
const payload = { ...formData }
if (props.dialogType === 'add') {
await api.save(withChannelDeptParams(payload))
ElMessage.success(t('page.form.addSuccess'))
} else {
await api.update(withChannelDeptParams(payload))
ElMessage.success(t('page.form.editSuccess'))
}
emit('success')
handleClose()
} catch (error) {
console.log('表单验证失败:', error)
}
}
</script> </script>

View File

@@ -1,36 +1,31 @@
<template> <template>
<el-dialog <el-dialog
v-model="visible" v-model="visible"
:title="dialogType === 'add' ? $t('page.form.titleAdd') : $t('page.form.titleEdit')" :title="$t('page.form.titleEdit')"
width="600px" width="600px"
align-center align-center
:close-on-click-modal="false" :close-on-click-modal="false"
@close="handleClose" @close="handleClose"
> >
<el-form ref="formRef" :model="formData" :rules="rules" label-width="120px"> <el-form ref="formRef" :model="formData" label-width="120px">
<el-form-item :label="$t('page.form.labelTestCount')" prop="test_count"> <el-form-item :label="$t('page.form.labelTestCount')" prop="test_count">
<el-input v-model="formData.test_count" :placeholder="$t('page.form.placeholderTestCount')" /> <el-input v-model="formData.test_count" :placeholder="$t('page.form.placeholderTestCount')" disabled />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.form.labelWeightSnapshot')" prop="weight_config_snapshot"> <el-form-item :label="$t('page.form.labelWeightSnapshot')" prop="weight_config_snapshot">
<el-input v-model="formData.weight_config_snapshot" :placeholder="$t('page.form.placeholderWeightSnapshot')" /> <el-input v-model="formData.weight_config_snapshot" :placeholder="$t('page.form.placeholderWeightSnapshot')" disabled />
</el-form-item> </el-form-item>
<el-form-item :label="$t('page.form.labelResultCounts')" prop="result_counts"> <el-form-item :label="$t('page.form.labelResultCounts')" prop="result_counts">
<el-input v-model="formData.result_counts" :placeholder="$t('page.form.placeholderResultCounts')" /> <el-input v-model="formData.result_counts" :placeholder="$t('page.form.placeholderResultCounts')" disabled />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="handleClose">{{ $t('common.cancel') }}</el-button> <el-button @click="handleClose">{{ $t('form.close') }}</el-button>
<el-button type="primary" @click="handleSubmit">{{ $t('table.form.submit') }}</el-button>
</template> </template>
</el-dialog> </el-dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import api from '../../../api/reward_config_record/index' import type { FormInstance } from 'element-plus'
import { ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { useI18n } from 'vue-i18n'
import { withChannelDeptParams } from '@/composables/useChannelDeptScope'
interface Props { interface Props {
modelValue: boolean modelValue: boolean
@@ -45,12 +40,11 @@
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
modelValue: false, modelValue: false,
dialogType: 'add', dialogType: 'edit',
data: undefined data: undefined
}) })
const emit = defineEmits<Emits>() const emit = defineEmits<Emits>()
const { t } = useI18n()
const formRef = ref<FormInstance>() const formRef = ref<FormInstance>()
@@ -62,13 +56,6 @@
set: (value) => emit('update:modelValue', value) set: (value) => emit('update:modelValue', value)
}) })
/**
* 表单验证规则
*/
const rules = computed<FormRules>(() => ({
test_count: [{ required: true, message: t('page.form.ruleTestCountRequired'), trigger: 'blur' }]
}))
/** /**
* 初始数据 * 初始数据
*/ */
@@ -76,7 +63,7 @@
id: null, id: null,
test_count: 100, test_count: 100,
weight_config_snapshot: '', weight_config_snapshot: '',
result_counts: '', result_counts: ''
} }
/** /**
@@ -85,7 +72,7 @@
const formData = reactive({ ...initialFormData }) const formData = reactive({ ...initialFormData })
/** /**
* 监听弹窗打开,初始化表单数据 * 监听弹窗打开,初始化表单数据(仅查看)
*/ */
watch( watch(
() => props.modelValue, () => props.modelValue,
@@ -100,9 +87,7 @@
* 初始化页面数据 * 初始化页面数据
*/ */
const initPage = async () => { const initPage = async () => {
// 先重置为初始值
Object.assign(formData, initialFormData) Object.assign(formData, initialFormData)
// 如果有数据,则填充数据
if (props.data) { if (props.data) {
await nextTick() await nextTick()
initForm() initForm()
@@ -110,7 +95,7 @@
} }
/** /**
* 初始化表单数据 * 回填表单数据
*/ */
const initForm = () => { const initForm = () => {
if (props.data) { if (props.data) {
@@ -123,31 +108,10 @@
} }
/** /**
* 关闭弹窗并重置表单 * 关闭弹窗
*/ */
const handleClose = () => { const handleClose = () => {
visible.value = false visible.value = false
formRef.value?.resetFields() formRef.value?.resetFields()
} }
/**
* 提交表单
*/
const handleSubmit = async () => {
if (!formRef.value) return
try {
await formRef.value.validate()
if (props.dialogType === 'add') {
await api.save(withChannelDeptParams(formData))
ElMessage.success(t('page.form.addSuccess'))
} else {
await api.update(withChannelDeptParams(formData))
ElMessage.success(t('page.form.editSuccess'))
}
emit('success')
handleClose()
} catch (error) {
console.log('表单验证失败:', error)
}
}
</script> </script>

View File

@@ -6,6 +6,7 @@ namespace app\api\controller\v1;
use app\api\cache\AuthTokenCache; use app\api\cache\AuthTokenCache;
use app\api\controller\BaseController; use app\api\controller\BaseController;
use app\api\util\ReturnCode; use app\api\util\ReturnCode;
use plugin\saiadmin\app\model\system\SystemUser;
use support\Request; use support\Request;
use support\Response; use support\Response;
use Tinywan\Jwt\JwtToken; use Tinywan\Jwt\JwtToken;
@@ -54,6 +55,14 @@ class AuthTokenController extends BaseController
return $this->fail('Signature verification failed', ReturnCode::FORBIDDEN); return $this->fail('Signature verification failed', ReturnCode::FORBIDDEN);
} }
$agent = SystemUser::where('agent_id', $agentId)->find();
if (!$agent || (int) ($agent->status ?? 0) !== 1) {
return $this->fail('Invalid agent_id', ReturnCode::FORBIDDEN);
}
if (empty($agent->dept_id) || (int) $agent->dept_id <= 0) {
return $this->fail('Agent channel is not configured', ReturnCode::FORBIDDEN);
}
$exp = (int) config('api.auth_token_exp', 86400); $exp = (int) config('api.auth_token_exp', 86400);
$tokenResult = JwtToken::generateToken([ $tokenResult = JwtToken::generateToken([
'id' => 0, 'id' => 0,

View File

@@ -7,7 +7,6 @@ use app\api\logic\UserLogic;
use app\api\util\ReturnCode; use app\api\util\ReturnCode;
use app\dice\model\game\DiceGame; use app\dice\model\game\DiceGame;
use app\dice\model\player\DicePlayer; use app\dice\model\player\DicePlayer;
use plugin\saiadmin\app\model\system\SystemUser;
use app\dice\model\play_record\DicePlayRecord; use app\dice\model\play_record\DicePlayRecord;
use app\dice\model\player_wallet_record\DicePlayerWalletRecord; use app\dice\model\player_wallet_record\DicePlayerWalletRecord;
use app\dice\model\player_ticket_record\DicePlayerTicketRecord; use app\dice\model\player_ticket_record\DicePlayerTicketRecord;
@@ -60,7 +59,7 @@ class GameController extends BaseController
public function getGameList(Request $request): Response public function getGameList(Request $request): Response
{ {
$lang = $this->resolveLang($request->post('lang', 'zh')); $lang = $this->resolveLang($request->post('lang', 'zh'));
$games = $this->buildPublicGameList($lang); $games = $this->buildPublicGameList($lang, $this->agentDeptId($request));
return $this->success([ return $this->success([
'game_list' => $games, 'game_list' => $games,
]); ]);
@@ -73,7 +72,7 @@ class GameController extends BaseController
public function getGameHall(Request $request): Response public function getGameHall(Request $request): Response
{ {
$lang = $this->resolveLang($request->post('lang', 'zh')); $lang = $this->resolveLang($request->post('lang', 'zh'));
$games = $this->buildPublicGameList($lang); $games = $this->buildPublicGameList($lang, $this->agentDeptId($request));
$hallUrl = ''; $hallUrl = '';
if (!empty($games)) { if (!empty($games)) {
$hallUrl = $games[0]['hall_url'] ?? ''; $hallUrl = $games[0]['hall_url'] ?? '';
@@ -106,23 +105,16 @@ class GameController extends BaseController
$time = (string) time(); $time = (string) time();
} }
$adminId = null; $deptId = $this->agentDeptId($request);
$adminIdsInTopDept = null; $adminId = $this->agentAdminId($request);
$agentId = trim((string) ($request->agent_id ?? '')); $adminIdsInTopDept = UserLogic::getAdminIdsByAgentIdTopDept(trim((string) ($request->agent_id ?? '')));
if ($agentId !== '') {
$systemUser = SystemUser::where('agent_id', $agentId)->find();
if ($systemUser) {
$adminId = (int) $systemUser->id;
$adminIdsInTopDept = UserLogic::getAdminIdsByAgentIdTopDept($agentId);
}
}
$lang = trim((string) ($request->post('lang', 'zh'))); $lang = trim((string) ($request->post('lang', 'zh')));
$lang = in_array($lang, ['en', 'zh'], true) ? $lang : 'zh'; $lang = in_array($lang, ['en', 'zh'], true) ? $lang : 'zh';
try { try {
$logic = new UserLogic(); $logic = new UserLogic();
$result = $logic->loginByUsername($username, $password, $lang, 0.0, $time, $adminId, $adminIdsInTopDept); $result = $logic->loginByUsername($username, $password, $lang, 0.0, $time, $adminId, $adminIdsInTopDept, $deptId);
} catch (\plugin\saiadmin\exception\ApiException $e) { } catch (\plugin\saiadmin\exception\ApiException $e) {
return $this->fail($e->getMessage(), ReturnCode::PARAMS_ERROR); return $this->fail($e->getMessage(), ReturnCode::PARAMS_ERROR);
} }
@@ -145,24 +137,25 @@ class GameController extends BaseController
{ {
$usernameRaw = $request->input('username', ''); $usernameRaw = $request->input('username', '');
$username = is_string($usernameRaw) ? trim($usernameRaw) : ''; $username = is_string($usernameRaw) ? trim($usernameRaw) : '';
$deptId = $this->agentDeptId($request);
if ($username === '') { if ($username === '') {
return $this->fail('username is required', ReturnCode::PARAMS_ERROR); return $this->fail('username is required', ReturnCode::PARAMS_ERROR);
} }
$cached = UserCache::getPlayerInfoSnapshotByUsername($username); $cached = UserCache::getPlayerInfoSnapshotByUsername($this->scopedUsername($deptId, $username));
if ($cached !== null) { if ($cached !== null) {
return $this->success($cached); return $this->success($cached);
} }
$player = DicePlayer::field(self::PLAYER_INFO_DB_FIELDS)->where('username', $username)->find(); $player = DicePlayer::field(self::PLAYER_INFO_DB_FIELDS)->where('username', $username)->where('dept_id', $deptId)->find();
if (!$player) { if (!$player) {
return $this->fail('User not found', ReturnCode::NOT_FOUND); return $this->fail('User not found', ReturnCode::PARAMS_ERROR);
} }
$hidden = ['password', 'lottery_config_id', 't1_weight', 't2_weight', 't3_weight', 't4_weight', 't5_weight', 'delete_time']; $hidden = ['password', 'lottery_config_id', 't1_weight', 't2_weight', 't3_weight', 't4_weight', 't5_weight', 'delete_time'];
$info = $player->hidden($hidden)->toArray(); $info = $player->hidden($hidden)->toArray();
UserCache::setPlayerInfoSnapshotByUsername($username, $info); UserCache::setPlayerInfoSnapshotByUsername($this->scopedUsername($deptId, $username), $info);
return $this->success($info); return $this->success($info);
} }
@@ -276,6 +269,7 @@ class GameController extends BaseController
public function getPlayerGameRecord(Request $request): Response public function getPlayerGameRecord(Request $request): Response
{ {
$username = trim((string) ($request->post('username', ''))); $username = trim((string) ($request->post('username', '')));
$deptId = $this->agentDeptId($request);
$startCreateTime = trim((string) ($request->post('start_create_time', ''))); $startCreateTime = trim((string) ($request->post('start_create_time', '')));
$endCreateTime = trim((string) ($request->post('end_create_time', ''))); $endCreateTime = trim((string) ($request->post('end_create_time', '')));
$window = $this->resolvePullRecordTimeWindow($startCreateTime, $endCreateTime); $window = $this->resolvePullRecordTimeWindow($startCreateTime, $endCreateTime);
@@ -284,10 +278,10 @@ class GameController extends BaseController
} }
$limit = $this->resolvePullRecordLimit($request); $limit = $this->resolvePullRecordLimit($request);
$query = DicePlayRecord::order('id', 'desc'); $query = DicePlayRecord::where('dept_id', $deptId)->order('id', 'desc');
if ($username !== '') { if ($username !== '') {
$player = DicePlayer::where('username', $username)->find(); $player = $this->findPlayerByUsername($username, $deptId);
if (!$player) { if (!$player) {
return $this->success([]); return $this->success([]);
} }
@@ -300,7 +294,7 @@ class GameController extends BaseController
$list = $query->limit($limit)->select()->toArray(); $list = $query->limit($limit)->select()->toArray();
$playerIds = array_unique(array_column($list, 'player_id')); $playerIds = array_unique(array_column($list, 'player_id'));
if (!empty($playerIds)) { if (!empty($playerIds)) {
$players = DicePlayer::whereIn('id', $playerIds)->field('id,username,phone')->select()->toArray(); $players = DicePlayer::whereIn('id', $playerIds)->where('dept_id', $deptId)->field('id,username,phone')->select()->toArray();
$playerMap = []; $playerMap = [];
foreach ($players as $p) { foreach ($players as $p) {
$playerMap[(int) ($p['id'] ?? 0)] = $p; $playerMap[(int) ($p['id'] ?? 0)] = $p;
@@ -321,6 +315,7 @@ class GameController extends BaseController
public function getPlayerWalletRecord(Request $request): Response public function getPlayerWalletRecord(Request $request): Response
{ {
$username = trim((string) ($request->post('username', ''))); $username = trim((string) ($request->post('username', '')));
$deptId = $this->agentDeptId($request);
$startCreateTime = trim((string) ($request->post('start_create_time', ''))); $startCreateTime = trim((string) ($request->post('start_create_time', '')));
$endCreateTime = trim((string) ($request->post('end_create_time', ''))); $endCreateTime = trim((string) ($request->post('end_create_time', '')));
$window = $this->resolvePullRecordTimeWindow($startCreateTime, $endCreateTime); $window = $this->resolvePullRecordTimeWindow($startCreateTime, $endCreateTime);
@@ -329,10 +324,10 @@ class GameController extends BaseController
} }
$limit = $this->resolvePullRecordLimit($request); $limit = $this->resolvePullRecordLimit($request);
$query = DicePlayerWalletRecord::order('id', 'desc'); $query = DicePlayerWalletRecord::where('dept_id', $deptId)->order('id', 'desc');
if ($username !== '') { if ($username !== '') {
$player = DicePlayer::where('username', $username)->find(); $player = $this->findPlayerByUsername($username, $deptId);
if (!$player) { if (!$player) {
return $this->success([]); return $this->success([]);
} }
@@ -357,6 +352,7 @@ class GameController extends BaseController
public function getPlayerTicketRecord(Request $request): Response public function getPlayerTicketRecord(Request $request): Response
{ {
$username = trim((string) ($request->post('username', ''))); $username = trim((string) ($request->post('username', '')));
$deptId = $this->agentDeptId($request);
$startCreateTime = trim((string) ($request->post('start_create_time', ''))); $startCreateTime = trim((string) ($request->post('start_create_time', '')));
$endCreateTime = trim((string) ($request->post('end_create_time', ''))); $endCreateTime = trim((string) ($request->post('end_create_time', '')));
$window = $this->resolvePullRecordTimeWindow($startCreateTime, $endCreateTime); $window = $this->resolvePullRecordTimeWindow($startCreateTime, $endCreateTime);
@@ -365,10 +361,10 @@ class GameController extends BaseController
} }
$limit = $this->resolvePullRecordLimit($request); $limit = $this->resolvePullRecordLimit($request);
$query = DicePlayerTicketRecord::order('id', 'desc'); $query = DicePlayerTicketRecord::where('dept_id', $deptId)->order('id', 'desc');
if ($username !== '') { if ($username !== '') {
$player = DicePlayer::where('username', $username)->find(); $player = $this->findPlayerByUsername($username, $deptId);
if (!$player) { if (!$player) {
return $this->success([]); return $this->success([]);
} }
@@ -394,6 +390,7 @@ class GameController extends BaseController
public function setPlayerWallet(Request $request): Response public function setPlayerWallet(Request $request): Response
{ {
$username = trim((string) ($request->post('username', ''))); $username = trim((string) ($request->post('username', '')));
$deptId = $this->agentDeptId($request);
$coin = $request->post('coin'); $coin = $request->post('coin');
if ($username === '') { if ($username === '') {
@@ -408,9 +405,9 @@ class GameController extends BaseController
return $this->fail('coin cannot be 0', ReturnCode::PARAMS_ERROR); return $this->fail('coin cannot be 0', ReturnCode::PARAMS_ERROR);
} }
$player = DicePlayer::where('username', $username)->find(); $player = $this->findPlayerByUsername($username, $deptId);
if (!$player) { if (!$player) {
return $this->fail('User not found', ReturnCode::NOT_FOUND); return $this->fail('User not found', ReturnCode::PARAMS_ERROR);
} }
$walletBefore = (float) ($player->coin ?? 0); $walletBefore = (float) ($player->coin ?? 0);
@@ -430,6 +427,7 @@ class GameController extends BaseController
$adminId = ($player->admin_id ?? null) ? (int) $player->admin_id : null; $adminId = ($player->admin_id ?? null) ? (int) $player->admin_id : null;
$record = DicePlayerWalletRecord::create([ $record = DicePlayerWalletRecord::create([
'dept_id' => $deptId,
'player_id' => (int) $player->id, 'player_id' => (int) $player->id,
'admin_id' => $adminId, 'admin_id' => $adminId,
'coin' => $coinVal, 'coin' => $coinVal,
@@ -452,6 +450,7 @@ class GameController extends BaseController
UserCache::deleteUser($player->id); UserCache::deleteUser($player->id);
if ($player->username !== '') { if ($player->username !== '') {
UserCache::deletePlayerByUsername($player->username); UserCache::deletePlayerByUsername($player->username);
UserCache::deletePlayerByUsername($this->scopedUsername($deptId, (string) $player->username));
} }
$recordArr = $record->toArray(); $recordArr = $record->toArray();
@@ -471,13 +470,14 @@ class GameController extends BaseController
return $langValue; return $langValue;
} }
private function buildPublicGameList(string $lang): array private function buildPublicGameList(string $lang, int $deptId): array
{ {
$rows = DiceGame::where('status', 1) $rows = DiceGame::where('status', 1)
->orderBy('sort', 'asc') ->where('dept_id', $deptId)
->orderBy('id', 'asc') ->order('sort', 'asc')
->select(array_merge(self::GAME_PUBLIC_FIELDS, ['game_name', 'game_name_en'])) ->order('id', 'asc')
->get() ->field(array_merge(self::GAME_PUBLIC_FIELDS, ['game_name', 'game_name_en']))
->select()
->toArray(); ->toArray();
if (empty($rows)) { if (empty($rows)) {
return []; return [];
@@ -495,4 +495,26 @@ class GameController extends BaseController
} }
return $games; return $games;
} }
private function agentDeptId(Request $request): int
{
return (int) ($request->agent_dept_id ?? 0);
}
private function agentAdminId(Request $request): ?int
{
$adminId = (int) ($request->agent_admin_id ?? 0);
return $adminId > 0 ? $adminId : null;
}
private function scopedUsername(int $deptId, string $username): string
{
return $deptId . ':' . $username;
}
private function findPlayerByUsername(string $username, int $deptId): ?DicePlayer
{
$player = DicePlayer::where('username', $username)->where('dept_id', $deptId)->find();
return $player ?: null;
}
} }

View File

@@ -76,7 +76,7 @@ class UserLogic
* @param int|null $adminId 创建新用户时关联的后台管理员IDsa_system_user.id可选 * @param int|null $adminId 创建新用户时关联的后台管理员IDsa_system_user.id可选
* @param int[]|null $adminIdsInTopDept 当前管理员顶级部门下的所有管理员ID用于按部门范围查找玩家为空时退化为仅按 username 查找 * @param int[]|null $adminIdsInTopDept 当前管理员顶级部门下的所有管理员ID用于按部门范围查找玩家为空时退化为仅按 username 查找
*/ */
public function loginByUsername(string $username, string $password, string $lang, float $coin, string $time, ?int $adminId = null, ?array $adminIdsInTopDept = null): array public function loginByUsername(string $username, string $password, string $lang, float $coin, string $time, ?int $adminId = null, ?array $adminIdsInTopDept = null, ?int $deptId = null): array
{ {
$username = trim($username); $username = trim($username);
if ($username === '') { if ($username === '') {
@@ -84,6 +84,9 @@ class UserLogic
} }
$query = DicePlayer::where('username', $username); $query = DicePlayer::where('username', $username);
if ($deptId !== null && $deptId > 0) {
$query->where('dept_id', $deptId);
}
if ($adminIdsInTopDept !== null && !empty($adminIdsInTopDept)) { if ($adminIdsInTopDept !== null && !empty($adminIdsInTopDept)) {
$query->whereIn('admin_id', $adminIdsInTopDept); $query->whereIn('admin_id', $adminIdsInTopDept);
} }
@@ -106,10 +109,13 @@ class UserLogic
$player->password = $this->hashPassword($password); $player->password = $this->hashPassword($password);
$player->status = self::STATUS_NORMAL; $player->status = self::STATUS_NORMAL;
$player->coin = $coin; $player->coin = $coin;
if ($deptId !== null && $deptId > 0) {
$player->dept_id = $deptId;
}
if ($adminId !== null && $adminId > 0) { if ($adminId !== null && $adminId > 0) {
$player->admin_id = $adminId; $player->admin_id = $adminId;
$adminUser = SystemUser::find($adminId); $adminUser = SystemUser::find($adminId);
if ($adminUser && !empty($adminUser->dept_id)) { if (($deptId === null || $deptId <= 0) && $adminUser && !empty($adminUser->dept_id)) {
$player->dept_id = $adminUser->dept_id; $player->dept_id = $adminUser->dept_id;
} }
} }
@@ -125,6 +131,7 @@ class UserLogic
]); ]);
$token = $tokenResult['access_token']; $token = $tokenResult['access_token'];
UserCache::setSessionByUsername($username, $token); UserCache::setSessionByUsername($username, $token);
UserCache::setCurrentUserToken((int) $player->id, $token);
$userArr = $player->hidden(['password', 'lottery_config_id', 't1_weight', 't2_weight', 't3_weight', 't4_weight', 't5_weight'])->toArray(); $userArr = $player->hidden(['password', 'lottery_config_id', 't1_weight', 't2_weight', 't3_weight', 't4_weight', 't5_weight'])->toArray();
UserCache::setUser((int) $player->id, $userArr); UserCache::setUser((int) $player->id, $userArr);

View File

@@ -5,6 +5,7 @@ namespace app\api\middleware;
use app\api\cache\AuthTokenCache; use app\api\cache\AuthTokenCache;
use app\api\util\ReturnCode; use app\api\util\ReturnCode;
use plugin\saiadmin\app\model\system\SystemUser;
use plugin\saiadmin\exception\ApiException; use plugin\saiadmin\exception\ApiException;
use Tinywan\Jwt\JwtToken; use Tinywan\Jwt\JwtToken;
use Tinywan\Jwt\Exception\JwtTokenException; use Tinywan\Jwt\Exception\JwtTokenException;
@@ -53,7 +54,17 @@ class AuthTokenMiddleware implements MiddlewareInterface
throw new ApiException('auth-token invalid or expired', ReturnCode::TOKEN_INVALID); throw new ApiException('auth-token invalid or expired', ReturnCode::TOKEN_INVALID);
} }
$agent = SystemUser::where('agent_id', $agentId)->find();
if (!$agent || (int) ($agent->status ?? 0) !== 1) {
throw new ApiException('Invalid agent_id', ReturnCode::FORBIDDEN);
}
if (empty($agent->dept_id) || (int) $agent->dept_id <= 0) {
throw new ApiException('Agent channel is not configured', ReturnCode::FORBIDDEN);
}
$request->agent_id = $agentId; $request->agent_id = $agentId;
$request->agent_admin_id = (int) $agent->id;
$request->agent_dept_id = (int) $agent->dept_id;
return $handler($request); return $handler($request);
} }
} }

View File

@@ -53,10 +53,14 @@ class TokenMiddleware implements MiddlewareInterface
if ($username === '') { if ($username === '') {
throw new ApiException('Invalid or expired token', ReturnCode::TOKEN_INVALID); throw new ApiException('Invalid or expired token', ReturnCode::TOKEN_INVALID);
} }
$userId = (int) ($extend['id'] ?? 0);
if ($userId <= 0) {
throw new ApiException('Invalid or expired token', ReturnCode::TOKEN_INVALID);
}
$currentToken = UserCache::getSessionTokenByUsername($username); $currentToken = UserCache::getCurrentUserToken($userId);
if ($currentToken === null || $currentToken === '') { if ($currentToken === null || $currentToken === '') {
$player = DicePlayer::where('username', $username)->find(); $player = DicePlayer::find($userId);
if (!$player) { if (!$player) {
throw new ApiException('Please register', ReturnCode::TOKEN_INVALID); throw new ApiException('Please register', ReturnCode::TOKEN_INVALID);
} }
@@ -68,17 +72,17 @@ class TokenMiddleware implements MiddlewareInterface
// 优先从 Redis 缓存取玩家,避免每次请求都查库 // 优先从 Redis 缓存取玩家,避免每次请求都查库
$player = null; $player = null;
$cached = UserCache::getPlayerByUsername($username); $cached = UserCache::getUser($userId);
if ($cached !== null && isset($cached['id'])) { if (!empty($cached) && isset($cached['id']) && (int) $cached['id'] === $userId) {
$player = (new DicePlayer())->data($cached, true); $player = (new DicePlayer())->data($cached, true);
} }
if ($player === null) { if ($player === null) {
$player = DicePlayer::where('username', $username)->find(); $player = DicePlayer::find($userId);
if (!$player) { if (!$player) {
UserCache::deleteSessionByUsername($username); UserCache::deleteSessionByUsername($username);
throw new ApiException('Please login again', ReturnCode::TOKEN_INVALID); throw new ApiException('Please login again', ReturnCode::TOKEN_INVALID);
} }
UserCache::setPlayerByUsername($username, $player->hidden(['password'])->toArray()); UserCache::setUser($userId, $player->hidden(['password'])->toArray());
} }
$request->player_id = (int) $player->id; $request->player_id = (int) $player->id;
$request->player = $player; $request->player = $player;

View File

@@ -144,31 +144,6 @@ class DicePlayRecordController extends BaseController
} }
} }
/**
* 更新数据
* @param Request $request
* @return Response
*/
#[Permission('玩家抽奖记录修改', 'dice:play_record:index:update')]
public function update(Request $request): Response
{
$data = $request->post();
$this->validate('update', $data);
$model = $this->logic->read($data['id'] ?? 0);
if ($model) {
$recordDeptId = is_array($model) ? ($model['dept_id'] ?? null) : ($model->dept_id ?? null);
if (! AdminScopeHelper::canAccessDept($this->adminInfo ?? null, $recordDeptId, $request->input('dept_id'))) {
return $this->fail('no permission to update this record');
}
}
$result = $this->logic->edit($data['id'], $data);
if ($result) {
return $this->success('update success');
} else {
return $this->fail('update failed');
}
}
/** /**
* 删除数据 * 删除数据
* @param Request $request * @param Request $request

View File

@@ -103,31 +103,6 @@ class DicePlayRecordTestController extends BaseController
} }
} }
/**
* 更新数据
* @param Request $request
* @return Response
*/
#[Permission('玩家抽奖记录(测试数据)修改', 'dice:play_record_test:index:update')]
public function update(Request $request): Response
{
$data = $request->post();
$this->validate('update', $data);
$model = $this->logic->read($data['id'] ?? 0);
if ($model) {
$recordDeptId = is_array($model) ? ($model['dept_id'] ?? null) : ($model->dept_id ?? null);
if (! AdminScopeHelper::canAccessDept($this->adminInfo ?? null, $recordDeptId, $request->input('dept_id'))) {
return $this->fail('no permission to update this record');
}
}
$result = $this->logic->edit($data['id'], $data);
if ($result) {
return $this->success('update success');
} else {
return $this->fail('update failed');
}
}
/** /**
* 删除数据 * 删除数据
* @param Request $request * @param Request $request

View File

@@ -117,31 +117,6 @@ class DicePlayerTicketRecordController extends BaseController
} }
} }
/**
* 更新数据
* @param Request $request
* @return Response
*/
#[Permission('抽奖券获取记录修改', 'dice:player_ticket_record:index:update')]
public function update(Request $request): Response
{
$data = $request->post();
$this->validate('update', $data);
$model = $this->logic->read($data['id'] ?? 0);
if ($model) {
$recordDeptId = is_array($model) ? ($model['dept_id'] ?? null) : ($model->dept_id ?? null);
if (! AdminScopeHelper::canAccessDept($this->adminInfo ?? null, $recordDeptId, $request->input('dept_id'))) {
return $this->fail('no permission to update this record');
}
}
$result = $this->logic->edit($data['id'], $data);
if ($result) {
return $this->success('update success');
} else {
return $this->fail('update failed');
}
}
/** /**
* 删除数据 * 删除数据
* @param Request $request * @param Request $request

View File

@@ -200,28 +200,4 @@ class DicePlayerWalletRecordController extends BaseController
return $this->fail('add failed'); return $this->fail('add failed');
} }
/**
* 更新数据
* @param Request $request
* @return Response
*/
#[Permission('玩家钱包流水修改', 'dice:player_wallet_record:index:update')]
public function update(Request $request): Response
{
$data = $request->post();
$this->validate('update', $data);
$model = $this->logic->read($data['id'] ?? 0);
if ($model) {
$recordDeptId = is_array($model) ? ($model['dept_id'] ?? null) : ($model->dept_id ?? null);
if (! AdminScopeHelper::canAccessDept($this->adminInfo ?? null, $recordDeptId, $request->input('dept_id'))) {
return $this->fail('no permission to update this record');
}
}
$result = $this->logic->edit($data['id'], $data);
if ($result) {
return $this->success('update success');
} else {
return $this->fail('update failed');
}
}
} }

View File

@@ -107,31 +107,6 @@ class DiceRewardConfigRecordController extends BaseController
} }
} }
/**
* 更新数据
* @param Request $request
* @return Response
*/
#[Permission('奖励配置权重测试记录修改', 'dice:reward_config_record:index:update')]
public function update(Request $request): Response
{
$data = $request->post();
$this->validate('update', $data);
$model = $this->logic->read($data['id'] ?? 0);
if ($model) {
$recordDeptId = is_array($model) ? ($model['dept_id'] ?? null) : ($model->dept_id ?? null);
if (! AdminScopeHelper::canAccessDept($this->adminInfo ?? null, $recordDeptId, $request->input('dept_id'))) {
return $this->fail('no permission to update this record');
}
}
$result = $this->logic->edit($data['id'], $data);
if ($result) {
return $this->success('update success');
} else {
return $this->fail('update failed');
}
}
/** /**
* 删除数据 * 删除数据
* @param Request $request * @param Request $request

View File

@@ -7,6 +7,13 @@ use plugin\saiadmin\basic\think\BaseModel as SaiBaseModel;
/** /**
* 大富翁模块模型基类:删除均为硬删除(物理删除) * 大富翁模块模型基类:删除均为硬删除(物理删除)
*
* 注意:
* - 不要在此重写实例方法 delete(),否则与 trait/父类的 delete() 相互覆盖,
* 在调用 $this->force()->delete() 时会无限递归force() 返回 $this
* 导致内存爆栈、HTTP 500。
* - 物理删除一律通过静态 destroy() 入口(强制 $force=true完成
* SoftDelete::destroy() 内部会按硬删除分支执行。
*/ */
abstract class DiceModel extends SaiBaseModel abstract class DiceModel extends SaiBaseModel
{ {
@@ -17,9 +24,4 @@ abstract class DiceModel extends SaiBaseModel
{ {
return parent::destroy($data, true); return parent::destroy($data, true);
} }
public function delete(): bool
{
return $this->force()->delete();
}
} }

View File

@@ -12,10 +12,18 @@ use plugin\saiadmin\basic\contracts\ModelInterface;
/** /**
* ThinkORM 模型基类 * ThinkORM 模型基类
*
* 全局策略:所有删除一律为硬删除(物理删除)。
* - 保留 SoftDelete trait 仅是为了兼容历史字段(如 delete_time与查询作用域
* 实际删除方法delete/destroy均通过 trait 别名重写为强制 force=true。
* - 项目中不使用 withTrashed/onlyTrashed/restore() 等软删除恢复接口。
*/ */
class BaseModel extends Model implements ModelInterface class BaseModel extends Model implements ModelInterface
{ {
use SoftDelete; use SoftDelete {
delete as protected softDeleteCascadeOriginal;
destroy as protected softDeleteDestroyOriginal;
}
/** /**
* 删除时间字段 * 删除时间字段
@@ -99,6 +107,25 @@ class BaseModel extends Model implements ModelInterface
} }
} }
/**
* 删除记录(静态入口):一律强制硬删除(物理删除)。
* @param mixed $data 主键、闭包或条件
* @param bool $force 兼容签名,内部一律按 true 处理
*/
public static function destroy($data, bool $force = true): bool
{
return static::softDeleteDestroyOriginal($data, true);
}
/**
* 删除记录(实例方法):一律强制硬删除。
*/
public function delete(): bool
{
$this->force(true);
return $this->softDeleteCascadeOriginal();
}
/** /**
* 新增前事件:自动写入 create_time有后台登录信息时写入 created_by * 新增前事件:自动写入 create_time有后台登录信息时写入 created_by
* @param Model $model * @param Model $model