<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>发布 - tag - 沐木</title><link>https://oldletter.cn/tags/%E5%8F%91%E5%B8%83/</link><description>发布 - tag - 沐木</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Sat, 11 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://oldletter.cn/tags/%E5%8F%91%E5%B8%83/" rel="self" type="application/rss+xml"/><item><title>测评平台（三）：批量保存、草稿试答与可追溯发布</title><link>https://oldletter.cn/posts/assessment-platform-03-draft-preview-publish/</link><pubDate>Sat, 11 Jul 2026 00:00:00 +0000</pubDate><author>mumu</author><guid>https://oldletter.cn/posts/assessment-platform-03-draft-preview-publish/</guid><description><![CDATA[<blockquote>
<p>系列导航：<a href="/posts/assessment-platform-01-resource-release-task/" rel="">5</a> | <a href="/posts/assessment-platform-02-multi-resource-runtime/" rel="">6</a> | <a href="/posts/assessment-platform-03-draft-preview-publish/" rel="">7</a></p></blockquote>
<p>配置后台需要允许半成品存在。题目编辑、评分配置和报告模板通常分几次完成，保存过程还会遇到浏览器刷新和并发修改。预览要尽量接近正式作答，发布后又必须冻结内容。把这几个动作压进一个 <code>status</code> 字段，容易出现草稿进入统计或审核后内容仍被修改。</p>
<h2 id="草稿状态和发布状态分开">草稿状态和发布状态分开</h2>
<p>草稿版本可以有 <code>editing</code>、<code>ready_for_review</code>、<code>rejected</code>；发布包只有 <code>building</code>、<code>published</code>、<code>retired</code>。试答引用草稿版本，正式任务只引用已发布的 <code>AssessmentRelease</code>。发布状态改变后，原发布包不再接受编辑。</p>
<div class="mermaid" id="id-2" data-mermaid-definition="c3RhdGVEaWFncmFtLXYyCiAgICBbKl0gLS0&#43;IEVkaXRpbmcKICAgIEVkaXRpbmcgLS0&#43;IFJldmlldzog5o&#43;Q5Lqk5a6h5qC4CiAgICBSZXZpZXcgLS0&#43;IEVkaXRpbmc6IOmps&#43;WbnuS/ruaUuQogICAgUmV2aWV3IC0tPiBCdWlsZGluZzog5a6h5qC46YCa6L&#43;HCiAgICBCdWlsZGluZyAtLT4gUHVibGlzaGVkOiDlv6vnhafkuI7moKHpqozmiJDlip8KICAgIEJ1aWxkaW5nIC0tPiBSZXZpZXc6IOeUn&#43;aIkOWksei0pQogICAgUHVibGlzaGVkIC0tPiBSZXRpcmVkOiDmlrDljIXmm7/mjaIKICAgIFB1Ymxpc2hlZCAtLT4gVHJpYWw6IOiNieeov&#43;ivleetlOWPquivu&#43;WPkeW4g&#43;W/q&#43;eFpw==">stateDiagram-v2
    [*] --&gt; Editing
    Editing --&gt; Review: 提交审核
    Review --&gt; Editing: 驳回修改
    Review --&gt; Building: 审核通过
    Building --&gt; Published: 快照与校验成功
    Building --&gt; Review: 生成失败
    Published --&gt; Retired: 新包替换
    Published --&gt; Trial: 草稿试答只读发布快照</div><p>图中的 <code>Trial</code> 不是发布包状态，它表示运行入口。试答可以使用草稿的 FormDSL，但评分和答案解析仍由服务端完成，避免页面自己解释规则。</p>
<h2 id="批量保存的边界">批量保存的边界</h2>
<p>90 道题的工作台不适合每次输入都提交整份 JSON。前端发送带路径的补丁：题目标识、字段路径、客户端序号和内容摘要。服务端校验路径属于当前草稿版本，再按版本号合并。两个窗口同时编辑时，后到的旧版本返回冲突，调用方选择重新加载或覆盖，不能静默丢失另一方修改。</p>
<p>批量保存接口返回服务端版本、已接受项和拒绝项。部分成功时，页面逐项显示错误，不能把整个请求显示为“保存失败”后让用户猜测哪些题已经写入。服务端保存时限制数组深度、字符串长度和单次补丁数量，防止一条请求耗尽解析资源。</p>
<p>草稿复制使用新版本 ID，复制源、操作人和时间进入审计。发布包不能通过改数据库指针“变成”另一个版本，所有关系用显式外键和状态校验保护。</p>
<h2 id="预览和正式作答共用解析器">预览和正式作答共用解析器</h2>
<p>试答页面调用和正式作答相同的 FormDSL 校验、可见性计算和 <code>FrozenFormAnswerResolver</code>。NUMBER 类型从 <code>numberValue</code> 解析，不能从展示字符串 <code>rawValue</code> 评分。试答结果标记为临时，不写入正式参与者报告和统计。</p>
<p>预览数据使用合成参与者 ID，提交令牌只在开发或测试范围有效。服务端返回给浏览器的快照递归移除 <code>score</code>、<code>scoreRangeJson</code>、标准答案和其他评分字段，键名大小写变化也要过滤。清理逻辑不能只处理顶层对象，否则嵌套报告规则会泄漏。</p>
<h2 id="发布是一个事务边界">发布是一个事务边界</h2>
<p>发布请求先确认草稿版本仍是审核通过状态，再编译 FormDSL、检查引用、写入题目快照和报告配置，计算 checksum，末步插入 <code>AssessmentRelease</code>。事务提交前，运行入口看不到这条发布记录。发布完成后，运行入口只接受 <code>release_id</code>，不接受前端传来的任意版本号。</p>]]></description></item><item><title>工程正确性（四）：构建成功不等于交付物可用</title><link>https://oldletter.cn/posts/engineering-correctness-04-coordinate-precision/</link><pubDate>Sat, 11 Jul 2026 00:00:00 +0000</pubDate><author>mumu</author><guid>https://oldletter.cn/posts/engineering-correctness-04-coordinate-precision/</guid><description><![CDATA[<blockquote>
<p>系列导航：<a href="/posts/engineering-correctness-01-green-tests-can-be-wrong/" rel="">11</a> | <a href="/posts/engineering-correctness-02-crypto-interoperability/" rel="">12</a> | <a href="/posts/engineering-correctness-03-database-migrations/" rel="">13</a> | <a href="/posts/engineering-correctness-04-coordinate-precision/" rel="">14</a></p></blockquote>
<p>编译器显示成功，只能证明源码在当前环境生成了产物。用户安装的是 JAR、npm 包或容器，真正的验收对象是这些文件能否被目标运行时加载，Native 资源是否随包分发，文档示例是否使用了正确入口。发布链路如果只跑单元测试，问题会在上传后才出现。</p>
<p>GMKit 的发布整理说明了这条边界：Java 的 <code>gmkit-sm9</code> 需要把多平台 Native 运行时装入同一个 JAR，TypeScript 包还要检查 tarball 内容和生产依赖。源码测试通过并不能证明打出来的包包含这些资源。</p>
<div class="mermaid" id="id-2" data-mermaid-definition="Zmxvd2NoYXJ0IFRECiAgICBzdWJncmFwaCBCdWlsZFvmupDnoIHpqozor4HkuI7mnoTlu7pdCiAgICAgICAgZGlyZWN0aW9uIExSCiAgICAgICAgU291cmNlW&#43;a6kOeggeS4juWQkemHj10gLS0&#43;IFZlcmlmeVvmtYvor5XkuI4gcGFyaXR5XSAtLT4gUGFja2FnZVvmnoTlu7rliLblk4FdCiAgICBlbmQKICAgIHN1YmdyYXBoIEF1ZGl0W&#43;WItuWTgeWuoeiuoV0KICAgICAgICBkaXJlY3Rpb24gTFIKICAgICAgICBJbnNwZWN0W&#43;WMheWGheWuueS4jiBjaGVja3N1bV0gLS0&#43;IEluc3RhbGxb5bmy5YeA546v5aKD5a6J6KOFXSAtLT4gU21va2Vb6L&#43;Q6KGM5pe2IHNtb2tlXQogICAgZW5kCiAgICBzdWJncmFwaCBSZWxlYXNlW&#43;WPkeW4g10KICAgICAgICBkaXJlY3Rpb24gTFIKICAgICAgICBQdWJsaXNoW&#43;WPkeW4g&#43;S7k&#43;W6k10KICAgIGVuZAogICAgUGFja2FnZSAtLT4gSW5zcGVjdAogICAgU21va2UgLS0&#43;IFB1Ymxpc2gKICAgIEluc3BlY3QgLS0&#43;fOe8uui1hOa6kOaIluWkmuS9meaWh&#43;S7tnwgUmVqZWN0W&#43;mYu&#43;aWreWPkeW4g10=">flowchart TD
    subgraph Build[源码验证与构建]
        direction LR
        Source[源码与向量] --&gt; Verify[测试与 parity] --&gt; Package[构建制品]
    end
    subgraph Audit[制品审计]
        direction LR
        Inspect[包内容与 checksum] --&gt; Install[干净环境安装] --&gt; Smoke[运行时 smoke]
    end
    subgraph Release[发布]
        direction LR
        Publish[发布仓库]
    end
    Package --&gt; Inspect
    Smoke --&gt; Publish
    Inspect --&gt;|缺资源或多余文件| Reject[阻断发布]</div><h2 id="先确定交付清单">先确定交付清单</h2>
<p>Java 发布清单写明 group、artifact、版本、依赖、服务文件和 Native 路径。<code>gmkit-sm9</code> 的 JAR 中应有 <code>META-INF/gmkit/sm9-native.properties</code>、各平台运行时和摘要文件，加载逻辑按当前平台选择资源并校验 checksum。Native 库缺失时启动要给出明确错误，不能回退到一个“未实现但返回成功”的空实现。</p>]]></description></item></channel></rss>