From 541d7842ce83f8da308499425897149b8aacbbdf Mon Sep 17 00:00:00 2001 From: Sun_sun <469361609@qq.com> Date: Tue, 30 Sep 2025 16:41:10 +0800 Subject: [PATCH] =?UTF-8?q?2025-09-30=20=E6=9C=AA=E8=AF=BB=E5=8F=98?= =?UTF-8?q?=E5=B7=B2=E8=AF=BB=20=E6=9F=A5=E8=AF=A2=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controller/index.ts | 3 +- src/controller/message.controller.ts | 23 +++++++++ src/controller/programGroup.controller.ts | 1 - src/entity/dto/read.dto.ts | 4 ++ src/entity/index.ts | 4 +- src/entity/programGroup.entity.ts | 46 +++++++++-------- src/service/chat.gateway.ts | 44 ++++++++--------- src/service/index.ts | 5 +- src/service/message.service.ts | 23 +++++++++ ...ice.service.ts => programGroup.service.ts} | 49 +++++++++++++++++-- src/service/redis.service.ts | 4 ++ 11 files changed, 153 insertions(+), 53 deletions(-) create mode 100644 src/controller/message.controller.ts create mode 100644 src/entity/dto/read.dto.ts create mode 100644 src/service/message.service.ts rename src/service/{programGroupService.service.ts => programGroup.service.ts} (72%) diff --git a/src/controller/index.ts b/src/controller/index.ts index 8fc531d..5a1f177 100644 --- a/src/controller/index.ts +++ b/src/controller/index.ts @@ -1,2 +1,3 @@ export * from './programGroup.controller'; -export * from './file.controller'; \ No newline at end of file +export * from './file.controller'; +export * from './message.controller' \ No newline at end of file diff --git a/src/controller/message.controller.ts b/src/controller/message.controller.ts new file mode 100644 index 0000000..7452702 --- /dev/null +++ b/src/controller/message.controller.ts @@ -0,0 +1,23 @@ +import {Body, Controller, Get, Post, Query} from "@nestjs/common"; +import {MessageService} from "../service"; +import {ProgramGroupMessageEntity, ReadDto} from "../entity"; + +@Controller('message') +export class MessageController { + + constructor(private messageService: MessageService) { + } + + // 获取未读消息 + @Get("unread") + async getUnread(@Query() readDto: ReadDto) { + return await this.messageService.getUnread(readDto) + } + + // 将未读消息置为已读 + @Post('read') + async read(@Body() readDto: ReadDto) { + return await this.messageService.read(readDto) + } + +} \ No newline at end of file diff --git a/src/controller/programGroup.controller.ts b/src/controller/programGroup.controller.ts index 9613900..3788b92 100644 --- a/src/controller/programGroup.controller.ts +++ b/src/controller/programGroup.controller.ts @@ -26,5 +26,4 @@ export class ProgramGroupController { async getOne(@Param() programGroup: ProgramGroupEntity) { return await this.programGroupService.getOne(programGroup); } - } \ No newline at end of file diff --git a/src/entity/dto/read.dto.ts b/src/entity/dto/read.dto.ts new file mode 100644 index 0000000..94dcb5c --- /dev/null +++ b/src/entity/dto/read.dto.ts @@ -0,0 +1,4 @@ +export class ReadDto { + groupId: number; + userId: number; +} \ No newline at end of file diff --git a/src/entity/index.ts b/src/entity/index.ts index 9534884..811ee7d 100644 --- a/src/entity/index.ts +++ b/src/entity/index.ts @@ -1,4 +1,6 @@ export * from './program.entity'; export * from './users.entity'; export * from './programGroup.entity'; -export * from './programGroupMessage.entity'; \ No newline at end of file +export * from './programGroupMessage.entity'; + +export * from './dto/read.dto' \ No newline at end of file diff --git a/src/entity/programGroup.entity.ts b/src/entity/programGroup.entity.ts index 9ef2a1f..4f062a2 100644 --- a/src/entity/programGroup.entity.ts +++ b/src/entity/programGroup.entity.ts @@ -1,32 +1,36 @@ -import { Column, Entity, PrimaryGeneratedColumn } from "typeorm"; +import {Column, Entity, PrimaryGeneratedColumn} from "typeorm"; import {ProgramEntity} from "./program.entity"; -@Entity("program_group", { schema: "test" }) +@Entity("program_group", {schema: "test"}) export class ProgramGroupEntity { - @PrimaryGeneratedColumn({ type: "int", name: "group_id", comment: "群组ID" }) - groupId: number; + @PrimaryGeneratedColumn({type: "int", name: "group_id", comment: "群组ID"}) + groupId: number; - @Column("int", { name: "program_id", comment: "项目ID" }) - programId: number; + @Column("int", {name: "program_id", comment: "项目ID"}) + programId: number; - @Column("int", { name: "charge_id", comment: "负责人ID" }) - chargeId: number; + @Column("int", {name: "charge_id", comment: "负责人ID"}) + chargeId: number; - @Column("int", { name: "customer_id", comment: "客服ID" }) - customerId: number; + @Column("int", {name: "customer_id", comment: "客服ID"}) + customerId: number; - @Column("int", { name: "user_id", comment: "用户ID" }) - userId: number; + @Column("int", {name: "user_id", comment: "用户ID"}) + userId: number; - @Column("datetime", { - name: "create_time", - comment: "创建时间", - default: () => "CURRENT_TIMESTAMP", - }) - createTime: Date; + @Column("datetime", { + name: "create_time", + comment: "创建时间", + default: () => "CURRENT_TIMESTAMP", + }) + createTime: Date; - program: ProgramEntity; + // 虚拟字段 + program: ProgramEntity; - page: number = 1; - size: number = 10; + page: number = 1; + size: number = 10; + + userUnread: any + chargeUnread: any } diff --git a/src/service/chat.gateway.ts b/src/service/chat.gateway.ts index f2672a7..a09be20 100644 --- a/src/service/chat.gateway.ts +++ b/src/service/chat.gateway.ts @@ -58,28 +58,6 @@ export class ChatGateway implements OnGatewayInit { */ @SubscribeMessage('clientMsg') async clientMessage(@MessageBody() data: ProgramGroupMessageEntity): Promise { - - // 不在线用户 - let userIds = await this.activeUser(data.groupId) - - if (userIds.length != 0) { - // 保存未读消息 redis - console.log(userIds) - for (let i = 0; i < userIds.length; i++) { - let item = userIds[i] - // 先取再存 - let redisData = await this.redisService.getValue(`${data.groupId}_${item}`) - if (redisData == null) { - this.redisService.setValue(`${data.groupId}_${item}`, JSON.stringify([data])) - } else { - let arr: any = [] - arr = JSON.parse(redisData) - arr.push(data) - this.redisService.setValue(`${data.groupId}_${item}`, JSON.stringify(arr)) - } - } - } - // 保存记录 let saveMessage = await this.messageRepository.save(data) @@ -89,6 +67,28 @@ export class ChatGateway implements OnGatewayInit { } }) + // 不在线用户 + let userIds = await this.activeUser(data.groupId) + + // 存储未读消息 + if (userIds.length != 0) { + // 保存未读消息 redis + console.log(userIds) + for (let i = 0; i < userIds.length; i++) { + let item = userIds[i] + // 先取再存 + let redisData = await this.redisService.getValue(`${data.groupId}_${item}`) + if (redisData == null) { + this.redisService.setValue(`${data.groupId}_${item}`, JSON.stringify([saveMessage])) + } else { + let arr: any = [] + arr = JSON.parse(redisData) + arr.push(saveMessage) + this.redisService.setValue(`${data.groupId}_${item}`, JSON.stringify(arr)) + } + } + } + this.server.to(data.groupId.toString()).emit('serverMsg', saveMessage) } diff --git a/src/service/index.ts b/src/service/index.ts index ac333a7..51262d5 100644 --- a/src/service/index.ts +++ b/src/service/index.ts @@ -1,4 +1,5 @@ -export * from './programGroupService.service'; +export * from './programGroup.service'; export * from './chat.gateway'; export * from './file.service'; -export * from './redis.service' \ No newline at end of file +export * from './redis.service' +export * from './message.service' \ No newline at end of file diff --git a/src/service/message.service.ts b/src/service/message.service.ts new file mode 100644 index 0000000..ba62aa8 --- /dev/null +++ b/src/service/message.service.ts @@ -0,0 +1,23 @@ +import {Injectable} from "@nestjs/common"; +import {ReadDto} from "../entity"; +import {RedisService} from "./redis.service"; +import {ResultData} from "../const/result"; + +@Injectable() +export class MessageService { + constructor(private readonly redisService: RedisService) { + } + + // 获取未读消息 + async getUnread(readDto: ReadDto) { + let data = await this.redisService.getValue(`${readDto.groupId}_${readDto.userId}`) + data = data == null ? [] : JSON.parse(data) + return ResultData.success(data) + } + + // 未读 -> 已读 + async read(readDto: ReadDto) { + this.redisService.delValue(`${readDto.groupId}_${readDto.userId}`) + return ResultData.success() + } +} \ No newline at end of file diff --git a/src/service/programGroupService.service.ts b/src/service/programGroup.service.ts similarity index 72% rename from src/service/programGroupService.service.ts rename to src/service/programGroup.service.ts index 6c88d3b..17fc5b1 100644 --- a/src/service/programGroupService.service.ts +++ b/src/service/programGroup.service.ts @@ -1,9 +1,10 @@ import {Injectable} from '@nestjs/common'; import {InjectRepository} from "@nestjs/typeorm"; -import {ProgramEntity, ProgramGroupEntity, UsersEntity} from "../entity"; +import {ProgramEntity, ProgramGroupEntity, ProgramGroupMessageEntity, UsersEntity} from "../entity"; import {Repository} from "typeorm"; import {ResultData} from "../const/result"; import {isEmptyString} from "../utils/common"; +import {RedisService} from "./redis.service"; @Injectable() export class ProgramGroupService { @@ -12,6 +13,7 @@ export class ProgramGroupService { @InjectRepository(ProgramGroupEntity) private readonly programGroupRepository: Repository, @InjectRepository(ProgramEntity) private readonly programRepository: Repository, @InjectRepository(UsersEntity) private readonly usersRepository: Repository, + private readonly redisService: RedisService, ) { } @@ -105,21 +107,45 @@ export class ProgramGroupService { "group.user_id = user.id" ) - if (programGroup.customerId != null){ + // 查询最新消息 + // queryBuilder.leftJoinAndMapOne( + // "group.latest_message", + // ProgramGroupMessageEntity, + // "message", + // ` + // message.group_id = group.group_id and + // group.group_id = ( + // select group_id from program_group_message + // where program_group_message.group_id = group.group_id + // order by create_time desc + // limit 1 + // ) + // `) + queryBuilder.addSelect(subQuery => { + return subQuery + .select("max(message.create_time)") + .from(ProgramGroupMessageEntity, "message") + .where("message.group_id = group.group_id"); + }, "latest_message_time") + + if (programGroup.customerId != null) { queryBuilder.andWhere("group.customer_id = :customerId", {customerId: programGroup.customerId}) } - if (programGroup.userId != null){ + if (programGroup.userId != null) { queryBuilder.orWhere("group.user_id = :userId", {userId: programGroup.userId}) queryBuilder.orWhere("group.charge_id = :charge_id", {charge_id: programGroup.userId}) } queryBuilder - .orderBy('program.id', 'DESC') + .orderBy("latest_message_time", 'DESC') .take(programGroup.size || 10) .skip(offset || 0); - const [items, total] = await queryBuilder.getManyAndCount(); + let [items, total] = await queryBuilder.getManyAndCount(); + + // 将未读数据(redis)塞入数据(list)中 + items = await this.searchUnRead(items) let data = { list: items, @@ -147,4 +173,17 @@ export class ProgramGroupService { return ResultData.fail(500, err); } } + + // 处理未读消息 + // 返回用户和项目负责人的未读消息 + async searchUnRead(list: ProgramGroupEntity[]) { + for (let i = 0; i < list.length; i++) { + let item = list[i]; + let userUnreadValue = await this.redisService.getValue(`${item.groupId}_${item.userId}`) + let chargeUnreadValue = await this.redisService.getValue(`${item.groupId}_${item.chargeId}`) + item.userUnread = userUnreadValue == null ? [] : JSON.parse(userUnreadValue) + item.chargeUnread = chargeUnreadValue == null ? [] : JSON.parse(chargeUnreadValue) + } + return list + } } diff --git a/src/service/redis.service.ts b/src/service/redis.service.ts index 6a1c861..0082b0f 100644 --- a/src/service/redis.service.ts +++ b/src/service/redis.service.ts @@ -22,4 +22,8 @@ export class RedisService { getValue(key: string) { return this.redisClient.get(key); } + + delValue(key: string) { + return this.redisClient.del(key); + } }