Files
webman-buildadmin/app/common/library/game/ZiHuaDictionary.php

227 lines
8.3 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;
/**
* 36 字花字典game_config.zi_hua_36_dictionary仅存 JSON 数组
* category 使用英文zodiac|beast|fowl|vermin|divine
*/
final class ZiHuaDictionary
{
public const CONFIG_KEY = 'zi_hua_36_dictionary';
/** 与表单下拉一致,顺序即展示顺序 */
public const CATEGORIES = ['zodiac', 'beast', 'fowl', 'vermin', 'divine'];
private const LEGACY_CATEGORY_MAP = [
'生肖' => 'zodiac',
'猛兽' => 'beast',
'飞禽' => 'fowl',
'虫蛇' => 'vermin',
'神兽' => 'divine',
'zodiac' => 'zodiac',
'beast' => 'beast',
'fowl' => 'fowl',
'vermin' => 'vermin',
'divine' => 'divine',
];
/**
* 默认 36 条(与 PRD 一致category 为英文)
*
* @return list<array{no: int, name: string, category: string}>
*/
public static function defaultItems(): array
{
return [
['no' => 1, 'name' => '鼠', 'category' => 'zodiac'],
['no' => 2, 'name' => '牛', 'category' => 'zodiac'],
['no' => 3, 'name' => '虎', 'category' => 'zodiac'],
['no' => 4, 'name' => '兔', 'category' => 'zodiac'],
['no' => 5, 'name' => '龙', 'category' => 'zodiac'],
['no' => 6, 'name' => '蛇', 'category' => 'zodiac'],
['no' => 7, 'name' => '马', 'category' => 'zodiac'],
['no' => 8, 'name' => '羊', 'category' => 'zodiac'],
['no' => 9, 'name' => '猴', 'category' => 'zodiac'],
['no' => 10, 'name' => '鸡', 'category' => 'zodiac'],
['no' => 11, 'name' => '狗', 'category' => 'zodiac'],
['no' => 12, 'name' => '猪', 'category' => 'zodiac'],
['no' => 13, 'name' => '狮', 'category' => 'beast'],
['no' => 14, 'name' => '象', 'category' => 'beast'],
['no' => 15, 'name' => '鹿', 'category' => 'beast'],
['no' => 16, 'name' => '熊', 'category' => 'beast'],
['no' => 17, 'name' => '狼', 'category' => 'beast'],
['no' => 18, 'name' => '豹', 'category' => 'beast'],
['no' => 19, 'name' => '鹰', 'category' => 'fowl'],
['no' => 20, 'name' => '鹤', 'category' => 'fowl'],
['no' => 21, 'name' => '孔雀', 'category' => 'fowl'],
['no' => 22, 'name' => '鸳鸯', 'category' => 'fowl'],
['no' => 23, 'name' => '鸵鸟', 'category' => 'fowl'],
['no' => 24, 'name' => '天鹅', 'category' => 'fowl'],
['no' => 25, 'name' => '蟾蜍', 'category' => 'vermin'],
['no' => 26, 'name' => '蜘蛛', 'category' => 'vermin'],
['no' => 27, 'name' => '蝙蝠', 'category' => 'vermin'],
['no' => 28, 'name' => '蜈蚣', 'category' => 'vermin'],
['no' => 29, 'name' => '蝎', 'category' => 'vermin'],
['no' => 30, 'name' => '蛇蜥', 'category' => 'vermin'],
['no' => 31, 'name' => '麒麟', 'category' => 'divine'],
['no' => 32, 'name' => '凤凰', 'category' => 'divine'],
['no' => 33, 'name' => '青龙', 'category' => 'divine'],
['no' => 34, 'name' => '白虎', 'category' => 'divine'],
['no' => 35, 'name' => '朱雀', 'category' => 'divine'],
['no' => 36, 'name' => '玄武', 'category' => 'divine'],
];
}
/**
* @return list<array{no: int, name: string, category: string}>
*/
public static function parseFromConfigValue(?string $configValue): array
{
if ($configValue === null || trim($configValue) === '') {
return self::defaultItems();
}
$decoded = json_decode($configValue, true);
if (!is_array($decoded)) {
return self::defaultItems();
}
if (isset($decoded['items']) && is_array($decoded['items'])) {
return self::normalizeItemsList($decoded['items']);
}
if ($decoded !== [] && array_is_list($decoded)) {
return self::normalizeItemsList($decoded);
}
return self::defaultItems();
}
/**
* @param list<mixed> $items
* @return list<array{no: int, name: string, category: string}>
*/
public static function normalizeItemsList(array $items): array
{
$defaults = self::defaultItems();
$defaultByNo = [];
foreach ($defaults as $row) {
$defaultByNo[$row['no']] = $row;
}
$out = [];
foreach ($items as $row) {
if (!is_array($row)) {
continue;
}
$no = $row['no'] ?? null;
if (!is_numeric($no)) {
continue;
}
$n = intval($no, 10);
if ($n < 1 || $n > 36) {
continue;
}
$name = isset($row['name']) && is_string($row['name']) ? trim($row['name']) : '';
if ($name === '') {
$name = $defaultByNo[$n]['name'] ?? '';
}
$catRaw = $row['category'] ?? 'zodiac';
$catStr = is_string($catRaw) ? trim($catRaw) : '';
$category = self::LEGACY_CATEGORY_MAP[$catStr] ?? (in_array($catStr, self::CATEGORIES, true) ? $catStr : 'zodiac');
$out[] = ['no' => $n, 'name' => $name, 'category' => $category];
}
if (count($out) !== 36) {
return self::defaultItems();
}
usort($out, static fn (array $a, array $b): int => $a['no'] <=> $b['no']);
$seen = [];
foreach ($out as $row) {
if (isset($seen[$row['no']])) {
return self::defaultItems();
}
$seen[$row['no']] = true;
}
for ($i = 1; $i <= 36; $i++) {
if (!isset($seen[$i])) {
return self::defaultItems();
}
}
return $out;
}
/**
* 校验 POST 数据并输出入库用的 36 条no 升序)
*
* @param list<array{no?: mixed, name?: mixed, category?: mixed}> $items
*
* @return list<array{no: int, name: string, category: string}>
*
* @throws InvalidArgumentException
*/
public static function prepareItemsForSave(array $items): array
{
if (count($items) !== 36) {
throw new InvalidArgumentException('必须恰好 36 条');
}
$nos = [];
$out = [];
foreach ($items as $idx => $row) {
if (!is_array($row)) {
throw new InvalidArgumentException('第 ' . ($idx + 1) . ' 行格式错误');
}
$no = $row['no'] ?? null;
if (!is_numeric($no)) {
throw new InvalidArgumentException('编号必须为数字');
}
$n = intval($no, 10);
if ($n < 1 || $n > 36) {
throw new InvalidArgumentException('编号必须在 136');
}
if (isset($nos[$n])) {
throw new InvalidArgumentException('编号重复:' . $n);
}
$nos[$n] = true;
$name = $row['name'] ?? '';
if (!is_string($name) || trim($name) === '') {
throw new InvalidArgumentException('编号 ' . $n . ' 名称不能为空');
}
$cat = $row['category'] ?? '';
$catTrim = is_string($cat) ? trim($cat) : '';
if (!in_array($catTrim, self::CATEGORIES, true)) {
throw new InvalidArgumentException('编号 ' . $n . ' 类型无效');
}
$out[] = ['no' => $n, 'name' => trim($name), 'category' => $catTrim];
}
for ($i = 1; $i <= 36; $i++) {
if (!isset($nos[$i])) {
throw new InvalidArgumentException('缺少编号:' . $i);
}
}
usort($out, static fn (array $a, array $b): int => $a['no'] <=> $b['no']);
return $out;
}
/**
* @param list<array{no: int, name: string, category: string}> $items
*/
public static function encodeForDb(array $items): string
{
usort($items, static fn (array $a, array $b): int => $a['no'] <=> $b['no']);
$encoded = json_encode($items, JSON_UNESCAPED_UNICODE);
if ($encoded === false) {
throw new InvalidArgumentException('JSON 编码失败');
}
return $encoded;
}
}