[{"data":1,"prerenderedAt":1747},["ShallowReactive",2],{"post:\u002Fposts\u002Fswdesign\u002Fsw-design-notes-08":3,"posts:ordered":1445},{"id":4,"title":5,"body":6,"category":1426,"cover":1427,"date":1428,"description":1429,"extension":1430,"featured":1431,"meta":1432,"navigation":960,"path":1433,"seo":1434,"series":1435,"seriesOrder":1436,"stem":1437,"tags":1438,"__hash__":1444},"posts\u002Fposts\u002FswDesign\u002Fsw-design-notes-08.md","软件分析与设计笔记 08：功能需求（一）：用例模型与活动图",{"type":7,"value":8,"toc":1390},"minimark",[9,16,25,30,33,38,45,52,56,61,64,119,123,126,148,155,159,163,166,187,190,197,203,228,232,235,241,256,259,265,269,278,281,293,296,299,362,368,371,379,393,397,461,468,479,483,487,494,497,503,519,522,537,541,544,554,561,565,568,582,585,588,592,595,641,644,651,655,659,665,668,688,719,723,726,729,736,740,747,750,824,828,843,850,900,907,920,942,946,949,987,991,994,1014,1018,1022,1025,1045,1048,1054,1061,1064,1090,1093,1153,1156],[10,11,12],"blockquote",{},[13,14,15],"p",{},"这是我在学习软件分析与设计时整理的系列笔记。概念部分是我按自己的理解写的，示例与图示都是自己编、自己画的。理解有偏差的地方欢迎指出。",[10,17,18],{},[13,19,20,24],{},[21,22,23],"strong",{},"本系列的贯穿案例","：片区家电维修派单系统 —— 客户报修 → 客服派单 → 工程师接单上门 → 回填工单 → 仓管备料 → 片区经理看板。角色：客户、客服、工程师、仓管、片区经理，以及外部的「配件供应商库存系统」。这一篇从这里开始，后面 09、10、12、14、18、21、22、27 用的都是它。",[26,27,29],"h2",{"id":28},"_0-故事写完开发还是问你一堆问题","0 故事写完，开发还是问你一堆问题",[13,31,32],{},"十条用户故事摆在开发面前，第一个问题通常是：",[10,34,35],{},[13,36,37],{},"「工程师到了现场，发现配件没有货，这单怎么办？」",[13,39,40,41,44],{},"故事里没写。因为用户故事天生只描述",[21,42,43],{},"从使用者视角的一句话","，它不负责回答「系统内部怎么处理各种情况」。",[13,46,47,48,51],{},"功能性需求（functional requirement）要把这一层补上，需要换一种描述方式：",[21,49,50],{},"用例（use case）","。",[26,53,55],{"id":54},"_1-从用户故事到用例","1 从用户故事到用例",[57,58,60],"h3",{"id":59},"_11-两种视角的差别","1.1 两种视角的差别",[13,62,63],{},"两者的视角不一样：",[65,66,67,82],"table",{},[68,69,70],"thead",{},[71,72,73,76,79],"tr",{},[74,75],"th",{},[74,77,78],{},"用户故事",[74,80,81],{},"用例",[83,84,85,97,108],"tbody",{},[71,86,87,91,94],{},[88,89,90],"td",{},"视角",[88,92,93],{},"使用者的诉求",[88,95,96],{},"系统与外部的一次完整交互",[71,98,99,102,105],{},[88,100,101],{},"粒度",[88,103,104],{},"一句话",[88,106,107],{},"一段可展开的流程",[71,109,110,113,116],{},[88,111,112],{},"关注点",[88,114,115],{},"为什么做",[88,117,118],{},"系统该做什么、按什么顺序做",[57,120,122],{"id":121},"_12-三种翻译情况","1.2 三种翻译情况",[13,124,125],{},"翻译时有三种常见情况：",[127,128,129,136,142],"ul",{},[130,131,132,135],"li",{},[21,133,134],{},"一对一"," —— 一条故事正好对应一个用例，例如「客户报修」；",[130,137,138,141],{},[21,139,140],{},"多对一"," —— 几条故事合成一个用例，例如「查询配件库存 \u002F 查看可约时段 \u002F 确认上门时间」最终都是「安排上门」这个用例的步骤；",[130,143,144,147],{},[21,145,146],{},"一对多"," —— 一条大故事拆成几个用例，例如「派单」会拆成「创建工单」「分配工程师」「通知客户」三个。",[13,149,150,151,154],{},"判断标准是：",[21,152,153],{},"一个用例应该有一个明确的目标达成点。","「工单建好了」是一个目标，「客户知道几点上门」是另一个。",[26,156,158],{"id":157},"_2-什么才算好的参与者","2 什么才算「好」的参与者",[57,160,162],{"id":161},"_21-三个判断要点","2.1 三个判断要点",[13,164,165],{},"把所有参与者与用例画在一起，就是用例图（use case diagram）；图加上每个用例的文字描述，构成完整的用例模型（use case model）。其中的基本单元是参与者（actor）—— 与系统交互的外部角色。三个判断要点：",[167,168,169,175,181],"ol",{},[130,170,171,174],{},[21,172,173],{},"是唯一的角色，不能是泛称。","「用户」不算一个 actor，「客户」「客服」「仓管」才是。因为不同的角色关心的东西不一样；",[130,176,177,180],{},[21,178,179],{},"可以是人，也可以是外部系统。"," 在贯穿案例里，「配件供应商库存系统」就是一个 actor —— 系统要去查它的库存；",[130,182,183,186],{},[21,184,185],{},"判断依据是目标，不是身份。"," 问一句：这个角色在系统里有没有自己要达成的目标？没有的就不是 actor，只是流程里的一个步骤。",[13,188,189],{},"第 3 条能过滤掉很多噪音。比如「打印机」不是 actor（它没有目标），但「片区经理」是 —— 他要看的看板和工程师要看的不一样。",[13,191,192],{},[193,194],"img",{"alt":195,"src":196},"用例图与活动图共十四个符号的特写：左列是用例图的参与者、用例、系统边界、关联线，以及 include、extend、泛化三种关系的箭头方向；右列是活动图的开始状态、结束状态、动作、转移、判断菱形、同步条、泳道。每个符号下方标出 draw.io 形状库里的搜索关键词","\u002Fimages\u002Fsw-design-notes-08-usecase-activity-symbols.svg",[13,198,199,200,51],{},"上面这张图把这两个图种要用的符号一次列全，",[21,201,202],{},"下面每讲到一处，都可以回来对照它在 draw.io 里搜什么",[10,204,205],{},[13,206,207,210,211,215,216,219,220,223,224,227],{},[21,208,209],{},"画这一笔","：draw.io 左侧搜索框输入 ",[212,213,214],"code",{},"actor","，把火柴人拖进画布，双击图中默认的 ",[212,217,218],{},"Actor"," 字样改成 ",[212,221,222],{},"Customer","。\n想快速复制同类角色：选中火柴人按 ",[212,225,226],{},"Ctrl+D","（复制），再双击改名。",[57,229,231],{"id":230},"_22-一个角色还是一类角色","2.2 一个角色还是一类角色",[13,233,234],{},"贯穿案例里有个真实的判断：「普通客户」和「买了延保的客户」要不要建成两个 actor？",[13,236,237,238],{},"判断方法是问一句：",[21,239,240],{},"他们做的事一样吗？",[127,242,243,250],{},[130,244,245,246,249],{},"一样，只是待遇不同（延保客户免费换件）→ ",[21,247,248],{},"合并成一个 actor","，差异写成业务规则，放到活动图的分支里；",[130,251,252,253,51],{},"不一样，能用的功能都不同（延保客户能自助申请换件）→ ",[21,254,255],{},"拆成两个 actor",[13,257,258],{},"这个判断会直接改变用例的个数，所以要早做。",[13,260,261],{},[193,262],{"alt":263,"src":264},"两个参与者连到同一个用例的三种画法：第一种是一条用例连两条实线，适用两人触发与执行方式完全一致的情况；第二种是参与者泛化，父角色挂公共用例、子角色只挂差异用例，适用共用的是一整批用例；第三种是拆成两个用例，适用两人用法不同。右下角给出判断方法：问这两个角色点进去看到的是不是同一个界面、走的是不是同一套步骤","\u002Fimages\u002Fsw-design-notes-08-shared-usecase.svg",[57,266,268],{"id":267},"_23-两个-actor-连同一个用例可以但有前提","2.3 两个 actor 连同一个用例：可以，但有前提",[13,270,271,274,275,51],{},[21,272,273],{},"一个用例连两个火柴人，是完全合法的画法"," —— 它表示",[21,276,277],{},"这两个角色触发并执行这个用例的方式完全一致",[13,279,280],{},"注意最后半句。讲义的原话是：",[10,282,283],{},[13,284,285,286,289,290],{},"both will ",[21,287,288],{},"trigger and execute"," the use case ",[21,291,292],{},"in the same way",[13,294,295],{},"它不是「两个角色都会用到这个功能」，而是「两人点进去之后看到同一个界面、走同一套步骤」。这两件事差很远。",[13,297,298],{},"本例里正好有一对可以对照：",[65,300,301,314],{},[68,302,303],{},[71,304,305,308,311],{},[74,306,307],{},"一对角色",[74,309,310],{},"用法一致吗",[74,312,313],{},"该怎么画",[83,315,316,336],{},[71,317,318,327,330],{},[88,319,320,323,324],{},[21,321,322],{},"客服"," 与 ",[21,325,326],{},"片区经理",[88,328,329],{},"「看待派单」「看排期」「改约」三个人事完全一样，经理只是多了「看报表」",[88,331,332,335],{},[21,333,334],{},"一条用例连两条线","（或做参与者泛化）",[71,337,338,345,348],{},[88,339,340,323,342],{},[21,341,322],{},[21,343,344],{},"工程师",[88,346,347],{},"客服是「派单」，工程师是「接单」—— 动作方向都不同",[88,349,350,353,354,357,358,361],{},[21,351,352],{},"拆成两个用例","（",[212,355,356],{},"Assign Engineer"," \u002F ",[212,359,360],{},"Accept Assignment","）",[13,363,364,367],{},[21,365,366],{},"第二行是很多人会画错的地方","：两个用例都跟「派单」有关，看起来像一个东西的两面，但用法完全不同 —— 所以必须分开画。",[13,369,370],{},"判断方法一句话：",[10,372,373],{},[13,374,375,378],{},[21,376,377],{},"这两个角色点进去之后，看到的是同一个界面、走的是同一套步骤吗？","\n是 → 一个用例连两根线；不是 → 拆成两个用例，哪怕名字听起来像。",[13,380,381,382,385,386,389,390],{},"讲义里还有一条方向相反、同样重要的提醒：",[21,383,384],{},"「好的 actor = 唯一角色」","。两组人扮演同一个角色时可以合并；但如果他们",[21,387,388],{},"将来会访问不同的用例","，就该保持分开。也就是说 —— ",[21,391,392],{},"能不能共用一个用例，由「用法是否一致」决定，不由「是不是同一批人」决定。",[57,394,396],{"id":395},"_24-共用的三种画法什么时候用哪种","2.4 共用的三种画法，什么时候用哪种",[65,398,399,412],{},[68,400,401],{},[71,402,403,406,409],{},[74,404,405],{},"画法",[74,407,408],{},"什么场景用",[74,410,411],{},"图上的样子",[83,413,414,434,451],{},[71,415,416,421,431],{},[88,417,418],{},[21,419,420],{},"一条用例 + 两条关联线",[88,422,423,426,427,430],{},[21,424,425],{},"首选","。两人只在",[21,428,429],{},"这一个","用例上用法一致",[88,432,433],{},"两个火柴人各拉一条实线到同一个椭圆",[71,435,436,441,448],{},[88,437,438],{},[21,439,440],{},"参与者泛化",[88,442,443,444,447],{},"两人共用的是",[21,445,446],{},"一整批","用例，只是权限不同",[88,449,450],{},"父角色（抽象）挂公共用例，子角色只挂差异用例，空心三角子→父",[71,452,453,455,458],{},[88,454,352],{},[88,456,457],{},"两人用法不同",[88,459,460],{},"两个椭圆，各连各的 actor",[13,462,463,464,467],{},"第一种和第二种怎么选，看",[21,465,466],{},"共用的是一个还是一批","：只共用一个，直接连两根线最省事；共用一整批，泛化更清楚（泛化会在笔记 10 里展开）。",[10,469,470],{},[13,471,472,474,475,478],{},[21,473,209],{},"：draw.io 里直接从第二个 actor 拖一条实线到这个已有椭圆即可，不需要新建图形。\n注意",[21,476,477],{},"不要画箭头"," —— 关联线表示 actor 主动触发用例，带箭头只在「系统主动推送」（比如定时提醒）时才用。",[26,480,482],{"id":481},"_3-系统边界哪些交互进系统","3 系统边界：哪些交互进系统",[57,484,486],{"id":485},"_31-边界怎么划","3.1 边界怎么划",[13,488,489,490,493],{},"用例图画在一个",[21,491,492],{},"系统边界（system boundary）"," 里。边界怎么划，是这一步最需要讨论的地方。",[13,495,496],{},"看贯穿案例里的一个常见分歧：「给客户发上门短信」这件事，在系统边界内还是外？",[13,498,499,500],{},"判断方法：",[21,501,502],{},"这个动作需不需要系统保存状态、做判断？",[127,504,505,512],{},[130,506,507,508,511],{},"「决定该给谁发、什么时候发、发什么内容」需要保存工单状态、判断工程师有没有出发 —— ",[21,509,510],{},"在边界内","，是一个用例；",[130,513,514,515,518],{},"「通过哪个短信服务商把短信发出去」是纯粹的通道 —— ",[21,516,517],{},"在边界外","，它是一个外部系统 actor。",[13,520,521],{},"边界划错会带来两种后果：划得太宽，需求里塞满了技术选型；划得太窄，该由系统负责的判断被漏掉了。",[10,523,524],{},[13,525,526,528,529,532,533,536],{},[21,527,209],{},"：draw.io 搜 ",[212,530,531],{},"boundary","（UML 分类下），拖入画布拉成一个大矩形，所有用例都放进去。\nactor 必须留在框",[21,534,535],{},"外"," —— 框里只放椭圆。这一条阅卷人扫一眼就能判断。",[57,538,540],{"id":539},"_32-边界画完之后","3.2 边界画完之后",[13,542,543],{},"边界确定后，把落在边界内的用例一个个列出来。贯穿案例的第一版清单大致是：",[545,546,551],"pre",{"className":547,"code":549,"language":550},[548],"language-text","客户      ：报修、查询上门进度、评价服务\n客服      ：创建工单、分配工程师、改派\n工程师    ：接单、回填工单、申请备料\n仓管      ：出库、归还余料\n片区经理  ：查看派单看板、查看工程师工作量\n供应商系统：查询配件库存（系统调用）\n","text",[212,552,549],{"__ignoreMap":553},"",[13,555,556,557,560],{},"这份清单还不是图，只是",[21,558,559],{},"画图前的材料","。下一步才是把它画出来。",[26,562,564],{"id":563},"_4-自动化到什么程度搬流程还是改流程","4 自动化到什么程度：搬流程还是改流程",[13,566,567],{},"边界划完之后，还有一个更根本的问题：这一段流程要不要原样保留？",[127,569,570,576],{},[130,571,572,575],{},[21,573,574],{},"digital automation（数字化自动化）"," —— 把现有手工作业 1:1 搬进系统。风险小，但会把现有流程里的低效也一起固化下来；",[130,577,578,581],{},[21,579,580],{},"digital transformation（数字化转型）"," —— 借这个机会重新设计流程本身，做的不是「原来的事换个工具」，而是「换一种做法」。",[13,583,584],{},"判断工具就是一句提问：「这个动作存在的理由是什么」。贯穿案例里，现在客服要「打电话问工程师今天有没有空」，问一句为什么 —— 答案往往是「因为不知道排期」。真正的目的（知道谁有空）完全可以通过系统排期实现，那这一步本身就不需要保留。",[13,586,587],{},"这个决定要早做，因为它直接改变用例的个数。",[26,589,591],{"id":590},"_5-用例描述必须写清的四件事","5 用例描述必须写清的四件事",[13,593,594],{},"只画图不够，每个用例都要配一段文字描述（use case description），至少包含：",[65,596,597,607],{},[68,598,599],{},[71,600,601,604],{},[74,602,603],{},"要素",[74,605,606],{},"内容",[83,608,609,617,625,633],{},[71,610,611,614],{},[88,612,613],{},"参与者（actor）",[88,615,616],{},"谁发起这次交互",[71,618,619,622],{},[88,620,621],{},"前置条件（precondition）",[88,623,624],{},"开始之前必须已经成立的状态",[71,626,627,630],{},[88,628,629],{},"主流程（basic flow）",[88,631,632],{},"一切顺利时的步骤序列",[71,634,635,638],{},[88,636,637],{},"例外流程（alternative flow）",[88,639,640],{},"出错、取消、超时、数据缺失时怎么办",[13,642,643],{},"前置条件常常被忽略。贯穿案例里，「回填工单」的前置条件是「工程师已到现场且工单状态为进行中」—— 没有这一句，讨论就不知道从这里开始。与之对应的后置条件（postcondition）则写明结束后应当成立的状态，例如「工单状态变为待结算，配件消耗已记录」。",[13,645,646,647,650],{},"例外流程才是真正的价值所在。",[21,648,649],{},"主流程只占一个用例的 30%，剩下 70% 的工作量都藏在例外里。"," 比如「申请备料」的例外是「仓库也没有这个配件」，这时整单要转成「待件挂起」，客户要收到改约通知 —— 这条路径如果没在用例里写出来，开发根本不会做。",[26,652,654],{"id":653},"_6-活动图把流程画到看得见分支","6 活动图：把流程画到看得见分支",[57,656,658],{"id":657},"_61-三类结构","6.1 三类结构",[13,660,661,662,51],{},"用例描述写清楚了，还需要一种图形化的表达方式，让业务方也能看懂 —— 这就是",[21,663,664],{},"活动图（activity diagram）",[13,666,667],{},"一张活动图里最该出现的是这三类结构：",[127,669,670,676,682],{},[130,671,672,675],{},[21,673,674],{},"判断节点（decision node）与汇合节点（merge node）"," —— 根据条件走不同分支，再合回一条路径。贯穿案例里最典型的是「是否在保修期内」，两条路径的处理完全不同；",[130,677,678,681],{},[21,679,680],{},"并行（fork \u002F join）"," —— 两件事同时进行。比如「通知客户」和「占用工程师时段」可以并行，不必等对方；",[130,683,684,687],{},[21,685,686],{},"泳道（swimlane）"," —— 用竖直分区把不同角色负责的步骤分开。",[10,689,690],{},[13,691,692,694,695,698,699,702,703,706,707,710,711,714,715,718],{},[21,693,209],{},"：draw.io 里这三个形状分别是 —— 判断用 ",[212,696,697],{},"decision","（菱形），并行用 ",[212,700,701],{},"fork","（粗黑横线，搜 ",[212,704,705],{},"horizontal bar","），泳道搜 ",[212,708,709],{},"lane","（竖长条）或直接手画矩形 + 分隔线。\n判断的分支条件要写在方括号里（",[212,712,713],{},"[yes]"," ",[212,716,717],{},"[no]","），draw.io 里是双击连线中间的位置加文字，不是拖一个文本框。",[57,720,722],{"id":721},"_62-泳道为什么最实用","6.2 泳道为什么最实用",[13,724,725],{},"泳道是最实用的一类。它一眼就能看出「这一步是谁在做」—— 如果一条流程在活动图上反复跨越泳道，通常说明这个流程本身有协调成本。",[13,727,728],{},"贯穿案例里有个很好的对比：「工程师申请备料」这件事，如果画出来是在工程师泳道和仓管泳道之间来回跨四次（申请 → 查库存 → 缺货 → 改约 → 通知），那这条流程的协调成本就很高。看到这种图，业务方自己就会说「能不能简化成一次申请」。",[10,730,731],{},[13,732,733,735],{},[21,734,209],{},"：泳道在 draw.io 里没有自动吸附。最省事的做法是手画：拖 N 个等宽矩形横排，顶部写角色名，然后所有活动框都放进对应的矩形里。\n缺点是一移动就乱。所以先把活动位置排好，最后再画泳道框。",[57,737,739],{"id":738},"_63-图之外还要一张表","6.3 图之外还要一张表",[13,741,742,743,746],{},"除了图形，实际交付时往往还会配一张",[21,744,745],{},"活动流程表（activity flow table）","：每一行是一个步骤，列出执行者、动作、条件、输出。图形负责沟通，表格负责精确。",[13,748,749],{},"一个例子（截自「申请备料」这一段）：",[65,751,752,768],{},[68,753,754],{},[71,755,756,759,762,765],{},[74,757,758],{},"活动",[74,760,761],{},"执行者",[74,763,764],{},"条件",[74,766,767],{},"说明",[83,769,770,783,797,811],{},[71,771,772,775,777,780],{},[88,773,774],{},"提交备料申请",[88,776,344],{},[88,778,779],{},"工单状态 = 进行中",[88,781,782],{},"填写配件编号与数量",[71,784,785,788,791,794],{},[88,786,787],{},"查询库存",[88,789,790],{},"系统",[88,792,793],{},"—",[88,795,796],{},"调供应商库存接口",[71,798,799,802,805,808],{},[88,800,801],{},"库存充足",[88,803,804],{},"仓管",[88,806,807],{},"库存 ≥ 申请量",[88,809,810],{},"生成出库单",[71,812,813,816,818,821],{},[88,814,815],{},"库存不足",[88,817,790],{},[88,819,820],{},"库存 \u003C 申请量",[88,822,823],{},"工单转「待件挂起」，通知客户改约",[57,825,827],{"id":826},"_64-把实体挂到活动图上","6.4 把实体挂到活动图上",[13,829,830,831,834,835,838,839,842],{},"考试里活动图那道题通常会加一句「",[21,832,833],{},"include entities","」。这不是让你多画几个框，而是要求这张图同时表达",[21,836,837],{},"流程","和",[21,840,841],{},"数据","两件事 —— 只画了动作和分支，等于只答了一半。",[13,844,845,846,849],{},"做法是给每个动作用到的业务实体系一条线。",[21,847,848],{},"方向取决于读还是写","，这是这一节唯一的难点：",[65,851,852,865],{},[68,853,854],{},[71,855,856,859,862],{},[74,857,858],{},"这一步在做什么",[74,860,861],{},"箭头方向",[74,863,864],{},"例子",[83,866,867,884],{},[71,868,869,875,878],{},[88,870,871,874],{},[21,872,873],{},"读","数据（查、取、显示）",[88,876,877],{},"实体 → 动作",[88,879,880,881],{},"「查询工单状态」← ",[212,882,883],{},"WorkOrder",[71,885,886,892,895],{},[88,887,888,891],{},[21,889,890],{},"写","数据（新增、更新、删除）",[88,893,894],{},"动作 → 实体",[88,896,897,898],{},"「回填工单」→ ",[212,899,883],{},[13,901,902,903,906],{},"一个记法：",[21,904,905],{},"数据流向动作就是读，动作流向数据就是写","。看起来像废话，但画的时候极容易顺手都画成同一个方向。",[13,908,909,910,912,913,912,916,919],{},"实体本身怎么挑，还是那三条标准（笔记 09）：业务概念而不是动作、需要长期记住、有多个实例。在贯穿案例里，",[212,911,883],{},"、",[212,914,915],{},"PartUsage",[212,917,918],{},"StockItem"," 都是实体；「提交申请」不是。",[10,921,922],{},[13,923,924,926,927,930,931,357,934,937,938,941],{},[21,925,209],{},"：实体用普通矩形（或搜 ",[212,928,929],{},"class"," 取一个空的三格框），直接放在对应泳道的右侧。连线时按上表定方向，标 ",[212,932,933],{},"read",[212,935,936],{},"write"," 在线上更清楚。\n不必给每个动作都挂实体 —— 挂那些",[21,939,940],{},"真正读写数据","的动作就够了，否则图会糊成一团。",[57,943,945],{"id":944},"_65-自查这张活动图","6.5 自查这张活动图",[13,947,948],{},"画完先过一遍这五条，能挡住大部分低级失分：",[127,950,953,963,969,975,981],{"className":951},[952],"contains-task-list",[130,954,957,962],{"className":955},[956],"task-list-item",[958,959],"input",{"disabled":960,"type":961},true,"checkbox"," 只有一个开始状态，且每个分支最终都能走到结束状态？",[130,964,966,968],{"className":965},[956],[958,967],{"disabled":960,"type":961}," 每个判断的出口都标了方括号条件？",[130,970,972,974],{"className":971},[956],[958,973],{"disabled":960,"type":961}," 泳道数量与参与角色数量对得上？",[130,976,978,980],{"className":977},[956],[958,979],{"disabled":960,"type":961}," 需要实体的动作都挂了实体，读写方向没画反？",[130,982,984,986],{"className":983},[956],[958,985],{"disabled":960,"type":961}," 没有画「是否输入有误」「操作成功」这类校验与确认框？（这一阶段应忽略异常）",[26,988,990],{"id":989},"_7-为什么值得画活动图","7 为什么值得画活动图",[13,992,993],{},"三个具体收益：",[167,995,996,1002,1008],{},[130,997,998,1001],{},[21,999,1000],{},"逼出例外流程。"," 画到分支处，必须回答「另一种情况怎么办」；",[130,1003,1004,1007],{},[21,1005,1006],{},"发现缺失的步骤。"," 从「客户点了提交」到「系统提示成功」之间，通常还漏着好几步；",[130,1009,1010,1013],{},[21,1011,1012],{},"可以直接拿去和用户核对。"," 一张图比一段文字更容易被指出错误 —— 「这里不是这样的」，这是需求验证最有效的时刻之一。",[26,1015,1017],{"id":1016},"_8-用例按什么打包","8 用例按什么打包",[57,1019,1021],{"id":1020},"_81-三种打包依据","8.1 三种打包依据",[13,1023,1024],{},"用例多了以后需要分组，也就是划分用例包（use case package）。常见的依据有三种：",[127,1026,1027,1033,1039],{},[130,1028,1029,1032],{},[21,1030,1031],{},"按业务主题（package by domain）"," —— 按业务范围归类，例如「报修相关」「备料相关」；",[130,1034,1035,1038],{},[21,1036,1037],{},"按角色"," —— 每个角色一包，便于按岗位做需求确认；",[130,1040,1041,1044],{},[21,1042,1043],{},"按流程阶段"," —— 对应业务的阶段划分，例如「上门前」「上门中」「上门后」。",[13,1046,1047],{},"贯穿案例按第一种分成三包比较自然：",[545,1049,1052],{"className":1050,"code":1051,"language":550},[548],"报修与派单   ：报修、创建工单、分配工程师、改派\n上门服务     ：接单、回填工单、申请备料\n结算与看板   ：评价、结算、派单看板、工作量统计\n",[212,1053,1051],{"__ignoreMap":553},[13,1055,1056,1057,1060],{},"打包的意义不只是整理。",[21,1058,1059],{},"它同时也是在回答「谁该来确认这一包需求」","：按角色打包的，就该由对应角色确认。",[26,1062,1063],{"id":1063},"本节自测",[167,1065,1066,1069,1072,1075,1078,1081,1084,1087],{},[130,1067,1068],{},"用户故事和用例的视角差别是什么？",[130,1070,1071],{},"判断一个角色是不是 actor，看的是身份还是目标？",[130,1073,1074],{},"贯穿案例里「给客户发上门短信」这件事，哪部分在系统边界内，哪部分在外？",[130,1076,1077],{},"为什么说用例的价值主要在例外流程里？举一个本例中的例外。",[130,1079,1080],{},"活动图里的泳道解决了什么问题？流程反复跨越泳道说明什么？",[130,1082,1083],{},"在 draw.io 里画泳道，为什么建议「最后再画泳道框」？",[130,1085,1086],{},"「普通客户」和「延保客户」，什么情况下合并成一个 actor，什么情况下拆成两个？",[130,1088,1089],{},"用例包按什么依据划，会直接影响哪件事？",[57,1091,1092],{"id":1092},"参考答案",[167,1094,1095,1101,1107,1119,1122,1129,1136,1146],{},[130,1096,1097,1100],{},[21,1098,1099],{},"视角差别","：用户故事是「用一句话说清谁要什么」，用例是「系统与外部的一次完整交互」。前者回答为什么做，后者回答系统做什么、按什么顺序做（§1.1）。",[130,1102,1103,1106],{},[21,1104,1105],{},"看目标。"," 判断标准是「这个角色在系统里有没有自己要达成的目标」。所以他是不是人、什么职位都不重要 —— 打印机没目标，不是 actor；供应商库存系统有目标（被查询），是 actor（§2.1）。",[130,1108,1109,1112,1113,1115,1116,1118],{},[21,1110,1111],{},"分两段看","：决定发给谁、何时发、发什么内容 → ",[21,1114,510],{},"（要保存工单状态、做判断）；通过哪个短信服务商发出去 → ",[21,1117,517],{},"（纯通道，是外部系统 actor）（§3.1）。",[130,1120,1121],{},"因为主流程只占 30%，例外里藏着大部分真实工作量。本例的典型例外：「申请备料时仓库也没有这个配件」→ 工单转「待件挂起」→ 通知客户改约 → 重新排期。这条路径不写出来，开发就不会做（§5）。",[130,1123,1124,1125,1128],{},"泳道解决「这一步是谁在做」，让人一眼看出责任归属。",[21,1126,1127],{},"反复跨越泳道说明这个流程的协调成本高"," —— 本例「申请备料」跨四次就是个信号，业务方看到会想简化（§6.2）。",[130,1130,1131,1132,1135],{},"因为 draw.io 的泳道框",[21,1133,1134],{},"不会自动吸附","活动框，一移动就乱。先排好活动位置、再画泳道框，可以少返工（§6.2 的画这一笔）。",[130,1137,1138,1141,1142,1145],{},[21,1139,1140],{},"合并","：两人做的事一样、只是待遇不同（延保客户免费换件）→ 差异写成业务规则，放进活动图分支；",[21,1143,1144],{},"拆分","：能用的功能都不同（延保客户能自助申请换件）→ 各自要有独立用例（§2.2）。",[130,1147,1148,1149,1152],{},"按什么依据打包，",[21,1150,1151],{},"直接决定「谁该来确认这一包需求」","。按角色打包就由对应角色确认，按阶段打包就由该阶段的负责人确认（§8.1）。",[26,1154,1155],{"id":1155},"术语表",[65,1157,1158,1171],{},[68,1159,1160],{},[71,1161,1162,1165,1168],{},[74,1163,1164],{},"术语",[74,1166,1167],{},"中文",[74,1169,1170],{},"一句话解释",[83,1172,1173,1184,1194,1205,1216,1226,1237,1248,1258,1269,1280,1291,1302,1313,1324,1335,1346,1357,1368,1379],{},[71,1174,1175,1178,1181],{},[88,1176,1177],{},"functional requirement",[88,1179,1180],{},"功能需求",[88,1182,1183],{},"系统要「做什么」",[71,1185,1186,1189,1191],{},[88,1187,1188],{},"use case",[88,1190,81],{},[88,1192,1193],{},"一个参与者通过系统达成某个目标的完整交互",[71,1195,1196,1199,1202],{},[88,1197,1198],{},"use case model",[88,1200,1201],{},"用例模型",[88,1203,1204],{},"参与者与用例的集合，描述系统的功能边界",[71,1206,1207,1210,1213],{},[88,1208,1209],{},"use case diagram",[88,1211,1212],{},"用例图",[88,1214,1215],{},"画出参与者、用例与系统边界的图",[71,1217,1218,1220,1223],{},[88,1219,214],{},[88,1221,1222],{},"参与者",[88,1224,1225],{},"与系统交互的外部角色，可以是人或外部系统",[71,1227,1228,1231,1234],{},[88,1229,1230],{},"system boundary",[88,1232,1233],{},"系统边界",[88,1235,1236],{},"区分「系统负责」与「外部负责」的那条线",[71,1238,1239,1242,1245],{},[88,1240,1241],{},"use case description",[88,1243,1244],{},"用例描述",[88,1246,1247],{},"用文字写清一个用例的流程与条件",[71,1249,1250,1253,1256],{},[88,1251,1252],{},"basic flow",[88,1254,1255],{},"主流程",[88,1257,632],{},[71,1259,1260,1263,1266],{},[88,1261,1262],{},"alternative flow",[88,1264,1265],{},"例外流程",[88,1267,1268],{},"出错、取消、超时等情况下的处理",[71,1270,1271,1274,1277],{},[88,1272,1273],{},"precondition",[88,1275,1276],{},"前置条件",[88,1278,1279],{},"用例开始前必须成立的状态",[71,1281,1282,1285,1288],{},[88,1283,1284],{},"postcondition",[88,1286,1287],{},"后置条件",[88,1289,1290],{},"用例结束后应当成立的状态",[71,1292,1293,1296,1299],{},[88,1294,1295],{},"activity diagram",[88,1297,1298],{},"活动图",[88,1300,1301],{},"描述流程步骤、分支与并行的图",[71,1303,1304,1307,1310],{},[88,1305,1306],{},"activity flow table",[88,1308,1309],{},"活动流程表",[88,1311,1312],{},"用表格逐行记录步骤、执行者与条件",[71,1314,1315,1318,1321],{},[88,1316,1317],{},"decision node",[88,1319,1320],{},"判断节点",[88,1322,1323],{},"按条件分叉的节点",[71,1325,1326,1329,1332],{},[88,1327,1328],{},"merge node",[88,1330,1331],{},"汇合节点",[88,1333,1334],{},"把多个分支重新合到一起的节点",[71,1336,1337,1340,1343],{},[88,1338,1339],{},"fork \u002F join",[88,1341,1342],{},"分叉 \u002F 汇合",[88,1344,1345],{},"表示并行开始的节点 \u002F 等待并行结束的节点",[71,1347,1348,1351,1354],{},[88,1349,1350],{},"swimlane",[88,1352,1353],{},"泳道",[88,1355,1356],{},"用分区表示每个步骤由谁负责",[71,1358,1359,1362,1365],{},[88,1360,1361],{},"use case package",[88,1363,1364],{},"用例包",[88,1366,1367],{},"按主题、角色或阶段对用例分组",[71,1369,1370,1373,1376],{},[88,1371,1372],{},"digital automation",[88,1374,1375],{},"数字化自动化",[88,1377,1378],{},"把现有手工流程原样搬进系统",[71,1380,1381,1384,1387],{},[88,1382,1383],{},"digital transformation",[88,1385,1386],{},"数字化转型",[88,1388,1389],{},"借系统建设重新设计流程本身",{"title":553,"searchDepth":1391,"depth":1391,"links":1392},3,[1393,1395,1399,1405,1409,1410,1411,1418,1419,1422,1425],{"id":28,"depth":1394,"text":29},2,{"id":54,"depth":1394,"text":55,"children":1396},[1397,1398],{"id":59,"depth":1391,"text":60},{"id":121,"depth":1391,"text":122},{"id":157,"depth":1394,"text":158,"children":1400},[1401,1402,1403,1404],{"id":161,"depth":1391,"text":162},{"id":230,"depth":1391,"text":231},{"id":267,"depth":1391,"text":268},{"id":395,"depth":1391,"text":396},{"id":481,"depth":1394,"text":482,"children":1406},[1407,1408],{"id":485,"depth":1391,"text":486},{"id":539,"depth":1391,"text":540},{"id":563,"depth":1394,"text":564},{"id":590,"depth":1394,"text":591},{"id":653,"depth":1394,"text":654,"children":1412},[1413,1414,1415,1416,1417],{"id":657,"depth":1391,"text":658},{"id":721,"depth":1391,"text":722},{"id":738,"depth":1391,"text":739},{"id":826,"depth":1391,"text":827},{"id":944,"depth":1391,"text":945},{"id":989,"depth":1394,"text":990},{"id":1016,"depth":1394,"text":1017,"children":1420},[1421],{"id":1020,"depth":1391,"text":1021},{"id":1063,"depth":1394,"text":1063,"children":1423},[1424],{"id":1092,"depth":1391,"text":1092},{"id":1155,"depth":1394,"text":1155},"学习",null,"2026-10-30","从用户故事到用例的翻译过程：什么才算「好」的参与者，系统边界怎么划，用例描述必须写清哪四件事，活动图怎么把分支、并行与泳道画清楚，以及这两个图在 draw.io 里各用哪个形状、最容易画错哪一笔。","md",false,{},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-08",{"title":5,"description":1429},"软件分析与设计笔记",8,"posts\u002FswDesign\u002Fsw-design-notes-08",[1439,1440,1441,1442,1443],"需求分析","用例建模","UML","绘图","学习笔记","ITasx75DFTTYRacjbQoTD3n592ejpSsR1d7Hzv7EybA",[1446,1452,1458,1464,1470,1476,1482,1488,1494,1500,1506,1512,1518,1524,1530,1536,1542,1548,1554,1560,1566,1567,1573,1579,1585,1591,1596,1601,1607,1613,1617,1621,1625,1629,1633,1637,1641,1645,1649,1653,1657,1663,1667,1673,1677,1681,1685,1689,1693,1697,1701,1706,1710,1714,1719,1724,1730,1734,1739,1743],{"path":1447,"title":1448,"description":1449,"date":1450,"series":1435,"seriesOrder":1451},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-28","软件分析与设计笔记 28：draw.io 作图手册：五张图的符号位置、落笔顺序与自检","不模拟软件界面，只讲清三件事：每个 UML 符号在 draw.io 里搜什么词、一张图该按什么顺序落笔、画完拿什么标准自查。覆盖用例图、活动图、顺序图、类图、界面草图五种图纸，末尾附「最容易画反的十二笔」总检表。","2026-11-19",28,{"path":1453,"title":1454,"description":1455,"date":1456,"series":1435,"seriesOrder":1457},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-27","软件分析与设计笔记 27：Paper 1 应试：从读案例到交卷的一次完整演算","拿一份完整的案例，从头到尾走一遍：怎么读案例列角色、怎么在四个小问之间分配时间、每一张图从哪一句话推出来、两张表怎么填、交卷前拿什么清单自查。附 C25 真题与样卷的题型分值对照。","2026-11-18",27,{"path":1459,"title":1460,"description":1461,"date":1462,"series":1435,"seriesOrder":1463},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-26","软件分析与设计笔记 26：总结：把整条链路串起来","系列收尾：从需求到代码的完整链路回顾、每一环的产出与验收方式、六条贯穿全系列的原则，以及一份可以直接拿去做项目自查的清单。","2026-11-17",26,{"path":1465,"title":1466,"description":1467,"date":1468,"series":1435,"seriesOrder":1469},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-25","软件分析与设计笔记 25：代码重构：坏味道与手法","重构不改变外部行为、只改善内部结构：四种常见坏味道与对应手法、为什么动手之前必须先有测试、什么时候该重构，以及重构和前面那些设计模型是什么关系。","2026-11-16",25,{"path":1471,"title":1472,"description":1473,"date":1474,"series":1435,"seriesOrder":1475},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-24","软件分析与设计笔记 24：设计原则：内聚、耦合与多态","好设计怎么衡量：内聚与耦合分别在说什么、一次从「差设计」到「多态重设计」的完整对照、抽象类与接口的选择依据，以及复用为什么会从优势变成风险。","2026-11-15",24,{"path":1477,"title":1478,"description":1479,"date":1480,"series":1435,"seriesOrder":1481},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-23","软件分析与设计笔记 23：设计建模：从对象模型到能实现的方案","分析模型能用但还不能直接开工：对象模型怎么映射到关系模型、继承映射的三种方案怎么选、数据访问层与 Data Broker 解决什么问题，以及三层架构与 MVC 的分工。","2026-11-14",23,{"path":1483,"title":1484,"description":1485,"date":1486,"series":1435,"seriesOrder":1487},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-22","软件分析与设计笔记 22：分析建模：边界、实体与控制对象","不经过分析直接写代码会踩什么坑、三种分析对象各管一段、怎么用顺序图完成一次用例实现，以及从顺序图导出类的属性、操作与关系的方法。","2026-11-13",22,{"path":1489,"title":1490,"description":1491,"date":1492,"series":1435,"seriesOrder":1493},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-21","软件分析与设计笔记 21：UML：类图与顺序图","为什么需要一种共同的画法：类符号三格怎么画、五种关系的符号与设计含义、每种关系落到 Java 是什么写法，顺序图的三要素怎么摆；以及这三种最容易找不到的符号在 draw.io 里各搜什么词。","2026-11-12",21,{"path":1495,"title":1496,"description":1497,"date":1498,"series":1435,"seriesOrder":1499},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-20","软件分析与设计笔记 20：面向对象概念：对象、关系与多态","面向对象不是「用某个语言写代码」：对象与类的区别、封装真正在保护什么、五种关系（依赖、关联、聚合、组合、继承）各自何时用，以及多态为什么能减少条件判断。","2026-11-11",20,{"path":1501,"title":1502,"description":1503,"date":1504,"series":1435,"seriesOrder":1505},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-19","软件分析与设计笔记 19：规范化与逻辑数据模型","一次改地址引发的数据不一致，讲清规范化的真正动机；UNF 到 3NF 每一步在解决什么问题，派生字段为什么不能存，优化为什么和规范化方向相反，以及逻辑数据模型在 3NF 之上补了什么。","2026-11-10",19,{"path":1507,"title":1508,"description":1509,"date":1510,"series":1435,"seriesOrder":1511},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-18","软件分析与设计笔记 18：数据库分析：实体、关系与数据字典","为什么不能拿到需求就动手建表：实体与关系怎么判定、基数为什么必须由业务方确认、数据命名要遵守什么规则，以及数据字典四列里最容易被空着的那一列。","2026-11-09",18,{"path":1513,"title":1514,"description":1515,"date":1516,"series":1435,"seriesOrder":1517},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-17","软件分析与设计笔记 17：用生成式 AI 做设计与开发：能用的场景与要防的坑","生成式 AI 在工作流里真正站得住的三个场景 —— 造示例数据、解释陌生代码、起草初稿；它为什么会复现社会偏见、为什么会编得像真的，以及哪些内容绝不能直接采用。","2026-11-08",17,{"path":1519,"title":1520,"description":1521,"date":1522,"series":1435,"seriesOrder":1523},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-16","软件分析与设计笔记 16：从 persona 到 URS：一份交付物的完整骨架","把前面十几篇串成一份能交出去的东西：用户需求规格说明书的九个部分、每一章来自哪个环节、由谁确认，以及面向决策者的提案该讲清哪三件事。","2026-11-07",16,{"path":1525,"title":1526,"description":1527,"date":1528,"series":1435,"seriesOrder":1529},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-15","软件分析与设计笔记 15：非功能需求：把「快、稳、安全」变成数字","为什么「系统要快」不是一条需求：非功能需求怎么量化，决定性能的用户量、交易量、数据量三张表怎么估，安全需求要写清什么，以及可靠性的「不可用」该怎么定义。","2026-11-06",15,{"path":1531,"title":1532,"description":1533,"date":1534,"series":1435,"seriesOrder":1535},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-14","软件分析与设计笔记 14：软件测试：层级、方法与等价类划分","「我们测过了」这句话问题在哪：为什么静态评审和动态测试缺一不可，五个测试层级各自验证什么，白盒与黑盒的差别，以及用等价类划分和边界值把无限输入变成有限用例的方法。","2026-11-05",14,{"path":1537,"title":1538,"description":1539,"date":1540,"series":1435,"seriesOrder":1541},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-13","软件分析与设计笔记 13：需求获取：五种调查方法与一场访谈","需求从哪里来：五种事实调查方法各自适合回答什么、代价是什么，访谈从准备到记录的全过程，问卷设计里最容易犯的错，以及访谈记录必须写下来的那几项信息。","2026-11-04",13,{"path":1543,"title":1544,"description":1545,"date":1546,"series":1435,"seriesOrder":1547},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-12","软件分析与设计笔记 12：前端设计：把流程变成屏幕","界面设计不是「画得好看」：从活动图走到屏幕的三步，CRUD 任务流怎么当检查表，界面必须回答的三个问题，信息架构与五种导航类型，视觉一致性与留白，以及移动端与 Web 端的差别。","2026-11-03",12,{"path":1549,"title":1550,"description":1551,"date":1552,"series":1435,"seriesOrder":1553},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-11","软件分析与设计笔记 11：Agentic AI 与业务流程自动化","AI agent 到底是什么：五项能力与四个核心组件，什么场景该用定时任务、什么该用程序、什么才值得用 agent，两种实现路径的差别，必须提前防住的四类失败模式，以及设计方法从提示词到编排的演进。","2026-11-02",11,{"path":1555,"title":1556,"description":1557,"date":1558,"series":1435,"seriesOrder":1559},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-10","软件分析与设计笔记 10：精炼用例模型：include、extend 与泛化","三种精炼用例关系的判断标准：include 什么时候用、它和 extend 的区别到底在哪、参与者泛化与用例泛化各解决什么问题，以及精炼到什么程度就该停手。","2026-11-01",10,{"path":1561,"title":1562,"description":1563,"date":1564,"series":1435,"seriesOrder":1565},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-09","软件分析与设计笔记 09：功能需求（二）：从流程到业务实体","活动图画完之后，怎么从流程里筛出真正的业务实体：三条判断标准、哪些名词其实不该建实体、属性从哪里来，以及领域模型和后面的数据模型分别负责什么。","2026-10-31",9,{"path":1433,"title":5,"description":1429,"date":1428,"series":1435,"seriesOrder":1436},{"path":1568,"title":1569,"description":1570,"date":1571,"series":1435,"seriesOrder":1572},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-07","软件分析与设计笔记 07：用户故事：把需求写成可验收的一句话","用户故事的三段结构，怎么从用户画像和数据地图里把它提取出来，一条故事多大算合适、时序为什么也是需求的一部分，以及验收标准怎么写才算真的可验收。","2026-10-29",7,{"path":1574,"title":1575,"description":1576,"date":1577,"series":1435,"seriesOrder":1578},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-06","软件分析与设计笔记 06：范围界定（数据侧）：用数据校验你的判断","访谈结论和数字对不上时该怎么办：分析的四个层次分别在回答什么问题、为什么不能跳级、数据准备与搜集怎么做成一张应用地图，以及数据最容易在哪几个地方骗你。","2026-10-28",6,{"path":1580,"title":1581,"description":1582,"date":1583,"series":1435,"seriesOrder":1584},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-05","软件分析与设计笔记 05：范围界定（用户侧）：用户画像与客户旅程图","为什么「所有用户」是一个陷阱，项目边界该怎么划，用户画像要写哪五个要素、最容易写成什么废纸，客户旅程图怎么把阶段、想法和情绪画在同一张纸上，以及怎么从这两样东西一步步推到可验证的需求。","2026-10-27",5,{"path":1586,"title":1587,"description":1588,"date":1589,"series":1435,"seriesOrder":1590},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-04","软件分析与设计笔记 04：接手陌生案例的第一步：三问与一张表","拿到一个完全陌生的业务案例，怎么从零开始不跑偏：只问 Who、What、Why 三个问题，把「问题—事实—假设—理由」记成一张四列表，说清为什么假设必须写明理由，以及带着问题进访谈为什么比空手去有效得多。","2026-10-26",4,{"path":1592,"title":1593,"description":1594,"date":1595,"series":1435,"seriesOrder":1391},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-03","软件分析与设计笔记 03：Agile 与 Scrum：迭代、角色与产出物","从「三周做完交付，客户说这不是我要的」讲起，说清瀑布式与迭代式的根本差别，Scrum 的三个角色和三种产出物，一个 sprint 里四次固定活动各自在解决什么问题，以及敏捷里最常见的几个误解。","2026-10-25",{"path":1597,"title":1598,"description":1599,"date":1600,"series":1435,"seriesOrder":1394},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-02","软件分析与设计笔记 02：设计思维、用户驱动与数据驱动","从「客服要一个能一次看完所有排期的界面」这个翻车案例讲起，说清设计思维到底是什么、用户驱动与数据驱动各自能看见什么又看不见什么、为什么真正的落点是把两者合起来的 data-informed design，以及访谈中怎么问出有用的信息。","2026-10-24",{"path":1602,"title":1603,"description":1604,"date":1605,"series":1435,"seriesOrder":1606},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-01","软件分析与设计笔记 01：课程地图与从需求到设计的主干","这一篇不写具体方法，先把整条链路铺开：软件分析与设计到底要解决什么问题，从需求到设计分成哪两段，中间有哪十几个环节，一份完整的交付物里应该有什么，以及后面 27 篇笔记怎么排、新人该按什么顺序读。","2026-10-23",1,{"path":1608,"title":1609,"description":1610,"date":1611,"series":1612,"seriesOrder":1606},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-01","Spring 学习笔记 01：IoC、依赖注入与第一个 Spring Boot 应用","从「手写 new 出来的三层结构」的痛点讲起，说清 IoC 容器、Bean 与依赖注入是怎么回事，再用 Maven 与 Spring Initializr 跑起第一个能返回 JSON 的应用。","2026-10-15","Spring 学习笔记",{"path":1614,"title":1615,"description":1616,"date":1611,"series":1612,"seriesOrder":1394},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-02","Spring 学习笔记 02：Spring Data JPA 基础——实体映射与 Repository","用 ORM 把类映射到表：@Entity、@Id、@GeneratedValue 怎么用，CrudRepository 三层接口怎么选，H2 与 MySQL 怎么配，以及 ddl-auto 到底该写哪个值。",{"path":1618,"title":1619,"description":1620,"date":1611,"series":1612,"seriesOrder":1391},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-03","Spring 学习笔记 03：JPA 查询——派生方法、JPQL 与分页排序","Repository 自带的方法只够按主键查，这篇讲三种自己造查询的方式：方法名派生、@Query 写 JPQL、原生 SQL，以及 Pageable 分页排序和投影返回。",{"path":1622,"title":1623,"description":1624,"date":1611,"series":1612,"seriesOrder":1590},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-04","Spring 学习笔记 04：JPA 关联映射与仓储测试","一对多、多对多怎么映射，谁负责维护外键，FetchType 与 CascadeType 该怎么选，顺带用 @DataJpaTest 与 AssertJ 把仓储层测起来。",{"path":1626,"title":1627,"description":1628,"date":1611,"series":1612,"seriesOrder":1584},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-05","Spring 学习笔记 05：Spring MVC 与 Thymeleaf 入门","一次页面请求在 Spring MVC 里走了哪几步，@Controller 怎么把数据交给模板，@PathVariable 与 @RequestParam 怎么取参数，以及控制器为什么必须是无状态的。",{"path":1630,"title":1631,"description":1632,"date":1611,"series":1612,"seriesOrder":1578},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-06","Spring 学习笔记 06：表单绑定与服务端校验","把表单字段绑到对象上，用 Bean Validation 注解做服务端校验，把错误信息显示回用户刚填的那一页，再补上自定义校验注解与全局异常处理。",{"path":1634,"title":1635,"description":1636,"date":1611,"series":1612,"seriesOrder":1572},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-07","Spring 学习笔记 07：Thymeleaf 片段复用与国际化","用 th:fragment 与 th:insert\u002Fth:replace 把导航栏、页脚抽成片段，页面套用统一布局，再用 messages.properties 与 LocaleResolver 做中英文切换。",{"path":1638,"title":1639,"description":1640,"date":1611,"series":1612,"seriesOrder":1436},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-08","Spring 学习笔记 08：HTTP Session 与会话状态管理","HTTP 无状态带来的问题，Cookie 与 Session 怎么分工，Spring 里读写会话的几种方式，以及会话固定、超时、多实例共享这些必须想清楚的事。",{"path":1642,"title":1643,"description":1644,"date":1611,"series":1612,"seriesOrder":1565},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-09","Spring 学习笔记 09：拦截器 HandlerInterceptor","登录检查、权限判断、请求耗时统计这类横切逻辑该放哪：HandlerInterceptor 的三个回调各在什么时机执行，怎么注册与排除路径，以及它和过滤器的分工。",{"path":1646,"title":1647,"description":1648,"date":1611,"series":1612,"seriesOrder":1559},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-10","Spring 学习笔记 10：Service 层与事务","一组数据库操作要么全成功要么全失败，靠 @Transactional 怎么做到；传播行为、隔离级别、回滚规则怎么选，以及自调用失效、乐观锁悲观锁这些实战里的坑。",{"path":1650,"title":1651,"description":1652,"date":1611,"series":1612,"seriesOrder":1553},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-11","Spring 学习笔记 11：REST API 基础——把 Service 暴露成接口","同一套 Service 怎么同时服务页面和客户端：@RestController 的路由与方法语义、状态码怎么给、DTO 怎么定，以及 MockMvc 端到端和切片测试的两种写法与各自的坑。",{"path":1654,"title":1655,"description":1656,"date":1611,"series":1612,"seriesOrder":1547},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-12","Spring 学习笔记 12：REST 进阶——版本、更新、跨域与文档化","接口能跑通之后要处理的几件事：URL 与响应结构怎么定、版本怎么演进、更新怎么做到可安全重试、CORS 怎么配、HATEOAS 与 Spring Data REST 该不该用。",{"path":1658,"title":1659,"description":1660,"date":1661,"series":1662,"seriesOrder":1590},"\u002Fposts\u002Fjava\u002Fjava-functionalprogramming-notes","Java 函数式编程笔记：Lambda、函数式接口与 Streams","学习 Java 函数式编程时的个人笔记：Lambda 表达式的五步简写与方法引用、常见函数式接口、Stream 流水线与惰性求值，以及创建流、中间操作、终止操作与收集器的用法。示例代码均为自写，运行结果来自本地实测。","2026-10-13","Java 学习笔记",{"path":1664,"title":1665,"description":1666,"date":1661,"series":1662,"seriesOrder":1391},"\u002Fposts\u002Fjava\u002Fjava-generics-notes","Java 泛型笔记：泛型方法、有界类型与泛型类","学习 Java 泛型时的个人笔记：从重复的重载方法与多态尝试讲起，再到泛型方法与类型推断、有界泛型类型、Comparable 与 Number 边界，以及泛型类在集合中的用法。示例代码均为自写，运行结果来自本地实测。",{"path":1668,"title":1669,"description":1670,"date":1671,"series":1672,"seriesOrder":1590},"\u002Fposts\u002Fmysql\u002Fmysql-learning-04","MySQL 学习笔记 04：数据操纵（增删改）","MySQL 学习笔记第四篇：INSERT 插入（含批量插入与 INSERT ... SELECT）、UPDATE 更新、DELETE 删除，以及各自的坑。","2026-10-10","SQL 学习笔记",{"path":1674,"title":1675,"description":1676,"date":1671,"series":1672,"seriesOrder":1584},"\u002Fposts\u002Fmysql\u002Fmysql-learning-05","MySQL 学习笔记 05：DDL 概览","MySQL 学习笔记第五篇：数据定义语言概览——Schema、Table、View、Index、Constraints 这几类对象分别对应哪些 CREATE \u002F ALTER \u002F DROP 命令。",{"path":1678,"title":1679,"description":1680,"date":1671,"series":1672,"seriesOrder":1578},"\u002Fposts\u002Fmysql\u002Fmysql-learning-06","MySQL 学习笔记 06：CREATE TABLE 与数据类型","MySQL 学习笔记第六篇：CREATE TABLE 的完整语法、常用数据类型的选型建议，以及外键的两种定义方式。",{"path":1682,"title":1683,"description":1684,"date":1671,"series":1672,"seriesOrder":1572},"\u002Fposts\u002Fmysql\u002Fmysql-learning-07","MySQL 学习笔记 07：索引","MySQL 学习笔记第七篇：索引的作用与 B+ 树结构、单列与复合索引的创建、索引的代价与适用场景。",{"path":1686,"title":1687,"description":1688,"date":1671,"series":1672,"seriesOrder":1436},"\u002Fposts\u002Fmysql\u002Fmysql-learning-08","MySQL 学习笔记 08：DROP 与 ALTER","MySQL 学习笔记第八篇：DROP 删除数据库对象、ALTER 修改表结构，以及常见的坑。",{"path":1690,"title":1691,"description":1692,"date":1671,"series":1672,"seriesOrder":1565},"\u002Fposts\u002Fmysql\u002Fmysql-learning-09","MySQL 学习笔记 09：约束与数据完整性","MySQL 学习笔记第九篇：SQL 约束——NOT NULL、CHECK、PRIMARY KEY、FOREIGN KEY 等如何保证数据完整性，以及它们的对比。",{"path":1694,"title":1695,"description":1696,"date":1671,"series":1672,"seriesOrder":1559},"\u002Fposts\u002Fmysql\u002Fmysql-learning-10","MySQL 学习笔记 10：视图","MySQL 学习笔记第十篇：视图（Views）的定义与特性、和表的对比、创建与使用，以及可更新视图的限制。",{"path":1698,"title":1699,"description":1700,"date":1671,"series":1672,"seriesOrder":1553},"\u002Fposts\u002Fmysql\u002Fmysql-learning-11","MySQL 学习笔记 11：存储过程","MySQL 学习笔记第十一篇：存储过程（Stored Procedures）的概念、创建与调用、带参数的写法，以及优缺点。",{"path":1702,"title":1703,"description":1704,"date":1705,"series":1672,"seriesOrder":1606},"\u002Fposts\u002Fmysql\u002Fmysql-learning-01","MySQL 学习笔记 01：MySQL 基础知识","MySQL 学习笔记第一篇：MySQL Server 与 Workbench 的安装，数据库与 DBMS 的基本概念，关系模型、ACID 事务、常见数据类型，以及后续的学习路线。","2026-10-09",{"path":1707,"title":1708,"description":1709,"date":1705,"series":1672,"seriesOrder":1394},"\u002Fposts\u002Fmysql\u002Fmysql-learning-02","MySQL 学习笔记 02：SELECT 查询与过滤","MySQL 学习笔记第二篇：SELECT 的基本语法与逻辑执行顺序，WHERE 过滤（比较、LIKE、IN、BETWEEN、NULL）、各种 JOIN、UNION 与子查询。",{"path":1711,"title":1712,"description":1713,"date":1705,"series":1672,"seriesOrder":1391},"\u002Fposts\u002Fmysql\u002Fmysql-learning-03","MySQL 学习笔记 03：聚合、分组与输出控制","MySQL 学习笔记第三篇：COUNT\u002FSUM\u002FAVG 等聚合函数、GROUP BY 分组、HAVING 过滤、ORDER BY 与 LIMIT，以及一条 SELECT 的完整逻辑执行顺序。",{"path":1715,"title":1716,"description":1717,"date":1718,"series":1662,"seriesOrder":1394},"\u002Fposts\u002Fjava\u002Fjava-oop-notes","Java 面向对象笔记：类、继承、多态与异常","学习 Java 面向对象编程时的个人笔记：类与对象、封装与访问修饰符、继承与多态、集合、抽象类与接口、异常处理。示例代码均为自写，运行结果来自本地实测。","2026-10-06",{"path":1720,"title":1721,"description":1722,"date":1723,"series":1662,"seriesOrder":1606},"\u002Fposts\u002Fjava\u002Fjava-basics-notes","Java 基础语法笔记：从环境安装到方法与数组","学习 Java 基础语法时的个人笔记：JDK 安装、输入输出、变量与运算符、条件与循环、字符串、数组与方法。示例代码均为自写，运行结果来自本地实测。","2026-10-05",{"path":1725,"title":1726,"description":1727,"date":1728,"series":1729,"seriesOrder":1606},"\u002Fposts\u002Fgit\u002Fgit-install-and-basics","Git 安装与基本使用","从装好 Git 到跑通日常提交流程，把最常用的几条命令和它们背后的三个区域讲清楚。","2026-10-04","Git 入门",{"path":1731,"title":1732,"description":1733,"date":1728,"series":1729,"seriesOrder":1394},"\u002Fposts\u002Fgit\u002Flocal-remote-repository","本地远程仓库的部署","用自己的机器当 Git 服务器：建一个裸仓库、走 SSH 连上去，把本地提交推上去。",{"path":1735,"title":1736,"description":1737,"date":1728,"series":1738,"seriesOrder":1606},"\u002Fposts\u002Fguide\u002Fguide-file-layout","文件都放在哪","文章、图片、封面分别放在仓库的什么位置，哪些目录可以放手改、哪些碰了会出问题。","博客使用指南",{"path":1740,"title":1741,"description":1742,"date":1728,"series":1738,"seriesOrder":1394},"\u002Fposts\u002Fguide\u002Fguide-frontmatter","frontmatter 每个字段是什么意思","文章开头那段被横线包住的字段，逐个说明作用、默认行为和写错的后果。",{"path":1744,"title":1745,"description":1746,"date":1728,"series":1738,"seriesOrder":1391},"\u002Fposts\u002Fguide\u002Fguide-markdown","正文怎么写 —— Markdown 与排版效果","标题、链接、图片、代码、表格分别怎么写，以及本站对它们做了哪些自定义处理。",1793268185008]