diff --git a/server/data/Chat.ts b/server/data/Chat.ts index 4033ed2..e4d13ce 100644 --- a/server/data/Chat.ts +++ b/server/data/Chat.ts @@ -8,6 +8,7 @@ import { SQLInputValue } from "node:sqlite" import chalk from "chalk" import User from "./User.ts" import ChatType from "./ChatType.ts" +import UserChatLinker from "./UserChatLinker.ts" /** * Chat.ts - Wrapper and manager @@ -55,14 +56,12 @@ export default class Chat { id, title, avatar, - members_list, settings - ) VALUES (?, ?, ?, ?, ?, ?);`).run( + ) VALUES (?, ?, ?, ?, ?);`).run( type, chatId, null, null, - "[]", "{}" ).lastInsertRowid )[0] @@ -80,19 +79,13 @@ export default class Chat { } getMembersList() { - return JSON.parse(this.bean.members_list) as string[] + return UserChatLinker.getChatMembers(this.bean.id) } - addMember(userId: string) { - const ls = this.getMembersList() - ls.push(userId) - this.setMembers(ls) + addMembers(userIds: string[]) { + userIds.forEach((v) => UserChatLinker.linkUserAndChat(v, this.bean.id)) } - setMembers(members: string[]) { - this.setAttr("members_list", JSON.stringify(members)) - } - removeMembers(members: string[]) { - const ls = this.getMembersList().filter((v) => !members.includes(v)) - this.setAttr("members_list", JSON.stringify(ls)) + removeMembers(userIds: string[]) { + userIds.forEach((v) => UserChatLinker.unlinkUserAndChat(v, this.bean.id)) } getAnotherUserForPrivate(userMySelf: User) { const user_a_id = this.getMembersList()[0] diff --git a/server/data/ChatPrivate.ts b/server/data/ChatPrivate.ts index 28889e5..09d1777 100644 --- a/server/data/ChatPrivate.ts +++ b/server/data/ChatPrivate.ts @@ -13,7 +13,7 @@ export default class ChatPrivate extends Chat { static createForPrivate(userA: User, userB: User) { const chat = this.create(this.getChatIdByUsersId(userA.bean.id, userB.bean.id), 'private') - chat.setMembers([ + chat.addMembers([ userA.bean.id, userB.bean.id ]) diff --git a/server/data/UserChatLinker.ts b/server/data/UserChatLinker.ts new file mode 100644 index 0000000..0204a23 --- /dev/null +++ b/server/data/UserChatLinker.ts @@ -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) + } +}