[{"data":1,"prerenderedAt":549},["ShallowReactive",2],{"rainink-post-base-dictionary-boundary":3,"rainink-content-index":360},{"id":4,"title":5,"body":6,"category":343,"cover":344,"date":345,"description":346,"draft":347,"extension":348,"featured":347,"lang":344,"meta":349,"navigation":350,"order":344,"path":351,"period":344,"pinned":347,"repo":344,"seo":352,"slug":344,"stack":344,"stem":353,"tags":354,"thumb":344,"toc":350,"updated":344,"__hash__":359},"content\u002Fposts\u002Fbase-dictionary-boundary.md","底座能力的边界：什么该沉进数据字典，什么必须单独立表",{"type":7,"value":8,"toc":332},"minimark",[9,16,21,29,36,39,117,120,123,139,142,145,148,151,154,160,164,167,170,179,182,204,210,217,221,224,290,297,301,316,323,326,329],[10,11,12],"blockquote",{},[13,14,15],"p",{},"平台底座做久了会发现，\"这个能不能用字典配\"是一个高频但容易答错的问题。",[17,18,20],"h2",{"id":19},"判断标准只有一条它是不是纯枚举","判断标准只有一条：它是不是\"纯枚举\"",[13,22,23,24,28],{},"数据字典适合承载的是",[25,26,27],"strong",{},"取值固定、没有独立生命周期、不需要被业务单据直接外键引用","的枚举。典型如：客户分类、供应商分类、区域、行业、原因码、结算方式、币种代码。",[13,30,31,32,35],{},"不适合的是那些",[25,33,34],{},"自己会变化、需要被引用、需要留历史","的对象。",[13,37,38],{},"用这条标准检验几个常见候选：",[40,41,42,58],"table",{},[43,44,45],"thead",{},[46,47,48,52,55],"tr",{},[49,50,51],"th",{},"候选",[49,53,54],{},"能否沉字典",[49,56,57],{},"理由",[59,60,61,73,83,93,104],"tbody",{},[46,62,63,67,70],{},[64,65,66],"td",{},"币种代码",[64,68,69],{},"可以",[64,71,72],{},"ISO 4217 固定集合，业务只需读代码与精度",[46,74,75,78,80],{},[64,76,77],{},"结算方式",[64,79,69],{},[64,81,82],{},"纯枚举，变化频率极低",[46,84,85,88,90],{},[64,86,87],{},"客户\u002F供应商分类",[64,89,69],{},[64,91,92],{},"分级靠编码前缀即可，不需要树",[46,94,95,98,101],{},[64,96,97],{},"税码",[64,99,100],{},"不建议",[64,102,103],{},"税码是客户\u002F供应商主数据上的属性，税率会随时间调整，且有申报口径",[46,105,106,109,114],{},[64,107,108],{},"汇率",[64,110,111],{},[25,112,113],{},"不可以",[64,115,116],{},"有时效性、需按币种对与日期查询、是计算依据",[17,118,119],{"id":119},"汇率为什么必须单独立表",[13,121,122],{},"汇率有两个字典给不了的能力：",[124,125,126,133],"ol",{},[127,128,129,132],"li",{},[25,130,131],{},"时间维度","。同一个币种对，不同日期的汇率不同，业务单据必须能取到\"单据日期对应那一版\"的汇率。字典项没有生效区间。",[127,134,135,138],{},[25,136,137],{},"双向与精度","。汇率是有方向和小数位要求的数值，不是标签。",[13,140,141],{},"用字典硬塞，最后一定会演化成\"把汇率拼进字典项的扩展字段里再按日期解析\"，这比一开始就建表麻烦十倍。",[13,143,144],{},"所以汇率单独建表、单独页面管理，并且明确：单据在创建时快照汇率，不随后续汇率调整而变。",[17,146,147],{"id":147},"税码为什么不属于字典",[13,149,150],{},"税码看起来像枚举，实际上它是客户\u002F供应商主数据上的采购\u002F销售属性。同一个物料卖给不同客户，适用的税码可能不同；同一个供应商供不同物料，进项税码也可能不同。",[13,152,153],{},"把它做成全局字典，会导致业务在单据上无法区分\"这条行项目的税率从哪来\"。放在主数据上，来源唯一，追溯清晰。",[13,155,156,157],{},"这条结论在评审时被反复确认过：",[25,158,159],{},"税码是主数据字段，不是字典项。",[17,161,163],{"id":162},"单级字典就够了不升级成树","单级字典就够了，不升级成树",[13,165,166],{},"底座现有的字典是单级的：一个类型下挂若干项，没有父子层级，也没有生效区间。",[13,168,169],{},"评审时讨论过要不要升级：",[171,172,173,176],"ul",{},[127,174,175],{},"加树结构：分类类数据（如物料分类）确实有多级需求；",[127,177,178],{},"加生效区间：看起来能给字典项做版本管理。",[13,180,181],{},"最后的决定是都不加，理由分别是：",[13,183,184,187,188,192,193,192,196,199,200,203],{},[25,185,186],{},"分类不用树，用编码前缀。"," 三级分类用 ",[189,190,191],"code",{},"01","、",[189,194,195],{},"0101",[189,197,198],{},"010101"," 这样的编码就能表达层级，查询时用 ",[189,201,202],{},"LIKE '01%'"," 即可。真正需要树结构的场景（比如组织架构、BOM）本身就不是字典该管的对象。为一个边缘需求给全平台字典加一套递归查询，收益不成立。",[13,205,206,209],{},[25,207,208],{},"生效区间不加。"," 需要版本语义的对象（如 BOM）已经在各自的领域模型里用生效区间解决。字典项如果也要区间，说明它其实不是字典项，是业务实体。",[13,211,212,213,216],{},"这两条决定的本质是：",[25,214,215],{},"底座的能力边界要窄而硬，业务差异放在业务模块里解决","。底座一旦为了通用性引入复杂度，所有接入方都要为它付维护成本。",[17,218,220],{"id":219},"底座该提供什么不该提供什么","底座该提供什么，不该提供什么",[13,222,223],{},"在 super-admin 这个底座上，我划的线大致是：",[40,225,226,236],{},[43,227,228],{},[46,229,230,233],{},[49,231,232],{},"能力",[49,234,235],{},"归属",[59,237,238,246,253,260,267,275,283],{},[46,239,240,243],{},[64,241,242],{},"RBAC、组织、用户、菜单权限",[64,244,245],{},"底座",[46,247,248,251],{},[64,249,250],{},"工作流引擎（四表模型、节点类型、转办\u002F加签\u002F撤回\u002F抄送）",[64,252,245],{},[46,254,255,258],{},[64,256,257],{},"数据字典（单级枚举）",[64,259,245],{},[46,261,262,265],{},[64,263,264],{},"审计日志、操作留痕",[64,266,245],{},[46,268,269,272],{},[64,270,271],{},"主数据（客户\u002F供应商\u002F物料\u002FBOM）",[64,273,274],{},"业务模块",[46,276,277,280],{},[64,278,279],{},"汇率、税率、多币种结算",[64,281,282],{},"业务模块（单独表）",[46,284,285,288],{},[64,286,287],{},"业务单据状态机",[64,289,274],{},[13,291,292,293,296],{},"判断依据很简单：",[25,294,295],{},"底座负责\"谁能在系统里做什么\"，业务模块负责\"业务长什么样\"。"," 工作流之所以在底座，是因为几乎所有业务单据都需要审批，且审批的语义与业务无关；主数据之所以不在底座，是因为每类业务的字段、状态、引用约束都不同。",[17,298,300],{"id":299},"一条容易被忽略的约束迁移与建表策略","一条容易被忽略的约束：迁移与建表策略",[13,302,303,304,307,308,311,312,315],{},"底座的能力再清楚，落地时也会被工程细节绊一下。比如用 TypeORM 的项目里，",[189,305,306],{},"data-source.ts"," 只手写了几个实体，",[189,309,310],{},"DB_SYNC=false","，",[189,313,314],{},"migrations"," 目录又是空的——部署到空库时直接报实体元数据找不到。",[13,317,318,319,322],{},"处理办法是：实体注册改成 glob 扫描模块目录，保证所有实体都被加载；在没有迁移文件的情况下，先用 ",[189,320,321],{},"DB_SYNC=true"," 让同步建表跑通，再把迁移补起来。这个顺序不要反，反了就是空库启动失败。",[17,324,325],{"id":325},"小结",[13,327,328],{},"\"能不能用字典\"这个问题，答案不在字典本身，而在对象的语义：有没有时间维度、要不要被引用、会不会有独立生命周期。",[13,330,331],{},"底座的克制，换来的是每个业务模块可以按自己的语义建模；底座的\"通用\"，最后往往变成所有人的技术债。",{"title":333,"searchDepth":334,"depth":334,"links":335},"",2,[336,337,338,339,340,341,342],{"id":19,"depth":334,"text":20},{"id":119,"depth":334,"text":119},{"id":147,"depth":334,"text":147},{"id":162,"depth":334,"text":163},{"id":219,"depth":334,"text":220},{"id":299,"depth":334,"text":300},{"id":325,"depth":334,"text":325},"tech",null,"2026-10-20","B 端底座能力的边界判断：哪些配置该沉进数据字典、哪些必须单独立表，以及单级字典不升级成树的理由。",false,"md",{},true,"\u002Fposts\u002Fbase-dictionary-boundary",{"title":5,"description":346},"posts\u002Fbase-dictionary-boundary",[355,356,357,358],"B端","架构设计","数据字典","super-admin","f9VN5sXdmouo4mBCHrDL00S5rgHxHMM8Xn5-HCsXuTI",{"all":361,"posts":484,"essays":509,"projects":510,"tags":517},[362,379,393,404,409,421,431,443,454,473],{"title":363,"description":364,"path":365,"slug":366,"category":343,"categoryLabel":367,"kind":368,"tags":369,"date":374,"updated":375,"minutes":376,"pinned":350,"draft":347,"order":377,"stack":378,"period":333,"repo":333,"thumb":333},"用 Nuxt 4 + Nuxt Content 搭一个纯静态个人站","站点骨架的取舍：内容目录怎么定、SSG 怎么预渲染、设计令牌怎么从原型抽成一套 CSS 变量，以及首屏主题闪烁的解法。","\u002Fposts\u002Fnuxt4-static-site","nuxt4-static-site","技术","post",[370,371,372,373],"Nuxt","Nuxt Content","SSG","前端工程化","2026-10-12","2026-10-18",5,0,[],{"title":380,"description":381,"path":382,"slug":383,"category":343,"categoryLabel":367,"kind":368,"tags":384,"date":390,"updated":333,"minutes":391,"pinned":347,"draft":347,"order":377,"stack":392,"period":333,"repo":333,"thumb":333},"国内服务器部署的十个坑：从镜像源到 certbot","从镜像源到 certbot 的十个真实故障：nvm 拉不动、目录权限、空库迁移、Host 头命中错站、证书续期与备案期验收。","\u002Fposts\u002Fcn-server-deploy-pitfalls","cn-server-deploy-pitfalls",[385,386,387,388,389],"部署","踩坑","Nginx","PowerShell","运维","2026-10-22",6,[],{"title":394,"description":395,"path":396,"slug":397,"category":343,"categoryLabel":367,"kind":368,"tags":398,"date":401,"updated":333,"minutes":402,"pinned":347,"draft":347,"order":377,"stack":403,"period":333,"repo":333,"thumb":333},"一台腾讯云轻量跑起多站点：从裸机到 HTTPS 的实际路径","一台 2C4G 腾讯云轻量同时承载后台、接口与静态站：备案施工顺序、Nginx 多 server 块、certbot 签证书与上线验收。","\u002Fposts\u002Fmulti-site-deploy-to-https","multi-site-deploy-to-https",[385,387,399,389,400],"HTTPS","腾讯云","2026-10-21",7,[],{"title":5,"description":346,"path":351,"slug":405,"category":343,"categoryLabel":367,"kind":368,"tags":406,"date":345,"updated":333,"minutes":407,"pinned":347,"draft":347,"order":377,"stack":408,"period":333,"repo":333,"thumb":333},"base-dictionary-boundary",[355,356,357,358],4,[],{"title":410,"description":411,"path":412,"slug":413,"category":343,"categoryLabel":367,"kind":368,"tags":414,"date":345,"updated":333,"minutes":407,"pinned":347,"draft":347,"order":377,"stack":420,"period":333,"repo":333,"thumb":333},"先画全链路，再切分期：我做 ERP\u002FWMS\u002FMES\u002FQC 需求的方法","ERP\u002FWMS\u002FMES\u002FQC 需求方法：先用编号体系与硬闸点把端到端流程关进文档，再按里程碑切成可交付的分期。","\u002Fposts\u002Ferp-wms-mes-qc-requirement-method","erp-wms-mes-qc-requirement-method",[415,416,417,418,419],"MES","ERP","WMS","需求设计","流程设计",[],{"title":422,"description":423,"path":424,"slug":425,"category":343,"categoryLabel":367,"kind":368,"tags":426,"date":429,"updated":333,"minutes":407,"pinned":347,"draft":347,"order":377,"stack":430,"period":333,"repo":333,"thumb":333},"主数据状态机不是字段，是一份跨模块公共契约","MES 主数据状态机的设计取舍：六态加停用怎么定、四方口径为什么必须交叉核对，以及 BOM 与编号体系为什么会互相污染。","\u002Fposts\u002Fmes-master-data-state-machine","mes-master-data-state-machine",[415,427,418,428],"领域建模","状态机","2026-10-19",[],{"title":432,"description":433,"path":434,"slug":435,"category":343,"categoryLabel":367,"kind":368,"tags":436,"date":441,"updated":333,"minutes":376,"pinned":347,"draft":347,"order":377,"stack":442,"period":333,"repo":333,"thumb":333},"B 端后台底座：把权限、菜单、请求收敛成三层","后台系统做多了会发现，真正需要沉淀的不是页面，而是权限模型、动态菜单和请求层这三块底座。","\u002Fposts\u002Fvue3-admin-base","vue3-admin-base",[437,438,439,440],"Vue","后台","架构","权限","2026-09-24",[],{"title":444,"description":445,"path":446,"slug":447,"category":343,"categoryLabel":367,"kind":368,"tags":448,"date":452,"updated":333,"minutes":376,"pinned":347,"draft":347,"order":377,"stack":453,"period":333,"repo":333,"thumb":333},"工作流引擎实操笔记：节点、流转与幂等","从状态机到可配置流程：一次工作流引擎落地中关于节点模型、流转条件、幂等与补偿的实操记录。","\u002Fposts\u002Fworkflow-engine-notes","workflow-engine-notes",[449,450,439,451],"工作流","后端","幂等","2026-08-30",[],{"title":455,"description":456,"path":457,"slug":358,"category":458,"categoryLabel":459,"kind":458,"tags":460,"date":461,"updated":333,"minutes":462,"pinned":347,"draft":347,"order":462,"stack":463,"period":471,"repo":333,"thumb":472},"Super Admin 后台底座","一套可复用的 B 端后台前端底座：权限三层收敛、动态菜单、请求层封装与通用表格表单组件，新项目接入只需替换业务页面。","\u002Fprojects\u002Fsuper-admin","project","项目",[437,438,373],"2026-05-20",1,[464,465,466,467,468,469,470],"Vue 3","TypeScript","Vite","Pinia","Antdv Next","Node.js","Next.js","2026 年至今","SA",{"title":474,"description":475,"path":476,"slug":477,"category":458,"categoryLabel":459,"kind":458,"tags":478,"date":375,"updated":333,"minutes":462,"pinned":347,"draft":347,"order":479,"stack":480,"period":482,"repo":333,"thumb":483},"雨墨 RainInk 本站","你正在看的这个站：Nuxt 4 + Nuxt Content 的纯静态个人站，手写设计令牌、无评论无用户输入、产物可直接静态托管。","\u002Fprojects\u002Frainink-site","rainink-site",[370,372,373],3,[481,464,371,465],"Nuxt 4","2026 年 10 月","RI",[485,488,491,494,497,500,503,506],{"title":363,"description":364,"path":365,"slug":366,"category":343,"categoryLabel":367,"kind":368,"tags":486,"date":374,"updated":375,"minutes":376,"pinned":350,"draft":347,"order":377,"stack":487,"period":333,"repo":333,"thumb":333},[370,371,372,373],[],{"title":380,"description":381,"path":382,"slug":383,"category":343,"categoryLabel":367,"kind":368,"tags":489,"date":390,"updated":333,"minutes":391,"pinned":347,"draft":347,"order":377,"stack":490,"period":333,"repo":333,"thumb":333},[385,386,387,388,389],[],{"title":394,"description":395,"path":396,"slug":397,"category":343,"categoryLabel":367,"kind":368,"tags":492,"date":401,"updated":333,"minutes":402,"pinned":347,"draft":347,"order":377,"stack":493,"period":333,"repo":333,"thumb":333},[385,387,399,389,400],[],{"title":5,"description":346,"path":351,"slug":405,"category":343,"categoryLabel":367,"kind":368,"tags":495,"date":345,"updated":333,"minutes":407,"pinned":347,"draft":347,"order":377,"stack":496,"period":333,"repo":333,"thumb":333},[355,356,357,358],[],{"title":410,"description":411,"path":412,"slug":413,"category":343,"categoryLabel":367,"kind":368,"tags":498,"date":345,"updated":333,"minutes":407,"pinned":347,"draft":347,"order":377,"stack":499,"period":333,"repo":333,"thumb":333},[415,416,417,418,419],[],{"title":422,"description":423,"path":424,"slug":425,"category":343,"categoryLabel":367,"kind":368,"tags":501,"date":429,"updated":333,"minutes":407,"pinned":347,"draft":347,"order":377,"stack":502,"period":333,"repo":333,"thumb":333},[415,427,418,428],[],{"title":432,"description":433,"path":434,"slug":435,"category":343,"categoryLabel":367,"kind":368,"tags":504,"date":441,"updated":333,"minutes":376,"pinned":347,"draft":347,"order":377,"stack":505,"period":333,"repo":333,"thumb":333},[437,438,439,440],[],{"title":444,"description":445,"path":446,"slug":447,"category":343,"categoryLabel":367,"kind":368,"tags":507,"date":452,"updated":333,"minutes":376,"pinned":347,"draft":347,"order":377,"stack":508,"period":333,"repo":333,"thumb":333},[449,450,439,451],[],[],[511,514],{"title":455,"description":456,"path":457,"slug":358,"category":458,"categoryLabel":459,"kind":458,"tags":512,"date":461,"updated":333,"minutes":462,"pinned":347,"draft":347,"order":462,"stack":513,"period":471,"repo":333,"thumb":472},[437,438,373],[464,465,466,467,468,469,470],{"title":474,"description":475,"path":476,"slug":477,"category":458,"categoryLabel":459,"kind":458,"tags":515,"date":375,"updated":333,"minutes":462,"pinned":347,"draft":347,"order":479,"stack":516,"period":482,"repo":333,"thumb":483},[370,372,373],[481,464,371,465],[518,520,521,522,523,524,525,527,528,529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,544,545,546,547,548],{"name":415,"count":334,"weight":519},1.6,{"name":387,"count":334,"weight":519},{"name":439,"count":334,"weight":519},{"name":389,"count":334,"weight":519},{"name":385,"count":334,"weight":519},{"name":418,"count":334,"weight":519},{"name":355,"count":462,"weight":526},1.25,{"name":416,"count":462,"weight":526},{"name":399,"count":462,"weight":526},{"name":370,"count":462,"weight":526},{"name":371,"count":462,"weight":526},{"name":388,"count":462,"weight":526},{"name":372,"count":462,"weight":526},{"name":358,"count":462,"weight":526},{"name":437,"count":462,"weight":526},{"name":417,"count":462,"weight":526},{"name":373,"count":462,"weight":526},{"name":438,"count":462,"weight":526},{"name":450,"count":462,"weight":526},{"name":449,"count":462,"weight":526},{"name":451,"count":462,"weight":526},{"name":357,"count":462,"weight":526},{"name":440,"count":462,"weight":526},{"name":356,"count":462,"weight":526},{"name":419,"count":462,"weight":526},{"name":428,"count":462,"weight":526},{"name":400,"count":462,"weight":526},{"name":386,"count":462,"weight":526},{"name":427,"count":462,"weight":526},1792738754341]