package handler import ( "fmt" "net/http" "strings" "bj_power_wms/ent/inventorybatch" "bj_power_wms/ent/material" "bj_power_wms/internal/svc" "github.com/xuri/excelize/v2" ) // excelInboundHandler Excel 批量导入入库(结构件) // 列头(第一行忽略):物料编码 | 批次号(可空,自动生成) | 数量 | 生产日期 | 供应商 | 区域 | 备注 func excelInboundHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if err := r.ParseMultipartForm(20 << 20); err != nil { fail(w, http.StatusBadRequest, "文件上传失败: "+err.Error()) return } f, _, err := r.FormFile("file") if err != nil { fail(w, http.StatusBadRequest, "缺少 file 字段") return } defer f.Close() xl, err := excelize.OpenReader(f) if err != nil { fail(w, http.StatusBadRequest, "无法解析 Excel: "+err.Error()) return } defer xl.Close() sheet := xl.GetSheetList()[0] rows, err := xl.GetRows(sheet) if err != nil { fail(w, http.StatusBadRequest, "读取工作表失败: "+err.Error()) return } type resultRow struct { Row int `json:"row"` Code string `json:"materialCode"` BatchNo string `json:"batchNo"` Qty int `json:"quantity"` Error string `json:"error,omitempty"` } results := []resultRow{} success := 0 for i, row := range rows { if i == 0 { continue // 表头 } cell := func(idx int) string { if idx < len(row) { return strings.TrimSpace(row[idx]) } return "" } code := cell(0) batchNo := cell(1) qtyStr := cell(2) qty := atoi(qtyStr, 0) prodDate := cell(3) supplier := cell(4) zone := cell(5) remark := cell(6) res := resultRow{Row: i + 1, Code: code, BatchNo: batchNo, Qty: qty} switch { case code == "": res.Error = "物料编码为空" case qty <= 0: res.Error = "数量必须大于 0" default: m, err := ctx.EntClient.Material.Query(). Where(material.CodeEQ(code)).Only(ctx0()) if err != nil { res.Error = "物料不存在" } else if m.ManageMode == 2 { res.Error = "精密件请使用 SN 扫码入库,不支持 Excel 导入" } else { if batchNo == "" { batchNo = genBatchNo(code) } // 同批次追加或新建 existing, err := ctx.EntClient.InventoryBatch.Query(). Where(inventorybatch.BatchNoEQ(batchNo)).Only(ctx0()) if err == nil { ctx.EntClient.InventoryBatch.UpdateOneID(existing.ID). SetQuantity(existing.Quantity + qty). SetNillableSupplier(strPtr(supplier)). ExecX(ctx0()) } else { ctx.EntClient.InventoryBatch.Create(). SetBatchNo(batchNo). SetMaterialCode(code). SetNillableMaterialName(strPtr(m.Name)). SetQuantity(qty). SetLockedQty(0). SetNillableProductionDate(strPtr(prodDate)). SetNillableSupplier(strPtr(supplier)). SetQualityStatus("未检"). SetNillableZoneCode(strPtr(zone)). SaveX(ctx0()) } inboundNo := "IB" + nowStr() + fmt.Sprintf("%03d", i) ctx.EntClient.InboundOrder.Create(). SetInboundNo(inboundNo). SetNillableInboundType(strPtr("purchase")). SetMaterialCode(code). SetNillableMaterialName(strPtr(m.Name)). SetManageMode(1). SetNillableZoneCode(strPtr(zone)). SetQuantity(qty). SetNillableRemark(strPtr(remark)). SetOperator("excel-import"). SaveX(ctx0()) res.BatchNo = batchNo success++ } } results = append(results, res) } ok(w, map[string]any{"total": len(results), "success": success, "rows": results}) } }