116 lines
3.8 KiB
TypeScript
116 lines
3.8 KiB
TypeScript
/** 会话与消息的读写(A3.7)。只碰平台库,不涉及业务数据(ADR-0008)。 */
|
||
import type { ChatMessage, SessionSummary } from '@aps/contracts';
|
||
|
||
import type { Database } from './db.js';
|
||
|
||
export interface StoredSession extends SessionSummary {
|
||
owner: { userId: string; tenantId: string; displayName: string; clientKind: 'desktop' | 'browser' };
|
||
}
|
||
|
||
interface SessionRow {
|
||
session_id: string;
|
||
project_id: string | null;
|
||
scope: string;
|
||
status: string;
|
||
title: string;
|
||
owner_user_id: string;
|
||
owner_tenant_id: string;
|
||
owner_display_name: string;
|
||
owner_client_kind: string;
|
||
created_at: number;
|
||
}
|
||
|
||
interface MessageRow {
|
||
message_id: string;
|
||
session_id: string;
|
||
role: string;
|
||
text: string;
|
||
ts: number;
|
||
}
|
||
|
||
const toSession = (row: SessionRow): StoredSession => ({
|
||
sessionId: row.session_id,
|
||
projectId: row.project_id,
|
||
scope: row.scope as SessionSummary['scope'],
|
||
status: row.status as SessionSummary['status'],
|
||
title: row.title,
|
||
createdAt: row.created_at,
|
||
owner: {
|
||
userId: row.owner_user_id,
|
||
tenantId: row.owner_tenant_id,
|
||
displayName: row.owner_display_name,
|
||
clientKind: row.owner_client_kind as 'desktop' | 'browser',
|
||
},
|
||
});
|
||
|
||
const toMessage = (row: MessageRow): ChatMessage => ({
|
||
messageId: row.message_id,
|
||
role: row.role as ChatMessage['role'],
|
||
ts: row.ts,
|
||
text: row.text,
|
||
});
|
||
|
||
export class SessionRepository {
|
||
constructor(private readonly db: Database) {}
|
||
|
||
insertSession(session: StoredSession): void {
|
||
this.db
|
||
.prepare(
|
||
'insert into sessions (session_id, project_id, scope, status, title, owner_user_id, owner_tenant_id, owner_display_name, owner_client_kind, created_at) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
||
)
|
||
.run(
|
||
session.sessionId,
|
||
session.projectId,
|
||
session.scope,
|
||
session.status,
|
||
session.title,
|
||
session.owner.userId,
|
||
session.owner.tenantId,
|
||
session.owner.displayName,
|
||
session.owner.clientKind,
|
||
session.createdAt,
|
||
);
|
||
}
|
||
|
||
getSession(sessionId: string): StoredSession | undefined {
|
||
const row = this.db.prepare('select * from sessions where session_id = ?').get(sessionId) as unknown as SessionRow | undefined;
|
||
return row ? toSession(row) : undefined;
|
||
}
|
||
|
||
listSessions(projectId?: string): StoredSession[] {
|
||
const rows = (
|
||
projectId === undefined
|
||
? this.db.prepare('select * from sessions order by created_at desc').all()
|
||
: this.db.prepare('select * from sessions where project_id = ? order by created_at desc').all(projectId)
|
||
) as unknown as SessionRow[];
|
||
return rows.map(toSession);
|
||
}
|
||
|
||
appendMessage(sessionId: string, message: ChatMessage): void {
|
||
this.db
|
||
.prepare('insert into messages (message_id, session_id, role, text, ts) values (?, ?, ?, ?, ?)')
|
||
.run(message.messageId, sessionId, message.role, message.text, message.ts);
|
||
}
|
||
|
||
listMessages(sessionId: string): ChatMessage[] {
|
||
const rows = this.db
|
||
.prepare('select * from messages where session_id = ? order by ts asc, rowid asc')
|
||
.all(sessionId) as unknown as MessageRow[];
|
||
return rows.map(toMessage);
|
||
}
|
||
|
||
rememberIdempotencyKey(key: string, sessionId: string, messageId: string, runId: string): void {
|
||
this.db
|
||
.prepare(
|
||
'insert or ignore into idempotency_keys (idempotency_key, session_id, message_id, run_id, created_at) values (?, ?, ?, ?, ?)',
|
||
)
|
||
.run(key, sessionId, messageId, runId, Date.now());
|
||
}
|
||
|
||
findByIdempotencyKey(key: string): { sessionId: string; messageId: string; runId: string } | undefined {
|
||
const row = this.db.prepare('select session_id, message_id, run_id from idempotency_keys where idempotency_key = ?').get(key) as
|
||
| { session_id: string; message_id: string; run_id: string }
|
||
| undefined;
|
||
return row ? { sessionId: row.session_id, messageId: row.message_id, runId: row.run_id } : undefined;
|
||
}
|
||
} |