init: ops-assistant codebase

This commit is contained in:
OpenClaw Agent
2026-03-19 21:23:28 +08:00
commit 81deba4766
94 changed files with 10767 additions and 0 deletions

154
internal/service/finance.go Normal file
View File

@@ -0,0 +1,154 @@
package service
import (
"math"
"regexp"
"strconv"
"time"
"ops-assistant/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
}