refactor: 重構 對話 成員的儲存邏輯

* 使用關聯資料庫, 鏈接 user_id 和 chat_id
This commit is contained in:
CrescentLeaf
2025-09-23 09:20:30 +08:00
parent 0ef2859291
commit 5d5b04ba05
3 changed files with 58 additions and 15 deletions

View File

@@ -8,6 +8,7 @@ import { SQLInputValue } from "node:sqlite"
import chalk from "chalk" import chalk from "chalk"
import User from "./User.ts" import User from "./User.ts"
import ChatType from "./ChatType.ts" import ChatType from "./ChatType.ts"
import UserChatLinker from "./UserChatLinker.ts"
/** /**
* Chat.ts - Wrapper and manager * Chat.ts - Wrapper and manager
@@ -55,14 +56,12 @@ export default class Chat {
id, id,
title, title,
avatar, avatar,
members_list,
settings settings
) VALUES (?, ?, ?, ?, ?, ?);`).run( ) VALUES (?, ?, ?, ?, ?);`).run(
type, type,
chatId, chatId,
null, null,
null, null,
"[]",
"{}" "{}"
).lastInsertRowid ).lastInsertRowid
)[0] )[0]
@@ -80,19 +79,13 @@ export default class Chat {
} }
getMembersList() { getMembersList() {
return JSON.parse(this.bean.members_list) as string[] return UserChatLinker.getChatMembers(this.bean.id)
} }
addMember(userId: string) { addMembers(userIds: string[]) {
const ls = this.getMembersList() userIds.forEach((v) => UserChatLinker.linkUserAndChat(v, this.bean.id))
ls.push(userId)
this.setMembers(ls)
} }
setMembers(members: string[]) { removeMembers(userIds: string[]) {
this.setAttr("members_list", JSON.stringify(members)) userIds.forEach((v) => UserChatLinker.unlinkUserAndChat(v, this.bean.id))
}
removeMembers(members: string[]) {
const ls = this.getMembersList().filter((v) => !members.includes(v))
this.setAttr("members_list", JSON.stringify(ls))
} }
getAnotherUserForPrivate(userMySelf: User) { getAnotherUserForPrivate(userMySelf: User) {
const user_a_id = this.getMembersList()[0] const user_a_id = this.getMembersList()[0]

View File

@@ -13,7 +13,7 @@ export default class ChatPrivate extends Chat {
static createForPrivate(userA: User, userB: User) { static createForPrivate(userA: User, userB: User) {
const chat = this.create(this.getChatIdByUsersId(userA.bean.id, userB.bean.id), 'private') const chat = this.create(this.getChatIdByUsersId(userA.bean.id, userB.bean.id), 'private')
chat.setMembers([ chat.addMembers([
userA.bean.id, userA.bean.id,
userB.bean.id userB.bean.id
]) ])

View File

@@ -0,0 +1,50 @@
import { DatabaseSync } from "node:sqlite"
import path from 'node:path'
import config from "../config.ts"
import { SQLInputValue } from "node:sqlite";
export default class UserChatLinker {
static database: DatabaseSync = this.init()
private static init(): DatabaseSync {
const db: DatabaseSync = new DatabaseSync(path.join(config.data_path, 'UserChatLinker.db'))
db.exec(`
CREATE TABLE IF NOT EXISTS UserChatLinker (
/* 序号 */ count INTEGER PRIMARY KEY AUTOINCREMENT,
/* 用戶 ID */ user_id TEXT NOT NULL,
/* Chat ID */ chat_id TEXT NOT NULL
);
`)
return db
}
/**
* 對用戶和對話建立關聯
* 自動檢測是否已關聯, 保證不會重複
*/
static linkUserAndChat(userId: string, chatId: string) {
if (!this.checkUserIsLinkedToChat(userId, chatId))
this.database.prepare(`INSERT INTO UserChatLinker (
user_id,
chat_id
) VALUES (?, ?);`).run(
userId,
chatId
)
}
static unlinkUserAndChat(userId: string, chatId: string) {
this.database.prepare(`DELETE FROM UserChatLinker WHERE user_id = ? AND chat_id = ?`).run(userId, chatId)
}
static checkUserIsLinkedToChat(userId: string, chatId: string) {
return this.findAllByCondition('user_id = ? AND chat_id = ?', userId, chatId).length != 0
}
static getUserChats(userId: string) {
return this.findAllByCondition('user_id = ?', userId).map((v) => v.chat_id) as string[]
}
static getChatMembers(chatId: string) {
return this.findAllByCondition('chat_id = ?', chatId).map((v) => v.user_id) as string[]
}
protected static findAllByCondition(condition: string, ...args: SQLInputValue[]) {
return this.database.prepare(`SELECT * FROM UserChatLinker WHERE ${condition}`).all(...args)
}
}