[{"data":1,"prerenderedAt":525},["ShallowReactive",2],{"rainink-post-mes-master-data-state-machine":3,"rainink-content-index":336},{"id":4,"title":5,"body":6,"category":319,"cover":320,"date":321,"description":322,"draft":323,"extension":324,"featured":323,"lang":320,"meta":325,"navigation":326,"order":320,"path":327,"period":320,"pinned":323,"repo":320,"seo":328,"slug":320,"stack":320,"stem":329,"tags":330,"thumb":320,"toc":326,"updated":320,"__hash__":335},"content\u002Fposts\u002Fmes-master-data-state-machine.md","主数据状态机不是字段，是一份跨模块公共契约",{"type":7,"value":8,"toc":309},"minimark",[9,16,21,24,27,30,43,51,54,57,146,153,156,159,173,176,179,182,188,194,200,204,207,223,226,230,233,244,247,261,264,268,279,282,300,303,306],[10,11,12],"blockquote",{},[13,14,15],"p",{},"主数据需求设计里最容易返工的部分，不是字段表，而是状态机。",[17,18,20],"h2",{"id":19},"一开始的状态机是抄出来的","一开始的状态机是\"抄\"出来的",[13,22,23],{},"做 MES 的主数据需求设计时，物料、客户、供应商、BOM 这几类对象的结构高度相似，最省事的做法是先写一份骨架，再逐份复制、改改字段。",[13,25,26],{},"复制本身没错，问题在于复制之后没有逐份校验差异。状态迁移表、允许操作矩阵、红线约束、验收项这四处，是四个不同的人（或者同一个人的四个不同时段）写出来的，一旦某份文档只改了其中两处，剩下两处就变成了自相矛盾的证据。",[13,28,29],{},"最典型的三处矛盾：",[31,32,33,37,40],"ul",{},[34,35,36],"li",{},"「关闭」和「停用」被当成同一个状态用；",[34,38,39],{},"删除允许范围的矩阵和正文对不上；",[34,41,42],{},"变更被驳回之后，状态应该回到哪里没有定义。",[13,44,45,46,50],{},"这三处都不是笔误，是",[47,48,49],"strong",{},"语义漂移","：同一个词在不同主数据里承担了不同的含义。",[17,52,53],{"id":53},"六态只是起点",[13,55,56],{},"最终定下来的统一状态机是六态：",[58,59,60,76],"table",{},[61,62,63],"thead",{},[64,65,66,70,73],"tr",{},[67,68,69],"th",{},"状态",[67,71,72],{},"含义",[67,74,75],{},"谁触发",[77,78,79,91,102,113,124,135],"tbody",{},[64,80,81,85,88],{},[82,83,84],"td",{},"草稿",[82,86,87],{},"已创建未提交，不参与任何引用校验",[82,89,90],{},"创建人",[64,92,93,96,99],{},[82,94,95],{},"审批中",[82,97,98],{},"已提交，审批流实例运行中",[82,100,101],{},"提交动作",[64,103,104,107,110],{},[82,105,106],{},"审批通过",[82,108,109],{},"生效，可被业务单据引用",[82,111,112],{},"审批完成",[64,114,115,118,121],{},[82,116,117],{},"已驳回",[82,119,120],{},"审批未通过，可修改后重提",[82,122,123],{},"审批人",[64,125,126,129,132],{},[82,127,128],{},"关闭",[82,130,131],{},"未审批通过就放弃，等价于\"这件事不做了\"",[82,133,134],{},"创建人或管理员",[64,136,137,140,143],{},[82,138,139],{},"撤回",[82,141,142],{},"提交后、审批完成前由发起人收回",[82,144,145],{},"发起人",[13,147,148,149,152],{},"然后追加第七个：",[47,150,151],{},"停用（INACTIVE）","，只对审批通过的对象开放，表示\"这条数据还在，但不能再被新单据引用\"。",[13,154,155],{},"加这个态的理由很直接：一个已经上线的物料，发现录错了，直接删会破坏历史单据的引用完整性；直接改会污染已发生业务。唯一正确的动作是让它退出流转，但保留可查。",[13,157,158],{},"这里必须把「关闭」和「停用」彻底分开：",[31,160,161,167],{},[34,162,163,166],{},[47,164,165],{},"关闭 = 未生效前的放弃","，这条记录从此不参与业务；",[34,168,169,172],{},[47,170,171],{},"停用 = 生效后的退场","，历史引用依然有效，新引用被阻断。",[13,174,175],{},"一个状态承载互斥语义，后面所有矩阵都会写错。",[17,177,178],{"id":178},"不是所有主数据都该有停用",[13,180,181],{},"把这个结论无差别推广到所有对象，就会出问题。两类例外：",[13,183,184,187],{},[47,185,186],{},"BOM 不启用停用。"," BOM 的\"失效\"应该由生效区间和 ECN（工程变更）承担。一个 BOM 版本的退场，本质是某一天之后不再适用，这是一个时间语义，不是一个状态语义。给它加停用，等于允许\"某个 BOM 既在生效区间内，又处于停用\"这种自相矛盾的数据存在。",[13,189,190,193],{},[47,191,192],{},"AVL（合格供应商名录）由名录状态承担。"," 名录本身的准入\u002F退出就是它的业务状态，再叠一层停用只会让\"这家供应商对这物料还能不能供货\"变成两个开关的组合判断。",[13,195,196,197],{},"判断标准可以简化成一句话：",[47,198,199],{},"如果这个对象的失效可以用时间来精确表达，就不要用状态表达。",[17,201,203],{"id":202},"引用保护只在审批通过态开口","引用保护：只在审批通过态开口",[13,205,206],{},"状态机定完，真正决定系统能不能用的，是引用规则：",[208,209,210,217,220],"ol",{},[34,211,212,213,216],{},"业务单据",[47,214,215],{},"只能引用审批通过态","的主数据，停用态不可选；",[34,218,219],{},"已被引用的主数据要改关键字段，必须二次确认，并留下影响面提示；",[34,221,222],{},"要停用一条已被引用的主数据，必须先清掉在途引用，否则阻断。",[13,224,225],{},"这三条如果不写进红线约束（R-MD 系列），实现阶段一定会被\"先让功能跑起来\"这个理由绕过去。绕过去之后的代价，是数据出问题以后只能人工修库。",[17,227,229],{"id":228},"四方一致落稿前的强制动作","四方一致：落稿前的强制动作",[13,231,232],{},"写完一版文档之后，我固定做一遍交叉核对，核对的不是文字，是四张表之间的一致性：",[234,235,240],"pre",{"className":236,"code":238,"language":239},[237],"language-text","状态迁移表  ↔  操作矩阵  ↔  红线约束(R-MD*)  ↔  验收项(A*)\n","text",[241,242,238],"code",{"__ignoreMap":243},"",[13,245,246],{},"任何一处新增状态、新增动作、放宽条件，都要同时问四个问题：",[31,248,249,252,255,258],{},[34,250,251],{},"迁移表里有没有这条边？",[34,253,254],{},"操作矩阵里，哪个角色在哪个状态下能做这个动作？",[34,256,257],{},"红线约束需不需要加一条？",[34,259,260],{},"验收项怎么验证它？",[13,262,263],{},"这一步花的时间，远少于开发阶段发现矛盾后回炉的时间。",[17,265,267],{"id":266},"另一类坑编号体系会互相污染","另一类坑：编号体系会互相污染",[13,269,270,271,274,275,278],{},"同一份文档里同时存在两套 Q 开头的编号——一套是质检节点 ",[241,272,273],{},"Q01~Q05","（来料 IQC、首检、巡检 IPQC、完工 FQC、出货 OQC），另一套是待确认问题 ",[241,276,277],{},"Q1~Q10","。",[13,280,281],{},"结果就是引用句子互相指错：本来要说\"免检策略待确认\"，实际指到了质检节点；本来要引功能点 F23（数据分发接口），写成了 F22（追溯查询）。",[13,283,284,285,288,289,292,293,292,296,299],{},"规则是：",[47,286,287],{},"一份文档里只保留一套编号语义","。引用另一套体系时，必须写全名（\"质检节点 Q01 来料 IQC\"），不能只写编号。落稿后再用脚本扫一遍所有 ",[241,290,291],{},"F*"," ",[241,294,295],{},"A*",[241,297,298],{},"Q*"," 引用，确认每个编号都真实存在且指向正确条目。",[17,301,302],{"id":302},"小结",[13,304,305],{},"状态机看起来是几个枚举值，实际上是主数据、业务单据、审批流三方共享的契约。它一旦漂移，下游全部错位。",[13,307,308],{},"所以值得在文档阶段较真：把互斥语义拆开、把时间语义还给时间、把引用规则写成硬约束，然后在落稿前做一遍四方交叉核对。这几步不做，省下的时间会在联调时加倍还回来。",{"title":243,"searchDepth":310,"depth":310,"links":311},2,[312,313,314,315,316,317,318],{"id":19,"depth":310,"text":20},{"id":53,"depth":310,"text":53},{"id":178,"depth":310,"text":178},{"id":202,"depth":310,"text":203},{"id":228,"depth":310,"text":229},{"id":266,"depth":310,"text":267},{"id":302,"depth":310,"text":302},"tech",null,"2026-10-19","MES 主数据状态机的设计取舍：六态加停用怎么定、四方口径为什么必须交叉核对，以及 BOM 与编号体系为什么会互相污染。",false,"md",{},true,"\u002Fposts\u002Fmes-master-data-state-machine",{"title":5,"description":322},"posts\u002Fmes-master-data-state-machine",[331,332,333,334],"MES","领域建模","需求设计","状态机","ITJtnRw2cB33YANYYhNhLmEfbdsyWy2G9XUKEmX7pEs",{"all":337,"posts":460,"essays":485,"projects":486,"tags":493},[338,355,369,380,393,403,407,419,430,449],{"title":339,"description":340,"path":341,"slug":342,"category":319,"categoryLabel":343,"kind":344,"tags":345,"date":350,"updated":351,"minutes":352,"pinned":326,"draft":323,"order":353,"stack":354,"period":243,"repo":243,"thumb":243},"用 Nuxt 4 + Nuxt Content 搭一个纯静态个人站","站点骨架的取舍：内容目录怎么定、SSG 怎么预渲染、设计令牌怎么从原型抽成一套 CSS 变量，以及首屏主题闪烁的解法。","\u002Fposts\u002Fnuxt4-static-site","nuxt4-static-site","技术","post",[346,347,348,349],"Nuxt","Nuxt Content","SSG","前端工程化","2026-10-12","2026-10-18",5,0,[],{"title":356,"description":357,"path":358,"slug":359,"category":319,"categoryLabel":343,"kind":344,"tags":360,"date":366,"updated":243,"minutes":367,"pinned":323,"draft":323,"order":353,"stack":368,"period":243,"repo":243,"thumb":243},"国内服务器部署的十个坑：从镜像源到 certbot","从镜像源到 certbot 的十个真实故障：nvm 拉不动、目录权限、空库迁移、Host 头命中错站、证书续期与备案期验收。","\u002Fposts\u002Fcn-server-deploy-pitfalls","cn-server-deploy-pitfalls",[361,362,363,364,365],"部署","踩坑","Nginx","PowerShell","运维","2026-10-22",6,[],{"title":370,"description":371,"path":372,"slug":373,"category":319,"categoryLabel":343,"kind":344,"tags":374,"date":377,"updated":243,"minutes":378,"pinned":323,"draft":323,"order":353,"stack":379,"period":243,"repo":243,"thumb":243},"一台腾讯云轻量跑起多站点：从裸机到 HTTPS 的实际路径","一台 2C4G 腾讯云轻量同时承载后台、接口与静态站：备案施工顺序、Nginx 多 server 块、certbot 签证书与上线验收。","\u002Fposts\u002Fmulti-site-deploy-to-https","multi-site-deploy-to-https",[361,363,375,365,376],"HTTPS","腾讯云","2026-10-21",7,[],{"title":381,"description":382,"path":383,"slug":384,"category":319,"categoryLabel":343,"kind":344,"tags":385,"date":390,"updated":243,"minutes":391,"pinned":323,"draft":323,"order":353,"stack":392,"period":243,"repo":243,"thumb":243},"底座能力的边界：什么该沉进数据字典，什么必须单独立表","B 端底座能力的边界判断：哪些配置该沉进数据字典、哪些必须单独立表，以及单级字典不升级成树的理由。","\u002Fposts\u002Fbase-dictionary-boundary","base-dictionary-boundary",[386,387,388,389],"B端","架构设计","数据字典","super-admin","2026-10-20",4,[],{"title":394,"description":395,"path":396,"slug":397,"category":319,"categoryLabel":343,"kind":344,"tags":398,"date":390,"updated":243,"minutes":391,"pinned":323,"draft":323,"order":353,"stack":402,"period":243,"repo":243,"thumb":243},"先画全链路，再切分期：我做 ERP\u002FWMS\u002FMES\u002FQC 需求的方法","ERP\u002FWMS\u002FMES\u002FQC 需求方法：先用编号体系与硬闸点把端到端流程关进文档，再按里程碑切成可交付的分期。","\u002Fposts\u002Ferp-wms-mes-qc-requirement-method","erp-wms-mes-qc-requirement-method",[331,399,400,333,401],"ERP","WMS","流程设计",[],{"title":5,"description":322,"path":327,"slug":404,"category":319,"categoryLabel":343,"kind":344,"tags":405,"date":321,"updated":243,"minutes":391,"pinned":323,"draft":323,"order":353,"stack":406,"period":243,"repo":243,"thumb":243},"mes-master-data-state-machine",[331,332,333,334],[],{"title":408,"description":409,"path":410,"slug":411,"category":319,"categoryLabel":343,"kind":344,"tags":412,"date":417,"updated":243,"minutes":352,"pinned":323,"draft":323,"order":353,"stack":418,"period":243,"repo":243,"thumb":243},"B 端后台底座：把权限、菜单、请求收敛成三层","后台系统做多了会发现，真正需要沉淀的不是页面，而是权限模型、动态菜单和请求层这三块底座。","\u002Fposts\u002Fvue3-admin-base","vue3-admin-base",[413,414,415,416],"Vue","后台","架构","权限","2026-09-24",[],{"title":420,"description":421,"path":422,"slug":423,"category":319,"categoryLabel":343,"kind":344,"tags":424,"date":428,"updated":243,"minutes":352,"pinned":323,"draft":323,"order":353,"stack":429,"period":243,"repo":243,"thumb":243},"工作流引擎实操笔记：节点、流转与幂等","从状态机到可配置流程：一次工作流引擎落地中关于节点模型、流转条件、幂等与补偿的实操记录。","\u002Fposts\u002Fworkflow-engine-notes","workflow-engine-notes",[425,426,415,427],"工作流","后端","幂等","2026-08-30",[],{"title":431,"description":432,"path":433,"slug":389,"category":434,"categoryLabel":435,"kind":434,"tags":436,"date":437,"updated":243,"minutes":438,"pinned":323,"draft":323,"order":438,"stack":439,"period":447,"repo":243,"thumb":448},"Super Admin 后台底座","一套可复用的 B 端后台前端底座：权限三层收敛、动态菜单、请求层封装与通用表格表单组件，新项目接入只需替换业务页面。","\u002Fprojects\u002Fsuper-admin","project","项目",[413,414,349],"2026-05-20",1,[440,441,442,443,444,445,446],"Vue 3","TypeScript","Vite","Pinia","Antdv Next","Node.js","Next.js","2026 年至今","SA",{"title":450,"description":451,"path":452,"slug":453,"category":434,"categoryLabel":435,"kind":434,"tags":454,"date":351,"updated":243,"minutes":438,"pinned":323,"draft":323,"order":455,"stack":456,"period":458,"repo":243,"thumb":459},"雨墨 RainInk 本站","你正在看的这个站：Nuxt 4 + Nuxt Content 的纯静态个人站，手写设计令牌、无评论无用户输入、产物可直接静态托管。","\u002Fprojects\u002Frainink-site","rainink-site",[346,348,349],3,[457,440,347,441],"Nuxt 4","2026 年 10 月","RI",[461,464,467,470,473,476,479,482],{"title":339,"description":340,"path":341,"slug":342,"category":319,"categoryLabel":343,"kind":344,"tags":462,"date":350,"updated":351,"minutes":352,"pinned":326,"draft":323,"order":353,"stack":463,"period":243,"repo":243,"thumb":243},[346,347,348,349],[],{"title":356,"description":357,"path":358,"slug":359,"category":319,"categoryLabel":343,"kind":344,"tags":465,"date":366,"updated":243,"minutes":367,"pinned":323,"draft":323,"order":353,"stack":466,"period":243,"repo":243,"thumb":243},[361,362,363,364,365],[],{"title":370,"description":371,"path":372,"slug":373,"category":319,"categoryLabel":343,"kind":344,"tags":468,"date":377,"updated":243,"minutes":378,"pinned":323,"draft":323,"order":353,"stack":469,"period":243,"repo":243,"thumb":243},[361,363,375,365,376],[],{"title":381,"description":382,"path":383,"slug":384,"category":319,"categoryLabel":343,"kind":344,"tags":471,"date":390,"updated":243,"minutes":391,"pinned":323,"draft":323,"order":353,"stack":472,"period":243,"repo":243,"thumb":243},[386,387,388,389],[],{"title":394,"description":395,"path":396,"slug":397,"category":319,"categoryLabel":343,"kind":344,"tags":474,"date":390,"updated":243,"minutes":391,"pinned":323,"draft":323,"order":353,"stack":475,"period":243,"repo":243,"thumb":243},[331,399,400,333,401],[],{"title":5,"description":322,"path":327,"slug":404,"category":319,"categoryLabel":343,"kind":344,"tags":477,"date":321,"updated":243,"minutes":391,"pinned":323,"draft":323,"order":353,"stack":478,"period":243,"repo":243,"thumb":243},[331,332,333,334],[],{"title":408,"description":409,"path":410,"slug":411,"category":319,"categoryLabel":343,"kind":344,"tags":480,"date":417,"updated":243,"minutes":352,"pinned":323,"draft":323,"order":353,"stack":481,"period":243,"repo":243,"thumb":243},[413,414,415,416],[],{"title":420,"description":421,"path":422,"slug":423,"category":319,"categoryLabel":343,"kind":344,"tags":483,"date":428,"updated":243,"minutes":352,"pinned":323,"draft":323,"order":353,"stack":484,"period":243,"repo":243,"thumb":243},[425,426,415,427],[],[],[487,490],{"title":431,"description":432,"path":433,"slug":389,"category":434,"categoryLabel":435,"kind":434,"tags":488,"date":437,"updated":243,"minutes":438,"pinned":323,"draft":323,"order":438,"stack":489,"period":447,"repo":243,"thumb":448},[413,414,349],[440,441,442,443,444,445,446],{"title":450,"description":451,"path":452,"slug":453,"category":434,"categoryLabel":435,"kind":434,"tags":491,"date":351,"updated":243,"minutes":438,"pinned":323,"draft":323,"order":455,"stack":492,"period":458,"repo":243,"thumb":459},[346,348,349],[457,440,347,441],[494,496,497,498,499,500,501,503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518,519,520,521,522,523,524],{"name":331,"count":310,"weight":495},1.6,{"name":363,"count":310,"weight":495},{"name":415,"count":310,"weight":495},{"name":365,"count":310,"weight":495},{"name":361,"count":310,"weight":495},{"name":333,"count":310,"weight":495},{"name":386,"count":438,"weight":502},1.25,{"name":399,"count":438,"weight":502},{"name":375,"count":438,"weight":502},{"name":346,"count":438,"weight":502},{"name":347,"count":438,"weight":502},{"name":364,"count":438,"weight":502},{"name":348,"count":438,"weight":502},{"name":389,"count":438,"weight":502},{"name":413,"count":438,"weight":502},{"name":400,"count":438,"weight":502},{"name":349,"count":438,"weight":502},{"name":414,"count":438,"weight":502},{"name":426,"count":438,"weight":502},{"name":425,"count":438,"weight":502},{"name":427,"count":438,"weight":502},{"name":388,"count":438,"weight":502},{"name":416,"count":438,"weight":502},{"name":387,"count":438,"weight":502},{"name":401,"count":438,"weight":502},{"name":334,"count":438,"weight":502},{"name":376,"count":438,"weight":502},{"name":362,"count":438,"weight":502},{"name":332,"count":438,"weight":502},1792738754342]