fix: complete jump-hop frontend wiring
This commit is contained in:
@@ -1265,3 +1265,19 @@
|
||||
- 处理:`BarkBattleConfigEditor` 需要区分“系统默认词池”和“创作者已手动编辑”。未手动编辑时随主题 / 形象描述自动重算;手动编辑后才冻结为自定义词池。默认词池只在命中狗相关关键词时加入狗叫词,非狗主题使用科技、幻想或通用高能词。
|
||||
- 验证:`npm run test -- src/components/bark-battle-creation/BarkBattleConfigEditor.test.tsx src/games/bark-battle/ui/__tests__/BarkBattleRuntimeShell.test.tsx`,并确认非狗主题的拟声词不含 `汪`。
|
||||
- 关联:`src/components/bark-battle-creation/BarkBattleConfigEditor.tsx`、`src/games/bark-battle/application/BarkBattleConfig.ts`、`src/games/bark-battle/ui/BarkBattleRuntimeShell.tsx`。
|
||||
|
||||
## Jenkins Web 构建公开作品号导出缺失优先补 publicWorkCode
|
||||
|
||||
- 现象:`Genarrative-Web-Build` 在 `npm run build:production-release -- --component web` 阶段失败,Rollup 报 `"buildJumpHopPublicWorkCode" is not exported by "src/services/publicWorkCode.ts"`,但导入方 `rpgEntryWorldPresentation.ts` 或 `PlatformEntryFlowShellImpl.tsx` 已经引用该玩法公开码函数。
|
||||
- 原因:玩法分支合并时容易只带入新玩法的 `publicWorkCode.ts` 导出,覆盖或遗漏另一个玩法的公开码 builder / matcher,Vite 构建会在静态导出检查阶段直接失败。
|
||||
- 处理:在 `src/services/publicWorkCode.ts` 中保持每个玩法的 `build<Play>PublicWorkCode` 与 `isSame<Play>PublicWorkCode` 成对导出;跳一跳使用 `JH-` 前缀和 profileId 后 8 位规范化后缀。补 `src/services/publicWorkCode.test.ts` 覆盖 builder 和 matcher,避免后续合并再次丢失导出。
|
||||
- 验证:`npm test -- src/services/publicWorkCode.test.ts`,并用 `npm run build:production-release -- --component web --name <临时名>` 复现 Jenkins web 构建路径。若 `npm run typecheck` 仍报 JumpHop 阶段或状态变量缺口,那是远端当前 JumpHop 接线未收齐的独立问题,不等同于该 Rollup 导出失败。
|
||||
- 关联:`src/services/publicWorkCode.ts`、`src/components/rpg-entry/rpgEntryWorldPresentation.ts`、`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
|
||||
|
||||
## 跳一跳前端壳层接线不要只合渲染分支
|
||||
|
||||
- 现象:`npm run typecheck` 大量报 `setJumpHopSession`、`jumpHopRun`、`jumpHopGalleryEntries`、`mapJumpHopWorkToPublicWorkDetail` 不存在,以及 `"jump-hop-runtime" is not assignable to SelectionStage`;即使 typecheck 过了,分享或刷新 `/runtime/jump-hop?work=...` 仍可能掉回首页。
|
||||
- 原因:跳一跳工作台、生成页、结果页、runtime 和推荐流渲染分支已经合入 `PlatformEntryFlowShellImpl.tsx`,但平台壳层状态、public detail mapper、`SelectionStage` union 与 `appPageRoutes.ts` 阶段路由映射没有一并合入;发现页卡片分类也没有先判断 `isJumpHopGalleryEntry`,导致 fallback 访问 RPG `themeMode`。
|
||||
- 处理:`platformEntryTypes.ts` 必须注册 `jump-hop-workspace/generating/result/runtime/gallery-detail`;`appPageRoutes.ts` 必须补 `/creation/jump-hop/workspace`、`/creation/jump-hop/generating`、`/creation/jump-hop/result`、`/gallery/jump-hop/detail`、`/runtime/jump-hop`;`PlatformEntryFlowShellImpl.tsx` 必须持有 JumpHop session/work/run/gallery/runtimeReturnStage/generationState/error/busy,并提供 `mapJumpHopWorkToPublicWorkDetail`;`RpgEntryHomeView.tsx` 的公开卡片类型描述要给 JumpHop 单独返回 `跳一跳`。
|
||||
- 验证:`npm run typecheck`,并跑 `npm test -- src/routing/appPageRoutes.test.ts` 覆盖 JumpHop 阶段路径。
|
||||
- 关联:`src/components/platform-entry/platformEntryTypes.ts`、`src/routing/appPageRoutes.ts`、`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/rpg-entry/RpgEntryHomeView.tsx`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
|
||||
|
||||
@@ -178,6 +178,8 @@ Nginx 负责站点和反向代理
|
||||
Jenkins 按 web / api / Spacetime module / build / deploy / publish 拆分
|
||||
```
|
||||
|
||||
`Genarrative-Web-Build` 的主站构建失败若出现 Rollup 报错 `"xxx" is not exported by "src/services/publicWorkCode.ts"`,优先按前端公开作品号工具缺失处理,而不是排查 Jenkins 节点环境。修复时要让 `publicWorkCode.ts` 的 `build<Play>PublicWorkCode` 与 `isSame<Play>PublicWorkCode` 成对导出,并补 `src/services/publicWorkCode.test.ts` 覆盖对应玩法前缀;随后用 `npm run build:production-release -- --component web --name <临时名>` 复现 Jenkins web 构建路径。
|
||||
|
||||
Windows Stdb module 构建流水线运行在 Jenkins `windows` 节点上。该流水线需要执行 PowerShell 逻辑时,统一通过 `bat` 显式调用 `%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe`,不要直接使用 Jenkins `powershell` step;本地 Jenkins durable-task 曾在 `Genarrative-Stdb-Module-Build` workspace 中启动裸 `powershell` 时触发 `CreateProcess error=5, 拒绝访问`。临时 `.ps1` 由 Jenkins `writeFile` 写出后要先转成 UTF-8 with BOM 再交给 Windows PowerShell 5.1 `-File` 解析,避免中文错误消息在无 BOM UTF-8 下被当成本地 ANSI 误解码。Checkout 阶段要优先复用 Jenkins GitSCM 已经完成的结果:`COMMIT_HASH` 为空或与当前 `HEAD` 一致时,不要再额外 `git clean` / `git checkout`,只在确实需要切到别的指定 commit 时才补 fetch、校验和切换。排查时先看对应 build log、`@tmp/durable-*` 下的 `powershellWrapper.ps1`,以及日志中的 `[jenkins-powershell] user/exe`。
|
||||
|
||||
生产环境变量模板:`deploy/env/api-server.env.example`。真实密钥只放服务器,不提交 Git,不写入文档示例。
|
||||
|
||||
@@ -118,7 +118,7 @@ RPG / 拼图等运行态存档选择入口统一在个人中心 `常用功能 >
|
||||
|
||||
运行态规则真相必须沉到 `module-jump-hop`,前端只做蓄力表现、角色位移、投影和落地反馈。通关、失败、分数、combo、运行态快照和发布作品状态以后端为准。公开列表应走 `jump_hop_gallery_card_view` 订阅缓存,不要每次 HTTP 请求调用 procedure 组装全量列表。
|
||||
|
||||
平台首页推荐、精选、最新、公开详情、搜索、已玩作品和公开试玩统一按 `sourceType='jump-hop'` 与 `JH-*` 公开作品号识别跳一跳作品;从公开详情或推荐流启动运行态时,若卡片摘要不足以携带角色图、地块图集和路径配置,必须先补读完整 work profile 再传入运行态。
|
||||
平台首页推荐、精选、最新、公开详情、搜索、已玩作品和公开试玩统一按 `sourceType='jump-hop'` 与 `JH-*` 公开作品号识别跳一跳作品;从公开详情或推荐流启动运行态时,若卡片摘要不足以携带角色图、地块图集和路径配置,必须先补读完整 work profile 再传入运行态。平台壳层必须同步注册 `jump-hop-workspace`、`jump-hop-generating`、`jump-hop-result`、`jump-hop-runtime`、`jump-hop-gallery-detail` 阶段,并在 `appPageRoutes.ts` 映射 `/creation/jump-hop/workspace`、`/creation/jump-hop/generating`、`/creation/jump-hop/result`、`/gallery/jump-hop/detail`、`/runtime/jump-hop`,同时持有 session、work、run、gallery、busy/error 与生成进度状态,避免只合入渲染分支但遗漏状态源或分享路径导致 typecheck 失败、刷新回首页。
|
||||
|
||||
## 抓大鹅 Match3D
|
||||
|
||||
|
||||
@@ -716,6 +716,12 @@ function mapVisualNovelWorkToPublicWorkDetail(
|
||||
return mapVisualNovelWorkToPlatformGalleryCard(item);
|
||||
}
|
||||
|
||||
function mapJumpHopWorkToPublicWorkDetail(
|
||||
item: JumpHopGalleryCardResponse | JumpHopWorkProfileResponse,
|
||||
): PlatformPublicGalleryCard {
|
||||
return mapJumpHopWorkToPlatformGalleryCard(item);
|
||||
}
|
||||
|
||||
function mapBarkBattleWorkToPublicWorkDetail(
|
||||
item: BarkBattleWorkSummary,
|
||||
): PlatformPublicGalleryCard {
|
||||
@@ -2563,6 +2569,22 @@ export function PlatformEntryFlowShellImpl({
|
||||
useState(false);
|
||||
const [squareHoleGenerationState, setSquareHoleGenerationState] =
|
||||
useState<MiniGameDraftGenerationState | null>(null);
|
||||
const [jumpHopSession, setJumpHopSession] =
|
||||
useState<JumpHopSessionSnapshotResponse | null>(null);
|
||||
const [jumpHopRun, setJumpHopRun] = useState<
|
||||
JumpHopRunResponse['run'] | null
|
||||
>(null);
|
||||
const [jumpHopWork, setJumpHopWork] =
|
||||
useState<JumpHopWorkProfileResponse | null>(null);
|
||||
const [jumpHopGalleryEntries, setJumpHopGalleryEntries] = useState<
|
||||
JumpHopGalleryCardResponse[]
|
||||
>([]);
|
||||
const [jumpHopRuntimeReturnStage, setJumpHopRuntimeReturnStage] =
|
||||
useState<JumpHopRuntimeReturnStage>('jump-hop-result');
|
||||
const [jumpHopGenerationState, setJumpHopGenerationState] =
|
||||
useState<MiniGameDraftGenerationState | null>(null);
|
||||
const [jumpHopError, setJumpHopError] = useState<string | null>(null);
|
||||
const [isJumpHopBusy, setIsJumpHopBusy] = useState(false);
|
||||
const [barkBattleWorks, setBarkBattleWorks] = useState<
|
||||
BarkBattleWorkSummary[]
|
||||
>([]);
|
||||
@@ -3674,6 +3696,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
...match3dGalleryEntries.map(mapMatch3DWorkToPublicWorkDetail),
|
||||
...puzzleGalleryEntries.map(mapPuzzleWorkToPlatformGalleryCard),
|
||||
...barkBattleGalleryEntries.map(mapBarkBattleWorkToPlatformGalleryCard),
|
||||
...jumpHopGalleryEntries.map(mapJumpHopWorkToPlatformGalleryCard),
|
||||
...(barkBattleGalleryEntries.length === 0
|
||||
? barkBattleWorks
|
||||
.filter((work) => work.status === 'published')
|
||||
|
||||
@@ -31,6 +31,11 @@ export type SelectionStage =
|
||||
| 'square-hole-generating'
|
||||
| 'square-hole-result'
|
||||
| 'square-hole-runtime'
|
||||
| 'jump-hop-workspace'
|
||||
| 'jump-hop-generating'
|
||||
| 'jump-hop-result'
|
||||
| 'jump-hop-runtime'
|
||||
| 'jump-hop-gallery-detail'
|
||||
| 'bark-battle-generating'
|
||||
| 'bark-battle-result'
|
||||
| 'bark-battle-runtime'
|
||||
|
||||
@@ -1910,7 +1910,9 @@ function describePublicGalleryCardKind(entry: PlatformPublicGalleryCard) {
|
||||
? '汪汪'
|
||||
: isEdutainmentGalleryEntry(entry)
|
||||
? entry.templateName
|
||||
: describePlatformThemeLabel(entry.themeMode);
|
||||
: isJumpHopGalleryEntry(entry)
|
||||
? '跳一跳'
|
||||
: describePlatformThemeLabel(entry.themeMode);
|
||||
return formatPlatformWorkDisplayTag(kind);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,4 +17,37 @@ describe('appPageRoutes', () => {
|
||||
'/profile/feedback',
|
||||
);
|
||||
});
|
||||
|
||||
it('resolves jump-hop creation, gallery and runtime routes', () => {
|
||||
expect(resolveSelectionStageFromPath('/creation/jump-hop/workspace')).toBe(
|
||||
'jump-hop-workspace',
|
||||
);
|
||||
expect(resolveSelectionStageFromPath('/creation/jump-hop/generating')).toBe(
|
||||
'jump-hop-generating',
|
||||
);
|
||||
expect(resolveSelectionStageFromPath('/creation/jump-hop/result')).toBe(
|
||||
'jump-hop-result',
|
||||
);
|
||||
expect(resolveSelectionStageFromPath('/gallery/jump-hop/detail')).toBe(
|
||||
'jump-hop-gallery-detail',
|
||||
);
|
||||
expect(resolveSelectionStageFromPath('/runtime/jump-hop')).toBe(
|
||||
'jump-hop-runtime',
|
||||
);
|
||||
expect(resolvePathForSelectionStage('jump-hop-workspace')).toBe(
|
||||
'/creation/jump-hop/workspace',
|
||||
);
|
||||
expect(resolvePathForSelectionStage('jump-hop-generating')).toBe(
|
||||
'/creation/jump-hop/generating',
|
||||
);
|
||||
expect(resolvePathForSelectionStage('jump-hop-result')).toBe(
|
||||
'/creation/jump-hop/result',
|
||||
);
|
||||
expect(resolvePathForSelectionStage('jump-hop-gallery-detail')).toBe(
|
||||
'/gallery/jump-hop/detail',
|
||||
);
|
||||
expect(resolvePathForSelectionStage('jump-hop-runtime')).toBe(
|
||||
'/runtime/jump-hop',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,6 +21,11 @@ const STAGE_ROUTE_ENTRIES = [
|
||||
['square-hole-agent-workspace', '/creation/square-hole/agent'],
|
||||
['square-hole-result', '/creation/square-hole/result'],
|
||||
['square-hole-runtime', '/runtime/square-hole'],
|
||||
['jump-hop-workspace', '/creation/jump-hop/workspace'],
|
||||
['jump-hop-generating', '/creation/jump-hop/generating'],
|
||||
['jump-hop-result', '/creation/jump-hop/result'],
|
||||
['jump-hop-gallery-detail', '/gallery/jump-hop/detail'],
|
||||
['jump-hop-runtime', '/runtime/jump-hop'],
|
||||
['bark-battle-generating', '/creation/bark-battle/generating'],
|
||||
['bark-battle-result', '/creation/bark-battle/result'],
|
||||
['bark-battle-runtime', '/runtime/bark-battle'],
|
||||
|
||||
26
src/services/publicWorkCode.test.ts
Normal file
26
src/services/publicWorkCode.test.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
buildJumpHopPublicWorkCode,
|
||||
isSameJumpHopPublicWorkCode,
|
||||
} from './publicWorkCode';
|
||||
|
||||
describe('publicWorkCode', () => {
|
||||
it('builds and matches jump-hop public work codes from profile ids', () => {
|
||||
expect(buildJumpHopPublicWorkCode('jump-hop-profile-12345678')).toBe(
|
||||
'JH-12345678',
|
||||
);
|
||||
expect(
|
||||
isSameJumpHopPublicWorkCode(
|
||||
'jh-12345678',
|
||||
'jump-hop-profile-12345678',
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isSameJumpHopPublicWorkCode(
|
||||
'jump hop profile 12345678',
|
||||
'jump-hop-profile-12345678',
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -67,6 +67,14 @@ export function buildBarkBattlePublicWorkCode(workId: string) {
|
||||
return `BB-${normalizeBarkBattlePublicWorkCodeSuffix(workId)}`;
|
||||
}
|
||||
|
||||
export function buildJumpHopPublicWorkCode(profileId: string) {
|
||||
const normalized = normalizePublicCodeText(profileId);
|
||||
const fallback = normalized || '00000000';
|
||||
const suffix = fallback.slice(-8).padStart(8, '0');
|
||||
|
||||
return `JH-${suffix}`;
|
||||
}
|
||||
|
||||
export function isSamePuzzlePublicWorkCode(keyword: string, profileId: string) {
|
||||
const normalizedKeyword = normalizePublicCodeText(keyword);
|
||||
|
||||
@@ -149,3 +157,13 @@ export function isSameBarkBattlePublicWorkCode(keyword: string, workId: string)
|
||||
normalizedKeyword === normalizeBarkBattlePublicWorkCodeSuffix(workId)
|
||||
);
|
||||
}
|
||||
|
||||
export function isSameJumpHopPublicWorkCode(keyword: string, profileId: string) {
|
||||
const normalizedKeyword = normalizePublicCodeText(keyword);
|
||||
|
||||
return (
|
||||
normalizedKeyword ===
|
||||
normalizePublicCodeText(buildJumpHopPublicWorkCode(profileId)) ||
|
||||
normalizedKeyword === normalizePublicCodeText(profileId)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user