package money import ( "base-go-v2/internal/db" response "base-go-v2/internal/reponse" "base-go-v2/internal/router" "base-go-v2/internal/utils/idgen" "base-go-v2/internal/utils/mapx" "fmt" "sort" "time" "github.com/shopspring/decimal" ) func InitMoneyRouter(r *router.Router) { group := r.Group("money") group.POST("/query", queryMoneyRecords) group.GET("/category", getUserCategory) group.POST("/insert", insertMoneyRecord) group.POST("/update", updateMoneyRecord) group.POST("/delete", deleteMoneyRecord) } // InitDefaultCategories 初始化系统默认分类到用户分类表 func InitDefaultCategories(userID int64) error { // 1. 查询系统分类表 sysCategories, err := db.SelectList("money_sys_category", "", "sort_number") if err != nil { return err } fmt.Printf("系统分类数量: %d\n", len(sysCategories)) // 2. 构建批量插入数据 userCategories := mapx.NewArr(len(sysCategories)) for i, row := range sysCategories { m := mapx.New().SetKV( "id", idgen.NextId(), "user_id", userID, "name", row.GetString("name"), "icon", row.GetString("icon"), "type", row.GetInt("type"), "sort_number", row.GetInt("sort_number"), ) fmt.Printf("用户分类 %d: %+v\n", i+1, m) userCategories = append(userCategories, m) } // 3. 批量插入到用户分类表 if _, err := db.InsertBatch("money_user_category", userCategories); err != nil { return err } return nil } func queryMoneyRecords(c *router.Context) error { bodyData, err := c.GetBodyWithRequired("record_time") // "YYYY-MM" if err != nil { return c.AddError(err) } recordMonth := bodyData.GetString("record_time") // 解析年月 startTime, err := time.Parse("2006-01", recordMonth) if err != nil { return c.AddError(fmt.Errorf("invalid record_time format: %v", err)) } // 获取当月最后一天 endTime := startTime.AddDate(0, 1, 0).Add(-time.Nanosecond) // 查询 SQL sqlStr := fmt.Sprintf(` SELECT mr.*, muc.name AS category_name, muc.icon AS category_icon, muc.type AS category_type FROM money_record mr LEFT JOIN money_user_category muc ON mr.category_id = muc.id WHERE mr.create_time BETWEEN '%s' AND '%s' ORDER BY mr.record_time DESC, mr.create_time DESC `, startTime.Format("2006-01-02 15:04:05"), endTime.Format("2006-01-02 15:04:05")) list, err := db.SelectBySql(sqlStr) if err != nil { return c.AddError(err) } // 分组数据 grouped := mapx.New() totalIncome := decimal.NewFromInt(0) totalExpense := decimal.NewFromInt(0) for _, r := range list { row := mapx.M(r) recordTime, ok := row["record_time"].(time.Time) if !ok { continue } day := recordTime.Format("2006-01-02") // 获取当天数据 dayDataI := grouped.Get(day) var dayData mapx.M if dayDataI == nil { dayData = mapx.New(). Set("records", mapx.NewArr()). Set("income", "0.00"). Set("expense", "0.00") } else { dayData = dayDataI.(mapx.M) } // 追加记录 records := dayData.GetArray("records") records = append(records, row) dayData.Set("records", records) // 计算当天收入支出 categoryType := row.GetInt("category_type") amount, err := decimal.NewFromString(row.GetString("amount")) if err != nil { continue } dayIncome, _ := decimal.NewFromString(dayData.GetString("income")) dayExpense, _ := decimal.NewFromString(dayData.GetString("expense")) if categoryType == 1 { dayIncome = dayIncome.Add(amount) totalIncome = totalIncome.Add(amount) } else if categoryType == 0 { dayExpense = dayExpense.Add(amount) totalExpense = totalExpense.Add(amount) } // ⭐ 保留两位小数 dayData.Set("income", dayIncome.Round(2).StringFixed(2)) dayData.Set("expense", dayExpense.Round(2).StringFixed(2)) grouped.Set(day, dayData) } // 对日期排序,最新在前 dates := make([]string, 0, len(grouped)) for k := range grouped { dates = append(dates, k) } sort.Slice(dates, func(i, j int) bool { return dates[i] > dates[j] }) // 构建最终 records 数组 recordsArr := make([]mapx.M, 0, len(dates)) for _, date := range dates { dayData := grouped.Get(date).(mapx.M) recordsArr = append(recordsArr, mapx.New(). Set("record_time", date). Set("income", dayData.GetString("income")). Set("expense", dayData.GetString("expense")). Set("records", dayData.GetArray("records"))) } // ⭐ 最终总收入/支出保留两位小数 result := mapx.New(). Set("records", recordsArr). Set("total_income", totalIncome.Round(2).StringFixed(2)). Set("total_expense", totalExpense.Round(2).StringFixed(2)) return response.Success(c).Data(result).Send() } func getUserCategory(c *router.Context) error { userId := c.GetUserID() // 查询当前用户的分类,已经按 sort_number 升序 userCategories, err := db.SelectList( "money_user_category", db.BuildWhereString(mapx.New().Set("user_id", userId)), "sort_number ASC", ) if err != nil { return c.AddError(err) } return response.Success(c).Data(userCategories).Send() } func insertMoneyRecord(c *router.Context) error { bodyData, err := c.GetBodyWithRequired("category_id", "amount", "record_time") if err != nil { return c.AddError(err) } categoryId := bodyData.GetInt64("category_id") recordTime := bodyData.GetString("record_time") amountStr := bodyData.GetString("amount") remark := bodyData.GetString("remark") amountDecimal, err := decimal.NewFromString(amountStr) if err != nil { return c.AddError(err) } newRecord := mapx.New().SetKV("id", idgen.NextId(), "category_id", categoryId, "user_id", c.GetUserID(), "amount", amountDecimal, "remark", remark, "record_time", recordTime, ) insertOne, err := db.InsertOne("money_record", newRecord) if err != nil { return response.Fail(c).Message("新增失败").Send() } if insertOne != 1 { return response.Fail(c).Message("新增失败").Send() } return response.Success(c).Data(newRecord).Message("新增成功").Send() } func updateMoneyRecord(c *router.Context) error { bodyData, err := c.GetBodyWithRequired("id") if err != nil { return c.AddError(err) } recordId := bodyData.GetInt64("id") record, err := db.SelectById("money_record", "id", recordId) if err != nil { return c.AddError(err) } if record == nil { return response.Fail(c).Message("该条账单不存在").Send() } categoryId := bodyData.GetInt64("category_id") recordTime := bodyData.GetString("record_time") amountStr := bodyData.GetString("amount") remark := bodyData.GetString("remark") updateRecord := mapx.New().SetKV("id", recordId) if record.GetInt64("category_id") != categoryId { updateRecord.SetKV("category_id", categoryId) } if record.GetString("record_time") != recordTime { updateRecord.SetKV("record_time", recordTime) } if record.GetString("amount") != amountStr { amountDecimal, err := decimal.NewFromString(amountStr) if err != nil { return c.AddError(err) } updateRecord.SetKV("amount", amountDecimal) } if record.GetString("remark") != remark { updateRecord.SetKV("remark", remark) } updateOne, err := db.UpdateOne("money_record", "id", recordId, updateRecord) if err != nil { return c.AddError(err) } if updateOne != 1 { return response.Fail(c).Message("更新失败").Send() } return response.Success(c).Message("更新成功").Send() } func deleteMoneyRecord(c *router.Context) error { bodyData, err := c.GetBodyWithRequired("id") if err != nil { return c.AddError(err) } recordId := bodyData.GetInt64("id") _, err = db.DeleteOne("money_record", "id", recordId) if err != nil { return c.AddError(err) } return response.Success(c).Message("删除成功").Send() }