Files
billai/server/model/bill.go
cheliangzhao 8a9de1b328 fix: 修复账单时间显示为UTC时区问题,改为本地时间
- 新增 LocalTime 自定义类型,JSON序列化输出本地时间格式
- 修改 CleanedBill.Time 字段类型为 LocalTime
- 更新 parseTime 函数返回 LocalTime 类型
- 前端添加 formatDateTime 工具函数(兼容处理)
- 版本号更新至 1.0.2
2026-01-11 21:40:27 +08:00

109 lines
4.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package model
import (
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/bsontype"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// LocalTime 自定义时间类型JSON 序列化时输出本地时间格式
type LocalTime time.Time
// MarshalJSON 实现 json.Marshaler 接口,输出本地时间格式
func (t LocalTime) MarshalJSON() ([]byte, error) {
tt := time.Time(t)
if tt.IsZero() {
return []byte(`""`), nil
}
// 输出格式: "2006-01-02 15:04:05"
return []byte(`"` + tt.Local().Format("2006-01-02 15:04:05") + `"`), nil
}
// UnmarshalJSON 实现 json.Unmarshaler 接口
func (t *LocalTime) UnmarshalJSON(data []byte) error {
s := string(data)
if s == `""` || s == "null" {
*t = LocalTime(time.Time{})
return nil
}
// 去掉引号
s = s[1 : len(s)-1]
// 尝试多种格式解析
formats := []string{
"2006-01-02 15:04:05",
"2006-01-02T15:04:05Z07:00",
"2006-01-02T15:04:05Z",
"2006-01-02",
}
for _, format := range formats {
if parsed, err := time.ParseInLocation(format, s, time.Local); err == nil {
*t = LocalTime(parsed)
return nil
}
}
return nil
}
// MarshalBSONValue 实现 bson.ValueMarshaler 接口
func (t LocalTime) MarshalBSONValue() (bsontype.Type, []byte, error) {
return bson.MarshalValue(time.Time(t))
}
// UnmarshalBSONValue 实现 bson.ValueUnmarshaler 接口
func (t *LocalTime) UnmarshalBSONValue(btype bsontype.Type, data []byte) error {
var tt time.Time
if err := bson.UnmarshalValue(btype, data, &tt); err != nil {
return err
}
*t = LocalTime(tt)
return nil
}
// Time 返回标准 time.Time
func (t LocalTime) Time() time.Time {
return time.Time(t)
}
// RawBill 原始账单记录(存储上传的原始数据)
type RawBill struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
BillType string `bson:"bill_type" json:"bill_type"` // 账单类型: alipay/wechat
SourceFile string `bson:"source_file" json:"source_file"` // 来源文件名
UploadBatch string `bson:"upload_batch" json:"upload_batch"` // 上传批次(时间戳)
RowIndex int `bson:"row_index" json:"row_index"` // 原始行号
RawData map[string]interface{} `bson:"raw_data" json:"raw_data"` // 原始字段数据
CreatedAt time.Time `bson:"created_at" json:"created_at"` // 创建时间
}
// CleanedBill 清洗后账单记录(标准化后的数据)
type CleanedBill struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
BillType string `bson:"bill_type" json:"bill_type"` // 账单类型: alipay/wechat
TransactionID string `bson:"transaction_id" json:"transaction_id"` // 交易订单号(用于去重)
MerchantOrderNo string `bson:"merchant_order_no" json:"merchant_order_no"` // 商家订单号(用于去重)
Time LocalTime `bson:"time" json:"time"` // 交易时间(本地时间格式)
Category string `bson:"category" json:"category"` // 交易分类
Merchant string `bson:"merchant" json:"merchant"` // 交易对方
Description string `bson:"description" json:"description"` // 商品说明
IncomeExpense string `bson:"income_expense" json:"income_expense"` // 收/支
Amount float64 `bson:"amount" json:"amount"` // 金额
PayMethod string `bson:"pay_method" json:"pay_method"` // 支付方式
Status string `bson:"status" json:"status"` // 交易状态
Remark string `bson:"remark" json:"remark"` // 备注
ReviewLevel string `bson:"review_level" json:"review_level"` // 复核等级: HIGH/LOW/空
CreatedAt time.Time `bson:"created_at" json:"created_at"` // 创建时间
UpdatedAt time.Time `bson:"updated_at" json:"updated_at"` // 更新时间
SourceFile string `bson:"source_file" json:"source_file"` // 来源文件名
UploadBatch string `bson:"upload_batch" json:"upload_batch"` // 上传批次(时间戳)
}
// MonthlyStat 月度统计数据
type MonthlyStat struct {
Month string `bson:"month" json:"month"` // 月份 YYYY-MM
Expense float64 `bson:"expense" json:"expense"` // 月支出总额
Income float64 `bson:"income" json:"income"` // 月收入总额
}