Compare commits
5 Commits
a10afa5add
...
e5f83846b3
| Author | SHA1 | Date | |
|---|---|---|---|
| e5f83846b3 | |||
| 5ab16243bd | |||
| 8d8cee696f | |||
| 74612f136e | |||
| 13d8adbfe0 |
@@ -169,9 +169,9 @@
|
||||
{ prop: 't3_wight', label: 'T3池权重', width: 100, formatter: weightFormatter('t3_wight') },
|
||||
{ prop: 't4_wight', label: 'T4池权重', width: 100, formatter: weightFormatter('t4_wight') },
|
||||
{ prop: 't5_wight', label: 'T5池权重', width: 100, formatter: weightFormatter('t5_wight') },
|
||||
{ prop: 'total_draw_count', label: '总抽奖次数' },
|
||||
{ prop: 'paid_draw_count', label: '购买抽奖次数' },
|
||||
{ prop: 'free_draw_count', label: '赠送抽奖次数' },
|
||||
{ prop: 'total_ticket_count', label: '总抽奖次数' },
|
||||
{ prop: 'paid_ticket_count', label: '购买抽奖次数' },
|
||||
{ prop: 'free_ticket_count', label: '赠送抽奖次数' },
|
||||
{ prop: 'created_at', label: '创建时间' },
|
||||
{ prop: 'updated_at', label: '更新时间' },
|
||||
{ prop: 'operation', label: '操作', width: 120, fixed: 'right', useSlot: true }
|
||||
|
||||
@@ -83,12 +83,12 @@
|
||||
username: undefined,
|
||||
use_coins_min: undefined,
|
||||
use_coins_max: undefined,
|
||||
total_draw_count_min: undefined,
|
||||
total_draw_count_max: undefined,
|
||||
paid_draw_count_min: undefined,
|
||||
paid_draw_count_max: undefined,
|
||||
free_draw_count_min: undefined,
|
||||
free_draw_count_max: undefined,
|
||||
total_ticket_count_min: undefined,
|
||||
total_ticket_count_max: undefined,
|
||||
paid_ticket_count_min: undefined,
|
||||
paid_ticket_count_max: undefined,
|
||||
free_ticket_count_min: undefined,
|
||||
free_ticket_count_max: undefined,
|
||||
create_time_min: undefined,
|
||||
create_time_max: undefined,
|
||||
create_time: undefined as [string, string] | undefined
|
||||
@@ -131,9 +131,9 @@
|
||||
{ prop: 'id', label: 'ID', width: 80 },
|
||||
{ prop: 'player_id', label: '玩家用户名', formatter: (row: Record<string, any>) => usernameFormatter(row) },
|
||||
{ prop: 'use_coins', label: '消耗硬币' },
|
||||
{ prop: 'total_draw_count', label: '总抽奖次数' },
|
||||
{ prop: 'paid_draw_count', label: '购买抽奖次数' },
|
||||
{ prop: 'free_draw_count', label: '赠送抽奖次数' },
|
||||
{ prop: 'total_ticket_count', label: '总抽奖次数' },
|
||||
{ prop: 'paid_ticket_count', label: '购买抽奖次数' },
|
||||
{ prop: 'free_ticket_count', label: '赠送抽奖次数' },
|
||||
{ prop: 'remark', label: '备注', width: 100, showOverflowTooltip: true },
|
||||
{ prop: 'create_time', label: '创建时间', width: 170 },
|
||||
{ prop: 'operation', label: '操作', width: 100, fixed: 'right', useSlot: true }
|
||||
|
||||
@@ -28,25 +28,25 @@
|
||||
<el-form-item label="消耗硬币" prop="use_coins">
|
||||
<el-input-number v-model="formData.use_coins" placeholder="请输入消耗硬币" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="购买抽奖次数" prop="paid_draw_count">
|
||||
<el-form-item label="购买抽奖次数" prop="paid_ticket_count">
|
||||
<el-input-number
|
||||
v-model="formData.paid_draw_count"
|
||||
v-model="formData.paid_ticket_count"
|
||||
placeholder="请输入购买抽奖次数"
|
||||
:min="0"
|
||||
@change="onDrawCountChange"
|
||||
@change="onTicketCountChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="赠送抽奖次数" prop="free_draw_count">
|
||||
<el-form-item label="赠送抽奖次数" prop="free_ticket_count">
|
||||
<el-input-number
|
||||
v-model="formData.free_draw_count"
|
||||
v-model="formData.free_ticket_count"
|
||||
placeholder="请输入赠送抽奖次数"
|
||||
:min="0"
|
||||
@change="onDrawCountChange"
|
||||
@change="onTicketCountChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="总抽奖次数" prop="total_draw_count">
|
||||
<el-form-item label="总抽奖次数" prop="total_ticket_count">
|
||||
<el-input-number
|
||||
:model-value="totalDrawCountComputed"
|
||||
:model-value="totalTicketCountComputed"
|
||||
placeholder="自动求和"
|
||||
:min="0"
|
||||
disabled
|
||||
@@ -110,23 +110,23 @@
|
||||
const rules = reactive<FormRules>({
|
||||
player_id: [{ required: true, message: '请选择玩家', trigger: 'change' }],
|
||||
use_coins: [{ required: true, message: '消耗硬币必需填写', trigger: 'blur' }],
|
||||
paid_draw_count: [{ required: true, message: '购买抽奖次数必需填写', trigger: 'blur' }],
|
||||
free_draw_count: [{ required: true, message: '赠送抽奖次数必需填写', trigger: 'blur' }],
|
||||
paid_ticket_count: [{ required: true, message: '购买抽奖次数必需填写', trigger: 'blur' }],
|
||||
free_ticket_count: [{ required: true, message: '赠送抽奖次数必需填写', trigger: 'blur' }],
|
||||
remark: [{ required: true, message: '备注必需填写', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
/** 玩家下拉选项(id、username) */
|
||||
const playerOptions = ref<Array<{ id: number; username: string }>>([])
|
||||
|
||||
/** 总抽奖次数 = 购买抽奖次数 + 赠送抽奖次数(只读展示) */
|
||||
const totalDrawCountComputed = computed(() => {
|
||||
const paid = Number(formData.paid_draw_count) || 0
|
||||
const free = Number(formData.free_draw_count) || 0
|
||||
/** total_ticket_count = paid_ticket_count + free_ticket_count(只读展示) */
|
||||
const totalTicketCountComputed = computed(() => {
|
||||
const paid = Number(formData.paid_ticket_count) || 0
|
||||
const free = Number(formData.free_ticket_count) || 0
|
||||
return paid + free
|
||||
})
|
||||
|
||||
function onDrawCountChange() {
|
||||
formData.total_draw_count = totalDrawCountComputed.value
|
||||
function onTicketCountChange() {
|
||||
formData.total_ticket_count = totalTicketCountComputed.value
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,9 +136,9 @@
|
||||
id: null,
|
||||
player_id: null,
|
||||
use_coins: null as number | null,
|
||||
total_draw_count: null as number | null,
|
||||
paid_draw_count: null as number | null,
|
||||
free_draw_count: null as number | null,
|
||||
total_ticket_count: null as number | null,
|
||||
paid_ticket_count: null as number | null,
|
||||
free_ticket_count: null as number | null,
|
||||
remark: ''
|
||||
}
|
||||
|
||||
@@ -188,9 +188,9 @@
|
||||
'id',
|
||||
'player_id',
|
||||
'use_coins',
|
||||
'total_draw_count',
|
||||
'paid_draw_count',
|
||||
'free_draw_count',
|
||||
'total_ticket_count',
|
||||
'paid_ticket_count',
|
||||
'free_ticket_count',
|
||||
'remark'
|
||||
]
|
||||
keys.forEach((key) => {
|
||||
@@ -210,12 +210,12 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交表单(总抽奖次数由购买+赠送自动求和,提交前写入)
|
||||
* 提交表单(total_ticket_count 由 paid_ticket_count + free_ticket_count 自动求和,提交前写入)
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
try {
|
||||
formData.total_draw_count = totalDrawCountComputed.value
|
||||
formData.total_ticket_count = totalTicketCountComputed.value
|
||||
await formRef.value.validate()
|
||||
if (props.dialogType === 'add') {
|
||||
const rest = { ...formData } as Record<string, unknown>
|
||||
|
||||
@@ -35,10 +35,10 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-bind="setSpan(6)">
|
||||
<el-form-item label="总抽奖次数" prop="total_draw_count_min">
|
||||
<el-form-item label="总抽奖次数" prop="total_ticket_count_min">
|
||||
<div class="range-wrap">
|
||||
<el-input-number
|
||||
v-model="formData.total_draw_count_min"
|
||||
v-model="formData.total_ticket_count_min"
|
||||
placeholder="最小"
|
||||
:min="0"
|
||||
controls-position="right"
|
||||
@@ -46,7 +46,7 @@
|
||||
/>
|
||||
<span class="range-sep">至</span>
|
||||
<el-input-number
|
||||
v-model="formData.total_draw_count_max"
|
||||
v-model="formData.total_ticket_count_max"
|
||||
placeholder="最大"
|
||||
:min="0"
|
||||
controls-position="right"
|
||||
@@ -56,10 +56,10 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-bind="setSpan(6)">
|
||||
<el-form-item label="购买抽奖次数" prop="paid_draw_count_min">
|
||||
<el-form-item label="购买抽奖次数" prop="paid_ticket_count_min">
|
||||
<div class="range-wrap">
|
||||
<el-input-number
|
||||
v-model="formData.paid_draw_count_min"
|
||||
v-model="formData.paid_ticket_count_min"
|
||||
placeholder="最小"
|
||||
:min="0"
|
||||
controls-position="right"
|
||||
@@ -67,7 +67,7 @@
|
||||
/>
|
||||
<span class="range-sep">至</span>
|
||||
<el-input-number
|
||||
v-model="formData.paid_draw_count_max"
|
||||
v-model="formData.paid_ticket_count_max"
|
||||
placeholder="最大"
|
||||
:min="0"
|
||||
controls-position="right"
|
||||
@@ -77,10 +77,10 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-bind="setSpan(6)">
|
||||
<el-form-item label="赠送抽奖次数" prop="free_draw_count_min">
|
||||
<el-form-item label="赠送抽奖次数" prop="free_ticket_count_min">
|
||||
<div class="range-wrap">
|
||||
<el-input-number
|
||||
v-model="formData.free_draw_count_min"
|
||||
v-model="formData.free_ticket_count_min"
|
||||
placeholder="最小"
|
||||
:min="0"
|
||||
controls-position="right"
|
||||
@@ -88,7 +88,7 @@
|
||||
/>
|
||||
<span class="range-sep">至</span>
|
||||
<el-input-number
|
||||
v-model="formData.free_draw_count_max"
|
||||
v-model="formData.free_ticket_count_max"
|
||||
placeholder="最大"
|
||||
:min="0"
|
||||
controls-position="right"
|
||||
|
||||
@@ -196,9 +196,9 @@
|
||||
align: 'center',
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{ prop: 'total_draw_count', label: '总抽奖次数', align: 'center' },
|
||||
{ prop: 'paid_draw_count', label: '购买抽奖次数', align: 'center' },
|
||||
{ prop: 'free_draw_count', label: '赠送抽奖次数', align: 'center' },
|
||||
{ prop: 'total_ticket_count', label: '总抽奖次数', align: 'center' },
|
||||
{ prop: 'paid_ticket_count', label: '购买抽奖次数', align: 'center' },
|
||||
{ prop: 'free_ticket_count', label: '赠送抽奖次数', align: 'center' },
|
||||
{ prop: 'create_time', label: '创建时间', width: 170, align: 'center' },
|
||||
{
|
||||
prop: 'operation',
|
||||
|
||||
@@ -17,10 +17,14 @@ REDIS_PASSWORD = ''
|
||||
REDIS_DB = 0
|
||||
|
||||
# API 鉴权与用户(可选,不填则用默认值)
|
||||
# API_AUTH_TOKEN_EXP = 86400
|
||||
# authToken 签名密钥(必填,与客户端约定,用于 signature 校验)
|
||||
API_AUTH_TOKEN_SECRET = xF75oK91TQj13s0UmNIr1NBWMWGfflNO
|
||||
# authToken 时间戳允许误差秒数,防重放,默认 300
|
||||
API_AUTH_TOKEN_TIME_TOLERANCE = 300
|
||||
API_AUTH_TOKEN_EXP = 86400
|
||||
# API_USER_TOKEN_EXP = 604800
|
||||
# API_USER_CACHE_EXPIRE = 604800
|
||||
# API_USER_ENCRYPT_KEY = dafuweng_api_user_cache_key_32
|
||||
API_USER_CACHE_EXPIRE = 86400
|
||||
API_USER_ENCRYPT_KEY = Wj818SK8dhKBKNOY3PUTmZfhQDMCXEZi
|
||||
|
||||
# 验证码配置,支持cache|session
|
||||
CAPTCHA_MODE = cache
|
||||
|
||||
54
server/app/api/cache/AuthTokenCache.php
vendored
Normal file
54
server/app/api/cache/AuthTokenCache.php
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\api\cache;
|
||||
|
||||
use support\think\Cache;
|
||||
|
||||
/**
|
||||
* 按设备标识存储当前有效的 auth-token,同一设备只保留最新一个,旧 token 自动失效
|
||||
*/
|
||||
class AuthTokenCache
|
||||
{
|
||||
private static function prefix(): string
|
||||
{
|
||||
return config('api.auth_token_device_prefix', 'api:auth_token:');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置该设备当前有效的 auth-token(会覆盖同设备之前的 token,使旧 token 失效)
|
||||
* @param string $device 设备标识,如 dice
|
||||
* @param string $token 完整 auth-token 字符串
|
||||
* @param int $ttl 过期时间(秒),应与 auth_token_exp 一致
|
||||
*/
|
||||
public static function setDeviceToken(string $device, string $token, int $ttl): bool
|
||||
{
|
||||
if ($device === '' || $ttl <= 0) {
|
||||
return false;
|
||||
}
|
||||
$key = self::prefix() . $device;
|
||||
return Cache::set($key, $token, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该设备当前有效的 auth-token,不存在或已过期返回 null
|
||||
*/
|
||||
public static function getDeviceToken(string $device): ?string
|
||||
{
|
||||
if ($device === '') {
|
||||
return null;
|
||||
}
|
||||
$key = self::prefix() . $device;
|
||||
$value = Cache::get($key);
|
||||
return $value !== null && $value !== '' ? (string) $value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验请求中的 token 是否为该设备当前唯一有效 token
|
||||
*/
|
||||
public static function isCurrentToken(string $device, string $token): bool
|
||||
{
|
||||
$current = self::getDeviceToken($device);
|
||||
return $current !== null && $current === $token;
|
||||
}
|
||||
}
|
||||
47
server/app/api/cache/UserCache.php
vendored
47
server/app/api/cache/UserCache.php
vendored
@@ -131,4 +131,51 @@ class UserCache
|
||||
$val = Cache::get($key);
|
||||
return $val !== null && $val !== '';
|
||||
}
|
||||
|
||||
/** 当前有效 user-token 按用户存储的 key 前缀(重新登录/注册后覆盖,保证单用户单 token) */
|
||||
private static function currentTokenPrefix(): string
|
||||
{
|
||||
return config('api.user_token_current_prefix', 'api:user:current_token:');
|
||||
}
|
||||
|
||||
private static function userTokenExpire(): int
|
||||
{
|
||||
return (int) config('api.user_token_exp', 604800);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置该用户当前唯一有效的 user-token(登录/注册时调用,会覆盖该用户之前的 token)
|
||||
* @param int $userId 用户 ID
|
||||
* @param string $token 完整 user-token 字符串
|
||||
*/
|
||||
public static function setCurrentUserToken(int $userId, string $token): bool
|
||||
{
|
||||
if ($userId <= 0 || $token === '') {
|
||||
return false;
|
||||
}
|
||||
$key = self::currentTokenPrefix() . $userId;
|
||||
return Cache::set($key, $token, self::userTokenExpire());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该用户当前在服务端登记的有效 user-token,不存在或已过期返回 null
|
||||
*/
|
||||
public static function getCurrentUserToken(int $userId): ?string
|
||||
{
|
||||
if ($userId <= 0) {
|
||||
return null;
|
||||
}
|
||||
$key = self::currentTokenPrefix() . $userId;
|
||||
$value = Cache::get($key);
|
||||
return $value !== null && $value !== '' ? (string) $value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验请求中的 token 是否为该用户当前唯一有效 token
|
||||
*/
|
||||
public static function isCurrentUserToken(int $userId, string $token): bool
|
||||
{
|
||||
$current = self::getCurrentUserToken($userId);
|
||||
return $current !== null && $current === $token;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,29 +7,84 @@ use support\Request;
|
||||
use support\Response;
|
||||
use Tinywan\Jwt\JwtToken;
|
||||
use plugin\saiadmin\basic\OpenController;
|
||||
use app\api\util\ReturnCode;
|
||||
use app\api\cache\AuthTokenCache;
|
||||
|
||||
/**
|
||||
* API 鉴权 Token 接口
|
||||
* 仅支持 GET,必传参数:signature、secret、device、time,签名规则:signature = md5(device . secret . time)
|
||||
* 后续所有 /api 接口调用均需在请求头携带此接口返回的 auth-token
|
||||
*/
|
||||
class AuthTokenController extends OpenController
|
||||
{
|
||||
/**
|
||||
* 获取 auth-token
|
||||
* GET 或 POST /api/authToken
|
||||
* GET /api/authToken
|
||||
* 参数:signature(签名)、secret(密钥)、device(设备标识)、time(时间戳,秒),四者均为必传且非空
|
||||
*/
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$exp = config('api.auth_token_exp', 86400);
|
||||
if (strtoupper($request->method()) !== 'GET') {
|
||||
return $this->fail('仅支持 GET 请求', ReturnCode::PARAMS_ERROR);
|
||||
}
|
||||
|
||||
$param = $request->get();
|
||||
$signature = trim((string) ($param['signature'] ?? ''));
|
||||
$secret = trim((string) ($param['secret'] ?? ''));
|
||||
$device = trim((string) ($param['device'] ?? ''));
|
||||
$time = trim((string) ($param['time'] ?? ''));
|
||||
|
||||
if ($signature === '' || $secret === '' || $device === '' || $time === '') {
|
||||
return $this->fail('signature、secret、device、time 均为必传且不能为空', ReturnCode::PARAMS_ERROR);
|
||||
}
|
||||
|
||||
$serverSecret = trim((string) config('api.auth_token_secret', ''));
|
||||
if ($serverSecret === '') {
|
||||
return $this->fail('服务未配置 API_AUTH_TOKEN_SECRET', ReturnCode::PARAMS_ERROR);
|
||||
}
|
||||
if ($secret !== $serverSecret) {
|
||||
return $this->fail('密钥错误', ReturnCode::FORBIDDEN);
|
||||
}
|
||||
|
||||
$tolerance = (int) config('api.auth_token_time_tolerance', 300);
|
||||
$now = time();
|
||||
$ts = is_numeric($time) ? (int) $time : 0;
|
||||
if ($ts <= 0 || abs($now - $ts) > $tolerance) {
|
||||
return $this->fail('时间戳无效或已过期', ReturnCode::PARAMS_ERROR);
|
||||
}
|
||||
|
||||
$sign = $this->getAuthToken($device, $serverSecret, $time);
|
||||
if ($sign !== $signature) {
|
||||
return $this->fail('签名验证失败', ReturnCode::FORBIDDEN);
|
||||
}
|
||||
|
||||
$exp = (int) config('api.auth_token_exp', 86400);
|
||||
$tokenResult = JwtToken::generateToken([
|
||||
'id' => 0,
|
||||
'plat' => 'api',
|
||||
'device' => $device,
|
||||
'access_exp' => $exp,
|
||||
]);
|
||||
|
||||
// 同一设备只保留最新 token,覆盖后旧 token 失效
|
||||
AuthTokenCache::setDeviceToken($device, $tokenResult['access_token'], $exp);
|
||||
|
||||
return $this->success([
|
||||
'auth-token' => $tokenResult['access_token'],
|
||||
'expires_in' => $tokenResult['expires_in'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名:signature = md5(device . secret . time)
|
||||
*
|
||||
* @param string $device 设备标识
|
||||
* @param string $secret 密钥(来自配置)
|
||||
* @param string $time 时间戳
|
||||
* @return string
|
||||
*/
|
||||
private function getAuthToken(string $device, string $secret, string $time): string
|
||||
{
|
||||
return md5($device . $secret . $time);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ namespace app\api\controller;
|
||||
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use app\api\logic\UserLogic;
|
||||
use app\api\logic\GameLogic;
|
||||
use app\api\logic\PlayStartLogic;
|
||||
use app\api\logic\UserLogic;
|
||||
use app\api\util\ReturnCode;
|
||||
use app\dice\model\play_record\DicePlayRecord;
|
||||
use app\dice\model\player\DicePlayer;
|
||||
@@ -23,30 +23,15 @@ class GameController extends OpenController
|
||||
/**
|
||||
* 购买抽奖券
|
||||
* POST /api/game/buyLotteryTickets
|
||||
* header: auth-token, user-token
|
||||
* header: auth-token, user-token(由 CheckUserTokenMiddleware 注入 request->user_id)
|
||||
* body: count = 1 | 5 | 10(1次/100coin, 5次/500coin, 10次/1000coin)
|
||||
* 记录钱包流水,并更新缓存中玩家的 total_draw_count、paid_draw_count、free_draw_count、coin
|
||||
*/
|
||||
public function buyLotteryTickets(Request $request): Response
|
||||
{
|
||||
$token = $request->header('user-token');
|
||||
if (empty($token)) {
|
||||
$auth = $request->header('authorization');
|
||||
if ($auth && stripos($auth, 'Bearer ') === 0) {
|
||||
$token = trim(substr($auth, 7));
|
||||
}
|
||||
}
|
||||
if (empty($token)) {
|
||||
return $this->fail('请携带 user-token', ReturnCode::MISSING_TOKEN);
|
||||
}
|
||||
$userId = UserLogic::getUserIdFromToken($token);
|
||||
if ($userId === null) {
|
||||
return $this->fail('user-token 无效或已过期', ReturnCode::TOKEN_TIMEOUT);
|
||||
}
|
||||
|
||||
$userId = UserLogic::getUserIdFromRequest($request) ?? 0;
|
||||
$count = (int) $request->post('count', 0);
|
||||
if (!in_array($count, [1, 5, 10], true)) {
|
||||
return $this->fail('购买抽奖券错误', ReturnCode::EMPTY_PARAMS);
|
||||
return $this->fail('购买抽奖券错误', ReturnCode::PARAMS_ERROR);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -60,7 +45,7 @@ class GameController extends OpenController
|
||||
$coin = $player ? (float) $player->coin : 0;
|
||||
return $this->success(['coin' => $coin], $msg);
|
||||
}
|
||||
return $this->fail($msg, ReturnCode::EMPTY_PARAMS);
|
||||
return $this->fail($msg, ReturnCode::BUSINESS_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,39 +64,24 @@ class GameController extends OpenController
|
||||
/**
|
||||
* 开始游戏(抽奖一局)
|
||||
* POST /api/game/playStart
|
||||
* header: auth-token, user-token
|
||||
* header: auth-token, user-token(由 CheckUserTokenMiddleware 注入 request->user_id)
|
||||
* body: rediction 必传,0=无 1=中奖
|
||||
* 余额不足时返回 code=200、message=玩家当前余额不足无法开启对局;超时返回 code=200、message=服务超时,并记录 status=0
|
||||
*/
|
||||
public function playStart(Request $request): Response
|
||||
{
|
||||
$token = $request->header('user-token');
|
||||
if (empty($token)) {
|
||||
$auth = $request->header('authorization');
|
||||
if ($auth && stripos($auth, 'Bearer ') === 0) {
|
||||
$token = trim(substr($auth, 7));
|
||||
}
|
||||
}
|
||||
if (empty($token)) {
|
||||
return $this->fail('请携带 user-token', ReturnCode::MISSING_TOKEN);
|
||||
}
|
||||
$userId = UserLogic::getUserIdFromToken($token);
|
||||
if ($userId === null) {
|
||||
return $this->fail('user-token 无效或已过期', ReturnCode::TOKEN_TIMEOUT);
|
||||
}
|
||||
|
||||
$userId = UserLogic::getUserIdFromRequest($request) ?? 0;
|
||||
$rediction = $request->post('rediction');
|
||||
if ($rediction === '' || $rediction === null) {
|
||||
return $this->fail('请传递 rediction 参数', ReturnCode::EMPTY_PARAMS);
|
||||
return $this->fail('请传递 rediction 参数', ReturnCode::PARAMS_ERROR);
|
||||
}
|
||||
$direction = (int) $rediction;
|
||||
if (!in_array($direction, [0, 1], true)) {
|
||||
return $this->fail('rediction 必须为 0 或 1', ReturnCode::EMPTY_PARAMS);
|
||||
return $this->fail('rediction 必须为 0 或 1', ReturnCode::PARAMS_ERROR);
|
||||
}
|
||||
|
||||
$player = DicePlayer::find($userId);
|
||||
if (!$player) {
|
||||
return $this->fail('用户不存在', ReturnCode::EMPTY_PARAMS);
|
||||
return $this->fail('用户不存在', ReturnCode::NOT_FOUND);
|
||||
}
|
||||
$minEv = (float) DiceRewardConfig::min('real_ev');
|
||||
$minCoin = abs($minEv + 100);
|
||||
@@ -125,7 +95,7 @@ class GameController extends OpenController
|
||||
$data = $logic->run($userId, $direction);
|
||||
return $this->success($data);
|
||||
} catch (ApiException $e) {
|
||||
return $this->fail($e->getMessage(), ReturnCode::EMPTY_PARAMS);
|
||||
return $this->fail($e->getMessage(), ReturnCode::BUSINESS_ERROR);
|
||||
} catch (\Throwable $e) {
|
||||
$timeoutRecord = null;
|
||||
try {
|
||||
|
||||
@@ -28,7 +28,7 @@ class UserController extends OpenController
|
||||
$phone = $request->post('phone', '');
|
||||
$password = $request->post('password', '');
|
||||
if ($phone === '' || $password === '') {
|
||||
return $this->fail('请填写手机号和密码', ReturnCode::EMPTY_PARAMS);
|
||||
return $this->fail('请填写手机号和密码', ReturnCode::PARAMS_ERROR);
|
||||
}
|
||||
$logic = new UserLogic();
|
||||
$data = $logic->login($phone, $password);
|
||||
@@ -50,7 +50,7 @@ class UserController extends OpenController
|
||||
$password = $request->post('password', '');
|
||||
$nickname = $request->post('nickname');
|
||||
if ($phone === '' || $password === '') {
|
||||
return $this->fail('请填写手机号和密码', ReturnCode::EMPTY_PARAMS);
|
||||
return $this->fail('请填写手机号和密码', ReturnCode::PARAMS_ERROR);
|
||||
}
|
||||
$logic = new UserLogic();
|
||||
$data = $logic->register($phone, $password, $nickname ? (string) $nickname : null);
|
||||
@@ -64,54 +64,31 @@ class UserController extends OpenController
|
||||
/**
|
||||
* 退出登录
|
||||
* POST /api/user/logout
|
||||
* header: user-token(或 Authorization: Bearer <user-token>)
|
||||
* 将当前 user-token 加入黑名单,之后该 token 无法再用于获取 user_id
|
||||
* header: user-token(由 CheckUserTokenMiddleware 校验并注入 request->userToken)
|
||||
*/
|
||||
public function logout(Request $request): Response
|
||||
{
|
||||
$token = $request->header('user-token');
|
||||
if (empty($token)) {
|
||||
$auth = $request->header('authorization');
|
||||
if ($auth && stripos($auth, 'Bearer ') === 0) {
|
||||
$token = trim(substr($auth, 7));
|
||||
}
|
||||
$token = $request->userToken ?? UserLogic::getTokenFromRequest($request);
|
||||
if ($token === '' || !UserLogic::logout($token)) {
|
||||
return $this->fail('退出失败或 token 已失效', ReturnCode::TOKEN_INVALID);
|
||||
}
|
||||
if (empty($token)) {
|
||||
return $this->fail('请携带 user-token', ReturnCode::MISSING_TOKEN);
|
||||
}
|
||||
if (UserLogic::logout($token)) {
|
||||
return $this->success('已退出登录');
|
||||
}
|
||||
return $this->fail('退出失败或 token 已失效', ReturnCode::TOKEN_TIMEOUT);
|
||||
return $this->success('已退出登录');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
* GET /api/user/info
|
||||
* header: user-token(或 Authorization: Bearer <user-token>)
|
||||
* 返回:id, username, phone, uid, name, coin, total_draw_count
|
||||
* header: user-token(由 CheckUserTokenMiddleware 校验并注入 request->user_id)
|
||||
* 返回:id, username, phone, uid, name, coin, total_ticket_count
|
||||
*/
|
||||
public function info(Request $request): Response
|
||||
{
|
||||
$token = $request->header('user-token');
|
||||
if (empty($token)) {
|
||||
$auth = $request->header('authorization');
|
||||
if ($auth && stripos($auth, 'Bearer ') === 0) {
|
||||
$token = trim(substr($auth, 7));
|
||||
}
|
||||
}
|
||||
if (empty($token)) {
|
||||
return $this->fail('请携带 user-token', ReturnCode::MISSING_TOKEN);
|
||||
}
|
||||
$userId = UserLogic::getUserIdFromToken($token);
|
||||
if ($userId === null) {
|
||||
return $this->fail('user-token 无效或已过期', ReturnCode::TOKEN_TIMEOUT);
|
||||
}
|
||||
$userId = UserLogic::getUserIdFromRequest($request) ?? 0;
|
||||
$user = UserLogic::getCachedUser($userId);
|
||||
if (empty($user)) {
|
||||
return $this->fail('用户不存在', ReturnCode::EMPTY_PARAMS);
|
||||
return $this->fail('用户不存在', ReturnCode::NOT_FOUND);
|
||||
}
|
||||
$fields = ['id', 'username', 'phone', 'uid', 'name', 'coin', 'total_draw_count'];
|
||||
$fields = ['id', 'username', 'phone', 'uid', 'name', 'coin', 'total_ticket_count'];
|
||||
$info = [];
|
||||
foreach ($fields as $field) {
|
||||
if (array_key_exists($field, $user)) {
|
||||
@@ -122,30 +99,16 @@ class UserController extends OpenController
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取钱包余额(仅读缓存,不查库,低延迟)
|
||||
* 获取钱包余额(优先读缓存,缓存未命中时从库拉取并回写缓存)
|
||||
* GET /api/user/balance
|
||||
* header: user-token(或 Authorization: Bearer <user-token>)
|
||||
* 返回:coin, phone, username(登录时已写入缓存,本接口只从缓存读取)
|
||||
* header: user-token(由 CheckUserTokenMiddleware 校验并注入 request->user_id)
|
||||
*/
|
||||
public function balance(Request $request): Response
|
||||
{
|
||||
$token = $request->header('user-token');
|
||||
if (empty($token)) {
|
||||
$auth = $request->header('authorization');
|
||||
if ($auth && stripos($auth, 'Bearer ') === 0) {
|
||||
$token = trim(substr($auth, 7));
|
||||
}
|
||||
}
|
||||
if (empty($token)) {
|
||||
return $this->fail('请携带 user-token', ReturnCode::MISSING_TOKEN);
|
||||
}
|
||||
$userId = UserLogic::getUserIdFromToken($token);
|
||||
if ($userId === null) {
|
||||
return $this->fail('user-token 无效或已过期', ReturnCode::TOKEN_TIMEOUT);
|
||||
}
|
||||
$user = UserCache::getUser($userId);
|
||||
$userId = UserLogic::getUserIdFromRequest($request) ?? 0;
|
||||
$user = UserLogic::getCachedUser($userId);
|
||||
if (empty($user)) {
|
||||
return $this->fail('缓存已过期,请重新登录', ReturnCode::TOKEN_TIMEOUT);
|
||||
return $this->fail('用户不存在', ReturnCode::NOT_FOUND);
|
||||
}
|
||||
$coin = $user['coin'] ?? 0;
|
||||
if (is_string($coin) && is_numeric($coin)) {
|
||||
@@ -161,26 +124,12 @@ class UserController extends OpenController
|
||||
/**
|
||||
* 玩家钱包流水
|
||||
* GET /api/user/walletRecord
|
||||
* header: user-token(或 Authorization: Bearer <user-token>)
|
||||
* header: user-token(由 CheckUserTokenMiddleware 校验并注入 request->user_id)
|
||||
* 参数: page 页码(默认1), limit 每页条数(默认10), create_time_min/create_time_max 创建时间范围(可选)
|
||||
*/
|
||||
public function walletRecord(Request $request): Response
|
||||
{
|
||||
$token = $request->header('user-token');
|
||||
if (empty($token)) {
|
||||
$auth = $request->header('authorization');
|
||||
if ($auth && stripos($auth, 'Bearer ') === 0) {
|
||||
$token = trim(substr($auth, 7));
|
||||
}
|
||||
}
|
||||
if (empty($token)) {
|
||||
return $this->fail('请携带 user-token', ReturnCode::MISSING_TOKEN);
|
||||
}
|
||||
$userId = UserLogic::getUserIdFromToken($token);
|
||||
if ($userId === null) {
|
||||
return $this->fail('user-token 无效或已过期', ReturnCode::TOKEN_TIMEOUT);
|
||||
}
|
||||
|
||||
$userId = UserLogic::getUserIdFromRequest($request) ?? 0;
|
||||
$page = (int) $request->post('page', 1);
|
||||
$limit = (int) $request->post('limit', 10);
|
||||
if ($page < 1) {
|
||||
@@ -217,26 +166,12 @@ class UserController extends OpenController
|
||||
/**
|
||||
* 游玩记录
|
||||
* GET /api/user/playGameRecord
|
||||
* header: user-token(或 Authorization: Bearer <user-token>)
|
||||
* header: user-token(由 CheckUserTokenMiddleware 校验并注入 request->user_id)
|
||||
* 参数: page 页码(默认1), limit 每页条数(默认10), create_time_min/create_time_max 创建时间范围(可选)
|
||||
*/
|
||||
public function playGameRecord(Request $request): Response
|
||||
{
|
||||
$token = $request->header('user-token');
|
||||
if (empty($token)) {
|
||||
$auth = $request->header('authorization');
|
||||
if ($auth && stripos($auth, 'Bearer ') === 0) {
|
||||
$token = trim(substr($auth, 7));
|
||||
}
|
||||
}
|
||||
if (empty($token)) {
|
||||
return $this->fail('请携带 user-token', ReturnCode::MISSING_TOKEN);
|
||||
}
|
||||
$userId = UserLogic::getUserIdFromToken($token);
|
||||
if ($userId === null) {
|
||||
return $this->fail('user-token 无效或已过期', ReturnCode::TOKEN_TIMEOUT);
|
||||
}
|
||||
|
||||
$userId = UserLogic::getUserIdFromRequest($request) ?? 0;
|
||||
$page = (int) $request->post('page', 1);
|
||||
$limit = (int) $request->post('limit', 10);
|
||||
if ($page < 1) {
|
||||
|
||||
@@ -29,7 +29,7 @@ class GameLogic
|
||||
* 购买抽奖券
|
||||
* @param int $playerId 玩家ID(即 user_id)
|
||||
* @param int $count 购买档位:1 / 5 / 10
|
||||
* @return array 更新后的 coin, total_draw_count, paid_draw_count, free_draw_count
|
||||
* @return array 更新后的 coin, total_ticket_count, paid_ticket_count, free_ticket_count
|
||||
*/
|
||||
public function buyLotteryTickets(int $playerId, int $count): array
|
||||
{
|
||||
@@ -52,9 +52,9 @@ class GameLogic
|
||||
}
|
||||
|
||||
$coinAfter = $coinBefore - $cost;
|
||||
$totalBefore = (int) ($player->total_draw_count ?? 0);
|
||||
$paidBefore = (int) ($player->paid_draw_count ?? 0);
|
||||
$freeBefore = (int) ($player->free_draw_count ?? 0);
|
||||
$totalBefore = (int) ($player->total_ticket_count ?? 0);
|
||||
$paidBefore = (int) ($player->paid_ticket_count ?? 0);
|
||||
$freeBefore = (int) ($player->free_ticket_count ?? 0);
|
||||
|
||||
Db::transaction(function () use (
|
||||
$player,
|
||||
@@ -70,9 +70,9 @@ class GameLogic
|
||||
$freeBefore
|
||||
) {
|
||||
$player->coin = $coinAfter;
|
||||
$player->total_draw_count = $totalBefore + $addTotal;
|
||||
$player->paid_draw_count = $paidBefore + $addPaid;
|
||||
$player->free_draw_count = $freeBefore + $addFree;
|
||||
$player->total_ticket_count = $totalBefore + $addTotal;
|
||||
$player->paid_ticket_count = $paidBefore + $addPaid;
|
||||
$player->free_ticket_count = $freeBefore + $addFree;
|
||||
$player->save();
|
||||
|
||||
// 钱包流水记录
|
||||
@@ -82,9 +82,9 @@ class GameLogic
|
||||
'type' => self::WALLET_TYPE_BUY_DRAW,
|
||||
'wallet_before' => $coinBefore,
|
||||
'wallet_after' => $coinAfter,
|
||||
'total_draw_count' => $addTotal,
|
||||
'paid_draw_count' => $addPaid,
|
||||
'free_draw_count' => $addFree,
|
||||
'total_ticket_count' => $addTotal,
|
||||
'paid_ticket_count' => $addPaid,
|
||||
'free_ticket_count' => $addFree,
|
||||
'remark' => "购买抽奖券{$addTotal}次(付费{$addPaid}次+赠送{$addFree}次)",
|
||||
]);
|
||||
|
||||
@@ -92,9 +92,9 @@ class GameLogic
|
||||
DicePlayerTicketRecord::create([
|
||||
'player_id' => $playerId,
|
||||
'use_coins' => $cost,
|
||||
'total_draw_count' => $addTotal,
|
||||
'paid_draw_count' => $addPaid,
|
||||
'free_draw_count' => $addFree,
|
||||
'total_ticket_count' => $addTotal,
|
||||
'paid_ticket_count' => $addPaid,
|
||||
'free_ticket_count' => $addFree,
|
||||
'remark' => "购买抽奖券{$addTotal}次(付费{$addPaid}次+赠送{$addFree}次)",
|
||||
]);
|
||||
});
|
||||
@@ -105,9 +105,9 @@ class GameLogic
|
||||
|
||||
return [
|
||||
'coin' => (float) $updated->coin,
|
||||
'total_draw_count' => (int) $updated->total_draw_count,
|
||||
'paid_draw_count' => (int) $updated->paid_draw_count,
|
||||
'free_draw_count' => (int) $updated->free_draw_count,
|
||||
'total_ticket_count' => (int) $updated->total_ticket_count,
|
||||
'paid_ticket_count' => (int) $updated->paid_ticket_count,
|
||||
'free_ticket_count' => (int) $updated->free_ticket_count,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,8 +54,8 @@ class PlayStartLogic
|
||||
throw new ApiException('当前玩家余额小于DiceRewardConfigMin.real_ev+100无法继续游戏');
|
||||
}
|
||||
|
||||
$paid = (int) ($player->paid_draw_count ?? 0);
|
||||
$free = (int) ($player->free_draw_count ?? 0);
|
||||
$paid = (int) ($player->paid_ticket_count ?? 0);
|
||||
$free = (int) ($player->free_ticket_count ?? 0);
|
||||
if ($paid + $free <= 0) {
|
||||
throw new ApiException('抽奖券不足');
|
||||
}
|
||||
@@ -125,21 +125,21 @@ class PlayStartLogic
|
||||
$coinBefore = (float) $p->coin;
|
||||
$coinAfter = $coinBefore + $winCoin;
|
||||
$p->coin = $coinAfter;
|
||||
$p->total_draw_count = max(0, (int) $p->total_draw_count - 1);
|
||||
$p->total_ticket_count = max(0, (int) $p->total_ticket_count - 1);
|
||||
if ($ticketType === self::LOTTERY_TYPE_PAID) {
|
||||
$p->paid_draw_count = max(0, (int) $p->paid_draw_count - 1);
|
||||
$p->paid_ticket_count = max(0, (int) $p->paid_ticket_count - 1);
|
||||
} else {
|
||||
$p->free_draw_count = max(0, (int) $p->free_draw_count - 1);
|
||||
$p->free_ticket_count = max(0, (int) $p->free_ticket_count - 1);
|
||||
}
|
||||
|
||||
// 若本局中奖档位为 T5,则额外赠送 1 次免费抽奖次数(总次数也 +1),并记录抽奖券获取记录
|
||||
if ($isTierT5) {
|
||||
$p->free_draw_count = (int) $p->free_draw_count + 1;
|
||||
$p->total_draw_count = (int) $p->total_draw_count + 1;
|
||||
$p->free_ticket_count = (int) $p->free_ticket_count + 1;
|
||||
$p->total_ticket_count = (int) $p->total_ticket_count + 1;
|
||||
|
||||
DicePlayerTicketRecord::create([
|
||||
'player_id' => $playerId,
|
||||
'free_draw_count' => 1,
|
||||
'free_ticket_count' => 1,
|
||||
'remark' => '中奖结果为T5',
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -56,6 +56,8 @@ class UserLogic
|
||||
UserCache::setUser((int) $user->id, $userArr);
|
||||
|
||||
$userToken = $this->generateUserToken((int) $user->id);
|
||||
// 同一用户只保留最新一次登录的 token,旧 token 自动失效
|
||||
UserCache::setCurrentUserToken((int) $user->id, $userToken);
|
||||
return [
|
||||
'user' => $userArr,
|
||||
'user-token' => $userToken,
|
||||
@@ -94,6 +96,8 @@ class UserLogic
|
||||
UserCache::setUser((int) $user->id, $userArr);
|
||||
|
||||
$userToken = $this->generateUserToken((int) $user->id);
|
||||
// 同一用户只保留最新一次登录的 token,旧 token 自动失效
|
||||
UserCache::setCurrentUserToken((int) $user->id, $userToken);
|
||||
return [
|
||||
'user' => $userArr,
|
||||
'user-token' => $userToken,
|
||||
@@ -123,6 +127,41 @@ class UserLogic
|
||||
return $result['access_token'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求中解析 user-token(header: user-token 或 Authorization: Bearer)
|
||||
* @param object $request 需有 header(string $name) 方法
|
||||
*/
|
||||
public static function getTokenFromRequest(object $request): string
|
||||
{
|
||||
$token = $request->header('user-token') ?? '';
|
||||
if ($token !== '') {
|
||||
return trim((string) $token);
|
||||
}
|
||||
$auth = $request->header('authorization');
|
||||
if ($auth && stripos($auth, 'Bearer ') === 0) {
|
||||
return trim(substr($auth, 7));
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求获取当前用户 ID:优先 request->user_id,否则从 header 的 user-token 解析
|
||||
* 中间件未正确注入时仍可兜底解析
|
||||
* @param object $request 需有 user_id 属性及 header() 方法
|
||||
*/
|
||||
public static function getUserIdFromRequest(object $request): ?int
|
||||
{
|
||||
$id = $request->user_id ?? null;
|
||||
if ($id !== null && (int) $id > 0) {
|
||||
return (int) $id;
|
||||
}
|
||||
$token = self::getTokenFromRequest($request);
|
||||
if ($token === '') {
|
||||
return null;
|
||||
}
|
||||
return self::getUserIdFromToken($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 user-token 获取 user_id(不写缓存,仅解析 JWT)
|
||||
* 若 token 已通过退出接口加入黑名单,返回 null
|
||||
@@ -139,7 +178,15 @@ class UserLogic
|
||||
return null;
|
||||
}
|
||||
$id = $extend['id'] ?? null;
|
||||
return $id !== null ? (int) $id : null;
|
||||
if ($id === null) {
|
||||
return null;
|
||||
}
|
||||
$userId = (int) $id;
|
||||
// 同一用户只允许当前登记的 token 生效,重新登录/注册后旧 token 失效
|
||||
if (!UserCache::isCurrentUserToken($userId, $userToken)) {
|
||||
return null;
|
||||
}
|
||||
return $userId;
|
||||
} catch (\Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\api\middleware;
|
||||
|
||||
use support\Log;
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
use Webman\MiddlewareInterface;
|
||||
use Tinywan\Jwt\JwtToken;
|
||||
use Tinywan\Jwt\Exception\JwtTokenException;
|
||||
use Tinywan\Jwt\Exception\JwtTokenExpiredException;
|
||||
use app\api\util\ReturnCode;
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
|
||||
/**
|
||||
* API 鉴权中间件
|
||||
* 校验请求头 auth-token(或 Authorization: Bearer xxx),白名单路径不校验
|
||||
*/
|
||||
class CheckApiAuthMiddleware implements MiddlewareInterface
|
||||
{
|
||||
/** 不需要 auth-token 的路径(仅获取 token 的接口) */
|
||||
private const WHITELIST = [
|
||||
'api/authToken',
|
||||
];
|
||||
|
||||
public function process(Request $request, callable $handler): Response
|
||||
{
|
||||
$path = trim($request->path(), '/');
|
||||
if ($this->isWhitelist($path)) {
|
||||
return $handler($request);
|
||||
}
|
||||
|
||||
$token = $request->header('auth-token');
|
||||
if (empty($token)) {
|
||||
$auth = $request->header('authorization');
|
||||
if ($auth && stripos($auth, 'Bearer ') === 0) {
|
||||
$token = trim(substr($auth, 7));
|
||||
}
|
||||
}
|
||||
if (empty($token)) {
|
||||
throw new ApiException('请携带 auth-token', ReturnCode::MISSING_TOKEN);
|
||||
}
|
||||
|
||||
try {
|
||||
// ACCESS_TOKEN = 1(JwtToken 内部私有常量)
|
||||
$decoded = JwtToken::verify(1, $token);
|
||||
$extend = $decoded['extend'] ?? [];
|
||||
if (($extend['plat'] ?? '') !== 'api') {
|
||||
throw new ApiException('auth-token 无效', ReturnCode::TOKEN_TIMEOUT);
|
||||
}
|
||||
} catch (JwtTokenExpiredException $e) {
|
||||
Log::error('auth-token 已过期, 报错信息'. $e);
|
||||
throw new ApiException('auth-token 已过期', ReturnCode::TOKEN_TIMEOUT);
|
||||
} catch (JwtTokenException $e) {
|
||||
Log::error('auth-token 无效, 报错信息'. $e);
|
||||
throw new ApiException($e->getMessage() ?: 'auth-token 无效', ReturnCode::TOKEN_TIMEOUT);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('auth-token 校验失败, 报错信息'. $e);
|
||||
throw new ApiException('auth-token 校验失败', ReturnCode::TOKEN_TIMEOUT);
|
||||
}
|
||||
|
||||
return $handler($request);
|
||||
}
|
||||
|
||||
private function isWhitelist(string $path): bool
|
||||
{
|
||||
foreach (self::WHITELIST as $prefix) {
|
||||
if ($path === $prefix || str_starts_with($path, $prefix . '/')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
109
server/app/api/middleware/CheckAuthTokenMiddleware.php
Normal file
109
server/app/api/middleware/CheckAuthTokenMiddleware.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\api\middleware;
|
||||
|
||||
use support\Log;
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
use Webman\MiddlewareInterface;
|
||||
use Tinywan\Jwt\JwtToken;
|
||||
use Tinywan\Jwt\Exception\JwtTokenException;
|
||||
use Tinywan\Jwt\Exception\JwtTokenExpiredException;
|
||||
use app\api\util\ReturnCode;
|
||||
use app\api\cache\AuthTokenCache;
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
|
||||
/**
|
||||
* 仅校验 auth-token 请求头
|
||||
* 白名单路径(如 /api/authToken)不校验,其它接口必须携带有效 auth-token 或 Authorization: Bearer <token>,且必须通过 JWT 签名与 plat=api 校验
|
||||
*/
|
||||
class CheckAuthTokenMiddleware implements MiddlewareInterface
|
||||
{
|
||||
/** 不需要 auth-token 的路径 */
|
||||
private const WHITELIST = [
|
||||
'api/authToken',
|
||||
];
|
||||
|
||||
/** JWT 至少为 xxx.yyy.zzz 三段 */
|
||||
private const JWT_PARTS_MIN = 3;
|
||||
|
||||
public function process(Request $request, callable $handler): Response
|
||||
{
|
||||
$path = trim((string) $request->path(), '/');
|
||||
if ($this->isWhitelist($path)) {
|
||||
return $handler($request);
|
||||
}
|
||||
|
||||
$token = $this->getAuthTokenFromRequest($request);
|
||||
if ($token === '') {
|
||||
throw new ApiException('请携带 auth-token', ReturnCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if (!$this->looksLikeJwt($token)) {
|
||||
throw new ApiException('auth-token 格式无效', ReturnCode::TOKEN_INVALID);
|
||||
}
|
||||
|
||||
$decoded = $this->verifyAuthToken($token);
|
||||
$extend = $decoded['extend'] ?? [];
|
||||
if (($extend['plat'] ?? '') !== 'api') {
|
||||
throw new ApiException('auth-token 无效(非 API 凭证)', ReturnCode::TOKEN_INVALID);
|
||||
}
|
||||
|
||||
// 同一设备只允许一个 auth-token 生效,非当前 token 视为已失效
|
||||
$device = (string) ($extend['device'] ?? '');
|
||||
if ($device !== '' && !AuthTokenCache::isCurrentToken($device, $token)) {
|
||||
throw new ApiException('auth-token 已失效(该设备已签发新凭证,请使用新 auth-token)', ReturnCode::TOKEN_INVALID);
|
||||
}
|
||||
|
||||
return $handler($request);
|
||||
}
|
||||
|
||||
private function getAuthTokenFromRequest(Request $request): string
|
||||
{
|
||||
$token = $request->header('auth-token');
|
||||
if ($token !== null && $token !== '') {
|
||||
return trim((string) $token);
|
||||
}
|
||||
$auth = $request->header('authorization');
|
||||
if ($auth && stripos($auth, 'Bearer ') === 0) {
|
||||
return trim(substr($auth, 7));
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
private function looksLikeJwt(string $token): bool
|
||||
{
|
||||
$parts = explode('.', $token);
|
||||
return count($parts) >= self::JWT_PARTS_MIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验 auth-token 有效性(签名、过期、iss 等),无效或过期必抛 ApiException
|
||||
*/
|
||||
private function verifyAuthToken(string $token): array
|
||||
{
|
||||
try {
|
||||
return JwtToken::verify(1, $token);
|
||||
} catch (JwtTokenExpiredException $e) {
|
||||
Log::error('auth-token 已过期, 报错信息' . $e);
|
||||
throw new ApiException('auth-token 已过期', ReturnCode::TOKEN_INVALID);
|
||||
} catch (JwtTokenException $e) {
|
||||
Log::error('auth-token 无效, 报错信息' . $e);
|
||||
throw new ApiException($e->getMessage() ?: 'auth-token 无效', ReturnCode::TOKEN_INVALID);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('auth-token 校验失败, 报错信息' . $e);
|
||||
throw new ApiException('auth-token 校验失败', ReturnCode::TOKEN_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
private function isWhitelist(string $path): bool
|
||||
{
|
||||
foreach (self::WHITELIST as $prefix) {
|
||||
if ($path === $prefix || str_starts_with($path, $prefix . '/')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
42
server/app/api/middleware/CheckUserTokenMiddleware.php
Normal file
42
server/app/api/middleware/CheckUserTokenMiddleware.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\api\middleware;
|
||||
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
use Webman\MiddlewareInterface;
|
||||
use app\api\logic\UserLogic;
|
||||
use app\api\util\ReturnCode;
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
|
||||
/**
|
||||
* 校验 user-token 请求头
|
||||
* 从 header 读取 user-token 或 Authorization: Bearer <user-token>,校验通过后将 user_id、userToken 写入 request 供控制器使用
|
||||
*/
|
||||
class CheckUserTokenMiddleware implements MiddlewareInterface
|
||||
{
|
||||
public function process(Request $request, callable $handler): Response
|
||||
{
|
||||
$token = $request->header('user-token');
|
||||
if (empty($token)) {
|
||||
$auth = $request->header('authorization');
|
||||
if ($auth && stripos($auth, 'Bearer ') === 0) {
|
||||
$token = trim(substr($auth, 7));
|
||||
}
|
||||
}
|
||||
if (empty($token)) {
|
||||
throw new ApiException('请携带 user-token', ReturnCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$userId = UserLogic::getUserIdFromToken($token);
|
||||
if ($userId === null) {
|
||||
throw new ApiException('user-token 无效或已过期', ReturnCode::TOKEN_INVALID);
|
||||
}
|
||||
|
||||
$request->user_id = $userId;
|
||||
$request->userToken = $token;
|
||||
|
||||
return $handler($request);
|
||||
}
|
||||
}
|
||||
@@ -109,7 +109,7 @@ class LotteryService
|
||||
return $tiers[4];
|
||||
}
|
||||
|
||||
/** 按 paid_draw_count 与 free_draw_count 权重随机抽取 0=付费 1=免费 */
|
||||
/** 按 paid_ticket_count 与 free_ticket_count 权重随机抽取 0=付费 1=免费 */
|
||||
public static function drawTicketType(int $paid, int $free): int
|
||||
{
|
||||
if ($paid <= 0 && $free <= 0) {
|
||||
|
||||
@@ -4,19 +4,32 @@ declare(strict_types=1);
|
||||
namespace app\api\util;
|
||||
|
||||
/**
|
||||
* API 状态码统一管理
|
||||
* API 统一状态码
|
||||
* 与 HTTP 语义对齐,便于前端与网关处理
|
||||
*/
|
||||
class ReturnCode
|
||||
{
|
||||
/** 200 成功 */
|
||||
public const SUCCESS = 200;
|
||||
|
||||
/** 201 请携带 token(auth-token / user-token) */
|
||||
public const MISSING_TOKEN = 201;
|
||||
/** 400 请求参数错误(缺少参数、参数无效、格式错误等) */
|
||||
public const PARAMS_ERROR = 400;
|
||||
|
||||
/** 202 缺少参数 / 参数错误 / 业务校验不通过(如余额不足、购买抽奖券错误等) */
|
||||
public const EMPTY_PARAMS = 202;
|
||||
/** 401 未授权(未携带 auth-token 或 user-token) */
|
||||
public const UNAUTHORIZED = 401;
|
||||
|
||||
/** 203 token 过期或无效(auth-token / user-token 过期、缓存已过期等) */
|
||||
public const TOKEN_TIMEOUT = 203;
|
||||
/** 402 token 无效或已过期(格式无效、签名错误、过期、非当前有效 token 等) */
|
||||
public const TOKEN_INVALID = 402;
|
||||
|
||||
/** 403 鉴权失败(密钥错误、签名验证失败等) */
|
||||
public const FORBIDDEN = 403;
|
||||
|
||||
/** 404 资源不存在(用户不存在等) */
|
||||
public const NOT_FOUND = 404;
|
||||
|
||||
/** 422 业务逻辑错误(余额不足、购买失败、业务校验不通过等) */
|
||||
public const BUSINESS_ERROR = 422;
|
||||
|
||||
/** 500 服务器内部错误 */
|
||||
public const SERVER_ERROR = 500;
|
||||
}
|
||||
|
||||
@@ -41,12 +41,12 @@ class DicePlayerTicketRecordController extends BaseController
|
||||
['username', ''],
|
||||
['use_coins_min', ''],
|
||||
['use_coins_max', ''],
|
||||
['total_draw_count_min', ''],
|
||||
['total_draw_count_max', ''],
|
||||
['paid_draw_count_min', ''],
|
||||
['paid_draw_count_max', ''],
|
||||
['free_draw_count_min', ''],
|
||||
['free_draw_count_max', ''],
|
||||
['total_ticket_count_min', ''],
|
||||
['total_ticket_count_max', ''],
|
||||
['paid_ticket_count_min', ''],
|
||||
['paid_ticket_count_max', ''],
|
||||
['free_ticket_count_min', ''],
|
||||
['free_ticket_count_max', ''],
|
||||
['create_time_min', ''],
|
||||
['create_time_max', ''],
|
||||
]);
|
||||
|
||||
@@ -25,28 +25,28 @@ class DicePlayerTicketRecordLogic extends BaseLogic
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加前:总抽奖次数 = 购买抽奖次数 + 赠送抽奖次数
|
||||
* 添加前:total_ticket_count = paid_ticket_count + free_ticket_count
|
||||
*/
|
||||
public function add(array $data): mixed
|
||||
{
|
||||
$data = $this->fillTotalDrawCount($data);
|
||||
$data = $this->fillTotalTicketCount($data);
|
||||
return parent::add($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改前:总抽奖次数 = 购买抽奖次数 + 赠送抽奖次数
|
||||
* 修改前:total_ticket_count = paid_ticket_count + free_ticket_count
|
||||
*/
|
||||
public function edit($id, array $data): mixed
|
||||
{
|
||||
$data = $this->fillTotalDrawCount($data);
|
||||
$data = $this->fillTotalTicketCount($data);
|
||||
return parent::edit($id, $data);
|
||||
}
|
||||
|
||||
private function fillTotalDrawCount(array $data): array
|
||||
private function fillTotalTicketCount(array $data): array
|
||||
{
|
||||
$paid = isset($data['paid_draw_count']) ? (int) $data['paid_draw_count'] : 0;
|
||||
$free = isset($data['free_draw_count']) ? (int) $data['free_draw_count'] : 0;
|
||||
$data['total_draw_count'] = $paid + $free;
|
||||
$paid = isset($data['paid_ticket_count']) ? (int) $data['paid_ticket_count'] : 0;
|
||||
$free = isset($data['free_ticket_count']) ? (int) $data['free_ticket_count'] : 0;
|
||||
$data['total_ticket_count'] = $paid + $free;
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ class DicePlayerWalletRecordLogic extends BaseLogic
|
||||
*/
|
||||
public function add(array $data): mixed
|
||||
{
|
||||
$data['total_draw_count'] = $data['total_draw_count'] ?? 0;
|
||||
$data['paid_draw_count'] = $data['paid_draw_count'] ?? 0;
|
||||
$data['free_draw_count'] = $data['free_draw_count'] ?? 0;
|
||||
$data['total_ticket_count'] = $data['total_ticket_count'] ?? 0;
|
||||
$data['paid_ticket_count'] = $data['paid_ticket_count'] ?? 0;
|
||||
$data['free_ticket_count'] = $data['free_ticket_count'] ?? 0;
|
||||
return parent::add($data);
|
||||
}
|
||||
|
||||
@@ -81,9 +81,9 @@ class DicePlayerWalletRecordLogic extends BaseLogic
|
||||
'wallet_after' => $walletAfter,
|
||||
'remark' => $remark,
|
||||
'user_id' => $adminId,
|
||||
'total_draw_count' => 0,
|
||||
'paid_draw_count' => 0,
|
||||
'free_draw_count' => 0,
|
||||
'total_ticket_count' => 0,
|
||||
'paid_ticket_count' => 0,
|
||||
'free_ticket_count' => 0,
|
||||
];
|
||||
|
||||
return $this->model->create($record);
|
||||
|
||||
@@ -28,9 +28,9 @@ use app\dice\model\lottery_config\DiceLotteryConfig;
|
||||
* @property $t3_wight T3池权重
|
||||
* @property $t4_wight T4池权重
|
||||
* @property $t5_wight T5池权重
|
||||
* @property $total_draw_count 总抽奖次数
|
||||
* @property $paid_draw_count 购买抽奖次数
|
||||
* @property $free_draw_count 赠送抽奖次数
|
||||
* @property $total_ticket_count 总抽奖次数
|
||||
* @property $paid_ticket_count 购买抽奖次数
|
||||
* @property $free_ticket_count 赠送抽奖次数
|
||||
* @property $created_at 创建时间
|
||||
* @property $updated_at 更新时间
|
||||
* @property $deleted_at 删除时间
|
||||
|
||||
@@ -18,9 +18,9 @@ use think\model\relation\BelongsTo;
|
||||
* @property $id ID
|
||||
* @property $player_id 玩家id
|
||||
* @property $use_coins 消耗硬币
|
||||
* @property $total_draw_count 总抽奖次数
|
||||
* @property $paid_draw_count 购买抽奖次数
|
||||
* @property $free_draw_count 赠送抽奖次数
|
||||
* @property $total_ticket_count 总抽奖次数
|
||||
* @property $paid_ticket_count 购买抽奖次数
|
||||
* @property $free_ticket_count 赠送抽奖次数
|
||||
* @property $remark 备注
|
||||
* @property $create_time 创建时间
|
||||
* @property $update_time 修改时间
|
||||
@@ -79,51 +79,51 @@ class DicePlayerTicketRecord extends BaseModel
|
||||
}
|
||||
}
|
||||
|
||||
/** 总抽奖次数下限 */
|
||||
public function searchTotalDrawCountMinAttr($query, $value)
|
||||
/** 总抽奖次数(total_ticket_count)下限 */
|
||||
public function searchTotalTicketCountMinAttr($query, $value)
|
||||
{
|
||||
if ($value !== '' && $value !== null) {
|
||||
$query->where('total_draw_count', '>=', $value);
|
||||
$query->where('total_ticket_count', '>=', $value);
|
||||
}
|
||||
}
|
||||
|
||||
/** 总抽奖次数上限 */
|
||||
public function searchTotalDrawCountMaxAttr($query, $value)
|
||||
/** 总抽奖次数(total_ticket_count)上限 */
|
||||
public function searchTotalTicketCountMaxAttr($query, $value)
|
||||
{
|
||||
if ($value !== '' && $value !== null) {
|
||||
$query->where('total_draw_count', '<=', $value);
|
||||
$query->where('total_ticket_count', '<=', $value);
|
||||
}
|
||||
}
|
||||
|
||||
/** 购买抽奖次数下限 */
|
||||
public function searchPaidDrawCountMinAttr($query, $value)
|
||||
/** 购买抽奖次数(paid_ticket_count)下限 */
|
||||
public function searchPaidTicketCountMinAttr($query, $value)
|
||||
{
|
||||
if ($value !== '' && $value !== null) {
|
||||
$query->where('paid_draw_count', '>=', $value);
|
||||
$query->where('paid_ticket_count', '>=', $value);
|
||||
}
|
||||
}
|
||||
|
||||
/** 购买抽奖次数上限 */
|
||||
public function searchPaidDrawCountMaxAttr($query, $value)
|
||||
/** 购买抽奖次数(paid_ticket_count)上限 */
|
||||
public function searchPaidTicketCountMaxAttr($query, $value)
|
||||
{
|
||||
if ($value !== '' && $value !== null) {
|
||||
$query->where('paid_draw_count', '<=', $value);
|
||||
$query->where('paid_ticket_count', '<=', $value);
|
||||
}
|
||||
}
|
||||
|
||||
/** 赠送抽奖次数下限 */
|
||||
public function searchFreeDrawCountMinAttr($query, $value)
|
||||
/** 赠送抽奖次数(free_ticket_count)下限 */
|
||||
public function searchFreeTicketCountMinAttr($query, $value)
|
||||
{
|
||||
if ($value !== '' && $value !== null) {
|
||||
$query->where('free_draw_count', '>=', $value);
|
||||
$query->where('free_ticket_count', '>=', $value);
|
||||
}
|
||||
}
|
||||
|
||||
/** 赠送抽奖次数上限 */
|
||||
public function searchFreeDrawCountMaxAttr($query, $value)
|
||||
/** 赠送抽奖次数(free_ticket_count)上限 */
|
||||
public function searchFreeTicketCountMaxAttr($query, $value)
|
||||
{
|
||||
if ($value !== '' && $value !== null) {
|
||||
$query->where('free_draw_count', '<=', $value);
|
||||
$query->where('free_ticket_count', '<=', $value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ use think\model\relation\BelongsTo;
|
||||
* @property $type 类型:0=充值 1=提现 2=购买抽奖次数
|
||||
* @property $wallet_before 钱包操作前
|
||||
* @property $wallet_after 钱包操作后
|
||||
* @property $total_draw_count 总抽奖次数
|
||||
* @property $paid_draw_count 购买抽奖次数
|
||||
* @property $free_draw_count 赠送抽奖次数
|
||||
* @property $total_ticket_count 总抽奖次数
|
||||
* @property $paid_ticket_count 购买抽奖次数
|
||||
* @property $free_ticket_count 赠送抽奖次数
|
||||
* @property $remark 备注
|
||||
* @property $user_id 操作管理员id(type 3/4 时记录)
|
||||
* @property $create_time 创建时间
|
||||
|
||||
@@ -19,9 +19,9 @@ class DicePlayerTicketRecordValidate extends BaseValidate
|
||||
protected $rule = [
|
||||
'player_id' => 'require',
|
||||
'use_coins' => 'require',
|
||||
'total_draw_count' => 'require',
|
||||
'paid_draw_count' => 'require',
|
||||
'free_draw_count' => 'require',
|
||||
'total_ticket_count' => 'require',
|
||||
'paid_ticket_count' => 'require',
|
||||
'free_ticket_count' => 'require',
|
||||
'remark' => 'require',
|
||||
];
|
||||
|
||||
@@ -31,9 +31,9 @@ class DicePlayerTicketRecordValidate extends BaseValidate
|
||||
protected $message = [
|
||||
'player_id' => '玩家id必须填写',
|
||||
'use_coins' => '消耗硬币必须填写',
|
||||
'total_draw_count' => '总抽奖次数必须填写',
|
||||
'paid_draw_count' => '购买抽奖次数必须填写',
|
||||
'free_draw_count' => '赠送抽奖次数必须填写',
|
||||
'total_ticket_count' => '总抽奖次数必须填写',
|
||||
'paid_ticket_count' => '购买抽奖次数必须填写',
|
||||
'free_ticket_count' => '赠送抽奖次数必须填写',
|
||||
'remark' => '备注必须填写',
|
||||
];
|
||||
|
||||
@@ -44,17 +44,17 @@ class DicePlayerTicketRecordValidate extends BaseValidate
|
||||
'save' => [
|
||||
'player_id',
|
||||
'use_coins',
|
||||
'total_draw_count',
|
||||
'paid_draw_count',
|
||||
'free_draw_count',
|
||||
'total_ticket_count',
|
||||
'paid_ticket_count',
|
||||
'free_ticket_count',
|
||||
'remark',
|
||||
],
|
||||
'update' => [
|
||||
'player_id',
|
||||
'use_coins',
|
||||
'total_draw_count',
|
||||
'paid_draw_count',
|
||||
'free_draw_count',
|
||||
'total_ticket_count',
|
||||
'paid_ticket_count',
|
||||
'free_ticket_count',
|
||||
'remark',
|
||||
],
|
||||
];
|
||||
|
||||
@@ -3,10 +3,18 @@
|
||||
* API 鉴权与用户相关配置
|
||||
*/
|
||||
return [
|
||||
// auth-token 签名密钥(与客户端约定,用于 /api/authToken 的 signature 校验,必填)
|
||||
'auth_token_secret' => env('API_AUTH_TOKEN_SECRET', ''),
|
||||
// auth-token 时间戳允许误差(秒),防重放,默认 300 秒
|
||||
'auth_token_time_tolerance' => (int) env('API_AUTH_TOKEN_TIME_TOLERANCE', 300),
|
||||
// auth-token 有效期(秒),默认 24 小时
|
||||
'auth_token_exp' => (int) env('API_AUTH_TOKEN_EXP', 86400),
|
||||
// auth-token 按设备存储的 Redis key 前缀(同一设备只保留最新一个 auth-token)
|
||||
'auth_token_device_prefix' => env('API_AUTH_TOKEN_DEVICE_PREFIX', 'api:auth_token:'),
|
||||
// user-token 有效期(秒),默认 7 天
|
||||
'user_token_exp' => (int) env('API_USER_TOKEN_EXP', 604800),
|
||||
// 按用户存储当前有效 user-token 的 Redis key 前缀(同一用户仅保留最新一次登录的 token)
|
||||
'user_token_current_prefix' => env('API_USER_TOKEN_CURRENT_PREFIX', 'api:user:current_token:'),
|
||||
// 用户信息 Redis 缓存过期时间(秒),默认 7 天
|
||||
'user_cache_expire' => (int) env('API_USER_CACHE_EXPIRE', 604800),
|
||||
// 用户缓存 Redis key 前缀
|
||||
|
||||
@@ -13,22 +13,29 @@
|
||||
*/
|
||||
|
||||
use Webman\Route;
|
||||
use app\api\middleware\CheckApiAuthMiddleware;
|
||||
use app\api\middleware\CheckAuthTokenMiddleware;
|
||||
use app\api\middleware\CheckUserTokenMiddleware;
|
||||
|
||||
// API 路由:需先调用 /api/authToken 获取 auth-token,请求时携带 header: auth-token 或 Authorization: Bearer <token>
|
||||
// 仅需 auth-token 的路由组(authToken 接口在中间件内白名单跳过)
|
||||
Route::group('/api', function () {
|
||||
Route::any('/authToken', [app\api\controller\AuthTokenController::class, 'index']);
|
||||
Route::post('/user/login', [app\api\controller\UserController::class, 'login']);
|
||||
Route::post('/user/register', [app\api\controller\UserController::class, 'register']);
|
||||
Route::post('/user/logout', [app\api\controller\UserController::class, 'logout']);
|
||||
Route::get('/user/info', [app\api\controller\UserController::class, 'info']);
|
||||
Route::get('/user/balance', [app\api\controller\UserController::class, 'balance']);
|
||||
Route::get('/user/walletRecord', [app\api\controller\UserController::class, 'walletRecord']);
|
||||
Route::get('/user/playGameRecord', [app\api\controller\UserController::class, 'playGameRecord']);
|
||||
Route::post('/game/buyLotteryTickets', [app\api\controller\GameController::class, 'buyLotteryTickets']);
|
||||
Route::get('/game/lotteryPool', [app\api\controller\GameController::class, 'lotteryPool']);
|
||||
Route::post('/game/playStart', [app\api\controller\GameController::class, 'playStart']);
|
||||
})->middleware([CheckApiAuthMiddleware::class]);
|
||||
|
||||
|
||||
Route::any('/user/login', [app\api\controller\UserController::class, 'login']);
|
||||
Route::any('/user/register', [app\api\controller\UserController::class, 'register']);
|
||||
})->middleware([
|
||||
CheckAuthTokenMiddleware::class,
|
||||
]);
|
||||
|
||||
// 需 auth-token + user-token 的路由组
|
||||
Route::group('/api', function () {
|
||||
Route::any('/user/logout', [app\api\controller\UserController::class, 'logout']);
|
||||
Route::any('/user/info', [app\api\controller\UserController::class, 'info']);
|
||||
Route::any('/user/balance', [app\api\controller\UserController::class, 'balance']);
|
||||
Route::any('/user/walletRecord', [app\api\controller\UserController::class, 'walletRecord']);
|
||||
Route::any('/user/playGameRecord', [app\api\controller\UserController::class, 'playGameRecord']);
|
||||
Route::any('/game/buyLotteryTickets', [app\api\controller\GameController::class, 'buyLotteryTickets']);
|
||||
Route::any('/game/lotteryPool', [app\api\controller\GameController::class, 'lotteryPool']);
|
||||
Route::any('/game/playStart', [app\api\controller\GameController::class, 'playStart']);
|
||||
})->middleware([
|
||||
CheckAuthTokenMiddleware::class,
|
||||
CheckUserTokenMiddleware::class,
|
||||
]);
|
||||
|
||||
@@ -20,6 +20,11 @@ namespace support;
|
||||
*/
|
||||
class Request extends \Webman\Http\Request
|
||||
{
|
||||
/** 由 CheckUserTokenMiddleware 注入:当前用户 ID */
|
||||
public ?int $user_id = null;
|
||||
|
||||
/** 由 CheckUserTokenMiddleware 注入:当前 user-token 原始字符串 */
|
||||
public ?string $userToken = null;
|
||||
|
||||
/**
|
||||
* 获取参数增强方法
|
||||
|
||||
Reference in New Issue
Block a user