feat: 支持ZIP压缩包上传(含密码保护)
This commit is contained in:
@@ -17,6 +17,12 @@ type CleanResult struct {
|
||||
Output string // 脚本输出信息
|
||||
}
|
||||
|
||||
// ConvertResult 格式转换结果
|
||||
type ConvertResult struct {
|
||||
OutputPath string // 转换后的文件路径
|
||||
BillType string // 检测到的账单类型: alipay/wechat
|
||||
}
|
||||
|
||||
// Cleaner 账单清洗器接口
|
||||
// 负责将原始账单数据清洗为标准格式
|
||||
type Cleaner interface {
|
||||
@@ -25,4 +31,9 @@ type Cleaner interface {
|
||||
// outputPath: 输出文件路径
|
||||
// opts: 清洗选项
|
||||
Clean(inputPath, outputPath string, opts *CleanOptions) (*CleanResult, error)
|
||||
|
||||
// Convert 转换账单文件格式(xlsx -> csv,处理 GBK 编码等)
|
||||
// inputPath: 输入文件路径
|
||||
// 返回: 转换后的文件路径, 检测到的账单类型, 错误
|
||||
Convert(inputPath string) (outputPath string, billType string, err error)
|
||||
}
|
||||
|
||||
@@ -185,6 +185,88 @@ func (c *Cleaner) downloadFile(remotePath, localPath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConvertResponse 转换响应
|
||||
type ConvertResponse struct {
|
||||
Success bool `json:"success"`
|
||||
BillType string `json:"bill_type"`
|
||||
Message string `json:"message"`
|
||||
OutputPath string `json:"output_path,omitempty"`
|
||||
}
|
||||
|
||||
// Convert 转换账单文件格式(xlsx -> csv,处理 GBK 编码等)
|
||||
func (c *Cleaner) Convert(inputPath string) (outputPath string, billType string, err error) {
|
||||
// 打开输入文件
|
||||
file, err := os.Open(inputPath)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("打开文件失败: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 创建 multipart form
|
||||
var body bytes.Buffer
|
||||
writer := multipart.NewWriter(&body)
|
||||
|
||||
// 添加文件
|
||||
part, err := writer.CreateFormFile("file", filepath.Base(inputPath))
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("创建表单文件失败: %w", err)
|
||||
}
|
||||
if _, err := io.Copy(part, file); err != nil {
|
||||
return "", "", fmt.Errorf("复制文件内容失败: %w", err)
|
||||
}
|
||||
writer.Close()
|
||||
|
||||
// 发送转换请求
|
||||
fmt.Printf("🌐 调用转换服务: %s/convert\n", c.baseURL)
|
||||
req, err := http.NewRequest("POST", c.baseURL+"/convert", &body)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("创建请求失败: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("HTTP 请求失败: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 读取响应
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("读取响应失败: %w", err)
|
||||
}
|
||||
|
||||
// 处理错误响应
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
var errResp ErrorResponse
|
||||
if err := json.Unmarshal(respBody, &errResp); err == nil {
|
||||
return "", "", fmt.Errorf("转换失败: %s", errResp.Detail)
|
||||
}
|
||||
return "", "", fmt.Errorf("转换失败: HTTP %d - %s", resp.StatusCode, string(respBody))
|
||||
}
|
||||
|
||||
// 解析成功响应
|
||||
var convertResp ConvertResponse
|
||||
if err := json.Unmarshal(respBody, &convertResp); err != nil {
|
||||
return "", "", fmt.Errorf("解析响应失败: %w", err)
|
||||
}
|
||||
|
||||
// 下载转换后的文件到本地(与输入文件同目录,但扩展名改为 .csv)
|
||||
localOutputPath := inputPath[:len(inputPath)-len(filepath.Ext(inputPath))] + ".csv"
|
||||
fmt.Printf(" 下载转换后文件: %s -> %s\n", convertResp.OutputPath, localOutputPath)
|
||||
if err := c.downloadFile(convertResp.OutputPath, localOutputPath); err != nil {
|
||||
return "", "", fmt.Errorf("下载转换结果失败: %w", err)
|
||||
}
|
||||
|
||||
// 验证文件是否存在
|
||||
if _, err := os.Stat(localOutputPath); err != nil {
|
||||
return "", "", fmt.Errorf("下载后文件不存在: %s", localOutputPath)
|
||||
}
|
||||
fmt.Printf(" 文件下载成功,已保存到: %s\n", localOutputPath)
|
||||
|
||||
return localOutputPath, convertResp.BillType, nil
|
||||
}
|
||||
|
||||
// HealthCheck 检查 Python 服务健康状态
|
||||
func (c *Cleaner) HealthCheck() error {
|
||||
resp, err := c.httpClient.Get(c.baseURL + "/health")
|
||||
|
||||
@@ -90,5 +90,11 @@ func detectBillTypeFromOutput(output string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Convert 转换账单文件格式(xlsx -> csv,处理 GBK 编码等)
|
||||
// 子进程模式不支持此功能,请使用 HTTP 模式
|
||||
func (c *Cleaner) Convert(inputPath string) (outputPath string, billType string, err error) {
|
||||
return "", "", fmt.Errorf("子进程模式不支持文件格式转换,请使用 HTTP 模式 (analyzer_mode: http)")
|
||||
}
|
||||
|
||||
// 确保 Cleaner 实现了 adapter.Cleaner 接口
|
||||
var _ adapter.Cleaner = (*Cleaner)(nil)
|
||||
|
||||
Reference in New Issue
Block a user