package handler import ( "net/http" "strconv" "time" "github.com/gin-gonic/gin" "billai-server/model" "billai-server/repository" ) // ListBillsRequest 账单列表请求参数 type ListBillsRequest struct { Page int `form:"page"` // 页码,从 1 开始 PageSize int `form:"page_size"` // 每页数量,默认 20 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/jd IncomeExpense string `form:"income_expense"` // 收支类型 收入/支出 } // ListBillsResponse 账单列表响应 type ListBillsResponse struct { Result bool `json:"result"` Message string `json:"message,omitempty"` Data *ListBillsData `json:"data,omitempty"` } // ListBillsData 账单列表数据 type ListBillsData struct { Total int64 `json:"total"` // 总记录数 TotalExpense float64 `json:"total_expense"` // 筛选条件下的总支出 TotalIncome float64 `json:"total_income"` // 筛选条件下的总收入 Page int `json:"page"` // 当前页码 PageSize int `json:"page_size"` // 每页数量 Pages int `json:"pages"` // 总页数 Bills []model.CleanedBill `json:"bills"` // 账单列表 } // ListBills 获取清洗后的账单列表 func ListBills(c *gin.Context) { var req ListBillsRequest if err := c.ShouldBindQuery(&req); err != nil { c.JSON(http.StatusBadRequest, ListBillsResponse{ Result: false, Message: "参数解析失败: " + err.Error(), }) return } // 设置默认值 if req.Page < 1 { req.Page = 1 } if req.PageSize < 1 { req.PageSize = 20 } // if req.PageSize > 100 { // req.PageSize = 100 // 限制最大每页数量 // } // 构建筛选条件 filter := make(map[string]interface{}) // 时间范围筛选 if req.StartDate != "" || req.EndDate != "" { timeFilter := make(map[string]interface{}) if req.StartDate != "" { // 使用本地时区解析日期,避免 UTC 时区问题 startTime, err := time.ParseInLocation("2006-01-02", req.StartDate, time.Local) if err == nil { timeFilter["$gte"] = startTime } } if req.EndDate != "" { // 使用本地时区解析日期,避免 UTC 时区问题 endTime, err := time.ParseInLocation("2006-01-02", req.EndDate, time.Local) if err == nil { // 结束日期包含当天,所以加一天 endTime = endTime.Add(24 * time.Hour) timeFilter["$lt"] = endTime } } if len(timeFilter) > 0 { filter["time"] = timeFilter } } // 分类筛选 if req.Category != "" { filter["category"] = req.Category } // 账单类型筛选 if req.Type != "" { filter["bill_type"] = req.Type } // 收支类型筛选 if req.IncomeExpense != "" { filter["income_expense"] = req.IncomeExpense } // 获取数据 repo := repository.GetRepository() if repo == nil { c.JSON(http.StatusInternalServerError, ListBillsResponse{ Result: false, Message: "数据库未连接", }) return } // 获取账单列表(带分页) bills, total, err := repo.GetCleanedBillsPaged(filter, req.Page, req.PageSize) if err != nil { c.JSON(http.StatusInternalServerError, ListBillsResponse{ Result: false, Message: "查询失败: " + err.Error(), }) return } // 获取聚合统计 totalExpense, totalIncome, err := repo.GetBillsAggregate(filter) if err != nil { c.JSON(http.StatusInternalServerError, ListBillsResponse{ Result: false, Message: "统计失败: " + err.Error(), }) return } // 计算总页数 pages := int(total) / req.PageSize if int(total)%req.PageSize > 0 { pages++ } c.JSON(http.StatusOK, ListBillsResponse{ Result: true, Data: &ListBillsData{ Total: total, TotalExpense: totalExpense, TotalIncome: totalIncome, Page: req.Page, PageSize: req.PageSize, Pages: pages, Bills: bills, }, }) } // parsePageParam 解析分页参数 func parsePageParam(s string, defaultVal int) int { if s == "" { return defaultVal } val, err := strconv.Atoi(s) if err != nil || val < 1 { return defaultVal } return val } // MonthlyStatsResponse 月度统计响应 type MonthlyStatsResponse struct { Result bool `json:"result"` Message string `json:"message,omitempty"` Data []model.MonthlyStat `json:"data,omitempty"` } // MonthlyStats 获取月度统计数据(全部数据,不受筛选条件影响) func MonthlyStats(c *gin.Context) { repo := repository.GetRepository() if repo == nil { c.JSON(http.StatusInternalServerError, MonthlyStatsResponse{ Result: false, Message: "数据库未连接", }) return } stats, err := repo.GetMonthlyStats() if err != nil { c.JSON(http.StatusInternalServerError, MonthlyStatsResponse{ Result: false, Message: "查询失败: " + err.Error(), }) return } c.JSON(http.StatusOK, MonthlyStatsResponse{ Result: true, Data: stats, }) } // ReviewStats 获取待复核数据统计 func ReviewStats(c *gin.Context) { repo := repository.GetRepository() // 从MongoDB查询所有需要复核的账单 bills, err := repo.GetBillsNeedReview() if err != nil { c.JSON(http.StatusInternalServerError, model.ReviewResponse{ Result: false, Message: "查询失败: " + err.Error(), }) return } highCount := 0 lowCount := 0 totalCount := 0 // 统计各等级的复核记录 for _, bill := range bills { totalCount++ if bill.ReviewLevel == "HIGH" { highCount++ } else if bill.ReviewLevel == "LOW" { lowCount++ } } c.JSON(http.StatusOK, model.ReviewResponse{ Result: true, Message: "获取成功", Data: &model.ReviewData{ Total: totalCount, High: highCount, Low: lowCount, Records: nil, // 统计接口不返回具体记录 }, }) }