✨ 完成实验部分后端
This commit is contained in:
parent
086c034a95
commit
a35ecd8a2f
|
@ -31,3 +31,5 @@ build/
|
||||||
|
|
||||||
### VS Code ###
|
### VS Code ###
|
||||||
.vscode/
|
.vscode/
|
||||||
|
/src/main/resources/application-dev.yml
|
||||||
|
/src/main/resources/dev.application.properties
|
||||||
|
|
32
pom.xml
32
pom.xml
|
@ -61,6 +61,38 @@
|
||||||
<version>3.0.4</version>
|
<version>3.0.4</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||||
|
<version>3.5.11</version>
|
||||||
|
</dependency>
|
||||||
|
<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>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.qcloud</groupId>
|
||||||
|
<artifactId>cos_api</artifactId>
|
||||||
|
<version>5.6.227</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.sun.mail</groupId>
|
||||||
|
<artifactId>jakarta.mail</artifactId>
|
||||||
|
<version>2.0.1</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
package top.lichx.webclassbackend.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "mail.smtp")
|
||||||
|
public class MailProperties {
|
||||||
|
private String host;
|
||||||
|
private int port;
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
private boolean auth;
|
||||||
|
private boolean starttls;
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public String getHost() {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHost(String host) {
|
||||||
|
this.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPort(int port) {
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAuth() {
|
||||||
|
return auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuth(boolean auth) {
|
||||||
|
this.auth = auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isStarttls() {
|
||||||
|
return starttls;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStarttls(boolean starttls) {
|
||||||
|
this.starttls = starttls;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package top.lichx.webclassbackend.config;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
import top.lichx.webclassbackend.interceptor.JwtInterceptor;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Slf4j
|
||||||
|
public class WebConfig implements WebMvcConfigurer {
|
||||||
|
@Resource
|
||||||
|
private JwtInterceptor jwtInterceptor;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
|
log.info("addInterceptors");
|
||||||
|
registry.addInterceptor(jwtInterceptor)
|
||||||
|
.addPathPatterns("/**") // 拦截所有路径
|
||||||
|
.excludePathPatterns("/login/**") // 放行登录接口
|
||||||
|
.excludePathPatterns("/file/uploadAvatar") // 放行头像上传接口
|
||||||
|
.excludePathPatterns("/error/**"); // 放行错误处理接口
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package top.lichx.webclassbackend.controller;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import top.lichx.webclassbackend.pojo.vo.AvatarInfo;
|
||||||
|
import top.lichx.webclassbackend.result.Result;
|
||||||
|
import top.lichx.webclassbackend.util.COSIO;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/file")
|
||||||
|
public class FileController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private COSIO cosio;
|
||||||
|
|
||||||
|
@GetMapping("/test")
|
||||||
|
public String test() {
|
||||||
|
// 使用注入的 COSIO 实例
|
||||||
|
System.out.println("COSIO: " + cosio);
|
||||||
|
return "test";
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PostMapping("/upload")
|
||||||
|
// public Result<?> uploadFile(@RequestParam("file") MultipartFile multipartFile) {
|
||||||
|
// try {
|
||||||
|
// // 将 MultipartFile 转换为 File
|
||||||
|
// var file = File.createTempFile("upload-", multipartFile.getOriginalFilename());
|
||||||
|
// multipartFile.transferTo(file);
|
||||||
|
//
|
||||||
|
// // 调用 COSIO 的上传方法
|
||||||
|
// String filename = cosio.UploadFile(file);
|
||||||
|
//
|
||||||
|
// // 删除临时文件
|
||||||
|
// file.delete();
|
||||||
|
//
|
||||||
|
// return Result.success("文件上传成功,文件路径:", filename);
|
||||||
|
// } catch (IOException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// // 处理文件上传失败的情况
|
||||||
|
// return Result.error("文件上传失败:" + e.getMessage());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/uploadAvatar")
|
||||||
|
public Result<?> uploadAvatar(@RequestParam("file") MultipartFile multipartFile) throws IOException {
|
||||||
|
var file = File.createTempFile("upload-", multipartFile.getOriginalFilename());
|
||||||
|
multipartFile.transferTo(file);
|
||||||
|
var fileName = cosio.UploadFile(file);
|
||||||
|
file.delete();
|
||||||
|
var fileURL = cosio.GetFileLink(fileName);
|
||||||
|
var avatarInfo = new AvatarInfo(fileName, fileURL);
|
||||||
|
return Result.success("文件上传成功", avatarInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/getFileLink")
|
||||||
|
public Result<?> getFileLink(@RequestParam String fileName) {
|
||||||
|
URL url = cosio.GetFileLink(fileName);
|
||||||
|
return Result.success("获取文件链接成功", url.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,287 @@
|
||||||
|
package top.lichx.webclassbackend.controller;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.mail.MessagingException;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import top.lichx.webclassbackend.pojo.dto.LoginDTO;
|
||||||
|
import top.lichx.webclassbackend.pojo.dto.PasswordResetDTO;
|
||||||
|
import top.lichx.webclassbackend.pojo.dto.RegisterDTO;
|
||||||
|
import top.lichx.webclassbackend.pojo.entity.User;
|
||||||
|
import top.lichx.webclassbackend.result.Result;
|
||||||
|
import top.lichx.webclassbackend.service.IdentifyingCode;
|
||||||
|
import top.lichx.webclassbackend.service.UserService;
|
||||||
|
import top.lichx.webclassbackend.util.JwtUtil;
|
||||||
|
import top.lichx.webclassbackend.util.MailUtil;
|
||||||
|
import top.lichx.webclassbackend.util.PasswordUtil;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/login")
|
||||||
|
public class Login {
|
||||||
|
|
||||||
|
private String mailTemplate = """
|
||||||
|
<body>
|
||||||
|
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="p-80 mpy-35 mpx-15" bgcolor="#FFFFFF" style="padding: 80px;">
|
||||||
|
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td
|
||||||
|
style="font-size:20px; line-height:42px; font-family:Arial, sans-serif, 'Motiva Sans'; text-align:left; padding-bottom: 30px; color:#002333; font-weight:bold;">
|
||||||
|
<span style="color: #007aff;">您好</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="text-18 c-grey4 pb-30"
|
||||||
|
style="font-size:18px; line-height:25px; font-family:Arial, sans-serif, 'Motiva Sans'; text-align:left; color:#002333; padding-bottom: 30px;">
|
||||||
|
{tip},有效时间为 {ExpireTime} 分钟。
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="pb-70 mpb-50" style="padding-bottom: 70px;">
|
||||||
|
<table width="100%" border="0" cellspacing="0" cellpadding="0"
|
||||||
|
bgcolor="#eaebff">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="py-30 px-56"
|
||||||
|
style="padding-top: 30px; padding-bottom: 30px; padding-left: 56px; padding-right: 56px;">
|
||||||
|
<table width="100%" border="0" cellspacing="0"
|
||||||
|
cellpadding="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="font-size:18px; line-height:25px; font-family:Arial, sans-serif, 'Motiva Sans'; color:#8f98a0; text-align:center;">
|
||||||
|
验证码
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding-bottom: 16px"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="title-48 c-blue1 fw-b a-center"
|
||||||
|
style="font-size:48px; line-height:52px; font-family:Arial, sans-serif, 'Motiva Sans'; color:#007aff; font-weight:bold; text-align:center;">
|
||||||
|
{Captcha}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||||
|
</table>
|
||||||
|
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="title-36 pb-30 c-grey6 fw-b"
|
||||||
|
style="font-size:30px; line-height:34px; font-family:Arial, sans-serif, 'Motiva Sans'; text-align:left; padding-bottom: 20px; color:#002333; font-weight:bold;">
|
||||||
|
不是您?
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="text-18 c-grey4 pb-30"
|
||||||
|
style="font-size:18px; line-height:25px; font-family:Arial, sans-serif, 'Motiva Sans'; text-align:left; color:#002333; padding-bottom: 30px;">
|
||||||
|
\s
|
||||||
|
如果这不是来自您的验证请求,请您忽略本邮件并<span style="color: #002333; font-weight: bold;">不要将验证码转发给任何人</span>。<br><br>
|
||||||
|
此电子邮件包含一个代码,您需要用它验证您的帐户。切勿与任何人分享此代码。
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="pt-30" style="padding-top: 30px;">
|
||||||
|
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="img" width="3" bgcolor="#007aff"
|
||||||
|
style="font-size:0pt; line-height:0pt; text-align:left;">
|
||||||
|
</td>
|
||||||
|
<td class="img" width="37"
|
||||||
|
style="font-size:0pt; line-height:0pt; text-align:left;">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<table width="100%" border="0" cellspacing="0"
|
||||||
|
cellpadding="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="text-16 py-20 c-grey4 fallback-font"
|
||||||
|
style="font-size:16px; line-height:22px; font-family:Arial, sans-serif, 'Motiva Sans'; text-align:left; padding-top: 20px; padding-bottom: 20px; color:#002333;">
|
||||||
|
祝您愉快,<br>
|
||||||
|
Lichx
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
""";
|
||||||
|
// @Resource
|
||||||
|
// private Test test;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MailUtil mailUtil;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IdentifyingCode identifyingCode;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PasswordUtil passwordUtil;
|
||||||
|
|
||||||
|
// @GetMapping("/test")
|
||||||
|
// public String test() {
|
||||||
|
// test.test();
|
||||||
|
// return "test";
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @PostMapping("/test/{id}")
|
||||||
|
// public String testPost(@PathVariable Integer id) {
|
||||||
|
// test.test();
|
||||||
|
// return "testPost";
|
||||||
|
// }
|
||||||
|
|
||||||
|
@PostMapping("/")
|
||||||
|
public Result<?> login(@RequestBody LoginDTO loginInfo, HttpServletResponse response) {
|
||||||
|
// 验证用户名和密码(此处省略具体逻辑)
|
||||||
|
String usernameOrEmail = loginInfo.getUsernameOrEmail();
|
||||||
|
String password = loginInfo.getPassword();
|
||||||
|
var user = usernameOrEmail.contains("@")?userService.getUserByEmail(usernameOrEmail) : userService.getUserByUserName(usernameOrEmail);
|
||||||
|
if (user != null && passwordUtil.matches(password,user.getPassword())) {
|
||||||
|
String token = JwtUtil.generateToken(user.getName());
|
||||||
|
// 设置HttpOnly和Secure属性的Cookie
|
||||||
|
response.setHeader("Set-Cookie", "Authorization=Bearer " + token + "; HttpOnly; Max-Age=86400; Path=/; SameSite=Strict");
|
||||||
|
// response.setHeader("Set-Cookie", "token=Bearer " + token + "; HttpOnly; Secure; Path=/; SameSite=Strict");
|
||||||
|
return Result.success("登录成功");
|
||||||
|
}
|
||||||
|
return Result.error("用户名或密码错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/logout")
|
||||||
|
public Result<?> logout(HttpServletResponse response) {
|
||||||
|
// 清除Cookie
|
||||||
|
response.setHeader("Set-Cookie", "Authorization=; HttpOnly; Max-Age=0; Path=/; SameSite=Strict");
|
||||||
|
return Result.success("注销成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/register/identifyingCode")
|
||||||
|
public Result<?> registerIdentifyingCode(String email) throws MessagingException {
|
||||||
|
var captcha = identifyingCode.getIdentifyingCode(email, IdentifyingCode.IdentifyingCodeType.REGISTER, 15 * 60);
|
||||||
|
if (captcha == null) {
|
||||||
|
return Result.error("验证码请求过于频繁,请稍后再试");
|
||||||
|
}
|
||||||
|
String mailTemplate = this.mailTemplate;
|
||||||
|
String tip = "您正在进行注册操作,这是您验证帐户所需的令牌验证码";
|
||||||
|
String expireTime = "15";
|
||||||
|
mailTemplate = mailTemplate.replace("{tip}", tip);
|
||||||
|
mailTemplate = mailTemplate.replace("{Captcha}", captcha);
|
||||||
|
mailTemplate = mailTemplate.replace("{ExpireTime}", expireTime);
|
||||||
|
mailUtil.sendHtmlEmail(
|
||||||
|
email,
|
||||||
|
"Lichx's WebClass",
|
||||||
|
mailTemplate);
|
||||||
|
return Result.success("验证码已发送到您的邮箱,请注意查收");
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/register")
|
||||||
|
public Result<?> register(@RequestBody RegisterDTO registerDTO) {
|
||||||
|
var user = new User();
|
||||||
|
user.setId(userService.getUserLastId());
|
||||||
|
user.setName(registerDTO.getUsername());
|
||||||
|
user.setPassword(passwordUtil.encrypt(registerDTO.getPassword()));
|
||||||
|
user.setEmail(registerDTO.getEmail());
|
||||||
|
user.setBirth(LocalDate.parse(registerDTO.getBirth(), DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay());
|
||||||
|
user.setAvatarURL(registerDTO.getAvatarFile());
|
||||||
|
|
||||||
|
if (!identifyingCode.testIdentifyingCode(registerDTO.getEmail(),
|
||||||
|
IdentifyingCode.IdentifyingCodeType.REGISTER,
|
||||||
|
registerDTO.getIdentifyingCode())) {
|
||||||
|
return Result.error("验证码错误或已过期");
|
||||||
|
}
|
||||||
|
if (userService.getUserByUserName(user.getName()) != null) {
|
||||||
|
return Result.error("用户名已存在");
|
||||||
|
}
|
||||||
|
userService.insertUser(user);
|
||||||
|
return Result.success("用户已注册");
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/passwordReset/identifyingCode")
|
||||||
|
public Result<?> resetIdentifyingCode(String usernameOrEmail) throws MessagingException {
|
||||||
|
var email = usernameOrEmail.contains("@") ? usernameOrEmail : userService.getUserByUserName(usernameOrEmail).getEmail();
|
||||||
|
var captcha = identifyingCode.getIdentifyingCode(email, IdentifyingCode.IdentifyingCodeType.PASSWORD_RESET, 15 * 60);
|
||||||
|
if (captcha == null) {
|
||||||
|
return Result.error("验证码请求过于频繁,请稍后再试");
|
||||||
|
}
|
||||||
|
String mailTemplate = this.mailTemplate;
|
||||||
|
String tip = "您正在进行密码重置操作,这是您验证帐户所需的令牌验证码";
|
||||||
|
String expireTime = "15";
|
||||||
|
mailTemplate = mailTemplate.replace("{tip}", tip);
|
||||||
|
mailTemplate = mailTemplate.replace("{Captcha}", captcha);
|
||||||
|
mailTemplate = mailTemplate.replace("{ExpireTime}", expireTime);
|
||||||
|
mailUtil.sendHtmlEmail(
|
||||||
|
email,
|
||||||
|
"Lichx's WebClass",
|
||||||
|
mailTemplate);
|
||||||
|
return Result.success("验证码已发送到您的邮箱,请注意查收");
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/passwordReset")
|
||||||
|
public Result<?> passwordReset(@RequestBody PasswordResetDTO passwordResetDTO) {
|
||||||
|
System.out.println();
|
||||||
|
String usernameOrEmail = passwordResetDTO.getUsernameOrEmail();
|
||||||
|
String password = passwordResetDTO.getPassword();
|
||||||
|
var user = usernameOrEmail.contains("@")?userService.getUserByEmail(usernameOrEmail) : userService.getUserByUserName(usernameOrEmail);
|
||||||
|
if (!identifyingCode.testIdentifyingCode(user.getEmail(),
|
||||||
|
IdentifyingCode.IdentifyingCodeType.PASSWORD_RESET,
|
||||||
|
passwordResetDTO.getIdentifyingCode())) {
|
||||||
|
return Result.error("验证码错误或已过期");
|
||||||
|
}
|
||||||
|
userService.changeUserPasswordByUserName(user.getName(), passwordUtil.encrypt(password));
|
||||||
|
return Result.success("密码已重置");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package top.lichx.webclassbackend.controller;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import top.lichx.webclassbackend.pojo.vo.VisibleUserInfoVO;
|
||||||
|
import top.lichx.webclassbackend.result.Result;
|
||||||
|
import top.lichx.webclassbackend.service.UserService;
|
||||||
|
import top.lichx.webclassbackend.util.COSIO;
|
||||||
|
import top.lichx.webclassbackend.util.JwtUtil;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/user")
|
||||||
|
public class User {
|
||||||
|
@Resource
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private COSIO cosio;
|
||||||
|
|
||||||
|
@GetMapping("/info")
|
||||||
|
public Result<?> getUserInfo(HttpServletRequest request) {
|
||||||
|
|
||||||
|
String cookieHeader = request.getHeader("Cookie");
|
||||||
|
String token = null;
|
||||||
|
if (cookieHeader != null) {
|
||||||
|
String[] cookies = cookieHeader.split("; ");
|
||||||
|
for (String cookie : cookies) {
|
||||||
|
if (cookie.startsWith("Authorization=")) {
|
||||||
|
token = cookie.substring("Authorization=".length());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
token = token.substring(7); // 去掉 "Bearer " 前缀
|
||||||
|
String username = JwtUtil.parseToken(token);
|
||||||
|
|
||||||
|
var user = userService.getUserByUserName(username);
|
||||||
|
if (user == null)
|
||||||
|
return Result.error("用户不存在");
|
||||||
|
var returnVO = new VisibleUserInfoVO();
|
||||||
|
returnVO.setUserId("" + user.getId());
|
||||||
|
returnVO.setUserName(user.getName());
|
||||||
|
returnVO.setUserEmail(user.getEmail());
|
||||||
|
returnVO.setUserAvatar(cosio.GetFileLink(user.getAvatarURL()));
|
||||||
|
return Result.success("", returnVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/avatar")
|
||||||
|
public Result<?> getUserAvatar(HttpServletRequest request) {
|
||||||
|
String cookieHeader = request.getHeader("Cookie");
|
||||||
|
String token = null;
|
||||||
|
if (cookieHeader != null) {
|
||||||
|
String[] cookies = cookieHeader.split("; ");
|
||||||
|
for (String cookie : cookies) {
|
||||||
|
if (cookie.startsWith("Authorization=")) {
|
||||||
|
token = cookie.substring("Authorization=".length());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
token = token.substring(7); // 去掉 "Bearer " 前缀
|
||||||
|
String username = JwtUtil.parseToken(token);
|
||||||
|
var user = userService.getUserByUserName(username);
|
||||||
|
if (user == null)
|
||||||
|
return Result.error("用户不存在");
|
||||||
|
return Result.success("", cosio.GetFileLink(user.getAvatarURL()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
package top.lichx.webclassbackend.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import top.lichx.webclassbackend.pojo.dto.UserManagerDTO;
|
||||||
|
import top.lichx.webclassbackend.pojo.entity.UserManagerInfo;
|
||||||
|
import top.lichx.webclassbackend.result.Result;
|
||||||
|
import top.lichx.webclassbackend.service.UserManagerService;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/userManager")
|
||||||
|
public class UserManager {
|
||||||
|
@Resource
|
||||||
|
private UserManagerService userManagerService;
|
||||||
|
|
||||||
|
@PostMapping("/addUser")
|
||||||
|
public Result<?> addUser(@RequestBody UserManagerDTO userManagerDTO)
|
||||||
|
{
|
||||||
|
var info = new UserManagerInfo();
|
||||||
|
info.setId(userManagerService.getUserManagerInfoLastId());
|
||||||
|
info.setName(userManagerDTO.getName());
|
||||||
|
info.setDate(LocalDate.parse(userManagerDTO.getDate(), DateTimeFormatter.ISO_LOCAL_DATE));
|
||||||
|
info.setProvince(userManagerDTO.getProvince());
|
||||||
|
info.setCity(userManagerDTO.getCity());
|
||||||
|
info.setAddress(userManagerDTO.getAddress());
|
||||||
|
info.setZip(userManagerDTO.getZip());
|
||||||
|
userManagerService.insertUserManagerInfo(info);
|
||||||
|
return Result.success("添加用户成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/page")
|
||||||
|
public Result<IPage<UserManagerInfo>> getUserManagerInfoPage(@RequestParam int page, @RequestParam int size, @RequestParam String situation) {
|
||||||
|
return Result.success("",userManagerService.getUserManagerInfoPage(page, size, situation));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/updateUser")
|
||||||
|
public Result<?>updateUser(@RequestBody UserManagerDTO userManagerDTO)
|
||||||
|
{
|
||||||
|
var info = new UserManagerInfo();
|
||||||
|
info.setId(userManagerDTO.getId());
|
||||||
|
info.setName(userManagerDTO.getName());
|
||||||
|
info.setDate(LocalDate.parse(userManagerDTO.getDate(), DateTimeFormatter.ISO_LOCAL_DATE));
|
||||||
|
info.setProvince(userManagerDTO.getProvince());
|
||||||
|
info.setCity(userManagerDTO.getCity());
|
||||||
|
info.setAddress(userManagerDTO.getAddress());
|
||||||
|
info.setZip(userManagerDTO.getZip());
|
||||||
|
userManagerService.updateUserManagerInfo(info);
|
||||||
|
return Result.success("更新用户成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("deleteUser")
|
||||||
|
public Result<?> deleteUser(@RequestParam Integer id)
|
||||||
|
{
|
||||||
|
userManagerService.deleteUserManagerInfo(id);
|
||||||
|
return Result.success("删除用户成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("total")
|
||||||
|
public Result<Integer> total(@RequestParam String situation)
|
||||||
|
{
|
||||||
|
return Result.success("", userManagerService.getTotal(situation));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package top.lichx.webclassbackend.exception;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
import top.lichx.webclassbackend.result.Result;
|
||||||
|
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class GlobalExceptionHandler {
|
||||||
|
|
||||||
|
// // 处理空指针异常
|
||||||
|
// @ExceptionHandler(NullPointerException.class)
|
||||||
|
// public Result<?> handleNullPointerException(NullPointerException ex) {
|
||||||
|
// ex.printStackTrace();
|
||||||
|
// return Result.error("空指针异常,请检查代码!");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 处理其他异常
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
public Result<?> handleException(Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
return Result.error("服务器内部错误:" + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package top.lichx.webclassbackend.interceptor;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.Cookie;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
import top.lichx.webclassbackend.util.JwtUtil;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class JwtInterceptor implements HandlerInterceptor {
|
||||||
|
@Override
|
||||||
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||||
|
String cookieHeader = request.getHeader("Cookie");
|
||||||
|
String token = null;
|
||||||
|
if (cookieHeader != null) {
|
||||||
|
String[] cookies = cookieHeader.split("; ");
|
||||||
|
for (String cookie : cookies) {
|
||||||
|
if (cookie.startsWith("Authorization=")) {
|
||||||
|
token = cookie.substring("Authorization=".length());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("token:{}, URI:{}", token, request.getRequestURI());
|
||||||
|
if (token == null || !token.startsWith("Bearer ")) {
|
||||||
|
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
token = token.substring(7); // 去掉 "Bearer " 前缀
|
||||||
|
String username = JwtUtil.parseToken(token);
|
||||||
|
if (username == null) {
|
||||||
|
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
log.error("Token解析失败");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
request.setAttribute("username", username);
|
||||||
|
log.info("用户 {} 访问了 {}", username, request.getRequestURI());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package top.lichx.webclassbackend.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import top.lichx.webclassbackend.pojo.entity.UserManagerInfo;
|
||||||
|
@Mapper
|
||||||
|
public interface userManagerInfoMapper extends BaseMapper<UserManagerInfo> { }
|
|
@ -0,0 +1,9 @@
|
||||||
|
package top.lichx.webclassbackend.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import top.lichx.webclassbackend.pojo.entity.User;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface userMapper extends BaseMapper<User> { }
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package top.lichx.webclassbackend.pojo.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class In {
|
||||||
|
Long id;
|
||||||
|
String test;
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package top.lichx.webclassbackend.pojo.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class LoginDTO {
|
||||||
|
|
||||||
|
private String usernameOrEmail;
|
||||||
|
private String password;
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package top.lichx.webclassbackend.pojo.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class PasswordResetDTO {
|
||||||
|
private String usernameOrEmail;
|
||||||
|
private String identifyingCode;
|
||||||
|
private String password;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package top.lichx.webclassbackend.pojo.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class RegisterCodeDTO {
|
||||||
|
private String email;
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package top.lichx.webclassbackend.pojo.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class RegisterDTO {
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
private String email;
|
||||||
|
private String identifyingCode;
|
||||||
|
private String birth;
|
||||||
|
private String avatarFile;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package top.lichx.webclassbackend.pojo.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class UserManagerDTO {
|
||||||
|
private Integer id;
|
||||||
|
private String name;
|
||||||
|
private String date;
|
||||||
|
private String province;
|
||||||
|
private String city;
|
||||||
|
private String address;
|
||||||
|
private String zip;
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package top.lichx.webclassbackend.pojo.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@TableName("userT")
|
||||||
|
public class User {
|
||||||
|
Integer id;
|
||||||
|
String name;
|
||||||
|
String password;
|
||||||
|
String email;
|
||||||
|
LocalDateTime birth;
|
||||||
|
@TableField("avatarURL")
|
||||||
|
String avatarURL;
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package top.lichx.webclassbackend.pojo.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@TableName("User")
|
||||||
|
public class UserManagerInfo {
|
||||||
|
Integer id;
|
||||||
|
String name;
|
||||||
|
LocalDate date;
|
||||||
|
String province;
|
||||||
|
String city;
|
||||||
|
String address;
|
||||||
|
String zip;
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package top.lichx.webclassbackend.pojo.vo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class AvatarInfo {
|
||||||
|
private String avatarFilePath;
|
||||||
|
private URL avatarFileURL;
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package top.lichx.webclassbackend.pojo.vo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class VisibleUserInfoVO {
|
||||||
|
private String userId;
|
||||||
|
private String userName;
|
||||||
|
private String userEmail;
|
||||||
|
private URL userAvatar;
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package top.lichx.webclassbackend.result;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class Result<T> implements Serializable {
|
||||||
|
private Integer code;
|
||||||
|
private String message;
|
||||||
|
private T data;
|
||||||
|
|
||||||
|
public static <T> Result<T> success() {
|
||||||
|
Result<T> result = new Result<>();
|
||||||
|
result.setCode(200);
|
||||||
|
result.setMessage("success");
|
||||||
|
result.data = null;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> success(String msg) {
|
||||||
|
Result<T> result = new Result<>();
|
||||||
|
result.setCode(200);
|
||||||
|
result.setMessage(msg);
|
||||||
|
result.data = null;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> success(String msg, T data) {
|
||||||
|
Result<T> result = new Result<>();
|
||||||
|
result.setCode(200);
|
||||||
|
result.setMessage(msg);
|
||||||
|
result.setData(data);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result<Object> error() {
|
||||||
|
Result<Object> result = new Result<>();
|
||||||
|
result.setCode(500);
|
||||||
|
result.setMessage("error");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result<Object> error(String msg) {
|
||||||
|
Result<Object> result = new Result<>();
|
||||||
|
result.setCode(500);
|
||||||
|
result.setMessage(msg);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package top.lichx.webclassbackend.service;
|
||||||
|
|
||||||
|
import jakarta.mail.MessagingException;
|
||||||
|
|
||||||
|
public interface IdentifyingCode {
|
||||||
|
String getIdentifyingCode(String email, IdentifyingCodeType type, long expireTimeInSeconds);
|
||||||
|
boolean testIdentifyingCode(String email, IdentifyingCodeType type, String code );
|
||||||
|
enum IdentifyingCodeType {
|
||||||
|
REGISTER,
|
||||||
|
PASSWORD_RESET
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package top.lichx.webclassbackend.service;
|
||||||
|
|
||||||
|
public interface Test {
|
||||||
|
void test();
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package top.lichx.webclassbackend.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import top.lichx.webclassbackend.pojo.entity.UserManagerInfo;
|
||||||
|
|
||||||
|
public interface UserManagerService {
|
||||||
|
public void insertUserManagerInfo(UserManagerInfo userManagerInfo);
|
||||||
|
public void updateUserManagerInfo(UserManagerInfo userManagerInfo);
|
||||||
|
public void deleteUserManagerInfo(Integer id);
|
||||||
|
public IPage<UserManagerInfo> getUserManagerInfoPage(int page, int size, String situation);
|
||||||
|
public Integer getUserManagerInfoLastId();
|
||||||
|
public Integer getTotal(String situation);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package top.lichx.webclassbackend.service;
|
||||||
|
|
||||||
|
import top.lichx.webclassbackend.pojo.entity.User;
|
||||||
|
|
||||||
|
public interface UserService {
|
||||||
|
public void insertUser(User user);
|
||||||
|
public User getUserByUserName(String userName);
|
||||||
|
public void changeUserPasswordByUserName(String userName, String password);
|
||||||
|
|
||||||
|
public User getUserByEmail(String email);
|
||||||
|
public Integer getUserLastId();
|
||||||
|
public Integer getTotal();
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
package top.lichx.webclassbackend.service.impl;
|
||||||
|
|
||||||
|
import jakarta.mail.MessagingException;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import top.lichx.webclassbackend.service.IdentifyingCode;
|
||||||
|
|
||||||
|
import java.util.Dictionary;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class IdentifyingCodeImpl implements IdentifyingCode {
|
||||||
|
|
||||||
|
Dictionary<String, IdentifyingCodeStructure> data = new Hashtable<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getIdentifyingCode(String email, IdentifyingCodeType type, long expireTimeInSeconds) {
|
||||||
|
dataClear();
|
||||||
|
var structure = data.get(IdentifyingCodeStructure.getId(email, type));
|
||||||
|
if (structure != null && System.currentTimeMillis() / 1000 < structure.startTimeInSeconds + 60)
|
||||||
|
return null;
|
||||||
|
structure = IdentifyingCodeStructure.createIdentifyingCodeStructure(email, type, expireTimeInSeconds);
|
||||||
|
data.put(structure.id, structure);
|
||||||
|
return structure.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean testIdentifyingCode(String email, IdentifyingCodeType type, String code) {
|
||||||
|
dataClear();
|
||||||
|
var structure = data.get(IdentifyingCodeStructure.getId(email, type));
|
||||||
|
if (structure == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (structure.isExpired()) {
|
||||||
|
data.remove(structure.id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (structure.code.equals(code)) {
|
||||||
|
data.remove(structure.id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dataClear() {
|
||||||
|
var keys = data.keys();
|
||||||
|
while (keys.hasMoreElements()) {
|
||||||
|
var key = keys.nextElement();
|
||||||
|
var structure = data.get(key);
|
||||||
|
if (structure.isExpired()) {
|
||||||
|
data.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
class IdentifyingCodeStructure {
|
||||||
|
public String id;
|
||||||
|
public String username;
|
||||||
|
public IdentifyingCode.IdentifyingCodeType type;
|
||||||
|
public String code;
|
||||||
|
public long startTimeInSeconds;
|
||||||
|
public long expireTimeInSeconds;
|
||||||
|
|
||||||
|
boolean isExpired() {
|
||||||
|
long currentTimeInSeconds = System.currentTimeMillis() / 1000;
|
||||||
|
return currentTimeInSeconds > startTimeInSeconds + expireTimeInSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getId(String username, IdentifyingCode.IdentifyingCodeType type) {
|
||||||
|
return username + type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static IdentifyingCodeStructure createIdentifyingCodeStructure(String email, IdentifyingCode.IdentifyingCodeType type, long expireTimeInSeconds) {
|
||||||
|
IdentifyingCodeStructure identifyingCodeStructure = new IdentifyingCodeStructure();
|
||||||
|
identifyingCodeStructure.id = email + type;
|
||||||
|
identifyingCodeStructure.username = email;
|
||||||
|
identifyingCodeStructure.type = type;
|
||||||
|
Random random = new Random();
|
||||||
|
int randomNumber = 100000 + random.nextInt(900000); // 范围是 100000 到 999999
|
||||||
|
identifyingCodeStructure.code = "" + randomNumber;
|
||||||
|
identifyingCodeStructure.startTimeInSeconds = System.currentTimeMillis() / 1000;
|
||||||
|
identifyingCodeStructure.expireTimeInSeconds = expireTimeInSeconds;
|
||||||
|
return identifyingCodeStructure;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package top.lichx.webclassbackend.service.impl;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import top.lichx.webclassbackend.service.Test;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class TestImpl implements Test {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void test() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package top.lichx.webclassbackend.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import top.lichx.webclassbackend.mapper.userManagerInfoMapper;
|
||||||
|
import top.lichx.webclassbackend.pojo.entity.UserManagerInfo;
|
||||||
|
import top.lichx.webclassbackend.service.UserManagerService;
|
||||||
|
@Service
|
||||||
|
public class UserManagerServiceImpl implements UserManagerService {
|
||||||
|
@Resource
|
||||||
|
private userManagerInfoMapper userManagerInfoMapper;
|
||||||
|
public void insertUserManagerInfo(UserManagerInfo userManagerInfo) {
|
||||||
|
// 插入用户信息
|
||||||
|
userManagerInfoMapper.insert(userManagerInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateUserManagerInfo(UserManagerInfo userManagerInfo) {
|
||||||
|
// 更新用户信息
|
||||||
|
userManagerInfoMapper.updateById(userManagerInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteUserManagerInfo(Integer id) {
|
||||||
|
// 删除用户信息
|
||||||
|
userManagerInfoMapper.deleteById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPage<UserManagerInfo> getUserManagerInfoPage(int page, int size, String situation) {
|
||||||
|
// 使用 MyBatis-Plus 提供的分页功能
|
||||||
|
Page<UserManagerInfo> pageRequest = new Page<>(page, size);
|
||||||
|
QueryWrapper<UserManagerInfo> queryWrapper = new QueryWrapper<>();
|
||||||
|
|
||||||
|
// 如果 situation 不为空,则添加模糊查询条件
|
||||||
|
if (situation != null && !situation.trim().isEmpty()) {
|
||||||
|
queryWrapper.like("name", situation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return userManagerInfoMapper.selectPage(pageRequest, queryWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getUserManagerInfoLastId() {
|
||||||
|
UserManagerInfo lastRecord = userManagerInfoMapper.selectOne(
|
||||||
|
new QueryWrapper<UserManagerInfo>().orderByDesc("id").last("LIMIT 1")
|
||||||
|
);
|
||||||
|
// 如果记录为空,返回 0,否则返回最后一条记录的 ID
|
||||||
|
return lastRecord == null ? 0 : lastRecord.getId() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getTotal(String situation) {
|
||||||
|
QueryWrapper<UserManagerInfo> queryWrapper = new QueryWrapper<>();
|
||||||
|
if (situation != null && !situation.trim().isEmpty()) {
|
||||||
|
queryWrapper.like("name", situation);
|
||||||
|
}
|
||||||
|
return userManagerInfoMapper.selectCount(queryWrapper).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package top.lichx.webclassbackend.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import top.lichx.webclassbackend.mapper.userMapper;
|
||||||
|
import top.lichx.webclassbackend.pojo.entity.User;
|
||||||
|
import top.lichx.webclassbackend.pojo.entity.UserManagerInfo;
|
||||||
|
import top.lichx.webclassbackend.service.UserService;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class UserServiceImpl implements UserService {
|
||||||
|
@Resource
|
||||||
|
private userMapper webclassMapper;
|
||||||
|
|
||||||
|
public void insertUser(User user) {
|
||||||
|
webclassMapper.insert(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUserByUserName(String userName) {
|
||||||
|
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.eq("name", userName);
|
||||||
|
return webclassMapper.selectOne(queryWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void changeUserPasswordByUserName(String userName, String password) {
|
||||||
|
User user = getUserByUserName(userName);
|
||||||
|
if (user != null) {
|
||||||
|
user.setPassword(password);
|
||||||
|
webclassMapper.updateById(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User getUserByEmail(String email) {
|
||||||
|
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.eq("email", email);
|
||||||
|
return webclassMapper.selectOne(queryWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getUserLastId() {
|
||||||
|
User lastRecord = webclassMapper.selectOne(
|
||||||
|
new QueryWrapper<User>().orderByDesc("id").last("LIMIT 1")
|
||||||
|
);
|
||||||
|
// 如果记录为空,返回 0,否则返回最后一条记录的 ID
|
||||||
|
return lastRecord == null ? 0 : lastRecord.getId() + 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getTotal() {
|
||||||
|
return webclassMapper.selectCount(null).intValue();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package top.lichx.webclassbackend.util;
|
||||||
|
|
||||||
|
import com.qcloud.cos.COSClient;
|
||||||
|
import com.qcloud.cos.ClientConfig;
|
||||||
|
import com.qcloud.cos.Headers;
|
||||||
|
import com.qcloud.cos.auth.BasicCOSCredentials;
|
||||||
|
import com.qcloud.cos.http.HttpMethodName;
|
||||||
|
import com.qcloud.cos.model.GeneratePresignedUrlRequest;
|
||||||
|
import com.qcloud.cos.model.PutObjectRequest;
|
||||||
|
import com.qcloud.cos.model.PutObjectResult;
|
||||||
|
import com.qcloud.cos.model.ResponseHeaderOverrides;
|
||||||
|
import com.qcloud.cos.region.Region;
|
||||||
|
import com.qcloud.cos.utils.DateUtils;
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class COSIO {
|
||||||
|
private COSClient cosClient;
|
||||||
|
|
||||||
|
@Value("${tencent.secretId}")
|
||||||
|
private String secretId;
|
||||||
|
|
||||||
|
@Value("${tencent.secretKey}")
|
||||||
|
private String secretKey;
|
||||||
|
|
||||||
|
@Value("${tencent.bucketName}")
|
||||||
|
private String bucketName;
|
||||||
|
|
||||||
|
@Value("${tencent.baseDIR}")
|
||||||
|
private String baseDir;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void Init() {
|
||||||
|
var cred = new BasicCOSCredentials(secretId, secretKey);
|
||||||
|
var region = new Region("ap-shanghai");
|
||||||
|
var clientConfig = new ClientConfig(region);
|
||||||
|
cosClient = new COSClient(cred, clientConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String UploadFile(File file) {
|
||||||
|
String fileName = UUID.randomUUID().toString();
|
||||||
|
String key = baseDir + fileName;
|
||||||
|
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, file);
|
||||||
|
cosClient.putObject(putObjectRequest);
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL GetFileLink(String fileName) {
|
||||||
|
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, baseDir + fileName, HttpMethodName.GET);
|
||||||
|
ResponseHeaderOverrides responseHeaderOverrides = new ResponseHeaderOverrides();
|
||||||
|
String responseCacheControl = "no-cache";
|
||||||
|
String cacheExpireStr =
|
||||||
|
DateUtils.formatRFC822Date(new Date(System.currentTimeMillis() + 10L * 60L * 1000L));
|
||||||
|
responseHeaderOverrides.setCacheControl(responseCacheControl);
|
||||||
|
responseHeaderOverrides.setExpires(cacheExpireStr);
|
||||||
|
request.setResponseHeaders(responseHeaderOverrides);
|
||||||
|
Date expirationDate = new Date(System.currentTimeMillis() + 10L * 60L * 1000L);
|
||||||
|
request.setExpiration(expirationDate);
|
||||||
|
request.putCustomRequestHeader(Headers.HOST, cosClient.getClientConfig().getEndpointBuilder().buildGeneralApiEndpoint(bucketName));
|
||||||
|
return cosClient.generatePresignedUrl(request);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package top.lichx.webclassbackend.util;
|
||||||
|
|
||||||
|
import io.jsonwebtoken.*;
|
||||||
|
import io.jsonwebtoken.security.Keys;
|
||||||
|
|
||||||
|
import java.security.Key;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class JwtUtil {
|
||||||
|
private static final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256); // 生成密钥
|
||||||
|
private static final long EXPIRATION_TIME = 86400000; // 1 天
|
||||||
|
|
||||||
|
public static String generateToken(String username) {
|
||||||
|
return Jwts.builder()
|
||||||
|
.setSubject(username)
|
||||||
|
.setIssuedAt(new Date())
|
||||||
|
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
|
||||||
|
.signWith(key)
|
||||||
|
.compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String parseToken(String token) {
|
||||||
|
try {
|
||||||
|
Claims claims = Jwts.parserBuilder()
|
||||||
|
.setSigningKey(key)
|
||||||
|
.build()
|
||||||
|
.parseClaimsJws(token)
|
||||||
|
.getBody();
|
||||||
|
return claims.getSubject();
|
||||||
|
} catch (JwtException e) {
|
||||||
|
return null; // Token 无效或过期
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package top.lichx.webclassbackend.util;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.mail.*;
|
||||||
|
import jakarta.mail.internet.InternetAddress;
|
||||||
|
import jakarta.mail.internet.MimeMessage;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import top.lichx.webclassbackend.config.MailProperties;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MailUtil {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MailProperties mailProperties;
|
||||||
|
|
||||||
|
public void sendHtmlEmail(String to, String subject, String htmlContent) throws MessagingException {
|
||||||
|
// 配置 SMTP 服务器
|
||||||
|
Properties props = new Properties();
|
||||||
|
props.put("mail.smtp.host", mailProperties.getHost());
|
||||||
|
props.put("mail.smtp.port", String.valueOf(mailProperties.getPort()));
|
||||||
|
props.put("mail.smtp.auth", String.valueOf(mailProperties.isAuth()));
|
||||||
|
props.put("mail.smtp.starttls.enable", String.valueOf(mailProperties.isStarttls()));
|
||||||
|
|
||||||
|
// 认证信息
|
||||||
|
String username = mailProperties.getUsername();
|
||||||
|
String password = mailProperties.getPassword();
|
||||||
|
|
||||||
|
// 创建会话
|
||||||
|
Session session = Session.getInstance(props, new Authenticator() {
|
||||||
|
@Override
|
||||||
|
protected PasswordAuthentication getPasswordAuthentication() {
|
||||||
|
return new PasswordAuthentication(username, password);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建邮件
|
||||||
|
Message message = new MimeMessage(session);
|
||||||
|
message.setFrom(new InternetAddress(username));
|
||||||
|
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
|
||||||
|
message.setSubject(subject);
|
||||||
|
message.setContent(htmlContent, "text/html; charset=utf-8"); // 设置内容为 HTML 格式
|
||||||
|
|
||||||
|
// 发送邮件
|
||||||
|
Transport.send(message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package top.lichx.webclassbackend.util;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class PasswordUtil {
|
||||||
|
|
||||||
|
// 加密密码(使用 SHA-256)
|
||||||
|
public String encrypt(String rawPassword) {
|
||||||
|
return DigestUtils.sha256Hex(rawPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证密码
|
||||||
|
public boolean matches(String rawPassword, String encryptedPassword) {
|
||||||
|
return encrypt(rawPassword).equals(encryptedPassword);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1,11 @@
|
||||||
spring.application.name=WebClassBackend
|
spring.application.name=WebClassBackend
|
||||||
|
|
||||||
|
# ???????
|
||||||
|
spring.datasource.url=jdbc:mariadb://localhost:3306/webclass
|
||||||
|
spring.datasource.username=webclass
|
||||||
|
spring.datasource.password=123456
|
||||||
|
|
||||||
|
spring.servlet.multipart.enabled=true
|
||||||
|
spring.servlet.multipart.max-file-size=50MB
|
||||||
|
spring.servlet.multipart.max-request-size=50MB
|
||||||
|
p
|
|
@ -0,0 +1,3 @@
|
||||||
|
spring:
|
||||||
|
profiles:
|
||||||
|
active: dev
|
Loading…
Reference in New Issue