fix: 修复19个Bug (P0-P3) — 持续性调试第7轮发现的问题
P0 (5): crypto/rand session ID, TTS fallback可达性, goroutine defer recover, adminAuth前缀修正 P1 (5): 普通用户密码验证, context传递, priority clamp, 超时重试, 自主思考速率限制 P2 (4): Briefing AI降级, 前端消息类型渲染, Docker Compose补全, PWA 192图标 P3 (5): goroutine错误处理, .gitignore完善, reminder created_at, voice Dockerfile, Go版本更新
This commit is contained in:
@@ -10,17 +10,18 @@ import (
|
||||
|
||||
// Briefing 每日简报模型
|
||||
type Briefing struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
Date string `json:"date"` // YYYY-MM-DD
|
||||
Weather *WeatherData `json:"weather"`
|
||||
News []NewsItem `json:"news"`
|
||||
Reminders []BriefReminder `json:"reminders"`
|
||||
Summary string `json:"summary"`
|
||||
Status string `json:"status"` // pending, generated, delivered
|
||||
GeneratedAt *time.Time `json:"generated_at,omitempty"`
|
||||
DeliveredAt *time.Time `json:"delivered_at,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
Date string `json:"date"` // YYYY-MM-DD
|
||||
Weather *WeatherData `json:"weather"`
|
||||
News []NewsItem `json:"news"`
|
||||
Reminders []BriefReminder `json:"reminders"`
|
||||
Summary string `json:"summary"`
|
||||
SummarySource string `json:"summary_source"` // "ai" | "fallback"
|
||||
Status string `json:"status"` // pending, generated, delivered
|
||||
GeneratedAt *time.Time `json:"generated_at,omitempty"`
|
||||
DeliveredAt *time.Time `json:"delivered_at,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// WeatherData 天气数据
|
||||
@@ -74,12 +75,14 @@ func (s *BriefingStore) migrate() error {
|
||||
news JSONB DEFAULT '[]',
|
||||
reminders JSONB DEFAULT '[]',
|
||||
summary TEXT DEFAULT '',
|
||||
summary_source VARCHAR(20) DEFAULT 'ai',
|
||||
status VARCHAR(20) DEFAULT 'pending',
|
||||
generated_at TIMESTAMPTZ,
|
||||
delivered_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(user_id, date)
|
||||
)`,
|
||||
`ALTER TABLE daily_briefings ADD COLUMN IF NOT EXISTS summary_source VARCHAR(20) DEFAULT 'ai'`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_briefings_user_id ON daily_briefings(user_id)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_briefings_date ON daily_briefings(date)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_briefings_user_date ON daily_briefings(user_id, date)`,
|
||||
@@ -108,19 +111,24 @@ func (s *BriefingStore) CreateOrUpdateBriefing(b *Briefing) error {
|
||||
return fmt.Errorf("序列化提醒数据失败: %w", err)
|
||||
}
|
||||
|
||||
if b.SummarySource == "" {
|
||||
b.SummarySource = "ai"
|
||||
}
|
||||
|
||||
_, err = s.db.Exec(
|
||||
`INSERT INTO daily_briefings (id, user_id, date, weather, news, reminders, summary, status, generated_at, delivered_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
`INSERT INTO daily_briefings (id, user_id, date, weather, news, reminders, summary, summary_source, status, generated_at, delivered_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||
ON CONFLICT (user_id, date) DO UPDATE SET
|
||||
weather = EXCLUDED.weather,
|
||||
news = EXCLUDED.news,
|
||||
reminders = EXCLUDED.reminders,
|
||||
summary = EXCLUDED.summary,
|
||||
summary_source = EXCLUDED.summary_source,
|
||||
status = EXCLUDED.status,
|
||||
generated_at = EXCLUDED.generated_at,
|
||||
delivered_at = EXCLUDED.delivered_at`,
|
||||
b.ID, b.UserID, b.Date, string(weatherJSON), string(newsJSON), string(remindersJSON),
|
||||
b.Summary, b.Status, b.GeneratedAt, b.DeliveredAt,
|
||||
b.Summary, b.SummarySource, b.Status, b.GeneratedAt, b.DeliveredAt,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("upsert 简报失败: %w", err)
|
||||
@@ -131,7 +139,7 @@ func (s *BriefingStore) CreateOrUpdateBriefing(b *Briefing) error {
|
||||
// GetBriefingByDate 获取指定日期简报
|
||||
func (s *BriefingStore) GetBriefingByDate(userID, date string) (*Briefing, error) {
|
||||
row := s.db.QueryRow(
|
||||
`SELECT id, user_id, date::TEXT, weather, news, reminders, summary, status, generated_at, delivered_at, created_at
|
||||
`SELECT id, user_id, date::TEXT, weather, news, reminders, summary, COALESCE(summary_source, 'ai'), status, generated_at, delivered_at, created_at
|
||||
FROM daily_briefings WHERE user_id = $1 AND date = $2::DATE`,
|
||||
userID, date,
|
||||
)
|
||||
@@ -153,7 +161,7 @@ func (s *BriefingStore) GetLatestBriefings(userID string, limit int) ([]Briefing
|
||||
}
|
||||
|
||||
rows, err := s.db.Query(
|
||||
`SELECT id, user_id, date::TEXT, weather, news, reminders, summary, status, generated_at, delivered_at, created_at
|
||||
`SELECT id, user_id, date::TEXT, weather, news, reminders, summary, COALESCE(summary_source, 'ai'), status, generated_at, delivered_at, created_at
|
||||
FROM daily_briefings WHERE user_id = $1
|
||||
ORDER BY date DESC LIMIT $2`,
|
||||
userID, limit,
|
||||
@@ -166,21 +174,22 @@ func (s *BriefingStore) GetLatestBriefings(userID string, limit int) ([]Briefing
|
||||
var briefings []Briefing
|
||||
for rows.Next() {
|
||||
var (
|
||||
id, uid, date, summary, status string
|
||||
id, uid, date, summary, summarySource, status string
|
||||
weatherRaw, newsRaw, remindersRaw []byte
|
||||
generatedAt, deliveredAt, createdAt sql.NullTime
|
||||
)
|
||||
if err := rows.Scan(&id, &uid, &date, &weatherRaw, &newsRaw, &remindersRaw,
|
||||
&summary, &status, &generatedAt, &deliveredAt, &createdAt); err != nil {
|
||||
&summary, &summarySource, &status, &generatedAt, &deliveredAt, &createdAt); err != nil {
|
||||
return nil, fmt.Errorf("扫描简报行失败: %w", err)
|
||||
}
|
||||
|
||||
b := Briefing{
|
||||
ID: id,
|
||||
UserID: uid,
|
||||
Date: date,
|
||||
Summary: summary,
|
||||
Status: status,
|
||||
ID: id,
|
||||
UserID: uid,
|
||||
Date: date,
|
||||
Summary: summary,
|
||||
SummarySource: summarySource,
|
||||
Status: status,
|
||||
}
|
||||
|
||||
if weatherRaw != nil {
|
||||
@@ -270,22 +279,23 @@ func (s *BriefingStore) GetAllUsers() ([]string, error) {
|
||||
// scanBriefing 扫描单行简报
|
||||
func (s *BriefingStore) scanBriefing(row *sql.Row) (*Briefing, error) {
|
||||
var (
|
||||
id, uid, date, summary, status string
|
||||
id, uid, date, summary, summarySource, status string
|
||||
weatherRaw, newsRaw, remindersRaw []byte
|
||||
generatedAt, deliveredAt, createdAt sql.NullTime
|
||||
)
|
||||
|
||||
if err := row.Scan(&id, &uid, &date, &weatherRaw, &newsRaw, &remindersRaw,
|
||||
&summary, &status, &generatedAt, &deliveredAt, &createdAt); err != nil {
|
||||
&summary, &summarySource, &status, &generatedAt, &deliveredAt, &createdAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b := &Briefing{
|
||||
ID: id,
|
||||
UserID: uid,
|
||||
Date: date,
|
||||
Summary: summary,
|
||||
Status: status,
|
||||
ID: id,
|
||||
UserID: uid,
|
||||
Date: date,
|
||||
Summary: summary,
|
||||
SummarySource: summarySource,
|
||||
Status: status,
|
||||
}
|
||||
|
||||
if weatherRaw != nil {
|
||||
|
||||
Reference in New Issue
Block a user