350 lines
12 KiB
PHP
350 lines
12 KiB
PHP
<?php
|
|
|
|
namespace app\admin\controller\game;
|
|
|
|
use app\common\controller\Backend;
|
|
use support\think\Db;
|
|
use support\Response;
|
|
use Webman\Http\Request as WebmanRequest;
|
|
|
|
/**
|
|
* 游戏对局记录
|
|
*/
|
|
class Record extends Backend
|
|
{
|
|
protected ?object $model = null;
|
|
|
|
protected string|array $preExcludeFields = ['id', 'create_time', 'update_time', 'platform_profit_amount', 'winner_user_count'];
|
|
|
|
protected string|array $quickSearchField = ['id', 'period_no'];
|
|
|
|
protected string|array $defaultSortField = ['id' => 'desc'];
|
|
|
|
protected string|array $orderGuarantee = ['id' => 'desc'];
|
|
|
|
protected bool $modelValidate = true;
|
|
|
|
protected bool $modelSceneValidate = true;
|
|
|
|
protected function initController(WebmanRequest $request): ?Response
|
|
{
|
|
$this->model = new \app\common\model\GameRecord();
|
|
return null;
|
|
}
|
|
|
|
protected function _index(): Response
|
|
{
|
|
if ($this->request && $this->request->get('select')) {
|
|
return $this->select($this->request);
|
|
}
|
|
|
|
list($where, $alias, $limit, $order) = $this->queryBuilder();
|
|
|
|
$res = $this->model
|
|
->field($this->indexField)
|
|
->withJoin($this->withJoinTable, $this->withJoinType)
|
|
->with($this->withJoinTable)
|
|
->alias($alias)
|
|
->where($where)
|
|
->order($order)
|
|
->paginate($limit);
|
|
|
|
$list = $res->items();
|
|
foreach ($list as $idx => $row) {
|
|
if (!is_array($row)) {
|
|
continue;
|
|
}
|
|
$status = isset($row['status']) && is_numeric((string) $row['status']) ? (int) $row['status'] : 0;
|
|
$reason = isset($row['void_reason']) && is_string($row['void_reason']) ? $row['void_reason'] : '';
|
|
// 将“系统自愈作废”的对局在列表中标记为【异常】(展示态,不改库中 status=5 的事实)
|
|
if ($status === 5 && $reason !== '' && str_starts_with($reason, 'system_recover:')) {
|
|
$row['status'] = 6;
|
|
$list[$idx] = $row;
|
|
}
|
|
}
|
|
|
|
return $this->success('', [
|
|
'list' => $list,
|
|
'total' => $res->total(),
|
|
'remark' => get_route_remark(),
|
|
]);
|
|
}
|
|
|
|
public function add(WebmanRequest $request): Response
|
|
{
|
|
$response = $this->initializeBackend($request);
|
|
if ($response !== null) {
|
|
return $response;
|
|
}
|
|
return $this->error(__('Game record is generated by system; manual creation is not allowed'));
|
|
}
|
|
|
|
public function edit(WebmanRequest $request): Response
|
|
{
|
|
$response = $this->initializeBackend($request);
|
|
if ($response !== null) {
|
|
return $response;
|
|
}
|
|
if ($request->method() === 'POST') {
|
|
return $this->error(__('Game record cannot be edited'));
|
|
}
|
|
$pk = $this->model->getPk();
|
|
$id = $request->get($pk);
|
|
$row = $this->model->find($id);
|
|
if (!$row) {
|
|
return $this->error(__('Record not found'));
|
|
}
|
|
return $this->success('', ['row' => $row]);
|
|
}
|
|
|
|
public function del(WebmanRequest $request): Response
|
|
{
|
|
$response = $this->initializeBackend($request);
|
|
if ($response !== null) {
|
|
return $response;
|
|
}
|
|
return $this->error(__('Game record cannot be deleted'));
|
|
}
|
|
|
|
public function abnormalList(WebmanRequest $request): Response
|
|
{
|
|
$response = $this->initializeBackend($request);
|
|
if ($response !== null) {
|
|
return $response;
|
|
}
|
|
|
|
$limitRaw = $request->get('limit', 30);
|
|
$limit = is_numeric((string) $limitRaw) ? (int) $limitRaw : 30;
|
|
if ($limit < 1) {
|
|
$limit = 1;
|
|
}
|
|
if ($limit > 200) {
|
|
$limit = 200;
|
|
}
|
|
|
|
$rows = Db::name('game_record')
|
|
->where('status', 5)
|
|
// 仅展示服务重启自愈导致的异常作废,排除管理员手动作废
|
|
->whereLike('void_reason', 'system_recover:%')
|
|
->field(['id', 'period_no', 'void_reason', 'update_time'])
|
|
->order('id', 'desc')
|
|
->limit($limit)
|
|
->select()
|
|
->toArray();
|
|
|
|
$periodIds = [];
|
|
foreach ($rows as $row) {
|
|
$pid = (int) ($row['id'] ?? 0);
|
|
if ($pid > 0) {
|
|
$periodIds[] = $pid;
|
|
}
|
|
}
|
|
$refundAggByPeriod = [];
|
|
$refundIdsByPeriod = [];
|
|
if ($periodIds !== []) {
|
|
$aggRows = Db::name('bet_order')
|
|
->whereIn('period_id', $periodIds)
|
|
->where('status', 3)
|
|
->fieldRaw('period_id as pid, COUNT(*) as cnt, COUNT(DISTINCT user_id) as users, COALESCE(SUM(total_amount), 0) as amt')
|
|
->group('period_id')
|
|
->select()
|
|
->toArray();
|
|
foreach ($aggRows as $a) {
|
|
$pid = (int) ($a['pid'] ?? 0);
|
|
if ($pid <= 0) {
|
|
continue;
|
|
}
|
|
$refundAggByPeriod[$pid] = [
|
|
'orders' => (int) ($a['cnt'] ?? 0),
|
|
'users' => (int) ($a['users'] ?? 0),
|
|
'amount' => (string) ($a['amt'] ?? '0.00'),
|
|
];
|
|
}
|
|
|
|
$idRows = Db::name('bet_order')
|
|
->whereIn('period_id', $periodIds)
|
|
->where('status', 3)
|
|
->field(['period_id', 'id'])
|
|
->order('id', 'desc')
|
|
->limit(2000)
|
|
->select()
|
|
->toArray();
|
|
foreach ($idRows as $r) {
|
|
$pid = (int) ($r['period_id'] ?? 0);
|
|
$bid = (int) ($r['id'] ?? 0);
|
|
if ($pid <= 0 || $bid <= 0) {
|
|
continue;
|
|
}
|
|
if (!isset($refundIdsByPeriod[$pid])) {
|
|
$refundIdsByPeriod[$pid] = [];
|
|
}
|
|
if (count($refundIdsByPeriod[$pid]) < 50) {
|
|
$refundIdsByPeriod[$pid][] = $bid;
|
|
}
|
|
}
|
|
}
|
|
|
|
$list = [];
|
|
foreach ($rows as $row) {
|
|
$meta = $this->parseRecoverVoidReason(is_string($row['void_reason'] ?? null) ? $row['void_reason'] : '');
|
|
$reason = is_string($row['void_reason'] ?? null) ? $row['void_reason'] : '';
|
|
$isAutoRecover = $this->isSystemRecoverReason($reason);
|
|
$pid = (int) ($row['id'] ?? 0);
|
|
$agg = $refundAggByPeriod[$pid] ?? null;
|
|
$users = (int) ($meta['users'] ?? 0);
|
|
$orders = (int) ($meta['orders'] ?? 0);
|
|
$amount = is_string($meta['amount'] ?? null) ? $meta['amount'] : '0.00';
|
|
if (is_array($agg)) {
|
|
if ($orders <= 0 && ($agg['orders'] ?? 0) > 0) {
|
|
$orders = (int) ($agg['orders'] ?? 0);
|
|
}
|
|
if ($users <= 0 && ($agg['users'] ?? 0) > 0) {
|
|
$users = (int) ($agg['users'] ?? 0);
|
|
}
|
|
if (bccomp($amount, '0', 2) <= 0 && is_string($agg['amount'] ?? null)) {
|
|
$amount = (string) $agg['amount'];
|
|
}
|
|
}
|
|
$list[] = [
|
|
'id' => (int) ($row['id'] ?? 0),
|
|
'period_no' => (string) ($row['period_no'] ?? ''),
|
|
'abnormal_from_status' => $meta['from_status'],
|
|
'refunded_user_count' => $users,
|
|
'refunded_order_count' => $orders,
|
|
'refunded_total_amount' => $amount,
|
|
'refunded_order_ids' => $refundIdsByPeriod[$pid] ?? [],
|
|
'recovered_at' => (int) ($row['update_time'] ?? 0),
|
|
'void_reason' => $reason,
|
|
'is_auto_recover' => $isAutoRecover ? 1 : 0,
|
|
];
|
|
}
|
|
|
|
return $this->success('', [
|
|
'list' => $list,
|
|
'total' => count($list),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 某期对局的游玩(压注)记录列表(用于对局记录页弹窗查看)。
|
|
*/
|
|
public function playRecordList(WebmanRequest $request): Response
|
|
{
|
|
$response = $this->initializeBackend($request);
|
|
if ($response !== null) {
|
|
return $response;
|
|
}
|
|
|
|
$periodIdRaw = $request->get('period_id');
|
|
if (!is_numeric((string) $periodIdRaw)) {
|
|
return $this->error(__('Parameter error'));
|
|
}
|
|
$periodId = (int) $periodIdRaw;
|
|
if ($periodId <= 0) {
|
|
return $this->error(__('Parameter error'));
|
|
}
|
|
|
|
$pageRaw = $request->get('page', 1);
|
|
$page = is_numeric((string) $pageRaw) ? (int) $pageRaw : 1;
|
|
if ($page < 1) {
|
|
$page = 1;
|
|
}
|
|
|
|
$limitRaw = $request->get('limit', 30);
|
|
$limit = is_numeric((string) $limitRaw) ? (int) $limitRaw : 30;
|
|
if ($limit < 1) {
|
|
$limit = 1;
|
|
}
|
|
if ($limit > 200) {
|
|
$limit = 200;
|
|
}
|
|
|
|
$query = Db::name('game_play_record')
|
|
->alias('pr')
|
|
->leftJoin('user u', 'u.id = pr.user_id')
|
|
->leftJoin('channel c', 'c.id = pr.channel_id')
|
|
->where('pr.period_id', $periodId);
|
|
|
|
$total = (int) $query->count('pr.id');
|
|
$list = $query
|
|
->field([
|
|
'pr.id',
|
|
'pr.period_id',
|
|
'pr.user_id',
|
|
'pr.channel_id',
|
|
'pr.pick_numbers',
|
|
'pr.total_amount',
|
|
'pr.streak_at_bet',
|
|
'pr.is_auto',
|
|
'pr.win_amount',
|
|
'pr.jackpot_extra_amount',
|
|
'pr.status',
|
|
'pr.idempotency_key',
|
|
'pr.create_time',
|
|
'pr.update_time',
|
|
'u.username as user_username',
|
|
'c.name as channel_name',
|
|
])
|
|
->order('pr.id', 'desc')
|
|
->page($page, $limit)
|
|
->select()
|
|
->toArray();
|
|
|
|
return $this->success('', [
|
|
'list' => $list,
|
|
'total' => $total,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @return array{from_status:int,users:int,orders:int,amount:string}
|
|
*/
|
|
private function parseRecoverVoidReason(string $reason): array
|
|
{
|
|
$meta = [
|
|
'from_status' => -1,
|
|
'users' => 0,
|
|
'orders' => 0,
|
|
'amount' => '0.00',
|
|
];
|
|
if (!$this->isSystemRecoverReason($reason)) {
|
|
return $meta;
|
|
}
|
|
$payload = substr($reason, strlen('system_recover:'));
|
|
if (!str_contains($payload, '=')) {
|
|
return $meta;
|
|
}
|
|
$parts = explode('|', $payload);
|
|
foreach ($parts as $part) {
|
|
$item = trim($part);
|
|
if ($item === '' || !str_contains($item, '=')) {
|
|
continue;
|
|
}
|
|
[$key, $value] = explode('=', $item, 2);
|
|
$key = trim($key);
|
|
$value = trim($value);
|
|
if ($key === 'from' && is_numeric($value)) {
|
|
$meta['from_status'] = (int) $value;
|
|
continue;
|
|
}
|
|
if ($key === 'users' && is_numeric($value)) {
|
|
$meta['users'] = (int) $value;
|
|
continue;
|
|
}
|
|
if ($key === 'orders' && is_numeric($value)) {
|
|
$meta['orders'] = (int) $value;
|
|
continue;
|
|
}
|
|
if ($key === 'amount' && $value !== '') {
|
|
$meta['amount'] = $value;
|
|
}
|
|
}
|
|
return $meta;
|
|
}
|
|
|
|
private function isSystemRecoverReason(string $reason): bool
|
|
{
|
|
return $reason !== '' && str_starts_with($reason, 'system_recover:');
|
|
}
|
|
}
|