package sybinnercode import ( "encoding/json" "errors" "go-admin/common/clientprincipal" "io" "net/http" "strconv" "strings" "go-admin/app/goauto/models" "github.com/gin-gonic/gin" "github.com/go-admin-team/go-admin-core/sdk/pkg" "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user" "gorm.io/gorm" ) type Handler struct{ DB *gorm.DB } func (h Handler) List(c *gin.Context) { service, ok := h.service(c) if !ok { return } page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "100")) result, err := service.List(c.Request.Context(), ListRequest{DateFrom: strings.TrimSpace(c.Query("dateFrom")), DateTo: strings.TrimSpace(c.Query("dateTo")), Keyword: strings.TrimSpace(c.Query("keyword")), Status: strings.TrimSpace(c.Query("status")), Page: page, PageSize: pageSize}) if err != nil { writeError(c, err) return } c.JSON(http.StatusOK, gin.H{"code": 200, "data": result}) } func (h Handler) Detail(c *gin.Context) { id, err := strconv.ParseUint(c.Param("recordId"), 10, 64) if err != nil || id == 0 { writeError(c, invalid("recordId 无效")) return } service, ok := h.service(c) if !ok { return } result, err := service.Detail(c.Request.Context(), id) if err != nil { writeError(c, err) return } c.JSON(http.StatusOK, gin.H{"code": 200, "data": gin.H{"item": result}}) } func (h Handler) Import(c *gin.Context) { c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, MaxUploadBytes+(1<<20)) file, err := c.FormFile("file") if err != nil { var tooLarge *http.MaxBytesError if errors.As(err, &tooLarge) { writeError(c, invalid("Excel 超过 10MB 上限")) } else { writeError(c, invalid("上传格式无效,请重新选择 Excel 后重试")) } return } src, err := file.Open() if err != nil { writeError(c, invalid("无法打开 Excel")) return } defer src.Close() head := make([]byte, 8) n, _ := io.ReadFull(src, head) if err := ValidateUpload(file.Filename, file.Size, head[:n]); err != nil { writeError(c, err) return } if _, err := src.Seek(0, io.SeekStart); err != nil { writeError(c, internal(err)) return } service, ok := h.service(c) if !ok { return } result, err := service.Import(c.Request.Context(), src, file.Filename, clientOperator(c), c.PostForm("requestId")) if err != nil { writeError(c, err) return } c.JSON(http.StatusOK, gin.H{"code": 200, "data": result}) } func clientOperator(c *gin.Context) uint64 { if p, ok := clientprincipal.Get(c); ok { return p.AuthorizedBy } return uint64(user.GetUserId(c)) } func (h Handler) Delete(c *gin.Context) { var request DeleteRequest if err := decode(c, &request); err != nil { writeError(c, invalid("请求 JSON 无效")) return } service, ok := h.service(c) if !ok { return } result, err := service.Delete(c.Request.Context(), clientOperator(c), request) if err != nil { writeError(c, err) return } c.JSON(http.StatusOK, gin.H{"code": 200, "data": result}) } func (h Handler) MatchJob(c *gin.Context) { service, ok := h.service(c) if !ok { return } var job models.SYBInnerCodeMatchJob if err := service.db.WithContext(c.Request.Context()).First(&job, "id = ?", c.Param("jobId")).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { writeError(c, &ServiceError{Code: CodeNotFound, Message: "匹配任务不存在"}) } else { writeError(c, internal(err)) } return } c.JSON(http.StatusOK, gin.H{"code": 200, "data": gin.H{"item": job}}) } func (h Handler) ApplyPreview(c *gin.Context) { var request ApplyPreviewRequest if err := decode(c, &request); err != nil { writeError(c, invalid("请求 JSON 无效")) return } service, ok := h.service(c) if !ok { return } result, err := service.PreviewApply(c.Request.Context(), request.IDs) if err != nil { writeError(c, err) return } c.JSON(http.StatusOK, gin.H{"code": 200, "data": result}) } func (h Handler) Apply(c *gin.Context) { var request ApplyRequest if err := decode(c, &request); err != nil { writeError(c, invalid("请求 JSON 无效")) return } service, ok := h.service(c) if !ok { return } result, err := service.QueueApply(c.Request.Context(), clientOperator(c), request) if err != nil { writeError(c, err) return } DefaultApplyRunner.Start(service.db, result.BatchID) c.JSON(http.StatusAccepted, gin.H{"code": 200, "data": result}) } func (h Handler) ApplyBatch(c *gin.Context) { service, ok := h.service(c) if !ok { return } var batch models.SYBInnerCodeApplyBatch if err := service.db.WithContext(c.Request.Context()).First(&batch, "id = ?", c.Param("batchId")).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { writeError(c, &ServiceError{Code: CodeNotFound, Message: "回写批次不存在"}) } else { writeError(c, internal(err)) } return } var items []models.SYBInnerCodeApplyItem if err := service.db.Where("batch_id = ?", batch.ID).Order("id").Find(&items).Error; err != nil { writeError(c, internal(err)) return } c.JSON(http.StatusOK, gin.H{"code": 200, "data": gin.H{"batch": batch, "items": items}}) } func (h Handler) Recheck(c *gin.Context) { id, err := strconv.ParseUint(c.Param("recordId"), 10, 64) if err != nil || id == 0 { writeError(c, invalid("recordId 无效")) return } service, ok := h.service(c) if !ok { return } reader, err := DefaultMatcher.Factory(c.Request.Context(), service.db) if err != nil { writeError(c, internal(err)) return } status, message, err := Recheck(c.Request.Context(), service.db, reader, id) if err != nil { writeError(c, err) return } c.JSON(http.StatusOK, gin.H{"code": 200, "data": gin.H{"status": status, "message": message}}) } func (h Handler) Rematch(c *gin.Context) { var request RematchRequest if err := decode(c, &request); err != nil { writeError(c, invalid("请求 JSON 无效")) return } service, ok := h.service(c) if !ok { return } result, err := service.QueueRematch(c.Request.Context(), request) if err != nil { writeError(c, err) return } c.JSON(http.StatusAccepted, gin.H{"code": 200, "data": result}) } func (h Handler) service(c *gin.Context) (*Service, bool) { db := h.DB var err error if db == nil { db, err = pkg.GetOrm(c) } if err != nil { writeError(c, internal(err)) return nil, false } return NewService(db).WithMatchEnqueuer(DefaultMatcher), true } func decode(c *gin.Context, target any) error { c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 1<<20) decoder := json.NewDecoder(c.Request.Body) decoder.DisallowUnknownFields() if err := decoder.Decode(target); err != nil { return err } if err := decoder.Decode(&struct{}{}); !errors.Is(err, io.EOF) { return errors.New("one object required") } return nil } func writeError(c *gin.Context, err error) { target := AsServiceError(err) status := http.StatusInternalServerError switch target.Code { case CodeInvalidRequest: status = http.StatusUnprocessableEntity case CodeConflict: status = http.StatusConflict case CodeNotFound: status = http.StatusNotFound } payload := gin.H{"code": target.Code, "message": target.Message} if target.Details != nil { payload["data"] = target.Details } c.JSON(status, payload) }