修复机器人聊天记录与修改机器人信息
This commit is contained in:
@@ -124,6 +124,81 @@ func (s *store) FindBotForIncomingPKIPacket(toNodeNum int64) (*botNodeRecord, er
|
||||
return bot, nil
|
||||
}
|
||||
|
||||
// botDirectConversation 是 /admin/bot/direct 侧边栏需要的会话摘要。
|
||||
// LastMessageAt / LastText / LastDirection 描述会话最后一条消息,便于按时间排序与预览;
|
||||
// UnreadCount 仅对 inbound 计数(即未读消息数)。
|
||||
type botDirectConversation struct {
|
||||
BotID uint64 `gorm:"column:bot_id"`
|
||||
PeerNodeID string `gorm:"column:peer_node_id"`
|
||||
PeerNodeNum int64 `gorm:"column:peer_node_num"`
|
||||
LastMessageAt time.Time `gorm:"column:last_message_at"`
|
||||
LastText string `gorm:"column:last_text"`
|
||||
LastDirection string `gorm:"column:last_direction"`
|
||||
UnreadCount int64 `gorm:"column:unread_count"`
|
||||
TotalCount int64 `gorm:"column:total_count"`
|
||||
}
|
||||
|
||||
// ListBotDirectConversations 聚合给定 bot 下的所有 (peer) 会话,返回最后一条消息及未读数。
|
||||
// 按最后一条消息时间倒序(最新会话排前面)。limit/offset 走 listOptions。
|
||||
func (s *store) ListBotDirectConversations(botID uint64, opts listOptions) ([]botDirectConversation, error) {
|
||||
if s == nil || s.db == nil {
|
||||
return nil, fmt.Errorf("store is not configured")
|
||||
}
|
||||
if botID == 0 {
|
||||
return nil, fmt.Errorf("bot id is required")
|
||||
}
|
||||
opts = normalizeListOptions(opts)
|
||||
var rows []botDirectConversation
|
||||
// 先把每对会话的最后一条消息 ID 取出来,再把这条消息的元数据 join 回去;
|
||||
// 同时聚合 unread_count(inbound 且 read_at IS NULL)和 total_count。
|
||||
// 这样的两步 join 避免在 GROUP BY 后引用非聚合列(MySQL 严格模式 / SQLite 兼容)。
|
||||
subLast := s.db.Model(&botDirectMessageRecord{}).
|
||||
Select("bot_id, peer_node_id, peer_node_num, MAX(id) AS last_id, COUNT(*) AS total_count, SUM(CASE WHEN direction = ? AND read_at IS NULL THEN 1 ELSE 0 END) AS unread_count", botDirectMessageDirectionInbound).
|
||||
Where("bot_id = ?", botID).
|
||||
Group("bot_id, peer_node_id, peer_node_num")
|
||||
q := s.db.Table("(?) AS agg", subLast).
|
||||
Select("agg.bot_id AS bot_id, agg.peer_node_id AS peer_node_id, agg.peer_node_num AS peer_node_num, m.created_at AS last_message_at, m.text AS last_text, m.direction AS last_direction, agg.unread_count AS unread_count, agg.total_count AS total_count").
|
||||
Joins("JOIN bot_direct_messages m ON m.id = agg.last_id").
|
||||
Order("m.created_at DESC").
|
||||
Order("m.id DESC").
|
||||
Limit(opts.Limit).
|
||||
Offset(opts.Offset)
|
||||
return rows, q.Scan(&rows).Error
|
||||
}
|
||||
|
||||
// MarkBotDirectMessagesRead 把 (bot, peer) 下未读的 inbound 消息全部标记为已读,返回更新行数。
|
||||
func (s *store) MarkBotDirectMessagesRead(botID uint64, peerNodeNum int64) (int64, error) {
|
||||
if s == nil || s.db == nil {
|
||||
return 0, fmt.Errorf("store is not configured")
|
||||
}
|
||||
if botID == 0 || peerNodeNum == 0 {
|
||||
return 0, fmt.Errorf("bot id and peer node num are required")
|
||||
}
|
||||
now := time.Now()
|
||||
result := s.db.Model(&botDirectMessageRecord{}).
|
||||
Where("bot_id = ? AND peer_node_num = ? AND direction = ? AND read_at IS NULL", botID, peerNodeNum, botDirectMessageDirectionInbound).
|
||||
Update("read_at", &now)
|
||||
if result.Error != nil {
|
||||
return 0, result.Error
|
||||
}
|
||||
return result.RowsAffected, nil
|
||||
}
|
||||
|
||||
// CountBotDirectUnread 返回某个 bot 全部未读 inbound 消息总数(用于头部小红点)。
|
||||
func (s *store) CountBotDirectUnread(botID uint64) (int64, error) {
|
||||
if s == nil || s.db == nil {
|
||||
return 0, fmt.Errorf("store is not configured")
|
||||
}
|
||||
if botID == 0 {
|
||||
return 0, fmt.Errorf("bot id is required")
|
||||
}
|
||||
var total int64
|
||||
err := s.db.Model(&botDirectMessageRecord{}).
|
||||
Where("bot_id = ? AND direction = ? AND read_at IS NULL", botID, botDirectMessageDirectionInbound).
|
||||
Count(&total).Error
|
||||
return total, err
|
||||
}
|
||||
|
||||
// isInboundBotDirectMessage 判断 record 是否是“PKI 加密、发往受管 bot”的入向 DM。
|
||||
// 仅在 type=text_message、pki_encrypted=true、packet_to_num 命中受管 bot 时返回 true。
|
||||
// 任何步骤失败都返回 false,让记录回落到 text_message 表(与之前行为兼容)。
|
||||
|
||||
Reference in New Issue
Block a user