- 新增删除按钮(带二次确认)到账单详情抽屉 - 后端实现软删除(设置 is_deleted 标记) - 所有查询方法自动过滤已删除记录 - 账单列表和复核页面都支持删除 - 版本更新至 1.2.0
157 lines
4.3 KiB
Go
157 lines
4.3 KiB
Go
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"`
|
|
ReviewLevel *string `json:"review_level,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 POST /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 req.ReviewLevel != nil {
|
|
// 允许设置为空字符串(清除复核等级)或 HIGH/LOW
|
|
v := strings.TrimSpace(*req.ReviewLevel)
|
|
if v != "" && v != "HIGH" && v != "LOW" {
|
|
c.JSON(http.StatusBadRequest, UpdateBillResponse{Result: false, Message: "review_level 只能是空、HIGH 或 LOW"})
|
|
return
|
|
}
|
|
updates["review_level"] = v
|
|
}
|
|
|
|
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})
|
|
}
|