Files
webman-buildadmin/app/common/library/game/DepositTier.php
2026-04-18 15:19:36 +08:00

352 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace app\common\library\game;
use InvalidArgumentException;
/**
* 充值档位game_config.deposit_tier仅存 JSON 数组
*
* 每一项字段mock/第三方支付模式,已不再保存收款账户信息;支持中英文双语):
* - id : string档位稳定 ID如 t_xxxxxxxx
* - title : string档位中文名称必填前端中文环境展示
* - title_en : string档位英文名称可选前端英文环境展示为空时回退到 title
* - amount : string充值金额4 位小数)
* - bonus_amount : string赠送金额4 位小数,可为 0
* - desc : string档位中文描述可空<=255
* - desc_en : string档位英文描述可空<=255为空时回退到 desc
* - sort : int排序权重小值在前
* - status : int0=停用1=启用
*
* 历史数据兼容:老字段 name 会在 title 缺失时作为 title 兜底(更老的 account_name 亦会兜底)。
*/
final class DepositTier
{
public const CONFIG_KEY = 'deposit_tier';
/**
* 从 game_config.config_value 中解析出档位数组(容错)
*
* @return list<array{
* id: string,
* title: string,
* title_en: string,
* amount: string,
* bonus_amount: string,
* desc: string,
* desc_en: string,
* sort: int,
* status: int,
* }>
*/
public static function parseFromConfigValue($raw): array
{
if (!is_string($raw) || trim($raw) === '') {
return [];
}
$decoded = json_decode($raw, true);
if (!is_array($decoded)) {
return [];
}
if (isset($decoded['tiers']) && is_array($decoded['tiers'])) {
$list = $decoded['tiers'];
} else {
$list = $decoded;
}
return self::normalizeList($list);
}
/**
* @param list<mixed> $items
*/
public static function normalizeList(array $items): array
{
$out = [];
foreach ($items as $row) {
if (!is_array($row)) {
continue;
}
$id = isset($row['id']) && is_string($row['id']) ? trim($row['id']) : '';
if ($id === '') {
$id = self::generateId();
}
$title = self::stringField($row, 'title');
if ($title === '') {
// 兼容历史:字段名 name 或更老的 account_name
$title = self::stringField($row, 'name');
if ($title === '') {
$title = self::stringField($row, 'account_name');
}
}
$titleEn = self::stringField($row, 'title_en');
$amount = self::normalizeAmount($row['amount'] ?? '');
$bonus = self::normalizeAmount($row['bonus_amount'] ?? '0');
$desc = self::stringField($row, 'desc');
if ($desc === '') {
$desc = self::stringField($row, 'remark');
}
$descEn = self::stringField($row, 'desc_en');
$sort = isset($row['sort']) && is_numeric($row['sort']) ? intval($row['sort']) : 0;
$status = isset($row['status']) && is_numeric($row['status']) ? intval($row['status']) : 1;
$status = $status === 1 ? 1 : 0;
$out[] = [
'id' => $id,
'title' => $title,
'title_en' => $titleEn,
'amount' => $amount,
'bonus_amount' => $bonus,
'desc' => $desc,
'desc_en' => $descEn,
'sort' => $sort,
'status' => $status,
];
}
usort($out, static function (array $a, array $b): int {
if ($a['sort'] !== $b['sort']) {
return $a['sort'] <=> $b['sort'];
}
$ida = is_string($a['id']) ? $a['id'] : '';
$idb = is_string($b['id']) ? $b['id'] : '';
return strcmp($ida, $idb);
});
return $out;
}
/**
* 校验 POST 数据并输出用于入库的清洁数据
*
* @param list<array<string, mixed>> $items
*
* @throws InvalidArgumentException
*/
public static function prepareItemsForSave(array $items): array
{
$seenId = [];
$out = [];
foreach ($items as $idx => $row) {
$no = $idx + 1;
if (!is_array($row)) {
throw new InvalidArgumentException('第 ' . $no . ' 行格式错误');
}
$id = isset($row['id']) && is_string($row['id']) ? trim($row['id']) : '';
if ($id === '') {
$id = self::generateId();
}
if (!preg_match('/^[a-zA-Z0-9_\-]{1,32}$/', $id)) {
throw new InvalidArgumentException('第 ' . $no . ' 行 ID 非法');
}
if (isset($seenId[$id])) {
throw new InvalidArgumentException('档位 ID 重复:' . $id);
}
$seenId[$id] = true;
$title = self::stringField($row, 'title');
if ($title === '') {
// 兼容上游(例如自动迁移脚本)传递历史 name 字段
$title = self::stringField($row, 'name');
}
if ($title === '') {
throw new InvalidArgumentException('第 ' . $no . ' 行中文充值名称不能为空');
}
if (mb_strlen($title) > 64) {
throw new InvalidArgumentException('第 ' . $no . ' 行中文充值名称过长');
}
$titleEn = self::stringField($row, 'title_en');
if (mb_strlen($titleEn) > 64) {
throw new InvalidArgumentException('第 ' . $no . ' 行英文充值名称过长');
}
$amount = self::normalizeAmount($row['amount'] ?? '');
if (bccomp($amount, '0', 4) <= 0) {
throw new InvalidArgumentException('第 ' . $no . ' 行充值金额必须大于 0');
}
$bonus = self::normalizeAmount($row['bonus_amount'] ?? '0');
if (bccomp($bonus, '0', 4) < 0) {
throw new InvalidArgumentException('第 ' . $no . ' 行赠送金额不能为负数');
}
$desc = self::stringField($row, 'desc');
if (mb_strlen($desc) > 255) {
throw new InvalidArgumentException('第 ' . $no . ' 行中文描述过长');
}
$descEn = self::stringField($row, 'desc_en');
if (mb_strlen($descEn) > 255) {
throw new InvalidArgumentException('第 ' . $no . ' 行英文描述过长');
}
$sort = isset($row['sort']) && is_numeric($row['sort']) ? intval($row['sort']) : 0;
$statusRaw = isset($row['status']) && is_numeric($row['status']) ? intval($row['status']) : 1;
$status = $statusRaw === 1 ? 1 : 0;
$out[] = [
'id' => $id,
'title' => $title,
'title_en' => $titleEn,
'amount' => $amount,
'bonus_amount' => $bonus,
'desc' => $desc,
'desc_en' => $descEn,
'sort' => $sort,
'status' => $status,
];
}
usort($out, static function (array $a, array $b): int {
if ($a['sort'] !== $b['sort']) {
return $a['sort'] <=> $b['sort'];
}
$ida = is_string($a['id']) ? $a['id'] : '';
$idb = is_string($b['id']) ? $b['id'] : '';
return strcmp($ida, $idb);
});
return $out;
}
/**
* @param list<array<string, mixed>> $items
*/
public static function encodeForDb(array $items): string
{
$encoded = json_encode($items, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if ($encoded === false) {
throw new InvalidArgumentException('JSON 编码失败');
}
return $encoded;
}
/**
* 过滤出启用档位并按 sort 升序,供移动端选择
*/
public static function publicList(array $items): array
{
$enabled = array_values(array_filter($items, static function (array $row): bool {
if (!isset($row['status'])) {
return false;
}
$val = is_numeric($row['status']) ? intval($row['status']) : 0;
return $val === 1;
}));
usort($enabled, static function (array $a, array $b): int {
$sa = isset($a['sort']) && is_numeric($a['sort']) ? intval($a['sort']) : 0;
$sb = isset($b['sort']) && is_numeric($b['sort']) ? intval($b['sort']) : 0;
if ($sa !== $sb) {
return $sa <=> $sb;
}
$ida = isset($a['id']) && is_string($a['id']) ? $a['id'] : '';
$idb = isset($b['id']) && is_string($b['id']) ? $b['id'] : '';
return strcmp($ida, $idb);
});
return $enabled;
}
/**
* 按 ID 从档位列表中取出指定档位;未找到返回 null
*/
public static function findById(array $items, string $id): ?array
{
foreach ($items as $row) {
if (!is_array($row)) {
continue;
}
$rid = $row['id'] ?? '';
if (is_string($rid) && $rid === $id) {
return $row;
}
}
return null;
}
/**
* 根据语言选择档位对外展示的 title/desc。
*
* @param array<string, mixed> $item
* @return array{title: string, desc: string}
*/
public static function localize(array $item, string $lang): array
{
$title = self::stringField($item, 'title');
$titleEn = self::stringField($item, 'title_en');
$desc = self::stringField($item, 'desc');
$descEn = self::stringField($item, 'desc_en');
$isEn = self::isEnglishLang($lang);
$pickedTitle = $isEn ? ($titleEn !== '' ? $titleEn : $title) : ($title !== '' ? $title : $titleEn);
$pickedDesc = $isEn ? ($descEn !== '' ? $descEn : $desc) : ($desc !== '' ? $desc : $descEn);
return [
'title' => $pickedTitle,
'desc' => $pickedDesc,
];
}
/**
* 生成 10 位稳定 IDt_ + 8 位随机 base32
*/
public static function generateId(): string
{
$chars = 'abcdefghijkmnpqrstuvwxyz23456789';
$len = strlen($chars);
$id = 't_';
for ($i = 0; $i < 8; $i++) {
$id .= $chars[random_int(0, $len - 1)];
}
return $id;
}
/**
* 将金额归一化为 4 位小数字符串;非法输入返回 '0.0000'
*/
public static function normalizeAmount($raw): string
{
if ($raw === null || $raw === '') {
return '0.0000';
}
if (is_string($raw)) {
$s = trim($raw);
} elseif (is_int($raw) || is_float($raw)) {
$s = strval($raw);
} else {
return '0.0000';
}
$s = str_replace(',', '.', $s);
if (!is_numeric($s)) {
return '0.0000';
}
return bcadd($s, '0', 4);
}
/**
* 从数组取字符串字段并 trim非字符串返回空串
*
* @param array<string, mixed> $row
*/
private static function stringField(array $row, string $key): string
{
if (!isset($row[$key])) {
return '';
}
$v = $row[$key];
return is_string($v) ? trim($v) : '';
}
private static function isEnglishLang(string $lang): bool
{
$normalized = strtolower(str_replace('_', '-', trim($lang)));
if ($normalized === '') {
return false;
}
return $normalized === 'en' || str_starts_with($normalized, 'en-');
}
}