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

134 lines
4.0 KiB
Go

package handler
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
"billai-server/model"
"billai-server/repository"
)
// ManualBillInput 手动输入的账单
type ManualBillInput struct {
Time string `json:"time" binding:"required"` // 交易时间
Category string `json:"category" binding:"required"` // 交易分类
Merchant string `json:"merchant" binding:"required"` // 交易对方
Description string `json:"description"` // 商品说明
IncomeExpense string `json:"income_expense" binding:"required"` // 收/支
Amount float64 `json:"amount" binding:"required"` // 金额
PayMethod string `json:"pay_method"` // 支付方式
Status string `json:"status"` // 交易状态
Remark string `json:"remark"` // 备注
}
// CreateManualBillsRequest 批量创建手动账单请求
type CreateManualBillsRequest struct {
Bills []ManualBillInput `json:"bills" binding:"required,min=1"`
}
// CreateManualBillsResponse 批量创建手动账单响应
type CreateManualBillsResponse struct {
Result bool `json:"result"`
Message string `json:"message,omitempty"`
Data *CreateManualBillsData `json:"data,omitempty"`
}
// CreateManualBillsData 批量创建手动账单数据
type CreateManualBillsData struct {
Success int `json:"success"` // 成功创建数量
Failed int `json:"failed"` // 失败数量
Duplicates int `json:"duplicates"` // 重复数量
}
// CreateManualBills 批量创建手动输入的账单
func CreateManualBills(c *gin.Context) {
var req CreateManualBillsRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, CreateManualBillsResponse{
Result: false,
Message: "参数解析失败: " + err.Error(),
})
return
}
// 获取 repository
repo := repository.GetRepository()
if repo == nil {
c.JSON(http.StatusInternalServerError, CreateManualBillsResponse{
Result: false,
Message: "数据库未连接",
})
return
}
// 转换为 CleanedBill
bills := make([]model.CleanedBill, 0, len(req.Bills))
for _, input := range req.Bills {
// 解析时间(使用本地时区,避免 UTC 时区问题)
t, err := time.ParseInLocation("2006-01-02 15:04:05", input.Time, time.Local)
if err != nil {
// 尝试另一种格式
t, err = time.ParseInLocation("2006-01-02T15:04:05Z07:00", input.Time, time.Local)
if err != nil {
c.JSON(http.StatusBadRequest, CreateManualBillsResponse{
Result: false,
Message: "时间格式错误: " + input.Time,
})
return
}
}
// 设置默认值
status := input.Status
if status == "" {
status = "交易成功"
}
bill := model.CleanedBill{
BillType: "manual",
TransactionID: "", // 手动账单不设置交易ID
MerchantOrderNo: "",
Time: model.LocalTime(t),
Category: input.Category,
Merchant: input.Merchant,
Description: input.Description,
IncomeExpense: input.IncomeExpense,
Amount: input.Amount,
PayMethod: input.PayMethod,
Status: status,
Remark: input.Remark,
ReviewLevel: "", // 手动账单不需要复核
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
SourceFile: "manual_input",
UploadBatch: time.Now().Format("20060102_150405"),
}
bills = append(bills, bill)
}
// 保存账单(带去重)
saved, duplicates, err := repo.SaveCleanedBills(bills)
if err != nil {
c.JSON(http.StatusInternalServerError, CreateManualBillsResponse{
Result: false,
Message: "保存失败: " + err.Error(),
})
return
}
failed := len(bills) - saved - duplicates
c.JSON(http.StatusOK, CreateManualBillsResponse{
Result: true,
Message: "创建成功",
Data: &CreateManualBillsData{
Success: saved,
Failed: failed,
Duplicates: duplicates,
},
})
}