diff --git a/inventory-backend/app/api/v1/inbound/stock.py b/inventory-backend/app/api/v1/inbound/stock.py index 6a38b06..f8eaee5 100644 --- a/inventory-backend/app/api/v1/inbound/stock.py +++ b/inventory-backend/app/api/v1/inbound/stock.py @@ -804,13 +804,13 @@ def export_stocktake(): return str(user_id) def to_beijing_time(dt): - """转换为北京时间(+8小时)""" + """直接使用数据库中存储的标准时间(服务器时区已正确)""" if not dt: return '' try: if isinstance(dt, str): return dt[:19] - return (dt + timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S') + return dt.strftime('%Y-%m-%d %H:%M:%S') except: return str(dt)[:19] @@ -1130,9 +1130,13 @@ def export_stocktake(): @permission_required('inventory_stocktake:operation') def generate_missing_stocktake(): """ - 生成漏盘数据: + 生成漏盘数据(幂等性): 找出所有真实库存 > 0,但未被当前会话盘点扫描到的物料, 自动生成盘点草稿,标记为盘亏(实盘=0,差异=-库存数) + + 幂等性保护:在重新计算差集之前,先删除当前 session 下所有 + 由系统自动生成的漏盘记录(quantity==0, user_id=='system'), + 保证该接口多次调用结果一致。 """ try: # ★ 获取 session_id 参数,用于隔离当前会话 @@ -1142,6 +1146,16 @@ def generate_missing_stocktake(): if not session_id: return jsonify({'code': 400, 'msg': '缺少 session_id 参数'}), 400 + # ★ 幂等性保护:先删除当前 session 下系统自动生成的漏盘记录 + # 特征:user_id == 'system' (表示由系统自动生成) + deleted_count = StocktakeDraft.query.filter( + StocktakeDraft.session_id == session_id, + StocktakeDraft.user_id == 'system' + ).delete() + if deleted_count > 0: + db.session.commit() + print(f"[generate_missing] 已清理 {deleted_count} 条历史漏盘记录") + # 1. 获取当前会话已有盘点记录的 (source_table, stock_id) 集合 existing_records = db.session.query( StocktakeDraft.source_table, @@ -1192,11 +1206,11 @@ def generate_missing_stocktake(): if key not in scanned_keys: # 生成漏盘草稿 draft = StocktakeDraft( - user_id='system', + user_id='system', # ★ 标记为系统自动生成,用于幂等性清理 uuid=f'MISSING-{stock["source_table"]}-{stock["stock_id"]}', quantity=0, # 实盘数为0 scan_time=beijing_time(), - session_id='AUTO_GENERATED', + session_id=session_id, # ★ 使用传入的 session_id source_table=stock['source_table'], stock_id=stock['stock_id'], stock_qty=stock['stock_qty'], diff --git a/inventory-web/src/api/inbound/stock.ts b/inventory-web/src/api/inbound/stock.ts index a28c06f..d116950 100644 --- a/inventory-web/src/api/inbound/stock.ts +++ b/inventory-web/src/api/inbound/stock.ts @@ -74,7 +74,7 @@ export function getBom(parentId: number) { } // 获取应盘物资清单(盘点基数) -export function getAllStocktakeItems(params?: { keyword?: string }) { +export function getAllStocktakeItems(params?: { keyword?: string; session_id?: string }) { return request({ url: '/v1/inbound/stock/stocktake/all-items', method: 'get', diff --git a/inventory-web/src/views/stock/stocktake/index.vue b/inventory-web/src/views/stock/stocktake/index.vue index 8ef1c00..78e3051 100644 --- a/inventory-web/src/views/stock/stocktake/index.vue +++ b/inventory-web/src/views/stock/stocktake/index.vue @@ -167,8 +167,8 @@ {{ currentItem.sku }}