- 将 Python 代码移至 analyzer/ 目录(含 venv) - 拆分 Go 服务器代码为模块化结构: - config/: 配置加载 - model/: 请求/响应模型 - service/: 业务逻辑 - handler/: API处理器 - 添加 .gitignore 文件 - 删除旧的独立脚本文件
120 lines
2.8 KiB
Go
120 lines
2.8 KiB
Go
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,
|
|
},
|
|
})
|
|
}
|
|
|