package controllers import ( "bytes" "errors" "fmt" "io" "mime/multipart" "net/http" "strings" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" "psi/constant" "psi/database" "psi/models" systemReq "psi/models/request" systemRes "psi/models/response" "psi/service" "psi/utils" "strconv" ) type ProcessApi struct{} var processService = &service.ProcessService{} // CreatePurchaseOrderWithWave 创建采购单并生成入库波次 func (r *ProcessApi) CreatePurchaseOrderWithWave(c *gin.Context) { var req systemReq.PurchaseOrderCreateRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "创建采购单请求参数异常", "参数错误: "+err.Error(), c, err) return } carService := &service.CarService{} capacity, err := carService.GetCarCapacityByID(req.CarID, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "查询小车容量异常", err, c, gin.H{"car_id": req.CarID}) return } if len(req.Items) == 0 { items, err := bindPurchaseOrderItems(c, int(capacity)) if err != nil { logAndFail(constant.LoggerChannelRequest, "绑定采购订单项异常", "参数错误: "+err.Error(), c) return } req.Items = items } userInfo := utils.GetUserInfo(c) purchaseOrderID, waveID, err := processService.CreatePurchaseOrderWithWave(req, userInfo.Username, userInfo.ID, int(capacity), database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "创建采购单及波次异常", err, c, req) return } systemRes.OkWithDetailed(gin.H{"order_id": purchaseOrderID, "wave_id": waveID}, "采购单创建成功,波次已生成", c) } // ReleaseWave 提交波次,生成采购订单明细和波次任务明细 func (r *ProcessApi) ReleaseWave(c *gin.Context) { var req systemReq.WaveRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "提交波次请求参数异常", "参数错误: "+err.Error(), c, err) return } carService := &service.CarService{} capacity, err := carService.GetCarCapacityByID(req.CarID, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "查询小车容量异常", err, c, gin.H{"car_id": req.CarID}) return } if len(req.Items) == 0 { items, err := bindWaveItems(c, int(capacity)) if err != nil { logAndFail(constant.LoggerChannelRequest, "绑定波次项异常", "参数错误: "+err.Error(), c) return } req.Items = items } waveID, waveNo, err := processService.ReleaseWave(req, capacity, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "提交波次异常", err, c, req) return } systemRes.OkWithDetailed(systemRes.WaveReleaseResponse{ WaveID: waveID, WaveNo: waveNo, }, "波次提交成功,任务和明细已生成", c) } // BindWave 绑定波次,创建入库单 func (r *ProcessApi) BindWave(c *gin.Context) { var req systemReq.BindWaveRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "绑定波次请求参数异常", "参数错误: "+err.Error(), c, err) return } receivingOrderID, waveTaskID, waveTaskBatchNo, err := processService.BindWave(req, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "绑定波次异常", err, c, req) return } systemRes.OkWithDetailed(map[string]interface{}{ "receiving_order_id": receivingOrderID, "wave_task_id": waveTaskID, "wave_task_batch_no": waveTaskBatchNo, }, "绑定波次成功,入库单已创建", c) } // GetWaveTaskInfo 获取波次任务信息 func (r *ProcessApi) GetWaveTaskInfo(c *gin.Context) { id, err := parsePathID(c, "id", "获取波次任务信息") if err != nil { return } info, err := processService.GetWaveTaskInfo(id, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "获取波次任务信息异常", err, c, gin.H{"id": id}) return } systemRes.OkWithDetailed(info, "获取成功", c) } // SubmitReceiving 提交入库 func (r *ProcessApi) SubmitReceiving(c *gin.Context) { var req systemReq.ReceivingSubmitRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "提交入库请求参数异常", "参数错误: "+err.Error(), c, err) return } if len(req.Items) == 0 { databaseConn := database.GetDB(c) var waveTask models.WaveTask if err := databaseConn.Where("id = ? AND is_del = 0", req.WaveTaskID).First(&waveTask).Error; err != nil { logAndFail(constant.LoggerChannelWork, "查询波次任务失败", "错误: "+err.Error(), c) return } carCapacity := int(waveTask.CarCapacity) if carCapacity <= 0 { logAndFail(constant.LoggerChannelWork, "波次任务未设置小车容量", fmt.Sprintf("wave_task_id: %d", req.WaveTaskID), c) return } items, err := bindReceivingItems(c, carCapacity) if err != nil { logAndFail(constant.LoggerChannelRequest, "绑定入库项异常", "参数错误: "+err.Error(), c) return } req.Items = items } userInfo := utils.GetUserInfo(c) err := processService.SubmitReceiving(req, userInfo.Username, userInfo.ID, userInfo.AboutID, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "提交入库异常", err, c, req) return } go func(receivingOrderID, aboutID, waveTaskID int64, itemProductIDs []int64) { databaseConn := database.DB var receivingOrder models.ReceivingOrder if err := databaseConn.Where("id = ? AND is_del = 0", receivingOrderID).First(&receivingOrder).Error; err != nil { utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{ "source": "查询入库单失败", "receiving_order_id": receivingOrderID, "err_msg": err.Error(), }) return } var warehouse models.Warehouse if err := databaseConn.Where("id = ? AND is_del = 0", receivingOrder.WarehouseID).First(&warehouse).Error; err != nil { utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{ "source": "查询仓库失败", "warehouse_id": receivingOrder.WarehouseID, "err_msg": err.Error(), }) return } warehouseId := warehouse.ID productIds := make([]string, len(itemProductIDs)) for i, pid := range itemProductIDs { productIds[i] = fmt.Sprintf("%d", pid) } productIdsStr := strings.Join(productIds, ",") url := "https://erp.buzhiyushu.cn/zhishu/product/releaseGoodsAuto" method := "POST" payload := &bytes.Buffer{} writer := multipart.NewWriter(payload) _ = writer.WriteField("userId", fmt.Sprintf("%d", aboutID)) _ = writer.WriteField("warehouseId", fmt.Sprintf("%d", warehouseId)) _ = writer.WriteField("productId", productIdsStr) err := writer.Close() if err != nil { utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{ "source": "关闭multipart writer失败", "err_msg": err.Error(), }) return } client := &http.Client{} req, err := http.NewRequest(method, url, payload) if err != nil { utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{ "source": "创建HTTP请求失败", "err_msg": err.Error(), }) return } req.Header.Add("Authorization", "Basic ZWxhc3RpYzo1bVJESVVnNTJWQzBmcDE0bnctRg==") req.Header.Set("Content-Type", writer.FormDataContentType()) res, err := client.Do(req) if err != nil { utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{ "source": "调用外部API失败", "err_msg": err.Error(), }) return } defer func(Body io.ReadCloser) { err := Body.Close() if err != nil { utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{ "source": "调用外部API失败-Body关闭失败", "err_msg": err.Error(), }) } }(res.Body) body, err := io.ReadAll(res.Body) if err != nil { utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{ "source": "读取响应失败", "err_msg": err.Error(), }) return } utils.InfoLog(constant.LoggerChannelWork, logrus.Fields{ "source": "外部API调用完成", "user_id": aboutID, "warehouse_id": warehouseId, "product_ids": productIdsStr, "status_code": res.StatusCode, "response": string(body), }) }(req.ReceivingOrderID, userInfo.AboutID, req.WaveTaskID, func() []int64 { productIDs := make([]int64, len(req.Items)) for i, item := range req.Items { productIDs[i] = item.ProductID } return productIDs }()) systemRes.OkWithMessage("入库提交成功", c) } // GetReceivingDetail 获取入库单详情 func (r *ProcessApi) GetReceivingDetail(c *gin.Context) { id, err := parsePathID(c, "id", "获取入库单详情") if err != nil { return } detail, err := processService.GetReceivingDetail(id, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "获取入库单详情异常", err, c, gin.H{"id": id}) return } systemRes.OkWithDetailed(detail, "获取成功", c) } // CreateSalesOrderWithDetail 创建销售订单和明细 func (r *ProcessApi) CreateSalesOrderWithDetail(c *gin.Context) { var req systemReq.SalesOrderCreateRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "创建销售订单请求参数异常", "参数错误: "+err.Error(), c, err) return } if len(req.Items) == 0 { items, err := bindSalesOrderItems(c) if err != nil { logAndFail(constant.LoggerChannelRequest, "绑定销售订单项异常", "参数错误: "+err.Error(), c) return } req.Items = items } salesOrderID, err := processService.CreateSalesOrderWithDetail(req) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "创建销售订单及波次异常", err, c, req) return } systemRes.OkWithDetailed(gin.H{"order_id": salesOrderID}, "销售订单创建成功", c) } // CreateOutboundOrder 基于多个销售订单创建出库单 func (r *ProcessApi) CreateOutboundOrder(c *gin.Context) { var req systemReq.CreateOutboundOrderRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "创建出库单请求参数异常", "参数错误: "+err.Error(), c, err) return } if len(req.SalesOrderIDs) == 0 { ids, err := parseIdsFrom(c, req.Total) if err != nil { systemRes.FailWithMessage("参数错误: "+err.Error(), c) return } req.SalesOrderIDs = ids } userInfo := utils.GetUserInfo(c) outboundOrderID, outNo, err := processService.CreateOutboundOrder(req, userInfo.Username, userInfo.ID, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "创建出库单异常", err, c, req) return } systemRes.OkWithDetailed(gin.H{ "outbound_order_id": outboundOrderID, "out_no": outNo, }, "出库单创建成功", c) } // CreateOutboundWave 基于出库单创建出库波次 func (r *ProcessApi) CreateOutboundWave(c *gin.Context) { var req systemReq.CreateOutboundWaveRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "创建出库波次请求参数异常", "参数错误: "+err.Error(), c, err) return } if req.OutboundOrderID == 0 { systemRes.FailWithMessage("参数错误: 出库单ID不能为空", c) return } userInfo := utils.GetUserInfo(c) waveID, err := processService.CreateOutboundWave(req, userInfo.Username, userInfo.ID, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "创建出库波次异常", err, c, req) return } systemRes.OkWithDetailed(systemRes.WaveReleaseResponse{ WaveID: waveID, }, "出库波次创建成功", c) } // ReleaseOutboundWave 提交出库波次,生成波次任务明细 func (r *ProcessApi) ReleaseOutboundWave(c *gin.Context) { var req systemReq.WaveRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "提交出库波次请求参数异常", "参数错误: "+err.Error(), c, err) return } waveID, waveNo, err := processService.ReleaseOutboundWave(req, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "提交出库波次异常", err, c, req) return } systemRes.OkWithDetailed(systemRes.WaveReleaseResponse{ WaveID: waveID, WaveNo: waveNo, }, "出库波次提交成功,任务和明细已生成", c) } // BindOutboundWave 绑定出库波次 func (r *ProcessApi) BindOutboundWave(c *gin.Context) { var req systemReq.BindWaveRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "绑定出库波次请求参数异常", "参数错误: "+err.Error(), c, err) return } outboundOrderID, waveTaskID, waveTaskBatchNo, err := processService.BindOutboundWave(req, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "绑定出库波次异常", err, c, req) return } systemRes.OkWithDetailed(map[string]interface{}{ "out_order_id": outboundOrderID, "wave_task_id": waveTaskID, "wave_task_batch_no": waveTaskBatchNo, }, "绑定出库波次成功", c) } // SubmitOutbound 提交出库 func (r *ProcessApi) SubmitOutbound(c *gin.Context) { var req systemReq.OutboundSubmitRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "提交出库请求参数异常", "参数错误: "+err.Error(), c, err) return } if len(req.Items) == 0 { databaseConn := database.GetDB(c) var waveTask models.WaveTask if err := databaseConn.Where("id = ? AND is_del = 0", req.WaveTaskID).First(&waveTask).Error; err != nil { logAndFail(constant.LoggerChannelWork, "查询波次任务失败", "错误: "+err.Error(), c) return } var count int64 if err := databaseConn.Model(&models.WaveTaskDetail{}).Where("wave_task_id = ? AND status = 1 AND is_del = 0", waveTask.ID).Count(&count).Error; err != nil { logAndFail(constant.LoggerChannelWork, "查询波次任务明细数量失败", "错误: "+err.Error(), c) return } items, err := bindOutboundItems(c, int(count)) if err != nil { logAndFail(constant.LoggerChannelRequest, "绑定出库项异常", "参数错误: "+err.Error(), c) return } req.Items = items } userInfo := utils.GetUserInfo(c) err := processService.SubmitOutbound(req, userInfo.Username, userInfo.ID, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "提交出库异常", err, c, req) return } systemRes.OkWithMessage("出库提交成功", c) } // GetOutboundDetail 获取发货单详情 func (r *ProcessApi) GetOutboundDetail(c *gin.Context) { id, err := parsePathID(c, "id", "获取发货单详情") if err != nil { return } detail, err := processService.GetOutboundDetail(id, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "获取发货单详情异常", err, c, gin.H{"id": id}) return } systemRes.OkWithDetailed(detail, "获取成功", c) } // CreateShippingOrder 基于多个出库单创建发货单 func (r *ProcessApi) CreateShippingOrder(c *gin.Context) { var req systemReq.CreateShippingOrderRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "创建发货单请求参数异常", "参数错误: "+err.Error(), c, err) return } if len(req.OutboundOrderIDs) == 0 { ids, err := parseIdsFrom(c, req.Total) if err != nil { systemRes.FailWithMessage("参数错误: "+err.Error(), c) return } req.OutboundOrderIDs = ids } userInfo := utils.GetUserInfo(c) shippingOrderID, shippingNo, err := processService.CreateShippingOrder(req, userInfo.Username, userInfo.ID, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "创建发货单异常", err, c, req) return } systemRes.OkWithDetailed(gin.H{ "shipping_order_id": shippingOrderID, "shipping_no": shippingNo, }, "发货单创建成功", c) } // UpdateShippingLogistics 更新发货单物流信息并回填销售订单明细 func (r *ProcessApi) UpdateShippingLogistics(c *gin.Context) { var req systemReq.UpdateShippingLogisticsRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "更新发货单物流信息请求参数异常", "参数错误: "+err.Error(), c, err) return } userInfo := utils.GetUserInfo(c) err := processService.UpdateShippingLogistics(req, userInfo.ID, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "更新发货单物流信息异常", err, c, req) return } systemRes.OkWithMessage("更新成功", c) } // CancelSalesOrder 取消销售订单 func (r *ProcessApi) CancelSalesOrder(c *gin.Context) { var req systemReq.CancelSalesOrderRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "取消销售订单请求参数异常", "参数错误: "+err.Error(), c, err) return } userInfo := utils.GetUserInfo(c) err := processService.CancelSalesOrder(req.OrderID, userInfo.Username, userInfo.ID, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "取消销售订单异常", err, c, req) return } systemRes.OkWithMessage("销售订单取消成功,库存已释放", c) } // CancelOutboundWave 取消出库波次 func (r *ProcessApi) CancelOutboundWave(c *gin.Context) { var req systemReq.CancelOutboundWaveRequest if err := c.ShouldBindJSON(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "取消出库波次请求参数异常", "参数错误: "+err.Error(), c, err) return } userInfo := utils.GetUserInfo(c) err := processService.CancelOutboundWave(req.WaveID, userInfo.Username, userInfo.ID, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "取消出库波次异常", err, c, req) return } systemRes.OkWithMessage("出库波次取消成功,库存已释放", c) } // AdjustInventory 盘库调整(加库存/减库存) func (r *ProcessApi) AdjustInventory(c *gin.Context) { var req systemReq.StockCheckAdjustRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "盘库调整请求参数异常", "参数错误: "+err.Error(), c, err) return } userInfo := utils.GetUserInfo(c) if err := processService.AdjustInventory(req, userInfo.Username, userInfo.ID, database.GetDB(c)); err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "盘库调整异常", err, c, req) return } systemRes.OkWithMessage("盘库调整成功", c) } // ReturnInventory 盘库退货 func (r *ProcessApi) ReturnInventory(c *gin.Context) { var req systemReq.StockCheckReturnRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "盘库退货请求参数异常", "参数错误: "+err.Error(), c, err) return } userInfo := utils.GetUserInfo(c) if err := processService.ReturnInventory(req, userInfo.Username, userInfo.ID, database.GetDB(c)); err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "盘库退货异常", err, c, req) return } systemRes.OkWithMessage("盘库退货成功", c) } // ChangeLocation 出库单切换库位 func (r *ProcessApi) ChangeLocation(c *gin.Context) { var req systemReq.ChangeLocationRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "出库单切换库位请求参数异常", "参数错误: "+err.Error(), c, err) return } userInfo := utils.GetUserInfo(c) if err := processService.ChangeLocation(req, userInfo.Username, userInfo.ID, database.GetDB(c)); err != nil { if errors.Is(err, utils.ErrNoAvailableLocation) { c.Status(http.StatusNoContent) return } utils.FailWithRequestLog(constant.LoggerChannelWork, "出库单切换库位异常", err, c, req) return } systemRes.OkWithMessage("库位切换成功", c) } // parsePathID 从URL路径参数中获取并验证ID func parsePathID(c *gin.Context, paramName, source string) (int64, error) { idStr := c.Param(paramName) if idStr == "" { utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{ "source": source + "请求参数异常", "err_msg": "ID参数不能为空", }) systemRes.FailWithValidateMessage("参数错误: ID不能为空", c) return 0, fmt.Errorf("ID参数不能为空") } var id int64 if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil || id <= 0 { utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{ "source": source + "请求参数异常", "err_msg": "ID格式错误: " + idStr, }) systemRes.FailWithValidateMessage("参数错误: ID格式不正确", c) return 0, fmt.Errorf("ID格式错误") } return id, nil } // parseOptionalDate 解析可选日期字段 func parseOptionalDate(c *gin.Context, key string) int64 { if str := c.PostForm(key); str != "" { if val, err := strconv.ParseInt(str, 10, 64); err == nil { return val } } return 0 } // bindSimpleItem 绑定简单项(ProductID + Quantity + UnitPrice) func bindSimpleItem[T any](c *gin.Context, i int, creator func(int64, int64, int64) T) (T, bool, error) { var zero T productID, hasProduct, err := getPostFormInt64(c, fmt.Sprintf("items[%d][product_id]", i)) if err != nil || !hasProduct { return zero, false, err } quantity, hasQuantity, err := getPostFormInt64(c, fmt.Sprintf("items[%d][quantity]", i)) if err != nil || !hasQuantity { return zero, false, err } unitPrice, hasPrice, err := getPostFormInt64(c, fmt.Sprintf("items[%d][unit_price]", i)) if err != nil || !hasPrice { return zero, false, err } return creator(productID, quantity, unitPrice), true, nil } // bindLocationItem 绑定带位置信息的项(ProductID + LocationID + Quantity + 批次信息) func bindLocationItem[T any](c *gin.Context, i int, creator func(int64, int64, string, int64, int64, int64) T) (T, bool, error) { var zero T productID, hasProduct, err := getPostFormInt64(c, fmt.Sprintf("items[%d][product_id]", i)) if err != nil || !hasProduct { return zero, false, err } locationID, hasLocation, err := getPostFormInt64(c, fmt.Sprintf("items[%d][location_id]", i)) if err != nil || !hasLocation { return zero, false, err } quantity, hasQuantity, err := getPostFormInt64(c, fmt.Sprintf("items[%d][quantity]", i)) if err != nil || !hasQuantity { return zero, false, err } batchNo := c.PostForm(fmt.Sprintf("items[%d][batch_no]", i)) productionDate := parseOptionalDate(c, fmt.Sprintf("items[%d][production_date]", i)) expiryDate := parseOptionalDate(c, fmt.Sprintf("items[%d][expiry_date]", i)) return creator(productID, locationID, batchNo, productionDate, expiryDate, quantity), true, nil } // bindPurchaseOrderItems 绑定采购订单项 func bindPurchaseOrderItems(c *gin.Context, capacity int) ([]systemReq.PurchaseOrderItemRequest, error) { return bindItemsWithForm(c, func(i int) (systemReq.PurchaseOrderItemRequest, bool, error) { return bindSimpleItem(c, i, func(productID, quantity, unitPrice int64) systemReq.PurchaseOrderItemRequest { return systemReq.PurchaseOrderItemRequest{ ProductID: productID, Quantity: quantity, UnitPrice: unitPrice, } }) }, capacity) } // bindWaveItems 绑定波次项 func bindWaveItems(c *gin.Context, capacity int) ([]systemReq.WaveItemRequest, error) { return bindItemsWithForm(c, func(i int) (systemReq.WaveItemRequest, bool, error) { return bindSimpleItem(c, i, func(productID, quantity, unitPrice int64) systemReq.WaveItemRequest { return systemReq.WaveItemRequest{ ProductID: productID, Quantity: quantity, UnitPrice: unitPrice, } }) }, capacity) } // bindReceivingItems 绑定入库项 func bindReceivingItems(c *gin.Context, maxItems int) ([]systemReq.ReceivingItemRequest, error) { return bindItemsWithForm(c, func(i int) (systemReq.ReceivingItemRequest, bool, error) { return bindLocationItem(c, i, func(productID, locationID int64, batchNo string, productionDate, expiryDate, quantity int64) systemReq.ReceivingItemRequest { return systemReq.ReceivingItemRequest{ ProductID: productID, LocationID: locationID, BatchNo: batchNo, ProductionDate: productionDate, ExpiryDate: expiryDate, Quantity: quantity, } }) }, maxItems) } // bindSalesOrderItems 绑定销售订单项 func bindSalesOrderItems(c *gin.Context) ([]systemReq.SalesOrderItemRequest, error) { return bindItemsWithForm(c, func(i int) (systemReq.SalesOrderItemRequest, bool, error) { return bindSimpleItem(c, i, func(productID, quantity, unitPrice int64) systemReq.SalesOrderItemRequest { return systemReq.SalesOrderItemRequest{ ProductID: productID, Quantity: quantity, UnitPrice: unitPrice, } }) }, 200) } // bindOutboundItems 绑定出库项 func bindOutboundItems(c *gin.Context, maxItems int) ([]systemReq.OutboundItemRequest, error) { return bindItemsWithForm(c, func(i int) (systemReq.OutboundItemRequest, bool, error) { return bindLocationItem(c, i, func(productID, locationID int64, batchNo string, productionDate, expiryDate, quantity int64) systemReq.OutboundItemRequest { return systemReq.OutboundItemRequest{ ProductID: productID, LocationID: locationID, BatchNo: batchNo, ProductionDate: productionDate, ExpiryDate: expiryDate, Quantity: quantity, } }) }, maxItems) } // bindItemsWithForm 通用表单项绑定函数 func bindItemsWithForm[T any](c *gin.Context, builder func(int) (T, bool, error), maxItems int) ([]T, error) { items := make([]T, 0) for i := 0; i < maxItems; i++ { item, hasData, err := builder(i) if err != nil { return nil, err } if !hasData { continue } items = append(items, item) } return items, nil } func parseIdsFrom(c *gin.Context, total int) ([]int64, error) { var ids []int64 for i := 0; i < total; i++ { idStr := c.PostForm(fmt.Sprintf("order_ids[%d]", i)) if idStr == "" { break } var id int64 if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil { return nil, fmt.Errorf("第%d个ID格式错误", i) } if id <= 0 { return nil, fmt.Errorf("第%d个ID必须大于0", i) } ids = append(ids, id) } if len(ids) == 0 { return nil, fmt.Errorf("至少需要一个ID") } return ids, nil } // getPostFormInt64 获取表单中的int64值 func getPostFormInt64(c *gin.Context, key string) (int64, bool, error) { str := c.PostForm(key) if str == "" { return 0, false, nil } val, err := strconv.ParseInt(str, 10, 64) if err != nil { return 0, true, err } return val, true, nil } // logAndFail 业务统一的错误日志和响应函数 func logAndFail(channel string, source, errMsg string, c *gin.Context) { utils.ErrorLog(channel, logrus.Fields{ "source": source, "err_msg": errMsg, }) systemRes.FailWithMessage(errMsg, c) } // ValidAndFail 参数统一的错误日志和响应函数 func ValidAndFail(channel string, source, errMsg string, c *gin.Context, err error) { utils.ErrorLog(channel, logrus.Fields{ "source": source, "err_msg": errMsg, }) utils.HandleValidationError(c, err) }