refactor!: 重新实现最近对话和收藏对话的逻辑 (破坏性变更)
This commit is contained in:
@@ -318,11 +318,11 @@ export default class UserApi extends BaseApi {
|
|||||||
const user = User.findById(token.author) as User
|
const user = User.findById(token.author) as User
|
||||||
const recentChats = user.getRecentChats()
|
const recentChats = user.getRecentChats()
|
||||||
const recentChatsList: any[] = []
|
const recentChatsList: any[] = []
|
||||||
for (const [chatId, content] of recentChats) {
|
for (const {chat_id, content} of recentChats) {
|
||||||
const chat = Chat.findById(chatId)
|
const chat = Chat.findById(chat_id)
|
||||||
recentChatsList.push({
|
recentChatsList.push({
|
||||||
content,
|
content,
|
||||||
id: chatId,
|
id: chat_id,
|
||||||
title: chat?.getTitle(user) || "未知",
|
title: chat?.getTitle(user) || "未知",
|
||||||
avatar_file_hash: chat?.getAvatarFileHash(user) ? chat?.getAvatarFileHash(user) : undefined
|
avatar_file_hash: chat?.getAvatarFileHash(user) ? chat?.getAvatarFileHash(user) : undefined
|
||||||
})
|
})
|
||||||
|
|||||||
10
server/data/RecentChatBean.ts
Normal file
10
server/data/RecentChatBean.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export default class RecentChatBean {
|
||||||
|
declare count: number
|
||||||
|
/** 最近对话所关联的用户 */
|
||||||
|
declare user_id: string
|
||||||
|
declare chat_id: string
|
||||||
|
declare content: string
|
||||||
|
declare updated_time: number
|
||||||
|
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
@@ -11,11 +11,10 @@ import UserBean from './UserBean.ts'
|
|||||||
import FileManager from './FileManager.ts'
|
import FileManager from './FileManager.ts'
|
||||||
import { SQLInputValue } from "node:sqlite"
|
import { SQLInputValue } from "node:sqlite"
|
||||||
import ChatPrivate from "./ChatPrivate.ts"
|
import ChatPrivate from "./ChatPrivate.ts"
|
||||||
import Chat from "./Chat.ts"
|
|
||||||
import ChatBean from "./ChatBean.ts"
|
|
||||||
import MapJson from "../MapJson.ts"
|
|
||||||
import DataWrongError from '../api/DataWrongError.ts'
|
import DataWrongError from '../api/DataWrongError.ts'
|
||||||
import UserChatLinker from "./UserChatLinker.ts";
|
import UserChatLinker from "./UserChatLinker.ts"
|
||||||
|
import UserFavouriteChatLinker from "./UserFavouriteChatLinker.ts"
|
||||||
|
import UserRecentChatLinker from "./UserRecentChatLinker.ts"
|
||||||
|
|
||||||
type UserBeanKey = keyof UserBean
|
type UserBeanKey = keyof UserBean
|
||||||
|
|
||||||
@@ -38,8 +37,6 @@ export default class User {
|
|||||||
/* 用户名 */ username TEXT,
|
/* 用户名 */ username TEXT,
|
||||||
/* 昵称 */ nickname TEXT NOT NULL,
|
/* 昵称 */ nickname TEXT NOT NULL,
|
||||||
/* 头像, 可选 */ avatar_file_hash TEXT,
|
/* 头像, 可选 */ avatar_file_hash TEXT,
|
||||||
/* 对话列表 */ favourite_chats TEXT NOT NULL,
|
|
||||||
/* 最近对话 */ recent_chats TEXT NOT NULL,
|
|
||||||
/* 设置 */ settings TEXT NOT NULL
|
/* 设置 */ settings TEXT NOT NULL
|
||||||
);
|
);
|
||||||
`)
|
`)
|
||||||
@@ -69,18 +66,14 @@ export default class User {
|
|||||||
username,
|
username,
|
||||||
nickname,
|
nickname,
|
||||||
avatar_file_hash,
|
avatar_file_hash,
|
||||||
favourite_chats,
|
|
||||||
recent_chats,
|
|
||||||
settings
|
settings
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);`).run(
|
) VALUES (?, ?, ?, ?, ?, ?, ?);`).run(
|
||||||
crypto.randomUUID(),
|
crypto.randomUUID(),
|
||||||
password,
|
password,
|
||||||
Date.now(),
|
Date.now(),
|
||||||
userName,
|
userName,
|
||||||
nickName,
|
nickName,
|
||||||
null,
|
null,
|
||||||
'[]',
|
|
||||||
JSON.stringify(new Map(), MapJson.replacer),
|
|
||||||
"{}"
|
"{}"
|
||||||
).lastInsertRowid
|
).lastInsertRowid
|
||||||
)[0]
|
)[0]
|
||||||
@@ -136,37 +129,25 @@ export default class User {
|
|||||||
this.setAttr("username", userName)
|
this.setAttr("username", userName)
|
||||||
}
|
}
|
||||||
updateRecentChat(chatId: string, content: string) {
|
updateRecentChat(chatId: string, content: string) {
|
||||||
const map = JSON.parse(this.bean.recent_chats, MapJson.reviver) as Map<string, string>
|
UserRecentChatLinker.updateOrAddRecentChat(this.bean.id, chatId, content)
|
||||||
map.delete(chatId)
|
|
||||||
map.set(chatId, content)
|
|
||||||
this.setAttr("recent_chats", JSON.stringify(map, MapJson.replacer))
|
|
||||||
}
|
}
|
||||||
getRecentChats(): Map<string, string> {
|
getRecentChats() {
|
||||||
try {
|
return UserRecentChatLinker.getUserRecentChatBeans(this.bean.id)
|
||||||
return JSON.parse(this.bean.recent_chats, MapJson.reviver)
|
}
|
||||||
} catch (e) {
|
|
||||||
console.log(chalk.yellow(`警告: 最近对话列表解析失敗: ${(e as Error).message}`))
|
getFavouriteChats() {
|
||||||
return new Map()
|
return UserFavouriteChatLinker.getUserFavouriteChats(this.bean.id)
|
||||||
}
|
}
|
||||||
|
addFavouriteChats(chatIds: string[]) {
|
||||||
|
chatIds.forEach((v) => UserFavouriteChatLinker.linkUserAndChat(this.bean.id, v))
|
||||||
|
}
|
||||||
|
removeFavouriteChats(chatIds: string[]) {
|
||||||
|
chatIds.forEach((v) => UserFavouriteChatLinker.unlinkUserAndChat(this.bean.id, v))
|
||||||
}
|
}
|
||||||
addFavouriteChat(chatId: string) {
|
addFavouriteChat(chatId: string) {
|
||||||
const ls = this.getFavouriteChats()
|
this.addFavouriteChats([chatId])
|
||||||
if (ls.indexOf(chatId) != -1 || ChatPrivate.getChatIdByUsersId(this.bean.id, this.bean.id) == chatId) return
|
|
||||||
ls.push(chatId)
|
|
||||||
this.setAttr("favourite_chats", JSON.stringify(ls))
|
|
||||||
}
|
|
||||||
removeFavouriteChats(contacts: string[]) {
|
|
||||||
const ls = this.getFavouriteChats().filter((v) => !contacts.includes(v))
|
|
||||||
this.setAttr("favourite_chats", JSON.stringify(ls))
|
|
||||||
}
|
|
||||||
getFavouriteChats() {
|
|
||||||
try {
|
|
||||||
return [...(JSON.parse(this.bean.favourite_chats) as string[]), ChatPrivate.findOrCreateForPrivate(this, this).bean.id]
|
|
||||||
} catch (e) {
|
|
||||||
console.log(chalk.yellow(`警告: 收藏对话解析失败: ${(e as Error).message}`))
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllChatsList() {
|
getAllChatsList() {
|
||||||
return UserChatLinker.getUserChats(this.bean.id)
|
return UserChatLinker.getUserChats(this.bean.id)
|
||||||
}
|
}
|
||||||
|
|||||||
57
server/data/UserFavouriteChatLinker.ts
Normal file
57
server/data/UserFavouriteChatLinker.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { DatabaseSync } from "node:sqlite"
|
||||||
|
import path from 'node:path'
|
||||||
|
|
||||||
|
import config from "../config.ts"
|
||||||
|
import { SQLInputValue } from "node:sqlite"
|
||||||
|
|
||||||
|
export default class UserFavouriteChatLinker {
|
||||||
|
static database: DatabaseSync = this.init()
|
||||||
|
|
||||||
|
private static init(): DatabaseSync {
|
||||||
|
const db: DatabaseSync = new DatabaseSync(path.join(config.data_path, 'UserFavouriteChatLinker.db'))
|
||||||
|
db.exec(`
|
||||||
|
CREATE TABLE IF NOT EXISTS UserFavouriteChatLinker (
|
||||||
|
/* 序号 */ count INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
/* 用戶 ID */ user_id TEXT NOT NULL,
|
||||||
|
/* Chat ID */ chat_id TEXT NOT NULL
|
||||||
|
);
|
||||||
|
`)
|
||||||
|
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_id ON UserFavouriteChatLinker(user_id);`)
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 若用户和对话未关联, 则进行关联
|
||||||
|
*/
|
||||||
|
static linkUserAndChat(userId: string, chatId: string) {
|
||||||
|
if (!this.checkUserIsLinkedToChat(userId, chatId))
|
||||||
|
this.database.prepare(`INSERT INTO UserFavouriteChatLinker (
|
||||||
|
user_id,
|
||||||
|
chat_id
|
||||||
|
) VALUES (?, ?);`).run(
|
||||||
|
userId,
|
||||||
|
chatId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 解除用户和对话的关联
|
||||||
|
*/
|
||||||
|
static unlinkUserAndChat(userId: string, chatId: string) {
|
||||||
|
this.database.prepare(`DELETE FROM UserFavouriteChatLinker 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 getUserFavouriteChats(userId: string) {
|
||||||
|
return this.findAllByCondition('user_id = ?', userId).map((v) => v.chat_id) as string[]
|
||||||
|
}
|
||||||
|
protected static findAllByCondition(condition: string, ...args: SQLInputValue[]) {
|
||||||
|
return this.database.prepare(`SELECT * FROM UserFavouriteChatLinker WHERE ${condition}`).all(...args)
|
||||||
|
}
|
||||||
|
}
|
||||||
71
server/data/UserRecentChatLinker.ts
Normal file
71
server/data/UserRecentChatLinker.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { DatabaseSync } from "node:sqlite"
|
||||||
|
import path from 'node:path'
|
||||||
|
|
||||||
|
import RecentChatBean from './RecentChatBean.ts'
|
||||||
|
import config from "../config.ts"
|
||||||
|
import { SQLInputValue } from "node:sqlite"
|
||||||
|
|
||||||
|
export default class UserRecentChatLinker {
|
||||||
|
static database: DatabaseSync = this.init()
|
||||||
|
|
||||||
|
private static init(): DatabaseSync {
|
||||||
|
const db: DatabaseSync = new DatabaseSync(path.join(config.data_path, 'UserRecentChatLinker.db'))
|
||||||
|
db.exec(`
|
||||||
|
CREATE TABLE IF NOT EXISTS UserRecentChatLinker (
|
||||||
|
/* 序号 */ count INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
/* 用戶 ID */ user_id TEXT NOT NULL,
|
||||||
|
/* Chat ID */ chat_id TEXT NOT NULL,
|
||||||
|
/* Last Message Content */ content TEXT NOT NULL,
|
||||||
|
/* Last Update Time */ updated_time INT8 NOT NULL
|
||||||
|
);
|
||||||
|
`)
|
||||||
|
db.exec(`CREATE INDEX IF NOT EXISTS idx_user_id ON UserRecentChatLinker(user_id);`)
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 若用户和对话未关联, 则进行关联
|
||||||
|
*/
|
||||||
|
static updateOrAddRecentChat(userId: string, chatId: string, content: string) {
|
||||||
|
if (!this.checkUserIsLinkedToChat(userId, chatId))
|
||||||
|
this.database.prepare(`INSERT INTO UserRecentChatLinker (
|
||||||
|
user_id,
|
||||||
|
chat_id,
|
||||||
|
content,
|
||||||
|
updated_time
|
||||||
|
) VALUES (?, ?, ?, ?);`).run(
|
||||||
|
userId,
|
||||||
|
chatId,
|
||||||
|
content,
|
||||||
|
Date.now()
|
||||||
|
)
|
||||||
|
else
|
||||||
|
this.database.prepare('UPDATE UserRecentChatLinker SET content = ?, updated_time = ? WHERE count = ?').run(
|
||||||
|
content,
|
||||||
|
Date.now(),
|
||||||
|
/* 既然已经 bind 了, 那么就不需要判断了? */
|
||||||
|
this.findAllByCondition('user_id = ? AND chat_id = ?', userId, chatId)[0].count
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 解除用户和对话的关联
|
||||||
|
*/
|
||||||
|
static removeRecentChat(userId: string, chatId: string) {
|
||||||
|
this.database.prepare(`DELETE FROM UserRecentChatLinker 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 getUserRecentChatBeans(userId: string) {
|
||||||
|
return this.findAllByCondition('user_id = ? ORDER BY updated_time DESC', userId) as unknown as RecentChatBean[]
|
||||||
|
}
|
||||||
|
protected static findAllByCondition(condition: string, ...args: SQLInputValue[]) {
|
||||||
|
return this.database.prepare(`SELECT * FROM UserRecentChatLinker WHERE ${condition}`).all(...args)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user