You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
4.3 KiB
144 lines
4.3 KiB
package memory
|
|
|
|
import (
|
|
"sort"
|
|
|
|
"yunyan/comm"
|
|
"yunyan/lego/sys/mysql"
|
|
"yunyan/pb"
|
|
)
|
|
|
|
/*
|
|
周期统计。
|
|
|
|
⚠️ 金额与完成数**全部走 SQL**,绝不让 LLM 算。LLM 只负责把算好的 stat_json
|
|
组织成一段适合朗读的话——数字算错比话说得干巴严重得多。
|
|
*/
|
|
|
|
// 明细条数上限。一个重度用户一周能攒两百条未完成,全塞进 prompt 会顶爆上下文、
|
|
// 拖慢生成,而且**播报出来没人听得完**。总数用 TodoUndone 表达,明细只举几条。
|
|
const statItemLimit = 20
|
|
|
|
type statBrief struct {
|
|
Id uint64 `json:"id"`
|
|
Title string `json:"title"`
|
|
HappenDate string `json:"happen_date"`
|
|
}
|
|
|
|
type statExpense struct {
|
|
Currency string `json:"currency"`
|
|
TotalCents int64 `json:"total_cents"`
|
|
Count int32 `json:"count"`
|
|
}
|
|
|
|
type memoryStat struct {
|
|
PeriodStart string `json:"period_start"`
|
|
PeriodEnd string `json:"period_end"`
|
|
|
|
TodoTotal int32 `json:"todo_total"`
|
|
TodoDone int32 `json:"todo_done"`
|
|
TodoUndone int32 `json:"todo_undone"`
|
|
AlarmTotal int32 `json:"alarm_total"`
|
|
IdeaCount int32 `json:"idea_count"`
|
|
|
|
UndoneItems []statBrief `json:"undone_items"`
|
|
UndoneTruncated bool `json:"undone_truncated"`
|
|
IdeaItems []statBrief `json:"idea_items"`
|
|
IdeaTruncated bool `json:"idea_truncated"`
|
|
|
|
Expenses []statExpense `json:"expenses"`
|
|
}
|
|
|
|
// hasContent 报告有没有值得播报的内容。全空就别生成了。
|
|
func (s *memoryStat) hasContent() bool {
|
|
return s.TodoTotal > 0 || s.AlarmTotal > 0 || s.IdeaCount > 0 || len(s.Expenses) > 0
|
|
}
|
|
|
|
// computeStats 按日期范围算统计(含 start 与 end 当天)
|
|
func (this *Memory) computeStats(uid, start, end string) (*memoryStat, error) {
|
|
st := &memoryStat{PeriodStart: start, PeriodEnd: end,
|
|
UndoneItems: []statBrief{}, IdeaItems: []statBrief{}, Expenses: []statExpense{}}
|
|
|
|
base := func() interface {
|
|
Count(*int64) error
|
|
} {
|
|
return nil
|
|
}
|
|
_ = base
|
|
|
|
type catCount struct {
|
|
Category string
|
|
State int32
|
|
N int64
|
|
}
|
|
rows := make([]catCount, 0)
|
|
err := mysql.Table(comm.TableMemoryItem).
|
|
Select("category, state, count(*) as n").
|
|
Where("uid = ? and happen_date >= ? and happen_date <= ?", uid, start, end).
|
|
Group("category, state").Find(&rows).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, r := range rows {
|
|
switch r.Category {
|
|
case comm.MemoryCatTodo:
|
|
st.TodoTotal += int32(r.N)
|
|
if r.State == int32(pb.MemoryState_MemoryState_Done) {
|
|
st.TodoDone += int32(r.N)
|
|
}
|
|
case comm.MemoryCatAlarm:
|
|
st.AlarmTotal += int32(r.N)
|
|
case comm.MemoryCatIdea:
|
|
st.IdeaCount += int32(r.N)
|
|
}
|
|
}
|
|
st.TodoUndone = st.TodoTotal - st.TodoDone
|
|
|
|
// 花销按币种聚合。金额是 int64 分,SUM 不会有浮点误差。
|
|
expRows := make([]statExpense, 0)
|
|
err = mysql.Table(comm.TableMemoryItem).
|
|
Select("currency, sum(amount_cents) as total_cents, count(*) as count").
|
|
Where("uid = ? and happen_date >= ? and happen_date <= ? and category = ?",
|
|
uid, start, end, comm.MemoryCatExpense).
|
|
Group("currency").Find(&expRows).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sort.Slice(expRows, func(i, j int) bool { return expRows[i].TotalCents > expRows[j].TotalCents })
|
|
st.Expenses = expRows
|
|
|
|
// 明细:未完成待办
|
|
undone := make([]*pb.DBMemoryItem, 0)
|
|
err = mysql.Table(comm.TableMemoryItem).
|
|
Where("uid = ? and happen_date >= ? and happen_date <= ? and category = ? and state = ?",
|
|
uid, start, end, comm.MemoryCatTodo, int32(pb.MemoryState_MemoryState_Pending)).
|
|
Order("happen_date asc").Limit(statItemLimit + 1).Find(&undone).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(undone) > statItemLimit {
|
|
st.UndoneTruncated = true
|
|
undone = undone[:statItemLimit]
|
|
}
|
|
for _, it := range undone {
|
|
st.UndoneItems = append(st.UndoneItems, statBrief{Id: it.Id, Title: it.Title, HappenDate: it.HappenDate})
|
|
}
|
|
|
|
// 明细:灵感
|
|
ideas := make([]*pb.DBMemoryItem, 0)
|
|
err = mysql.Table(comm.TableMemoryItem).
|
|
Where("uid = ? and happen_date >= ? and happen_date <= ? and category = ?",
|
|
uid, start, end, comm.MemoryCatIdea).
|
|
Order("happen_date asc").Limit(statItemLimit + 1).Find(&ideas).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(ideas) > statItemLimit {
|
|
st.IdeaTruncated = true
|
|
ideas = ideas[:statItemLimit]
|
|
}
|
|
for _, it := range ideas {
|
|
st.IdeaItems = append(st.IdeaItems, statBrief{Id: it.Id, Title: it.Title, HappenDate: it.HappenDate})
|
|
}
|
|
return st, nil
|
|
}
|
|
|