feat: 支持账单编辑(PATCH /api/bills/:id)
This commit is contained in:
145
server/handler/update_bill.go
Normal file
145
server/handler/update_bill.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"billai-server/model"
|
||||
"billai-server/repository"
|
||||
)
|
||||
|
||||
// UpdateBillRequest 账单更新请求(字段均为可选)
|
||||
type UpdateBillRequest struct {
|
||||
Time *string `json:"time,omitempty"`
|
||||
Category *string `json:"category,omitempty"`
|
||||
Merchant *string `json:"merchant,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
IncomeExpense *string `json:"income_expense,omitempty"`
|
||||
Amount *float64 `json:"amount,omitempty"`
|
||||
PayMethod *string `json:"pay_method,omitempty"`
|
||||
Status *string `json:"status,omitempty"`
|
||||
Remark *string `json:"remark,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateBillResponse struct {
|
||||
Result bool `json:"result"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Data *model.CleanedBill `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func parseBillTime(s string) (time.Time, error) {
|
||||
s = strings.TrimSpace(s)
|
||||
formats := []string{
|
||||
"2006-01-02 15:04:05",
|
||||
"2006-01-02T15:04:05Z07:00",
|
||||
"2006-01-02T15:04:05Z",
|
||||
"2006-01-02",
|
||||
}
|
||||
for _, f := range formats {
|
||||
if t, err := time.ParseInLocation(f, s, time.Local); err == nil {
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
return time.Time{}, fmt.Errorf("unsupported time format")
|
||||
}
|
||||
|
||||
// UpdateBill PATCH /api/bills/:id 更新清洗后的账单记录
|
||||
func UpdateBill(c *gin.Context) {
|
||||
id := strings.TrimSpace(c.Param("id"))
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, UpdateBillResponse{Result: false, Message: "缺少账单 ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var req UpdateBillRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, UpdateBillResponse{Result: false, Message: "参数解析失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
updates := map[string]interface{}{}
|
||||
|
||||
if req.Time != nil {
|
||||
t, err := parseBillTime(*req.Time)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, UpdateBillResponse{Result: false, Message: "时间格式错误"})
|
||||
return
|
||||
}
|
||||
updates["time"] = t
|
||||
}
|
||||
|
||||
if req.Category != nil {
|
||||
v := strings.TrimSpace(*req.Category)
|
||||
if v == "" {
|
||||
c.JSON(http.StatusBadRequest, UpdateBillResponse{Result: false, Message: "分类不能为空"})
|
||||
return
|
||||
}
|
||||
updates["category"] = v
|
||||
}
|
||||
|
||||
if req.Merchant != nil {
|
||||
v := strings.TrimSpace(*req.Merchant)
|
||||
if v == "" {
|
||||
c.JSON(http.StatusBadRequest, UpdateBillResponse{Result: false, Message: "商家不能为空"})
|
||||
return
|
||||
}
|
||||
updates["merchant"] = v
|
||||
}
|
||||
|
||||
if req.Description != nil {
|
||||
updates["description"] = strings.TrimSpace(*req.Description)
|
||||
}
|
||||
|
||||
if req.IncomeExpense != nil {
|
||||
v := strings.TrimSpace(*req.IncomeExpense)
|
||||
if v != "" && v != "收入" && v != "支出" {
|
||||
c.JSON(http.StatusBadRequest, UpdateBillResponse{Result: false, Message: "income_expense 只能是 收入 或 支出"})
|
||||
return
|
||||
}
|
||||
updates["income_expense"] = v
|
||||
}
|
||||
|
||||
if req.Amount != nil {
|
||||
updates["amount"] = *req.Amount
|
||||
}
|
||||
|
||||
if req.PayMethod != nil {
|
||||
updates["pay_method"] = strings.TrimSpace(*req.PayMethod)
|
||||
}
|
||||
|
||||
if req.Status != nil {
|
||||
updates["status"] = strings.TrimSpace(*req.Status)
|
||||
}
|
||||
|
||||
if req.Remark != nil {
|
||||
updates["remark"] = strings.TrimSpace(*req.Remark)
|
||||
}
|
||||
|
||||
if len(updates) == 0 {
|
||||
c.JSON(http.StatusBadRequest, UpdateBillResponse{Result: false, Message: "没有可更新的字段"})
|
||||
return
|
||||
}
|
||||
updates["updated_at"] = time.Now()
|
||||
|
||||
repo := repository.GetRepository()
|
||||
if repo == nil {
|
||||
c.JSON(http.StatusInternalServerError, UpdateBillResponse{Result: false, Message: "数据库未连接"})
|
||||
return
|
||||
}
|
||||
|
||||
updated, err := repo.UpdateCleanedBillByID(id, updates)
|
||||
if err != nil {
|
||||
if err == repository.ErrNotFound {
|
||||
c.JSON(http.StatusNotFound, UpdateBillResponse{Result: false, Message: "账单不存在"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, UpdateBillResponse{Result: false, Message: "更新失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, UpdateBillResponse{Result: true, Message: "更新成功", Data: updated})
|
||||
}
|
||||
Reference in New Issue
Block a user