設(shè)計方案:從臺賬、流水到并發(fā)控制的實戰(zhàn)指南)
簡介這份《庫存管理系統(tǒng)設(shè)計方案》是一份面向管理信息系統(tǒng)初學(xué)者、相關(guān)專業(yè)學(xué)生及企業(yè)信息化人員的完整設(shè)計方案文檔。內(nèi)容圍繞庫存管理系統(tǒng)的建設(shè)展開從MIS基本概念、研究背景與意義入手系統(tǒng)梳理了數(shù)據(jù)庫系統(tǒng)設(shè)計與SQL語言應(yīng)用并以Visual Basic搭配Access 2000為開發(fā)工具詳細(xì)說明了需求分析、模塊劃分、數(shù)據(jù)庫設(shè)計與程序結(jié)構(gòu)等環(huán)節(jié)。通過入庫、出庫、庫存查詢、庫存預(yù)警等功能模塊展示了如何實現(xiàn)數(shù)據(jù)一致性強、安全性較高的庫存管理應(yīng)用系統(tǒng)。資源為1個doc文檔壓縮包大小約604KB篇幅完整、結(jié)構(gòu)清晰其中包含需求分析、模塊劃分、數(shù)據(jù)庫設(shè)計、程序結(jié)構(gòu)及源代碼等章節(jié)適合用于課程設(shè)計參考、畢業(yè)設(shè)計選題或企業(yè)庫存管理系統(tǒng)的早期方案借鑒。已有126人學(xué)習(xí)下載具備一定的參考價值。1. 庫存管理系統(tǒng)設(shè)計方案賬面庫存不等于可用庫存的落差某電商團隊的新系統(tǒng)上線第三周前臺仍然顯示“有貨”倉庫里卻已經(jīng)空了。后臺工程師核對數(shù)據(jù)后發(fā)現(xiàn)系統(tǒng)里的“庫存”做得就像一個計數(shù)器入庫加、出庫減減到負(fù)數(shù)也不管盤點差異全靠人肉改數(shù)。這個場景不是在講某個開源項目而是在講「庫存管理系統(tǒng)設(shè)計方案」這個文檔到底該覆蓋什么。一套能用的方案核心不是把增刪改查寫完而是要定義清楚四件事庫存賬長什么樣、每筆變動從哪來、并發(fā)下怎么保證不超賣、盤點對不上賬時怎么處置。這篇筆記就按這個順序展開適合準(zhǔn)備從零搭建庫存系統(tǒng)或正在重構(gòu)老庫存模塊的工程師。方案里不引入微服務(wù)、不堆中間件全部基于一個關(guān)系型數(shù)據(jù)庫就能落地。2. 先把架子搭對模塊邊界、數(shù)據(jù)流向與單據(jù)先行2.1 六個核心模塊劃分庫存臺賬是唯一事實源我把一個標(biāo)準(zhǔn)庫存管理系統(tǒng)拆成六個模塊商品中心、倉庫中心、單據(jù)中心、庫存臺賬、庫存流水、對賬與預(yù)警。商品中心管 SKU 與計量單位倉庫中心管倉庫、庫位、庫區(qū)狀態(tài)單據(jù)中心管所有庫存變動的來源包括采購入庫單、銷售出庫單、盤點單、調(diào)撥單、調(diào)整單臺賬管實時余量流水管每一筆變動軌跡對賬與預(yù)警做每日勾稽和異常提醒。這個劃分里最關(guān)鍵的一條紅線是庫存臺賬是全系統(tǒng)唯一的“事實源”任何時候要回答“現(xiàn)在有多少貨”只看臺賬表不看單據(jù)匯總不算歷史加減。很多系統(tǒng)翻車就是因為不同模塊各算各的賬采購模塊問采購數(shù)、銷售模塊問可賣數(shù)兩邊數(shù)據(jù)不一致最后線上超賣、倉庫空轉(zhuǎn)。臺賬獨立出來的好處是所有模塊共用同一份數(shù)據(jù)任何模塊要改庫存必須先寫單據(jù)再由單據(jù)去驅(qū)動臺賬變化。我一般還會在倉庫中心里給庫位加一個狀態(tài)字段標(biāo)記“正?!薄皟鼋Y(jié)”“盤點中”。這個字段看著不起眼但在后面處理盤點凍結(jié)和庫位隔離時會省掉大量麻煩。如果你要接倉儲作業(yè)WMS這個模塊可以直接擴展成庫位級的上架、揀貨、復(fù)核不需要改臺賬結(jié)構(gòu)。2.2 數(shù)據(jù)流向與單據(jù)驅(qū)動先有單據(jù)、后有庫存變動單據(jù)驅(qū)動是這套方案的第二個紅線。簡單說不允許任何代碼路徑直接對 inventory_balance 做 UPDATE 或 INSERT所有庫存變動必須經(jīng)過“單據(jù)審核 → 臺賬變動 → 流水記錄 → 單據(jù)完成”這條鏈路。我設(shè)計單據(jù)狀態(tài)時只保留四個狀態(tài)草稿、已審核、已完成、已作廢。草稿是錄入階段不影響庫存已審核代表單據(jù)業(yè)務(wù)上已生效但還沒扣減庫存已完成代表庫存事務(wù)已經(jīng)成功提交已作廢用于反操作。日常最容易踩的坑是客服說訂單取消開發(fā)為了省事直接寫一條 UPDATE 把庫存加回去。這個操作當(dāng)時沒問題但月底對賬時流水里找不到這筆回補賬就永遠(yuǎn)對不平了。正確的反操作流程是原銷售出庫單作廢生成一張紅字沖銷單沖銷單審核后走一次庫存增加流水里留下“SALES_RETURN / 銷售退貨”類型的記錄。這樣一來臺賬、流水、單據(jù)三者始終能對上對賬腳本只需要檢查“臺賬期末 臺賬期初 流水發(fā)生額”就夠了不用猜測某一筆數(shù)據(jù)是人肉改的。2.3 技術(shù)選型參考從單庫到可擴展的折中方案技術(shù)選型不用追求新潮庫存系統(tǒng)對一致性要求遠(yuǎn)高于對性能的要求。第一版建議用一個支持事務(wù)的關(guān)系型數(shù)據(jù)庫比如 MySQL 或 PostgreSQL單實例部署臺賬和流水放同一個庫。緩存只用來做熱點 SKU 的查詢加速不參與扣減扣減永遠(yuǎn)回源到數(shù)據(jù)庫。我整理了這樣一張選型參考表層選型理由數(shù)據(jù)庫MySQL/PostgreSQL事務(wù)保證行鎖與樂觀鎖成熟緩存Redis只緩存查詢結(jié)果不作為扣減依據(jù)應(yīng)用層任選后端語言事務(wù)邊界由代碼控制語言無關(guān)分庫分表暫不做單庫可以支撐絕大多數(shù)中小業(yè)務(wù)量為什么我不建議第一版就上分庫分表因為庫存表按 SKU 維度存儲熱點行其實很集中分庫后跨庫事務(wù)會讓“扣減流水單據(jù)完成”的一致性變得非常難做。真到單庫扛不住的時候優(yōu)先把流水表按月分區(qū)或歸檔臺賬表依然單庫單表這樣能撐更久。記住一個原則庫存方案先求對再求快性能問題可以用緩存擋一致性問題擋不住。3. 庫存核心表設(shè)計表建對了一半的坑就消失了3.1 庫存余額表把維度和數(shù)量約束立住庫存余額表的粒度是“倉庫 庫位 SKU”這張表只存當(dāng)前實時數(shù)量不存歷史。字段上我會拆出三個數(shù)量字段quantity 表示賬面總庫存locked_quantity 表示已被訂單預(yù)占的數(shù)量兩者相減就是可賣庫存。不把可賣庫存單獨冗余成一個字段是為了避免出現(xiàn)“總量對、可賣不對”的臟數(shù)據(jù)。我一般這樣建表CREATE TABLE inventory_balance ( id BIGINT PRIMARY KEY AUTO_INCREMENT, warehouse_id BIGINT NOT NULL COMMENT 倉庫ID, location_id BIGINT NOT NULL COMMENT 庫位ID, sku_id BIGINT NOT NULL COMMENT 商品SKU ID, quantity INT NOT NULL DEFAULT 0 COMMENT 賬面庫存總量, locked_quantity INT NOT NULL DEFAULT 0 COMMENT 預(yù)占鎖定數(shù)量, version INT NOT NULL DEFAULT 0 COMMENT 樂觀鎖版本號, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_wh_loc_sku (warehouse_id, location_id, sku_id), CONSTRAINT chk_quantity CHECK (quantity 0), CONSTRAINT chk_locked CHECK (locked_quantity 0 AND locked_quantity quantity) ) COMMENT 庫存余額表;注意兩個約束quantity 必須大于等于 0locked_quantity 必須小于等于 quantity。這兩個約束是防超賣的最后一道閘門。但要特別提醒MySQL 5.7 及以下對 CHECK 約束是解析通過但不強制執(zhí)行的實際生效得靠應(yīng)用層事務(wù)里的條件更新。如果你用的版本不支持 CHECK一定要在扣減 SQL 的 WHERE 條件里帶上數(shù)量判斷而不能只依賴代碼里的 if。唯一索引 uk_wh_loc_sku 保證同一倉庫同一庫位同一 SKU 只有一行。這樣做的直接好處是扣減時可以用行鎖鎖定這一行不會因為并發(fā)插入產(chǎn)生多條記錄。如果你把庫位維度去掉直接按“倉庫 SKU”建表會讓很多庫位管理、盤點凍結(jié)的功能做不深入。建議一開始就帶上庫位哪怕當(dāng)前用不到。3.2 庫存流水表每一筆變動都要能回放流水表是庫存系統(tǒng)的黑匣子它的價值在出問題時才體現(xiàn)出來。我設(shè)計的流水表不只記錄“增加了多少、減少了多少”還把變動前數(shù)量、變動后數(shù)量、來源單據(jù)號、變動類型都存下來。這樣任何一筆可疑變動都可以直接回放出來不需要翻業(yè)務(wù)日志。CREATE TABLE inventory_flow ( id BIGINT PRIMARY KEY AUTO_INCREMENT, flow_no VARCHAR(32) NOT NULL COMMENT 流水號, doc_type VARCHAR(32) NOT NULL COMMENT 單據(jù)類型PO/SO/RETURN/ALLOCATE/CHECK/ADJUST, doc_no VARCHAR(32) NOT NULL COMMENT 來源單據(jù)號, change_type VARCHAR(32) NOT NULL COMMENT 變動類型IN/OUT/LOCK/UNLOCK/CHECK_IN/CHECK_OUT, sku_id BIGINT NOT NULL COMMENT 商品SKU ID, warehouse_id BIGINT NOT NULL COMMENT 倉庫ID, location_id BIGINT NOT NULL COMMENT 庫位ID, before_qty INT NOT NULL COMMENT 變動前賬面總量, change_qty INT NOT NULL COMMENT 變動量正數(shù)增加負(fù)數(shù)減少, after_qty INT NOT NULL COMMENT 變動后賬面總量, operator VARCHAR(32) NOT NULL COMMENT 操作人, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_doc (doc_type, doc_no), KEY idx_sku_time (sku_id, created_at) ) COMMENT 庫存流水表;關(guān)鍵點是 before_qty 和 after_qty 不能省。只存 change_qty 的話某天數(shù)據(jù)出問題想確認(rèn)“當(dāng)時是不是真的從 50 變成了 10”還得靠猜。有了前后值對賬時可以精確檢查每一步加減是否符合預(yù)期。流水表不需要唯一鍵約束流水號但生產(chǎn)環(huán)境我會給 flow_no 加一個唯一索引防止重復(fù)寫入。流水表會漲得很快建議定期歸檔。做法是按 created_at 做月度分區(qū)三個月前的分區(qū)數(shù)據(jù)遷到歷史庫。日常查詢盡量都帶 sku_id 和 created_at 范圍避免全表掃描拖垮主庫。記住流水表永遠(yuǎn)只追加不 UPDATE、不 DELETE這是審計的基本前提。3.3 鎖與并發(fā)樂觀鎖選型與悲觀鎖的邊界庫存系統(tǒng)并發(fā)扣減是逃不開的問題。我的默認(rèn)方案是樂觀鎖加版本號寫 SQL 時把版本號作為 WHERE 條件一條 UPDATE 同時完成“判斷數(shù)量、扣減、版本號自增”。這樣做的好處是不用顯式開啟事務(wù)去鎖行性能好。UPDATE inventory_balance SET quantity quantity - 1, version version 1 WHERE id 123 AND quantity 1 AND version 5;這條 SQL 影響行數(shù)為 1 說明扣減成功為 0 說明版本沖突或庫存不足應(yīng)用層捕獲后重新查詢再重試。參數(shù)上要注意兩點第一個是 version 必須用舊值去匹配不能寫成 version 5否則重試就沒有意義第二個是 quantity 1 這個條件不能省它是并發(fā)下防超賣的關(guān)鍵。樂觀鎖的缺點是當(dāng)同一個熱門 SKU 并發(fā)非常高時大部分請求都會因為版本沖突失敗重試反而放大數(shù)據(jù)庫壓力。我一般以單 SKU 每秒幾十筆以上的扣減量為分界線超過這個量就換成悲觀鎖用 SELECT ... FOR UPDATE 鎖住那一行讓請求排隊處理。對大多數(shù)業(yè)務(wù)來說樂觀鎖完全夠用沒必要一上來就上悲觀鎖。還有一個折中做法把熱門 SKU 的庫存拆分到多個庫存行比如 1000 件拆成 10 行每行 100 件扣減時隨機選行從設(shè)計上降低沖突概率。這個方案帶來一個新問題——盤點時要把多行合并所以我只在活動大促場景臨時用日常不拆。4. 入庫、出庫與盤點三大流程的狀態(tài)機落地4.1 入庫流程采購入庫與退貨入庫的兩種沖銷邏輯入庫不只是“加庫存”。采購入庫增加的是可賣庫存而銷售退貨入庫要區(qū)分“還能不能賣”能賣的進(jìn)正常庫存不能賣的進(jìn)次品庫位。我用一張庫存調(diào)整單來承載這些差異而不是直接改臺賬。采購入庫的流程是采購單審核后生成入庫單倉庫掃碼確認(rèn)后在一個事務(wù)里更新臺賬、寫流水、置單據(jù)完成。def confirm_purchase_in(doc_no, items): with db.transaction(): doc get_doc(doc_no) if doc.status ! APPROVED: raise BizException(單據(jù)未審核不能入庫) for item in items: # 更新臺賬不存在則插入新行存在則累加 updated db.execute( UPDATE inventory_balance SET quantity quantity %s, version version 1 WHERE warehouse_id %s AND location_id %s AND sku_id %s, (item.qty, item.warehouse_id, item.location_id, item.sku_id) ) if updated 0: insert_balance(item) # 寫流水 insert_flow(doc_typePO, doc_nodoc_no, change_typeIN, change_qtyitem.qty) # 最后才把單據(jù)置為完成 update_doc_status(doc_no, DONE)這里的事務(wù)邊界要特別注意先判斷單據(jù)狀態(tài)、再更新臺賬、再寫流水、最后更新單據(jù)狀態(tài)四步必須在同一個事務(wù)里。如果在更新完臺賬之后事務(wù)還沒提交服務(wù)就重啟了回滾會把臺賬和數(shù)據(jù)恢復(fù)到一致狀態(tài)不會出現(xiàn)“流水有了、臺賬沒變”的中間臟數(shù)據(jù)。insert_balance 的時候要注意唯一鍵沖突并發(fā)下第一筆插入和第二筆更新可能同時發(fā)生穩(wěn)妥做法是捕獲唯一鍵沖突后轉(zhuǎn)成 UPDATE或者直接使用 INSERT ... ON DUPLICATE KEY UPDATE。4.2 出庫流程預(yù)占、扣減、回滾出庫是庫存系統(tǒng)里最考驗設(shè)計的一環(huán)。我堅持“預(yù)占與扣減分離”下單成功后鎖定庫存訂單發(fā)貨后才真正扣減。預(yù)占階段把可賣數(shù)量轉(zhuǎn)成鎖定數(shù)量可賣數(shù)量減少但總庫存不變扣減階段把總庫存和鎖定數(shù)量一起減少。這樣既防止超賣又允許訂單取消時釋放預(yù)占。-- 預(yù)占可賣數(shù)量 quantity - locked_quantity必須足夠 UPDATE inventory_balance SET locked_quantity locked_quantity %s, version version 1 WHERE warehouse_id %s AND location_id %s AND sku_id %s AND quantity - locked_quantity %s;這條 SQL 是防超賣的核心影響行數(shù)為 1 代表預(yù)占成功為 0 代表庫存不足。參數(shù)里那個 quantity - locked_quantity %s 是原子判斷必須在一條 SQL 里完成不能先查出來再在應(yīng)用層判斷否則高并發(fā)下兩個請求同時查到“還剩 10 件”各自扣 8 件最后就超賣了。預(yù)占成功后寫一條 LOCK 類型的流水訂單號記錄在 doc_no 里方便后續(xù)釋放??蹨p階段則在一個事務(wù)里完成總庫存減少、鎖定數(shù)量減少、寫 OUT 流水、更新發(fā)貨單狀態(tài)。如果訂單走到發(fā)貨前被取消要新建一張作廢單把 locked_quantity 減回去寫一條 UNLOCK 流水。這里最容易做錯的是取消訂單時只減 locked_quantity 不減 quantity導(dǎo)致總庫存虛高。記住鎖定時不動 quantity扣減時同時動 quantity 和 locked_quantity釋放時只動 locked_quantity三者各有語義混了賬就亂了。4.3 盤點流程凍結(jié)、實盤、差異調(diào)整盤點不能“邊賣邊數(shù)”否則數(shù)出來的結(jié)果毫無意義。我的做法是三步走盤點前凍結(jié)庫位或 SKU 范圍盤點時錄入實盤數(shù)量盤點完成后生成盤盈盤虧調(diào)整單。凍結(jié)的本質(zhì)是限制業(yè)務(wù)單據(jù)對凍結(jié)范圍內(nèi)庫存的操作出庫單和調(diào)撥單在凍結(jié)期間不允許審核通過。-- 盤點差異計算賬面數(shù) vs 實盤數(shù) SELECT b.quantity AS book_qty, c.count_qty AS counted_qty, c.count_qty - b.quantity AS diff_qty FROM inventory_balance b JOIN check_snapshot c ON b.sku_id c.sku_id AND b.location_id c.location_id WHERE c.check_id %s AND c.count_qty ! b.quantity;盤點結(jié)果不會直接改動 inventory_balance而是生成盤盈盤虧調(diào)整單。盤盈走 IN 類型的調(diào)整盤虧走 OUT 類型的調(diào)整兩張單據(jù)都必須寫明原因比如“倉庫盤點差異”“損耗”。這樣臺賬變動永遠(yuǎn)有單據(jù)支撐盤點對賬才能閉環(huán)。我在 check_snapshot 表里會保存盤點時點的賬面快照而不是直接關(guān)聯(lián)當(dāng)前余量表防止盤點期間余量變化導(dǎo)致差異算不準(zhǔn)。提示盤點期間如果業(yè)務(wù)不允許凍結(jié)太長時間至少要在生成盤點單時記錄賬面快照并在盤點確認(rèn)前校驗該范圍是否發(fā)生過出入庫流水發(fā)生過的一律強制重盤不要直接按實盤數(shù)覆蓋。5. 庫存翻車現(xiàn)場盤點和并發(fā)下的五個典型坑及排查路徑5.1 負(fù)庫存數(shù)據(jù)庫 check 約束沒攔住現(xiàn)象某 SKU 賬面數(shù)量變成 -3訂單依然在正常發(fā)出財務(wù)對賬發(fā)現(xiàn)庫存金額是負(fù)數(shù)。原因數(shù)據(jù)庫版本不強制校驗 CHECK 約束應(yīng)用層判斷庫存足夠的邏輯在并發(fā)下同時通過兩條扣減同時執(zhí)行就把數(shù)量扣成了負(fù)數(shù)。解決把數(shù)量判斷下沉到 UPDATE 語句的 WHERE 條件里用一條 SQL 原子完成“查庫存、扣數(shù)量”。同時加一個定時任務(wù)每天掃描 inventory_balance 里 quantity 小于 0 的記錄第一時間報警。不要指望開發(fā)人員在所有代碼路徑上都記得加判斷數(shù)據(jù)庫和 SQL 層的約束才是最后一道閘門。5.2 盤點期間照常出庫賬面庫存被覆蓋現(xiàn)象倉庫盤點一個庫位花了三個小時盤點確認(rèn)后差異異常大重盤兩次數(shù)據(jù)還是對不上。原因盤點單生成后業(yè)務(wù)仍然在這個庫位正常出庫到確認(rèn)盤點結(jié)果時系統(tǒng)直接把臺賬數(shù)量改成實盤數(shù)期間出庫的數(shù)量被覆蓋了。賬面、流水、實盤三者已經(jīng)不一致之后怎么對都對不平。解決盤點單審核時嚴(yán)格凍結(jié)庫位凍結(jié)范圍內(nèi)不允許出庫和調(diào)撥如果凍結(jié)會影響業(yè)務(wù)那就退而求其次在盤點確認(rèn)前檢查凍結(jié)時間內(nèi)該范圍有沒有新增流水有流水的 SKU 自動剔除強制重新盤點。這一步必須做成系統(tǒng)邏輯不能靠倉庫主管口頭協(xié)調(diào)。5.3 樂觀鎖版本號更新放錯位置重試邏輯白寫現(xiàn)象加了 version 字段后并發(fā)扣減偶爾會丟更新重試機制一直沒有生效。原因應(yīng)用層把版本號比較和更新拆成了兩步第一步查出 version5第二步執(zhí)行UPDATE ... SET quantityquantity-1 WHERE id123完全沒帶版本號條件或者帶了條件但 SET 里沒有 versionversion1。這等于樂觀鎖形同虛設(shè)。解決版本號更新必須和業(yè)務(wù)字段更新放在同一條 SQL 里即UPDATE ... SET quantity quantity - %s, version version 1 WHERE id %s AND version %s。重試邏輯要捕獲“影響行數(shù)為 0”的結(jié)果重新查詢最新版本號后再次執(zhí)行。還有一個容易忽略的細(xì)節(jié)重試之后業(yè)務(wù)邏輯要整體重新計算不能只換版本號、庫存數(shù)量還按舊值算。5.4 調(diào)撥或移庫時兩步更新不在一個事務(wù)現(xiàn)象調(diào)撥單顯示已完成但兩個倉庫的庫存都變少了或者一個多了另一個沒少月底一查全是這種半截賬。原因跨倉調(diào)撥被實現(xiàn)成“出庫倉減庫存、入庫倉加庫存”兩條獨立 UPDATE第一條成功了、第二條失敗事務(wù)沒有回滾。解決調(diào)撥單必須在一個數(shù)據(jù)庫事務(wù)里完成先減后加任一步失敗整體回滾。如果未來要跨庫調(diào)撥需要引入分布式事務(wù)或本地消息表但在單庫階段直接一個事務(wù)包住兩步更新就是最可靠的做法。排查時優(yōu)先看 inventory_flow 里 ALLOCATE 相關(guān)的流水兩邊單據(jù)號應(yīng)該成對出現(xiàn)。5.5 直接更新臺賬修數(shù)據(jù)流水成了擺設(shè)現(xiàn)象某天發(fā)現(xiàn)庫存不平開發(fā)為了省事直接改了 inventory_balance 里的 quantity第二天對賬腳本跑出大量差異。原因臺賬被繞過流水里沒有對應(yīng)記錄庫存余額表變成了一個可以隨意篡改的數(shù)字對賬結(jié)果自然永遠(yuǎn)對不上。更麻煩的是時間一長沒人記得哪些數(shù)據(jù)是手工改的整張表的數(shù)據(jù)可信度下降。解決任何庫存修正必須走庫存調(diào)整單調(diào)整單審核后在事務(wù)里更新臺賬、寫 ADJUST 流水。哪怕是初始化導(dǎo)入也要通過導(dǎo)入單完成??梢员A粢粋€“超級管理員”權(quán)限做數(shù)據(jù)修復(fù)但每一次修復(fù)都會留下流水和單據(jù)痕跡方便事后審計。我自己的習(xí)慣是工具可以寫但工具生成的每一筆變更都必須有完整鏈路。6. 讓這套方案更抗打每日對賬、預(yù)警閾值與擴展預(yù)留6.1 每日自動對賬臺賬對流水、流水對單證對賬是庫存系統(tǒng)最樸素的護城河。我每天凌晨跑一遍賬把每行庫存的期初值加當(dāng)天流水發(fā)生額和期末值比對再把流水按 doc_no 匯總和單據(jù)表比對。任何一條對不上系統(tǒng)自動生成差異記錄推給指定負(fù)責(zé)人。這個腳本不復(fù)雜但能兜住絕大多數(shù)人肉改數(shù)、漏單、重復(fù)扣減的問題。6.2 預(yù)警閾值安全庫存與周轉(zhuǎn)提醒在 SKU 上配置 min_qty 和 max_qty低于最低值生成補貨建議高于最高值提示滯銷。閾值設(shè)置上我會結(jié)合最近三十天的日均銷量乘以采購提前期來算而不是拍腦袋填一個固定數(shù)字。預(yù)警不在精在于穩(wěn)定觸發(fā)這樣運營才會認(rèn)真對待。6.3 擴展預(yù)留批次、序列號與多計量單位方案里我刻意保留了擴展位流水表已經(jīng)具備 doc_no 關(guān)聯(lián)能力未來加批次時只需要在 inventory_balance 和 inventory_flow 上增加 batch_no 字段按“倉庫 庫位 SKU 批次”建唯一索引即可。序列號管理則需要單獨建一張表來記錄每個唯一碼的流轉(zhuǎn)狀態(tài)不放在臺賬里。多計量單位在商品中心處理庫存表始終以基礎(chǔ)計量單位記賬不做單位換算。這套方案我早期在某內(nèi)部系統(tǒng)里踩過一輪坑最大的教訓(xùn)就是庫存沒有玄學(xué)只有賬沒建對、順序沒走對、并發(fā)沒防對。希望幫到你。本文還有配套的精品資源點擊獲取