✨ 完成实验部分后端
This commit is contained in:
parent
086c034a95
commit
a35ecd8a2f
|
@ -31,3 +31,5 @@ build/
|
|||
|
||||
### VS Code ###
|
||||
.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>
|
||||
<scope>test</scope>
|
||||
</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>
|
||||
|
||||
<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.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