diff --git a/src/main/java/com/bao/dating/common/aliyun/SmsUtil.java b/src/main/java/com/bao/dating/common/aliyun/SmsUtil.java index 290c766..65f7b39 100644 --- a/src/main/java/com/bao/dating/common/aliyun/SmsUtil.java +++ b/src/main/java/com/bao/dating/common/aliyun/SmsUtil.java @@ -75,6 +75,8 @@ 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())) { @@ -145,6 +147,7 @@ public class SmsUtil { jsonBuilder.append("}"); return sendSms(phoneNumber, templateCode, jsonBuilder.toString()); } + } diff --git a/src/main/java/com/bao/dating/config/WebConfig.java b/src/main/java/com/bao/dating/config/WebConfig.java index 098abff..0d789b4 100644 --- a/src/main/java/com/bao/dating/config/WebConfig.java +++ b/src/main/java/com/bao/dating/config/WebConfig.java @@ -33,7 +33,9 @@ public class WebConfig implements WebMvcConfigurer { "/user/register", "/user/emailLogin", "/api/verification/send-email-code", - "/ip/location" + "/ip/location", + "/user/login", + "/user/sendCode" ); } } diff --git a/src/main/java/com/bao/dating/controller/UserController.java b/src/main/java/com/bao/dating/controller/UserController.java index ac6b4af..781c1da 100644 --- a/src/main/java/com/bao/dating/controller/UserController.java +++ b/src/main/java/com/bao/dating/controller/UserController.java @@ -4,8 +4,8 @@ import com.bao.dating.anno.Log; import com.bao.dating.common.Result; import com.bao.dating.common.ResultCode; import com.bao.dating.context.UserContext; -import com.bao.dating.pojo.dto.UserInfoDTO; import com.bao.dating.pojo.dto.UserLoginDTO; +import com.bao.dating.pojo.dto.UserInfoDTO; import com.bao.dating.pojo.vo.UserInfoVO; import com.bao.dating.pojo.vo.UserLoginVO; import com.bao.dating.service.UserService; @@ -13,7 +13,9 @@ 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; /** * 用户接口 @@ -64,6 +66,7 @@ public class UserController { * @param file 头像文件 * @return 上传后的文件URL列表 */ + @Log @PostMapping(value = "/info/uploadAvatar", consumes = "multipart/form-data") public Result uploadAvatar(@RequestParam("file") MultipartFile file) { String fileUrl = userService.uploadAvatar(file); @@ -75,6 +78,7 @@ public class UserController { * @param file 背景文件 * @return 上传后的文件URL列表 */ + @Log @PostMapping(value = "/info/uploadBackground", consumes = "multipart/form-data") public Result uploadBackground(@RequestParam("file") MultipartFile file) { String fileUrl = userService.uploadBackground(file); @@ -86,14 +90,82 @@ public class UserController { * @param userInfoUpdateDTO 用户信息更新参数 * @return 更新后的用户信息 */ + @Log @PostMapping("/info/update") - public Result userInfoUpdate(@RequestBody UserInfoDTO userInfoUpdateDTO) { + public Result userInfoUpdate(@RequestBody UserInfoUpdateDTO userInfoUpdateDTO) { Long userId = UserContext.getUserId(); userInfoUpdateDTO.setUserId(userId); UserInfoVO userInfoVO =userService.updateUserInfo(userInfoUpdateDTO); return Result.success(ResultCode.SUCCESS, "用户信息更新成功", userInfoVO); } + @PostMapping("/sendCode") + public Result sendCode(@RequestBody Map body) { + String phone = body.get("phone"); + userService.sendSmsCode(phone); + return Result.success(ResultCode.SUCCESS, "验证码发送成功"); + } + + @PostMapping("/loginByCode") + public Result loginByCode(@RequestBody Map 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 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> 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 nearbyUsers = userService.findNearbyUsers(latitude, longitude, distance); + + // 返回成功结果 + return Result.success(ResultCode.SUCCESS, "查询成功", nearbyUsers); + } + + /** * 用户注册 * @param userName 用户名称 diff --git a/src/main/java/com/bao/dating/mapper/CommentsMapper.java b/src/main/java/com/bao/dating/mapper/CommentsMapper.java index 78ca350..7313266 100644 --- a/src/main/java/com/bao/dating/mapper/CommentsMapper.java +++ b/src/main/java/com/bao/dating/mapper/CommentsMapper.java @@ -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 comments(content, user_id, post_id, created_at) VALUES(#{content}, #{user_id}, #{post_id}, #{created_at})") + @Insert("INSERT INTO dating.comments(content, user_id, post_id, created_at) VALUES(#{content}, #{user_id}, #{post_id}, #{created_at})") int addComment(Comments comments); // 删除评论 - @Delete("DELETE FROM comments WHERE user_id = #{user_id}") + @Delete("DELETE FROM dating.comments WHERE user_id = #{user_id}") int deleteComments(@Param("user_id") Long user_id); // 根据动态ID查询评论列表 - @Select("SELECT * FROM comments WHERE post_id = #{post_id} ORDER BY created_at DESC") + @Select("SELECT * FROM dating.comments WHERE post_id = #{post_id} ORDER BY created_at DESC") List getCommentsByPostId(@Param("post_id") Long post_id); /** diff --git a/src/main/java/com/bao/dating/mapper/PostFavoriteMapper.java b/src/main/java/com/bao/dating/mapper/PostFavoriteMapper.java index 2557ba8..06c8c23 100644 --- a/src/main/java/com/bao/dating/mapper/PostFavoriteMapper.java +++ b/src/main/java/com/bao/dating/mapper/PostFavoriteMapper.java @@ -8,7 +8,7 @@ import org.apache.ibatis.annotations.Param; import java.util.List; @Mapper -public interface PostFavoriteMapper { +public interface PostFavoriteMapper { //查询当前已收藏所有用户 List selectUserIDByPostID(@Param("postId") Long postId); //添加收藏 diff --git a/src/main/java/com/bao/dating/mapper/UserMapper.java b/src/main/java/com/bao/dating/mapper/UserMapper.java index 260fdd8..29b83aa 100644 --- a/src/main/java/com/bao/dating/mapper/UserMapper.java +++ b/src/main/java/com/bao/dating/mapper/UserMapper.java @@ -2,9 +2,12 @@ package com.bao.dating.mapper; import com.bao.dating.pojo.dto.UserInfoDTO; 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 * @author KilLze @@ -54,4 +57,15 @@ public interface UserMapper { */ User selectByUserEmailUser(@Param("userEmail") String email); + User selectByPhone(@Param("phone") String phone); + + /** + * 根据经纬度范围查询用户 + * @param minLat 最小纬度 + * @param maxLat 最大纬度 + * @param minLng 最小经度 + * @param maxLng 最大经度 + * @return 用户列表 + */ + List findByLatLngRange(@Param("minLat") double minLat, @Param("maxLat") double maxLat, @Param("minLng") double minLng, @Param("maxLng") double maxLng); } diff --git a/src/main/java/com/bao/dating/pojo/entity/User.java b/src/main/java/com/bao/dating/pojo/entity/User.java index e668af5..9bf9641 100644 --- a/src/main/java/com/bao/dating/pojo/entity/User.java +++ b/src/main/java/com/bao/dating/pojo/entity/User.java @@ -43,4 +43,8 @@ public class User implements Serializable { private String userEmail; private String userPhone; + + private Double latitude; // 纬度 + + private Double longitude; // 经度 } diff --git a/src/main/java/com/bao/dating/pojo/vo/UserInfoVO.java b/src/main/java/com/bao/dating/pojo/vo/UserInfoVO.java index c5a81fa..35c5c90 100644 --- a/src/main/java/com/bao/dating/pojo/vo/UserInfoVO.java +++ b/src/main/java/com/bao/dating/pojo/vo/UserInfoVO.java @@ -24,4 +24,6 @@ public class UserInfoVO implements Serializable { private String signature; private LocalDateTime updatedAt; private LocalDateTime createdAt; + private Double latitude; + private Double longitude; } diff --git a/src/main/java/com/bao/dating/service/UserService.java b/src/main/java/com/bao/dating/service/UserService.java index 5b7b67c..9881b4c 100644 --- a/src/main/java/com/bao/dating/service/UserService.java +++ b/src/main/java/com/bao/dating/service/UserService.java @@ -7,6 +7,8 @@ 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 @@ -76,4 +78,20 @@ public interface UserService { * @return 用户 */ UserInfoDTO getUserInfoById(Long userId); + 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 用户列表 + */ + List findNearbyUsers(double lat,double lng,double radiusKm); } diff --git a/src/main/java/com/bao/dating/service/impl/UserServiceImpl.java b/src/main/java/com/bao/dating/service/impl/UserServiceImpl.java index 4785af1..0c892ce 100644 --- a/src/main/java/com/bao/dating/service/impl/UserServiceImpl.java +++ b/src/main/java/com/bao/dating/service/impl/UserServiceImpl.java @@ -5,17 +5,21 @@ import com.bao.dating.common.ResultCode; 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; import com.bao.dating.context.UserContext; import com.bao.dating.mapper.UserMapper; -import com.bao.dating.pojo.dto.UserInfoDTO; +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; import com.bao.dating.service.VerificationCodeService; +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; @@ -39,6 +43,10 @@ import java.util.concurrent.TimeUnit; @Service public class UserServiceImpl implements UserService { + + @Autowired + private SmsUtil smsUtil; + @Autowired private AliOssUtil ossUtil; @@ -54,6 +62,9 @@ public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; + @Autowired + private StringRedisTemplate stringRedisTemplate; + @Autowired private VerificationCodeService verificationCodeService; @@ -236,7 +247,7 @@ public class UserServiceImpl implements UserService { * @param userInfoUpdateDTO 用户信息更新参数 */ @Override - public UserInfoVO updateUserInfo(UserInfoDTO userInfoUpdateDTO) { + public UserInfoVO updateUserInfo(UserInfoUpdateDTO userInfoUpdateDTO) { Long userId = userInfoUpdateDTO.getUserId(); User user = userMapper.selectByUserId(userId); if (user == null) { @@ -391,4 +402,85 @@ public class UserServiceImpl implements UserService { dto.setNickname(user.getNickname()); return dto; } + + // 发送短信验证码 + @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("请勿频繁发送验证码"); + } + + // 生成验证码 + String code = CodeUtil.generateCode(); + // 发送短信 + smsUtil.sendVerificationCode(phone, code); + //存 Redis(5分钟过期) + 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 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 byLatLngRange = userMapper.findByLatLngRange(minLat, maxLat, minLng, maxLng); + + //精确计算距离,筛选在半径内的用户 + List 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; + } } diff --git a/src/main/java/com/bao/dating/util/CodeUtil.java b/src/main/java/com/bao/dating/util/CodeUtil.java new file mode 100644 index 0000000..be43c7f --- /dev/null +++ b/src/main/java/com/bao/dating/util/CodeUtil.java @@ -0,0 +1,12 @@ +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); + } +} diff --git a/src/main/java/com/bao/dating/util/DistanceUtil.java b/src/main/java/com/bao/dating/util/DistanceUtil.java new file mode 100644 index 0000000..f994f31 --- /dev/null +++ b/src/main/java/com/bao/dating/util/DistanceUtil.java @@ -0,0 +1,29 @@ +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; + } +} diff --git a/src/main/resources/com/bao/dating/mapper/UserMapper.xml b/src/main/resources/com/bao/dating/mapper/UserMapper.xml index d56d41b..625a9fd 100644 --- a/src/main/resources/com/bao/dating/mapper/UserMapper.xml +++ b/src/main/resources/com/bao/dating/mapper/UserMapper.xml @@ -32,6 +32,8 @@ + + @@ -89,5 +93,42 @@ WHERE user_id = #{userId} + + + + + + + + + + + + + + + + + + + \ No newline at end of file