package handler import ( "errors" "net/http" "net/url" "strconv" "github.com/gin-gonic/gin" "github.com/wmms/wmms/internal/middleware" "github.com/wmms/wmms/internal/service" "github.com/wmms/wmms/pkg/response" ) // DownloadMaterialImportTemplate 返回物料导入模板文件。模板随代码生成, // 不落盘,保证下载到的永远和当前校验规则一致。 func (h *Handler) DownloadMaterialImportTemplate(c *gin.Context) { data, err := service.BuildMaterialImportTemplate() if err != nil { response.InternalError(c, "模板生成失败") return } fileName := url.PathEscape("物料导入模板.xlsx") c.Header("Content-Disposition", `attachment; filename="material-import-template.xlsx"; filename*=UTF-8''`+fileName) c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", data) } // ImportMaterials 接收上传的 xlsx,校验通过即整批入库。 // 校验失败返回 400 且 data 里带逐行错误明细。 func (h *Handler) ImportMaterials(c *gin.Context) { orgID, ok := organizationID(c) if !ok { return } // 在 multipart 解析前限制读取;文件限额之外仅给表单头部预留少量空间。 const maxRequestBytes = service.MaterialImportMaxBytes + (64 << 10) if c.Request.ContentLength > maxRequestBytes { response.BadRequest(c, "文件超过 1MB 上限或表单数据过大") return } c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, maxRequestBytes) defer c.Request.Body.Close() defer func() { if c.Request.MultipartForm != nil { _ = c.Request.MultipartForm.RemoveAll() } }() file, err := c.FormFile("file") if err != nil { var sizeErr *http.MaxBytesError if errors.As(err, &sizeErr) { response.BadRequest(c, "文件超过 1MB 上限或表单数据过大") return } response.BadRequest(c, "请选择要导入的 xlsx 文件") return } if file.Size > service.MaterialImportMaxBytes { response.BadRequest(c, "文件超过 1MB 上限") return } source, err := file.Open() if err != nil { response.BadRequest(c, "文件读取失败") return } defer source.Close() operator := middleware.GetCurrentUsername(c) if operator == "" { operator = "系统" } result, err := h.materialImportSvc.Import(orgID, operator, file.Filename, source) if err != nil { if errors.Is(err, service.ErrImportTemplateInvalid) || errors.Is(err, service.ErrImportTooManyRows) { response.BadRequest(c, err.Error()) return } response.InternalError(c, err.Error()) return } if result.FailedRows > 0 { response.ErrorWithData(c, http.StatusBadRequest, "有 "+strconv.Itoa(result.FailedRows)+" 行校验未通过,本次未导入任何数据", result) return } response.Created(c, "成功导入 "+strconv.Itoa(result.SuccessRows)+" 条物料", result) }