2 Commits

12 changed files with 221 additions and 19 deletions

View File

@@ -171,6 +171,12 @@
<artifactId>spring-boot-starter-data-redis</artifactId> <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.7</version>
</dependency>
</dependencies> </dependencies>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>

View File

@@ -0,0 +1,28 @@
package com.bao.dating.common.result;
import lombok.Data;
import java.util.List;
@Data
public class PageResult<T> {
/** 总记录数 */
private Long total;
/** 当前页码 */
private Integer page;
/** 每页大小 */
private Integer size;
/** 总页数 */
private Integer pages;
/** 当前页数据 */
private List<T> records;
public PageResult(Long total, Integer page, Integer size, List<T> records) {
this.total = total;
this.page = page;
this.size = size;
this.records = records;
// 计算总页数
this.pages = (int) Math.ceil((double) total / size);
}
}

View File

@@ -4,9 +4,7 @@ package com.bao.dating.config;
import com.bao.dating.interceptor.TokenInterceptor; import com.bao.dating.interceptor.TokenInterceptor;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/** /**
@@ -34,14 +32,4 @@ public class WebConfig implements WebMvcConfigurer {
"/user/login" "/user/login"
); );
} }
/**
* 配置静态资源映射
* @param registry 静态资源注册器
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/static/");
}
} }

View File

@@ -1,7 +1,7 @@
package com.bao.dating.config; package com.bao.dating.config;
import com.bao.dating.handler.ChatWebSocketHandler; import com.bao.dating.controller.websocket.ChatWebSocketHandler;
import com.bao.dating.interceptor.WsAuthInterceptor; import com.bao.dating.interceptor.WsAuthInterceptor;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;

View File

@@ -0,0 +1,42 @@
package com.bao.dating.controller;
import com.bao.dating.common.Result;
import com.bao.dating.common.ResultCode;
import com.bao.dating.common.result.PageResult;
import com.bao.dating.context.UserContext;
import com.bao.dating.pojo.dto.ChatHistoryQueryDTO;
import com.bao.dating.pojo.vo.ChatRecordsVO;
import com.bao.dating.service.ChatService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 聊天控制器
* @author lenovo
*/
@Slf4j
@RestController
@RequestMapping("/chat")
public class ChatController {
@Autowired
private ChatService chatService;
/**
* 获取聊天记录
* @param targetUserId 目标用户ID
* @param queryDTO 查询参数
* @return 聊天记录列表
*/
@GetMapping("/history/{targetUserId}")
public Result<PageResult<ChatRecordsVO>> getChatHistory(
@PathVariable Long targetUserId,
ChatHistoryQueryDTO queryDTO){
Long currentUserId = UserContext.getUserId();
PageResult<ChatRecordsVO> history = chatService.getChatHistory(currentUserId, targetUserId, queryDTO);
return Result.success(ResultCode.SUCCESS, "获取聊天记录成功", history);
}
}

View File

@@ -1,4 +1,4 @@
package com.bao.dating.handler; package com.bao.dating.controller.websocket;
import com.bao.dating.message.WsMessage; import com.bao.dating.message.WsMessage;
import com.bao.dating.pojo.dto.ChatRecordSendDTO; import com.bao.dating.pojo.dto.ChatRecordSendDTO;

View File

@@ -42,7 +42,6 @@ public class WsAuthInterceptor implements HandshakeInterceptor {
// 从URL参数中获取token // 从URL参数中获取token
String token = servletRequest.getParameter("token"); String token = servletRequest.getParameter("token");
log.info("从URL参数获取到的token: {}", token != null ? "存在" : "不存在");
if (StringUtils.isBlank(token)) { if (StringUtils.isBlank(token)) {
log.error("WebSocket握手失败令牌丢失"); log.error("WebSocket握手失败令牌丢失");
@@ -66,7 +65,6 @@ public class WsAuthInterceptor implements HandshakeInterceptor {
// 验证token并获取用户ID // 验证token并获取用户ID
String userIdStr = JwtUtil.getSubjectFromToken(token); String userIdStr = JwtUtil.getSubjectFromToken(token);
log.info("从token解析出的用户ID: {}", userIdStr);
if (StringUtils.isBlank(userIdStr) || !userIdStr.matches("\\d+")) { if (StringUtils.isBlank(userIdStr) || !userIdStr.matches("\\d+")) {
log.error("无效的用户ID格式: {}", userIdStr); log.error("无效的用户ID格式: {}", userIdStr);

View File

@@ -1,8 +1,13 @@
package com.bao.dating.mapper; package com.bao.dating.mapper;
import com.bao.dating.pojo.dto.ChatHistoryQueryDTO;
import com.bao.dating.pojo.entity.ChatRecords; import com.bao.dating.pojo.entity.ChatRecords;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
@Mapper @Mapper
public interface ChatRecordsMapper { public interface ChatRecordsMapper {
@@ -10,4 +15,16 @@ public interface ChatRecordsMapper {
* 插入聊天记录 * 插入聊天记录
*/ */
int insert(ChatRecords chatRecords); int insert(ChatRecords chatRecords);
/**
* 根据用户ID和接收方用户ID查询聊天记录
*/
List<ChatRecords> selectByUsersAndConditions(
@Param("currentUserId") Long currentUserId,
@Param("targetUserId") Long targetUserId,
@Param("messageContent") String messageContent,
@Param("startTime") LocalDateTime startTime,
@Param("endTime") LocalDateTime endTime,
@Param("messageType") Integer messageType
);
} }

View File

@@ -0,0 +1,23 @@
package com.bao.dating.pojo.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate;
/**
* 聊天记录查询参数
* @author KilLze
*/
@Data
public class ChatHistoryQueryDTO {
private Integer page;
private Integer size;
private Long targetUserId;
private String messageContent;
@JsonFormat(pattern = "yyyy-MM-dd")
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate date;
private Integer messageType;
}

View File

@@ -1,8 +1,14 @@
package com.bao.dating.service; package com.bao.dating.service;
import com.bao.dating.common.result.PageResult;
import com.bao.dating.pojo.dto.ChatHistoryQueryDTO;
import com.bao.dating.pojo.dto.ChatRecordSendDTO; import com.bao.dating.pojo.dto.ChatRecordSendDTO;
import com.bao.dating.pojo.vo.ChatRecordsVO; import com.bao.dating.pojo.vo.ChatRecordsVO;
/**
* 聊天服务
* @author lenovo
*/
public interface ChatService { public interface ChatService {
/** /**
* 消息入库,如果会话不存在则创建会话 * 消息入库,如果会话不存在则创建会话
@@ -11,4 +17,13 @@ public interface ChatService {
* @return 聊天记录VO * @return 聊天记录VO
*/ */
ChatRecordsVO createSession(Long senderUserId, ChatRecordSendDTO dto); ChatRecordsVO createSession(Long senderUserId, ChatRecordSendDTO dto);
/**
* 获取聊天记录
* @param currentUserId 发送方用户ID
* @param targetUserId 接收方用户ID
* @param queryDTO 查询参数
* @return 聊天记录VO列表
*/
PageResult<ChatRecordsVO> getChatHistory(Long currentUserId, Long targetUserId, ChatHistoryQueryDTO queryDTO);
} }

View File

@@ -1,7 +1,9 @@
package com.bao.dating.service.impl; package com.bao.dating.service.impl;
import com.bao.dating.common.result.PageResult;
import com.bao.dating.mapper.ChatRecordsMapper; import com.bao.dating.mapper.ChatRecordsMapper;
import com.bao.dating.mapper.ChatSessionsMapper; import com.bao.dating.mapper.ChatSessionsMapper;
import com.bao.dating.pojo.dto.ChatHistoryQueryDTO;
import com.bao.dating.pojo.dto.ChatRecordSendDTO; import com.bao.dating.pojo.dto.ChatRecordSendDTO;
import com.bao.dating.pojo.dto.UserNicknameDTO; import com.bao.dating.pojo.dto.UserNicknameDTO;
import com.bao.dating.pojo.entity.ChatRecords; import com.bao.dating.pojo.entity.ChatRecords;
@@ -9,6 +11,8 @@ import com.bao.dating.pojo.entity.ChatSessions;
import com.bao.dating.pojo.vo.ChatRecordsVO; import com.bao.dating.pojo.vo.ChatRecordsVO;
import com.bao.dating.service.ChatService; import com.bao.dating.service.ChatService;
import com.bao.dating.service.UserService; import com.bao.dating.service.UserService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -16,8 +20,9 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
import java.util.stream.Collectors;
/** /**
@@ -108,4 +113,58 @@ public class ChatServiceImpl implements ChatService {
return vo; return vo;
} }
/**
* 获取聊天记录
* @param currentUserId 当前用户ID
* @param targetUserId 目标用户ID
* @param queryDTO 查询参数
* @return 聊天记录列表
*/
@Override
public PageResult<ChatRecordsVO> getChatHistory(Long currentUserId, Long targetUserId, ChatHistoryQueryDTO queryDTO) {
Integer page = queryDTO.getPage();
if (page == null || page < 1) {
page = 1;
}
Integer size = queryDTO.getSize();
if (size == null || size < 1 || size > 100) {
size = 50;
}
LocalDateTime startTime = null;
LocalDateTime endTime = null;
if (queryDTO.getDate() != null) {
startTime = queryDTO.getDate().atStartOfDay();
endTime = queryDTO.getDate().atTime(LocalTime.MAX);
}
// 分页
PageHelper.startPage(page, size);
// 查询
List<ChatRecords> recordsList = chatRecordsMapper.selectByUsersAndConditions(
currentUserId,
targetUserId,
queryDTO.getMessageContent(),
startTime,
endTime,
queryDTO.getMessageType()
);
// 使用 PageInfo 封装查询结果
PageInfo<ChatRecords> pageInfo = new PageInfo<>(recordsList);
// 转换为 VO
List<ChatRecordsVO> voList = recordsList.stream()
.map(record -> {
ChatRecordsVO vo = new ChatRecordsVO();
BeanUtils.copyProperties(record, vo);
return vo;
})
.collect(Collectors.toList());
// 使用 PageInfo 的信息创建 PageResult
return new PageResult<>(pageInfo.getTotal(), pageInfo.getPageNum(), pageInfo.getPageSize(), voList);
}
} }

View File

@@ -31,4 +31,30 @@
) )
</insert> </insert>
<!-- 根据两个用户ID及可选条件查询聊天记录 (按发送时间倒序) - PageHelper 会自动处理分页 -->
<select id="selectByUsersAndConditions" resultType="com.bao.dating.pojo.entity.ChatRecords">
SELECT
chat_id, sender_user_id, receiver_user_id, message_content, message_type,
read_status, read_time, send_time, message_status, created_at, updated_at
FROM chat_records
WHERE (
(sender_user_id = #{currentUserId} AND receiver_user_id = #{targetUserId})
OR (sender_user_id = #{targetUserId} AND receiver_user_id = #{currentUserId})
)
AND message_status = 1
<!-- 动态条件 -->
<if test="messageContent != null and messageContent != ''">
AND message_content LIKE CONCAT('%', #{messageContent}, '%')
</if>
<if test="startTime != null">
AND send_time >= #{startTime}
</if>
<if test="endTime != null">
AND send_time &lt;= #{endTime}
</if>
<if test="messageType != null">
AND message_type = #{messageType}
</if>
ORDER BY send_time DESC
</select>
</mapper> </mapper>