93 lines
2.8 KiB
Go
93 lines
2.8 KiB
Go
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, "文件超过 5MB 上限或表单数据过大")
|
||
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, "文件超过 5MB 上限或表单数据过大")
|
||
return
|
||
}
|
||
response.BadRequest(c, "请选择要导入的 xlsx 文件")
|
||
return
|
||
}
|
||
if file.Size > service.MaterialImportMaxBytes {
|
||
response.BadRequest(c, "文件超过 5MB 上限")
|
||
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)
|
||
}
|