refactor: 收口公开码搜索计划

This commit is contained in:
2026-06-04 02:32:08 +08:00
parent 83ae363670
commit 4e8cac3856
7 changed files with 257 additions and 117 deletions

View File

@@ -1355,6 +1355,14 @@
- 验证方式:`npm run test -- src/components/platform-entry/platformCreationUrlStateModel.test.ts src/components/platform-entry/platformPuzzleIdentityModel.test.ts``npm run test -- src/services/creationUrlState.test.ts``npm run test -- src/components/platform-entry/platformDraftGenerationShelfModel.test.ts`、针对新 Module 执行 ESLint、`npm run typecheck``npm run check:encoding` - 验证方式:`npm run test -- src/components/platform-entry/platformCreationUrlStateModel.test.ts src/components/platform-entry/platformPuzzleIdentityModel.test.ts``npm run test -- src/services/creationUrlState.test.ts``npm run test -- src/components/platform-entry/platformDraftGenerationShelfModel.test.ts`、针对新 Module 执行 ESLint、`npm run typecheck``npm run check:encoding`
- 关联文档:`docs/technical/【前端架构】CreationUrlStateModel收口计划-2026-06-03.md` - 关联文档:`docs/technical/【前端架构】CreationUrlStateModel收口计划-2026-06-03.md`
## 2026-06-04 Platform Public Code Search Model 收口
- 背景:`PlatformEntryFlowShellImpl.tsx` 的公开搜索回调内联判断内部用户 ID、陶泥号、RPG 作品号、各玩法公开作品号前缀和 fallback 顺序,壳层同时承担纯搜索计划与网络 / 打开副作用。
- 决策:新增 `src/components/platform-entry/platformPublicCodeSearchModel.ts`,以 `resolvePlatformPublicCodeSearchPlan(keyword)` 返回 `normalizedKeyword``steps``user_` / `user-` 只查用户 ID玩法前缀直达对应作品`CW` / 纯数字先查 RPG 作品再查陶泥号;普通关键词和 `SY` 保持既有用户号、RPG 作品、汪汪声浪、用户号兜底顺序。壳层只按 step 执行既有查找、详情打开、Bark Battle runtime 特例和 missing work 归航。
- 影响范围:发现页 / 推荐页公开搜索、作品详情深链初始搜索、陶泥号命中面板、各玩法公开作品号直达。
- 验证方式:`npm run test -- src/components/platform-entry/platformPublicCodeSearchModel.test.ts`、针对新 Module 和 `PlatformEntryFlowShellImpl.tsx` 执行 ESLint、`npm run typecheck``npm run check:encoding`
- 关联文档:`docs/technical/【前端架构】PlatformPublicCodeSearchModel收口计划-2026-06-04.md``docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`
## 2026-06-03 Public Work Presentation 收口 ## 2026-06-03 Public Work Presentation 收口
- 背景:作品卡、推荐 runtime meta、排行项、分类项、搜索结果和桌面 hero 共用玩法类型 label 与紧凑计数格式,但规则仍在 `RpgEntryHomeView.tsx` 页面 Implementation 内。 - 背景:作品卡、推荐 runtime meta、排行项、分类项、搜索结果和桌面 hero 共用玩法类型 label 与紧凑计数格式,但规则仍在 `RpgEntryHomeView.tsx` 页面 Implementation 内。

View File

@@ -49,6 +49,8 @@ AI 文字游戏模板接入以 [AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_
创作入口点击的占位、隐藏模板拦截、未知入口 no-op 与工作台启动目标收口到 `src/components/platform-entry/platformCreationLaunchModel.ts`,壳层只执行启动前准备、错误提示和受保护动作,规则见 [【前端架构】PlatformCreationLaunchModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformCreationLaunchModel收口计划-2026-06-04.md)。 创作入口点击的占位、隐藏模板拦截、未知入口 no-op 与工作台启动目标收口到 `src/components/platform-entry/platformCreationLaunchModel.ts`,壳层只执行启动前准备、错误提示和受保护动作,规则见 [【前端架构】PlatformCreationLaunchModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformCreationLaunchModel收口计划-2026-06-04.md)。
平台入口公开码搜索的用户 ID、陶泥号、RPG 作品号、各玩法作品号前缀和失败回退顺序收口到 `src/components/platform-entry/platformPublicCodeSearchModel.ts`,壳层只按计划执行网络读取、详情打开和错误归航副作用,规则见 [【前端架构】PlatformPublicCodeSearchModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformPublicCodeSearchModel收口计划-2026-06-04.md)。
平台入口创作生成通知、pending 作品架占位、失败覆盖、拼图稳定 ID 和草稿 Tab 未读点收口到 `src/components/platform-entry/platformDraftGenerationShelfModel.ts`,规则见 [【前端架构】DraftGenerationShelfModel收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91DraftGenerationShelfModel%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。 平台入口创作生成通知、pending 作品架占位、失败覆盖、拼图稳定 ID 和草稿 Tab 未读点收口到 `src/components/platform-entry/platformDraftGenerationShelfModel.ts`,规则见 [【前端架构】DraftGenerationShelfModel收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91DraftGenerationShelfModel%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
平台入口创作恢复 URL 私有 query、初始恢复判定、创作直达恢复目标解析、恢复目标身份匹配、跳一跳 / 敲木鱼恢复阶段落点、拼图 runtime query 与拼图稳定身份互推收口到 `src/components/platform-entry/platformCreationUrlStateModel.ts``src/components/platform-entry/platformPuzzleIdentityModel.ts`,规则见 [【前端架构】CreationUrlStateModel收口计划-2026-06-03.md](./technical/【前端架构】CreationUrlStateModel收口计划-2026-06-03.md)。 平台入口创作恢复 URL 私有 query、初始恢复判定、创作直达恢复目标解析、恢复目标身份匹配、跳一跳 / 敲木鱼恢复阶段落点、拼图 runtime query 与拼图稳定身份互推收口到 `src/components/platform-entry/platformCreationUrlStateModel.ts``src/components/platform-entry/platformPuzzleIdentityModel.ts`,规则见 [【前端架构】CreationUrlStateModel收口计划-2026-06-03.md](./technical/【前端架构】CreationUrlStateModel收口计划-2026-06-03.md)。

View File

@@ -0,0 +1,37 @@
# 【前端架构】Platform Public Code Search Model 收口计划
## 背景
`PlatformEntryFlowShellImpl.tsx` 的公开搜索回调曾直接在壳层内判断 `user_` / `user-``PZ``BF``JH``WF``BO``M3``SH``VN``BB``CW`、纯数字和普通关键词的优先级。壳层因此既要持有搜索输入到查找顺序的纯规则,又要执行各玩法公开详情读取、用户读取、运行态启动和错误归航副作用。
公开搜索的“先查什么、失败后回退什么”是稳定的分流规则,应有独立测试面;壳层只应作为副作用 Adapter按计划执行网络读取与打开动作。
## 决策
新增 `src/components/platform-entry/platformPublicCodeSearchModel.ts` 作为 Platform Public Code Search **Module**。其公开 **Interface** 为:
- `resolvePlatformPublicCodeSearchPlan(keyword)`:输入用户搜索词,输出 `{ normalizedKeyword, steps }`;空输入返回 `null`
- `PlatformPublicCodeSearchStep`:枚举壳层可执行的查找步骤,包括 `user-id``public-user-code``rpg-work`、各玩法公开作品步骤与 `bark-battle-work`
`PlatformEntryFlowShellImpl.tsx` 仍作为 **Adapter**:它保留 `getPublicAuthUserByCode`、各玩法 gallery 刷新 / 详情打开、Bark Battle runtime 特例和 missing work 归航副作用,只按 `steps` 顺序执行,前一步失败才尝试下一步。
## Interface 约束
- 空白搜索词返回 `null`,壳层不得进入搜索 loading。
- `user_` / `user-` 开头的内部用户 ID 只执行 `user-id`,不回退作品号。
- `PZ``BF``JH``WF``BO``M3``SH``VN``BB` 前缀只进入对应玩法公开作品查找;`M3D-*` 继续归入 `M3` / 抓大鹅。
- `CW``1-8` 位纯数字先查 RPG 公开作品,再回退陶泥号。
- 普通关键词与 `SY` 陶泥号保持既有顺序:先查陶泥号,再查 RPG 公开作品,再查汪汪声浪作品,最后再以陶泥号兜底。
## Depth / Leverage / Locality
- **Depth**:壳层只消费短小的 `steps` Interface搜索前缀、优先级和回退顺序藏入 Module Implementation。
- **Leverage**:新增公开作品前缀时,先扩展 Module 的 step union、前缀表和单测再在壳层 Adapter 绑定对应执行函数。
- **Locality**:搜索计划规则集中在一个纯 ModuleUI、网络、详情打开与 runtime 启动副作用继续留在壳层,避免把副作用 setter 变成浅 Interface。
## 验收
- `npm run test -- src/components/platform-entry/platformPublicCodeSearchModel.test.ts`
- `npx eslint src/components/platform-entry/platformPublicCodeSearchModel.ts src/components/platform-entry/platformPublicCodeSearchModel.test.ts src/components/platform-entry/PlatformEntryFlowShellImpl.tsx --quiet`
- `npm run typecheck`
- `npm run check:encoding`

View File

@@ -1,6 +1,6 @@
# 平台入口与玩法链路 # 平台入口与玩法链路
更新时间:`2026-06-03` 更新时间:`2026-06-04`
## 平台创作入口 ## 平台创作入口
@@ -57,6 +57,8 @@
发现页 / 推荐页公开作品卡的作者行只显示可读公开昵称;不得把手机号掩码、`SY-*` 陶泥号或作品号拼接进卡片作者名。陶泥号搜索、作品号复制和完整作品身份只在搜索、详情页或明确的复制入口展示,避免卡片列表暴露账号标识。 发现页 / 推荐页公开作品卡的作者行只显示可读公开昵称;不得把手机号掩码、`SY-*` 陶泥号或作品号拼接进卡片作者名。陶泥号搜索、作品号复制和完整作品身份只在搜索、详情页或明确的复制入口展示,避免卡片列表暴露账号标识。
平台公开搜索的分流顺序统一由 `platformPublicCodeSearchModel.ts` 判定:`user_` / `user-` 内部用户 ID 只查用户 ID`PZ``BF``JH``WF``BO``M3``SH``VN``BB` 前缀分别直达对应玩法公开作品;`CW` 与 1-8 位纯数字先查 RPG 公开作品再回退陶泥号;普通关键词和 `SY` 陶泥号保持先查陶泥号、再查 RPG 作品、再查汪汪声浪作品、最后陶泥号兜底的既有顺序。平台壳只按计划执行网络读取、详情打开、Bark Battle runtime 特例和缺失作品归航,不在壳层重复维护前缀布尔链。
发现 Tab、创作 Tab 与草稿 Tab 的页面根内容区不再套 `platform-page-stage` 外层全局卡片壳,让列表、筛选和玩法卡获得更宽的横向空间;推荐页和我的页仍按各自页面设计保留原有全局卡片口径。移动端“我的”页仍按顶部头像 / 昵称 / 陶泥号、会员横幅、三张统计卡、每日任务、五项常用功能宫格、设置入口和法律信息组织,不保留旧的底部“填邀请码”次级入口;常用功能当前只展示四项常驻入口时必须按四列铺满整行,不保留五列网格导致左对齐空位;每日任务卡必须读取 `/api/profile/tasks` 的当前任务摘要并在领取后同步刷新卡片进度。字号必须维持平台普通 UI 档位,不能因为窄屏把卡片标题、功能 label 或法律信息撑成展示级字号;最后一屏内容必须能在底部 dock 上方完整滚动露出,不得被固定底部导航遮挡。 发现 Tab、创作 Tab 与草稿 Tab 的页面根内容区不再套 `platform-page-stage` 外层全局卡片壳,让列表、筛选和玩法卡获得更宽的横向空间;推荐页和我的页仍按各自页面设计保留原有全局卡片口径。移动端“我的”页仍按顶部头像 / 昵称 / 陶泥号、会员横幅、三张统计卡、每日任务、五项常用功能宫格、设置入口和法律信息组织,不保留旧的底部“填邀请码”次级入口;常用功能当前只展示四项常驻入口时必须按四列铺满整行,不保留五列网格导致左对齐空位;每日任务卡必须读取 `/api/profile/tasks` 的当前任务摘要并在领取后同步刷新卡片进度。字号必须维持平台普通 UI 档位,不能因为窄屏把卡片标题、功能 label 或法律信息撑成展示级字号;最后一屏内容必须能在底部 dock 上方完整滚动露出,不得被固定底部导航遮挡。
## RPG / 自定义世界 ## RPG / 自定义世界

View File

@@ -512,6 +512,10 @@ import {
resolveMatch3DRuntimeGeneratedBackgroundAsset, resolveMatch3DRuntimeGeneratedBackgroundAsset,
resolveMatch3DRuntimeGeneratedItemAssets, resolveMatch3DRuntimeGeneratedItemAssets,
} from './platformMatch3DRuntimeProfile'; } from './platformMatch3DRuntimeProfile';
import {
type PlatformPublicCodeSearchStep,
resolvePlatformPublicCodeSearchPlan,
} from './platformPublicCodeSearchModel';
import { import {
getPlatformPublicGalleryEntryKey, getPlatformPublicGalleryEntryKey,
getPlatformRecommendRuntimeKind, getPlatformRecommendRuntimeKind,
@@ -13449,53 +13453,17 @@ export function PlatformEntryFlowShellImpl({
const handlePublicCodeSearch = useCallback( const handlePublicCodeSearch = useCallback(
async (keyword: string) => { async (keyword: string) => {
const normalizedKeyword = keyword.trim(); const searchPlan = resolvePlatformPublicCodeSearchPlan(keyword);
if (!normalizedKeyword) { if (!searchPlan) {
return; return;
} }
const { normalizedKeyword } = searchPlan;
setIsSearchingPublicCode(true); setIsSearchingPublicCode(true);
setPublicSearchError(null); setPublicSearchError(null);
setSearchedPublicUser(null); setSearchedPublicUser(null);
const upperKeyword = normalizedKeyword.toUpperCase();
const shouldSearchUserIdFirst = /^user[_-][a-z0-9_-]+$/iu.test(
normalizedKeyword,
);
const shouldSearchBigFishFirst = upperKeyword.startsWith('BF');
const shouldSearchBabyObjectFirst = upperKeyword.startsWith('BO');
const shouldSearchJumpHopFirst = upperKeyword.startsWith('JH');
const shouldSearchWoodenFishFirst = upperKeyword.startsWith('WF');
const shouldSearchMatch3DFirst = upperKeyword.startsWith('M3');
const shouldSearchPuzzleFirst = upperKeyword.startsWith('PZ');
const shouldSearchSquareHoleFirst = upperKeyword.startsWith('SH');
const shouldSearchVisualNovelFirst = upperKeyword.startsWith('VN');
const shouldSearchBarkBattleFirst = upperKeyword.startsWith('BB');
const shouldSearchWorkFirst =
!shouldSearchUserIdFirst &&
!shouldSearchBabyObjectFirst &&
!shouldSearchBigFishFirst &&
!shouldSearchJumpHopFirst &&
!shouldSearchWoodenFishFirst &&
!shouldSearchMatch3DFirst &&
!shouldSearchPuzzleFirst &&
!shouldSearchSquareHoleFirst &&
!shouldSearchVisualNovelFirst &&
!shouldSearchBarkBattleFirst &&
(upperKeyword.startsWith('CW') || /^\d{1,8}$/u.test(normalizedKeyword));
const shouldSearchUserFirst =
shouldSearchUserIdFirst ||
upperKeyword.startsWith('SY') ||
(!shouldSearchWorkFirst &&
!shouldSearchBigFishFirst &&
!shouldSearchBabyObjectFirst &&
!shouldSearchJumpHopFirst &&
!shouldSearchWoodenFishFirst &&
!shouldSearchMatch3DFirst &&
!shouldSearchPuzzleFirst &&
!shouldSearchSquareHoleFirst &&
!shouldSearchVisualNovelFirst);
const tryOpenGalleryEntry = async () => { const tryOpenGalleryEntry = async () => {
const entry = const entry =
await getRpgEntryWorldGalleryDetailByCode(normalizedKeyword); await getRpgEntryWorldGalleryDetailByCode(normalizedKeyword);
@@ -13717,95 +13685,66 @@ export function PlatformEntryFlowShellImpl({
); );
}; };
try { const runSearchStep = async (step: PlatformPublicCodeSearchStep) => {
if (shouldSearchUserIdFirst) { switch (step) {
case 'user-id': {
const user = await getPublicAuthUserById(normalizedKeyword); const user = await getPublicAuthUserById(normalizedKeyword);
setSearchedPublicUser(user); setSearchedPublicUser(user);
return; return;
} }
case 'public-user-code': {
if (shouldSearchPuzzleFirst) { const user = await getPublicAuthUserByCode(normalizedKeyword);
setSearchedPublicUser(user);
return;
}
case 'rpg-work':
await tryOpenGalleryEntry();
return;
case 'puzzle-work':
await tryOpenPuzzleGalleryEntry(); await tryOpenPuzzleGalleryEntry();
return; return;
} case 'big-fish-work':
if (shouldSearchBigFishFirst) {
await tryOpenBigFishGalleryEntry(); await tryOpenBigFishGalleryEntry();
return; return;
} case 'jump-hop-work':
if (shouldSearchJumpHopFirst) {
await tryOpenJumpHopGalleryEntry(); await tryOpenJumpHopGalleryEntry();
return; return;
} case 'wooden-fish-work':
if (shouldSearchWoodenFishFirst) {
await tryOpenWoodenFishGalleryEntry(); await tryOpenWoodenFishGalleryEntry();
return; return;
} case 'baby-object-match-work':
if (shouldSearchBabyObjectFirst) {
await tryOpenBabyObjectMatchGalleryEntry(); await tryOpenBabyObjectMatchGalleryEntry();
return; return;
} case 'match3d-work':
if (shouldSearchMatch3DFirst) {
await tryOpenMatch3DGalleryEntry(); await tryOpenMatch3DGalleryEntry();
return; return;
} case 'square-hole-work':
if (shouldSearchSquareHoleFirst) {
await tryOpenSquareHoleGalleryEntry(); await tryOpenSquareHoleGalleryEntry();
return; return;
} case 'visual-novel-work':
if (shouldSearchVisualNovelFirst) {
await tryOpenVisualNovelGalleryEntry(); await tryOpenVisualNovelGalleryEntry();
return; return;
} case 'bark-battle-work':
if (shouldSearchBarkBattleFirst) {
await tryOpenBarkBattleGalleryEntry(); await tryOpenBarkBattleGalleryEntry();
return; return;
} default: {
const exhaustive: never = step;
if (shouldSearchWorkFirst) { return exhaustive;
try {
await tryOpenGalleryEntry();
return;
} catch {
// 作品号优先时允许继续回退到用户号搜索。
} }
} }
};
if (shouldSearchUserFirst) {
try {
const user = await getPublicAuthUserByCode(normalizedKeyword);
setSearchedPublicUser(user);
return;
} catch {
// 用户号优先时允许继续回退到作品号搜索。
}
}
if (!shouldSearchWorkFirst) {
try {
await tryOpenGalleryEntry();
return;
} catch {
// 常规作品未命中时继续尝试汪汪声浪作品号。
}
try { try {
await tryOpenBarkBattleGalleryEntry(); for (const [index, step] of searchPlan.steps.entries()) {
try {
await runSearchStep(step);
return; return;
} catch { } catch (error) {
// 汪汪声浪作品未命中时继续回退到陶泥号搜索。 if (index === searchPlan.steps.length - 1) {
throw error;
}
} }
} }
const user = await getPublicAuthUserByCode(normalizedKeyword);
setSearchedPublicUser(user);
} catch (error) { } catch (error) {
if (selectionStage === 'work-detail') { if (selectionStage === 'work-detail') {
setSelectedPublicWorkDetail(null); setSelectedPublicWorkDetail(null);

View File

@@ -0,0 +1,69 @@
import { describe, expect, test } from 'vitest';
import {
type PlatformPublicCodeSearchStep,
resolvePlatformPublicCodeSearchPlan,
} from './platformPublicCodeSearchModel';
function expectSearchSteps(
keyword: string,
steps: readonly PlatformPublicCodeSearchStep[],
) {
expect(resolvePlatformPublicCodeSearchPlan(keyword)?.steps).toEqual(steps);
}
describe('platformPublicCodeSearchModel', () => {
test('ignores empty public code search input', () => {
expect(resolvePlatformPublicCodeSearchPlan(' ')).toBeNull();
});
test('normalizes public code search keyword before planning', () => {
expect(resolvePlatformPublicCodeSearchPlan(' PZ-00000001 ')).toEqual({
normalizedKeyword: 'PZ-00000001',
steps: ['puzzle-work'],
});
});
test('searches internal user ids directly without work fallback', () => {
expectSearchSteps('user_00000001', ['user-id']);
expectSearchSteps('USER-profile-1', ['user-id']);
});
test('routes known public work prefixes to their play-specific lookup', () => {
const cases: Array<
[keyword: string, step: PlatformPublicCodeSearchStep]
> = [
['PZ-EPUBLIC1', 'puzzle-work'],
['BF-NPUBLIC1', 'big-fish-work'],
['JH-EPUBLIC1', 'jump-hop-work'],
['WF-EPUBLIC1', 'wooden-fish-work'],
['BO-EPUBLIC1', 'baby-object-match-work'],
['M3-EPUBLIC1', 'match3d-work'],
['M3D-LEGACY1', 'match3d-work'],
['SH-EPUBLIC1', 'square-hole-work'],
['VN-EPUBLIC1', 'visual-novel-work'],
['BB-EPUBLIC1', 'bark-battle-work'],
];
for (const [keyword, step] of cases) {
expectSearchSteps(keyword, [step]);
}
});
test('searches RPG public works before public user codes for CW and numeric codes', () => {
expectSearchSteps('CW-00000001', ['rpg-work', 'public-user-code']);
expectSearchSteps('12345678', ['rpg-work', 'public-user-code']);
});
test('keeps legacy user-code-first fallback for SY and ordinary keywords', () => {
const legacyFallbackSteps = [
'public-user-code',
'rpg-work',
'bark-battle-work',
'public-user-code',
] as const;
expectSearchSteps('SY-00000001', legacyFallbackSteps);
expectSearchSteps('月井守望', legacyFallbackSteps);
});
});

View File

@@ -0,0 +1,83 @@
export type PlatformPublicCodeSearchStep =
| 'user-id'
| 'public-user-code'
| 'rpg-work'
| 'puzzle-work'
| 'big-fish-work'
| 'jump-hop-work'
| 'wooden-fish-work'
| 'baby-object-match-work'
| 'match3d-work'
| 'square-hole-work'
| 'visual-novel-work'
| 'bark-battle-work';
export type PlatformPublicCodeSearchPlan = {
normalizedKeyword: string;
steps: readonly PlatformPublicCodeSearchStep[];
};
const PLATFORM_PUBLIC_USER_ID_PATTERN = /^user[_-][a-z0-9_-]+$/iu;
const PLATFORM_RPG_WORK_NUMERIC_CODE_PATTERN = /^\d{1,8}$/u;
const DIRECT_WORK_PREFIX_STEPS: ReadonlyArray<
readonly [prefix: string, step: PlatformPublicCodeSearchStep]
> = [
['PZ', 'puzzle-work'],
['BF', 'big-fish-work'],
['JH', 'jump-hop-work'],
['WF', 'wooden-fish-work'],
['BO', 'baby-object-match-work'],
['M3', 'match3d-work'],
['SH', 'square-hole-work'],
['VN', 'visual-novel-work'],
['BB', 'bark-battle-work'],
];
/** 收口公开码搜索顺序,壳层只按步骤执行网络读取与打开副作用。 */
export function resolvePlatformPublicCodeSearchPlan(
keyword: string,
): PlatformPublicCodeSearchPlan | null {
const normalizedKeyword = keyword.trim();
if (!normalizedKeyword) {
return null;
}
if (PLATFORM_PUBLIC_USER_ID_PATTERN.test(normalizedKeyword)) {
return {
normalizedKeyword,
steps: ['user-id'],
};
}
const upperKeyword = normalizedKeyword.toUpperCase();
const directWorkStep = DIRECT_WORK_PREFIX_STEPS.find(([prefix]) =>
upperKeyword.startsWith(prefix),
)?.[1];
if (directWorkStep) {
return {
normalizedKeyword,
steps: [directWorkStep],
};
}
if (
upperKeyword.startsWith('CW') ||
PLATFORM_RPG_WORK_NUMERIC_CODE_PATTERN.test(normalizedKeyword)
) {
return {
normalizedKeyword,
steps: ['rpg-work', 'public-user-code'],
};
}
return {
normalizedKeyword,
steps: [
'public-user-code',
'rpg-work',
'bark-battle-work',
'public-user-code',
],
};
}