diff --git a/sql/qqbot-init.sql b/sql/qqbot-init.sql index c8763c3..0e1222a 100644 --- a/sql/qqbot-init.sql +++ b/sql/qqbot-init.sql @@ -598,39 +598,39 @@ ON DUPLICATE KEY UPDATE INSERT INTO `admin_dict` (`id`, `dict_code`, `label`, `value`, `children_code`, `sort`, `status`) VALUES - (2041700000000301101, 'FF14_MARKET_REGION', '中国', '中国', NULL, 1, 1), - (2041700000000301201, 'FF14_MARKET_DATA_CENTER', '陆行鸟', '陆行鸟', '中国', 1, 1), - (2041700000000301202, 'FF14_MARKET_DATA_CENTER', '莫古力', '莫古力', '中国', 2, 1), - (2041700000000301203, 'FF14_MARKET_DATA_CENTER', '猫小胖', '猫小胖', '中国', 3, 1), - (2041700000000301204, 'FF14_MARKET_DATA_CENTER', '豆豆柴', '豆豆柴', '中国', 4, 1), - (2041700000000301301, 'FF14_MARKET_WORLD', '红玉海', '红玉海', '陆行鸟', 1, 1), - (2041700000000301302, 'FF14_MARKET_WORLD', '神意之地', '神意之地', '陆行鸟', 2, 1), - (2041700000000301303, 'FF14_MARKET_WORLD', '拉诺西亚', '拉诺西亚', '陆行鸟', 3, 1), - (2041700000000301304, 'FF14_MARKET_WORLD', '幻影群岛', '幻影群岛', '陆行鸟', 4, 1), - (2041700000000301305, 'FF14_MARKET_WORLD', '萌芽池', '萌芽池', '陆行鸟', 5, 1), - (2041700000000301306, 'FF14_MARKET_WORLD', '宇宙和音', '宇宙和音', '陆行鸟', 6, 1), - (2041700000000301307, 'FF14_MARKET_WORLD', '沃仙曦染', '沃仙曦染', '陆行鸟', 7, 1), - (2041700000000301308, 'FF14_MARKET_WORLD', '晨曦王座', '晨曦王座', '陆行鸟', 8, 1), - (2041700000000301309, 'FF14_MARKET_WORLD', '白银乡', '白银乡', '莫古力', 9, 1), - (2041700000000301310, 'FF14_MARKET_WORLD', '白金幻象', '白金幻象', '莫古力', 10, 1), - (2041700000000301311, 'FF14_MARKET_WORLD', '神拳痕', '神拳痕', '莫古力', 11, 1), - (2041700000000301312, 'FF14_MARKET_WORLD', '潮风亭', '潮风亭', '莫古力', 12, 1), - (2041700000000301313, 'FF14_MARKET_WORLD', '旅人栈桥', '旅人栈桥', '莫古力', 13, 1), - (2041700000000301314, 'FF14_MARKET_WORLD', '拂晓之间', '拂晓之间', '莫古力', 14, 1), - (2041700000000301315, 'FF14_MARKET_WORLD', '龙巢神殿', '龙巢神殿', '莫古力', 15, 1), - (2041700000000301316, 'FF14_MARKET_WORLD', '梦羽宝境', '梦羽宝境', '莫古力', 16, 1), - (2041700000000301317, 'FF14_MARKET_WORLD', '紫水栈桥', '紫水栈桥', '猫小胖', 17, 1), - (2041700000000301318, 'FF14_MARKET_WORLD', '延夏', '延夏', '猫小胖', 18, 1), - (2041700000000301319, 'FF14_MARKET_WORLD', '静语庄园', '静语庄园', '猫小胖', 19, 1), - (2041700000000301320, 'FF14_MARKET_WORLD', '摩杜纳', '摩杜纳', '猫小胖', 20, 1), - (2041700000000301321, 'FF14_MARKET_WORLD', '海猫茶屋', '海猫茶屋', '猫小胖', 21, 1), - (2041700000000301322, 'FF14_MARKET_WORLD', '柔风海湾', '柔风海湾', '猫小胖', 22, 1), - (2041700000000301323, 'FF14_MARKET_WORLD', '琥珀原', '琥珀原', '猫小胖', 23, 1), - (2041700000000301324, 'FF14_MARKET_WORLD', '水晶塔', '水晶塔', '豆豆柴', 24, 1), - (2041700000000301325, 'FF14_MARKET_WORLD', '银泪湖', '银泪湖', '豆豆柴', 25, 1), - (2041700000000301326, 'FF14_MARKET_WORLD', '太阳海岸', '太阳海岸', '豆豆柴', 26, 1), - (2041700000000301327, 'FF14_MARKET_WORLD', '伊修加德', '伊修加德', '豆豆柴', 27, 1), - (2041700000000301328, 'FF14_MARKET_WORLD', '红茶川', '红茶川', '豆豆柴', 28, 1) + (2041700000000301101, 'FF14_MARKET_REGION', '中国', '中国', 'FF14_MARKET_DATA_CENTER_CN', 1, 1), + (2041700000000301201, 'FF14_MARKET_DATA_CENTER_CN', '陆行鸟', '陆行鸟', 'FF14_MARKET_WORLD_CN_LUXINGNIAO', 1, 1), + (2041700000000301202, 'FF14_MARKET_DATA_CENTER_CN', '莫古力', '莫古力', 'FF14_MARKET_WORLD_CN_MOGULI', 2, 1), + (2041700000000301203, 'FF14_MARKET_DATA_CENTER_CN', '猫小胖', '猫小胖', 'FF14_MARKET_WORLD_CN_MAOXIAOPANG', 3, 1), + (2041700000000301204, 'FF14_MARKET_DATA_CENTER_CN', '豆豆柴', '豆豆柴', 'FF14_MARKET_WORLD_CN_DOUDOUCHAI', 4, 1), + (2041700000000301301, 'FF14_MARKET_WORLD_CN_LUXINGNIAO', '红玉海', '红玉海', NULL, 1, 1), + (2041700000000301302, 'FF14_MARKET_WORLD_CN_LUXINGNIAO', '神意之地', '神意之地', NULL, 2, 1), + (2041700000000301303, 'FF14_MARKET_WORLD_CN_LUXINGNIAO', '拉诺西亚', '拉诺西亚', NULL, 3, 1), + (2041700000000301304, 'FF14_MARKET_WORLD_CN_LUXINGNIAO', '幻影群岛', '幻影群岛', NULL, 4, 1), + (2041700000000301305, 'FF14_MARKET_WORLD_CN_LUXINGNIAO', '萌芽池', '萌芽池', NULL, 5, 1), + (2041700000000301306, 'FF14_MARKET_WORLD_CN_LUXINGNIAO', '宇宙和音', '宇宙和音', NULL, 6, 1), + (2041700000000301307, 'FF14_MARKET_WORLD_CN_LUXINGNIAO', '沃仙曦染', '沃仙曦染', NULL, 7, 1), + (2041700000000301308, 'FF14_MARKET_WORLD_CN_LUXINGNIAO', '晨曦王座', '晨曦王座', NULL, 8, 1), + (2041700000000301309, 'FF14_MARKET_WORLD_CN_MOGULI', '白银乡', '白银乡', NULL, 1, 1), + (2041700000000301310, 'FF14_MARKET_WORLD_CN_MOGULI', '白金幻象', '白金幻象', NULL, 2, 1), + (2041700000000301311, 'FF14_MARKET_WORLD_CN_MOGULI', '神拳痕', '神拳痕', NULL, 3, 1), + (2041700000000301312, 'FF14_MARKET_WORLD_CN_MOGULI', '潮风亭', '潮风亭', NULL, 4, 1), + (2041700000000301313, 'FF14_MARKET_WORLD_CN_MOGULI', '旅人栈桥', '旅人栈桥', NULL, 5, 1), + (2041700000000301314, 'FF14_MARKET_WORLD_CN_MOGULI', '拂晓之间', '拂晓之间', NULL, 6, 1), + (2041700000000301315, 'FF14_MARKET_WORLD_CN_MOGULI', '龙巢神殿', '龙巢神殿', NULL, 7, 1), + (2041700000000301316, 'FF14_MARKET_WORLD_CN_MOGULI', '梦羽宝境', '梦羽宝境', NULL, 8, 1), + (2041700000000301317, 'FF14_MARKET_WORLD_CN_MAOXIAOPANG', '紫水栈桥', '紫水栈桥', NULL, 1, 1), + (2041700000000301318, 'FF14_MARKET_WORLD_CN_MAOXIAOPANG', '延夏', '延夏', NULL, 2, 1), + (2041700000000301319, 'FF14_MARKET_WORLD_CN_MAOXIAOPANG', '静语庄园', '静语庄园', NULL, 3, 1), + (2041700000000301320, 'FF14_MARKET_WORLD_CN_MAOXIAOPANG', '摩杜纳', '摩杜纳', NULL, 4, 1), + (2041700000000301321, 'FF14_MARKET_WORLD_CN_MAOXIAOPANG', '海猫茶屋', '海猫茶屋', NULL, 5, 1), + (2041700000000301322, 'FF14_MARKET_WORLD_CN_MAOXIAOPANG', '柔风海湾', '柔风海湾', NULL, 6, 1), + (2041700000000301323, 'FF14_MARKET_WORLD_CN_MAOXIAOPANG', '琥珀原', '琥珀原', NULL, 7, 1), + (2041700000000301324, 'FF14_MARKET_WORLD_CN_DOUDOUCHAI', '水晶塔', '水晶塔', NULL, 1, 1), + (2041700000000301325, 'FF14_MARKET_WORLD_CN_DOUDOUCHAI', '银泪湖', '银泪湖', NULL, 2, 1), + (2041700000000301326, 'FF14_MARKET_WORLD_CN_DOUDOUCHAI', '太阳海岸', '太阳海岸', NULL, 3, 1), + (2041700000000301327, 'FF14_MARKET_WORLD_CN_DOUDOUCHAI', '伊修加德', '伊修加德', NULL, 4, 1), + (2041700000000301328, 'FF14_MARKET_WORLD_CN_DOUDOUCHAI', '红茶川', '红茶川', NULL, 5, 1) ON DUPLICATE KEY UPDATE `label` = VALUES(`label`), `children_code` = VALUES(`children_code`), diff --git a/src/admin/dict/dict.service.ts b/src/admin/dict/dict.service.ts index c374fc7..2b5a6a8 100644 --- a/src/admin/dict/dict.service.ts +++ b/src/admin/dict/dict.service.ts @@ -29,7 +29,7 @@ type AdminDictSerialized = { value: string; }; -type AdminDictTreeItem = AdminDictSerialized & { +export type AdminDictTreeItem = AdminDictSerialized & { children?: AdminDictTreeItem[]; treeKey: string; }; diff --git a/src/qqbot/command/qqbot-command-parser.service.ts b/src/qqbot/command/qqbot-command-parser.service.ts index 154a972..90aac96 100644 --- a/src/qqbot/command/qqbot-command-parser.service.ts +++ b/src/qqbot/command/qqbot-command-parser.service.ts @@ -4,6 +4,7 @@ import type { QqbotCommand } from './qqbot-command.entity'; import type { QqbotNormalizedMessage } from '../qqbot.types'; import { buildQqbotFf14MarketCatalog, + buildQqbotFf14MarketCatalogFromTree, QQBOT_FF14_MARKET_DICT_CODES, type QqbotFf14MarketCatalog, isQqbotFf14DataCenterName, @@ -276,6 +277,13 @@ export class QqbotCommandParserService { } private async getFf14MarketCatalog() { + const treeCatalog = buildQqbotFf14MarketCatalogFromTree( + await this.dictService.tree({ + dictCode: QQBOT_FF14_MARKET_DICT_CODES.region, + }), + ); + if (treeCatalog.dataCenters.length > 0) return treeCatalog; + const [regions, dataCenters, worlds] = await Promise.all([ this.dictService.getDictItemsByKey(QQBOT_FF14_MARKET_DICT_CODES.region), this.dictService.getDictItemsByKey( diff --git a/src/qqbot/plugins/ff14Market/qqbot-ff14-client.service.ts b/src/qqbot/plugins/ff14Market/qqbot-ff14-client.service.ts index 858b7ea..5fb04fe 100644 --- a/src/qqbot/plugins/ff14Market/qqbot-ff14-client.service.ts +++ b/src/qqbot/plugins/ff14Market/qqbot-ff14-client.service.ts @@ -5,6 +5,7 @@ import * as https from 'node:https'; import { DictService } from '../../../admin/dict/dict.service'; import { buildQqbotFf14MarketCatalog, + buildQqbotFf14MarketCatalogFromTree, QQBOT_FF14_MARKET_DICT_CODES, resolveQqbotFf14MarketTarget, } from './qqbot-ff14-worlds'; @@ -290,6 +291,13 @@ export class QqbotFf14ClientService { } private async getFf14MarketCatalog() { + const treeCatalog = buildQqbotFf14MarketCatalogFromTree( + await this.dictService.tree({ + dictCode: QQBOT_FF14_MARKET_DICT_CODES.region, + }), + ); + if (treeCatalog.dataCenters.length > 0) return treeCatalog; + const [regions, dataCenters, worlds] = await Promise.all([ this.dictService.getDictItemsByKey(QQBOT_FF14_MARKET_DICT_CODES.region), this.dictService.getDictItemsByKey( diff --git a/src/qqbot/plugins/ff14Market/qqbot-ff14-worlds.spec.ts b/src/qqbot/plugins/ff14Market/qqbot-ff14-worlds.spec.ts index 2db4364..82606d4 100644 --- a/src/qqbot/plugins/ff14Market/qqbot-ff14-worlds.spec.ts +++ b/src/qqbot/plugins/ff14Market/qqbot-ff14-worlds.spec.ts @@ -1,5 +1,6 @@ import { buildQqbotFf14MarketCatalog, + buildQqbotFf14MarketCatalogFromTree, resolveQqbotFf14MarketTarget, } from './qqbot-ff14-worlds'; @@ -34,4 +35,52 @@ describe('qqbot ff14 market worlds', () => { }), ).toThrow('服务器 琥珀原 不属于大区 陆行鸟'); }); + + it('builds catalog from dict tree relations', () => { + const treeCatalog = buildQqbotFf14MarketCatalogFromTree([ + { + children: [ + { + children: [ + { + dictCode: 'FF14_MARKET_WORLD_CN_MAOXIAOPANG', + id: 'world-1', + label: '琥珀原', + sort: 1, + status: 1, + treeKey: 'region-1/dc-1/world-1', + value: '琥珀原', + }, + ], + childrenCode: 'FF14_MARKET_WORLD_CN_MAOXIAOPANG', + dictCode: 'FF14_MARKET_DATA_CENTER_CN', + id: 'dc-1', + label: '猫小胖', + sort: 1, + status: 1, + treeKey: 'region-1/dc-1', + value: '猫小胖', + }, + ], + childrenCode: 'FF14_MARKET_DATA_CENTER_CN', + dictCode: 'FF14_MARKET_REGION', + id: 'region-1', + label: '中国', + sort: 1, + status: 1, + treeKey: 'region-1', + value: '中国', + }, + ]); + + expect( + resolveQqbotFf14MarketTarget(treeCatalog, { world: '琥珀原' }), + ).toEqual({ + dataCenter: '猫小胖', + label: '中国 / 猫小胖 / 琥珀原', + region: '中国', + target: '琥珀原', + world: '琥珀原', + }); + }); }); diff --git a/src/qqbot/plugins/ff14Market/qqbot-ff14-worlds.ts b/src/qqbot/plugins/ff14Market/qqbot-ff14-worlds.ts index a0d3406..5b7f9fa 100644 --- a/src/qqbot/plugins/ff14Market/qqbot-ff14-worlds.ts +++ b/src/qqbot/plugins/ff14Market/qqbot-ff14-worlds.ts @@ -1,4 +1,7 @@ -import type { AdminDictItem } from '../../../admin/dict/dict.service'; +import type { + AdminDictItem, + AdminDictTreeItem, +} from '../../../admin/dict/dict.service'; export type QqbotFf14DataCenter = { name: string; @@ -56,6 +59,40 @@ export function buildQqbotFf14MarketCatalog(input: { }; } +export function buildQqbotFf14MarketCatalogFromTree( + roots: AdminDictTreeItem[], +): QqbotFf14MarketCatalog { + const regionNodes = roots.filter( + (item) => item.dictCode === QQBOT_FF14_MARKET_DICT_CODES.region, + ); + const regions = regionNodes.map(getDictDisplayValue).filter(Boolean); + const defaultRegion = regions[0]; + const dataCenters = regionNodes.flatMap((regionNode) => { + const region = getDictDisplayValue(regionNode) || defaultRegion || ''; + + return (regionNode.children || []) + .map((dataCenterNode) => { + const name = getDictDisplayValue(dataCenterNode); + if (!name) return null; + + return { + name, + region, + worlds: (dataCenterNode.children || []) + .map(getDictDisplayValue) + .filter(Boolean), + }; + }) + .filter((item): item is QqbotFf14DataCenter => !!item); + }); + + return { + dataCenters, + defaultRegion, + regions, + }; +} + export function isQqbotFf14DataCenterName( catalog: QqbotFf14MarketCatalog, value?: string,