議管理系統(tǒng)詳細(xì)設(shè)計(jì)說(shuō)明書(shū)怎么寫(xiě):從表結(jié)構(gòu)到接口的實(shí)戰(zhàn)指南)
簡(jiǎn)介會(huì)議管理系統(tǒng)詳細(xì)設(shè)計(jì)說(shuō)明書(shū)是一份面向軟件開(kāi)發(fā)、系統(tǒng)設(shè)計(jì)與項(xiàng)目管理人員的技術(shù)參考文檔用于指導(dǎo)會(huì)議管理系統(tǒng)從總體架構(gòu)到具體對(duì)象設(shè)計(jì)的落地實(shí)現(xiàn)。文檔圍繞概要設(shè)計(jì)、系統(tǒng)環(huán)境、系統(tǒng)模塊命名規(guī)范、對(duì)象設(shè)計(jì)四大部分展開(kāi)其中系統(tǒng)環(huán)境明確了操作系統(tǒng)、數(shù)據(jù)庫(kù)與客戶端選型依據(jù)對(duì)象設(shè)計(jì)部分給出了總體類圖結(jié)構(gòu)、關(guān)鍵類定義以及信息管理、預(yù)約管理、會(huì)議管理、人員管理、部門管理、職位管理、設(shè)備管理等核心模塊的關(guān)系類圖能幫助讀者快速理解系統(tǒng)分層結(jié)構(gòu)與權(quán)限邊界。包內(nèi)為1個(gè)doc格式文檔壓縮包約1.9MB內(nèi)容排版規(guī)整包含目錄、版本變更記錄與模塊簽字確認(rèn)頁(yè)便于團(tuán)隊(duì)評(píng)審和二次開(kāi)發(fā)參考。該資源已有444人學(xué)習(xí)下載適合需要編寫(xiě)同類詳細(xì)設(shè)計(jì)說(shuō)明書(shū)或梳理會(huì)議類系統(tǒng)架構(gòu)的開(kāi)發(fā)人員直接參考。1. 會(huì)議管理系統(tǒng)詳細(xì)設(shè)計(jì)說(shuō)明書(shū)到底該寫(xiě)多細(xì)先想清楚它是給誰(shuí)看的一份會(huì)議管理系統(tǒng)的詳細(xì)設(shè)計(jì)說(shuō)明書(shū).doc在多數(shù)團(tuán)隊(duì)里是“寫(xiě)了沒(méi)人看、評(píng)審走過(guò)場(chǎng)、開(kāi)發(fā)自己另搞一套”的尷尬存在。但如果你把它當(dāng)成會(huì)議管理系統(tǒng)從需求到代碼之間的唯一施工圖情況會(huì)完全不同后端要照著定表結(jié)構(gòu)和接口前端要照著畫(huà)頁(yè)面流轉(zhuǎn)測(cè)試要照著寫(xiě)用例新同事要照著接需求。詳細(xì)設(shè)計(jì)說(shuō)明書(shū)不是給領(lǐng)導(dǎo)匯報(bào)的 PPT不是把需求文檔復(fù)制粘貼一遍而是把“做什么”翻譯成“怎么做”的技術(shù)契約。寫(xiě)細(xì)了沒(méi)人讀得動(dòng)寫(xiě)粗了開(kāi)發(fā)全靠猜。我的經(jīng)驗(yàn)是這份文檔的粗細(xì)標(biāo)準(zhǔn)只有一條——坐在工位上的后端和前端能不能不看產(chǎn)品、不找你復(fù)聊直接動(dòng)手寫(xiě)代碼。能達(dá)到這個(gè)標(biāo)準(zhǔn)這份詳細(xì)設(shè)計(jì)說(shuō)明書(shū)就是一個(gè)能落地的方案。2. 先把骨架搭起來(lái)會(huì)議管理系統(tǒng)詳細(xì)設(shè)計(jì)說(shuō)明書(shū)的章節(jié)結(jié)構(gòu)與最小目錄2.1 章節(jié)組織順序從用例到類圖再落到接口和表結(jié)構(gòu)很多初寫(xiě)者一上來(lái)就貼數(shù)據(jù)庫(kù)建表語(yǔ)句結(jié)果評(píng)審會(huì)上被人問(wèn)“為什么會(huì)議要分主表和擴(kuò)展表”時(shí)答不上來(lái)。詳細(xì)設(shè)計(jì)說(shuō)明書(shū)的章節(jié)順序本質(zhì)上是設(shè)計(jì)推導(dǎo)過(guò)程的復(fù)現(xiàn)先有業(yè)務(wù)用例才抽象出實(shí)體先有實(shí)體關(guān)系才定表結(jié)構(gòu)先有操作意圖才設(shè)計(jì)接口和頁(yè)面交互。我慣用的章節(jié)順序是引言與術(shù)語(yǔ) → 系統(tǒng)架構(gòu)與部署視圖 → 功能模塊設(shè)計(jì)含用例到類的映射 → 數(shù)據(jù)庫(kù)設(shè)計(jì) → 接口設(shè)計(jì) → 頁(yè)面流轉(zhuǎn)設(shè)計(jì) → 定時(shí)任務(wù)與異常處理 → 安全與權(quán)限。其中數(shù)據(jù)庫(kù)設(shè)計(jì)、接口設(shè)計(jì)、頁(yè)面流轉(zhuǎn)三章占全文字?jǐn)?shù)的七成以上其他章節(jié)能短則短。術(shù)語(yǔ)表是新手最容易應(yīng)付、老手最容易翻車的地方。會(huì)議管理系統(tǒng)里“會(huì)議室占用”和“會(huì)議室預(yù)訂”是兩個(gè)概念前者是物理狀態(tài)后者是業(yè)務(wù)單據(jù)。如果文檔里混用開(kāi)發(fā)出來(lái)的代碼會(huì)出現(xiàn)“預(yù)訂成功但會(huì)議室狀態(tài)沒(méi)改”這種低級(jí) bug。我一般會(huì)在術(shù)語(yǔ)表里把“會(huì)議、預(yù)定、占用、釋放、取消、作廢”這六個(gè)詞的定義寫(xiě)死后續(xù)所有章節(jié)只用這套術(shù)語(yǔ)。2.2 用表格定義角色與權(quán)限邊界評(píng)審會(huì)揪最多的地方權(quán)限設(shè)計(jì)是會(huì)議管理系統(tǒng)里最容易產(chǎn)生歧義的模塊。普通員工能訂會(huì)議室嗎部門秘書(shū)能訂跨部門的會(huì)議室嗎管理員能不能改已結(jié)束的會(huì)議這些問(wèn)題不寫(xiě)清楚開(kāi)發(fā)就會(huì)自由發(fā)揮驗(yàn)收時(shí)產(chǎn)品才發(fā)現(xiàn)權(quán)限模型對(duì)不上。角色可創(chuàng)建會(huì)議可修改/取消會(huì)議可審批會(huì)議可管理會(huì)議室資源普通員工是僅本人是僅本人創(chuàng)建的否否部門秘書(shū)是是本部門所有是本部門會(huì)議否系統(tǒng)管理員是是全部是全部是這張表確定之后數(shù)據(jù)庫(kù)里的角色字段、后端接口里的權(quán)限注解、前端按鈕的顯隱規(guī)則全部從這張表推導(dǎo)。具體實(shí)現(xiàn)時(shí)我給會(huì)議表加了creator_id和dept_id兩個(gè)字段權(quán)限判斷就不需要聯(lián)表查用戶部門直接比對(duì)即可。這套設(shè)計(jì)的代價(jià)是部門調(diào)整時(shí)歷史數(shù)據(jù)歸屬不變但對(duì)會(huì)議系統(tǒng)這種輕量業(yè)務(wù)來(lái)說(shuō)換部門前創(chuàng)建的會(huì)議仍歸原部門業(yè)務(wù)上是合理的。2.3 畫(huà)圖規(guī)范用什么圖、畫(huà)到什么粒度詳細(xì)設(shè)計(jì)說(shuō)明書(shū)里最常出現(xiàn)的圖有四種用例圖、類圖、時(shí)序圖、狀態(tài)圖。我的經(jīng)驗(yàn)是用例圖畫(huà)到“角色 動(dòng)詞 對(duì)象”即可比如“員工創(chuàng)建會(huì)議”“秘書(shū)審批會(huì)議”類圖必須畫(huà)出核心實(shí)體和關(guān)鍵方法但不要畫(huà)到每個(gè) getter/setter時(shí)序圖只畫(huà)跨模塊的復(fù)雜交互比如“創(chuàng)建會(huì)議 → 鎖定會(huì)議室 → 發(fā)送通知”的完整鏈路狀態(tài)圖只畫(huà)會(huì)議狀態(tài)和會(huì)議室狀態(tài)兩條線。畫(huà)圖的粒度以“開(kāi)發(fā)能照著寫(xiě)代碼”為準(zhǔn)。類圖里Meeting類要標(biāo)出id、title、startTime、endTime、status這些字段和createMeeting()、cancelMeeting()這種關(guān)鍵方法但不必要畫(huà)getTitle()。時(shí)序圖里要畫(huà)出前端調(diào)接口、后端查庫(kù)、消息隊(duì)列發(fā)通知這三層但不要把 SQL 語(yǔ)句寫(xiě)進(jìn)時(shí)序圖。很多工具能直接從代碼反向生成文檔但那是記錄不是設(shè)計(jì)反推不出當(dāng)初為什么這么定。3. 數(shù)據(jù)模型是整份文檔的心臟從會(huì)議表設(shè)計(jì)到狀態(tài)機(jī)3.1 會(huì)議主表與參會(huì)關(guān)系表字段、類型、索引怎么定會(huì)議系統(tǒng)的核心表就三張會(huì)議主表、參會(huì)人關(guān)系表、會(huì)議室資源表。設(shè)計(jì)這三張表時(shí)最容易犯的錯(cuò)是把參會(huì)人直接存成逗號(hào)分隔的字符串。表面看省了一張表和一次聯(lián)表查詢實(shí)際上參會(huì)人統(tǒng)計(jì)、按人查會(huì)議、會(huì)前提醒按人推送全都變成字符串拆分的臟活。先把會(huì)議主表的建表語(yǔ)句寫(xiě)出來(lái)這是整個(gè)詳細(xì)設(shè)計(jì)說(shuō)明書(shū)的基準(zhǔn)CREATE TABLE meeting ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 會(huì)議ID, title VARCHAR(128) NOT NULL COMMENT 會(huì)議標(biāo)題, description TEXT COMMENT 會(huì)議描述, room_id BIGINT NOT NULL COMMENT 關(guān)聯(lián)會(huì)議室ID, creator_id BIGINT NOT NULL COMMENT 創(chuàng)建人ID, dept_id BIGINT NOT NULL COMMENT 創(chuàng)建人部門ID, start_time DATETIME NOT NULL COMMENT 會(huì)議開(kāi)始時(shí)間, end_time DATETIME NOT NULL COMMENT 會(huì)議結(jié)束時(shí)間, status TINYINT NOT NULL DEFAULT 0 COMMENT 狀態(tài): 0草稿, 1已發(fā)布, 2進(jìn)行中, 3已結(jié)束, 4已取消, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_room_time (room_id, start_time, end_time), KEY idx_creator (creator_id), KEY idx_status_start (status, start_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT會(huì)議主表;這張表的關(guān)鍵設(shè)計(jì)點(diǎn)有三個(gè)。第一room_id和start_time、end_time組成聯(lián)合索引因?yàn)椴樵儭澳硶?huì)議室在某個(gè)時(shí)間段是否可用”是最頻繁的操作索引能直接覆蓋。第二status用 TINYINT 而不是字符串省空間且查詢快狀態(tài)含義在文檔里用枚舉定義清楚。第三時(shí)間字段用 DATETIME 而不是 TIMESTAMP因?yàn)闀?huì)議有可能跨天DATETIME 不受 2038 年問(wèn)題限制。參會(huì)人關(guān)系表的設(shè)計(jì)重點(diǎn)在唯一約束CREATE TABLE meeting_participant ( id BIGINT AUTO_INCREMENT PRIMARY KEY, meeting_id BIGINT NOT NULL, user_id BIGINT NOT NULL, role TINYINT NOT NULL DEFAULT 0 COMMENT 0參會(huì)人, 1主持人, attend_status TINYINT NOT NULL DEFAULT 0 COMMENT 0待確認(rèn), 1已接受, 2已拒絕, UNIQUE KEY uk_meeting_user (meeting_id, user_id), KEY idx_user (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT會(huì)議參會(huì)人表;UNIQUE KEY uk_meeting_user (meeting_id, user_id)這條約束保證了同一個(gè)人不會(huì)被重復(fù)加入同一場(chǎng)會(huì)議這是業(yè)務(wù)規(guī)則在數(shù)據(jù)庫(kù)層的兜底。如果不在表結(jié)構(gòu)層面約束應(yīng)用層并發(fā)插入時(shí)會(huì)出現(xiàn)兩條重復(fù)記錄到時(shí)候清理數(shù)據(jù)就是血淚工程。attend_status字段是為了支持會(huì)前提醒和統(tǒng)計(jì)參會(huì)率如果需求里不需要“接受/拒絕”這個(gè)字段可以去掉但絕大多數(shù)會(huì)議系統(tǒng)都有這個(gè)需求。3.2 會(huì)議狀態(tài)機(jī)從草稿到已結(jié)束的狀態(tài)流轉(zhuǎn)會(huì)議狀態(tài)機(jī)是評(píng)審會(huì)上最容易扯皮的設(shè)計(jì)點(diǎn)。很多文檔寫(xiě)“會(huì)議有草稿和已發(fā)布兩種狀態(tài)”但沒(méi)寫(xiě)清楚從草稿到已發(fā)布誰(shuí)能操作、取消會(huì)議后會(huì)議室資源什么時(shí)候釋放。狀態(tài)機(jī)必須用表格把每個(gè)流轉(zhuǎn)的觸發(fā)條件和操作方定死當(dāng)前狀態(tài)目標(biāo)狀態(tài)觸發(fā)操作操作方附帶動(dòng)作草稿已發(fā)布提交發(fā)布創(chuàng)建人/秘書(shū)鎖定會(huì)議室、發(fā)通知已發(fā)布進(jìn)行中到達(dá)開(kāi)始時(shí)間系統(tǒng)自動(dòng)無(wú)進(jìn)行中已結(jié)束到達(dá)結(jié)束時(shí)間系統(tǒng)自動(dòng)釋放會(huì)議室已發(fā)布已取消取消會(huì)議創(chuàng)建人/秘書(shū)/管理員釋放會(huì)議室、發(fā)通知草稿已取消刪除草稿創(chuàng)建人無(wú)這里最容易漏的是“已發(fā)布 → 已取消”這條流轉(zhuǎn)中的會(huì)議室釋放動(dòng)作。如果取消會(huì)議只改狀態(tài)不釋放資源會(huì)議室資源表就會(huì)出現(xiàn)永久占用后面所有想訂這個(gè)會(huì)議室的人全部被擋。我在文檔里專門加了一條約束任何狀態(tài)變?yōu)椤耙讶∠被颉耙呀Y(jié)束”的事務(wù)必須同時(shí)釋放會(huì)議室資源兩步寫(xiě)在同一個(gè)數(shù)據(jù)庫(kù)事務(wù)里。會(huì)議室資源表相對(duì)簡(jiǎn)單核心是room_status字段0 空閑, 1 占用。會(huì)議室沒(méi)有“預(yù)訂中”這種中間狀態(tài)因?yàn)轭A(yù)訂動(dòng)作要么成功要么失敗成功即占用。這樣設(shè)計(jì)避免了一個(gè)會(huì)議室被兩場(chǎng)會(huì)議同時(shí)鎖定的并發(fā)問(wèn)題。3.3 關(guān)鍵約束與數(shù)據(jù)一致性并發(fā)訂會(huì)議室的坑會(huì)議室預(yù)訂本質(zhì)上是一個(gè)資源搶占問(wèn)題兩個(gè)人在同一毫秒提交同一會(huì)議室的預(yù)訂請(qǐng)求誰(shuí)能成功答案是只有一個(gè)能成功。實(shí)現(xiàn)這個(gè)約束的常見(jiàn)做法是“樂(lè)觀鎖 唯一索引”雙層保障。樂(lè)觀鎖的思路是更新會(huì)議室狀態(tài)前先查當(dāng)前狀態(tài)UPDATE room SET room_status 1 WHERE id ? AND room_status 0這條 SQL 在 InnoDB 下會(huì)行鎖天然串行化。但光靠樂(lè)觀鎖還不夠。如果會(huì)議室表和會(huì)議主表分離可能出現(xiàn)在會(huì)議室表更新成功、會(huì)議主表插入失敗的事務(wù)回滾后會(huì)議室狀態(tài)沒(méi)恢復(fù)的問(wèn)題。我的方案是把“插入會(huì)議記錄 更新會(huì)議室狀態(tài)”放在同一個(gè)數(shù)據(jù)庫(kù)事務(wù)里并且先更新會(huì)議室狀態(tài)再插入會(huì)議記錄這樣行鎖能盡早獲取降低死鎖概率。詳細(xì)設(shè)計(jì)說(shuō)明書(shū)里必須把這條事務(wù)邏輯和鎖順序?qū)懬宄駝t開(kāi)發(fā)實(shí)現(xiàn)時(shí)完全看心情。4. 把接口和頁(yè)面流轉(zhuǎn)寫(xiě)成開(kāi)發(fā)能直接抄作業(yè)的粒度4.1 接口定義請(qǐng)求/響應(yīng)示例加錯(cuò)誤碼詳細(xì)設(shè)計(jì)說(shuō)明書(shū)的接口章節(jié)不是寫(xiě)“創(chuàng)建會(huì)議接口入?yún)⑹菚?huì)議信息出參是成功失敗”而是要把請(qǐng)求體、響應(yīng)體、錯(cuò)誤碼、邊界條件全部寫(xiě)出來(lái)。以創(chuàng)建會(huì)議接口為例我慣用的寫(xiě)法是給每個(gè)必要字段標(biāo)注約束規(guī)則POST /api/v1/meetings 請(qǐng)求體: { title: 周會(huì), room_id: 12, start_time: 2025-06-10 10:00:00, end_time: 2025-06-10 11:00:00, participants: [101, 102, 103] } 響應(yīng)體成功: { code: 0, message: success, data: { meeting_id: 888, status: 0 } } 響應(yīng)體會(huì)議室沖突: { code: 40901, message: room_conflict, data: null }接口定義里最容易漏的是邊界條件。我在文檔中專門列了一張“參數(shù)校驗(yàn)規(guī)則表”把每個(gè)字段的校驗(yàn)邏輯寫(xiě)清楚title必填且最長(zhǎng) 128 字符start_time必須早于end_timeroom_id必須存在且狀態(tài)為 0participants數(shù)組長(zhǎng)度不能為 0 且不能包含創(chuàng)建人自己創(chuàng)建人自動(dòng)成為參會(huì)人。這些規(guī)則如果不寫(xiě)進(jìn)文檔前端要做一遍校驗(yàn)后端還要做一遍兩邊不一致就會(huì)出現(xiàn)前端提示“成功”而后端返回 400 的體驗(yàn)事故。錯(cuò)誤碼的規(guī)劃也要在文檔里定好。我習(xí)慣用“模塊號(hào) 序號(hào)”的結(jié)構(gòu)比如40901表示“會(huì)議室沖突”40001表示“參數(shù)校驗(yàn)失敗”40301表示“無(wú)權(quán)限操作該會(huì)議”。錯(cuò)誤碼一旦上線就不能輕易改所以文檔階段就要把可能出現(xiàn)的錯(cuò)誤場(chǎng)景列全。最簡(jiǎn)單的辦法是讓團(tuán)隊(duì)所有人對(duì)著頁(yè)面流程走一遍創(chuàng)建會(huì)議會(huì)出什么錯(cuò)、取消會(huì)議會(huì)出什么錯(cuò)、修改會(huì)議時(shí)間會(huì)出什么錯(cuò)逐一編號(hào)。4.2 頁(yè)面流轉(zhuǎn)與交互狀態(tài)每個(gè)按鈕背后改什么字段頁(yè)面流轉(zhuǎn)設(shè)計(jì)是連接前端和后端的橋梁。很多詳細(xì)設(shè)計(jì)說(shuō)明書(shū)在這里畫(huà)一個(gè)頁(yè)面原型圖就結(jié)束了但開(kāi)發(fā)拿到原型圖照樣不知道“取消會(huì)議”這個(gè)按鈕按下后要調(diào)用哪個(gè)接口、成功后頁(yè)面跳轉(zhuǎn)到哪里。我在文檔里用表格描述每個(gè)頁(yè)面的每個(gè)操作頁(yè)面操作調(diào)用接口成功后行為失敗后行為會(huì)議列表頁(yè)點(diǎn)擊“新建會(huì)議”無(wú)跳轉(zhuǎn)創(chuàng)建會(huì)議頁(yè)無(wú)創(chuàng)建會(huì)議頁(yè)點(diǎn)擊“提交”POST /api/v1/meetings跳轉(zhuǎn)會(huì)議詳情頁(yè)彈出錯(cuò)誤提示停留在當(dāng)前頁(yè)會(huì)議詳情頁(yè)點(diǎn)擊“取消會(huì)議”PUT /api/v1/meetings/{id}/cancel按鈕置灰狀態(tài)改為已取消彈出錯(cuò)誤提示按鈕可再點(diǎn)擊這張表的價(jià)值在于把前端的狀態(tài)管理和后端的接口調(diào)用對(duì)齊了。前端知道“取消會(huì)議”成功后要把按鈕置灰、顯示“已取消”標(biāo)簽后端知道要更新 meeting 表的 status 字段并釋放會(huì)議室資源。兩邊各做各的不會(huì)等到聯(lián)調(diào)時(shí)才發(fā)現(xiàn)“前端以為取消是同步操作后端實(shí)際做了異步處理”。4.3 定時(shí)任務(wù)與消息通知會(huì)前提醒怎么設(shè)計(jì)會(huì)議系統(tǒng)很少有純同步的業(yè)務(wù)會(huì)前提醒、會(huì)議超時(shí)未開(kāi)始自動(dòng)取消這類需求必須依賴定時(shí)任務(wù)。詳細(xì)設(shè)計(jì)說(shuō)明書(shū)中要明確兩個(gè)問(wèn)題任務(wù)輪詢的頻率多高、通知發(fā)的通道是什么。我的常見(jiàn)做法是每分鐘掃一次會(huì)議表找出start_time在 15 分鐘后且status 1的記錄發(fā)送站內(nèi)信和郵件通知。掃描 SQL 要帶上status條件否則已取消的會(huì)議也會(huì)被掃出來(lái)把參會(huì)人嚇一跳。通知失敗的處理也要寫(xiě)進(jìn)文檔。郵件發(fā)送失敗是常態(tài)常見(jiàn)做法是記錄失敗次數(shù)連續(xù)失敗 3 次后放棄并標(biāo)記為“通知失敗”由管理員在后臺(tái)人工處理。這塊如果不在文檔里定義清楚開(kāi)發(fā)實(shí)現(xiàn)時(shí)可能只捕獲異常打日志用戶收不到提醒也不知道找誰(shuí)反饋。5. 詳細(xì)設(shè)計(jì)說(shuō)明書(shū)避坑指南評(píng)審被問(wèn)倒的 5 個(gè)常見(jiàn)問(wèn)題5.1 現(xiàn)象會(huì)議室時(shí)間沖突檢測(cè)用應(yīng)用層代碼判斷很多實(shí)現(xiàn)的思路是先查一遍該時(shí)間段有沒(méi)有會(huì)議記錄沒(méi)有就插入有就報(bào)錯(cuò)。這是典型的先查后寫(xiě)check-then-act模式并發(fā)場(chǎng)景下兩請(qǐng)求同時(shí)查都發(fā)現(xiàn)沒(méi)有記錄然后都插入成功會(huì)議室被預(yù)訂了兩場(chǎng)。原因是沒(méi)有在數(shù)據(jù)庫(kù)層做唯一約束或鎖保護(hù)。解決方法是把沖突檢測(cè)放在數(shù)據(jù)庫(kù)層用room_id start_time end_time的區(qū)間判斷配合數(shù)據(jù)庫(kù)鎖實(shí)現(xiàn)或者在會(huì)議室表上做條件更新。我在文檔里直接寫(xiě)死方案事務(wù)內(nèi)先更新會(huì)議室狀態(tài)用UPDATE room SET room_status 1 WHERE id ? AND room_status 0的受影響行數(shù)判斷是否沖突受影響行數(shù)為 0 說(shuō)明搶不到直接返回“會(huì)議室沖突”。5.2 現(xiàn)象修改會(huì)議時(shí)間時(shí)沒(méi)有檢查會(huì)議室可用性會(huì)議已發(fā)布后允許改時(shí)間這個(gè)需求很常見(jiàn)。但很多實(shí)現(xiàn)只改了meeting表的start_time和end_time完全沒(méi)有檢查新時(shí)間段有沒(méi)有與其他會(huì)議沖突。結(jié)果就是會(huì)議室被兩場(chǎng)會(huì)議疊加占用現(xiàn)場(chǎng)參會(huì)的人撞車。原因是詳細(xì)設(shè)計(jì)說(shuō)明書(shū)里只寫(xiě)了創(chuàng)建會(huì)議做沖突檢測(cè)漏了修改時(shí)間的場(chǎng)景。解決方法是文檔里明確更新會(huì)議時(shí)間走獨(dú)立的接口該接口在事務(wù)里重新做一次會(huì)議室占用檢測(cè)檢測(cè)通過(guò)才允許更新。接口返回的錯(cuò)誤碼可以沿用40901前端拿到后提示“該時(shí)間段會(huì)議室已被預(yù)訂”。5.3 現(xiàn)象取消會(huì)議后會(huì)議室狀態(tài)還是“占用”這類 bug 極難排查現(xiàn)象是會(huì)議室明明沒(méi)人用但系統(tǒng)提示不可預(yù)訂。原因多半是取消會(huì)議的邏輯只更新了meeting表的status沒(méi)有把room表的room_status改回來(lái)。這是因?yàn)闀?huì)議狀態(tài)機(jī)表里“已取消”狀態(tài)標(biāo)了但“附帶動(dòng)作”漏寫(xiě)了。解決方法是文檔里把這類跨表操作全部畫(huà)進(jìn)狀態(tài)機(jī)表每一條狀態(tài)流轉(zhuǎn)的附帶動(dòng)作寫(xiě)清楚并且要求開(kāi)發(fā)實(shí)現(xiàn)時(shí)用事務(wù)保證兩步操作同時(shí)成功或同時(shí)失敗。我通常在文檔中加一條硬性約束任何涉及會(huì)議室釋放的邏輯都必須寫(xiě)單元測(cè)試驗(yàn)證“取消后會(huì)議室可被其他會(huì)議預(yù)訂”。5.4 現(xiàn)象參會(huì)人收到了已取消會(huì)議的通知會(huì)前提醒的定時(shí)任務(wù)掃到了狀態(tài)為“已取消”的會(huì)議給參會(huì)人推送了“會(huì)議即將開(kāi)始”的通知。用戶一臉懵地按時(shí)到會(huì)議室結(jié)果會(huì)議室空著。原因是定時(shí)任務(wù)的 SQL 只過(guò)濾了start_time在 15 分鐘后沒(méi)有過(guò)濾status。解決方法是文檔里明確掃描條件必須包含status 1已發(fā)布狀態(tài)排除草稿、已取消、已結(jié)束的會(huì)議。我還會(huì)在文檔里加一條狀態(tài)為“草稿”的會(huì)議不推送任何通知因?yàn)閯?chuàng)建人還在編輯推送出去等于泄露半成品。5.5 現(xiàn)象刪除會(huì)議記錄導(dǎo)致歷史統(tǒng)計(jì)報(bào)表對(duì)不上管理系統(tǒng)里必然有“統(tǒng)計(jì)各部門本月開(kāi)會(huì)次數(shù)”的需求統(tǒng)計(jì)一般直接查meeting表按dept_id分組。如果允許物理刪除會(huì)議記錄統(tǒng)計(jì)結(jié)果就會(huì)和實(shí)際發(fā)生過(guò)的情況對(duì)不上時(shí)間一長(zhǎng)數(shù)據(jù)完全不可信。原因是文檔里沒(méi)區(qū)分“刪除草稿”和“刪除已發(fā)布會(huì)議”。解決方案是設(shè)計(jì)兩條路徑草稿可以物理刪除已發(fā)布或已結(jié)束的會(huì)議只能“取消”或“歸檔”不能物理刪除。deleted_flag字段設(shè)置為 1 表示已刪除查詢默認(rèn)加WHERE deleted_flag 0但統(tǒng)計(jì)報(bào)表會(huì)查全量數(shù)據(jù)。6. 從詳細(xì)設(shè)計(jì)說(shuō)明書(shū)到代碼骨架用文檔反哺開(kāi)發(fā)效率詳細(xì)設(shè)計(jì)說(shuō)明書(shū)寫(xiě)完不是扔給開(kāi)發(fā)就完事了它可以反哺開(kāi)發(fā)過(guò)程。我習(xí)慣在文檔定稿后做兩件事第一把數(shù)據(jù)庫(kù)設(shè)計(jì)章節(jié)的建表語(yǔ)句直接發(fā)給 DBA 評(píng)審讓 DBA 在索引和字段類型上提意見(jiàn)這比開(kāi)發(fā)寫(xiě)完代碼再改表結(jié)構(gòu)省太多事第二把接口定義章節(jié)導(dǎo)出成 Postman 集合或直接用工具生成前后端的基礎(chǔ)代碼后端拿到接口路徑和參數(shù)定義就能寫(xiě) Controller前端拿到響應(yīng)結(jié)構(gòu)就能寫(xiě)類型定義不需要來(lái)回翻文檔。驗(yàn)證文檔是否合格的最直接方法是讓一個(gè)不熟悉這個(gè)項(xiàng)目的新同事只讀文檔、不找任何人問(wèn)嘗試搭一個(gè)最小可用的會(huì)議創(chuàng)建功能。如果他能獨(dú)立完成“填表單 → 調(diào)接口 → 寫(xiě)庫(kù) → 返回成功”的全鏈路說(shuō)明這份詳細(xì)設(shè)計(jì)說(shuō)明書(shū)的粒度是達(dá)標(biāo)的。如果中途卡住卡住的地方就是文檔缺失或含糊的部分記錄下來(lái)補(bǔ)進(jìn)去。實(shí)踐下來(lái)評(píng)審會(huì)開(kāi)三輪不如這樣試一次有效。我踩過(guò)的最大一個(gè)坑是文檔里只畫(huà)了類圖和時(shí)序圖沒(méi)寫(xiě)清狀態(tài)機(jī)的“附帶動(dòng)作”結(jié)果開(kāi)發(fā)把“釋放會(huì)議室”這一步實(shí)現(xiàn)成了訂閱消息異步處理。線上出現(xiàn)會(huì)議室被永久占用后排查了兩天才定位到是異步消息積壓。后來(lái)我給自己定了一條規(guī)矩詳細(xì)設(shè)計(jì)說(shuō)明書(shū)里的每一條狀態(tài)流轉(zhuǎn)、每一次字段更新、每一個(gè)通知發(fā)送都要寫(xiě)出是同步還是異步、失敗怎么兜底。這份會(huì)議管理系統(tǒng)詳細(xì)設(shè)計(jì)說(shuō)明書(shū)寫(xiě)到這個(gè)粒度開(kāi)發(fā)幾乎不用回來(lái)找你問(wèn)“這塊邏輯你當(dāng)初是怎么想的”。希望這份實(shí)戰(zhàn)拆解能幫你在寫(xiě)同類文檔時(shí)少走彎路。本文還有配套的精品資源點(diǎn)擊獲取