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)
This commit is contained in:
1 parent
b0fbb8bc0d
commit
09fba77f87
15 files changed
+430
-516
No files matched your search
@@ -7,34 +7,29 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// This file owns the two derived per-session display fields carried in the session
|
||||
// list (GET /api/v1/sessions): the last-message preview and the unread count. Both are
|
||||
// read-only projections so a client can render a conversation row (with its unread
|
||||
// badge) without one request per session. The preview is truncated for display, and
|
||||
// neither field ever feeds identity or permission decisions — identity comes from
|
||||
// channel actor metadata / JWT (project red line).
|
||||
// 本文件负责会话列表中携带的两个派生展示字段(GET /api/v1/sessions):最后
|
||||
// 消息预览与未读数。两者都是只读投影,使客户端无需逐会话请求就能渲染对话行
|
||||
// (及其未读角标)。预览会被截断以便展示,两个字段都绝不参与身份或权限判定
|
||||
// —— 身份来自渠道 actor 元数据 / JWT(项目红线)。
|
||||
|
||||
// lastMessagePreviewRunes bounds the preview content so a long message cannot bloat the
|
||||
// session-list response. A longer message is whitespace-collapsed and truncated to this
|
||||
// many runes with an ellipsis.
|
||||
// lastMessagePreviewRunes 限制预览内容,使一条长消息不会撑大会话列表响应。
|
||||
// 更长的消息会先折叠空白,再截断到这么多 rune 并加省略号。
|
||||
const lastMessagePreviewRunes = 60
|
||||
|
||||
// MessageSummary is a display-only preview of a session's most recent message.
|
||||
// MessageSummary 是会话最新消息的纯展示预览。
|
||||
type MessageSummary struct {
|
||||
Role string // "user" | "assistant"
|
||||
Content string // whitespace-collapsed, truncated preview
|
||||
CreatedAt int64 // Unix milliseconds
|
||||
Role string // "user" | "assistant"(用户 | 助手)
|
||||
Content string // 已折叠空白、已截断的预览
|
||||
CreatedAt int64 // Unix 毫秒
|
||||
}
|
||||
|
||||
// listSessionsQuery reads every session plus its newest message and its unread count
|
||||
// in one statement. The LEFT JOIN's correlated subquery resolves each session's newest
|
||||
// row via the (session_id, created_at DESC) index (rowid DESC breaks a same-millisecond
|
||||
// tie by insertion order), so a session with no messages still appears with NULL preview
|
||||
// columns. The unread count is a correlated COUNT(*) over the same index
|
||||
// (session_id = s.id AND created_at > s.read_at is an equality + range scan), so no
|
||||
// per-session round trip (N+1) is needed and no extra index is required. Ordering floats
|
||||
// pinned sessions first, then keeps the main session before unpinned subs, then subs
|
||||
// oldest-first.
|
||||
// listSessionsQuery 用一条语句读取每个会话及其最新消息与未读数。LEFT JOIN 的
|
||||
// 关联子查询经由 (session_id, created_at DESC) 索引解析每个会话最新的那一行
|
||||
// (rowid DESC 按插入顺序打破同一毫秒的并列),因此没有消息的会话仍会以预览
|
||||
// 列全为 NULL 的形式出现。未读数是同一个索引上的关联 COUNT(*)
|
||||
// (session_id = s.id AND created_at > s.read_at 是一次等值 + 范围扫描),
|
||||
// 因此无需逐会话往返(N+1),也不需要额外索引。排序把置顶会话浮到最前,
|
||||
// 再把主会话排在未置顶子会话之前,然后子会话最旧优先。
|
||||
const listSessionsQuery = `SELECT s.id, s.type, s.title, s.pinned, s.created_at, s.persona_id,
|
||||
m.role, m.content, m.created_at,
|
||||
(SELECT COUNT(*) FROM chat_messages c
|
||||
@@ -48,11 +43,10 @@ const listSessionsQuery = `SELECT s.id, s.type, s.title, s.pinned, s.created_at,
|
||||
)
|
||||
ORDER BY s.pinned DESC, (s.type = ?) DESC, s.created_at ASC`
|
||||
|
||||
// ListSessions returns pinned sessions first, then the main session, then every sub
|
||||
// session, oldest first, each carrying a LastMessage preview of its newest message (nil
|
||||
// when the session has none) and the UnreadCount derived from the session's read cursor.
|
||||
// Sessions, previews and unread counts are read in a single query, so the query count
|
||||
// does not grow with the number of sessions. The returned slice is non-nil.
|
||||
// ListSessions 按置顶会话在前、其后是主会话、最后是每个子会话(最旧优先)的
|
||||
// 顺序返回,每个会话携带其最新消息的 LastMessage 预览(没有消息时为 nil)与
|
||||
// 由会话读游标推导出的 UnreadCount。会话、预览与未读数在单次查询中读出,
|
||||
// 因此查询数量不随会话数增长。返回切片非 nil。
|
||||
func (s *Store) ListSessions(ctx context.Context) ([]SessionMeta, error) {
|
||||
rows, err := s.db.QueryContext(ctx, listSessionsQuery, SessionMain)
|
||||
if err != nil {
|
||||
@@ -74,10 +68,9 @@ func (s *Store) ListSessions(ctx context.Context) ([]SessionMeta, error) {
|
||||
return sessions, nil
|
||||
}
|
||||
|
||||
// scanSessionMeta reads one joined chat_sessions + last-message + unread-count row. The
|
||||
// three message columns are NULL for a session that has never received a message, so they
|
||||
// scan into nullable holders and only produce a LastMessage when a row is present. The
|
||||
// unread count is a non-NULL COUNT(*), so it scans into a plain int.
|
||||
// scanSessionMeta 读取一行已连接的 chat_sessions + 最新消息 + 未读数。从未收到
|
||||
// 消息的会话那三个消息列为 NULL,因此它们扫描到可空的容器里,只有该行存在时
|
||||
// 才产出 LastMessage。未读数是非 NULL 的 COUNT(*),因此扫描到普通 int。
|
||||
func scanSessionMeta(sc rowScanner) (SessionMeta, error) {
|
||||
var (
|
||||
meta SessionMeta
|
||||
@@ -102,8 +95,8 @@ func scanSessionMeta(sc rowScanner) (SessionMeta, error) {
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
// previewContent collapses whitespace runs (newlines included) to single spaces and
|
||||
// truncates to lastMessagePreviewRunes runes with an ellipsis. It is display-only.
|
||||
// previewContent 把连续空白(含换行)折叠为单个空格,并截断到
|
||||
// lastMessagePreviewRunes 个 rune 加省略号。它只用于展示。
|
||||
func previewContent(content string) string {
|
||||
joined := strings.Join(strings.Fields(content), " ")
|
||||
runes := []rune(joined)
|
||||
|
||||
Reference in new issue
Block a user