package handler import ( "fmt" "io" "net/http" "os" "os/exec" "path/filepath" "strings" "time" "github.com/gin-gonic/gin" "billai-server/config" "billai-server/model" ) // Upload 处理账单上传和清理请求 func Upload(c *gin.Context) { // 1. 获取上传的文件 file, header, err := c.Request.FormFile("file") if err != nil { c.JSON(http.StatusBadRequest, model.UploadResponse{ Result: false, Message: "请上传账单文件 (参数名: file)", }) return } defer file.Close() // 2. 解析请求参数 var req model.UploadRequest c.ShouldBind(&req) if req.Format == "" { req.Format = "csv" } // 3. 保存上传的文件 timestamp := time.Now().Format("20060102_150405") inputFileName := fmt.Sprintf("%s_%s", timestamp, header.Filename) uploadDirAbs := config.ResolvePath(config.Global.UploadDir) inputPath := filepath.Join(uploadDirAbs, inputFileName) dst, err := os.Create(inputPath) if err != nil { c.JSON(http.StatusInternalServerError, model.UploadResponse{ Result: false, Message: "保存文件失败: " + err.Error(), }) return } defer dst.Close() io.Copy(dst, file) // 4. 构建输出文件路径 baseName := strings.TrimSuffix(header.Filename, filepath.Ext(header.Filename)) outputExt := ".csv" if req.Format == "json" { outputExt = ".json" } outputFileName := fmt.Sprintf("%s_%s_cleaned%s", timestamp, baseName, outputExt) outputDirAbs := config.ResolvePath(config.Global.OutputDir) outputPath := filepath.Join(outputDirAbs, outputFileName) // 5. 构建命令参数 cleanScriptAbs := config.ResolvePath(config.Global.CleanScript) args := []string{cleanScriptAbs, inputPath, outputPath} if req.Year != "" { args = append(args, "--year", req.Year) } if req.Month != "" { args = append(args, "--month", req.Month) } if req.Start != "" { args = append(args, "--start", req.Start) } if req.End != "" { args = append(args, "--end", req.End) } if req.Format != "" { args = append(args, "--format", req.Format) } // 6. 执行 Python 脚本 pythonPathAbs := config.ResolvePath(config.Global.PythonPath) cmd := exec.Command(pythonPathAbs, args...) cmd.Dir = config.Global.ProjectRoot output, err := cmd.CombinedOutput() outputStr := string(output) if err != nil { c.JSON(http.StatusInternalServerError, model.UploadResponse{ Result: false, Message: "处理失败: " + err.Error(), }) return } // 7. 检测账单类型 billType := "" if strings.Contains(outputStr, "支付宝") { billType = "alipay" } else if strings.Contains(outputStr, "微信") { billType = "wechat" } // 8. 返回成功响应 c.JSON(http.StatusOK, model.UploadResponse{ Result: true, Message: "处理成功", Data: &model.UploadData{ BillType: billType, FileURL: fmt.Sprintf("/download/%s", outputFileName), FileName: outputFileName, }, }) }