Skip to content
Open
37 changes: 33 additions & 4 deletions frontend/src/entities/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
/**
* entities 唯一公开入口。外部不得绕过本文件访问内部文件。
* 本次只提交类型与接口,不提交实现。
* Entity 层的唯一公开入口。
*
* Page 和 Feature 只从 `@/entities` 导入,不直接访问某个 Entity 的内部文件。
* 这不是为了少写一段路径,而是为了稳定模块边界:内部文件可以重构,
* 但公开名称和依赖方向必须经过本文件明确审核。
*
* 这里只暴露 Entity 级别的数据结构、后端端口契约以及必要的本地 Store 工厂。
* 页面状态、路由、弹窗和按钮行为不属于 Entity,不应从此处导出。
*/

/* 项目 —— 全局约束:视角、朝向、精灵尺寸、画风 */
Expand Down Expand Up @@ -55,10 +61,28 @@ export type {
/* 媒体引用 —— 不承诺 URL 或后端 Media ID 的具体表示 */
export type { MediaReference } from './media'

/* 工作流 —— 节点与运行状态都由前端管理 */
export { WORKFLOW_STEP_ORDER } from './workflow-run'
/*
* 工作流 —— 记录“一次用户任务如何运行”。
* 它不是角色/动作资产,也不是负责调后端的 WorkflowController。
*/
export {
ACTION_FIRST_FRAME_CANDIDATE_COUNT,
CHARACTER_CANDIDATE_COUNT,
createWorkflowRunService,
createWorkflowRunStore,
WORKFLOW_STEP_ORDERS,
} from './workflow-run'
export type {
ActionFirstFrameCandidateBatch,
ActionReviewFrame,
ActionReviewResult,
CreateWorkflowRunStoreOptions,
CreateWorkflowRunServiceOptions,
CreateWorkflowRunInput,
CharacterCandidateBatch,
CharacterCandidateConfirmationApis,
ConfirmCharacterSelectionInput,
ConfirmActionFirstFrameInput,
ExportStatus,
GenerationStatus,
WorkflowDriver,
Expand All @@ -68,6 +92,11 @@ export type {
WorkflowRevision,
WorkflowRevisionStatus,
WorkflowRun,
WorkflowRunStore,
WorkflowRunService,
WorkflowRunPurpose,
WorkflowRunStatus,
PublishActionResult,
StartActionRunInput,
StartCharacterRunInput,
} from './workflow-run'
196 changes: 41 additions & 155 deletions frontend/src/entities/workflow-run/index.ts
Original file line number Diff line number Diff line change
@@ -1,157 +1,43 @@
import type { Generation } from '../generation'

/** Quick Start 与手动工作流只改变输入方式,共用同一种运行模型。 */
export type WorkflowDriver = 'ai' | 'manual'

/** 创建 WorkflowRun 时要完成的用户意图。 */
export type WorkflowRunPurpose = 'create_character' | 'add_action'

/**
* 流程步骤类型的唯一标准顺序;它不是后端 Workflow 或 Execution 定义。
* 某个 Revision 已进入执行线的步骤顺序,由 WorkflowRevision.nodes 的数组位置表达。
*/
export const WORKFLOW_STEP_ORDER = [
'character-setup',
'character-template',
'template-candidate',
'action-setup',
'first-frame',
'complete-animation',
'review',
'export',
] as const

/** 前端流程步骤类型,与 WORKFLOW_STEP_ORDER 的成员保持一致。 */
export type WorkflowStepType = (typeof WORKFLOW_STEP_ORDER)[number]

/**
* 步骤的可用性和执行结果;不直接复用后端任务状态。
* locked/available 表示尚未执行,active 表示当前页面阶段,passed/failed 表示结果。
*/
export type WorkflowStepStatus = 'locked' | 'available' | 'active' | 'passed' | 'failed'

/**
* 单个版本的生命周期。
* abandoned 表示停止沿用但仍保留为历史。
*/
export type WorkflowRevisionStatus = 'active' | 'completed' | 'failed' | 'abandoned'

/**
* 整次流程的汇总状态。
* interrupted 只表示用户主动停止自动推进:历史仍保留且可只读查看,它不等于 failed 或 completed。
* 后端生成任务是否真正停止是独立问题;从历史重启成功后可重新进入 active。
*/
export type WorkflowRunStatus = 'active' | 'interrupted' | 'completed' | 'failed'

/**
* 当前版本在生成阶段的汇总状态;素材准备期间为 not_started。
* 它是版本级别的汇总,不是单次生成任务的状态——后者是 TaskStatus。
*/
export type GenerationStatus = 'not_started' | 'in_progress' | 'completed' | 'failed'

/** 当前版本在导出阶段的汇总状态。 */
export type ExportStatus = 'not_exported' | 'exporting' | 'exported' | 'failed'

/**
* 一个 Revision 中已经进入执行线的流程步骤。
* 步骤自身不重复保存顺序;其在 nodes 中的数组位置就是该版本的执行顺序。
*/
export interface WorkflowStep {
/** 只用于编排和页面定位,不作为业务 ID 发送给后端。 */
id: string
type: WorkflowStepType
status: WorkflowStepStatus
/** 进入步骤时保存的输入快照。 */
input: unknown
/** 步骤完成后的结果或引用;尚无结果时为 null。 */
output: unknown
/**
* 本步骤已提交、结果尚未写回 output 的 Generation ID;没有在途任务时为 null。
* 它由前端随 WorkflowRun 一起维护,据此查回在途任务的状态,因而不会在同一次
* 前端运行中重复发起生成。是否写入浏览器存储属于前端实现,不形成后端契约。
* Generation 本身不认识步骤,反向关联不存在。
*
* 字段名沿用后端的 task_id。步骤类型不能从 Generation.type 反推——后端只有
* character_image 和 character_action 两种,本步骤是哪一步以 WorkflowStep.type 为准。
*/
taskId: Generation['id'] | null
/** 该步骤沿用或依赖的步骤 ID,用于版本来源追踪,不代表后端执行依赖。 */
referenceStepIds: string[]
}

/**
* 一次页面执行版本;当前版本会推进,从旧步骤重开则追加新版本。
* WorkflowRun Entity 的对外入口。
*
* MVP 只走单条执行线:revisions 恒为一个成员,basedOnRevisionId 与 restartStepId 恒为 null。
* 「从历史步骤重开并保留旧版本」尚未进入产品定义,结构先留出位置但不实现,
* 避免真要做时改动波及 WorkflowRun 的持久化形状。
*/
export interface WorkflowRevision {
id: string
/** 首次创建的版本没有来源,因此为 null。 */
basedOnRevisionId: string | null
/** 在来源版本中选择的重启步骤 ID;非重启创建的版本为 null。 */
restartStepId: string | null
status: WorkflowRevisionStatus
/**
* 已进入当前执行线的步骤;数组位置是该版本步骤顺序的唯一来源。
* 尚未推进到的后续步骤可以不存在;完整步骤类型顺序以 WORKFLOW_STEP_ORDER 为准。
*/
steps: WorkflowStep[]
generationStatus: GenerationStatus
exportStatus: ExportStatus
createdAt: string
}

/**
* 一次由前端推进的页面流程。
* 步骤推进和运行状态都由前端管理;后端不读取、不推进、也不持久化 WorkflowRun。
* 后端只处理生成任务,并在用户最终确认时持久化角色与动作资产。
*/
export interface WorkflowRun {
id: string
projectId: string
/** 已关联的 Character ID;角色尚未创建或确认时为 null。 */
characterId: string | null
/** 已有角色加动作时的目标造型;新建角色时为 null。 */
outfitId: string | null
purpose: WorkflowRunPurpose
driver: WorkflowDriver
status: WorkflowRunStatus
/** 当前可编辑版本 ID;必须能在 revisions 中找到。 */
currentRevisionId: string
/** 按创建顺序保存的全部版本;历史版本保留用于只读查看和重启。 */
revisions: WorkflowRevision[]
/** Quick Start 的规范化提示词;空白输入或手动模式无提示词时为 null。 */
prompt: string | null
}

/** 两种入口共享的创建字段。 */
interface CreateWorkflowRunInputBase {
projectId: string
driver: WorkflowDriver
/** Quick Start 的自然语言需求;提交时去除首尾空白,空字符串按 null 保存。 */
prompt?: string
}

/**
* 创建 WorkflowRun 的输入。
* add_action 分支把已有角色、造型、母版和基准帧设为必填,避免创建无法恢复的半成品运行。
*/
export type CreateWorkflowRunInput = CreateWorkflowRunInputBase &
(
| {
purpose: 'create_character'
characterId?: never
outfitId?: never
characterTemplateUrl?: never
baseFrameUrls?: never
}
| {
purpose: 'add_action'
characterId: string
outfitId: string
characterTemplateUrl: string
baseFrameUrls: readonly string[]
}
)
* 外部模块只从这里获取 WorkflowRun 能力,不绕过入口直接依赖 model/store
* 内部文件。这样既保留了子目录的职责分工,又不把内部结构变成全仓库 API。
*/

export {
ACTION_FIRST_FRAME_CANDIDATE_COUNT,
CHARACTER_CANDIDATE_COUNT,
WORKFLOW_STEP_ORDERS,
} from './model'
export type {
CreateWorkflowRunInput,
ExportStatus,
GenerationStatus,
WorkflowDriver,
WorkflowRevision,
WorkflowRevisionStatus,
WorkflowRun,
WorkflowRunPurpose,
WorkflowRunStatus,
WorkflowStep,
WorkflowStepStatus,
WorkflowStepType,
} from './model'
export { createWorkflowRunStore } from './store'
export type { CreateWorkflowRunStoreOptions, WorkflowRunStore } from './store'
export { createWorkflowRunService } from './service'
export type {
ActionFirstFrameCandidateBatch,
ActionReviewFrame,
ActionReviewResult,
CharacterCandidateBatch,
CharacterCandidateConfirmationApis,
ConfirmCharacterSelectionInput,
ConfirmActionFirstFrameInput,
CreateWorkflowRunServiceOptions,
PublishActionResult,
StartActionRunInput,
StartCharacterRunInput,
WorkflowRunService,
} from './service'
63 changes: 63 additions & 0 deletions frontend/src/entities/workflow-run/model/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* WorkflowRun 的业务词汇和步骤模板。
*
* 常量数组同时服务于三个地方:TypeScript 联合类型、运行时水合校验、
* 以及页面的进度顺序。只保留一份定义,可以避免“类型说可以,恢复时却拒绝”。
*/

/** 该 Run 是由 AI 自动引导,还是用户在编辑器中手动推进。 */
export const WORKFLOW_DRIVERS = ['ai', 'manual'] as const

/**
* 一个 Run 只有一个目标。新建角色和追加动作可在同一界面连续操作,
* 但是两次独立任务,因此使用两个 WorkflowRun。
*/
export const WORKFLOW_PURPOSES = ['create_character', 'add_action'] as const

/** Run 级状态:描述整个用户任务,不等于某次后端生成任务的状态。 */
export const WORKFLOW_RUN_STATUSES = ['active', 'interrupted', 'completed', 'failed'] as const

/**
* Revision 级状态。用户从旧步骤重做时,旧 Revision 变为 abandoned,
* 并追加新 Revision;不覆盖历史,才能说清“这个结果从哪次重做而来”。
*/
export const WORKFLOW_REVISION_STATUSES = ['active', 'completed', 'failed', 'abandoned'] as const

/** 当前 Revision 中生成阶段的汇总状态,不是单个 GenerationTask.status。 */
export const GENERATION_STATUSES = ['not_started', 'in_progress', 'completed', 'failed'] as const

/** 导出阶段的汇总状态;角色生成 Run 没有导出步骤时保持 not_exported。 */
export const EXPORT_STATUSES = ['not_exported', 'exporting', 'exported', 'failed'] as const

/**
* 单个步骤的状态。locked 表示前置条件未满足,available 表示可开始,
* active 表示当前正在处理,passed/failed 是已结束结果。
*/
export const WORKFLOW_STEP_STATUSES = ['locked', 'available', 'active', 'passed', 'failed'] as const

/** 角色形象每次生成 4 张临时候选;用户只会确认其中 1 张为正式资产。 */
export const CHARACTER_CANDIDATE_COUNT = 4

/** 动作也先生成 4 张独立首帧,避免错误姿势直接扩展成完整动画。 */
export const ACTION_FIRST_FRAME_CANDIDATE_COUNT = 4

/**
* 按任务目的分开定义步骤顺序。
*
* create_character 到“四选一并保存正式角色”就结束;
* add_action 从已有角色/造型开始,不重复跑角色母版生成。
*
* 两个 Run 可以由同一页面连续展示,但数据上必须拆开,否则历史记录、
* 失败重试和后续追加动作都无法准确归属。
*/
export const WORKFLOW_STEP_ORDERS = {
create_character: ['character-setup', 'character-template', 'template-candidate'],
add_action: [
'action-setup',
'first-frame',
'first-frame-candidate',
'complete-animation',
'review',
'export',
],
} as const
27 changes: 27 additions & 0 deletions frontend/src/entities/workflow-run/model/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* WorkflowRun 领域模型的子目录入口。
*
* 本目录只定义“WorkflowRun 是什么”:业务词汇、步骤模板、Run/Revision/Step
* 类型以及创建输入。它不知道 localStorage、订阅者或页面,因此可被
* Store、Controller 和页面共同依赖,而不产生反向依赖。
*/

export {
ACTION_FIRST_FRAME_CANDIDATE_COUNT,
CHARACTER_CANDIDATE_COUNT,
WORKFLOW_STEP_ORDERS,
} from './constants'
export type {
CreateWorkflowRunInput,
ExportStatus,
GenerationStatus,
WorkflowDriver,
WorkflowRevision,
WorkflowRevisionStatus,
WorkflowRun,
WorkflowRunPurpose,
WorkflowRunStatus,
WorkflowStep,
WorkflowStepStatus,
WorkflowStepType,
} from './types'
Loading