Files
Cyrene_v2/internal/runtime/history/session_read.go
T
AskaEth 09fba77f87
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
docs(comments): 注释中文化 · internal/runtime/history(15 文件,纯替换)
- 只翻注释/文档字符串:剥离注释后代码行逐字节不变(机械校验只剩 4 条行尾注释的翻译,代码前缀全等)
- 交叉引用(design/16 §4.6、D4/B3、0010 迁移文件名)逐字保留
- 保留未译技术词:DDL/rowid/LIKE/LEFT JOIN/DTO/rune/WS/actor 及全部标识符与表名
- 顺带副作用:store.go、session_persona_test.go 由注释文字改变而变得 gofmt 干净(未运行 gofmt -w)
2026-10-04 12:28:48 +08:00

49 lines
2.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package history
import (
"context"
"fmt"
"time"
)
// 本文件负责每会话的读游标(design/16 §4.2,Android 协同批次 D4/B3)。本层
// 只有一个主人账号、没有按设备的身份,因此游标是会话作用域的:在任一端已读
// 即为该账号的每一端把该会话标为已读。游标存放在 chat_sessions.read_at,与
// chat_messages.created_at 同为毫秒单位,因此未读角标就是一个朴素的
// `created_at > read_at` 计数(见 listSessionsQuery)。
// MarkSessionRead 把会话的读游标推进到 before,并把结果游标作为 Unix 毫秒
// 时间戳返回。before 为零意味着「现在」—— 调用方「把我看到的全部标为已读」
// 的场景,无需自己读时钟。
//
// 游标只向前移动:该 UPDATE 使用 SQLite 的双参数标量 MAX,因此一个过期或
// 乱序的请求(两端竞争、请求重试)绝不能让消息重新变为未读。负的 before
// 因此不会挪动已存的值,而不是把它向后移。
//
// 未知的会话 id 返回 ErrSessionNotFound,与 RenameSession/SetPinned 一致
// —— 隔离(群聊/陌生人)会话没有 chat_sessions 行,在此不可寻址。
func (s *Store) MarkSessionRead(ctx context.Context, sessionID string, before time.Time) (int64, error) {
ms := before.UnixMilli()
if before.IsZero() {
ms = time.Now().UnixMilli()
}
res, err := s.db.ExecContext(ctx,
`UPDATE chat_sessions SET read_at = MAX(read_at, ?) WHERE id = ?`, ms, sessionID)
if err != nil {
return 0, fmt.Errorf("history: mark session read %s: %w", sessionID, err)
}
n, err := res.RowsAffected()
if err != nil {
return 0, fmt.Errorf("history: mark session read %s rows: %w", sessionID, err)
}
if n == 0 {
return 0, fmt.Errorf("history: mark session read %s: %w", sessionID, ErrSessionNotFound)
}
var readAt int64
if err := s.db.QueryRowContext(ctx,
`SELECT read_at FROM chat_sessions WHERE id = ?`, sessionID).Scan(&readAt); err != nil {
return 0, fmt.Errorf("history: read session cursor %s: %w", sessionID, err)
}
return readAt, nil
}