- TG: /chart 本月分类饼图, /week 近7天消费柱状图 - QQ: 统计/报表 本月文本统计 - 新增 go-chart 依赖生成 PNG 图表 - 新增 GetCategoryStats/GetDailyStats 查询方法
155 lines
3.9 KiB
Go
155 lines
3.9 KiB
Go
package service
|
||
|
||
import (
|
||
"math"
|
||
"regexp"
|
||
"strconv"
|
||
"time"
|
||
|
||
"xiaji-go/models"
|
||
|
||
"github.com/yanyiwu/gojieba"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
type FinanceService struct {
|
||
db *gorm.DB
|
||
jieba *gojieba.Jieba
|
||
}
|
||
|
||
func NewFinanceService(db *gorm.DB) *FinanceService {
|
||
return &FinanceService{
|
||
db: db,
|
||
jieba: gojieba.NewJieba(),
|
||
}
|
||
}
|
||
|
||
func (s *FinanceService) Close() {
|
||
s.jieba.Free()
|
||
}
|
||
|
||
// ParseText 从自然语言文本中提取金额(分)和分类
|
||
func (s *FinanceService) ParseText(text string) (int64, string) {
|
||
// 1. 提取金额 — 优先匹配带单位的,如 "15.5元"、"¥30"、"20块"
|
||
amountPatterns := []*regexp.Regexp{
|
||
regexp.MustCompile(`[¥¥]\s*(\d+\.?\d*)`),
|
||
regexp.MustCompile(`(\d+\.?\d*)\s*[元块]`),
|
||
}
|
||
|
||
var amountStr string
|
||
for _, re := range amountPatterns {
|
||
m := re.FindStringSubmatch(text)
|
||
if len(m) > 1 {
|
||
amountStr = m[1]
|
||
break
|
||
}
|
||
}
|
||
|
||
// 兜底:取最后一个独立数字
|
||
if amountStr == "" {
|
||
re := regexp.MustCompile(`(\d+\.?\d*)`)
|
||
matches := re.FindAllStringSubmatch(text, -1)
|
||
if len(matches) > 0 {
|
||
amountStr = matches[len(matches)-1][1]
|
||
}
|
||
}
|
||
|
||
if amountStr == "" {
|
||
return 0, ""
|
||
}
|
||
|
||
amountFloat, err := strconv.ParseFloat(amountStr, 64)
|
||
if err != nil || amountFloat <= 0 {
|
||
return 0, ""
|
||
}
|
||
|
||
// 转为分
|
||
amountCents := int64(math.Round(amountFloat * 100))
|
||
|
||
// 2. 提取分类(Jieba 分词 + 数据库匹配)
|
||
words := s.jieba.Cut(text, true)
|
||
category := "其他"
|
||
|
||
for _, word := range words {
|
||
var ck models.CategoryKeyword
|
||
if err := s.db.Where("keyword = ?", word).First(&ck).Error; err == nil {
|
||
category = ck.Category
|
||
break
|
||
}
|
||
}
|
||
|
||
return amountCents, category
|
||
}
|
||
|
||
// AddTransaction 解析文本并创建一条交易记录
|
||
func (s *FinanceService) AddTransaction(userID int64, text string) (int64, string, error) {
|
||
amount, category := s.ParseText(text)
|
||
if amount == 0 {
|
||
return 0, "", nil
|
||
}
|
||
|
||
tx := models.Transaction{
|
||
UserID: userID,
|
||
Amount: amount,
|
||
Category: category,
|
||
Note: text,
|
||
Date: time.Now().Format("2006-01-02"),
|
||
}
|
||
|
||
return amount, category, s.db.Create(&tx).Error
|
||
}
|
||
|
||
// GetTransactions 获取用户的交易记录
|
||
func (s *FinanceService) GetTransactions(userID int64, limit int) ([]models.Transaction, error) {
|
||
var items []models.Transaction
|
||
err := s.db.Where("user_id = ? AND is_deleted = ?", userID, false).
|
||
Order("id desc").Limit(limit).Find(&items).Error
|
||
return items, err
|
||
}
|
||
|
||
// GetTransactionsByDate 获取用户指定日期的交易记录
|
||
func (s *FinanceService) GetTransactionsByDate(userID int64, date string) ([]models.Transaction, error) {
|
||
var items []models.Transaction
|
||
err := s.db.Where("user_id = ? AND date = ? AND is_deleted = ?", userID, date, false).
|
||
Order("id desc").Find(&items).Error
|
||
return items, err
|
||
}
|
||
|
||
// CategoryStat 分类统计结果
|
||
type CategoryStat struct {
|
||
Category string
|
||
Total int64
|
||
Count int
|
||
}
|
||
|
||
// GetCategoryStats 获取用户指定日期范围的分类统计
|
||
func (s *FinanceService) GetCategoryStats(userID int64, dateFrom, dateTo string) ([]CategoryStat, error) {
|
||
var stats []CategoryStat
|
||
err := s.db.Model(&models.Transaction{}).
|
||
Select("category, SUM(amount) as total, COUNT(*) as count").
|
||
Where("user_id = ? AND date >= ? AND date <= ? AND is_deleted = ?", userID, dateFrom, dateTo, false).
|
||
Group("category").
|
||
Order("total desc").
|
||
Find(&stats).Error
|
||
return stats, err
|
||
}
|
||
|
||
// DailyStat 每日统计结果
|
||
type DailyStat struct {
|
||
Date string
|
||
Total int64
|
||
Count int
|
||
}
|
||
|
||
// GetDailyStats 获取用户指定日期范围的每日统计
|
||
func (s *FinanceService) GetDailyStats(userID int64, dateFrom, dateTo string) ([]DailyStat, error) {
|
||
var stats []DailyStat
|
||
err := s.db.Model(&models.Transaction{}).
|
||
Select("date, SUM(amount) as total, COUNT(*) as count").
|
||
Where("user_id = ? AND date >= ? AND date <= ? AND is_deleted = ?", userID, dateFrom, dateTo, false).
|
||
Group("date").
|
||
Order("date asc").
|
||
Find(&stats).Error
|
||
return stats, err
|
||
}
|