feat: 群聊提醒 — 提醒关联平台/频道/适配器,到期发回原群聊
- reminder_create 新增 platform/channel_type/channel_id/adapter_name 参数 - Gateway Reminder 模型 + DB 迁移 + CRUD 全部支持新字段 - 提醒到期时:群聊提醒 → platform-bridge 发回原群聊 - 非群聊提醒 → 走 Web 端推送 - Thinker 新增 PushPlatformMessage 方法 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -35,7 +35,11 @@ type CreateReminderRequest struct {
|
||||
RemindAt string `json:"remind_at" binding:"required"` // ISO 8601 格式
|
||||
RepeatType string `json:"repeat_type"` // none, daily, weekly, monthly
|
||||
SessionID string `json:"session_id"`
|
||||
UserID string `json:"user_id,omitempty"` // 内部调用时直接传 userID
|
||||
UserID string `json:"user_id,omitempty"`
|
||||
Platform string `json:"platform,omitempty"`
|
||||
ChannelType string `json:"channel_type,omitempty"`
|
||||
ChannelID string `json:"channel_id,omitempty"`
|
||||
AdapterName string `json:"adapter_name,omitempty"` // 内部调用时直接传 userID
|
||||
}
|
||||
|
||||
// UpdateReminderRequest 更新提醒请求体
|
||||
@@ -129,6 +133,10 @@ func (h *ReminderHandler) Create(c *gin.Context) {
|
||||
Status: "pending",
|
||||
RepeatType: repeatType,
|
||||
SessionID: req.SessionID,
|
||||
Platform: req.Platform,
|
||||
ChannelType: req.ChannelType,
|
||||
ChannelID: req.ChannelID,
|
||||
AdapterName: req.AdapterName,
|
||||
Notified: false,
|
||||
}
|
||||
|
||||
@@ -356,10 +364,14 @@ func calculateNextRemindAt(current time.Time, repeatType string) time.Time {
|
||||
|
||||
func triggerLLMReminder(aiCoreURL, internalToken string, r store.Reminder) {
|
||||
body := map[string]string{
|
||||
"title": r.Title,
|
||||
"description": r.Description,
|
||||
"user_id": r.UserID,
|
||||
"session_id": r.SessionID,
|
||||
"title": r.Title,
|
||||
"description": r.Description,
|
||||
"user_id": r.UserID,
|
||||
"session_id": r.SessionID,
|
||||
"platform": r.Platform,
|
||||
"channel_type": r.ChannelType,
|
||||
"channel_id": r.ChannelID,
|
||||
"adapter_name": r.AdapterName,
|
||||
}
|
||||
reqBody, _ := json.Marshal(body)
|
||||
req, err := http.NewRequest("POST", aiCoreURL+"/api/v1/internal/reminder-trigger", bytes.NewReader(reqBody))
|
||||
|
||||
@@ -19,6 +19,10 @@ type Reminder struct {
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
||||
RepeatType string `json:"repeat_type"` // none, daily, weekly, monthly
|
||||
SessionID string `json:"session_id"`
|
||||
Platform string `json:"platform,omitempty"` // 来源平台
|
||||
ChannelType string `json:"channel_type,omitempty"` // 频道类型
|
||||
ChannelID string `json:"channel_id,omitempty"` // 频道ID
|
||||
AdapterName string `json:"adapter_name,omitempty"` // 适配器名
|
||||
Notified bool `json:"notified"`
|
||||
}
|
||||
|
||||
@@ -59,6 +63,10 @@ func (s *ReminderStore) migrate() error {
|
||||
`CREATE INDEX IF NOT EXISTS idx_reminders_remind_at ON reminders(remind_at)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_reminders_status ON reminders(status)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_reminders_due ON reminders(remind_at, status, notified)`,
|
||||
`ALTER TABLE reminders ADD COLUMN IF NOT EXISTS platform VARCHAR(32) DEFAULT ''`,
|
||||
`ALTER TABLE reminders ADD COLUMN IF NOT EXISTS channel_type VARCHAR(16) DEFAULT ''`,
|
||||
`ALTER TABLE reminders ADD COLUMN IF NOT EXISTS channel_id VARCHAR(64) DEFAULT ''`,
|
||||
`ALTER TABLE reminders ADD COLUMN IF NOT EXISTS adapter_name VARCHAR(64) DEFAULT ''`,
|
||||
}
|
||||
|
||||
for _, q := range queries {
|
||||
@@ -75,7 +83,7 @@ func (s *ReminderStore) CreateReminder(r *Reminder) error {
|
||||
r.CreatedAt = time.Now()
|
||||
}
|
||||
_, err := s.db.Exec(
|
||||
`INSERT INTO reminders (id, user_id, title, description, remind_at, status, created_at, repeat_type, session_id)
|
||||
`INSERT INTO reminders (id, user_id, title, description, remind_at, status, created_at, repeat_type, session_id, platform, channel_type, channel_id, adapter_name)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
|
||||
r.ID, r.UserID, r.Title, r.Description, r.RemindAt, r.Status, r.CreatedAt, r.RepeatType, r.SessionID,
|
||||
)
|
||||
@@ -99,14 +107,14 @@ func (s *ReminderStore) GetRemindersByUser(userID, status string, limit, offset
|
||||
|
||||
if status != "" {
|
||||
rows, err = s.db.Query(
|
||||
`SELECT id, user_id, title, description, remind_at, status, created_at, completed_at, repeat_type, session_id, notified
|
||||
`SELECT id, user_id, title, description, remind_at, status, created_at, completed_at, repeat_type, session_id, notified, platform, channel_type, channel_id, adapter_name
|
||||
FROM reminders WHERE user_id = $1 AND status = $2
|
||||
ORDER BY remind_at ASC LIMIT $3 OFFSET $4`,
|
||||
userID, status, limit, offset,
|
||||
)
|
||||
} else {
|
||||
rows, err = s.db.Query(
|
||||
`SELECT id, user_id, title, description, remind_at, status, created_at, completed_at, repeat_type, session_id, notified
|
||||
`SELECT id, user_id, title, description, remind_at, status, created_at, completed_at, repeat_type, session_id, notified, platform, channel_type, channel_id, adapter_name
|
||||
FROM reminders WHERE user_id = $1
|
||||
ORDER BY remind_at ASC LIMIT $2 OFFSET $3`,
|
||||
userID, limit, offset,
|
||||
@@ -121,7 +129,7 @@ func (s *ReminderStore) GetRemindersByUser(userID, status string, limit, offset
|
||||
for rows.Next() {
|
||||
var r Reminder
|
||||
if err := rows.Scan(&r.ID, &r.UserID, &r.Title, &r.Description, &r.RemindAt,
|
||||
&r.Status, &r.CreatedAt, &r.CompletedAt, &r.RepeatType, &r.SessionID, &r.Notified); err != nil {
|
||||
&r.Status, &r.CreatedAt, &r.CompletedAt, &r.RepeatType, &r.SessionID, &r.Notified, &r.Platform, &r.ChannelType, &r.ChannelID, &r.AdapterName); err != nil {
|
||||
return nil, fmt.Errorf("扫描提醒行失败: %w", err)
|
||||
}
|
||||
reminders = append(reminders, r)
|
||||
@@ -136,7 +144,7 @@ func (s *ReminderStore) GetRemindersByUser(userID, status string, limit, offset
|
||||
// GetDueReminders 获取所有到期且未通知的提醒
|
||||
func (s *ReminderStore) GetDueReminders() ([]Reminder, error) {
|
||||
rows, err := s.db.Query(
|
||||
`SELECT id, user_id, title, description, remind_at, status, created_at, completed_at, repeat_type, session_id, notified
|
||||
`SELECT id, user_id, title, description, remind_at, status, created_at, completed_at, repeat_type, session_id, notified, platform, channel_type, channel_id, adapter_name
|
||||
FROM reminders
|
||||
WHERE remind_at <= NOW() AND status = 'pending' AND notified = FALSE
|
||||
ORDER BY remind_at ASC`,
|
||||
@@ -150,7 +158,7 @@ func (s *ReminderStore) GetDueReminders() ([]Reminder, error) {
|
||||
for rows.Next() {
|
||||
var r Reminder
|
||||
if err := rows.Scan(&r.ID, &r.UserID, &r.Title, &r.Description, &r.RemindAt,
|
||||
&r.Status, &r.CreatedAt, &r.CompletedAt, &r.RepeatType, &r.SessionID, &r.Notified); err != nil {
|
||||
&r.Status, &r.CreatedAt, &r.CompletedAt, &r.RepeatType, &r.SessionID, &r.Notified, &r.Platform, &r.ChannelType, &r.ChannelID, &r.AdapterName); err != nil {
|
||||
return nil, fmt.Errorf("扫描到期提醒行失败: %w", err)
|
||||
}
|
||||
reminders = append(reminders, r)
|
||||
|
||||
Reference in New Issue
Block a user