[{"data":1,"prerenderedAt":828},["ShallowReactive",2],{"\u002F2026-04-15":3,"\u002F2026-04-15-rel":240},{"id":4,"title":5,"body":6,"column":223,"date":224,"description":12,"extension":225,"hero_image":226,"meta":227,"navigation":228,"path":229,"seo":230,"series_id":226,"severity":226,"stem":231,"summary":232,"tags":233,"__hash__":239},"posts\u002F2026-04-15-一周四个演示项目最小可信实现.md","一周四个演示项目，什么可以是假的？",{"type":7,"value":8,"toc":214},"minimark",[9,13,17,20,23,26,29,32,40,43,46,49,52,55,63,68,71,74,77,84,103,106,109,113,116,119,122,127,130,145,148,159,162,176,179,211],[10,11,12],"p",{},"同期接到四个演示项目：智慧园区停车系统（SmartPark）、校园班车调度（campus-bus）、通用 IoT 平台（iot-platform）、餐盘识别系统（clean-plate）。限期一周内\"看起来能用\"。这类项目最容易踩的坑是：在非关键细节上过度完备，结果关键路径没时间打磨。",[14,15,16],"h2",{"id":16},"什么必须真",[10,18,19],{},"数据流通和关键交互是演示的生命线。用户点一下按钮，必须有实时反馈。用户看一张报表，数字必须算对。",[10,21,22],{},"SmartPark 的核心是可视化——地图展示、停车位实时状态、收费计算。这三个环节缺一不可。地图集成（高德 API）、数据绑定、前端计算逻辑都必须真实运转。相反，权限管理的细分（运营、财务、巡查员各自的权限边界）在演示阶段可以完全省掉，所有用户走同一条权限路径就够了。",[10,24,25],{},"campus-bus 的核心是班车轨迹和到达预测。班车坐标实时更新、用户能看到倒计时，这才能说\"班车调度系统\"。与其花时间做用户认证和学工处接口对接，不如用 mock 数据把轨迹动画做流畅。MSW 库（SmartPark 里用了）可以在开发阶段拦截 HTTP 请求，返回写好的假数据，前端感知不到后端是否存在。",[10,27,28],{},"iot-platform 设计文档里提到多租户、RBAC、分布式锁、Kafka 消费端幂等等，这些都是生产级别才需要的。演示阶段关键路径是：设备能连上 → 上报数据 → 看板展示。单租户模式启动（环境变量控制，文档里明确支持），所有权限检查用一个假 admin 令牌搞定，Kafka 改成单 broker 部署，Redis 用单机版。核心的时序数据写入（Micro-Batch 到 MongoDB Time Series Collection）是真的（因为这是展示数据的基础），但\"百万设备并发时的背压机制\"完全可以不实现。",[10,30,31],{},"clean-plate（餐盘识别）的瓶颈在 AI 模型推理。假设已经有了一个能用的识别模型，演示重点是前端能拍照 → 调推理服务 → 展示识别结果。模型的精度、冷启动优化、批推理这些可以后来再优。",[10,33,34,35,39],{},"共同的一条线是：",[36,37,38],"strong",{},"数据可以是假的（用 mock 返回），但数据流通的路径必须是真的","。",[14,41,42],{"id":42},"什么可以假",[10,44,45],{},"并发和容错是演示的盲点。开发环境单机跑，谁会同时登 50 个用户？",[10,47,48],{},"SmartPark 不需要考虑\"10 万辆车同时更新位置\"——演示用几十辆数据刷屏就很炫了。不需要分布式锁保证停车位售出不超卖，单机内存里用个计数器就够。不需要支付系统的幂等设计和对账，演示时支付直接假装成功。",[10,50,51],{},"campus-bus 不需要处理\"100 班班车同时调度冲突\"。没有并发冲突检测，也不需要队列和任务调度系统。轨迹上报和用户查询都走单个实例，简单粗暴。",[10,53,54],{},"iot-platform 设计文档里关于\"设备状态一致性\"的三层保障（心跳 TTL、断连事件、状态修复巡检）在演示版完全可以忽略。直接写 Redis，没有巡检任务，设备掉线了刷新页面重新查就行。Kafka 消费端幂等（messageId 防重）也不需要，反正数据量小。",[10,56,57,58,62],{},"异常分支更不用说——登录失败、网络超时、服务宕机这些错误处理在演示里往往是 ",[59,60,61],"code",{},"console.error"," 一行了事，或者弹个提示框就过去了。没人会用 Sentry 做错误追踪，没人会写降级方案。",[10,64,65],{},[36,66,67],{},"演示项目的数据一致性、高可用、容错，这些生产级的需求全部可以零实现。",[14,69,70],{"id":70},"复用的界限",[10,72,73],{},"四个项目用的技术栈不完全一样：SmartPark 和 campus-bus 都是 Vue 3，iot-platform 是 React，clean-plate 是什么具体没看。完全共用一套脚手架不现实。",[10,75,76],{},"但能复用的是设计令牌和组件库选型。SmartPark 用 Ant Design Vue 4，campus-bus 虽然更简洁但也能接入 Ant Design。iot-platform React 用的是 Ant Design 5。共用 Ant Design 意味着色板、字体、组件 API 是一致的，用户看起来像是\"一家人\"。",[10,78,79,80,83],{},"关键是",[36,81,82],{},"把差异收在业务页面","。共同的部分是：",[85,86,87,91,94,97,100],"ul",{},[88,89,90],"li",{},"登录\u002F权限框架（虽然演示时全部跳过，但代码结构保留）",[88,92,93],{},"路由和菜单结构",[88,95,96],{},"数据 API 调用层（axios 或 fetch 封装）",[88,98,99],{},"表单、表格、弹框这些通用组件",[88,101,102],{},"色彩、间距、阴影这些设计系统",[10,104,105],{},"业务差异放在各自的模块里：地图组件（SmartPark 专属）、时序图表（iot-platform 专属）、图像识别 UI（clean-plate 专属）。",[10,107,108],{},"实际效果是：前端架构像一个\"半骨架\"，部分通用，部分预留接口。新项目接入时快速填充业务逻辑即可。",[14,110,112],{"id":111},"反思时间分配的陷阱","反思：时间分配的陷阱",[10,114,115],{},"一周四个项目，最容易掉的坑：在非关键处投入时间。",[10,117,118],{},"比如某个项目花了两天做完整的权限管理系统——细粒度权限点、角色继承、数据权限范围等等。结果发现核心数据展示还没做完。演示时别人只看到\"权限菜单很齐全但数据看不到\"。这就本末倒置了。",[10,120,121],{},"又比如某个项目花时间做\"错误处理和重试逻辑\"，包括网络超时、服务异常、降级方案。实际演示时网络稳定，这些代码完全用不到，反而增加了调试难度。",[10,123,124],{},[36,125,126],{},"正确的姿势是：先把关键路径跑通（最短的从输入到输出的数据链），再看有没有时间加糖。",[10,128,129],{},"SmartPark 的时间分配应该是：",[131,132,133,136,139,142],"ol",{},[88,134,135],{},"高德地图集成 + mock 停车位数据（2 天）",[88,137,138],{},"停车位状态展示和实时更新（1 天）",[88,140,141],{},"收费计算逻辑（0.5 天）",[88,143,144],{},"UI 打磨和动画效果（1.5 天）\n省掉权限、支付对接、后台管理这些，演示版根本用不到。",[10,146,147],{},"campus-bus：",[131,149,150,153,156],{},[88,151,152],{},"Mock 班车轨迹数据 + 地图展示（1.5 天）",[88,154,155],{},"轨迹动画和到达倒计时（1.5 天）",[88,157,158],{},"UI 美化和粒子效果（1 天）\n省掉用户认证、学工处接口、调度算法优化。",[10,160,161],{},"iot-platform：",[131,163,164,167,170,173],{},[88,165,166],{},"设备 mock 数据和 EMQX 单机部署（1 天）",[88,168,169],{},"设备列表、状态展示、时序图表（2 天）",[88,171,172],{},"简单的指令下发模拟（0.5 天）",[88,174,175],{},"看板美化（0.5 天）\n省掉多租户隔离、复杂的 Kafka 消费逻辑、灰度发布、99.9% 可用性这些架构细节。",[14,177,178],{"id":178},"关键实践",[131,180,181,187,193,199,205],{},[88,182,183,186],{},[36,184,185],{},"明确最少可行演示","：在写代码前，列出\"演示时必须工作的 3-5 个功能\"。其他一切都是可选的。",[88,188,189,192],{},[36,190,191],{},"默认 mock 所有后端接口","：除非后端已经完成且稳定，否则前端用 mock 数据。这样前后端可以完全解耦开发，演示时换成真实 API 即可。",[88,194,195,198],{},[36,196,197],{},"用环境变量控制功能开关","：生产级的功能（权限校验、幂等检查、限流）用 feature flag 包裹，演示环境全部关闭。不是删代码，而是不执行。",[88,200,201,204],{},[36,202,203],{},"复用而不共享","：建立一套通用的组件库和设计系统，但不强制每个项目都用。允许项目选择最适合的技术栈，只要最后看起来协调就行。",[88,206,207,210],{},[36,208,209],{},"最后一天留给抛光","：前六天完成功能，最后一天专注 UI 细节、动画、错误提示文案。演示的第一印象很重要。",[10,212,213],{},"演示项目成功的标志不是架构有多完善、功能有多全面，而是用户第一眼看到\"这东西能用\"。所有投入都应该指向这一点。",{"title":215,"searchDepth":216,"depth":216,"links":217},"",2,[218,219,220,221,222],{"id":16,"depth":216,"text":16},{"id":42,"depth":216,"text":42},{"id":70,"depth":216,"text":70},{"id":111,"depth":216,"text":112},{"id":178,"depth":216,"text":178},"工程手记","2026-04-15","md",null,{},true,"\u002F2026-04-15",{"title":5,"description":12},"2026-04-15-一周四个演示项目最小可信实现","演示项目的核心是取舍：什么路径必须打磨到真实，什么细节可以简化甚至假装。",[234,235,236,237,238],"Vue","React","项目交付","快速演示","技术取舍","L9hcuUGczTuAZDIGq7VEKYJsTPmuTpYF4U8JBh4hKgM",[241,548,692],{"id":242,"title":243,"body":244,"column":223,"date":535,"description":248,"extension":225,"hero_image":226,"meta":536,"navigation":228,"path":537,"seo":538,"series_id":226,"severity":226,"stem":539,"summary":540,"tags":541,"__hash__":547},"posts\u002F2026-07-29-一年八千次提交AI辅助开发工作流.md","一年八千次提交，我是怎么干的",{"type":7,"value":245,"toc":517},[246,249,252,255,260,263,266,269,273,276,279,293,296,300,303,306,309,313,316,319,339,342,346,349,360,363,367,370,384,387,390,394,397,400,404,407,410,413,417,420,423,447,454,457,463,466,469,472,479,482,485,511,514],[10,247,248],{},"2026 上半年，主要项目累计提交 7787 次（其中 newCodex 因为是 fork 项目，1922 次提交含上游历史；纯新增代码的项目是 yun-claude、yun-claw、new-openclaw 等）。提交数看起来多，但每个提交背后的价值不在数量，而在流程的可重复性。这篇文章把工作流写清楚。",[14,250,251],{"id":251},"流程的六个环节",[10,253,254],{},"整个开发周期分六步：需求澄清 → 设计文档审阅 → 拆实施计划 → 分阶段实现 → 代码审查 → 故障归档。每一步都有明确的输入输出和验证方式。",[256,257,259],"h3",{"id":258},"_1-需求澄清","1. 需求澄清",[10,261,262],{},"开始前问清楚，不要猜。典型问题：这个功能要处理哪些用户场景？边界条件是什么？现有系统的哪部分会受影响？",[10,264,265],{},"这一步的产出是一份结构化的需求文档，包括功能范围、约束条件、风险假设。不是长篇幅的铺垫，而是一份清单式的澄清记录。",[10,267,268],{},"在 new-openclaw 项目里，这一步通常是用 Claude 的 superpowers:brainstorming skill 来展开。提问方式很重要：我会列出已知条件，然后问\"这个设计下会遇到什么问题？\"而不是\"你觉得应该怎么做？\"让 AI 在我的约束框架内工作。",[256,270,272],{"id":271},"_2-设计文档与人工审阅","2. 设计文档与人工审阅",[10,274,275],{},"这是整个流程里最关键的检查点。花一小时审一份 200 行的设计文档，比花五小时审一份 2000 行的代码便宜得多。而且回头修改设计的成本远低于修改实现。",[10,277,278],{},"new-openclaw 项目现有 30 份设计文档（specs 目录），每份文档都包括：",[85,280,281,284,287,290],{},[88,282,283],{},"范围：这个设计覆盖什么、不覆盖什么",[88,285,286],{},"决策与权衡：为什么选这个方案，放弃了什么",[88,288,289],{},"接口契约：如果涉及多个模块，清晰定义每个边界",[88,291,292],{},"风险清单：已知的坑和防护措施",[10,294,295],{},"写完设计文档以后，我会读一遍、问几个\"为什么\"，然后提出修改意见。这一步排除了 80% 的方向错误。常见的修改方向有：缩小范围（第一个版本不用处理那么多边界情况）、明确约束（系统资源、网络延迟、并发数的假设）、补充防护（熔断、限流、幂等）。",[256,297,299],{"id":298},"_3-实施计划与-dod-定义","3. 实施计划与 DoD 定义",[10,301,302],{},"设计文档定下来以后，拆成实施计划。计划的粒度是\"一个可验证的功能单元\"——通常是一个小时到半天的工作量，完成后能单独验证成功。",[10,304,305],{},"计划文档里必须写清完成定义（DoD，Definition of Done）。不是\"实现登录功能\"，而是\"写出能拒绝无效格式的登录端点、覆盖单点故障下的重试、有端到端的冒烟测试\"。",[10,307,308],{},"new-openclaw 项目现有 36 份实施计划（plans 目录），跨度从一周的 S0 阶段（骨架 + Mock 后台）到数周的 S1 阶段（真实业务后台）。每份计划都带着清晰的 checklist，这样我在执行时能随时问 Claude：\"下一步应该是什么？\"而不是脑子里模糊地记着进度。",[256,310,312],{"id":311},"_4-分阶段实现与逐步验证","4. 分阶段实现与逐步验证",[10,314,315],{},"有了计划以后，按顺序实现。关键是每一步都要有验证：单元测试、集成测试、或者一个小的 end-to-end 冒烟测试。验证不通过就停在这一步，不往下推。",[10,317,318],{},"这一步会用到三个代理：",[85,320,321,327,333],{},[88,322,323,326],{},[36,324,325],{},"superpowers:test-driven-development"," —— 先写测试，再写实现",[88,328,329,332],{},[36,330,331],{},"superpowers:subagent-driven-development"," —— 复杂任务拆成独立的子任务，并行推进",[88,334,335,338],{},[36,336,337],{},"superpowers:systematic-debugging"," —— 遇到测试失败，用这个代理追根溯源，不要盲目修改代码",[10,340,341],{},"实施过程中如果发现设计假设错了（比如某个接口响应时间远超预期，或者并发场景下出现竞态条件），就停下来回到第 2 步重新审视设计，而不是继续往下推。",[256,343,345],{"id":344},"_5-代码审查","5. 代码审查",[10,347,348],{},"实现完成以后，不是立即合并，而是过一遍 code-reviewer 代理。审查的重点不在代码风格（那个自动工具做），而在：",[85,350,351,354,357],{},[88,352,353],{},"这段代码实现的是设计文档里的哪一部分？偏离了吗？",[88,355,356],{},"错误处理有没有遗漏？边界情况有没有考虑？",[88,358,359],{},"有没有意外改动无关的代码？",[10,361,362],{},"审查通常能抓住两类问题。一类是逻辑问题：某个条件判断漏了一个分支，或者并发场景下两个操作的顺序反了。另一类是\"设计和实现对不上\"：实现了一个设计里没提到的特性，或者某个约束（比如\"这个值不能为空\"）没在代码里强制。",[256,364,366],{"id":365},"_6-故障归档","6. 故障归档",[10,368,369],{},"系统上线以后，bug 是难免的。重要的是怎么处理它。new-openclaw 项目有一套 bug 知识库规范（CLAUDE.md 里定义），每个 bug 修好以后都要新建一份独立文档，包括：",[85,371,372,375,378,381],{},[88,373,374],{},"现象和复现路径",[88,376,377],{},"根因分析：为什么会发生，触发链路是什么",[88,379,380],{},"修复方法：改了什么，为什么选这个方案",[88,382,383],{},"预防措施：代码改进、测试添加、还是构建期检查",[10,385,386],{},"80 份 bug 文档（截至 5 月中旬）不是问题的多，而是追根溯源的记录的多。下次遇到类似现象，能直接查库而不是重新排查。",[14,388,389],{"id":389},"三条铁律",[256,391,393],{"id":392},"_1-假设必须显式声明","1. 假设必须显式声明",[10,395,396],{},"不要猜。不清楚的地方就问，把问题写成澄清清单。\"这个 API 能处理多大的请求体？\"、\"离线场景下要缓存多长时间？\"、\"错误重试间隔是指数退避还是固定时间？\"。",[10,398,399],{},"这些问题看起来小，但决定了实现的复杂度和测试用例的多少。猜错了会导致前期设计精美，但方向错误，后面要推倒重来。",[256,401,403],{"id":402},"_2-修改必须精确","2. 修改必须精确",[10,405,406],{},"只改需要改的部分。这听起来像常识，但在实际工作中容易出现\"顺手改一下边上的代码\"的情况 —— 格式不规范了，变量命名不一致了，某个函数太长了，\"顺便\"重构一下。",[10,408,409],{},"结果是一个改动影响了五个文件，代码审查花了双倍时间，引入了新 bug 的风险。",[10,411,412],{},"规则是：改动必须对应需求的某一行。格式、风格、无关的重构，单独立项，不要混在功能改动里。",[256,414,416],{"id":415},"_3-成功标准必须可验证","3. 成功标准必须可验证",[10,418,419],{},"\"添加验证\"这个说法是模糊的。改写成：\"写出一个测试用例，输入非法邮箱格式，验证 API 返回 400；输入合法邮箱，验证返回 200 和预期数据结构\"。",[10,421,422],{},"每个 plan 文档里的 DoD 都是这样写的。拿一个阶段做例子：",[424,425,426,429],"blockquote",{},[10,427,428],{},"S0 阶段的 DoD：",[131,430,431,434,441,444],{},[88,432,433],{},"openapi\u002Fapi-v1.yaml 包含 spec 全部 11 个端点的字段级 schema，可被 swagger-ui 加载 ✓",[88,435,436,437,440],{},"backend\u002F Mock 服务 ",[59,438,439],{},"npm start"," 后能响应全部端点，返回符合契约的 mock 数据 ✓",[88,442,443],{},"launcher\u002F Rust 项目能编译为 launcher.exe，运行后完成所有阶段扫描并输出 diagnostics.json ✓",[88,445,446],{},"至少一个 end-to-end 冒烟测试通过（launcher 上报 → mock 后台收到 → 审计日志记录） ✓",[10,448,449,450,453],{},"每一条都能通过一个具体的命令来验证。\"能工作\"太模糊，\"运行 ",[59,451,452],{},"npm test"," 且所有测试通过\"才是可验证的。",[14,455,456],{"id":456},"流程失效的情况",[10,458,459,460,39],{},"这套流程在一个关键点会失效：",[36,461,462],{},"需求本身没想清楚时",[10,464,465],{},"我遇到过的例子是这样的。客户说\"要支持 USB 存储检测\"，这个需求很清楚，所以设计文档写了 20 多页，规划了三个阶段，列出了 30 多个 test case。然后两周后，客户补充说\"哦对了，还要处理网络驱动器\"。",[10,467,468],{},"这时前面的设计和计划都要回头改。检测逻辑复杂了，测试场景翻倍，阶段划分要调整。这不是流程的问题，这是需求的问题。流程本身反而帮助我及时暴露了这个风险 —— 如果没有设计文档，可能要到代码审查阶段，甚至系统上线以后才发现这个遗漏。",[10,470,471],{},"应对办法是在第 1 步（需求澄清）多花时间。列出你想到的所有场景，问\"还有其他我忽略的情况吗？\"。不是要求完全预测未来，而是把已知的不确定性显式写出来，而不是假设需求是固定的。",[10,473,474,475,478],{},"另一个失效的情况是",[36,476,477],{},"设计和实现的沟通不畅","。如果设计文档是给另一个人读的（或者给 AI 代理读的），但执行者没有理解透彻，实现出来会偏离设计。预防办法是在开始实施前，再过一遍设计文档，确认\"我清楚要做什么\"。",[14,480,481],{"id":481},"提交数字背后的故事",[10,483,484],{},"为什么能积累 7787 次提交？不是因为每次都在写新功能。真实的分布大概是：",[85,486,487,493,499,505],{},[88,488,489,492],{},[36,490,491],{},"功能实现","：40%",[88,494,495,498],{},[36,496,497],{},"设计文档编写和迭代","：25%",[88,500,501,504],{},[36,502,503],{},"测试编写","：20%",[88,506,507,510],{},[36,508,509],{},"bug 修复与回归测试","：15%",[10,512,513],{},"关键是这些提交都有上下文。每个提交的 message 都指向一个设计文档或一个 plan 的某个环节，或者一个 bug 记录。下次有问题要追查根因时，能快速定位到那个提交，看当时的设计决策是什么。",[10,515,516],{},"另外，有 80 份 bug 记录和 66 份设计 + 计划文档（30 specs + 36 plans）这件事本身说明了一点：文档不是负担，文档是工作的实际产出。代码只是文档的一个落地形式。",{"title":215,"searchDepth":216,"depth":216,"links":518},[519,528,533,534],{"id":251,"depth":216,"text":251,"children":520},[521,523,524,525,526,527],{"id":258,"depth":522,"text":259},3,{"id":271,"depth":522,"text":272},{"id":298,"depth":522,"text":299},{"id":311,"depth":522,"text":312},{"id":344,"depth":522,"text":345},{"id":365,"depth":522,"text":366},{"id":389,"depth":216,"text":389,"children":529},[530,531,532],{"id":392,"depth":522,"text":393},{"id":402,"depth":522,"text":403},{"id":415,"depth":522,"text":416},{"id":456,"depth":216,"text":456},{"id":481,"depth":216,"text":481},"2026-07-29",{},"\u002F2026-07-29-ai",{"title":243,"description":248},"2026-07-29-一年八千次提交AI辅助开发工作流","从需求澄清到故障归档，一套系统化的 AI 辅助开发流程：先设计文档过审，再分阶段实施验证，最后代码审查与故障存档，三条铁律支撑整个流程。",[542,543,544,545,546],"Claude","工作流","AI 辅助开发","代码审查","文档驱动","DN7sqbGmg2UybSvRXTMLW-dj0UURXET3KsSNeW_1V9I",{"id":549,"title":550,"body":551,"column":223,"date":679,"description":555,"extension":225,"hero_image":226,"meta":680,"navigation":228,"path":681,"seo":682,"series_id":226,"severity":226,"stem":683,"summary":684,"tags":685,"__hash__":691},"posts\u002F2026-07-20-远程桌面管理器DPAPI与60万次迭代.md","密码，不该由我保管",{"type":7,"value":552,"toc":673},[553,556,560,563,566,569,583,586,590,593,596,599,602,605,616,619,622,625,631,637,643,653,667,670],[10,554,555],{},"远程服务器资料管理工具需要妥善保存账户凭据。这个工具采用两层加密设计：本机存储用 Windows DPAPI，备份使用基于密码的密钥派生。两层各有边界，理解这些边界对使用决策至关重要。",[14,557,559],{"id":558},"第一层dpapi-的便利与代价","第一层：DPAPI 的便利与代价",[10,561,562],{},"本机存储的凭据使用 Windows DPAPI（Data Protection API）加密，由当前 Windows 用户身份保护。这是 Windows 内置的用户级加密机制，密钥由操作系统管理，与登录用户的 SID 和机器的本地安全数据库绑定。不需要用户记一个额外的主密码，启动应用即可直接使用保存的凭据。",[10,564,565],{},"DPAPI 的好处显而易见：启动应用即用，无需输入密码，用户体验最优。代价是它的加密密钥锁定在当前用户、当前机器。凭据无法直接迁移到另一台电脑或另一个 Windows 用户——这不是工具的限制，而是 DPAPI 的设计约束。Windows 操作系统就是这样设计的，其他工具也无法绕过。",[10,567,568],{},"实际操作中的含义很明确：",[85,570,571,574,577,580],{},[88,572,573],{},"重装 Windows 前必须先导出备份。原有凭据会因为用户 SID 变化和机器密钥更新而无法解密，即便登录同一账户也不行。",[88,575,576],{},"更换电脑前需要先创建备份并妥善保存备份密码，目标电脑上导入时需要重新输入这个备份密码。",[88,578,579],{},"在同一电脑上切换 Windows 用户登录，旧用户的凭据对新用户完全不可见，因为加密密钥是用户级的。",[88,581,582],{},"多用户共享一台电脑的场景下，凭据不会跨用户暴露。",[10,584,585],{},"这些限制会在实际使用中暴露出来——比如忙于工作时重装系统忽略了导出，或者在公用工作电脑上多个人使用。正因为如此，工具强制要求提供备份机制。备份不是可选功能，而是必需的。",[14,587,589],{"id":588},"第二层备份加密的固定迭代设计","第二层：备份加密的固定迭代设计",[10,591,592],{},"备份采用 PBKDF2-SHA256（基于密码的密钥派生函数 2，使用 SHA-256 哈希）加密，固定执行 600,000 次迭代。用户创建备份时设置一个密码，导入时输入这个密码。PBKDF2 通过重复应用哈希函数来增加破解难度，迭代次数越多，从密码派生密钥所需的计算时间越长，攻击者进行暴力破解也需要投入成倍的计算资源。",[10,594,595],{},"600,000 次迭代的来源是什么？这是一个有意识的设计决定，而不是随意选择。迭代次数越多，暴力破解的成本越高，但加密和解密的耗时也越长。设计者需要在两者之间找到平衡点：够强以抵御现代硬件的破解能力，又不能强到让普通用户的导入操作变得难以忍受。600,000 次这个数字反映的是这个平衡的结果。",[10,597,598],{},"为什么固定而非可配置？这是一个纪律问题。可配置听起来更灵活、更给用户掌控权，但在实践中会诱使用户为了更快的备份导入速度而降低迭代次数，从而削弱安全强度。安全不应该由便利让步。人们往往倾向于选择快速方案，尤其当他们没有安全专业知识时。固定的迭代次数消除了这种选择权，保证了所有备份都有相同的防护等级。工具的职责是做出最合理的决定，而不是把这个决定推给用户。",[14,600,601],{"id":601},"性能实测与数据解读",[10,603,604],{},"在 Windows 11 构建机上，对 1 MiB 大小的备份数据进行加密与解密的完整往返，预热缓存后的连续五次耗时分别为：108.919 ms、104.079 ms、100.418 ms、94.933 ms、103.470 ms。中位数为 103.470 ms。这意味着从你按下\"导入备份\"到凭据被解密并加载到内存，大约需要 100 毫秒的等待时间。",[10,606,607,608,611,612,615],{},"这组数据收集的目的是",[36,609,610],{},"记录性能表现","。它提供了一个具体的参考：用户在 Windows 11 系统上可以预期导入备份的延迟大约是这个量级。这对评估工具的可用性很有用。但它明确",[36,613,614],{},"不","作为调整迭代次数的依据。这种表述听起来有些冗余，但它是设计纪律的一部分——必须写下来的目的是防止后续有人看到\"100 毫秒确实有点慢\"就建议降低迭代次数。防止的是这样的推理：因为性能数据显示延迟不够快，所以降低迭代次数。这个逻辑链条在安全工程中是禁止的。",[10,617,618],{},"相反，如果实践证明 100 毫秒对用户体验构成问题，正确的做法是要么接受这个成本作为安全性的代价，要么在未来硬件更新换代后自然加速。绝不是削弱密钥派生强度。性能和安全的权衡应该在上层的需求决策中做，而不是在密码学参数中做。",[14,620,621],{"id":621},"工具的明确边界",[10,623,624],{},"这个工具的安全设计有明确的保护范围和限制：",[10,626,627,630],{},[36,628,629],{},"DPAPI 层的限制","：本机凭据的安全性最终依赖于 Windows 用户密码。如果 Windows 账户被破解，攻击者用该账户登录电脑，DPAPI 解密会自动进行。如果用户以管理员身份运行工具（虽然不需要管理员权限），攻击者获得管理员权限后理论上也可能绕过某些保护。安全链的强度由最弱的一环决定——如果你的 Windows 用户密码很弱，或者电脑物理上被他人访问，DPAPI 的保护就名存实亡。",[10,632,633,636],{},[36,634,635],{},"备份密码的强度","：导入备份时用户设置的密码决定了备份的抗暴力破解能力。PBKDF2 提供的防护再强，也无法弥补一个简单密码的缺陷。\"123456\"这样的备份密码，在 600,000 次迭代和现代 GPU 的破解能力面前，可能在几秒到几分钟内被破解。",[10,638,639,642],{},[36,640,641],{},"系统策略的约束","：工具运行在 Windows 系统上，不会绕过任何系统级的安全机制。Windows SmartScreen 对未签名程序的警告、远程桌面连接的安全确认对话、Group Policy 的限制——这些工具都无法绕过。安装包为未签名的内部制品，Windows SmartScreen 会在首次运行时显示\"未知发布者\"警告。这不是工具的缺陷，而是系统安全策略的正常行为。",[10,644,645,648,649,652],{},[36,646,647],{},"加密设计的范围","：这套两层加密设计防的是",[36,650,651],{},"离线攻击","——攻击者获得了备份文件或本机的加密数据，在没有用户交互的情况下尝试破解。它防不了的情况：",[85,654,655,658,661,664],{},[88,656,657],{},"备份密码通过社工或偷看被直接获取",[88,659,660],{},"备份文件在网络传输过程中被中间人截获（如果使用不安全的传输方式）",[88,662,663],{},"凭据被恶意软件在内存中窃取（工具启动后、密码解密到内存这段时间内）",[88,665,666],{},"Windows 账户本身被已经登录电脑的恶意软件控制",[10,668,669],{},"对这些风险的防护需要用户在安全习惯和网络安全措施上自行补足——设置强密码、在信任的网络上操作、定期更新系统补丁、使用反恶意软件工具。",[10,671,672],{},"使用这套工具前要明确：本机凭据带来了便利，代价是将安全依赖在 Windows 用户身份上；备份凭据提供了迁移能力，代价是密码强度必须由用户自己把关。都不是\"一次设置永久安全\"的方案，都需要持续的安全意识和维护。",{"title":215,"searchDepth":216,"depth":216,"links":674},[675,676,677,678],{"id":558,"depth":216,"text":559},{"id":588,"depth":216,"text":589},{"id":601,"depth":216,"text":601},{"id":621,"depth":216,"text":621},"2026-07-20",{},"\u002F2026-07-20-dpapi60",{"title":550,"description":555},"2026-07-20-远程桌面管理器DPAPI与60万次迭代","两层加密保护远程桌面凭据，本机用 DPAPI 便利性换易用性，备份用 PBKDF2 固定 60 万迭代；为什么迭代次数不可配置，性能数据如何解读。",[686,687,688,689,690],"Windows","DPAPI","PBKDF2","凭据存储","加密设计","TzKUoZoFLtAsO_VcB1TOOdWOpwcoDj6AUwEdS6d7pv0",{"id":693,"title":694,"body":695,"column":223,"date":815,"description":699,"extension":225,"hero_image":226,"meta":816,"navigation":228,"path":817,"seo":818,"series_id":226,"severity":226,"stem":819,"summary":820,"tags":821,"__hash__":827},"posts\u002F2026-07-17-把设计文档变成可讲的演示.md","转不成演示的设计文档，本来就没讲清",{"type":7,"value":696,"toc":809},[697,700,706,709,712,715,718,736,743,747,750,756,767,776,779,782,785,788,791,794,797,800,803,806],[10,698,699],{},"一年积累了 200 多份设计文档，最初想法是直接拿这些文档去讲。结果发现这些东西不适合讲。设计文档是给人写的，演示文稿是给人听的，形式完全不同。",[10,701,702,703,39],{},"解决这个问题的思路不是\"写个通用 PPT 编辑器\"，而是\"从结构化文档一键生成演示\"。本质差异在这里——编辑器要处理用户的任意编辑行为，演示工具只要转换",[36,704,705],{},"已有的结构",[14,707,708],{"id":708},"为什么选单向转换而不是编辑器",[10,710,711],{},"设计文档有稳定的模板：背景、方案、架构、取舍、参考。这个顺序不是随意的，恰好就是讲一个设计时的叙述顺序。",[10,713,714],{},"用户不需要\"先生成再改\"，需要的是\"文档秒变幻灯片\"。一旦你改，就回到编辑器的坑里去了——要支持拖拽、删除、排版，工作量爆炸，而且多数用户不会调，生成好的东西就是定版。",[10,716,717],{},"这个判断来自实际数据。我的工具做两个决策：",[131,719,720,730],{},[88,721,722,725,726,729],{},[36,723,724],{},"产物是自包含 HTML","，不是 Office 文件（",[59,727,728],{},".pptx"," 需要可编辑格式，门槛高；HTML 在浏览器里就能放映，自包含意味着内联了所有 CSS、JS、图片）。",[88,731,732,735],{},[36,733,734],{},"内容由 LLM 生成，不开放编辑面板","（用一个\"预览挑选\"的两阶段流程，让用户在 3 种风格的封面里选一个，然后生成整份）。",[10,737,738,739,742],{},"这两个约束听起来很严格，实际上契合了需求的本质：",[36,740,741],{},"文档的目的是记录决策，演示的目的是讲述决策","。不需要演示过程中再改决策，改了就回到文档去改。",[14,744,746],{"id":745},"什么样的结构能转什么样的不能","什么样的结构能转、什么样的不能",[10,748,749],{},"设计文档要是能一键转成演示，必须满足几个条件。",[10,751,752,755],{},[36,753,754],{},"架构图可以直接映射","。我的演示工具支持 AI 生图，也支持用户指定图片 URL。当文档里写了架构设计时，LLM 看到这个描述会生成对应的图，然后内联到演示文稿里。舞台是固定的 1920×1080，所有图片容器有最小尺寸约束（GSAP 时间轴动画要能在任意时间点求值，不能靠动态布局）。",[10,757,758,761,762,766],{},[36,759,760],{},"关键数据用表格记录最不容易犯错","。表格这种结构容易转化——",[763,764,765],"span",{},"待补：具体表格如何转成演示页的实现规则","。如果文档里的数据以段落文字形式写，转成演示时就卡住了，LLM 要从自然语言反推结构。",[10,768,769,772,773],{},[36,770,771],{},"取舍（trade-off）部分","：",[763,774,775],{},"待补：设计文档中的取舍说明如何映射成演示内容的规则",[10,777,778],{},"一个更深的观察：文档转不出来好演示，通常不是演示工具的问题，是文档本身没讲清楚。",[10,780,781],{},"我见过一类项目文档，有 20 多份模板文件，但只要换个配色其他全一样。这说明什么？说明那些\"模板\"实际上没有结构差异，只有视觉差异。结构不清，自然转不出演示。或者反过来说，如果要证明自己的文档结构是清晰的，试试能不能一键转成演示——转不出来就是信号，说明需要先梳理文档本身。",[14,783,784],{"id":784},"工具的硬约束",[10,786,787],{},"演示不同于其他产物，有独特的硬约束。我的工具选择了 1920×1080 的固定舞台，所有动画用 GSAP 时间轴。这些看起来像限制，实际上是为了保证可靠性。",[10,789,790],{},"固定舞台尺寸意味着设计者写风格预设时要算好留白和排版，不能寄希望于\"容器自适应就行了\"。GSAP 时间轴的特点是能在任意时间点求值（渲染管线会 seek 到任意帧截图），不能用 CSS animation 这种依赖真实时间流逝的东西。这限制了动效，但换来的是确定性——动效一定会在预期时间点发生，不会因为网络卡而错位。",[10,792,793],{},"生成流程分两个阶段，有个细节很实用。第一阶段只生成 3 个风格的封面单页，第二阶段才生成整份演示文稿。这个设计看似多一步，实际上优雅地解决了两个问题。一是给用户选择风格的机会（不是非此即彼的\"生成或不生成\"）；二是掩盖异步生图的等待时间（生图 1-2 分钟，正好被用户在选择封面时吸收了）。",[14,795,796],{"id":796},"从文档能否转演示看结构清晰度",[10,798,799],{},"最后回到起点。为什么要做这个工具？",[10,801,802],{},"表面原因是 200 多份文档用演讲方式讲会更有力。深层原因是这个过程本身就是对文档质量的检验。",[10,804,805],{},"一份设计文档如果结构清晰——背景交代得清，方案对比得充分，架构图画得明确，取舍理由说得透彻——那转成演示就是平移内容，不费劲。转不出来、或者转出来很别扭，就是信号，说明某个环节讲得不够好。",[10,807,808],{},"这个反馈机制比任何 review 注释都直白。\"这个表述为什么转不成幻灯片？\"往往能逼出真实的问题——\"哦，因为我其实还没想清楚这个方案为什么比另一个好\"。",{"title":215,"searchDepth":216,"depth":216,"links":810},[811,812,813,814],{"id":708,"depth":216,"text":708},{"id":745,"depth":216,"text":746},{"id":784,"depth":216,"text":784},{"id":796,"depth":216,"text":796},"2026-07-17",{},"\u002F2026-07-17",{"title":694,"description":699},"2026-07-17-把设计文档变成可讲的演示","不做通用PPT编辑器，只做文档→演示的单向转换；关键在识别文档的稳定结构。",[822,823,824,825,826],"演示文稿","文档结构","设计工具","HTML","GSAP","Reh87_TKXZ8nl5BhDhHzVOONAzwRS-Im8LTYrIfeuNk",1785406912236]