9 Commits

Author SHA1 Message Date
bao
d20ce917a2 init: 合并代码 2025-12-24 23:55:00 +08:00
bao
d8b16e0287 Merge remote-tracking branch 'refs/remotes/origin/master' into feature-Post
# Conflicts:
#	src/main/java/com/bao/dating/controller/PostController.java
2025-12-24 23:54:29 +08:00
bao
d5ec858b06 init: 添加工具类 2025-12-24 23:13:50 +08:00
bao
7331553528 init: 优化代码 2025-12-24 21:34:13 +08:00
bao
6a51eb48e4 Merge branch 'refs/heads/dev' 2025-12-23 14:09:57 +08:00
bao
2fe875a6ae Merge remote-tracking branch 'refs/remotes/origin/feature-Comment' into dev 2025-12-23 14:07:54 +08:00
lihaiyang
c96af708df 评论功能 2025-12-23 13:48:40 +08:00
bao
fcdba51570 合并分区 2025-12-23 00:37:34 +08:00
bao
b1ea85b5e4 测试 like 2025-12-19 02:02:48 +08:00
24 changed files with 944 additions and 168 deletions

43
pom.xml
View File

@@ -19,6 +19,13 @@
<artifactId>spring-boot-starter</artifactId> <artifactId>spring-boot-starter</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
@@ -103,6 +110,38 @@
<version>0.2.8</version> <version>0.2.8</version>
</dependency> </dependency>
<!-- JWT 相关依赖 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<!-- 阿里云短信服务SDK -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>3.0.0</version>
</dependency>
<!-- Spring Mail 邮件发送 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
</dependencies> </dependencies>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
@@ -123,8 +162,8 @@
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version> <version>3.8.1</version>
<configuration> <configuration>
<source>1.8</source> <source>9</source>
<target>1.8</target> <target>9</target>
<encoding>UTF-8</encoding> <encoding>UTF-8</encoding>
</configuration> </configuration>
</plugin> </plugin>

View File

@@ -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; // 评论时间
}

View File

@@ -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; // 创建时间
}

View File

@@ -8,7 +8,8 @@ public enum ResultCode {
UNAUTHORIZED(401, "未登录或 Token 失效"), UNAUTHORIZED(401, "未登录或 Token 失效"),
FORBIDDEN(403, "无权限"), FORBIDDEN(403, "无权限"),
NOT_FOUND(404, "数据不存在"), NOT_FOUND(404, "数据不存在"),
SYSTEM_ERROR(500, "系统异常"); SYSTEM_ERROR(500, "系统异常"),
FAIL(500, "操作失败");
private final int code; private final int code;
private final String msg; private final String msg;

View File

@@ -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<String, String> params) {
StringBuilder jsonBuilder = new StringBuilder("{");
boolean first = true;
for (Map.Entry<String, String> 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());
}
}

View File

@@ -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<String> 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<String> 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<List<Comments>> getCommentList(@PathVariable Long post_id) {
List<Comments> commentList = commentsService.getCommentByPostId(post_id);
return Result.success(ResultCode.SUCCESS, commentList);
}
}

View File

@@ -1,39 +0,0 @@
package com.bao.dating.controller;
import com.bao.dating.pojo.entity.Notice;
import com.bao.dating.service.NoticeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 公告控制器
*/
@RestController
@RequestMapping("/notice")
public class NoticeController {
@Autowired
private NoticeService noticeService;
/**
* 查询所有公告数据
* @return 公告列表
*/
@GetMapping("/list")
public List<Notice> getNoticeList() {
return noticeService.getNoticeList();
}
/**
* 判断是否有公告数据
* @return true=有数据 false=无数据
*/
@GetMapping("/hasData")
public boolean hasNoticeData() {
List<Notice> noticeList = noticeService.getNoticeList();
return !noticeList.isEmpty();
}
}

View File

@@ -21,7 +21,7 @@ public class PostController {
private PostService postService; private PostService postService;
/** /**
* 上传媒体文件接口 like * 上传媒体文件接口
* @param files 媒体文件数组 * @param files 媒体文件数组
* @return 上传后的文件URL列表 * @return 上传后的文件URL列表
*/ */
@@ -33,14 +33,13 @@ public class PostController {
/** /**
* 发布动态接口 - JSON格式请求 * 发布动态接口 - JSON格式请求
* @param postDTO 动态信息 * @param postRequestDTO 动态信息
* @param userId 用户ID
* @return 发布的动态对象 * @return 发布的动态对象
*/ */
@PostMapping(consumes = "application/json") @PostMapping(consumes = "application/json")
public Result<Post> createPostJson(@RequestBody PostRequestDTO postDTO, @RequestParam Long userId) { public Result<Post> createPostJson(@RequestBody PostRequestDTO postRequestDTO) {
// 调用 Service 层处理发布动态业务逻辑 // 调用 Service 层处理发布动态业务逻辑
Post result = postService.createPost(userId, postDTO); Post result = postService.createPost(postRequestDTO);
return Result.success(ResultCode.SUCCESS_REVIEW, "动态发布成功,等待审核。", result); return Result.success(ResultCode.SUCCESS_REVIEW, "动态发布成功,等待审核。", result);
} }
@@ -51,7 +50,7 @@ public class PostController {
* @return 删除结果 * @return 删除结果
*/ */
@DeleteMapping("/{postId}") @DeleteMapping("/{postId}")
public Result<String> deleteById(@PathVariable Long postId){ public Result<String> deleteById(@PathVariable Integer postId){
postService.deletePostById(postId); postService.deletePostById(postId);
return Result.success(ResultCode.SUCCESS_DELETE, "动态删除成功", null); return Result.success(ResultCode.SUCCESS_DELETE, "动态删除成功", null);
} }

View File

@@ -1,4 +0,0 @@
package com.bao.dating.controller;
public class text {
}

View File

@@ -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<Comments> getCommentsByPostId(@Param("post_id") Long post_id);
}

View File

@@ -1,16 +0,0 @@
package com.bao.dating.mapper;
import com.bao.dating.pojo.entity.Notice;
import java.util.List;
/**
* 公告Mapper接口
*/
public interface NoticeMapper {
/**
* 查询所有公告
* @return 公告列表
*/
List<Notice> selectNoticeList();
}

View File

@@ -1,40 +0,0 @@
package com.bao.dating.pojo.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
/**
* 公告实体类
*/
@Data
public class Notice {
/**
* 公告ID
*/
private Long noticeId;
/**
* 公告标题
*/
private String noticeTitle;
/**
* 公告内容
*/
private String noticeContent;
/**
* 状态0正常 1关闭
*/
private String noticeStatus;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
}

View File

@@ -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<Comments> getCommentByPostId(Long post_id);
}

View File

@@ -1,16 +0,0 @@
package com.bao.dating.service;
import com.bao.dating.pojo.entity.Notice;
import java.util.List;
/**
* 公告服务接口
*/
public interface NoticeService {
/**
* 查询所有公告
* @return 公告列表
*/
List<Notice> getNoticeList();
}

View File

@@ -17,11 +17,11 @@ public interface PostService {
/** /**
* 创建动态 * 创建动态
* @param userId 用户ID *
* @param postRequestDTO 动态数据传输对象 * @param postRequestDTO 动态数据传输对象
* @return 创建的动态对象 * @return 创建的动态对象
*/ */
Post createPost(Long userId, PostRequestDTO postRequestDTO); Post createPost(PostRequestDTO postRequestDTO);
/** /**
* 删除动态 * 删除动态

View File

@@ -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<Comments> getCommentByPostId(Long post_id) {
return commentsMapper.getCommentsByPostId(post_id);
}
}

View File

@@ -1,18 +0,0 @@
package com.bao.dating.service.impl;
import com.bao.dating.mapper.NoticeMapper;
import com.bao.dating.pojo.entity.Notice;
import com.bao.dating.service.NoticeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class NoticeServiceImpl implements NoticeService {
@Autowired
private NoticeMapper noticeMapper;
@Override
public List<Notice> getNoticeList() {
return noticeMapper.selectNoticeList();
}
}

View File

@@ -79,12 +79,11 @@ public class PostServiceImpl implements PostService {
/** /**
* 创建动态 * 创建动态
* *
* @param userId 用户ID
* @param postRequestDTO 动态数据传输对象 * @param postRequestDTO 动态数据传输对象
* @return 创建的动态对象 * @return 创建的动态对象
*/ */
@Override @Override
public Post createPost(Long userId, PostRequestDTO postRequestDTO) { public Post createPost(PostRequestDTO postRequestDTO) {
// 创建动态对象 // 创建动态对象
Post post = new Post(); Post post = new Post();
@@ -166,7 +165,7 @@ public class PostServiceImpl implements PostService {
*/ */
@Override @Override
public void deletePostById(Integer postId) { public void deletePostById(Integer postId) {
postMapper.deletePostById(postId); postMapper.deletePostById(Long.valueOf(postId));
} }
@Override @Override

View File

@@ -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(
"<html><body>" +
"<h2>您的验证码</h2>" +
"<p>验证码:<strong style='color: #1890ff; font-size: 24px;'>%s</strong></p>" +
"<p>验证码有效期为10分钟请勿泄露给他人。</p>" +
"</body></html>", 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(
"<html><body>" +
"<h2>您的验证码</h2>" +
"<p>验证码:<strong style='color: #1890ff; font-size: 24px;'>%s</strong></p>" +
"<p>验证码有效期为10分钟请勿泄露给他人。</p>" +
"</body></html>", 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;
}
}

View File

@@ -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<String, Object> 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);
}
}

View File

@@ -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();
}
}

View File

@@ -7,6 +7,25 @@ spring:
username: root username: root
password: JoyeeServe2025 password: JoyeeServe2025
driver-class-name: com.mysql.cj.jdbc.Driver 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 配置
mybatis: mybatis:
@@ -26,3 +45,10 @@ aliyun:
accessKeyId: LTAI5t5vpcbCZwweNHEDDDaF accessKeyId: LTAI5t5vpcbCZwweNHEDDDaF
secret: bBHBAPiCqGyVBHUv07348wsHXkKqrk secret: bBHBAPiCqGyVBHUv07348wsHXkKqrk
scenes: antispam scenes: antispam
# 阿里云短信服务配置
# sms:
# access-key-id: LTAI5t5vpcbCZwweNHEDDDaF
# access-key-secret: bBHBAPiCqGyVBHUv07348wsHXkKqrk
# region-id: cn-hangzhou
# sign-name:
# template-code: SMS_123456789

View File

@@ -1,20 +0,0 @@
<?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.NoticeMapper">
<!-- 通用查询结果列 -->
<sql id="Notice_Column_List">
notice_id, notice_title, notice_content, notice_status, create_time, update_time
</sql>
<!-- 查询所有公告 -->
<select id="selectNoticeList" resultType="com.bao.dating.pojo.entity.Notice">
select
<include refid="Notice_Column_List"/>
from sys_notice
where notice_status = '0'
order by create_time desc
</select>
</mapper>

View File

@@ -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><body>" +
"<h2 style='color: #1890ff;'>欢迎使用我们的服务</h2>" +
"<p>这是一封<strong>HTML格式</strong>的测试邮件。</p>" +
"<p>邮件内容支持:</p>" +
"<ul>" +
"<li>HTML标签</li>" +
"<li>样式设置</li>" +
"<li>富文本内容</li>" +
"</ul>" +
"<p style='color: #999;'>感谢您的使用!</p>" +
"</body></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);
}
}