package handler import ( "fmt" "net/http" "os" "path/filepath" "strconv" "strings" "time" "github.com/gin-gonic/gin" "github.com/rovina/zsp-backend/internal/model" "github.com/rovina/zsp-backend/internal/service" ) func (h *ContractHandler) List(c *gin.Context) { contractType := c.Query("contractType") list, err := h.service.List(contractType) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "data": list}) } func (h *ContractHandler) Get(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "invalid id"}) return } contract, err := h.service.GetByID(uint(id)) if err != nil { c.JSON(http.StatusNotFound, gin.H{"success": false, "message": "contract not found"}) return } c.JSON(http.StatusOK, gin.H{"success": true, "data": contract}) } func (h *ContractHandler) Update(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "invalid id"}) return } existing, err := h.service.GetByID(uint(id)) if err != nil { c.JSON(http.StatusNotFound, gin.H{"success": false, "message": "contract not found"}) return } var body struct { Name string `json:"name"` ContractType string `json:"contractType"` ContractCode string `json:"contractCode"` BuyParty string `json:"buyParty"` SellParty string `json:"sellParty"` Commodity string `json:"commodity"` Count float64 `json:"count"` UnitPrice float64 `json:"unitPrice"` BLNumber string `json:"blNumber"` DeliveryTime string `json:"deliveryTime"` PickUpTime string `json:"pickUpTime"` Packaging string `json:"packaging"` Remarks string `json:"remarks"` } if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": err.Error()}) return } existing.Name = body.Name existing.ContractType = body.ContractType existing.ContractCode = body.ContractCode existing.BuyParty = body.BuyParty existing.SellParty = body.SellParty existing.Commodity = body.Commodity existing.Count = body.Count existing.UnitPrice = body.UnitPrice existing.BLNumber = body.BLNumber existing.DeliveryTime = body.DeliveryTime existing.PickUpTime = body.PickUpTime existing.Packaging = body.Packaging existing.Remarks = body.Remarks existing.UpdateTime = time.Now() if err := h.service.Update(existing); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "message": "updated"}) } func (h *ContractHandler) UpdateWithFiles(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "invalid id"}) return } form, err := c.MultipartForm() if err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": err.Error()}) return } formMap := make(map[string]string) for k, v := range form.Value { if len(v) > 0 { formMap[k] = v[0] } } keepFileIds := parseUintSlice(formMap["keepFileIds"]) newFileHeaders := form.File["files"] if len(keepFileIds) == 0 && len(newFileHeaders) == 0 { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "至少需要保留一个合同扫描件"}) return } var newFileModels []model.ContractFile path := "./data/contract/uploads" os.MkdirAll(path, os.ModePerm) for _, fh := range newFileHeaders { filename := fmt.Sprintf("%d_%s", time.Now().UnixNano(), fh.Filename) savePath := filepath.Join(path, filename) if err := c.SaveUploadedFile(fh, savePath); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } newFileModels = append(newFileModels, model.ContractFile{ Name: fh.Filename, URL: "uploads/" + filename, }) } if len(keepFileIds)+len(newFileModels) == 0 { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "至少需要保留一个合同扫描件"}) return } if err := h.service.UpdateWithFiles(uint(id), keepFileIds, newFileModels, formMap); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "message": "updated"}) } func parseUintSlice(s string) []uint { if s == "" { return nil } parts := strings.Split(s, ",") var result []uint for _, p := range parts { p = strings.TrimSpace(p) if p == "" { continue } if v, err := strconv.ParseUint(p, 10, 32); err == nil { result = append(result, uint(v)) } } return result } func (h *ContractHandler) Delete(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "invalid id"}) return } if err := h.service.Delete(uint(id)); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "message": "deleted"}) } type ContractHandler struct { service service.ContractService folderService service.ContractFolderService batchService service.ContractBatchService } func NewContractHandler( service service.ContractService, folderService service.ContractFolderService, batchService service.ContractBatchService, ) *ContractHandler { return &ContractHandler{ service: service, folderService: folderService, batchService: batchService, } } func (h *ContractHandler) Upload(c *gin.Context) { var contract model.Contract contract.Name = c.PostForm("name") contract.ContractType = c.PostForm("contractType") contract.ContractCode = c.PostForm("contractCode") contract.BuyParty = c.PostForm("buyParty") contract.SellParty = c.PostForm("sellParty") contract.Commodity = c.PostForm("commodity") contract.BLNumber = c.PostForm("BLNumber") contract.DeliveryTime = c.PostForm("deliveryTime") contract.PickUpTime = c.PostForm("pickUpTime") contract.Packaging = c.PostForm("packaging") contract.Remarks = c.PostForm("remarks") if countStr := c.PostForm("count"); countStr != "" { if v, err := strconv.ParseFloat(countStr, 64); err == nil { contract.Count = v } } if priceStr := c.PostForm("unitPrice"); priceStr != "" { if v, err := strconv.ParseFloat(priceStr, 64); err == nil { contract.UnitPrice = v } } contract.CreateTime = time.Now() contract.UpdateTime = time.Now() form, err := c.MultipartForm() if err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": err.Error()}) return } files := form.File["files"] if len(files) == 0 { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "no files"}) return } path := "./data/contract/uploads" os.MkdirAll(path, os.ModePerm) var fileModels []model.ContractFile for _, file := range files { filename := fmt.Sprintf("%d_%s", time.Now().UnixNano(), file.Filename) savePath := filepath.Join(path, filename) if err := c.SaveUploadedFile(file, savePath); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } fileModels = append(fileModels, model.ContractFile{ Name: file.Filename, URL: "uploads/" + filename, }) } if err := h.service.CreateContract(&contract, fileModels); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "success": true, "message": "upload success", }) } func (h *ContractHandler) BatchUpload(c *gin.Context) { path := c.PostForm("path") if path == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "path required"}) return } form, err := c.MultipartForm() if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid form"}) return } files := form.File["files"] if len(files) == 0 { c.JSON(http.StatusBadRequest, gin.H{"error": "no files"}) return } saved, err := h.service.BatchSaveFiles(path, files) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "success": true, "count": saved, }) } // ========== 批量归档文件夹 ========== func (h *ContractHandler) ListFolders(c *gin.Context) { list, err := h.folderService.List() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "data": list}) } func (h *ContractHandler) CreateFolder(c *gin.Context) { var body struct { Name string `json:"name" binding:"required"` Description string `json:"description"` } if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": err.Error()}) return } f, err := h.folderService.Create(body.Name, body.Description) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "data": f}) } func (h *ContractHandler) GetFolder(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "invalid id"}) return } f, err := h.folderService.GetByID(uint(id)) if err != nil { c.JSON(http.StatusNotFound, gin.H{"success": false, "message": "not found"}) return } c.JSON(http.StatusOK, gin.H{"success": true, "data": f}) } func (h *ContractHandler) UpdateFolder(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "invalid id"}) return } var body struct { Name string `json:"name" binding:"required"` Description string `json:"description"` } if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": err.Error()}) return } if err := h.folderService.Update(uint(id), body.Name, body.Description); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "message": "updated"}) } func (h *ContractHandler) DeleteFolder(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "invalid id"}) return } if err := h.folderService.Delete(uint(id)); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "message": "deleted"}) } // ========== 批量上传记录 ========== func (h *ContractHandler) ListBatches(c *gin.Context) { var folderID *uint if idStr := c.Query("folderId"); idStr != "" { if id, err := strconv.ParseUint(idStr, 10, 32); err == nil { uid := uint(id) folderID = &uid } } list, err := h.batchService.List(folderID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "data": list}) } func (h *ContractHandler) CreateBatch(c *gin.Context) { folderIDStr := c.PostForm("folderId") batchName := c.PostForm("batchName") remark := c.PostForm("remark") if folderIDStr == "" || batchName == "" { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "folderId and batchName required"}) return } folderID, err := strconv.ParseUint(folderIDStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "invalid folderId"}) return } form, err := c.MultipartForm() if err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": err.Error()}) return } files := form.File["files"] if len(files) == 0 { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "no files"}) return } path := "./data/contract/batch_archive" os.MkdirAll(path, os.ModePerm) var fileModels []model.ContractBatchFile for _, file := range files { filename := fmt.Sprintf("%d_%s", time.Now().UnixNano(), file.Filename) savePath := filepath.Join(path, filename) if err := c.SaveUploadedFile(file, savePath); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } fileModels = append(fileModels, model.ContractBatchFile{ Name: file.Filename, URL: "batch_archive/" + filename, FileSize: file.Size, }) } b, err := h.batchService.Create(uint(folderID), batchName, remark, fileModels) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "data": b, "message": "created"}) } func (h *ContractHandler) GetBatch(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "invalid id"}) return } b, err := h.batchService.GetByID(uint(id)) if err != nil { c.JSON(http.StatusNotFound, gin.H{"success": false, "message": "not found"}) return } c.JSON(http.StatusOK, gin.H{"success": true, "data": b}) } func (h *ContractHandler) DeleteBatch(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "invalid id"}) return } if err := h.batchService.Delete(uint(id)); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "message": "deleted"}) }