diff --git a/pom.xml b/pom.xml index 2587f61..aa960d4 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,13 @@ spring-boot-starter + + + org.mybatis + mybatis + 3.5.10 + + org.springframework.boot spring-boot-starter-web @@ -103,6 +110,38 @@ 0.2.8 + + + io.jsonwebtoken + jjwt-api + 0.11.5 + + + io.jsonwebtoken + jjwt-impl + 0.11.5 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.11.5 + runtime + + + + + com.aliyun + dysmsapi20170525 + 3.0.0 + + + + + org.springframework.boot + spring-boot-starter-mail + + @@ -123,8 +162,8 @@ maven-compiler-plugin 3.8.1 - 1.8 - 1.8 + 9 + 9 UTF-8 diff --git a/src/main/java/com/bao/dating/common/Comments.java b/src/main/java/com/bao/dating/common/Comments.java new file mode 100644 index 0000000..45e30b4 --- /dev/null +++ b/src/main/java/com/bao/dating/common/Comments.java @@ -0,0 +1,19 @@ +package com.bao.dating.common; + + +import lombok.Data; + +import java.time.LocalDateTime; + + +/** + * 评论表 + */ +@Data +public class Comments { + private Long comment_id; // 评论ID + private String content; // 评论内容 + private Long user_id; // 评论人ID + private Long post_id; // 关联动态ID + private LocalDateTime created_at; // 评论时间 +} diff --git a/src/main/java/com/bao/dating/common/Post.java b/src/main/java/com/bao/dating/common/Post.java new file mode 100644 index 0000000..10293a3 --- /dev/null +++ b/src/main/java/com/bao/dating/common/Post.java @@ -0,0 +1,16 @@ +package com.bao.dating.common; + + +import lombok.Data; + +import java.time.LocalDateTime; +/** + * 动态表 + */ +@Data +public class Post { + private Long post_id; // 动态ID + private String content; // 动态内容 + private Long user_id; // 发布人ID + private LocalDateTime created_at; // 创建时间 +} diff --git a/src/main/java/com/bao/dating/common/ResultCode.java b/src/main/java/com/bao/dating/common/ResultCode.java index baf6419..d577366 100644 --- a/src/main/java/com/bao/dating/common/ResultCode.java +++ b/src/main/java/com/bao/dating/common/ResultCode.java @@ -8,7 +8,8 @@ public enum ResultCode { UNAUTHORIZED(401, "未登录或 Token 失效"), FORBIDDEN(403, "无权限"), NOT_FOUND(404, "数据不存在"), - SYSTEM_ERROR(500, "系统异常"); + SYSTEM_ERROR(500, "系统异常"), + FAIL(500, "操作失败"); private final int code; private final String msg; diff --git a/src/main/java/com/bao/dating/util/AliOssUtil.java b/src/main/java/com/bao/dating/common/aliyun/AliOssUtil.java similarity index 89% rename from src/main/java/com/bao/dating/util/AliOssUtil.java rename to src/main/java/com/bao/dating/common/aliyun/AliOssUtil.java index f4035b3..f0f49fb 100644 --- a/src/main/java/com/bao/dating/util/AliOssUtil.java +++ b/src/main/java/com/bao/dating/common/aliyun/AliOssUtil.java @@ -1,4 +1,4 @@ -package com.bao.dating.util; +package com.bao.dating.common.aliyun; import com.aliyun.oss.ClientException; import com.aliyun.oss.OSS; @@ -7,12 +7,15 @@ import com.aliyun.oss.OSSException; import lombok.AllArgsConstructor; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; import java.io.ByteArrayInputStream; @Data -@AllArgsConstructor @Slf4j +@Component +@ConfigurationProperties(prefix = "aliyun.oss") public class AliOssUtil { private String endpoint; diff --git a/src/main/java/com/bao/dating/common/aliyun/GreenImageScan.java b/src/main/java/com/bao/dating/common/aliyun/GreenImageScan.java index 75cacec..cd6c7c2 100644 --- a/src/main/java/com/bao/dating/common/aliyun/GreenImageScan.java +++ b/src/main/java/com/bao/dating/common/aliyun/GreenImageScan.java @@ -61,19 +61,24 @@ public class GreenImageScan { if (scanImageResponse.getStatusCode() == 200) { - List subResults = scanImageResponse.body.data.results.get(0).getSubResults(); + List results = scanImageResponse.body.data.results; - ListIterator listIterator = subResults.listIterator(); - while (listIterator.hasNext()) { - ScanImageResponseBody.ScanImageResponseBodyDataResultsSubResults item = listIterator.next(); - - if (!item.suggestion.equals("pass")) { - resultMap.put("suggestion", item.suggestion); - resultMap.put("label", item.label); - return resultMap; + // 遍历每一张图片的审核结果 + for (ScanImageResponseBody.ScanImageResponseBodyDataResults result : results) { + List subResults = result.getSubResults(); + + // 检查这张图片的所有子审核项 + for (ScanImageResponseBody.ScanImageResponseBodyDataResultsSubResults subResult : subResults) { + // 如果有任何一项未通过审核,则整个审核不通过 + if (!subResult.suggestion.equals("pass")) { + resultMap.put("suggestion", subResult.suggestion); + resultMap.put("label", subResult.label); + return resultMap; + } } } - // 所有审核项都通过时,返回pass结果 + + // 所有图片的所有审核项都通过时,返回pass结果 resultMap.put("suggestion", "pass"); resultMap.put("label", "normal"); return resultMap; @@ -94,4 +99,4 @@ public class GreenImageScan { return null; } } -} +} \ No newline at end of file diff --git a/src/main/java/com/bao/dating/common/aliyun/SmsUtil.java b/src/main/java/com/bao/dating/common/aliyun/SmsUtil.java new file mode 100644 index 0000000..1083218 --- /dev/null +++ b/src/main/java/com/bao/dating/common/aliyun/SmsUtil.java @@ -0,0 +1,149 @@ +package com.bao.dating.common.aliyun; + +import com.aliyun.dysmsapi20170525.Client; +import com.aliyun.dysmsapi20170525.models.SendSmsRequest; +import com.aliyun.dysmsapi20170525.models.SendSmsResponse; +import com.aliyun.teaopenapi.models.Config; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * 阿里云短信服务工具类 + * @author KilLze + */ +@Data +@Slf4j +@Component +@ConfigurationProperties(prefix = "aliyun.sms") +public class SmsUtil { + + /** + * 访问密钥ID + */ + private String accessKeyId; + + /** + * 访问密钥Secret + */ + private String accessKeySecret; + + /** + * 短信服务区域节点 + */ + private String regionId = "cn-hangzhou"; + + /** + * 默认签名名称 + */ + private String signName; + + /** + * 默认模板代码 + */ + private String templateCode; + + /** + * 创建短信客户端 + * @return Client对象 + * @throws Exception 创建失败时抛出异常 + */ + private Client createClient() throws Exception { + Config config = new Config() + .setAccessKeyId(accessKeyId) + .setAccessKeySecret(accessKeySecret) + .setEndpoint("dysmsapi.aliyuncs.com"); + return new Client(config); + } + + /** + * 发送短信 + * @param phoneNumber 手机号码 + * @param templateCode 模板代码(如果为空则使用默认模板代码) + * @param templateParam 模板参数(JSON格式字符串,如:{"code":"123456"}) + * @return 是否发送成功 + */ + public boolean sendSms(String phoneNumber, String templateCode, String templateParam) { + try { + Client client = createClient(); + SendSmsRequest sendSmsRequest = new SendSmsRequest() + .setPhoneNumbers(phoneNumber) + .setSignName(signName) + .setTemplateCode(templateCode != null ? templateCode : this.templateCode) + .setTemplateParam(templateParam); + + SendSmsResponse response = client.sendSms(sendSmsRequest); + + if ("OK".equals(response.getBody().getCode())) { + log.info("短信发送成功,手机号:{},请求ID:{}", phoneNumber, response.getBody().getRequestId()); + return true; + } else { + log.error("短信发送失败,手机号:{},错误码:{},错误信息:{}", + phoneNumber, response.getBody().getCode(), response.getBody().getMessage()); + return false; + } + } catch (Exception e) { + log.error("发送短信异常,手机号:{},异常信息:{}", phoneNumber, e.getMessage(), e); + return false; + } + } + + /** + * 发送短信(使用默认模板代码) + * @param phoneNumber 手机号码 + * @param templateParam 模板参数(JSON格式字符串) + * @return 是否发送成功 + */ + public boolean sendSms(String phoneNumber, String templateParam) { + return sendSms(phoneNumber, null, templateParam); + } + + /** + * 发送验证码短信 + * @param phoneNumber 手机号码 + * @param code 验证码 + * @return 是否发送成功 + */ + public boolean sendVerificationCode(String phoneNumber, String code) { + String templateParam = String.format("{\"code\":\"%s\"}", code); + return sendSms(phoneNumber, templateParam); + } + + /** + * 发送验证码短信(使用指定模板代码) + * @param phoneNumber 手机号码 + * @param code 验证码 + * @param templateCode 模板代码 + * @return 是否发送成功 + */ + public boolean sendVerificationCode(String phoneNumber, String code, String templateCode) { + String templateParam = String.format("{\"code\":\"%s\"}", code); + return sendSms(phoneNumber, templateCode, templateParam); + } + + /** + * 发送短信(使用Map参数) + * @param phoneNumber 手机号码 + * @param templateCode 模板代码 + * @param params 模板参数Map + * @return 是否发送成功 + */ + public boolean sendSmsWithParams(String phoneNumber, String templateCode, Map params) { + StringBuilder jsonBuilder = new StringBuilder("{"); + boolean first = true; + for (Map.Entry entry : params.entrySet()) { + if (!first) { + jsonBuilder.append(","); + } + jsonBuilder.append("\"").append(entry.getKey()).append("\":\"") + .append(entry.getValue()).append("\""); + first = false; + } + jsonBuilder.append("}"); + return sendSms(phoneNumber, templateCode, jsonBuilder.toString()); + } +} + diff --git a/src/main/java/com/bao/dating/config/OssConfiguration.java b/src/main/java/com/bao/dating/config/OssConfiguration.java deleted file mode 100644 index 01189d1..0000000 --- a/src/main/java/com/bao/dating/config/OssConfiguration.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.bao.dating.config; - - -import com.bao.dating.properties.AliOssProperties; -import com.bao.dating.util.AliOssUtil; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * 配置类,用于创建AliOssUtil对象 - */ -@Configuration -@Slf4j -public class OssConfiguration { - @Bean - @ConditionalOnMissingBean - public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties){ - log.info("开始创建阿里云文件上传工具类对象:{}",aliOssProperties); - return new AliOssUtil(aliOssProperties.getEndpoint(), - aliOssProperties.getAccessKeyId(), - aliOssProperties.getAccessKeySecret(), - aliOssProperties.getBucketName()); - } -} diff --git a/src/main/java/com/bao/dating/controller/CommentController.java b/src/main/java/com/bao/dating/controller/CommentController.java new file mode 100644 index 0000000..4f49f00 --- /dev/null +++ b/src/main/java/com/bao/dating/controller/CommentController.java @@ -0,0 +1,52 @@ +package com.bao.dating.controller; + +import com.bao.dating.common.Comments; +import com.bao.dating.common.Result; +import com.bao.dating.common.ResultCode; +import com.bao.dating.service.CommentsService; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; + +@RestController +@RequestMapping("/comments") +@CrossOrigin +public class CommentController { + @Resource + private CommentsService commentsService; + + /** + * 添加评论 + * @param comment 评论对象(含content、userId、dynamicId) + */ + @PostMapping("/add") + public Result addComment(@RequestBody Comments comment) { + boolean success = commentsService.addComment(comment); + return success ? + Result.success(ResultCode.SUCCESS, "评论成功") : + Result.error(ResultCode.FAIL, "评论失败"); + } + + /** + * 删除评论 + * @param user_id 评论ID + */ + @DeleteMapping("/delete/{user_id}") + public Result deleteComment(@PathVariable Long user_id) { + boolean success = commentsService.deleteComment(user_id); + return success ? + Result.success(ResultCode.SUCCESS, "删除成功") : + Result.error(ResultCode.FAIL, "删除失败"); + } + + /** + * 根据动态ID查询评论列表 + * @param post_id 动态ID + */ + @GetMapping("/list/{post_id}") + public Result> getCommentList(@PathVariable Long post_id) { + List commentList = commentsService.getCommentByPostId(post_id); + return Result.success(ResultCode.SUCCESS, commentList); + } +} diff --git a/src/main/java/com/bao/dating/controller/PostController.java b/src/main/java/com/bao/dating/controller/PostController.java index 5343553..ee337d3 100644 --- a/src/main/java/com/bao/dating/controller/PostController.java +++ b/src/main/java/com/bao/dating/controller/PostController.java @@ -5,6 +5,7 @@ import com.bao.dating.common.Result; import com.bao.dating.common.ResultCode; import com.bao.dating.pojo.dto.PostRequestDTO; import com.bao.dating.pojo.entity.Post; +import com.bao.dating.pojo.vo.PostEditVO; import com.bao.dating.service.PostService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -20,7 +21,7 @@ public class PostController { private PostService postService; /** - * 上传媒体文件接口 123321 + * 上传媒体文件接口 * @param files 媒体文件数组 * @return 上传后的文件URL列表 */ @@ -53,4 +54,27 @@ public class PostController { postService.deletePostById(postId); return Result.success(ResultCode.SUCCESS_DELETE, "动态删除成功", null); } + + /** + * 根据ID查询动态接口 + * @param postId 动态ID + * @return 动态对象 + */ + @GetMapping("/{postId}") + public Result getPostById(@PathVariable Long postId) { + PostEditVO postEditVO = postService.getPostForEdit(postId); + return Result.success(ResultCode.SUCCESS,"查询成功", postEditVO); + } + + /** + * 更新动态接口 + * @param postId 动态ID + * @param postRequestDTO 动态信息 + * @return 更新后的动态对象 + */ + @PutMapping("/{postId}") + public Result updatePost(@PathVariable Long postId, @RequestBody PostRequestDTO postRequestDTO) { + PostEditVO result = postService.updatePost(postId, postRequestDTO); + return Result.success(ResultCode.SUCCESS_REVIEW, "动态更新成功", result); + } } \ No newline at end of file diff --git a/src/main/java/com/bao/dating/controller/PostFavoriteController.java b/src/main/java/com/bao/dating/controller/PostFavoriteController.java new file mode 100644 index 0000000..fd44e56 --- /dev/null +++ b/src/main/java/com/bao/dating/controller/PostFavoriteController.java @@ -0,0 +1,34 @@ +package com.bao.dating.controller; + +import com.bao.dating.common.Result; +import com.bao.dating.common.ResultCode; +import com.bao.dating.pojo.entity.User; +import com.bao.dating.service.PostFavoriteService; +import com.bao.dating.service.PostService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +@RestController +@RequestMapping("/posts") +public class PostFavoriteController { + @Autowired + private PostFavoriteService postFavoriteService; + @PostMapping("/{post_id}/favorites") + public Result> addPostFavorite(@PathVariable("post_id")Long postId, User user){ + if (user == null){ + return Result.error(ResultCode.PARAM_ERROR); + } + Long userId = user.getUserId(); + return postFavoriteService.postFavorite(userId,postId); + } + @DeleteMapping("/{post_id}/favorites") + public Result deletePostFavorite(@PathVariable("post_id")Long postId, User user){ + if (user == null){ + return Result.error(ResultCode.PARAM_ERROR); + } + Long userId = user.getUserId(); + return postFavoriteService.deletePostFavorite(userId, postId); + } +} diff --git a/src/main/java/com/bao/dating/controller/PostLikeController.java b/src/main/java/com/bao/dating/controller/PostLikeController.java index 2c6fa17..ef246e7 100644 --- a/src/main/java/com/bao/dating/controller/PostLikeController.java +++ b/src/main/java/com/bao/dating/controller/PostLikeController.java @@ -17,6 +17,12 @@ public class PostLikeController { @Autowired private PostLikeService postLikeService; + /** + * 点赞接口 + * @param postId + * @param body + * @return + */ @PostMapping("/{postId}/likes") public Result likePost(@PathVariable Long postId, @RequestBody Map body){ // 从请求体中取出 user_id @@ -27,6 +33,11 @@ public class PostLikeController { return postLikeService.likePost(postId,userId); } + /** + * 取消点赞接口 + * @param postId + * @param body + */ @DeleteMapping("/{postId}/likes") public void unlike(@PathVariable Long postId, @RequestBody Map body){ // 从请求体中获取 user_id diff --git a/src/main/java/com/bao/dating/mapper/CommentsMapper.java b/src/main/java/com/bao/dating/mapper/CommentsMapper.java new file mode 100644 index 0000000..26acdb3 --- /dev/null +++ b/src/main/java/com/bao/dating/mapper/CommentsMapper.java @@ -0,0 +1,22 @@ +package com.bao.dating.mapper; + + +import com.bao.dating.common.Comments; +import com.bao.dating.common.Post; +import org.apache.ibatis.annotations.*; + +import java.util.List; + +public interface CommentsMapper { + // 添加评论 + @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 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") + 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 new file mode 100644 index 0000000..a0aa443 --- /dev/null +++ b/src/main/java/com/bao/dating/mapper/PostFavoriteMapper.java @@ -0,0 +1,15 @@ +package com.bao.dating.mapper; + +import com.bao.dating.pojo.entity.PostFavorite; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface PostFavoriteMapper { + //查询当前已收藏所有用户 + List selectUserIDByPostID(@Param("postId") Long postId); + int addPostFavorite(PostFavorite postFavorite); + int deletePostFavorite(@Param("postId") Long postId); +} diff --git a/src/main/java/com/bao/dating/mapper/PostMapper.java b/src/main/java/com/bao/dating/mapper/PostMapper.java index 4e31426..668be55 100644 --- a/src/main/java/com/bao/dating/mapper/PostMapper.java +++ b/src/main/java/com/bao/dating/mapper/PostMapper.java @@ -2,24 +2,39 @@ package com.bao.dating.mapper; import com.bao.dating.pojo.entity.Post; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; @Mapper public interface PostMapper { /** * 插入动态 * - * @param post 动态对象 - * @return 插入的行数 + * @param post */ - int insert(Post post); + void insert(Post post); /** * 根据ID删除动态 * * @param postId 动态ID - * @return 删除的行数 */ - int deletePostById(Integer postId); + void deletePostById(@Param("postId") Long postId); + + /** + * 根据ID查询动态 + * + * @param postId + * @return + */ + Post selectById(@Param("postId") Long postId); + + /** + * 根据ID更新动态 + * + * @param post + * @return + */ + void updateById(Post post); /** * 查询点赞数 @@ -44,4 +59,34 @@ public interface PostMapper { * @return 影响行数 */ int decreaseLikeCount(Long postId); + + /** + * 查询当前动态属于哪个用户id + * @param postId 动态id + * @return 用户id + */ + Long selectUserIdByPostId(@Param("post_id") Long postId); + + /** + * 查询点赞数 + * + * @param postId + * @return + */ + int selectFavoriteCount(Long postId); + + /** + * 收藏数+1 + * + * @param postId 动态ID + * @return 影响行数 + */ + int increaseFavoriteCount(Long postId); + /** + * 收藏数-1 + * + * @param postId 动态ID + * @return 影响行数 + */ + int decreaseFavoriteCount(Long postId); } diff --git a/src/main/java/com/bao/dating/pojo/dto/PostRequestDTO.java b/src/main/java/com/bao/dating/pojo/dto/PostRequestDTO.java index fde914d..27b23b9 100644 --- a/src/main/java/com/bao/dating/pojo/dto/PostRequestDTO.java +++ b/src/main/java/com/bao/dating/pojo/dto/PostRequestDTO.java @@ -1,14 +1,15 @@ package com.bao.dating.pojo.dto; import lombok.Data; -import org.springframework.web.multipart.MultipartFile; import java.util.List; +/** + * 动态数据传输对象 + */ @Data public class PostRequestDTO { - private Long userId; private String content; + private List mediaOssKeys; private List tags; - private List mediaUrls; } \ No newline at end of file diff --git a/src/main/java/com/bao/dating/pojo/vo/PostEditVO.java b/src/main/java/com/bao/dating/pojo/vo/PostEditVO.java new file mode 100644 index 0000000..87e6bdd --- /dev/null +++ b/src/main/java/com/bao/dating/pojo/vo/PostEditVO.java @@ -0,0 +1,19 @@ +package com.bao.dating.pojo.vo; + +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 修改内容查询返回数据 + */ +@Data +public class PostEditVO { + private Long postId; + private String content; + private List mediaOssKeys; + private List tags; + private Integer isPublic; + private LocalDateTime updatedAt; +} \ No newline at end of file diff --git a/src/main/java/com/bao/dating/properties/AliOssProperties.java b/src/main/java/com/bao/dating/properties/AliOssProperties.java deleted file mode 100644 index 7756754..0000000 --- a/src/main/java/com/bao/dating/properties/AliOssProperties.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.bao.dating.properties; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; - -@Component -@ConfigurationProperties(prefix = "aliyun.oss") -@Data -public class AliOssProperties { - private String endpoint; - private String accessKeyId; - private String accessKeySecret; - private String bucketName; -} diff --git a/src/main/java/com/bao/dating/service/CommentsService.java b/src/main/java/com/bao/dating/service/CommentsService.java new file mode 100644 index 0000000..202a362 --- /dev/null +++ b/src/main/java/com/bao/dating/service/CommentsService.java @@ -0,0 +1,20 @@ +package com.bao.dating.service; + + +import com.bao.dating.common.Comments; + +import java.util.List; + +public interface CommentsService{ + // 添加评论 + boolean addComment(Comments comments); + + // 删除评论 + boolean deleteComment(Long user_id); + + // 根据动态ID查询评论 + List getCommentByPostId(Long post_id); + + + +} diff --git a/src/main/java/com/bao/dating/service/PostFavoriteService.java b/src/main/java/com/bao/dating/service/PostFavoriteService.java new file mode 100644 index 0000000..32ded85 --- /dev/null +++ b/src/main/java/com/bao/dating/service/PostFavoriteService.java @@ -0,0 +1,10 @@ +package com.bao.dating.service; + +import com.bao.dating.common.Result; + +import java.util.Map; + +public interface PostFavoriteService { + Result> postFavorite(Long userid,Long postId); + Result deletePostFavorite(Long userid,Long postId); +} diff --git a/src/main/java/com/bao/dating/service/PostService.java b/src/main/java/com/bao/dating/service/PostService.java index a60fff8..1021c0f 100644 --- a/src/main/java/com/bao/dating/service/PostService.java +++ b/src/main/java/com/bao/dating/service/PostService.java @@ -2,6 +2,7 @@ package com.bao.dating.service; import com.bao.dating.pojo.dto.PostRequestDTO; import com.bao.dating.pojo.entity.Post; +import com.bao.dating.pojo.vo.PostEditVO; import org.springframework.web.multipart.MultipartFile; import java.util.List; @@ -16,15 +17,39 @@ public interface PostService { /** * 创建动态 - * @param postRequestDTO 动态请求数据传输对象 + * @param userId 用户ID + * @param postRequestDTO 动态数据传输对象 * @return 创建的动态对象 */ - Post createPost(PostRequestDTO postRequestDTO); + Post createPost(Long userId, PostRequestDTO postRequestDTO); /** * 删除动态 * @param postId 动态ID * @return 删除的动态对象 */ + void deletePostById(Long postId); + void deletePostById(Integer postId); + + /** + * 查询动态详情(用于编辑) + * @param postId 动态ID + */ + PostEditVO getPostForEdit(Long postId); + + /** + * 修改动态 + * @param postId 动态ID + * @param postRequestDTO 修改的动态数据传输对象 + * @return 修改后的动态对象 + */ + PostEditVO updatePost(Long postId, PostRequestDTO postRequestDTO); + + /** + * 查询 + * @param postId 动态id + * @return 用户id + */ + Long selectUserIdByPostId(Long postId); } \ No newline at end of file diff --git a/src/main/java/com/bao/dating/service/impl/CommentsServiceImpl.java b/src/main/java/com/bao/dating/service/impl/CommentsServiceImpl.java new file mode 100644 index 0000000..f7248da --- /dev/null +++ b/src/main/java/com/bao/dating/service/impl/CommentsServiceImpl.java @@ -0,0 +1,39 @@ +package com.bao.dating.service.impl; + +import com.bao.dating.common.Comments; + +import com.bao.dating.mapper.CommentsMapper; + + +import com.bao.dating.service.CommentsService; +import org.springframework.stereotype.Service; + + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.List; + +@Service +public class CommentsServiceImpl implements CommentsService { + + + + @Resource + private CommentsMapper commentsMapper; + + @Override + public boolean addComment(Comments comments) { + // 设置创建时间 + comments.setCreated_at(LocalDateTime.now()); + return commentsMapper.addComment(comments) > 0; + } + @Override + public boolean deleteComment(Long user_id) { + return commentsMapper.deleteComments(user_id) > 0; + } + + @Override + public List getCommentByPostId(Long post_id) { + return commentsMapper.getCommentsByPostId(post_id); + } +} diff --git a/src/main/java/com/bao/dating/service/impl/PostFavoriteServiceImpl.java b/src/main/java/com/bao/dating/service/impl/PostFavoriteServiceImpl.java new file mode 100644 index 0000000..9424f11 --- /dev/null +++ b/src/main/java/com/bao/dating/service/impl/PostFavoriteServiceImpl.java @@ -0,0 +1,61 @@ +package com.bao.dating.service.impl; + +import com.bao.dating.common.Result; +import com.bao.dating.common.ResultCode; +import com.bao.dating.mapper.PostFavoriteMapper; +import com.bao.dating.mapper.PostMapper; +import com.bao.dating.pojo.entity.PostFavorite; +import com.bao.dating.service.PostFavoriteService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class PostFavoriteServiceImpl implements PostFavoriteService { + @Autowired + private PostFavoriteMapper postFavoriteMapper; + @Autowired + private PostMapper postMapper; + @Override + @Transactional + public Result> postFavorite(Long userid, Long postId) { + Long userId = postMapper.selectUserIdByPostId(postId); + if (userid.equals(userId)){ + return Result.error(ResultCode.FORBIDDEN,"无法收藏自己发布动态"); + } + List allUserId = postFavoriteMapper.selectUserIDByPostID(postId); + if (allUserId.contains(userid)){ + return Result.error(ResultCode.FORBIDDEN,"已收藏"); + } + PostFavorite postFavorite = new PostFavorite(); + postFavorite.setPostId(postId); + postFavorite.setUserId(userid); + postFavorite.setCreatedAt(LocalDateTime.now()); + postFavoriteMapper.addPostFavorite(postFavorite); + postMapper.increaseFavoriteCount(postId); + Long count = (long) postMapper.selectFavoriteCount(postId); + Map data = new HashMap<>(); + data.put("favorite_id",postFavorite.getFavoriteId()); + data.put("post_id",postId); + data.put("user_id",userid); + data.put("current_favorites_count",count); + return Result.success(ResultCode.SUCCESS_REVIEW,"收藏成功",data); + } + + @Override + @Transactional + public Result deletePostFavorite(Long userid, Long postId) { + List allUserId = postFavoriteMapper.selectUserIDByPostID(postId); + if (! allUserId.contains(userid)){ + return Result.error(ResultCode.FORBIDDEN,"请先收藏"); + } + postFavoriteMapper.deletePostFavorite(postId); + postMapper.decreaseFavoriteCount(postId); + return null; + } +} diff --git a/src/main/java/com/bao/dating/service/impl/PostServiceImpl.java b/src/main/java/com/bao/dating/service/impl/PostServiceImpl.java index 96d802d..e6478cb 100644 --- a/src/main/java/com/bao/dating/service/impl/PostServiceImpl.java +++ b/src/main/java/com/bao/dating/service/impl/PostServiceImpl.java @@ -5,9 +5,11 @@ import com.bao.dating.common.aliyun.GreenTextScan; 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.vo.PostEditVO; import com.bao.dating.service.PostService; -import com.bao.dating.util.AliOssUtil; +import com.bao.dating.common.aliyun.AliOssUtil; import com.bao.dating.util.FileUtil; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @@ -77,11 +79,23 @@ public class PostServiceImpl implements PostService { /** * 创建动态 * - * @param postRequestDTO 动态请求数据传输对象 + * @param userId 用户ID + * @param postRequestDTO 动态数据传输对象 * @return 创建的动态对象 */ @Override - public Post createPost(PostRequestDTO postRequestDTO) { + public Post createPost(Long userId, PostRequestDTO postRequestDTO) { + + // 创建动态对象 + Post post = new Post(); + post.setUserId(userId); + post.setContent(postRequestDTO.getContent()); + post.setTags(postRequestDTO.getTags()); + post.setMediaOssKeys(new ArrayList<>()); + // 如果有传入的媒体链接,则使用它们 + if (postRequestDTO.getMediaOssKeys() != null && !postRequestDTO.getMediaOssKeys().isEmpty()) { + post.setMediaOssKeys(postRequestDTO.getMediaOssKeys()); + } // 1. 文本内容审核 Map textResult; @@ -97,37 +111,39 @@ public class PostServiceImpl implements PostService { } // 2. 图片审核(如果有) - if (postRequestDTO.getMediaUrls() != null && !postRequestDTO.getMediaUrls().isEmpty()) { + if (postRequestDTO.getMediaOssKeys() != null && !postRequestDTO.getMediaOssKeys().isEmpty()) { Map imageResult; try { - imageResult = greenImageScan.imageScan(postRequestDTO.getMediaUrls()); + imageResult = greenImageScan.imageScan(postRequestDTO.getMediaOssKeys()); } catch (Exception e) { throw new RuntimeException(e); } + // 图片审核结果 + String imageSuggestion = (String) imageResult.get("suggestion"); - if ("block".equals(imageResult.get("suggestion"))) { - throw new RuntimeException("图片内容违规,禁止发布"); + // 根据审核结果设置状态 + if ("block".equals(textSuggestion) || "block".equals(imageSuggestion)) { + // 审核未通过,允许用户修改 + post.setIsPublic(2); + } else if ("review".equals(textSuggestion) || "review".equals(imageSuggestion)) { + // 待审核,需人工审核 + post.setIsPublic(1); + } else { + // 审核通过 + post.setIsPublic(0); } - } - - Post post = new Post(); - post.setUserId(Long.valueOf(postRequestDTO.getUserId())); - post.setContent(postRequestDTO.getContent()); - post.setTags(postRequestDTO.getTags()); - post.setMediaOssKeys(new ArrayList<>()); - - // 如果有传入的媒体链接,则使用它们 - if (postRequestDTO.getMediaUrls() != null && !postRequestDTO.getMediaUrls().isEmpty()) { - post.setMediaOssKeys(postRequestDTO.getMediaUrls()); - } - - // 4. 根据审核结果设置状态 - if ("review".equals(textSuggestion)) { - // 待审核,需人工审核 - post.setIsPublic(0); } else { - // 审核通过 - post.setIsPublic(1); + // 只有文本内容的情况 + if ("block".equals(textSuggestion)) { + // 审核未通过,允许用户修改 + post.setIsPublic(2); + } else if ("review".equals(textSuggestion)) { + // 待审核,需人工审核 + post.setIsPublic(1); + } else { + // 审核通过 + post.setIsPublic(0); + } } post.setUpdatedAt(LocalDateTime.now()); post.setCreatedAt(LocalDateTime.now()); @@ -137,6 +153,11 @@ public class PostServiceImpl implements PostService { return post; } + @Override + public void deletePostById(Long postId) { + postMapper.deletePostById(postId); + } + /** * 删除动态 * @@ -145,6 +166,105 @@ public class PostServiceImpl implements PostService { */ @Override public void deletePostById(Integer postId) { - postMapper.deletePostById(postId); + postMapper.deletePostById(Long.valueOf(postId)); } + + @Override + public PostEditVO getPostForEdit(Long postId) { + + Post post = postMapper.selectById(postId); + if (post == null) { + throw new RuntimeException("动态不存在"); + } + PostEditVO postEditVO = new PostEditVO(); + BeanUtils.copyProperties(post, postEditVO); + return postEditVO; + } + + /** + * 修改动态 + * @param postId 动态ID + * @param postRequestDTO 修改的动态数据传输对象 + * @return + */ + @Override + public PostEditVO updatePost(Long postId, PostRequestDTO postRequestDTO) { + // 查询动态 + Post post = postMapper.selectById(postId); + if (post == null) { + throw new RuntimeException("动态不存在"); + } + post.setContent(postRequestDTO.getContent()); + if (postRequestDTO.getMediaOssKeys() != null && !postRequestDTO.getMediaOssKeys().isEmpty()) { + post.setMediaOssKeys(postRequestDTO.getMediaOssKeys()); + } + + // 1. 文本内容审核 + Map textResult; + try { + textResult = greenTextScan.greeTextScan(postRequestDTO.getContent()); + } catch (Exception e) { + throw new RuntimeException("文本审核失败"); + } + // 文本审核结果 + String textSuggestion = (String) textResult.get("suggestion"); + + // 2. 图片审核(如果有) + if (postRequestDTO.getMediaOssKeys() != null && !postRequestDTO.getMediaOssKeys().isEmpty()) { + Map imageResult; + try { + imageResult = greenImageScan.imageScan(postRequestDTO.getMediaOssKeys()); + } catch (Exception e) { + throw new RuntimeException(e); + } + // 图片审核结果 + String imageSuggestion = (String) imageResult.get("suggestion"); + + // 根据审核结果设置状态 + if ("block".equals(textSuggestion) || "block".equals(imageSuggestion)) { + // 审核未通过,允许用户修改 + post.setIsPublic(2); + } else if ("review".equals(textSuggestion) || "review".equals(imageSuggestion)) { + // 待审核,需人工审核 + post.setIsPublic(1); + } else { + // 审核通过 + post.setIsPublic(0); + } + } else { + // 只有文本内容的情况 + if ("block".equals(textSuggestion)) { + // 审核未通过,允许用户修改 + post.setIsPublic(2); + } else if ("review".equals(textSuggestion)) { + // 待审核,需人工审核 + post.setIsPublic(1); + } else { + // 审核通过 + post.setIsPublic(0); + } + } + post.setTags(postRequestDTO.getTags()); + post.setUpdatedAt(LocalDateTime.now()); + + // 更新动态 + postMapper.updateById(post); + + // 返回动态详情 + PostEditVO postEditVO = new PostEditVO(); + BeanUtils.copyProperties(post, postEditVO); + return postEditVO; + } + + /** + * 根据动态ID查询用户ID + * + * @param postId 动态ID + * @return 用户ID + */ + @Override + public Long selectUserIdByPostId(Long postId) { + return postMapper.selectUserIdByPostId(postId); + } + } \ No newline at end of file diff --git a/src/main/java/com/bao/dating/util/EmailUtil.java b/src/main/java/com/bao/dating/util/EmailUtil.java new file mode 100644 index 0000000..afcb813 --- /dev/null +++ b/src/main/java/com/bao/dating/util/EmailUtil.java @@ -0,0 +1,178 @@ +package com.bao.dating.util; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.stereotype.Component; + +import javax.mail.MessagingException; +import javax.mail.internet.MimeMessage; +import java.io.File; + +/** + * 邮箱工具类 + * 提供邮件发送功能(支持普通文本邮件和HTML邮件) + * @author KilLze + */ +@Slf4j +@Component +public class EmailUtil { + + /** + * 邮件发送器(由Spring自动注入) + */ + private final JavaMailSender mailSender; + + /** + * 发件人邮箱地址(从配置中读取) + */ + private final String from; + + /** + * 构造函数,用于注入JavaMailSender和配置 + * @param mailSender 邮件发送器 + * @param from 发件人邮箱地址 + */ + public EmailUtil(JavaMailSender mailSender, @Value("${spring.mail.username}") String from) { + this.mailSender = mailSender; + this.from = from; + } + + /** + * 发送简单文本邮件 + * @param to 收件人邮箱地址 + * @param subject 邮件主题 + * @param text 邮件内容 + * @return 是否发送成功 + */ + public boolean sendSimpleMail(String to, String subject, String text) { + try { + SimpleMailMessage message = new SimpleMailMessage(); + message.setFrom(from); + message.setTo(to); + message.setSubject(subject); + message.setText(text); + mailSender.send(message); + log.info("简单邮件发送成功,收件人:{},主题:{}", to, subject); + return true; + } catch (Exception e) { + log.error("简单邮件发送失败,收件人:{},异常信息:{}", to, e.getMessage(), e); + return false; + } + } + + /** + * 发送HTML格式邮件 + * @param to 收件人邮箱地址 + * @param subject 邮件主题 + * @param htmlContent HTML内容 + * @return 是否发送成功 + */ + public boolean sendHtmlMail(String to, String subject, String htmlContent) { + try { + MimeMessage message = mailSender.createMimeMessage(); + MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); + helper.setFrom(from); + helper.setTo(to); + helper.setSubject(subject); + helper.setText(htmlContent, true); + mailSender.send(message); + log.info("HTML邮件发送成功,收件人:{},主题:{}", to, subject); + return true; + } catch (MessagingException e) { + log.error("HTML邮件发送失败,收件人:{},异常信息:{}", to, e.getMessage(), e); + return false; + } + } + + /** + * 发送带附件的邮件 + * @param to 收件人邮箱地址 + * @param subject 邮件主题 + * @param text 邮件内容 + * @param attachmentPath 附件文件路径 + * @param attachmentName 附件显示名称 + * @return 是否发送成功 + */ + public boolean sendMailWithAttachment(String to, String subject, String text, + String attachmentPath, String attachmentName) { + try { + MimeMessage message = mailSender.createMimeMessage(); + MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); + helper.setFrom(from); + helper.setTo(to); + helper.setSubject(subject); + helper.setText(text); + + File file = new File(attachmentPath); + if (file.exists()) { + helper.addAttachment(attachmentName, file); + } else { + log.warn("附件文件不存在:{}", attachmentPath); + return false; + } + + mailSender.send(message); + log.info("带附件邮件发送成功,收件人:{},主题:{},附件:{}", to, subject, attachmentName); + return true; + } catch (MessagingException e) { + log.error("带附件邮件发送失败,收件人:{},异常信息:{}", to, e.getMessage(), e); + return false; + } + } + + /** + * 发送验证码邮件 + * @param to 收件人邮箱地址 + * @param code 验证码 + * @return 是否发送成功 + */ + public boolean sendVerificationCode(String to, String code) { + String subject = "验证码"; + String htmlContent = String.format( + "" + + "

您的验证码

" + + "

验证码:%s

" + + "

验证码有效期为10分钟,请勿泄露给他人。

" + + "", code); + return sendHtmlMail(to, subject, htmlContent); + } + + /** + * 发送验证码邮件(自定义主题) + * @param to 收件人邮箱地址 + * @param code 验证码 + * @param subject 邮件主题 + * @return 是否发送成功 + */ + public boolean sendVerificationCode(String to, String code, String subject) { + String htmlContent = String.format( + "" + + "

您的验证码

" + + "

验证码:%s

" + + "

验证码有效期为10分钟,请勿泄露给他人。

" + + "", code); + return sendHtmlMail(to, subject, htmlContent); + } + + /** + * 批量发送邮件 + * @param toList 收件人邮箱地址数组 + * @param subject 邮件主题 + * @param text 邮件内容 + * @return 成功发送的数量 + */ + public int sendBatchMail(String[] toList, String subject, String text) { + int successCount = 0; + for (String to : toList) { + if (sendSimpleMail(to, subject, text)) { + successCount++; + } + } + log.info("批量邮件发送完成,总数:{},成功:{}", toList.length, successCount); + return successCount; + } +} + diff --git a/src/main/java/com/bao/dating/util/JwtUtil.java b/src/main/java/com/bao/dating/util/JwtUtil.java new file mode 100644 index 0000000..002b887 --- /dev/null +++ b/src/main/java/com/bao/dating/util/JwtUtil.java @@ -0,0 +1,204 @@ +package com.bao.dating.util; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; + +import javax.crypto.SecretKey; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * JWT工具类 + * 提供JWT Token的生成、解析和验证功能 + * @author KilLze + */ +public class JwtUtil { + + /** + * 默认密钥(建议从配置文件读取) + */ + private static final String DEFAULT_SECRET = "dating-application-secret-key-for-jwt-token-generation-2025"; + + /** + * 默认过期时间(毫秒)- 7天 + */ + private static final long DEFAULT_EXPIRATION = 7 * 24 * 60 * 60 * 1000L; + + /** + * 生成JWT Token + * @param subject 主题(通常是用户ID或用户名) + * @return JWT Token字符串 + */ + public static String generateToken(String subject) { + return generateToken(subject, DEFAULT_EXPIRATION); + } + + /** + * 生成JWT Token(自定义过期时间) + * @param subject 主题(通常是用户ID或用户名) + * @param expiration 过期时间(毫秒) + * @return JWT Token字符串 + */ + public static String generateToken(String subject, long expiration) { + return generateToken(subject, expiration, null); + } + + /** + * 生成JWT Token(带自定义claims) + * @param subject 主题(通常是用户ID或用户名) + * @param expiration 过期时间(毫秒) + * @param claims 自定义claims + * @return JWT Token字符串 + */ + public static String generateToken(String subject, long expiration, Map claims) { + Date now = new Date(); + Date expiryDate = new Date(now.getTime() + expiration); + + SecretKey key = getSecretKey(); + + if (claims == null) { + claims = new HashMap<>(); + } + + return Jwts.builder() + .setClaims(claims) + .setSubject(subject) + .setIssuedAt(now) + .setExpiration(expiryDate) + .signWith(key, SignatureAlgorithm.HS256) + .compact(); + } + + /** + * 生成JWT Token(使用自定义密钥) + * @param subject 主题 + * @param expiration 过期时间(毫秒) + * @param secret 自定义密钥 + * @return JWT Token字符串 + */ + public static String generateTokenWithSecret(String subject, long expiration, String secret) { + Date now = new Date(); + Date expiryDate = new Date(now.getTime() + expiration); + + SecretKey key = getSecretKey(secret); + + return Jwts.builder() + .setSubject(subject) + .setIssuedAt(now) + .setExpiration(expiryDate) + .signWith(key, SignatureAlgorithm.HS256) + .compact(); + } + + /** + * 从Token中获取Claims + * @param token JWT Token + * @return Claims对象 + */ + public static Claims getClaimsFromToken(String token) { + return getClaimsFromToken(token, DEFAULT_SECRET); + } + + /** + * 从Token中获取Claims(使用自定义密钥) + * @param token JWT Token + * @param secret 密钥 + * @return Claims对象 + */ + public static Claims getClaimsFromToken(String token, String secret) { + try { + SecretKey key = getSecretKey(secret); + return Jwts.parserBuilder() + .setSigningKey(key) + .build() + .parseClaimsJws(token) + .getBody(); + } catch (Exception e) { + throw new RuntimeException("解析JWT Token失败", e); + } + } + + /** + * 从Token中获取主题(通常是用户ID) + * @param token JWT Token + * @return 主题字符串 + */ + public static String getSubjectFromToken(String token) { + Claims claims = getClaimsFromToken(token); + return claims.getSubject(); + } + + /** + * 验证Token是否有效 + * @param token JWT Token + * @return 是否有效 + */ + public static boolean validateToken(String token) { + return validateToken(token, DEFAULT_SECRET); + } + + /** + * 验证Token是否有效(使用自定义密钥) + * @param token JWT Token + * @param secret 密钥 + * @return 是否有效 + */ + public static boolean validateToken(String token, String secret) { + try { + Claims claims = getClaimsFromToken(token, secret); + return !isTokenExpired(claims); + } catch (Exception e) { + return false; + } + } + + /** + * 检查Token是否过期 + * @param claims Claims对象 + * @return 是否过期 + */ + private static boolean isTokenExpired(Claims claims) { + Date expiration = claims.getExpiration(); + return expiration.before(new Date()); + } + + /** + * 获取Token的过期时间 + * @param token JWT Token + * @return 过期时间 + */ + public static Date getExpirationDateFromToken(String token) { + Claims claims = getClaimsFromToken(token); + return claims.getExpiration(); + } + + /** + * 获取默认密钥的SecretKey对象 + * @return SecretKey对象 + */ + private static SecretKey getSecretKey() { + return getSecretKey(DEFAULT_SECRET); + } + + /** + * 根据字符串密钥生成SecretKey对象 + * @param secret 密钥字符串 + * @return SecretKey对象 + */ + private static SecretKey getSecretKey(String secret) { + // 确保密钥长度至少为256位(32字节)以支持HS256算法 + byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8); + if (keyBytes.length < 32) { + // 如果密钥太短,进行填充 + byte[] paddedKey = new byte[32]; + System.arraycopy(keyBytes, 0, paddedKey, 0, Math.min(keyBytes.length, 32)); + return Keys.hmacShaKeyFor(paddedKey); + } + return Keys.hmacShaKeyFor(keyBytes); + } +} + diff --git a/src/main/java/com/bao/dating/util/MD5Util.java b/src/main/java/com/bao/dating/util/MD5Util.java new file mode 100644 index 0000000..cfc5518 --- /dev/null +++ b/src/main/java/com/bao/dating/util/MD5Util.java @@ -0,0 +1,91 @@ +package com.bao.dating.util; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * MD5工具类 + * 提供MD5加密功能 + * @author KilLze + */ +public class MD5Util { + + /** + * 对字符串进行MD5加密 + * @param input 待加密的字符串 + * @return MD5加密后的32位小写字符串 + */ + public static String encrypt(String input) { + if (input == null || input.isEmpty()) { + return null; + } + + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] digest = md.digest(input.getBytes()); + return bytesToHex(digest); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("MD5算法不可用", e); + } + } + + /** + * 对字符串进行MD5加密(带盐值) + * @param input 待加密的字符串 + * @param salt 盐值 + * @return MD5加密后的32位小写字符串 + */ + public static String encryptWithSalt(String input, String salt) { + if (input == null || input.isEmpty()) { + return null; + } + if (salt == null) { + salt = ""; + } + return encrypt(input + salt); + } + + /** + * 验证字符串与MD5值是否匹配 + * @param input 原始字符串 + * @param md5Hash MD5哈希值 + * @return 是否匹配 + */ + public static boolean verify(String input, String md5Hash) { + if (input == null || md5Hash == null) { + return false; + } + return encrypt(input).equalsIgnoreCase(md5Hash); + } + + /** + * 验证字符串与MD5值是否匹配(带盐值) + * @param input 原始字符串 + * @param salt 盐值 + * @param md5Hash MD5哈希值 + * @return 是否匹配 + */ + public static boolean verifyWithSalt(String input, String salt, String md5Hash) { + if (input == null || md5Hash == null) { + return false; + } + if (salt == null) { + salt = ""; + } + return encryptWithSalt(input, salt).equalsIgnoreCase(md5Hash); + } + + /** + * 将字节数组转换为十六进制字符串 + * @param bytes 字节数组 + * @return 十六进制字符串 + */ + private static String bytesToHex(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } +} + diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8ab6301..8bcdeac 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -7,6 +7,25 @@ spring: username: root password: JoyeeServe2025 driver-class-name: com.mysql.cj.jdbc.Driver + # 邮箱SMTP配置 + mail: + host: smtp.163.com # QQ邮箱SMTP服务器地址 + port: 465 # SMTP端口 + username: 19271189822@163.com # 发件人邮箱 + password: CAwXh39PXajy3fyH # 邮箱授权码 + default-encoding: UTF-8 + properties: + mail: + smtp: + auth: true + starttls: + enable: true + required: true + ssl: + enable: true # 使用587端口时设为false,使用465端口时设为true + connectiontimeout: 10000 # 连接超时时间(毫秒) + timeout: 10000 # 读取超时时间(毫秒) + writetimeout: 10000 # 写入超时时间(毫秒) # MyBatis 配置 mybatis: @@ -20,9 +39,16 @@ mybatis: aliyun: oss: endpoint: oss-cn-beijing.aliyuncs.com - access-key-id: LTAI5tKo9TpWH1aW6JxWm1Gp - access-key-secret: LHk9DdHECKCwIdaIM9fkGgEuowt18W - bucket-name: heimato + access-key-id: LTAI5t5vpcbCZwweNHEDDDaF + access-key-secret: bBHBAPiCqGyVBHUv07348wsHXkKqrk + bucket-name: heimatoo accessKeyId: LTAI5t5vpcbCZwweNHEDDDaF secret: bBHBAPiCqGyVBHUv07348wsHXkKqrk scenes: antispam + # 阿里云短信服务配置 +# sms: +# access-key-id: LTAI5t5vpcbCZwweNHEDDDaF +# access-key-secret: bBHBAPiCqGyVBHUv07348wsHXkKqrk +# region-id: cn-hangzhou +# sign-name: +# template-code: SMS_123456789 diff --git a/src/main/resources/com/bao/dating/mapper/PostFavoriteMapper.xml b/src/main/resources/com/bao/dating/mapper/PostFavoriteMapper.xml new file mode 100644 index 0000000..41b5195 --- /dev/null +++ b/src/main/resources/com/bao/dating/mapper/PostFavoriteMapper.xml @@ -0,0 +1,17 @@ + + + + + + insert into post_favorite values (null,#{userId},#{postId},#{createdAt}) + + + + delete from post_favorite where post_id = #{postId} + + + + \ No newline at end of file diff --git a/src/main/resources/com/bao/dating/mapper/PostMapper.xml b/src/main/resources/com/bao/dating/mapper/PostMapper.xml index fcb9c1d..54e4911 100644 --- a/src/main/resources/com/bao/dating/mapper/PostMapper.xml +++ b/src/main/resources/com/bao/dating/mapper/PostMapper.xml @@ -29,10 +29,60 @@ - + DELETE FROM post WHERE post_id = #{postId} + + + + + + + + + + + + + + + + + + + UPDATE post + + + content = #{content}, + + + tags = #{tags, typeHandler=com.bao.dating.handler.ListToVarcharTypeHandler}, + + + media_oss_keys = + #{mediaOssKeys, typeHandler=com.bao.dating.handler.ListToVarcharTypeHandler}, + + is_public = #{isPublic}, + updated_at = #{updatedAt} + + WHERE post_id = #{postId} + + update dating.post set like_count = like_count + 1 where post.post_id = #{postId} @@ -42,10 +92,26 @@ update dating.post set like_count= like_count - 1 where post.post_id = #{postId} + + + update post set favorite_count = favorite_count + 1 where post_id = #{post_id} + + + + update post set favorite_count = favorite_count - 1 where post_id = #{post_id} + + + + + \ No newline at end of file diff --git a/src/test/java/com/bao/dating/DatingApplicationTests.java b/src/test/java/com/bao/dating/DatingApplicationTests.java index 852efdf..46325b6 100644 --- a/src/test/java/com/bao/dating/DatingApplicationTests.java +++ b/src/test/java/com/bao/dating/DatingApplicationTests.java @@ -31,10 +31,7 @@ public class DatingApplicationTests { public void testUploadAndScanImageFromUrl() { try { // 阿里oss图片url - String imageUrl = "https://heimato.oss-cn-beijing.aliyuncs.com/75d067b3-8605-494a-a839-b0a9d32c993a.jpg" + - "?x-oss-credential=LTAI5tKo9TpWH1aW6JxWm1Gp%2F20251218%2Fcn-beijing%2Foss%2Faliyun_v4_request" + - "&x-oss-date=20251218T164128Z&x-oss-expires=32400&x-oss-signature-version=OSS4-HMAC-SHA256" + - "&x-oss-signature=e9c46381a5d33e5c541fe2e838ff3ebeeb5dbc51f792ed31ae992aa0a4109d8a"; + String imageUrl = "https://heimatoo.oss-cn-beijing.aliyuncs.com/ScreenShot_2025-12-01_135624_491.png?x-oss-credential=LTAI5t5vpcbCZwweNHEDDDaF%2F20251222%2Fcn-beijing%2Foss%2Faliyun_v4_request&x-oss-date=20251222T092951Z&x-oss-expires=32400&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-signature=c06742a4e530c7a095aa4c376c3c88e605dddcd232d0ee07da76988e5ccb7727"; // 将图片URL添加到列表中进行检测 List imageUrls = Arrays.asList(imageUrl); diff --git a/src/test/java/com/bao/dating/util/EmailAndSmsTest.java b/src/test/java/com/bao/dating/util/EmailAndSmsTest.java new file mode 100644 index 0000000..49c80d9 --- /dev/null +++ b/src/test/java/com/bao/dating/util/EmailAndSmsTest.java @@ -0,0 +1,75 @@ +package com.bao.dating.util; + +import com.bao.dating.common.aliyun.SmsUtil; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.HashMap; +import java.util.Map; + +/** + * 邮箱和短信发送测试类 + * @author KilLze + */ +@SpringBootTest +public class EmailAndSmsTest { + + @Autowired + private EmailUtil emailUtil; + + @Autowired + private SmsUtil smsUtil; + + /** + * 测试发送简单文本邮件 + */ + @Test + public void testSendSimpleMail() { + String to = "n_1127@qq.com"; + String subject = "测试邮件 - 简单文本"; + String text = "这是一封测试邮件,用于测试简单文本邮件发送功能。\n\n如果您收到此邮件,说明邮件发送功能正常。"; + + boolean result = emailUtil.sendSimpleMail(to, subject, text); + System.out.println("简单邮件发送结果: " + (result ? "成功" : "失败")); + } + + /** + * 测试发送HTML格式邮件 + */ + @Test + public void testSendHtmlMail() { + String to = "n_1127@qq.com"; + String subject = "测试邮件 - HTML格式"; + String htmlContent = "" + + "

欢迎使用我们的服务

" + + "

这是一封HTML格式的测试邮件。

" + + "

邮件内容支持:

" + + "
    " + + "
  • HTML标签
  • " + + "
  • 样式设置
  • " + + "
  • 富文本内容
  • " + + "
" + + "

感谢您的使用!

" + + ""; + + boolean result = emailUtil.sendHtmlMail(to, subject, htmlContent); + System.out.println("HTML邮件发送结果: " + (result ? "成功" : "失败")); + } + + /** + * 测试发送验证码邮件 + */ + @Test + public void testSendVerificationCodeEmail() { + String to = "n_1127@qq.com"; + String code = "123456"; + + boolean result = emailUtil.sendVerificationCode(to, code); + System.out.println("验证码邮件发送结果: " + (result ? "成功" : "失败")); + System.out.println("验证码: " + code); + } + + +} +