2 Commits

Author SHA1 Message Date
KilLze
fb2b7823b8 完成创建会话添加对方昵称 2026-01-05 22:48:53 +08:00
KilLze
951cd7249d 完成基本对话功能、消息入库和创建会话功能 2026-01-05 21:33:03 +08:00
45 changed files with 909 additions and 513 deletions

View File

@@ -4,32 +4,11 @@ import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@MapperScan("com.bao.dating.mapper")
@SpringBootApplication
public class DatingApplication {
public static void main(String[] args) {
SpringApplication.run(DatingApplication.class, args);
// 读取并打印 ciallo.txt 文件内容
printCialloFile();
}
/**
* 读取并打印 ciallo.txt 文件内容
*/
private static void printCialloFile() {
try (InputStream inputStream = DatingApplication.class.getClassLoader().getResourceAsStream("ciallo.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.err.println("读取 ciallo.txt 文件时发生错误: " + e.getMessage());
}
}
}

View File

@@ -75,8 +75,6 @@ public class SmsUtil {
.setTemplateCode(templateCode != null ? templateCode : this.templateCode)
.setTemplateParam(templateParam);
log.error("TemplateParam 实际值 = {}", templateParam);
SendSmsResponse response = client.sendSms(sendSmsRequest);
if ("OK".equals(response.getBody().getCode())) {
@@ -147,7 +145,6 @@ public class SmsUtil {
jsonBuilder.append("}");
return sendSms(phoneNumber, templateCode, jsonBuilder.toString());
}
}

View File

@@ -1,16 +0,0 @@
package com.bao.dating.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliyunOSSConfig {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
}

View File

@@ -14,9 +14,9 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
// 创建RedisTemplate对象
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
RedisTemplate redisTemplate = new RedisTemplate<>();
// 设置redis的连接工厂对象
redisTemplate.setConnectionFactory(redisConnectionFactory);

View File

@@ -4,7 +4,9 @@ package com.bao.dating.config;
import com.bao.dating.interceptor.TokenInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
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.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
@@ -29,9 +31,17 @@ public class WebConfig implements WebMvcConfigurer {
.addPathPatterns("/**")
// 忽略的接口
.excludePathPatterns(
"/user/login",
"/user/sendCode",
"/download/{postId}"
"/user/login"
);
}
/**
* 配置静态资源映射
* @param registry 静态资源注册器
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/static/");
}
}

View File

@@ -0,0 +1,35 @@
package com.bao.dating.config;
import com.bao.dating.handler.ChatWebSocketHandler;
import com.bao.dating.interceptor.WsAuthInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
/**
* WebSocket 配置类
* @author lenovo
*/
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Autowired
private ChatWebSocketHandler chatWebSocketHandler;
@Autowired
private WsAuthInterceptor wsAuthInterceptor;
/**
* 注册 WebSocket 处理器
* @param registry
*/
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(chatWebSocketHandler, "/ws/chat")
.addInterceptors(wsAuthInterceptor)
.setAllowedOrigins("*");
}
}

View File

@@ -12,9 +12,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.util.List;
/**
@@ -90,20 +87,4 @@ public class PostController {
PostEditVO result = postService.updatePost(postId, postRequestDTO);
return Result.success(ResultCode.SUCCESS, "动态更新成功", result);
}
@GetMapping("/download/{postId}")
public void downloadPostImage(@PathVariable Long postId, HttpServletResponse response) throws Exception {
try {
//Service 返回已经加好水印的图片
BufferedImage image = postService.downloadWithWatermark(postId);
//设置响应头,触发浏览器下载
response.setContentType("image/jpeg");
response.setHeader("Content-Disposition", "attachment;filename=post_" + postId + ".jpg");
//输出到浏览器
ImageIO.write(image, "jpg", response.getOutputStream());
response.getOutputStream().flush();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -6,7 +6,6 @@ import com.bao.dating.common.ResultCode;
import com.bao.dating.context.UserContext;
import com.bao.dating.pojo.dto.UserInfoUpdateDTO;
import com.bao.dating.pojo.dto.UserLoginDTO;
import com.bao.dating.pojo.entity.User;
import com.bao.dating.pojo.vo.UserInfoVO;
import com.bao.dating.pojo.vo.UserLoginVO;
import com.bao.dating.service.UserService;
@@ -15,9 +14,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* 用户接口
@@ -68,6 +65,7 @@ public class UserController {
* @param file 头像文件
* @return 上传后的文件URL列表
*/
@Log
@PostMapping(value = "/info/uploadAvatar", consumes = "multipart/form-data")
public Result<String> uploadAvatar(@RequestParam("file") MultipartFile file) {
String fileUrl = userService.uploadAvatar(file);
@@ -79,6 +77,7 @@ public class UserController {
* @param file 背景文件
* @return 上传后的文件URL列表
*/
@Log
@PostMapping(value = "/info/uploadBackground", consumes = "multipart/form-data")
public Result<String> uploadBackground(@RequestParam("file") MultipartFile file) {
String fileUrl = userService.uploadBackground(file);
@@ -90,6 +89,7 @@ public class UserController {
* @param userInfoUpdateDTO 用户信息更新参数
* @return 更新后的用户信息
*/
@Log
@PostMapping("/info/update")
public Result<UserInfoVO> userInfoUpdate(@RequestBody UserInfoUpdateDTO userInfoUpdateDTO) {
Long userId = UserContext.getUserId();
@@ -97,71 +97,4 @@ public class UserController {
UserInfoVO userInfoVO =userService.updateUserInfo(userInfoUpdateDTO);
return Result.success(ResultCode.SUCCESS, "用户信息更新成功", userInfoVO);
}
@PostMapping("/sendCode")
public Result sendCode(@RequestBody Map<String, String> body) {
String phone = body.get("phone");
userService.sendSmsCode(phone);
return Result.success(ResultCode.SUCCESS, "验证码发送成功");
}
@PostMapping("/loginByCode")
public Result loginByCode(@RequestBody Map<String, String> body){
boolean ok = userService.verifyCode(body.get("phone"), body.get("code"));
return ok ? Result.success(ResultCode.SUCCESS, "登录成功") : Result.error(ResultCode.SYSTEM_ERROR, "登录失败");
}
/**
* 登录
* @param body 登录参数
*/
@PostMapping("/loginPhone")
public Result<?> loginPhone(@RequestBody Map<String, String> body) {
String phone = body.get("phone");
String code = body.get("code");
//校验验证码
boolean verify = userService.verifyCode(phone, code);
if (!verify){
return Result.error(ResultCode.SYSTEM_ERROR, "验证码错误或已过期");
}
//登录
UserLoginVO vo = userService.loginByPhone(phone);
return Result.success(ResultCode.SUCCESS, "登录成功", vo);
}
@GetMapping("/nearby")
public Result<List<UserInfoVO>> nearby(@RequestParam(defaultValue = "5") double distance){
// 获取当前线程的用户 ID
Long userId = UserContext.getUserId();
if (userId == null) {
return Result.error(ResultCode.SYSTEM_ERROR, "用户未登录");
}
// 通过 UserID 获取当前用户的经纬度信息
UserInfoVO currentUser = userService.getUserInfo(userId);
if (currentUser == null) {
return Result.error(ResultCode.SYSTEM_ERROR, "用户信息获取失败");
}
// 获取当前用户的经纬度
Double latitude = currentUser.getLatitude();
Double longitude = currentUser.getLongitude();
// 检查经纬度是否为空
if (latitude == null || longitude == null) {
return Result.error(ResultCode.SYSTEM_ERROR, "用户经纬度信息未完善");
}
// 这里可以添加默认值,比如如果 latitude 或 longitude 为 null则设置为某个默认值例如 0
latitude = (latitude != null) ? latitude : 0.0;
longitude = (longitude != null) ? longitude : 0.0;
// 查询附近用户
List<UserInfoVO> nearbyUsers = userService.findNearbyUsers(latitude, longitude, distance);
// 返回成功结果
return Result.success(ResultCode.SUCCESS, "查询成功", nearbyUsers);
}
}

View File

@@ -0,0 +1,107 @@
package com.bao.dating.handler;
import com.bao.dating.message.WsMessage;
import com.bao.dating.pojo.dto.ChatRecordSendDTO;
import com.bao.dating.pojo.vo.ChatRecordsVO;
import com.bao.dating.service.ChatService;
import com.bao.dating.session.WsSessionManager;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
/**
* WebSocket处理器类
* @author lenovo
*/
@Slf4j
@Component
public class ChatWebSocketHandler extends TextWebSocketHandler {
@Autowired
private ObjectMapper objectMapper;
@Autowired
private ChatService chatService;
@Autowired
private WsSessionManager sessionManager;
/**
* 用户建立连接(上线)
* @param session
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) {
Long userId = (Long) session.getAttributes().get("userId");
sessionManager.addSession(userId, session);
log.info("用户 " + userId + " 已上线");
}
/**
* 接收并处理消息
* @param session
* @param message
*/
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception{
// 获取当前用户ID
Long senderUserId = (Long) session.getAttributes().get("userId");
if (senderUserId == null) {
log.error("WebSocket session 中未找到 userId");
return;
}
// 解析消息
WsMessage<ChatRecordSendDTO > wsMessage =
objectMapper.readValue(message.getPayload(),
new TypeReference<WsMessage<ChatRecordSendDTO>>(){});
// 处理私聊消息
if ("chat".equals(wsMessage.getType())) {
handlePrivateChat(senderUserId, wsMessage.getData());
}
}
/**
* 私聊处理
*/
private void handlePrivateChat(Long senderUserId, ChatRecordSendDTO dto) throws Exception {
// 1. 消息入库 + 会话更新
ChatRecordsVO chatRecordsVO = chatService.createSession(senderUserId, dto);
// 2. 推送给接收方
WebSocketSession receiverSession =
sessionManager.getSession(dto.getReceiverUserId());
if (receiverSession != null && receiverSession.isOpen()) {
WsMessage<ChatRecordsVO> pushMsg = new WsMessage<>();
pushMsg.setType("chat");
pushMsg.setData(chatRecordsVO);
receiverSession.sendMessage(
new TextMessage(objectMapper.writeValueAsString(pushMsg))
);
}
}
/**
* 用户断开连接(下线)
* @param session
* @param status
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status){
// 下线处理
Long userId = (Long) session.getAttributes().get("userId");
sessionManager.removeSession(userId);
log.info("用户 " + userId + " 已下线");
}
}

View File

@@ -23,7 +23,7 @@ import org.springframework.web.servlet.HandlerInterceptor;
public class TokenInterceptor implements HandlerInterceptor {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private RedisTemplate redisTemplate;
/**
* 在请求处理之前进行拦截
@@ -68,7 +68,7 @@ public class TokenInterceptor implements HandlerInterceptor {
}
// 解析 token
String userId = JwtUtil.getSubjectFromToken(token);
Long userId = Long.valueOf(JwtUtil.getSubjectFromToken(token));
// 从Redis获取存储的token进行比对
Object redisTokenObj = redisTemplate.opsForValue().get("login:token:" + userId);
@@ -85,7 +85,7 @@ public class TokenInterceptor implements HandlerInterceptor {
log.info("用户: {}", userId);
// 保存 userId 到 ThreadLocal
UserContext.setUserId(Long.valueOf(userId));
UserContext.setUserId(userId);
return true;
} catch (Exception e) {
log.error("Token 校验失败: {}", e.getMessage());

View File

@@ -0,0 +1,113 @@
package com.bao.dating.interceptor;
import com.bao.dating.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* WebSocket 认证拦截器
*/
@Slf4j
@Component
public class WsAuthInterceptor implements HandshakeInterceptor {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 拦截WebSocket连接请求
*/
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
log.info("开始WebSocket握手认证");
// 获取请求参数
if (!(request instanceof ServletServerHttpRequest)) {
log.error("WebSocket握手失败非HTTP请求");
return false;
}
// 获取HttpServletRequest对象
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
// 从URL参数中获取token
String token = servletRequest.getParameter("token");
log.info("从URL参数获取到的token: {}", token != null ? "存在" : "不存在");
if (StringUtils.isBlank(token)) {
log.error("WebSocket握手失败令牌丢失");
return false;
}
try {
// 验证 token 是否有效(包括是否过期)
if (!JwtUtil.validateToken(token)) {
log.error("Token无效或已过期: {}", token);
return false;
}
// 检查 token 是否在黑名单中
String blacklistKey = "jwt:blacklist:" + token;
Object blacklistToken = redisTemplate.opsForValue().get(blacklistKey);
if (blacklistToken != null) {
log.error("Token已在黑名单中: {}", token);
return false;
}
// 验证token并获取用户ID
String userIdStr = JwtUtil.getSubjectFromToken(token);
log.info("从token解析出的用户ID: {}", userIdStr);
if (StringUtils.isBlank(userIdStr) || !userIdStr.matches("\\d+")) {
log.error("无效的用户ID格式: {}", userIdStr);
return false;
}
Long userId = Long.valueOf(userIdStr);
// 从Redis获取存储的token进行比对
String redisTokenKey = "login:token:" + userId;
Object redisTokenObj = redisTemplate.opsForValue().get(redisTokenKey);
String redisToken = redisTokenObj != null ? redisTokenObj.toString() : null;
log.info("Redis中存储的token: {}", redisToken != null ? "存在" : "不存在");
// 验证Redis中的token是否存在且匹配
if (redisToken == null || !redisToken.equals(token)) {
log.error("登录已失效 - Redis中token不存在或不匹配");
return false;
}
log.info("WebSocket认证成功用户ID: {}", userId);
// 将用户ID保存到attributes中
attributes.put("userId", userId);
return true;
}
catch (NumberFormatException e) {
log.error("用户ID格式转换异常: {}", e.getMessage());
return false;
}
catch (Exception e) {
log.error("WebSocket握手失败{}", e.getMessage(), e);
return false;
}
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
if (exception != null) {
log.error("WebSocket握手后出现异常", exception);
} else {
log.info("WebSocket握手完成");
}
}
}

View File

@@ -0,0 +1,13 @@
package com.bao.dating.mapper;
import com.bao.dating.pojo.entity.ChatRecords;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ChatRecordsMapper {
/**
* 插入聊天记录
*/
int insert(ChatRecords chatRecords);
}

View File

@@ -0,0 +1,39 @@
package com.bao.dating.mapper;
import com.bao.dating.pojo.entity.ChatRecords;
import com.bao.dating.pojo.entity.ChatSessions;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface ChatSessionsMapper {
/**
* 如果发送方不存在会话则创建
* @param chatSessions 会话
* @return 影响行数
*/
int insertIfNotExistsForSender(ChatSessions chatSessions);
/**
* 如果接收方不存在会话则创建
* @param chatSessions 会话
* @return 影响行数
*/
int insertIfNotExistsForReceiver(ChatSessions chatSessions);
/**
* 更新发送方的会话信息
* @param chatSessions 会话
* @return 影响行数
*/
int updateSessionForSender(ChatSessions chatSessions);
/**
* 更新接收方的会话信息
* @param chatSessions 会话
* @return 影响行数
*/
int updateSessionForReceiver(ChatSessions chatSessions);
}

View File

@@ -5,18 +5,18 @@ import com.bao.dating.pojo.entity.Comments;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface CommentsMapper {
// 添加评论
@Insert("INSERT INTO dating.comments(content, user_id, post_id, created_at) VALUES(#{content}, #{user_id}, #{post_id}, #{created_at})")
@Insert("INSERT INTO comments(content, user_id, post_id, created_at) VALUES(#{content}, #{user_id}, #{post_id}, #{created_at})")
int addComment(Comments comments);
// 删除评论
@Delete("DELETE FROM dating.comments WHERE user_id = #{user_id}")
@Delete("DELETE FROM comments WHERE user_id = #{user_id}")
int deleteComments(@Param("user_id") Long user_id);
// 根据动态ID查询评论列表
@Select("SELECT * FROM dating.comments WHERE post_id = #{post_id} ORDER BY created_at DESC")
@Select("SELECT * FROM comments WHERE post_id = #{post_id} ORDER BY created_at DESC")
List<Comments> getCommentsByPostId(@Param("post_id") Long post_id);
/**

View File

@@ -5,7 +5,6 @@ import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* 动态Mapper
@@ -96,12 +95,4 @@ public interface PostMapper {
* @return 影响行数
*/
int decreaseFavoriteCount(Long postId);
/**
* 根据动态id查询用户名和媒体信息
*
* @param postId 动态id
* @return 用户名和媒体信息
*/
Map<String, Object> getUsernameByUserId(Long postId);
}

View File

@@ -2,11 +2,7 @@ package com.bao.dating.mapper;
import com.bao.dating.pojo.dto.UserInfoUpdateDTO;
import com.bao.dating.pojo.entity.User;
import com.bao.dating.pojo.vo.UserInfoVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 用户Mapper
@@ -37,15 +33,4 @@ public interface UserMapper {
*/
void updateUserInfoByUserId(UserInfoUpdateDTO userInfoUpdateDTO);
User selectByPhone(@Param("phone") String phone);
/**
* 根据经纬度范围查询用户
* @param minLat 最小纬度
* @param maxLat 最大纬度
* @param minLng 最小经度
* @param maxLng 最大经度
* @return 用户列表
*/
List<UserInfoVO> findByLatLngRange(@Param("minLat") double minLat, @Param("maxLat") double maxLat, @Param("minLng") double minLng, @Param("maxLng") double maxLng);
}

View File

@@ -0,0 +1,17 @@
package com.bao.dating.message;
import lombok.Data;
/**
* WebSocket 消息
* @author KilLze
*/
@Data
public class WsMessage<T> {
/** 消息类型chat / read / system */
private String type;
/** 消息体 */
private T data;
}

View File

@@ -0,0 +1,17 @@
package com.bao.dating.pojo.dto;
import lombok.Data;
/**
* 聊天记录发送数据传输对象
* @author KilLze
*/
@Data
public class ChatRecordSendDTO {
/** 接收者用户ID */
private Long receiverUserId;
/** 消息内容 */
private String messageContent;
/** 消息类型 1-文本消息2-文件消息) */
private Integer messageType;
}

View File

@@ -0,0 +1,32 @@
package com.bao.dating.pojo.dto;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 聊天记录数据传输对象
* @author KilLze
*/
@Data
public class ChatRecordsDTO {
/** 聊天记录ID */
private Long chatId;
/** 发送者用户ID */
private Long senderUserId;
/** 接收者用户ID */
private Long receiverUserId;
/** 消息内容 */
private String messageContent;
/** 消息类型 1-文本消息2-文件消息) */
private Integer messageType;
/** 阅读状态 0-未读1-已读) */
private Integer readStatus;
/** 阅读时间 */
private LocalDateTime readTime;
/** 发送时间 */
private LocalDateTime sendTime;
/** 消息状态 1-正常2-已撤回3-已删除) */
private Integer messageStatus;
}

View File

@@ -0,0 +1,20 @@
package com.bao.dating.pojo.dto;
import lombok.Data;
/**
* 创建会话数据传输对象
* @author KilLze
*/
@Data
public class ChatSessionCreateDTO {
/** 所属用户ID */
private Long userId;
/** 目标用户ID */
private Long targetUserId;
/** 会话名称 */
private String sessionName;
}

View File

@@ -0,0 +1,23 @@
package com.bao.dating.pojo.dto;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 会话更新数据传输对象
* @author KilLze
*/
@Data
public class ChatSessionUpdateDTO {
/** 会话ID */
private Long sessionId;
/** 最后一条消息ID 关联chat_records.chat_id */
private Long lastMessageId;
/** 最后一条消息内容 */
private String lastMessageContent;
/** 最后一条消息时间 */
private LocalDateTime lastMessageTime;
/** 未读消息数量 */
private Integer unreadCount;
}

View File

@@ -0,0 +1,13 @@
package com.bao.dating.pojo.dto;
import lombok.Data;
/**
* 用户昵称数据传输对象
* @author KilLze
*/
@Data
public class UserNicknameDTO {
private Long userId;
private String nickname;
}

View File

@@ -0,0 +1,35 @@
package com.bao.dating.pojo.entity;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 聊天记录表
* @author lenovo
*/
@Data
public class ChatRecords {
/** 聊天记录ID */
private Long chatId;
/** 发送者用户ID */
private Long senderUserId;
/** 接收者用户ID */
private Long receiverUserId;
/** 消息内容 */
private String messageContent;
/** 消息类型 1-文本消息2-文件消息) */
private Integer messageType;
/** 阅读状态 0-未读1-已读) */
private Integer readStatus;
/** 阅读时间 */
private LocalDateTime readTime;
/** 发送时间 */
private LocalDateTime sendTime;
/** 消息状态 1-正常2-已撤回3-已删除) */
private Integer messageStatus;
/** 创建时间 */
private LocalDateTime createdAt;
/** 更新时间 */
private LocalDateTime updatedAt;
}

View File

@@ -0,0 +1,40 @@
package com.bao.dating.pojo.entity;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 会话表
* @author KilLze
*/
@Data
public class ChatSessions {
/** 会话ID */
private Long sessionId;
/** 所属用户ID */
private Long userId;
/** 目标用户ID */
private Long targetUserId;
/** 会话名称 */
private String sessionName;
/** 最后一条消息ID 关联chat_records.chat_id */
private Long lastMessageId;
/** 最后一条消息内容 */
private String lastMessageContent;
/** 最后一条消息时间 */
private LocalDateTime lastMessageTime;
/** 未读消息数量 */
private Integer unreadCount;
/** 会话状态 1-正常2-已隐藏3-已删除) */
private Integer sessionStatus;
/** 置顶状态 0-未置顶1-已置顶) */
private Integer topStatus;
/** 免打扰状态 0-正常提醒1-免打扰) */
private Integer muteStatus;
/** 创建时间 */
private LocalDateTime createdAt;
/** 更新时间 */
private LocalDateTime updatedAt;
}

View File

@@ -43,8 +43,4 @@ public class User implements Serializable {
private String userEmail;
private String userPhone;
private Double latitude; // 纬度
private Double longitude; // 经度
}

View File

@@ -0,0 +1,29 @@
package com.bao.dating.pojo.vo;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 聊天记录返回数据
* @author KilLze
*/
@Data
public class ChatRecordsVO {
/** 聊天记录ID */
private Long chatId;
/** 发送者用户ID */
private Long senderUserId;
/** 接收者用户ID */
private Long receiverUserId;
/** 消息内容 */
private String messageContent;
/** 消息类型 1-文本消息2-文件消息) */
private Integer messageType;
/** 阅读状态 0-未读1-已读) */
private Integer readStatus;
/** 发送时间 */
private LocalDateTime sendTime;
/** 消息状态 1-正常2-已撤回3-已删除) */
private Integer messageStatus;
}

View File

@@ -0,0 +1,19 @@
package com.bao.dating.pojo.vo;
import lombok.Data;
import java.util.List;
/**
* 聊天会话详情
* @author KilLze
*/
@Data
public class ChatSessionDetailVO {
/** 会话信息 */
private ChatSessionsVO session;
/** 聊天记录列表 */
private List<ChatRecordsVO> records;
}

View File

@@ -0,0 +1,34 @@
package com.bao.dating.pojo.vo;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 会话信息
* @author KilLze
*/
@Data
public class ChatSessionsVO {
/** 会话ID */
private Long sessionId;
/** 当前用户ID */
private Long userId;
/** 对方用户ID */
private Long targetUserId;
/** 会话名称 */
private String sessionName;
/** 最后一条消息内容 */
private String lastMessageContent;
/** 最后一条消息时间 */
private LocalDateTime lastMessageTime;
/** 未读消息数量 */
private Integer unreadCount;
/** 会话状态 */
private Integer sessionStatus;
/** 置顶状态 */
private Integer topStatus;
/** 免打扰状态 */
private Integer muteStatus;
}

View File

@@ -24,6 +24,4 @@ public class UserInfoVO implements Serializable {
private String signature;
private LocalDateTime updatedAt;
private LocalDateTime createdAt;
private Double latitude;
private Double longitude;
}

View File

@@ -0,0 +1,14 @@
package com.bao.dating.service;
import com.bao.dating.pojo.dto.ChatRecordSendDTO;
import com.bao.dating.pojo.vo.ChatRecordsVO;
public interface ChatService {
/**
* 消息入库,如果会话不存在则创建会话
* @param senderUserId 发送方用户ID
* @param dto 发送参数
* @return 聊天记录VO
*/
ChatRecordsVO createSession(Long senderUserId, ChatRecordSendDTO dto);
}

View File

@@ -5,7 +5,6 @@ import com.bao.dating.pojo.entity.Post;
import com.bao.dating.pojo.vo.PostEditVO;
import org.springframework.web.multipart.MultipartFile;
import java.awt.image.BufferedImage;
import java.util.List;
/**
@@ -56,11 +55,4 @@ public interface PostService {
* @return 用户id
*/
Long selectUserIdByPostId(Long postId);
/**
* 下载动态图片并添加水印
* @param postId 动态ID
* @return 带水印的图片
*/
BufferedImage downloadWithWatermark(Long postId) throws Exception;
}

View File

@@ -2,13 +2,11 @@ package com.bao.dating.service;
import com.bao.dating.pojo.dto.UserInfoUpdateDTO;
import com.bao.dating.pojo.dto.UserLoginDTO;
import com.bao.dating.pojo.entity.User;
import com.bao.dating.pojo.dto.UserNicknameDTO;
import com.bao.dating.pojo.vo.UserInfoVO;
import com.bao.dating.pojo.vo.UserLoginVO;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
* 用户服务接口
* @author KilLze
@@ -56,18 +54,10 @@ public interface UserService {
*/
UserInfoVO updateUserInfo(UserInfoUpdateDTO userInfoUpdateDTO);
void sendSmsCode(String phone);
boolean verifyCode(String phone, String code);
UserLoginVO loginByPhone(String phone);
/**
* 获取指定经纬度范围内的用户
* @param lat 用户纬度
* @param lng 用户经度
* @param radiusKm 半径 km
* @return 用户列表
* 根据用户ID查询用户昵称
* @param userId 用户ID
* @return 用户
*/
List<UserInfoVO> findNearbyUsers(double lat,double lng,double radiusKm);
UserNicknameDTO getUserNicknameById(Long userId);
}

View File

@@ -0,0 +1,111 @@
package com.bao.dating.service.impl;
import com.bao.dating.mapper.ChatRecordsMapper;
import com.bao.dating.mapper.ChatSessionsMapper;
import com.bao.dating.pojo.dto.ChatRecordSendDTO;
import com.bao.dating.pojo.dto.UserNicknameDTO;
import com.bao.dating.pojo.entity.ChatRecords;
import com.bao.dating.pojo.entity.ChatSessions;
import com.bao.dating.pojo.vo.ChatRecordsVO;
import com.bao.dating.service.ChatService;
import com.bao.dating.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
/**
* 聊天服务实现类
* @author lenovo
*/
@Slf4j
@Service
public class ChatServiceImpl implements ChatService {
@Autowired
private ChatRecordsMapper chatRecordsMapper;
@Autowired
private ChatSessionsMapper chatSessionsMapper;
@Autowired
private UserService userService;
/**
* 消息入库,如果会话不存在则创建会话
* @param senderUserId 发送者用户ID
* @param dto 消息
* @return 聊天记录
*/
@Override
@Transactional(rollbackFor = Exception.class)
public ChatRecordsVO createSession(Long senderUserId, ChatRecordSendDTO dto) {
ChatRecords record = new ChatRecords();
record.setSenderUserId(senderUserId);
record.setReceiverUserId(dto.getReceiverUserId());
record.setMessageContent(dto.getMessageContent());
record.setMessageType(dto.getMessageType());
record.setReadStatus(0);
record.setMessageStatus(1);
record.setSendTime(LocalDateTime.now());
// 插入消息记录
chatRecordsMapper.insert(record);
// 创建接收方会话
ChatSessions sessions = new ChatSessions();
sessions.setUserId(senderUserId);
sessions.setTargetUserId(dto.getReceiverUserId());
// 获取接收方昵称作为发送方会话名称
try {
UserNicknameDTO receiverNicknameInfo = userService.getUserNicknameById(dto.getReceiverUserId());
if (receiverNicknameInfo != null && receiverNicknameInfo.getNickname() != null) {
sessions.setSessionName(receiverNicknameInfo.getNickname());
} else {
log.warn("接收用户ID获取用户信息时出现错误: {} , 使用默认id", dto.getReceiverUserId());
sessions.setSessionName("用户" + dto.getReceiverUserId());
}
} catch (Exception e) {
log.error("接收用户ID获取用户信息时出现错误: {} , 使用默认id", dto.getReceiverUserId(), e);
sessions.setSessionName("用户" + dto.getReceiverUserId());
}
sessions.setLastMessageId(record.getChatId());
sessions.setLastMessageContent(record.getMessageContent());
sessions.setLastMessageTime(record.getSendTime());
chatSessionsMapper.insertIfNotExistsForSender(sessions);
chatSessionsMapper.updateSessionForSender(sessions);
// 创建接收方会话
sessions.setUserId(dto.getReceiverUserId());
sessions.setTargetUserId(senderUserId);
try {
UserNicknameDTO senderNicknameInfo = userService.getUserNicknameById(senderUserId);
if (senderNicknameInfo != null && senderNicknameInfo.getNickname() != null) {
sessions.setSessionName(senderNicknameInfo.getNickname());
} else {
log.warn("接收用户ID获取用户信息时出现错误: {} , 使用默认id", senderUserId);
sessions.setSessionName("用户" + senderUserId);
}
} catch (Exception e) {
log.error("接收用户ID获取用户信息时出现错误: {} , 使用默认id", senderUserId, e);
sessions.setSessionName("用户" + senderUserId);
}
chatSessionsMapper.insertIfNotExistsForReceiver(sessions);
chatSessionsMapper.updateSessionForReceiver(sessions);
// 3. 返回 VO
ChatRecordsVO vo = new ChatRecordsVO();
BeanUtils.copyProperties(record, vo);
return vo;
}
}

View File

@@ -10,13 +10,11 @@ import com.bao.dating.mapper.PostLikeMapper;
import com.bao.dating.mapper.PostMapper;
import com.bao.dating.pojo.dto.PostRequestDTO;
import com.bao.dating.pojo.entity.Post;
import com.bao.dating.pojo.entity.User;
import com.bao.dating.pojo.vo.PostEditVO;
import com.bao.dating.service.PostService;
import com.bao.dating.common.aliyun.AliOssUtil;
import com.bao.dating.service.UserService;
import com.bao.dating.util.FileUtil;
import com.bao.dating.util.WatermarkUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -24,7 +22,6 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -60,9 +57,6 @@ public class PostServiceImpl implements PostService {
@Autowired
private CommentsMapper commentsMapper;
@Autowired
private WatermarkUtil watermarkUtil;
/**
* 上传媒体文件
* @param files 媒体文件数组
@@ -337,61 +331,4 @@ public class PostServiceImpl implements PostService {
return postMapper.selectUserIdByPostId(postId);
}
/**
* 下载动态图片并添加水印
*
* @param postId 动态ID
* @return 带水印的图片
* @throws Exception
*/
@Override
public BufferedImage downloadWithWatermark(Long postId) throws Exception {
// 一次性查出 动态图片 + 作者信息
Map<String, Object> map = postMapper.getUsernameByUserId(postId);
if (map == null || map.isEmpty()) {
Post post = postMapper.selectById(postId);
if (post == null) {
throw new RuntimeException("未找到指定postId的帖子: " + postId);
}
throw new RuntimeException("未找到与postId相关的用户和媒体信息: " + postId);
}
String mediaUrl = (String) map.get("media_oss_keys");
String username = (String) map.get("user_name");
Object userIdObj = map.get("user_id");
if (mediaUrl == null || username == null || userIdObj == null) {
throw new RuntimeException("用户或媒体信息不完整: " + map);
}
mediaUrl = mediaUrl.trim();
if (mediaUrl.isEmpty()) {
throw new RuntimeException("媒体URL为空postId: " + postId);
}
Long userId = userIdObj instanceof Number
? ((Number) userIdObj).longValue()
: Long.valueOf(userIdObj.toString());
// 解析 OSS ObjectKey支持完整URL和直接存key两种
String cleanUrl = mediaUrl.split("\\?")[0]; // 去掉 ? 后面的参数
String objectKey;
if (cleanUrl.startsWith("http")) {
// https://xxx.oss-cn-xxx.aliyuncs.com/post/xxx.jpg → post/xxx.jpg
objectKey = cleanUrl.substring(cleanUrl.indexOf(".com/") + 5);
} else {
objectKey = cleanUrl;
}
if (objectKey.trim().isEmpty()) {
throw new RuntimeException("解析后的ObjectKey为空url: " + mediaUrl);
}
// 下载并动态加水印只给下载的人看OSS原图不改数据库不动
return watermarkUtil.downloadAndWatermark(objectKey, username, userId);
}
}

View File

@@ -3,7 +3,6 @@ package com.bao.dating.service.impl;
import com.bao.dating.common.aliyun.AliOssUtil;
import com.bao.dating.common.aliyun.GreenImageScan;
import com.bao.dating.common.aliyun.GreenTextScan;
import com.bao.dating.common.aliyun.SmsUtil;
import com.bao.dating.common.result.AliOssResult;
import com.bao.dating.common.result.GreenAuditResult;
import com.bao.dating.config.RedisConfig;
@@ -11,12 +10,11 @@ import com.bao.dating.context.UserContext;
import com.bao.dating.mapper.UserMapper;
import com.bao.dating.pojo.dto.UserInfoUpdateDTO;
import com.bao.dating.pojo.dto.UserLoginDTO;
import com.bao.dating.pojo.dto.UserNicknameDTO;
import com.bao.dating.pojo.entity.User;
import com.bao.dating.pojo.vo.UserInfoVO;
import com.bao.dating.pojo.vo.UserLoginVO;
import com.bao.dating.service.UserService;
import com.bao.dating.util.*;
import com.bao.dating.util.CodeUtil;
import com.bao.dating.util.FileUtil;
import com.bao.dating.util.JwtUtil;
import com.bao.dating.util.MD5Util;
@@ -24,7 +22,6 @@ import io.jsonwebtoken.Claims;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@@ -41,10 +38,6 @@ import java.util.concurrent.TimeUnit;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private SmsUtil smsUtil;
@Autowired
private AliOssUtil ossUtil;
@@ -60,9 +53,6 @@ public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 用户登录
*
@@ -323,84 +313,22 @@ public class UserServiceImpl implements UserService {
return userInfoVO;
}
// 发送短信验证码
/**
* 根据用户ID获取用户昵称
*
* @param userId 用户ID
* @return 用户昵称
*/
@Override
public void sendSmsCode(String phone) {
//防刷60 秒内只能发一次
String key = "sms:code:" + phone;
Boolean exists = stringRedisTemplate.hasKey(key);
if (Boolean.TRUE.equals(exists)){
throw new RuntimeException("请勿频繁发送验证码");
public UserNicknameDTO getUserNicknameById(Long userId) {
// 查询数据库获取昵称
User user = userMapper.selectByUserId(userId);
if (user == null) {
throw new RuntimeException("没有此用户");
}
// 生成验证码
String code = CodeUtil.generateCode();
// 发送短信
smsUtil.sendVerificationCode(phone, code);
//存 Redis5分钟过期
stringRedisTemplate.opsForValue()
.set(key,code, 5, TimeUnit.MINUTES);
}
// 校验验证码
@Override
public boolean verifyCode(String phone, String code) {
String key = "sms:code:" + phone;
String realCode = stringRedisTemplate.opsForValue().get(key);
//过期,未发送
if (realCode ==null){
return false;
}
//不匹配
if (!realCode.equals(code)){
return false;
}
// 校验成功,删除验证码(一次性)
stringRedisTemplate.delete(key);
return true;
}
@Override
public UserLoginVO loginByPhone(String phone) {
//根据手机号查询用户
User user = userMapper.selectByPhone(phone);
//创建token
String token = JwtUtil.generateToken(user.getUserId().toString());
//封装返回结果
UserLoginVO VO = new UserLoginVO();
VO.setUserId(user.getUserId());
VO.setToken(token);
return VO;
}
@Override
public List<UserInfoVO> findNearbyUsers(double lat, double lng, double radiusKm) {
//先用经纬度范围筛选(矩形框,提高性能)
double delta = radiusKm / 111.0; // 1° ≈ 111km
double minLat = lat - delta;// 最小纬度
double maxLat = lat + delta;// 最大纬度
double minLng = lng - delta;// 最小经度
double maxLng = lng + delta;// 最大经度
// 打印经纬度范围
System.out.println("Min Latitude: " + minLat + ", Max Latitude: " + maxLat);
System.out.println("Min Longitude: " + minLng + ", Max Longitude: " + maxLng);
List<UserInfoVO> byLatLngRange = userMapper.findByLatLngRange(minLat, maxLat, minLng, maxLng);
//精确计算距离,筛选在半径内的用户
List<UserInfoVO> result = new ArrayList<>();
for (UserInfoVO u:byLatLngRange){
// 检查用户是否有经纬度信息
if (u.getLatitude() != null && u.getLongitude() != null) {
double distance = DistanceUtil.calculate(lat, lng, u.getLatitude(), u.getLongitude());
if (distance <= radiusKm){
result.add(u);
}
}
}
return result;
UserNicknameDTO dto = new UserNicknameDTO();
dto.setUserId(user.getUserId());
dto.setNickname(user.getNickname());
return dto;
}
}

View File

@@ -0,0 +1,51 @@
package com.bao.dating.session;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* WebSocketSession 管理类
* @author KilLze
*/
@Component
public class WsSessionManager {
private final Map<Long, WebSocketSession> SESSION_MAP = new ConcurrentHashMap<>();
/**
* 添加 WebSocketSession
* @param userId 用户ID
* @param session WebSocketSession
*/
public void addSession(Long userId, WebSocketSession session) {
SESSION_MAP.put(userId, session);
}
/**
* 移除 WebSocketSession
* @param userId 用户ID
*/
public void removeSession(Long userId) {
SESSION_MAP.remove(userId);
}
/**
* 获取 WebSocketSession
* @param userId 用户ID
* @return WebSocketSession
*/
public WebSocketSession getSession(Long userId) {
return SESSION_MAP.get(userId);
}
/**
* 判断用户是否在线
* @param userId 用户ID
* @return true-在线false-离线
*/
public boolean isOnline(Long userId) {
return SESSION_MAP.containsKey(userId);
}
}

View File

@@ -1,12 +0,0 @@
package com.bao.dating.util;
import java.util.Random;
public class CodeUtil {
// 生成6位数字验证码
public static String generateCode() {
Random random = new Random();
int code=100000+random.nextInt(900000);
return String.valueOf(code);
}
}

View File

@@ -1,29 +0,0 @@
package com.bao.dating.util;
//Haversine 公式(标准写法)
public class DistanceUtil {
private static final double EARTH_RADIUS = 6371.0; // 地球半径 km
/**
* 计算两点之间距离单位km
*/
public static double calculate(
double lat1, double lon1,
double lat2, double lon2) {
double dLat = Math.toRadians(lat2 - lat1);
double dLon = Math.toRadians(lon2 - lon1);
lat1 = Math.toRadians(lat1);
lat2 = Math.toRadians(lat2);
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
+ Math.cos(lat1) * Math.cos(lat2)
* Math.sin(dLon / 2) * Math.sin(dLon / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return EARTH_RADIUS * c;
}
}

View File

@@ -1,64 +0,0 @@
package com.bao.dating.util;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.bao.dating.config.AliyunOSSConfig;
import com.bao.dating.mapper.PostMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.InputStream;
@Component
public class WatermarkUtil {
@Autowired
private AliyunOSSConfig aliyunOSSConfig;
public BufferedImage downloadAndWatermark(String objectKey, String username, Long userId) throws Exception {
OSS ossClient = new OSSClientBuilder().build(
aliyunOSSConfig.getEndpoint(),
aliyunOSSConfig.getAccessKeyId(),
aliyunOSSConfig.getAccessKeySecret()
);
InputStream inputStream = ossClient.getObject(aliyunOSSConfig.getBucketName(), objectKey).getObjectContent();
BufferedImage image = ImageIO.read(inputStream);
Graphics2D g2d = image.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
// 字体小一点
Font font = new Font("微软雅黑", Font.BOLD, 24);
g2d.setFont(font);
String text = "作者:" + username + " (ID:" + userId + ")";
FontMetrics fm = g2d.getFontMetrics();
int textWidth = fm.stringWidth(text);
int textHeight = fm.getHeight();
// 右下角留边距
int x = image.getWidth() - textWidth - 20;
int y = image.getHeight() - 20;
// 黑色描边
g2d.setColor(Color.BLACK);
g2d.drawString(text, x - 1, y - 1);
g2d.drawString(text, x + 1, y - 1);
g2d.drawString(text, x - 1, y + 1);
g2d.drawString(text, x + 1, y + 1);
// 白色正文
g2d.setColor(Color.WHITE);
g2d.drawString(text, x, y);
g2d.dispose();
ossClient.shutdown();
return image;
}
}

View File

@@ -1,9 +0,0 @@
▄▄▄▄ ██ ▄▄▄▄ ▄▄▄▄ ▄▄ ▄▄
██▀▀▀▀█ ▀▀ ▀▀██ ▀▀██ ██ ▄▄ ██
██▀ ████ ▄█████▄ ██ ██ ▄████▄ ▄▄▄ ▄█▀ ▄█▀ ██ █▄ ▄▄▄█ ▀█▄ ▄▄▄▄
██ ██ ▀ ▄▄▄██ ██ ██ ██▀ ▀██ ▀ ▀▀▄▄ ▄ ██ ▄█▀ ██ ██ ██ ▄▄█▀▀▀ ██ █▀▀ ▀█▄ █▄
██▄ ██ ▄██▀▀▀██ ██ ██ ██ ██ ▀▀▀ ██ ▄█▄▄▄▄▄ ▀▀ ██ ██ ██ ▀▀█▄▄▄ ██ █▀ █ ▀▀████▀
██▄▄▄▄█ ▄▄▄██▄▄▄ ██▄▄▄███ ██▄▄▄ ██▄▄▄ ▀██▄▄██▀ ▀█▄ ▀▀▀▀▀▀▀▀ ██▄██▄██ ▀▀▀█ ▄█▀ █▀▀█
▀▀▀▀ ▀▀▀▀▀▀▀▀ ▀▀▀▀ ▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ██ ▀▀▀ ▀▀▀ ██
▀▀ ▀▀

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bao.dating.mapper.ChatRecordsMapper">
<!-- 插入聊天记录 -->
<insert id="insert" useGeneratedKeys="true" keyProperty="chatId">
INSERT INTO chat_records
(
sender_user_id,
receiver_user_id,
message_content,
message_type,
read_status,
send_time,
message_status,
created_at,
updated_at
)
VALUES
(
#{senderUserId},
#{receiverUserId},
#{messageContent},
#{messageType},
#{readStatus},
#{sendTime},
#{messageStatus},
NOW(),
NOW()
)
</insert>
</mapper>

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bao.dating.mapper.ChatSessionsMapper">
<!-- 如果发送方不存在会话则创建 -->
<insert id="insertIfNotExistsForSender" parameterType="com.bao.dating.pojo.entity.ChatSessions">
INSERT INTO chat_sessions
(user_id, target_user_id, session_name, last_message_id, last_message_content, last_message_time,
unread_count, session_status, top_status, mute_status, created_at, updated_at)
SELECT
#{userId}, #{targetUserId}, #{sessionName}, #{lastMessageId}, #{lastMessageContent}, #{lastMessageTime},
0, 1, 0, 0, NOW(), NOW()
FROM DUAL
WHERE NOT EXISTS (
SELECT 1 FROM chat_sessions
WHERE user_id = #{userId} AND target_user_id = #{targetUserId}
);
</insert>
<!-- 如果接收方不存在会话则创建 -->
<insert id="insertIfNotExistsForReceiver" parameterType="com.bao.dating.pojo.entity.ChatSessions">
INSERT INTO chat_sessions
(user_id, target_user_id, session_name, last_message_id, last_message_content, last_message_time,
unread_count, session_status, top_status, mute_status, created_at, updated_at)
SELECT
#{userId}, #{targetUserId}, #{sessionName}, #{lastMessageId}, #{lastMessageContent}, #{lastMessageTime},
0, 1, 0, 0, NOW(), NOW()
FROM DUAL
WHERE NOT EXISTS (
SELECT 1 FROM chat_sessions
WHERE user_id = #{userId} AND target_user_id = #{targetUserId}
)
</insert>
<!-- 更新发送方的会话信息 -->
<update id="updateSessionForSender" parameterType="com.bao.dating.pojo.entity.ChatSessions">
UPDATE chat_sessions
SET
last_message_id = #{lastMessageId},
last_message_content = #{lastMessageContent},
last_message_time = #{lastMessageTime},
unread_count = 0,
updated_at = NOW()
WHERE user_id = #{userId}
AND target_user_id = #{targetUserId};
</update>
<!-- 更新接收方的会话信息 -->
<update id="updateSessionForReceiver" parameterType="com.bao.dating.pojo.entity.ChatSessions">
UPDATE chat_sessions
SET
last_message_id = #{lastMessageId},
last_message_content = #{lastMessageContent},
last_message_time = #{lastMessageTime},
unread_count = unread_count + 1,
updated_at = NOW()
WHERE user_id = #{userId}
AND target_user_id = #{targetUserId}
</update>
</mapper>

View File

@@ -122,11 +122,5 @@
<select id="selectFavoriteCount" resultType="java.lang.Integer">
select dating.post.favorite_count from dating.post where post.post_id = #{postId}
</select>
<select id="getUsernameByUserId" resultType="map">
SELECT u.user_name, u.user_id, p.media_oss_keys
FROM post p
LEFT JOIN user u ON p.user_id = u.user_id
WHERE p.post_id = #{postId}
</select>
</mapper>

View File

@@ -28,8 +28,6 @@
<result property="signature" column="signature"/>
<result property="createdAt" column="created_at"/>
<result property="updatedAt" column="updated_at"/>
<result property="latitude" column="user_latitude"/>
<result property="longitude" column="user_longitude"/>
</resultMap>
<select id="selectByUserId" resultMap="UserResultMap">
SELECT
@@ -43,10 +41,8 @@
hobbies,
signature,
created_at,
updated_at,
user_latitude,
user_longitude
FROM dating.user WHERE user_id = #{userId}
updated_at
FROM user WHERE user_id = #{userId}
</select>
<!--根据ID更新动态-->
@@ -78,43 +74,4 @@
</set>
WHERE user_id = #{userId}
</update>
<select id="selectByPhone" resultType="com.bao.dating.pojo.entity.User">
select * from dating.user where user_phone =#{phone}
</select>
<resultMap id="UserInfoVOResultMap" type="com.bao.dating.pojo.vo.UserInfoVO">
<id property="userId" column="user_id"/>
<result property="userName" column="user_name"/>
<result property="nickname" column="nickname"/>
<result property="avatarUrl" column="avatar_url"/>
<result property="backgroundUrl" column="background_url"/>
<result property="gender" column="gender"/>
<result property="birthday" column="birthday"/>
<result property="hobbies" column="hobbies" typeHandler="com.bao.dating.handler.ListToJsonTypeHandler"/>
<result property="signature" column="signature"/>
<result property="createdAt" column="created_at"/>
<result property="updatedAt" column="updated_at"/>
<result property="latitude" column="user_latitude"/>
<result property="longitude" column="user_longitude"/>
</resultMap>
<select id="findByLatLngRange" resultMap="UserInfoVOResultMap">
SELECT
user_id,
user_name,
nickname,
avatar_url,
background_url,
gender,
birthday,
hobbies,
signature,
created_at,
updated_at,
user_latitude,
user_longitude
FROM user WHERE user_latitude BETWEEN #{minLat} AND #{maxLat} AND user_longitude BETWEEN #{minLng} AND #{maxLng}
</select>
</mapper>