feat: 支持京东白条账单上传和清洗

This commit is contained in:
CHE LIANG ZHAO
2026-01-26 13:44:22 +08:00
parent 7b2d6a9fbb
commit 3cf39b4664
17 changed files with 383 additions and 27 deletions

View File

@@ -13,14 +13,14 @@ type CleanOptions struct {
// CleanResult 清洗结果
type CleanResult struct {
BillType string // 检测到的账单类型: alipay/wechat
BillType string // 检测到的账单类型: alipay/wechat/jd
Output string // 脚本输出信息
}
// ConvertResult 格式转换结果
type ConvertResult struct {
OutputPath string // 转换后的文件路径
BillType string // 检测到的账单类型: alipay/wechat
BillType string // 检测到的账单类型: alipay/wechat/jd
}
// Cleaner 账单清洗器接口

View File

@@ -87,6 +87,9 @@ func detectBillTypeFromOutput(output string) string {
if strings.Contains(output, "微信") {
return "wechat"
}
if strings.Contains(output, "京东") {
return "jd"
}
return ""
}

View File

@@ -18,7 +18,7 @@ type ListBillsRequest struct {
StartDate string `form:"start_date"` // 开始日期 YYYY-MM-DD
EndDate string `form:"end_date"` // 结束日期 YYYY-MM-DD
Category string `form:"category"` // 分类筛选
Type string `form:"type"` // 账单类型 alipay/wechat
Type string `form:"type"` // 账单类型 alipay/wechat/jd
IncomeExpense string `form:"income_expense"` // 收支类型 收入/支出
}

View File

@@ -145,6 +145,8 @@ func Upload(c *gin.Context) {
billType = "alipay"
} else if strings.Contains(fileName, "微信") || strings.Contains(fileName, "wechat") {
billType = "wechat"
} else if strings.Contains(fileName, "京东") || strings.Contains(fileName, "jd") {
billType = "jd"
}
}
if billType == "" {
@@ -152,15 +154,15 @@ func Upload(c *gin.Context) {
service.CleanupExtractedFiles(extractedFiles)
c.JSON(http.StatusBadRequest, model.UploadResponse{
Result: false,
Message: "无法识别账单类型,请指定 type 参数 (alipaywechat)",
Message: "无法识别账单类型,请指定 type 参数 (alipay/wechat/jd)",
})
return
}
if billType != "alipay" && billType != "wechat" {
if billType != "alipay" && billType != "wechat" && billType != "jd" {
service.CleanupExtractedFiles(extractedFiles)
c.JSON(http.StatusBadRequest, model.UploadResponse{
Result: false,
Message: "账单类型无效,仅支持 alipaywechat",
Message: "账单类型无效,仅支持 alipay/wechat/jd",
})
return
}

View File

@@ -70,7 +70,7 @@ func (t LocalTime) Time() time.Time {
// RawBill 原始账单记录(存储上传的原始数据)
type RawBill struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
BillType string `bson:"bill_type" json:"bill_type"` // 账单类型: alipay/wechat
BillType string `bson:"bill_type" json:"bill_type"` // 账单类型: alipay/wechat/jd
SourceFile string `bson:"source_file" json:"source_file"` // 来源文件名
UploadBatch string `bson:"upload_batch" json:"upload_batch"` // 上传批次(时间戳)
RowIndex int `bson:"row_index" json:"row_index"` // 原始行号
@@ -81,7 +81,7 @@ type RawBill struct {
// CleanedBill 清洗后账单记录(标准化后的数据)
type CleanedBill struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
BillType string `bson:"bill_type" json:"bill_type"` // 账单类型: alipay/wechat
BillType string `bson:"bill_type" json:"bill_type"` // 账单类型: alipay/wechat/jd
TransactionID string `bson:"transaction_id" json:"transaction_id"` // 交易订单号(用于去重)
MerchantOrderNo string `bson:"merchant_order_no" json:"merchant_order_no"` // 商家订单号(用于去重)
Time LocalTime `bson:"time" json:"time"` // 交易时间(本地时间格式)

View File

@@ -2,7 +2,7 @@ package model
// UploadRequest 上传请求参数
type UploadRequest struct {
Type string `form:"type"` // 账单类型: alipay/wechat可选会自动检测
Type string `form:"type"` // 账单类型: alipay/wechat/jd(可选,会自动检测)
Password string `form:"password"` // ZIP 文件密码(可选)
Year string `form:"year"` // 年份筛选
Month string `form:"month"` // 月份筛选

View File

@@ -2,7 +2,7 @@ package model
// UploadData 上传响应数据
type UploadData struct {
BillType string `json:"bill_type,omitempty"` // alipay/wechat
BillType string `json:"bill_type,omitempty"` // alipay/wechat/jd
FileURL string `json:"file_url,omitempty"` // 下载链接
FileName string `json:"file_name,omitempty"` // 文件名
RawCount int `json:"raw_count,omitempty"` // 存储到原始数据集合的记录数

View File

@@ -105,6 +105,8 @@ func ExtractZip(zipPath, destDir, password string) (*ExtractResult, error) {
result.BillType = "alipay"
} else if strings.Contains(fileName, "微信") || strings.Contains(strings.ToLower(fileName), "wechat") {
result.BillType = "wechat"
} else if strings.Contains(fileName, "京东") || strings.Contains(strings.ToLower(fileName), "jd") {
result.BillType = "jd"
}
}
}

View File

@@ -150,6 +150,10 @@ func detectBillTypeAndIdField(header []string) (billType string, idFieldIdx int)
if col == "交易类型" || col == "金额(元)" {
billType = "wechat"
}
// 京东特征
if col == "商户名称" || col == "交易说明" {
billType = "jd"
}
// 查找去重字段(优先使用交易订单号/交易号)
if col == "交易订单号" || col == "交易号" || col == "交易单号" {

View File

@@ -36,6 +36,9 @@ func DetectBillTypeFromOutput(output string) string {
if containsSubstring(output, "微信") {
return "wechat"
}
if containsSubstring(output, "京东") {
return "jd"
}
return ""
}