主题
MOD-01 内容数据、配置与版本:开发与架构设计
最后更新:2026-09-30
模块状态:🟤 架构设计中
产品方案状态:已批准
产品批准记录:2026-09-30,用户确认继续,由整章产品方案进入架构设计
架构方案状态:待批准
编码状态:未开始
当前交付:覆盖MOD-01-F001至MOD-01-F008的整章实现方案;本文计划路径和接口均为后续施工约定,不表示已经实现
目标、范围与关键决策
交付一个本机可用的内容工作台:能完整登记获批工作集,编辑并预览内容,检查缺项和能力要求,生成不可改写的内容包,选择新局版本并保护旧局引用。工具建档完整、内容可用于对局、正式美术验收分别判断。
| 决策 | 本模块采用方案 | 产品影响与理由 |
|---|---|---|
| 工具形态 | 本地浏览器工作台+仅监听回环地址的本地服务;命令行共用同一应用服务 | 内容人员使用表单与预览;自动检查不用另造一套规则 |
| 后台 | Node.js 24 LTS、TypeScript 5.9严格模式、ESM;原生HTTP路由、Worker任务 | 类型契约贯穿本模块;不建立在线账号或远程后台 |
| 工具客户端 | React 19、Vite 7、原生CSS;只生成工作台静态资源 | 适合目录、表单、差异与预览;不决定游戏客户端引擎 |
| 数据校验 | JSON Schema 2020-12、Ajv 8的2020实现+显式业务检查器 | 文档结构错误与跨内容关系错误分别报告;不执行配置脚本 |
| 草稿存储 | better-sqlite3 12、SQLite单写入者;不可变修订+当前指针 | 保存和批量修改可事务提交,多标签页不覆盖彼此 |
| 发布格式 | UTF-8规范化JSON+按摘要命名的资源文件;完整目录包 | 游戏侧按公开格式读取,不依赖Node或SQLite |
| 构建与测试 | pnpm 10独立工具工作区,tsc、node:test、Playwright定向检查 | 锁定依赖,功能测试不等同于大规模对局模拟 |
以上版本系列是本次选型;首次获批实施时在工具自己的锁文件固定具体补丁版本,后续升级需检查兼容性与数据迁移。Node 24的LTS状态、SQLite单写事务及better-sqlite3事务API以官方资料为依据;工作台形态和分层是本项目决策。Node发布表 · SQLite事务 · better-sqlite3维护者文档 · React版本 · Vite文档
不引入云服务、任意脚本、运行时插件或武将ID分支。MOD-02定义并实现能力,MOD-03负责游戏启动提示,MOD-04负责对局状态与保存,MOD-05负责未核候选和历史战阵规则,MOD-18负责对外发行。当前只定义这些模块使用内容包的边界,不创建它们的目录或代码。
组件与信任边界
HTTP进程不直接运行长校验或同步SQLite操作:一个数据库Worker持有连接,串行执行读写命令;一个任务Worker处理校验、摘要及打包。浏览器不得访问任意本机文件、直接写SQLite或自行判定发布成功。领域检查器只接受不可变快照,不能写数据库、启动网络请求或根据当前页面筛选改变检查范围。
服务默认绑定127.0.0.1:4180,与文档站4173分开。端口被占用时明确退出,不任意换端口连接未知服务。启动器先取得服务实例ID和工作区ID;仅在二者匹配时打开已有实例,否则提示冲突。不同工作区不能共用同一服务端口。
每次启动生成随机会话密钥,由启动器放入首次打开URL片段;客户端读取后立即清除片段,仅保存在内存。所有API请求携带该密钥;写请求另要求精确同源Origin,拒绝跨站与非本机Host,不开启跨域。密钥不写日志、审计或包文件。静态资源路径固定,用户文本按纯文本显示,不渲染可执行HTML;内容和资源导入禁止网络URL抓取、符号链接与路径穿越。本机操作者是受信任制作人员,此设计不提供多人权限管理。
数据契约与版本
公共表示
所有对象使用项目稳定ID:类别前缀_小写UUIDv4,由服务端创建,调用Node crypto.randomUUID();前缀只辅助排错,不参与玩法判定。内容、人物身份、关系、文字、资源、缺口分别使用cnt、ident、rel、txt、ast、gap。ID不能由参考号、中文名或导入行号计算;已有ID不复用,不允许客户端改ID或类别。类别变更必须新建身份并显式修订引用,旧身份保留。
时间统一为UTC ISO 8601字符串。规则数值使用JSON安全整数;概率和倍率使用约分的{numerator,denominator},分母为正,禁止浮点近似权重。界面日期按本地时区显示,不能改变包中的序列化结果。
每个事实字段使用带状态的值:known必须携带合法value与依据引用;unknown必须携带gapId且禁止value;notApplicable必须携带原因且禁止value。明确0、空候选集合与未知分开。基础生命、护甲、费用等字段的合法范围由类别Schema和获批基线共同校验,不在架构里补写玩法数值。
记录统一外壳为{id,kind,revision,status,baselineKey,identityId,body}:revision为从1起的整数,status为active/disabled/retired;baselineKey定位批准基线内的类别、参考身份与形态,不作为运行时ID。identityId仅人物或实体形态需要;其他类别显式不适用。外壳和各类别Schema默认拒绝未知字段,不接受可执行表达式。
内容与关联结构
| 对象 | 必需字段与约束 | 主要引用 |
|---|---|---|
| 人物/实体身份 | 中文全名、三国身份说明、批准依据;普通/神将可以共享,不同同名人物不能合并 | PRE-01对应条目 |
| 主公 | 名称、肖像身份、常规生命与护甲、起始主公技、候选条件事实 | 主公技、候选条件需求 |
| 武将及衍生武将 | 名称、品阶、实际势力集合、基础攻血、形态、对应普通/神将、池身份、效果需求 | 人物身份、能力、生成关系 |
| 主公技/锦囊/神兵/天意 | 类别、名称、费用事实、档位或时机、合法目标需求、效果需求、来源条件 | 变化技能、关联档位、能力与条件 |
| 来源定义型输出 | 生成者、已核参数与未核缺口、输出形态约束 | 来源内容;禁止伪造固定成品 |
rel关系 | fromId,toId,type,conditionRefs,evidenceRefs,certainty;type为形态、持有技能、生成或替换,certainty为确定或条件 | 两端必须存在且类别组合合法;不把关联检索误当生成边 |
| 能力使用 | capabilityId,contractVersion,parameters,requirementIds | 参数只允许声明Schema中的有限JSON;未找到能力时只保留需求,不造替代代码 |
gap缺口 | ownerModule,sourceAnchor,affectedPaths,question,state,resolutionEvidence | state为未闭合/已闭合;闭合需要原主责模块批准依据,工具操作不能自行授予批准 |
txt文字 | ownerId,slot,locale,template,parameterBindings,sourceRulesDigest | 首阶段locale固定zh-CN;slot为名称、效果、关键词解释或提示;数值绑定指向同版本字段 |
ast资源 | ownerId,slot,blobHash,mime,width,height,durationMs,usage,rights,acceptance,variants | usage为评审/临时/正式;正式须有承接MOD验收依据;允许多个owner引用同一blob |
势力技术枚举固定nanman,xiliang,han,yuanshao,shu,wu,wei,yuanshu,jin,huangjin,对应PRE-01十势力;factions为去重数组,另以factionMode=listed/none/all区分,none/all时数组为空。多势力不新增虚构类别。普通/神将配对需双向一致;未配对的特殊形态必须有来源证明,不能由工具自动补普通版。
正文中的“名称”保存为nameTextId,“效果”保存为effectTextId;具体中文只存于txt记录。资源variants为{role,blobHash,crop}数组,crop使用原图整数像素矩形并检查边界。rights包含creator,source,licenseEvidence,allowedUsages;acceptance包含ownerModule,decision,evidenceRef,reviewer,time,decision为未验收/通过/拒绝,正式用途必须为通过。证据引用统一包含document,anchor,revisionDigest,decisionRef,不可只有一个无法定位版本的网页标题。图片使用width/height,音频使用durationMs,其他不适用元数据显式省略并按mime判别Schema检查。
parameters只存字面量、稳定ID或声明允许的字段引用;不允许代码、动态文件名、SQL、脚本或任意求值。解释器、触发顺序和循环边界归MOD-02,MOD-01只检查引用与参数契约。生成图允许真实来源关系形成环,采用显式队列与visited集合检查有限图,不递归展开对局实例,也不因有环直接删除合法生成链。
基线与能力声明
批准基线使用独立的只读JSON快照,包含baselineId,snapshotDate,sourceCommit,documentDigests,approvalRefs,expectedKeys,approvedFacts,openGaps,generationEdges。首次建档由内容负责人逐条录入并核对PRE-01,不下载客户端数据、不从旧工程导入。稳定ID映射登记在工作区注册表并随资料包导出;同一基线再次导入必须复用映射,冲突列出差异,不能按姓名重建ID。
基线的expectedKeys是唯一覆盖分母,分类统计继承116主公、247标准武将身份(246可用、1禁用)、72锦囊、234神兵、43天意、583衍生设计和3来源定义型输出,形态项不混入身份计数。每项记录关联原条目;180项确定路径、403项条件路径和1169条直接来源边独立对账。基线升级须新建快照,记录原主责模块的批准依据及逐项差异;编辑器只能提出变更,不能修改只读基线让错误数据自行通过。
能力提供方声明为{providerModule,providerVersion,artifactDigest,capabilities[],acceptanceRef},每个能力有id,contractVersion,parameterSchema,phase,bounds,presentationSlots。正式支持注册表随经过验收的能力模块构建提供,记录实际提供物摘要与验收依据;不是由内容人员勾选“已支持”。工具仅导入已登记提供方、匹配摘要的声明;运行加载方仍需匹配自己实际提供的能力,不能只相信包内自带声明。
测试声明位于测试夹具并带testOnly=true,只允许专用测试进程使用。正常工作台、内部联调及可用对局包均不得把它当真实支持。MOD-02未交付时,MOD-01仍可完成资料建档、工具功能及拒绝路径验收;不能因此发布带虚假能力的对局包。
本地存储与事务
工作区位置与表职责
工作区位于用户选择的本地磁盘,默认%LOCALAPPDATA%/QunXiongZhanZhen/Content/<workspaceId>/;拒绝网络共享、同步盘目录和只读目录作为活动数据库位置。仓库只提交产品、源码、Schema、受审阅的基线与测试夹具,用户草稿库、运行包、备份与日志不入库。
数据库使用journal_mode=DELETE、synchronous=FULL、foreign_keys=ON、busy_timeout=5000。一个数据库Worker持有唯一应用写连接,事务使用BEGIN IMMEDIATE;事务内只做有限数据库操作,不等待上传、图像解码或文件复制。SQLite的一次写事务限制作为串行写队列依据。SQLite事务说明
| 表 | 主键与主要列 | 约束/生命周期 |
|---|---|---|
workspace_meta | 单行;schemaVersion、workspaceId、generation、baselineId | generation每次有效内容事务加1;迁移和基线变更也使快照失效 |
id_registry | id;kind、baselineKey、createdAt、retiredAt | id唯一;非空baselineKey按类别/形态唯一;永不物理复用 |
record_revisions | (id,revision);payloadJson、payloadDigest、operationId | 外键registry;修订只追加,body通过类别Schema检查 |
record_heads | id;revision | 外键指向已有revision;一次批量事务同时更新全部head |
snapshots | snapshotId;generation、baselineDigest、supportDigest、revisionMap、contentDigest | 捕获全部head,不包含当前列表过滤条件;不可变 |
validation_runs | validationId;snapshotId、profile、validatorVersion、reportJson、reportDigest | 结果绑定确切快照及校验器版本,不能复用于新修订 |
operations | operationId;requestDigest、type、state、resultJson、createdAt | 幂等登记;请求摘要不同不得复用同一operationId |
plans | planId;type、generation、inputJson、inputDigest、diffJson、expiresAt、state | 预览计划持久化,15分钟有效;提交后保留结果,再次提交返回原结果 |
uploads | uploadId;blobHash、mime、metadataJson、createdAt、state | 仅完成核验的上传可被计划引用;临时文件不冒充有效blob |
build_jobs | jobId;operationId、snapshotId、validationId、state、packageId、error | operationId唯一;状态机见发布流程 |
packages | packageId;manifestDigest、purpose、quality、state、sourceSnapshot、receiptJson | packageId为完整manifest摘要;state为ready/invalid/deleting/deleted |
channels | (mode,quality);packageId、revision | 所属仓库内仅指向ready且purpose为playable的包;空值表示不能开新局 |
consumer_registry | consumerId;epoch、lastSequence、reconciled | 新启动或提供方失联标记未对账,不自动解锁清理 |
package_pins | (consumerId,ownerKind,ownerId);packageId、createdAt | ownerKind为session/save/history/replay;引用不得靠超时自动删除 |
audit_events | 单调seq;operationId、operatorLabel、action、beforeRef、afterRef、time | 仅记录制作操作和变更依据,不读取游戏客户端日志 |
基线和支持注册表按其摘要存入工作区approved/只读文件区,workspace_meta与snapshot同时记录摘要,不依赖文件名判定版本。资源、文字、关系与缺口同样使用registry/revisions/heads机制,基线key仅内容身份需要;人物身份等允许baselineKey为空。关系from/to索引和目录检索索引从head派生,在同一保存事务内更新。channels与package_pins对packages建立外键;ready以外的状态再由业务条件阻止新引用。审计、修订、发布回执和幂等终态不自动过期,空间不足时明确阻断新写入。
所有SQL使用参数绑定,字段名和排序选项从固定枚举选择。plans只保存已经校验的规范输入和影响清单,apply不接受客户端重新传入一套待执行内容;过期、generation变化或摘要不符均要求重新生成计划。上传结果按uploads登记读取,不以用户传来的blob路径取代记录。
除标出的可空字段外均非空;版本与序号为非负安全整数。查询建立baselineKey唯一索引、kind/status/name检索索引、关系from/to索引、pins.packageId索引;索引只是派生内容,重建不改变身份。中文名称检索为规范化子串匹配,按全名、id排序分页,不引入另一套姓名规则。
保存、冲突与导入
每次修改携带operationId与expectedGeneration,详情保存另携带expectedRevision。服务端在同一事务校验两者、追加修订、更新head、登记操作结果与审计,并递增generation。不同标签页基于旧版本保存返回409及当前revision;不自动合并数值、关系或资源,保留用户未保存表单供比较后重试。
同一operationId和相同请求重试返回最初结果,不重复新建ID或增加revision;同键不同请求返回409。批量修改先生成预览计划,计划绑定generation和变更摘要;用户确认后携带同一计划,任一条不满足约束则全部回滚。业务缺口允许作为显式unknown保存,结构损坏和非法引用不允许落盘。
导入分为检查、差异预览、一次事务应用三步。新对象由服务预分配ID,计划失效时作废但不复用;已知baselineKey按注册表复用,外部ID冲突不自动覆盖。不允许把半批内容标成已导入;导入规模超过限制时拒绝并提示拆成明确工作集,不后台分批假装原子完成。
资源先流式写入暂存文件、计算SHA-256、校验类型与尺寸、刷新文件,再以摘要路径存入blobs/<sha256>,最后提交资源记录。事务失败只留下未被引用的blob,不让数据库指向未完成上传。blob一经登记不原地覆盖,修图生成新blob和资源修订;未引用blob只能通过显式整理计划清理。
未保存表单在浏览器内存中保留;离开页面提示保存、放弃或继续。浏览器意外关闭可能丢失未保存输入,因此界面持续区分“未保存”和服务已确认revision;不暗示未确认内容已经持久保存。
迁移与工作区恢复
启动先检查schemaVersion与数据库完整性;未知高版本拒绝写入。迁移前用SQLite备份API生成可读备份并登记版本;迁移在单一事务内执行,失败回滚并只读展示原因,不自动用空库替换。恢复备份要求关闭该工作区写入,检查备份与blob引用,再切换到新的恢复工作区;原目录保留供人工核对。
服务启动需排他工作区锁,锁记录PID、进程启动标识、实例ID。重复启动先连接同实例;残留锁只有确认原进程不存在且数据库未被占用时才移除,不能仅凭时间过久认定锁失效。
校验、编译与资源门禁
校验输入固定为Snapshot + Baseline + SupportRegistry + Profile + ValidatorVersion。输出为排序稳定的{validationId,inputDigests,passed,counts,issues[]};issue含code,severity,recordId,jsonPointer,sourceAnchor,ownerModule,message,suggestedAction。同一输入必须产生同一规则问题清单,按code、recordId、jsonPointer排序;运行时间和任务ID不计入报告语义摘要。
检查顺序固定为Schema→身份与覆盖→引用和形态→获批事实差异→生成与未知缺口→能力兼容→文字与资源→包闭包与用途。前置损坏导致无法检查的项目标为未检查,不能算通过;继续收集可安全定位的问题。
| 层级 | 具体检查 | 阻断范围 |
|---|---|---|
| 身份与覆盖 | 唯一ID、基线key一对一、分类数量、禁用项、普通/神将关系 | 结构不完整不得生成任何交付包;资料包也需保留完整工作集 |
| 事实与生成 | known值与批准基线相同;unknown保留缺口;条件边和排除项原样对账 | 未核可进入资料包;受影响内容及依赖不得进入联调/对局包 |
| 能力 | 提供方与实际版本摘要、契约版本、参数Schema、次数边界、必要表现槽 | 缺失/不兼容/未验收能力阻断运行用途,不执行任意表达式 |
| 文字 | 中文名称与完整效果齐全;绑定路径有效;sourceRulesDigest匹配规则字段摘要 | 规则修改使说明过期时阻断运行用途;不能靠人点忽略通过 |
| 资源 | blob存在且摘要一致,类型和尺寸有效,owner/slot正确,权利凭据与用途合格 | 评审图永不满足运行资源槽;临时资源仅可满足批准的灰盒用途 |
| 可达闭包 | 从模式入口经显式关系队列遍历所有合法可达内容,校验每个引用 | 未知边不得视作不存在;playable覆盖完整模式,integration必须显式列根与缺口 |
包用途编码reference/integration/playable对应资料检查、内部联调和可用对局;质量编码draft/temporary/formal。reference只允许draft,integration允许temporary/formal,playable只允许与交付目标批准记录匹配的temporary/formal。用途与质量独立,不把“可运行”冒充“正式美术通过”。资料包可包含不执行的未知事实和资源槽需求;运行包必须有完整闭包和实际支持。当前缺少真实能力时只能交付reference及工具测试结果,不能给playable打空支持注册表。
资源槽固定为portrait,avatar,hand,detail,unit,icon,frame,animation,audio,staticFallback;各类别必需槽由只读视听profile规定。普通与神将可以引用同一主体blob,但形态标识和效果文字必须各自满足。正式图像与音频的尺寸、透明边、帧与音频格式按PRE-03检查,画面是否清楚、身份是否正确由美术验收记录负责;自动尺寸通过不能自动设置acceptance。
首阶段运行资源接受PNG、WebP、WAV和必要字体;可编辑PSD等源稿只登记为制作来源,不进运行包;SVG仅接受项目维护的受审查图标,禁止导入任意含脚本SVG直接预览。缩略图为派生缓存,按blob摘要+尺寸生成,不覆盖原稿,画质验收查看原图和实际尺寸。
发布前的“编译”只做字段规范化、引用解析、文字参数绑定与资源闭包收集,不翻译或推断未核规则,也不生成技能脚本。运行包保留capabilityId及参数,由实际规则模块解释;序列化顺序不会变成战斗执行顺序。
内容包与发布状态机
可移植包格式
包根只含manifest.json、records.json、relations.json、texts/zh-CN.json、assets.json及blobs/<sha256>。不含SQLite、绝对本机路径、会话密钥或参考游戏原始数据。首次版本不支持压缩包导入,采用明确目录导入以减少解压与路径边界;所有路径固定为POSIX相对路径,拒绝..、盘符、重复规范化路径和链接文件。
manifest字段固定为formatVersion=1,baselineId,baselineDigest,purpose,quality,mode,contentDigest,sourceSnapshotDigest,validatorVersion,supportRequirements,approvalRefs,files;files按相对路径排序,每项含路径、字节数与SHA-256。manifest不列自身,也不含时间、工作区路径、操作人或packageId;packageId=SHA-256(规范化manifest字节),避免自引用摘要。创建时间、操作人和备注只记数据库发布回执,不参与包身份。
规范化JSON只允许null、布尔、安全整数、字符串、数组和对象;对象键按Unicode码点序排序,字符串原样使用统一JSON转义,UTF-8无BOM、无缩进、结尾单个LF。规则上有顺序的数组原样保留;集合数组按契约指定键排序。资源以原字节计算摘要。不同环境的同一输入快照和资源必须生成同一包标识。
发布流程
任务状态为queued → validating → writing → verifying → ready,失败为failed,用户取消为cancelled。ready只表示包完成,不自动选为新局版本。每次发布请求固定snapshotId、profile及operationId;即便编辑器随后继续保存,任务仍使用原快照。
- 在事务中捕获revisionMap与基线、能力声明摘要,创建任务;写入队列随后释放,长任务不占数据库事务。
- Worker校验并写
staging/<jobId>/,每个文件写完刷新、核对字节数与摘要;不足空间或取消立即停止写入,将任务终态写回。 - 全部文件核对后生成manifest并计算packageId;写包完成标记,关闭文件句柄,再在同一磁盘把完整目录移到
packages/<packageId>/。目标已存在时只验证完全相同,不覆盖。 - 重新读取最终目录核对manifest和files,再事务登记packages.ready、回执和任务结果。只有此事务成功后API才返回ready,选用入口才可见。
- 文件完成但登记未成功时,下次启动按build_jobs与完成标记恢复核验;一致则补登记,不一致标invalid并隔离,不自动选用。数据库有ready但文件缺失或摘要不符时同样标invalid,保留所有引用和故障原因。
数据库与文件系统不伪装成一个原子事务;可恢复状态机负责跨边界。取消在最终目录核验与登记前有效;已经ready则返回已完成,不能以取消删除被其他操作引用的包。中断后的staging只能由对应job重建或显式整理,不能被包扫描器当成有效版本。
本地接口与客户端行为
公共协议
API前缀/api/v1,JSON UTF-8。同源会话使用Authorization: Bearer <sessionToken>;所有写请求携带Idempotency-Key(即operationId)及If-Match表示expectedGeneration或专用对象revision。请求摘要包括方法、规范路径、完整body和版本前提;相同操作键不同摘要返回409。
记录写入的If-Match为workspace:<generation>,body中的expectedGeneration必须相同;channel与job分别使用channel:<revision>和job:<revision>。写入服务先查幂等终态,再检查当前前提,故成功后的原请求重试仍返回原结果;未发生提交的失败可使用新operationId重新检查,不能把失败记录当已保存。上传请求摘要包括文件字节摘要与元数据,未完成上传不得登记成功终态。
成功返回{data,meta:{requestId,generation}};失败返回{error:{code,message,details,retryable},meta}。浏览器仅显示简体中文message与可定位条目,内部路径只在制作详情中显示;游戏玩家提示由MOD-03翻译为PRE-02规定的失败文案。分页参数cursor、limit默认50、最大200,排序规则和快照generation写入cursor;代际变化时返回409要求刷新,不用旧cursor拼接不同版本列表。
| 接口 | 输入 | 输出/状态变化 |
|---|---|---|
GET /workspace | 会话 | workspaceId、generation、baseline、能力状态、当前任务;不泄露任意本机路径 |
GET /operations/:id | operationId | 未开始/处理中/成功/失败及已登记结果;网络中断后先查询,未找到不等于操作成功 |
GET /records | kind、name、faction、state、gap、cursor、limit | 目录页和独立覆盖统计;过滤不改变工作集 |
GET /records/:id | id,选填revision | 对应记录、引用摘要、修订;历史revision只读 |
POST /records | kind、body、expectedGeneration | 服务分配id,事务保存revision=1;非法结构400 |
PUT /records/:id | body、expectedRevision、expectedGeneration | 原ID新revision;冲突409,失效引用422 |
POST /plans | type=batch/import/retire/cleanup、输入或uploadId、expectedGeneration | planId、变更与依赖列表、输入摘要;不执行变更 |
POST /plans/:id/apply | planDigest、expectedGeneration | 验证计划未过期后一次事务应用;cleanup另遵循清理状态机 |
POST /uploads | multipart文件,purpose与类型 | uploadId、blobHash、类型及尺寸;流式限额,不自动创建内容关联 |
GET /records/:id/references | direction=in/out、cursor | 真实关系和资源/文字引用,不按名字推断 |
GET /diff | snapshotA、snapshotB | 按字段的增加/修改/停用、资源与文字变化及影响条目 |
POST /snapshots | expectedGeneration | snapshotId、contentDigest、完整revisionMap摘要 |
POST /validations | snapshotId、purpose、quality、mode、显式联调roots | jobId;完成后报告绑定该快照 |
POST /builds | snapshotId、profile、validationId | jobId;验证输入摘要和检查器一致后进入发布任务 |
GET /jobs/:id | jobId | 状态、进度、报告或结果;客户端每秒轮询,结束即停止 |
POST /jobs/:id/cancel | taskRevision | 标记取消请求;不可取消的完成任务返回既有结果 |
GET /packages、GET /packages/:id | purpose或packageId | 已登记包、验证状态、用途、依赖和引用数 |
GET /packages/:id/files/:manifestPath | 包内manifest允许的相对路径 | 只读下载对应字节并给出摘要;命令行export逐项复制到用户指定的新目录,再核验完整性 |
GET /media/:blobHash | 已登记摘要 | 只读媒体流;绑定工作区允许列表,禁止任意磁盘路径 |
POST /channels/:mode/:quality/select | packageId、expectedChannelRevision、runtimeProfile | 核对完整性、用途、模式及实际能力后事务切换;回退同一接口 |
POST /sessions/acquire | ownerId、mode、quality、runtimeProfile、选填指定packageId | 原子读取选用版本并创建session pin,返回只读ContentHandle |
POST /references/reconcile | consumerId、epoch、sequence、完整pins及来源清单 | 对账后更新引用;缺失提供方保持unknown,不能授权清理 |
POST /references/transfer | sessionOwner、持久owner、packageId、expectedSequence | 原子新增save/history/replay pin;删除session pin需确认持久状态已写入 |
POST /references/release | ownerKind、ownerId、packageId、expectedSequence | 来源确认结束且无保存需求时移除该pin;不影响其他引用 |
工作区记录类PUT同样用于资源、文字、关系和缺口,不再为每类设计不同事务语义。上传与导入均通过检查计划再绑定。首次只读批准基线由启动器导入已审阅文件,普通编辑API不得修改批准依据或能力验收记录。
导入计划输入明确format=records/package:records携带记录数组与引用映射;package携带manifest以及{relativePath,uploadId}清单,先验证规范路径、全量文件摘要、包格式和模式,不向工作区解压任意路径。资料包导入只导入草稿映射;外部运行包登记ready须重新检查支持与资源,不接受外部自称已通过的报告。重复导入相同packageId只复核,不新造版本。新局acquire仅接受playable包;integration预览通过只读读取契约,不冒充玩家对局。
错误码至少包含INVALID_INPUT(400)、UNAUTHORIZED(401)、NOT_FOUND(404)、REVISION_CONFLICT(409)、IDEMPOTENCY_CONFLICT(409)、PLAN_STALE(409)、VALIDATION_FAILED(422)、UNSUPPORTED_CAPABILITY(422)、PACKAGE_INCOMPATIBLE(422)、PACKAGE_IN_USE(409)、REFERENCES_UNKNOWN(409)、CAPACITY_LIMIT(413)、DISK_UNAVAILABLE(507)、WORKSPACE_BUSY(503)。错误不触发隐式重抽、版本替换、记录删除或全量重试;前端只对网络中断使用原幂等键查询结果。
五个工作视图
目录显示全名、类别、形态、可用状态及分项覆盖;详情按身份、规则、关系、能力、文字、资源分区,unknown有主责与原文链接。差异页显示提交前后值及影响项,批量提交必须通过计划摘要确认。检查页按错误/警告/未检查分组,点击定位字段,禁止“一键忽略全部”。包页把用途、质量、依赖、选用版本和旧局引用分开显示。
静态预览从同一snapshot渲染头像、手牌、详情和棋子,不执行技能。显示内容版本、临时/正式标记与“未进行对局验证”,支持16/20/24逻辑像素文字、键盘焦点、844宽横屏和1920/1280桌面布局。大正文滚动阅读;不存在以整张PRE-03图片充当可操作工作台的实现路径。
界面网络中断时保留表单与最后确认revision;恢复先查询原operationId结果再决定重试。包生成使用持久job,关浏览器不等于取消任务。保存、批量应用、选用与清理均显示实际结果,重复点击不会重复写入。
指定版本加载、回退与清理
只读加载契约
给后续模块的语言无关输入为{packageId,runtimeProfile:{formatVersions,providerArtifacts,capabilityContracts},purpose,quality};输出ContentHandle={packageId,manifestDigest,recordIndex,textIndex,assetIndex}或明确错误。实际适配器先核对manifest、全部声明文件摘要、模式用途和自身能力,再提供只读索引;不兼容时不能退到默认新包。适配器只读取内容,不生成随机数、不执行技能、不迁移对局状态。
MOD-01提供Node参考读取器及小型契约夹具验证格式;后续引擎语言的适配器由相应模块实现同一格式契约,不要求游戏进程启动Node工作台。包schema版本变更必须提高formatVersion;读取器只接受明确支持的版本,不自动猜测升级字段。
制作仓库与运行仓库隔离
制作工作区保存草稿、来源和发布包;玩家安装后的运行仓库只保存已安装完整包、选用版本、引用登记与操作回执。二者不共用SQLite文件、包目录或可变资源路径,也不通过硬链接共享待清理文件。复制/安装完整包并核验后,运行仓库才登记ready;制作侧删除或改草稿不影响已安装游戏内容。
下面的acquire、回退和清理规则针对一次明确仓库操作。参考实现的仓库类型为authoring/runtime,由启动配置固定root和storeId;HTTP会话只能访问启动时选定的仓库,不能把用户传入的目录当root。运行仓库采用独立catalog.db,包含packages、channels、consumer_registry、package_pins、operations、plans、uploads、audit_events,另有单行catalog_meta(storeId,schemaVersion,generation)与install_jobs(jobId,operationId,packageId,state,error);这些表之间的事务不跨制作数据库。制作侧快照与发布任务也只在自身数据库完成。
runtime模式只开放包导入核验、只读媒体、包查询、channel、引用和清理相关接口,禁止records、草稿编辑、能力验收及编译;GET /workspace返回storeId、类型和catalog generation,基线与revisionMap字段不适用。包导入计划apply返回install jobId,按queued/writing/verifying/ready及failed/cancelled持久化,使用同一jobs查询与取消接口;复用完整文件核验、目录登记及中断恢复步骤,不生成新玩法数据。每次改变包、channel或引用记录都在同事务递增catalog generation,使清理计划能识别并发变化。authoring模式的运行包预览只读,不创建真实玩家session;真实acquire和持久引用属于runtime模式。
MOD-01交付Node本地运行仓库参考实现,供指定包预览和契约验收;游戏引擎侧在MOD-03/MOD-04内实现或嵌入同样的仓库契约。游戏的acquire在自身安装目录与引用库中执行,不调用制作机器或浏览器工作台。HTTP接口表是本模块参考工具的接口;语言无关的输入输出和原子性约束才是后续游戏必须继承的部分。游戏运行不要求Node内容服务常驻。
引用生命周期与竞争处理
选用版本、acquire、pin变更和清理计划提交都通过所属仓库的同一数据库写队列。新局acquire在单事务中读取channel并创建session pin后才返回版本,消除“读到旧包后被清理”的空窗。游戏在自身运行仓库不可用时不能自行创建未登记的新局;已经获取ContentHandle并有pin的原局可以继续只读使用该包。
保存流程先持有session pin,在保存前登记save pin,再写持久保存;写入失败保留session并撤回已确认无保存文件的save pin。成功后才能释放session。恢复先对账保存记录和对应pin,再按保存中packageId加载。关闭游戏、进程失联和暂时无法恢复都不自动释放pin;幽灵引用只会占用空间,不得为省空间冒险删除可能有效旧包。
每个引用提供方用新的epoch开始全量对账,并按递增sequence更新。服务重启将已登记提供方标为未对账;保存/历史/回放提供方全部提交其完整引用清单且确认扫描完成,才可判定全局引用已知。新接入提供方先登记为unknown。对账不解释历史战阵玩法,具体内容迁移仍由MOD-05与MOD-04批准。
回退只通过channel选择接口修改后续新局,不能修改任何ContentHandle、保存内容或pin的packageId。旧包与新程序不兼容时拒绝回退,提供原版本需求;不自动迁移正在进行的局。
可恢复清理
清理分为预览计划和确认执行。提交时在事务中重新检查目标包不被channel、任何pin、活动build或未完成对账保护,并校验计划的channel与引用代际;有变化返回409。标记deleting后acquire拒绝该包,随后把目录移到本工作区trash/<operationId>/;移走失败恢复ready,成功登记deleted及审计。真正删除只处理已核对的trash目标,不递归删除用户给出的任意目录。
制作仓库只管理制作侧预览与导出任务的引用,运行仓库只管理自身session/save/history/replay引用;跨仓库复制不形成一个可远程释放的pin。每次目录移动或删除前解析绝对路径、检查链接与目录身份,并确认目标仍在该storeId的packages或trash边界内;边界不符直接失败,不拼接到另一shell执行。
重启发现deleting时按数据库记录和真实目录位置核对:原目录还在则重新验证后恢复ready;仅trash存在则完成deleted登记;两处都异常则标invalid并保持禁用,等待人工处理。已删除包的发布回执、ID注册表和变更依据永久保留。普通“发布”“回退”均不触发自动清理。
容量、异常与安全边界
以下是工具保护限额,不是玩法上限,也不是已经测得的性能数据。超过限额须停止操作并显示具体项目,不能截断后继续发布;调整限额须重新评估内存、磁盘和测试,不改变内容覆盖要求。
| 项目 | 首阶段限额或策略 | 失败结果 |
|---|---|---|
| 内容与关系 | 每工作区20,000条内容定义、100,000条关系;JSON深度16 | 导入前检查,超过返回CAPACITY_LIMIT |
| 文本与请求 | 单条记录256KiB、单段文字32KiB;普通请求8MiB;批量最多500条 | 不截断中文或半批提交 |
| 图片与上传 | 单文件64MiB、最大8192×8192;源稿仅登记,不进运行包 | 头部与解码限制都检查,异常文件不进入预览 |
| 包 | 元数据总量64MiB、文件总数100,000、单包资源16GiB;流式读写 | 生成前按预计新增量+10%余量检查磁盘,过程中继续处理磁盘错误 |
| 并发 | 1个数据库Worker、1个任务Worker;1个发布任务,最多2个活动上传 | 多余任务排队最多16项,队列满返回503 |
| 超时 | 写锁最多5秒;普通API30秒;长任务轮询;无进度10分钟标失败 | 不重放未确认的变更,保留任务和快照 |
| 工作区容量 | blob及包可增长,达到用户磁盘可用空间门槛时停止新写入 | 不自动删旧包或保存,展示可清理但未执行的计划 |
关闭工作台服务先停止接收新写入并完成当前短事务,再关闭Worker;长任务记为中断待核验。访问被拒、磁盘满、文件被占用或摘要不符分别保留原状态与可定位原因,不用无限重试掩盖失败。数据库损坏时只读诊断并要求从已验证备份恢复,不创建同名空库。
计划文件与职责
下列路径在架构批准后按工作项建立。本次不新增源码、Schema实现、数据包或数据库,不引用旧实现作为设计依据。
| 计划路径 | 职责/允许依赖 |
|---|---|
tools/content-workbench/package.json、pnpm-lock.yaml、tsconfig.json | 独立工具依赖、精确版本与严格类型设置;不改变文档站依赖 |
tools/content-workbench/contracts/ | 内容、资源、基线、能力、API和manifest Schema;不得依赖UI或数据库 |
tools/content-workbench/src/domain/ | 身份、事实状态、引用图、覆盖、能力和资源门禁、规范化编码;纯函数 |
tools/content-workbench/src/application/ | 保存、批量计划、快照、校验、发布、选用、引用与清理用例;只依赖领域与存储接口 |
tools/content-workbench/src/storage/ | SQLite表、迁移、修订、blob、包目录与备份;不包含玩法判断 |
tools/content-workbench/src/server/ | HTTP、会话、输入限额、静态资源与错误映射;路由调用用例 |
tools/content-workbench/src/workers/ | 数据库串行队列及校验/打包任务,消息只传不可变契约 |
tools/content-workbench/src/client/ | 五个工作视图、表单、差异、静态预览;不自行写文件或批准能力 |
tools/content-workbench/src/reader/ | 引擎无关包格式的Node参考读取器、摘要与兼容检查 |
tools/content-workbench/src/catalog/ | 独立运行仓库参考实现,packages/channels/pins事务与安装校验;不能访问草稿数据库 |
tools/content-workbench/src/cli/ | 启动、检查、生成、导出与诊断;通过本地服务共用幂等语义 |
tools/content-workbench/baselines/ | 经内容负责人核对的批准基线快照、项目ID登记导出;不保存历史工程或客户端数据 |
tools/content-workbench/tests/ | 小型契约、领域、存储与浏览器夹具;测试能力声明不进入分发资源 |
.gitignore | 补充本地工作区、密钥、包、缓存与备份的忽略规则;不忽略基线和源码 |
运行数据workspace.db、blobs/、packages/、staging/、trash/和backups/均位于本地工作区。只允许明确用户选择的文件通过上传导入;运行服务不扫描客户端日志,不从仓库外备份恢复旧游戏实现。
实施顺序与工作项
工作项只表示架构批准后的计划,全部为待完成;同一功能跨责任方分别列项。后台表示本地内容处理服务,不表示新增在线服务。美术负责资源关联与预览模板的正确性,不在本模块重画全部角色。
| 工作项 | 交付结果 | 依赖与验收 | 状态 |
|---|---|---|---|
| MOD-01-F001-BACKEND-W001 【后台】建立身份契约与基线建档 | Schema、稳定ID、基线导入、全量覆盖及修订存储 | 架构批准后开始;覆盖与重复身份检查 | ⚪ 待完成 |
| MOD-01-F001-CLIENT-W001 【客户端】实现内容目录与身份详情 | 筛选、分类统计、同名与形态辨识 | 依赖F001后台契约;零结果与完整身份可读 | ⚪ 待完成 |
| MOD-01-F002-BACKEND-W001 【后台】实现编辑事务与差异计划 | revision冲突、幂等、批量原子提交、引用与审计 | 依赖F001;冲突不覆盖、失败不半提交 | ⚪ 待完成 |
| MOD-01-F002-CLIENT-W001 【客户端】实现表单、差异与批量确认 | 保留未保存输入、比较冲突、计划确认 | 依赖F002接口;取消、过期计划、重试语义一致 | ⚪ 待完成 |
| MOD-01-F003-BACKEND-W001 【后台】实现资源和文字关联 | blob入库、权利来源、文字版本与资源用途校验 | 依赖F001/F002;无空引用或过期说明放行 | ⚪ 待完成 |
| MOD-01-F003-ART-W001 【美术】校核样例资源与复用关联 | 十势力、普通/神将、初/高阶及动态来源的关联样例 | 依PRE-03规格;评审图不冒充正式资源 | ⚪ 待完成 |
| MOD-01-F004-BACKEND-W001 【后台】实现缺口、能力与闭包检查 | 固定输入报告、真实支持声明、用途门禁 | 依赖F001/F003;未知与测试能力不能进入运行包 | ⚪ 待完成 |
| MOD-01-F005-CLIENT-W001 【客户端】实现检查与静态预览 | 错误定位、多位置预览、三档文字和原图查看 | 依赖F003/F004;明确未进行对局验证 | ⚪ 待完成 |
| MOD-01-F005-ART-W001 【美术】验收静态预览身份与阅读 | 检查人物、全名、形态、势力色与实际尺寸 | 依赖F005客户端;不将效果图整页嵌入冒充工具 | ⚪ 待完成 |
| MOD-01-F006-BACKEND-W001 【后台】实现快照、编译与发布状态机 | 规范包、摘要、任务持久化与恢复核验 | 依赖F004;同输入同包,失败不改有效版本 | ⚪ 待完成 |
| MOD-01-F006-CLIENT-W001 【客户端】实现内容包视图与任务反馈 | 用途、质量、版本差异、检查与发布结果 | 依赖F006后台;重复点击与关页不重复发布 | ⚪ 待完成 |
| MOD-01-F007-BACKEND-W001 【后台】实现只读加载和引用登记 | ContentHandle、兼容判断、原子acquire、pin对账 | 依赖F006;原局不换版本、引用不明保持保护 | ⚪ 待完成 |
| MOD-01-F007-CLIENT-W001 【客户端】接入指定包预览与兼容提示 | 按packageId阅读,区分缺失、损坏与不兼容 | 依赖F007后台;游戏恢复集成由MOD-03/04验收 | ⚪ 待完成 |
| MOD-01-F008-BACKEND-W001 【后台】实现回退和可恢复清理 | channel切换、清理计划、竞争复核与deleted回执 | 依赖F007;有引用包不得清理,失败保留旧选择 | ⚪ 待完成 |
| MOD-01-F008-CLIENT-W001 【客户端】实现回退与清理确认 | 明确影响新局、引用详情、计划变化与结果 | 依赖F008后台;不提供强制覆盖原局操作 | ⚪ 待完成 |
顺序为F001→F002→F003→F004→F005/F006→F007→F008。每阶段同时完成对应轻量测试;完整玩法能力、全量正式资源与真实对局恢复分别由承接模块提供,不能把这些外部结果虚报成本模块已完成。
自动检查与人工验收
以下为实施后的计划,不表示本次已经运行游戏或存储测试。自动测试使用独立临时工作区和小型固定夹具,不操作真实玩家保存或工作包;不运行压力、性能、全量对局或真实崩溃/故障注入。磁盘失败、进程中断位置与并发顺序由纯状态机和替代文件接口的定向单元测试验证;需要真实故障实验时另行取得当次许可。
| 对应功能 | 自动检查 | 人工验收 |
|---|---|---|
| F001 | 稳定ID不因名称改变;重复baselineKey、配对缺项、分类计数、unknown/0区分 | 用同名与普通/神将样例从目录定位正确身份;全量建档逐项对账 |
| F002 | 两个旧revision竞争只成功一个;同幂等键重试只写一次;批量某项非法全部回滚 | 保留未保存输入,冲突前后值可读,过期计划要求重新检查 |
| F003 | blob摘要、非法路径、缺中文、过期说明、资源用途、缺权利凭据被阻断 | 确认刘备与以德励士分开、麴义两形态关联正确,原图可读 |
| F004 | 测试能力不能进运行包;真实提供物不匹配被拒;未知边不从闭包消失;问题排序固定 | 内容负责人能从错误定位原字段与主责,不出现一键跳过门禁 |
| F005 | snapshot一致、不同使用位置同名同规则、纯文本防注入、焦点与弹层关闭 | 三种布局和三档文字,临时/正式、未验证对局标记明显 |
| F006 | 规范化向量、跨进程同输入同摘要;逐阶段失败恢复;半包不可见;取消不删ready包 | 确认包用途、变化、范围与实际结果,关页后可查看同一任务 |
| F007 | acquire与清理串行顺序两种情况;pin不自动过期;缺包不回退;恢复固定packageId | 对接MOD-04后更新内容仍保持原局规则、文字与资源 |
| F008 | channel CAS、旧包不兼容、引用unknown拒删、deleting恢复、失败不改原channel | 只影响新局的说明明确,清理前完整显示影响,旧局记录保持 |
本模块验收先交付工具和资料包,功能进度仅在对应结果通过后更新;实际完整对局包需要后续能力与内容核查闭合。全量资料校验属于有界静态检查,不能用它宣称规则效果、画质或对局稳定性全部验收通过。
主要风险与批准边界
风险主要是未知资料被默认值掩盖、支持声明与实际能力脱节、图像可加载被误认为正式质量、跨文件与数据库写入中断,以及旧局引用尚未对账却清理内容。分别由事实状态、实际提供物摘要校验、资源用途与人工美术验收、发布恢复状态机、保守pin与清理复核控制。
本章已给出八项功能的契约、流程、路径、责任与验收,等待整章架构批准。批准前所有架构工作项保持待完成,产品功能保持待开发;本次不创建运行时代码或安装新工具依赖。若实施必须改变存储、包格式、兼容或原局保护规则,先修订本章并重新评审。