parent
004a0c6fa7
commit
9f16537549
|
@ -8,7 +8,6 @@ import com.guwan.backend.security.CustomUserDetails;
|
|||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
|
@ -31,6 +30,7 @@ import java.util.Arrays;
|
|||
public class OperationLogAspect {
|
||||
|
||||
private final SysLogMapper sysLogMapper;
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
|
@ -83,8 +83,7 @@ public class OperationLogAspect {
|
|||
|
||||
// 获取当前用户信息
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails) {
|
||||
CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
|
||||
if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails userDetails) {
|
||||
sysLog.setUserId(userDetails.getUserId());
|
||||
sysLog.setUsername(userDetails.getUsername());
|
||||
}
|
||||
|
|
|
@ -122,17 +122,7 @@ public class UserController {
|
|||
return Result.success("手机验证码发送成功");
|
||||
}
|
||||
|
||||
@PostMapping("/email/code")
|
||||
public Result<Void> sendEmailCode(
|
||||
@RequestParam @Email(message = "邮箱格式不正确") String email) {
|
||||
try {
|
||||
userService.sendEmailCode(email);
|
||||
return Result.success();
|
||||
} catch (Exception e) {
|
||||
log.error("发送邮箱验证码失败", e);
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,42 +1,10 @@
|
|||
package com.guwan.backend.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.guwan.backend.entity.SysLog;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface SysLogMapper extends BaseMapper<SysLog> {
|
||||
|
||||
/**
|
||||
* 分页查询日志
|
||||
*/
|
||||
@Select("SELECT * FROM sys_log WHERE user_id = #{userId} ORDER BY create_time DESC")
|
||||
IPage<SysLog> selectPageByUserId(Page<SysLog> page, @Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 查询指定时间范围内的日志
|
||||
*/
|
||||
@Select("SELECT * FROM sys_log WHERE create_time BETWEEN #{startTime} AND #{endTime}")
|
||||
List<SysLog> selectByTimeRange(@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime);
|
||||
|
||||
/**
|
||||
* 查询用户的操作统计
|
||||
*/
|
||||
@Select("SELECT operation, COUNT(*) as count FROM sys_log " +
|
||||
"WHERE user_id = #{userId} GROUP BY operation")
|
||||
List<OperationCount> selectUserOperationCount(@Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 查询错误日志
|
||||
*/
|
||||
@Select("SELECT * FROM sys_log WHERE status = 0 ORDER BY create_time DESC LIMIT #{limit}")
|
||||
List<SysLog> selectLatestErrors(@Param("limit") Integer limit);
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package com.guwan.backend.service;
|
||||
|
||||
public interface SmsService {
|
||||
/**
|
||||
* 发送验证码短信
|
||||
*/
|
||||
void sendVerificationCode(String phone, String code);
|
||||
|
||||
/**
|
||||
* 发送通知短信
|
||||
*/
|
||||
void sendNotification(String phone, String content);
|
||||
}
|
|
@ -3,10 +3,6 @@ package com.guwan.backend.service;
|
|||
import com.guwan.backend.dto.user.LoginDto;
|
||||
import com.guwan.backend.dto.user.RegisterDTO;
|
||||
import com.guwan.backend.dto.user.UserDTO;
|
||||
import com.guwan.backend.vo.user.EmailRegisterRequest;
|
||||
import com.guwan.backend.vo.user.LoginRequest;
|
||||
import com.guwan.backend.vo.user.PhoneRegisterRequest;
|
||||
import com.guwan.backend.vo.user.RegisterRequest;
|
||||
|
||||
public interface UserService {
|
||||
/**
|
||||
|
@ -29,8 +25,5 @@ public interface UserService {
|
|||
void changePassword(String oldPassword, String newPassword);
|
||||
void resetPassword(String email);
|
||||
public String refreshToken(String token);
|
||||
UserDTO registerByEmail(EmailRegisterRequest request);
|
||||
UserDTO registerByPhone(PhoneRegisterRequest request);
|
||||
void sendEmailCode(String email);
|
||||
void sendPhoneCode(String phone);
|
||||
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
package com.guwan.backend.service.impl;
|
||||
|
||||
import com.aliyun.dysmsapi20170525.Client;
|
||||
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
|
||||
import com.aliyun.teaopenapi.models.Config;
|
||||
import com.guwan.backend.service.SmsService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
@Slf4j
|
||||
public class AliyunSmsServiceImpl implements SmsService {
|
||||
|
||||
@Value("${aliyun.sms.access-key-id}")
|
||||
private String accessKeyId;
|
||||
|
||||
@Value("${aliyun.sms.access-key-secret}")
|
||||
private String accessKeySecret;
|
||||
|
||||
@Value("${aliyun.sms.sign-name}")
|
||||
private String signName;
|
||||
|
||||
@Value("${aliyun.sms.template-code}")
|
||||
private String templateCode;
|
||||
|
||||
private Client createClient() throws Exception {
|
||||
Config config = new Config()
|
||||
.setAccessKeyId(accessKeyId)
|
||||
.setAccessKeySecret(accessKeySecret)
|
||||
.setEndpoint("dysmsapi.aliyuncs.com");
|
||||
return new Client(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendVerificationCode(String phone, String code) {
|
||||
try {
|
||||
Client client = createClient();
|
||||
SendSmsRequest request = new SendSmsRequest()
|
||||
.setPhoneNumbers(phone)
|
||||
.setSignName(signName)
|
||||
.setTemplateCode(templateCode)
|
||||
.setTemplateParam("{\"code\":\"" + code + "\"}");
|
||||
|
||||
client.sendSms(request);
|
||||
log.info("短信验证码发送成功:{} -> {}", phone, code);
|
||||
} catch (Exception e) {
|
||||
log.error("短信发送失败:" + e.getMessage(), e);
|
||||
throw new RuntimeException("短信发送失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendNotification(String phone, String content) {
|
||||
// 实现通知短信发送
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package com.guwan.backend.service.impl;
|
||||
|
||||
import com.guwan.backend.service.SmsService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@Primary
|
||||
public class DummySmsServiceImpl implements SmsService {
|
||||
|
||||
@Override
|
||||
public void sendVerificationCode(String phone, String code) {
|
||||
log.info("模拟发送短信验证码到 {}: {}", phone, code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendNotification(String phone, String content) {
|
||||
log.info("模拟发送短信通知到 {}: {}", phone, content);
|
||||
}
|
||||
}
|
|
@ -14,15 +14,12 @@ import com.guwan.backend.service.VerificationService;
|
|||
import com.guwan.backend.util.JwtUtil;
|
||||
import com.guwan.backend.util.RedisUtil;
|
||||
import com.guwan.backend.util.RedisUtils;
|
||||
import com.guwan.backend.vo.user.EmailRegisterRequest;
|
||||
import com.guwan.backend.vo.user.PhoneRegisterRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
@ -154,6 +151,7 @@ public class UserServiceImpl implements UserService {
|
|||
return userDTO;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public UserDTO getCurrentUser() {
|
||||
Long userId = getCurrentUserId();
|
||||
|
@ -221,26 +219,6 @@ public class UserServiceImpl implements UserService {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDTO registerByEmail(EmailRegisterRequest request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDTO registerByPhone(PhoneRegisterRequest request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEmailCode(String email) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPhoneCode(String phone) {
|
||||
|
||||
}
|
||||
|
||||
private User findByUsername(String username) {
|
||||
return userMapper.selectOne(
|
||||
new LambdaQueryWrapper<User>()
|
||||
|
|
|
@ -1,122 +0,0 @@
|
|||
package com.guwan.backend.service.impl;
|
||||
|
||||
import com.guwan.backend.service.SmsService;
|
||||
import com.guwan.backend.service.VerificationService;
|
||||
import com.guwan.backend.util.RedisUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.mail.SimpleMailMessage;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class VerificationServiceImpl implements VerificationService {
|
||||
|
||||
private final RedisUtil redisUtil;
|
||||
private final JavaMailSender mailSender;
|
||||
private final SmsService smsService;
|
||||
|
||||
private static final String EMAIL_CODE_KEY = "verification:email:";
|
||||
private static final String SMS_CODE_KEY = "verification:sms:";
|
||||
private static final int CODE_EXPIRE_MINUTES = 5;
|
||||
private static final int CODE_LENGTH = 6;
|
||||
private static final String SMS_SEND_TIME_KEY = "sms:send:time:";
|
||||
private static final String SMS_COUNT_KEY = "sms:count:";
|
||||
private static final int MAX_DAILY_SMS_COUNT = 10;
|
||||
|
||||
@Override
|
||||
public void sendEmailCode(String email) {
|
||||
String code = generateCode();
|
||||
// 存储验证码
|
||||
redisUtil.set(EMAIL_CODE_KEY + email, code, CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);
|
||||
|
||||
// 发送邮件
|
||||
SimpleMailMessage message = new SimpleMailMessage();
|
||||
message.setTo(email);
|
||||
message.setSubject("注册验证码");
|
||||
message.setText("您的验证码是:" + code + "," + CODE_EXPIRE_MINUTES + "分钟内有效。");
|
||||
mailSender.send(message);
|
||||
|
||||
log.info("发送邮箱验证码:{} -> {}", email, code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSmsCode(String phone) {
|
||||
// 检查发送频率限制
|
||||
checkSendFrequency(phone);
|
||||
|
||||
String code = generateCode();
|
||||
// 存储验证码
|
||||
redisUtil.set(SMS_CODE_KEY + phone, code, CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);
|
||||
|
||||
// 发送短信
|
||||
smsService.sendVerificationCode(phone, code);
|
||||
|
||||
// 记录发送时间
|
||||
redisUtil.set(SMS_SEND_TIME_KEY + phone, System.currentTimeMillis(), 24, TimeUnit.HOURS);
|
||||
|
||||
log.info("发送短信验证码:{} -> {}", phone, code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifyEmailCode(String email, String code) {
|
||||
String key = EMAIL_CODE_KEY + email;
|
||||
String savedCode = (String) redisUtil.get(key);
|
||||
if (savedCode != null && savedCode.equals(code)) {
|
||||
redisUtil.delete(key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifySmsCode(String phone, String code) {
|
||||
String key = SMS_CODE_KEY + phone;
|
||||
String savedCode = (String) redisUtil.get(key);
|
||||
if (savedCode != null && savedCode.equals(code)) {
|
||||
redisUtil.delete(key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String generateCode() {
|
||||
Random random = new Random();
|
||||
StringBuilder code = new StringBuilder();
|
||||
for (int i = 0; i < CODE_LENGTH; i++) {
|
||||
code.append(random.nextInt(10));
|
||||
}
|
||||
return code.toString();
|
||||
}
|
||||
|
||||
private void checkSendFrequency(String phone) {
|
||||
// 检查是否在一分钟内重复发送
|
||||
String sendTimeKey = SMS_SEND_TIME_KEY + phone;
|
||||
Object lastSendTime = redisUtil.get(sendTimeKey);
|
||||
if (lastSendTime != null) {
|
||||
long timeDiff = System.currentTimeMillis() - (Long) lastSendTime;
|
||||
if (timeDiff < 60000) { // 60秒内不能重复发送
|
||||
throw new IllegalArgumentException("发送太频繁,请稍后再试");
|
||||
}
|
||||
}
|
||||
|
||||
// 检查当天发送次数
|
||||
String countKey = SMS_COUNT_KEY + phone;
|
||||
Integer count = (Integer) redisUtil.get(countKey);
|
||||
if (count != null && count >= MAX_DAILY_SMS_COUNT) {
|
||||
throw new IllegalArgumentException("今日发送次数已达上限");
|
||||
}
|
||||
|
||||
// 增加发送次数
|
||||
if (count == null) {
|
||||
redisUtil.set(countKey, 1, 24, TimeUnit.HOURS);
|
||||
} else {
|
||||
redisUtil.increment(countKey, 1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package com.guwan.backend.vo.user;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class EmailRegisterRequest {
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
@Email(message = "邮箱格式不正确")
|
||||
private String email;
|
||||
|
||||
@NotBlank(message = "验证码不能为空")
|
||||
private String code;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
|
||||
private String nickname;
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package com.guwan.backend.vo.user;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class FaceLoginRequest {
|
||||
private String faceImage; // Base64编码的人脸图片
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package com.guwan.backend.vo.user;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class LoginRequest {
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package com.guwan.backend.vo.user;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class PhoneRegisterRequest {
|
||||
@NotBlank(message = "手机号不能为空")
|
||||
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
||||
private String phone;
|
||||
|
||||
@NotBlank(message = "验证码不能为空")
|
||||
private String code;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
|
||||
private String nickname;
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package com.guwan.backend.vo.user;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RegisterRequest {
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_]{4,16}$", message = "用户名必须是4-16位字母、数字或下划线")
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,}$",
|
||||
message = "密码必须包含大小写字母和数字,且不少于8位")
|
||||
private String password;
|
||||
|
||||
@Email(message = "邮箱格式不正确")
|
||||
private String email;
|
||||
|
||||
@NotBlank(message = "邮箱验证码不能为空")
|
||||
private String emailCode;
|
||||
|
||||
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
||||
private String phone;
|
||||
|
||||
@NotBlank(message = "手机验证码不能为空")
|
||||
private String phoneCode;
|
||||
|
||||
}
|
|
@ -73,10 +73,10 @@ jwt:
|
|||
# 阿里云配置
|
||||
aliyun:
|
||||
sms:
|
||||
access-key-id: your-access-key-id
|
||||
access-key-secret: your-access-key-secret
|
||||
sign-name: your-sign-name
|
||||
template-code: SMS_123456789
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 文件上传配置
|
||||
file:
|
||||
|
|
Loading…
Reference in New Issue