最后我们再来实现下收藏功能:

聊天的时候可以收藏某条消息,然后在收藏列表查看。
消息分为文字、图片、文件三类,收藏也是这三类。
我们先加一个收藏表:

model Favorite {
id Int @id @default(autoincrement())
chatHistoryId Int
uerId Int
createTime DateTime @default(now())
updateTime DateTime @updatedAt
}
每个收藏只要记录对应的 chatHistoryId 和 userId 即可。
这里我们同样没有用外键。
用 migrate dev 生成表:
npx prisma migrate dev --name favorite

sql 没啥问题:

然后创建一个模块:
nest g resource favorite

在 FavorateController 添加三个路由:
import { Controller, Get, Query } from "@nestjs/common";
import { FavoriteService } from "./favorite.service";
import { RequireLogin, UserInfo } from "src/custom.decorator";
@Controller("favorite")
@RequireLogin()
export class FavoriteController {
constructor(private readonly favoriteService: FavoriteService) {}
@Get("list")
async list(@UserInfo("userId") userId: number) {
return this.favoriteService.list(userId);
}
@Get("add")
async add(
@UserInfo("userId") userId: number,
@Query("chatHistoryId") chatHistoryId: number
) {
return this.favoriteService.add(userId, chatHistoryId);
}
@Get("del")
async del(@Query("id") id: number) {
return this.favoriteService.del(id);
}
}
list、add、del 这三个路由都需要登录,在 Controller 上加上 @RequireLogin 装饰器。
然后取 request.user 里的 userId 传入 handler。
分别在 service 实现这三个方法:
import { Inject, Injectable } from "@nestjs/common";
import { PrismaService } from "src/prisma/prisma.service";
@Injectable()
export class FavoriteService {
@Inject(PrismaService)
private prismaService: PrismaService;
async list(userId: number) {
const favorites = await this.prismaService.favorite.findMany({
where: {
uerId: userId,
},
});
const res = [];
for (let i = 0; i < favorites.length; i++) {
const chatHistory = await this.prismaService.chatHistory.findUnique(
{
where: {
id: favorites[i].chatHistoryId,
},
}
);
res.push({
...favorites[i],
chatHistory,
});
}
return res;
}
async add(userId: number, chatHistoryId: number) {
return this.prismaService.favorite.create({
data: {
uerId: userId,
chatHistoryId,
},
});
}
async del(id: number) {
return this.prismaService.favorite.deleteMany({
where: {
id,
},
});
}
}
list 方法把关联的 chatHistory 查出来。
测试下:
添加两条收藏:


查看下:

删除一条:

查看下:

这样,接口就都完成了。
我们再写下前端页面:
先在 interfaces 添加几个接口:
export async function queryFavoriteList() {
return axiosInstance.get(`/favorite/list`);
}
export async function favoriteAdd(chatHistoryId: number) {
return axiosInstance.get(`/favorite/add`, {
params: {
chatHistoryId,
},
});
}
export async function favoriteDel(id: number) {
return axiosInstance.get(`/favorite/del`, {
params: {
id,
},
});
}
写下页面 src/pages/Collection/index.tsx
import { Table, message } from "antd";
import { useEffect, useState } from "react";
import { ColumnsType } from "antd/es/table";
import { queryFavoriteList } from "../../interfaces";
interface Favorite {
id: number;
chatHistory: {
id: number;
content: string;
type: number;
createTime: Date;
};
}
export function Collection() {
const [favoriteList, setFavoriteList] = useState<Array<Favorite>>([]);
const columns: ColumnsType<Favorite> = [
{
title: "ID",
dataIndex: "id",
},
{
title: "内容",
render: (_, record) => (
<div>
{record.chatHistory.type === 0 ? (
record.chatHistory.content
) : record.chatHistory.type === 1 ? (
<img
src={record.chatHistory.content}
style={{ maxHeight: 200 }}
/>
) : (
<a href={record.chatHistory.content} download>
{record.chatHistory.content}
</a>
)}
</div>
),
},
{
title: "发表时间",
render: (_, record) => (
<div>
{new Date(record.chatHistory.createTime).toLocaleString()}
</div>
),
},
{
title: "操作",
render: (_, record) => (
<div>
<a href="" onClick={() => {}}>
删除
</a>
</div>
),
},
];
const query = async () => {
try {
const res = await queryFavoriteList();
if (res.status === 201 || res.status === 200) {
setFavoriteList(
res.data.map((item: Favorite) => {
return {
...item,
key: item.id,
};
})
);
}
} catch (e: any) {
message.error(e.response?.data?.message || "系统繁忙,请稍后再试");
}
};
useEffect(() => {
query();
}, []);
return (
<div id="friendship-container">
<div className="favorite-table">
<Table
columns={columns}
dataSource={favoriteList}
style={{ width: "1000px" }}
/>
</div>
</div>
);
}
就是请求列表接口,用 table 展示。

我们实现下收藏功能。
简化下交互,双击聊天记录触发收藏:

onDoubleClick={() => {
addToFavorite(item.id)
}}
async function addToFavorite(chatHistoryId: number) {
try {
const res = await favoriteAdd(chatHistoryId);
if (res.status === 201 || res.status === 200) {
message.success("收藏成功");
}
} catch (e: any) {
message.error(e.response?.data?.message || "系统繁忙,请稍后再试");
}
}
我们收藏几条消息:

提示收藏成功,之后在收藏页面就可以看到了:

然后再做下删除:

<Popconfirm
title="删除收藏"
description="确认删除吗?"
onConfirm={() => delFavorite(record.id)}
okText="Yes"
cancelText="No">
<a href="#">删除</a>
</Popconfirm>
async function delFavorite(id: number) {
try {
const res = await favoriteDel(id);
if (res.status === 201 || res.status === 200) {
message.success("删除成功");
query();
}
} catch (e: any) {
message.error(e.response?.data?.message || "系统繁忙,请稍后再试");
}
}
测试下:

没啥问题。
这样,收藏功能就完成了。
总结
这节我们实现了收藏功能。
首先创建了收藏表,关联 user 和 chatHistory。
然后创建了 list、add、del 三个接口。
之后在前端通过 table 展示 list 接口的数据,然后双击聊天记录的时候调用 add 添加收藏,点击删除的时候调用 del 删除收藏
这样就实现了收藏功能。
