feat(all): 添加了头像、验证码相关功能,添加了社团相关类
This commit is contained in:
parent
f4d3cdd319
commit
c1d7b0e05b
13
.idea/dataSources.xml
generated
13
.idea/dataSources.xml
generated
@ -15,5 +15,18 @@
|
||||
</jdbc-additional-properties>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
<data-source source="LOCAL" name="0@server.wzpmc.cn" uuid="41d00101-c8a0-446a-a28b-8027776414f2">
|
||||
<driver-ref>redis</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>jdbc.RedisDriver</jdbc-driver>
|
||||
<jdbc-url>jdbc:redis://server.wzpmc.cn:6379/0</jdbc-url>
|
||||
<jdbc-additional-properties>
|
||||
<property name="com.intellij.clouds.kubernetes.db.host.port" />
|
||||
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
|
||||
<property name="com.intellij.clouds.kubernetes.db.resource.type" value="Deployment" />
|
||||
<property name="com.intellij.clouds.kubernetes.db.container.port" />
|
||||
</jdbc-additional-properties>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
@ -48,6 +48,8 @@ dependencies {
|
||||
implementation("com.alibaba.fastjson2:fastjson2:2.0.48")
|
||||
// https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2-extension-spring6
|
||||
implementation("com.alibaba.fastjson2:fastjson2-extension-spring6:2.0.48")
|
||||
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-redis:3.2.4")
|
||||
implementation("com.auth0:java-jwt:4.4.0")
|
||||
compileOnly("org.projectlombok:lombok")
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||
|
@ -2,8 +2,10 @@ package org.mmga.clubs;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
public class ItzxClubsHomeServerApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ItzxClubsHomeServerApplication.class, args);
|
||||
|
@ -57,6 +57,7 @@ public class AuthorizationConfiguration implements HandlerInterceptor, WebMvcCon
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void writeAuthorizationFailedResponse(HttpServletResponse response, JWTVerificationException e) throws IOException {
|
||||
log.debug("用户鉴权时出现错误:", e);
|
||||
ServletOutputStream outputStream = response.getOutputStream();
|
||||
|
@ -0,0 +1,22 @@
|
||||
package org.mmga.clubs.configuration;
|
||||
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class CorsConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedOrigins("http://localhost:5173", "http://127.0.0.1:5173")
|
||||
.allowedMethods("*")
|
||||
.allowedHeaders("*")
|
||||
.exposedHeaders("*");
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package org.mmga.clubs.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
@Configuration
|
||||
public class DirConfiguration {
|
||||
private File tempDir;
|
||||
private File avatarDir;
|
||||
@Bean
|
||||
public File tempDir() throws IOException {
|
||||
if (this.tempDir != null) {
|
||||
return this.tempDir;
|
||||
}
|
||||
this.tempDir = new File("tmp");
|
||||
if (!this.tempDir.exists()) {
|
||||
if (!this.tempDir.mkdir()) {
|
||||
throw new IOException("Failed to create temp dir");
|
||||
}
|
||||
}
|
||||
return this.tempDir;
|
||||
}
|
||||
@Bean
|
||||
public File avatarDir() throws IOException {
|
||||
if (this.avatarDir != null) {
|
||||
return this.avatarDir;
|
||||
}
|
||||
this.avatarDir = new File("avatar");
|
||||
if (!this.avatarDir.exists()) {
|
||||
if (!this.avatarDir.mkdir()) {
|
||||
throw new IOException("Failed to create avatar dir");
|
||||
}
|
||||
}
|
||||
return this.avatarDir;
|
||||
}
|
||||
}
|
@ -19,28 +19,7 @@ public class FastJsonConfiguration implements WebMvcConfigurer {
|
||||
**/
|
||||
@Override
|
||||
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
// 1. 配置fastjson
|
||||
FastJsonConfig config = new FastJsonConfig();
|
||||
|
||||
config.setDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
config.setCharset(StandardCharsets.UTF_8);
|
||||
config.setWriterFeatures(
|
||||
JSONWriter.Feature.WriteNullListAsEmpty,
|
||||
//json格式化
|
||||
JSONWriter.Feature.PrettyFormat,
|
||||
//输出map中value为null的数据
|
||||
JSONWriter.Feature.WriteMapNullValue,
|
||||
//输出boolean 为 false
|
||||
JSONWriter.Feature.WriteNullBooleanAsFalse,
|
||||
//输出list 为 []
|
||||
JSONWriter.Feature.WriteNullListAsEmpty,
|
||||
//输出number 为 0
|
||||
JSONWriter.Feature.WriteNullNumberAsZero,
|
||||
//输出字符串 为 ""
|
||||
JSONWriter.Feature.WriteNullStringAsEmpty,
|
||||
//对map进行排序
|
||||
JSONWriter.Feature.SortMapEntriesByKeys
|
||||
);
|
||||
FastJsonConfig config = getFastJsonConfig();
|
||||
|
||||
// 2. 添加fastjson转换器
|
||||
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
|
||||
@ -70,4 +49,29 @@ public class FastJsonConfiguration implements WebMvcConfigurer {
|
||||
converter.setFastJsonConfig(config);
|
||||
converters.add(0,converter);
|
||||
}
|
||||
|
||||
private static FastJsonConfig getFastJsonConfig() {
|
||||
FastJsonConfig config = new FastJsonConfig();
|
||||
|
||||
config.setDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
config.setCharset(StandardCharsets.UTF_8);
|
||||
config.setWriterFeatures(
|
||||
JSONWriter.Feature.WriteNullListAsEmpty,
|
||||
//json格式化
|
||||
JSONWriter.Feature.PrettyFormat,
|
||||
//输出map中value为null的数据
|
||||
JSONWriter.Feature.WriteMapNullValue,
|
||||
//输出boolean 为 false
|
||||
JSONWriter.Feature.WriteNullBooleanAsFalse,
|
||||
//输出list 为 []
|
||||
JSONWriter.Feature.WriteNullListAsEmpty,
|
||||
//输出number 为 0
|
||||
JSONWriter.Feature.WriteNullNumberAsZero,
|
||||
//输出字符串 为 ""
|
||||
JSONWriter.Feature.WriteNullStringAsEmpty,
|
||||
//对map进行排序
|
||||
JSONWriter.Feature.SortMapEntriesByKeys
|
||||
);
|
||||
return config;
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package org.mmga.clubs.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
@ -12,15 +13,16 @@ import org.mmga.clubs.entities.user.*;
|
||||
import org.mmga.clubs.entities.user.change.UserChangeAuthVo;
|
||||
import org.mmga.clubs.entities.user.change.UserChangePasswordVo;
|
||||
import org.mmga.clubs.entities.user.change.UserRenameVo;
|
||||
import org.mmga.clubs.entities.verify.VerifyResponseVo;
|
||||
import org.mmga.clubs.service.UserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/user")
|
||||
@Tag(name = "用户", description = "用户相关接口")
|
||||
@Slf4j
|
||||
@CrossOrigin(allowCredentials = "true", allowedHeaders = {"Set-Authorization", "Authorization"}, origins = {"http://localhost:5173"})
|
||||
public class UserController {
|
||||
private final UserService service;
|
||||
@Autowired
|
||||
@ -66,5 +68,27 @@ public class UserController {
|
||||
public BaseResponse<Boolean> changeAuth(@RequestBody UserChangeAuthVo userChangeAuthVo){
|
||||
return service.changeAuth(userChangeAuthVo);
|
||||
}
|
||||
|
||||
@PostMapping("/avatar")
|
||||
@Operation(description = "上传头像")
|
||||
@AuthorizationRequired
|
||||
public BaseResponse<String> changeAvatar(MultipartFile file, @RequestAttribute("user") int userId){
|
||||
return service.changeAvatar(file, userId);
|
||||
}
|
||||
@PutMapping("/avatar")
|
||||
@Operation(description = "修改用户头像")
|
||||
@AuthorizationRequired
|
||||
public BaseResponse<Boolean> changeAvatar(@RequestParam("code") @Schema(description = "修改头像操作码,可以通过将图片文件上传至") String avatarOperationCode, @RequestAttribute("user") int uid){
|
||||
return service.changeAvatar(avatarOperationCode, uid);
|
||||
}
|
||||
@GetMapping("/verify")
|
||||
@Operation(description = "获取验证码")
|
||||
public BaseResponse<VerifyResponseVo> generatorVerifyCode(){
|
||||
return service.getVerifyCode();
|
||||
}
|
||||
@GetMapping("/avatar/{sha1}")
|
||||
@Operation(description = "获取头像文件")
|
||||
public void getAvatar(HttpServletResponse response, @Schema(description = "头像文件SHA1值") @PathVariable("sha1") String sha1){
|
||||
service.getAvatar(response, sha1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,4 +19,5 @@ public interface UserDao {
|
||||
int changePassword(int id, String old, String password);
|
||||
void changePasswordAdmin(int id, String password);
|
||||
void changeAuth(int id, int authId);
|
||||
void changeAvatar(int id, String sha);
|
||||
}
|
||||
|
8
src/main/java/org/mmga/clubs/dao/redis/AvatarDao.java
Normal file
8
src/main/java/org/mmga/clubs/dao/redis/AvatarDao.java
Normal file
@ -0,0 +1,8 @@
|
||||
package org.mmga.clubs.dao.redis;
|
||||
|
||||
import org.mmga.clubs.entities.user.change.UserAvatarOperationVo;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
public interface AvatarDao extends CrudRepository<UserAvatarOperationVo, String> {
|
||||
UserAvatarOperationVo findByOperationCode(String operationCode);
|
||||
}
|
8
src/main/java/org/mmga/clubs/dao/redis/VerifyDao.java
Normal file
8
src/main/java/org/mmga/clubs/dao/redis/VerifyDao.java
Normal file
@ -0,0 +1,8 @@
|
||||
package org.mmga.clubs.dao.redis;
|
||||
|
||||
import org.mmga.clubs.entities.verify.VerifyVo;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
public interface VerifyDao extends CrudRepository<VerifyVo, String> {
|
||||
VerifyVo getByKey(String key);
|
||||
}
|
7
src/main/java/org/mmga/clubs/entities/ClubUserVo.java
Normal file
7
src/main/java/org/mmga/clubs/entities/ClubUserVo.java
Normal file
@ -0,0 +1,7 @@
|
||||
package org.mmga.clubs.entities;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public record ClubUserVo(int clubId, int userId, Date createTime, Date updateTime) {
|
||||
}
|
||||
|
6
src/main/java/org/mmga/clubs/entities/club/ClubVo.java
Normal file
6
src/main/java/org/mmga/clubs/entities/club/ClubVo.java
Normal file
@ -0,0 +1,6 @@
|
||||
package org.mmga.clubs.entities.club;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public record ClubVo(int id, String name, String commit, Date createTime, Date updateTime) {
|
||||
}
|
@ -3,5 +3,5 @@ package org.mmga.clubs.entities.user;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "用户登录参数类")
|
||||
public record UserLoginVo(@Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED) String username, @Schema(description = "用户密码(使用MD5摘要后的hex字符串)") String password) {
|
||||
public record UserLoginVo(@Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED) String username, @Schema(description = "用户密码(使用MD5摘要后的hex字符串)") String password, @Schema(description = "验证码密钥") String key, @Schema(description = "验证码") String code) {
|
||||
}
|
||||
|
@ -3,5 +3,5 @@ package org.mmga.clubs.entities.user;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "用户注册参数")
|
||||
public record UserRegVo(@Schema(description = "用户名") String username,@Schema(description = "密码(使用MD5摘要后的hex字符串)") String password,@Schema(description = "用户的权限组ID") int auth) {
|
||||
public record UserRegVo(@Schema(description = "用户名") String username,@Schema(description = "密码(使用MD5摘要后的hex字符串)") String password,@Schema(description = "用户的权限组ID") int auth, @Schema(description = "验证码密钥") String key, @Schema(description = "验证码") String code) {
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
package org.mmga.clubs.entities.user.change;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.redis.core.RedisHash;
|
||||
import org.springframework.data.redis.core.index.Indexed;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@RedisHash("avatar")
|
||||
public record UserAvatarOperationVo(@Id String operationCode, @Indexed String avatarSHA, @Indexed int userId, @Indexed Date createTime) {
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package org.mmga.clubs.entities.user.change;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "用户修改头像请求体")
|
||||
public record UserChangeAvatarVo(@Schema(description = "被修改的用户ID") int userId,@Schema(description = "修改头像操作码,可以通过将图片文件上传至") String avatarOperationCode) {
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package org.mmga.clubs.entities.verify;
|
||||
|
||||
public record VerifyImgVo(byte[] img, String code) {
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package org.mmga.clubs.entities.verify;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "验证码返回值")
|
||||
public record VerifyResponseVo(@Schema(description = "验证码图像base64") String img,@Schema(description = "验证码验证密钥") String key) {
|
||||
}
|
11
src/main/java/org/mmga/clubs/entities/verify/VerifyVo.java
Normal file
11
src/main/java/org/mmga/clubs/entities/verify/VerifyVo.java
Normal file
@ -0,0 +1,11 @@
|
||||
package org.mmga.clubs.entities.verify;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.redis.core.RedisHash;
|
||||
import org.springframework.data.redis.core.index.Indexed;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@RedisHash("verify")
|
||||
public record VerifyVo(@Id String key, @Indexed String answer, @Indexed Date createTime) {
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package org.mmga.clubs.schedule;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.mmga.clubs.dao.redis.AvatarDao;
|
||||
import org.mmga.clubs.dao.redis.VerifyDao;
|
||||
import org.mmga.clubs.entities.user.change.UserAvatarOperationVo;
|
||||
import org.mmga.clubs.entities.verify.VerifyVo;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class RemoveTempAvatarSchedule {
|
||||
private final AvatarDao avatarDao;
|
||||
private final File tempDir;
|
||||
private final VerifyDao verifyDao;
|
||||
|
||||
@Autowired
|
||||
public RemoveTempAvatarSchedule(AvatarDao avatarDao, File tempDir, VerifyDao verifyDao){
|
||||
this.avatarDao = avatarDao;
|
||||
this.tempDir = tempDir;
|
||||
this.verifyDao = verifyDao;
|
||||
}
|
||||
@Scheduled(fixedDelay = 1000)
|
||||
public void removeTempAvatar() {
|
||||
for (UserAvatarOperationVo userAvatarOperationVo : avatarDao.findAll()) {
|
||||
long i = new Date().getTime() - userAvatarOperationVo.createTime().getTime();
|
||||
if (i >= 300000){
|
||||
avatarDao.delete(userAvatarOperationVo);
|
||||
String s = userAvatarOperationVo.avatarSHA();
|
||||
File file = new File(this.tempDir, s);
|
||||
String absolutePath = file.getAbsolutePath();
|
||||
log.info("删除了临时头像文件:{}", absolutePath);
|
||||
if (file.exists()) {
|
||||
if (!file.delete()) {
|
||||
log.error("无法删除临时头像文件:{}", absolutePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Scheduled(fixedDelay = 1000)
|
||||
public void removeExpiredVerify() {
|
||||
for (VerifyVo verifyVo : verifyDao.findAll()) {
|
||||
long i = new Date().getTime() - verifyVo.createTime().getTime();
|
||||
if (i >= 300000){
|
||||
verifyDao.delete(verifyVo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,33 +1,66 @@
|
||||
package org.mmga.clubs.service;
|
||||
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.mmga.clubs.dao.UserDao;
|
||||
import org.mmga.clubs.dao.redis.AvatarDao;
|
||||
import org.mmga.clubs.entities.BaseResponse;
|
||||
import org.mmga.clubs.entities.Pager;
|
||||
import org.mmga.clubs.entities.user.*;
|
||||
import org.mmga.clubs.entities.user.change.UserAvatarOperationVo;
|
||||
import org.mmga.clubs.entities.user.change.UserChangeAuthVo;
|
||||
import org.mmga.clubs.entities.user.change.UserChangePasswordVo;
|
||||
import org.mmga.clubs.entities.user.change.UserRenameVo;
|
||||
import org.mmga.clubs.entities.verify.VerifyImgVo;
|
||||
import org.mmga.clubs.entities.verify.VerifyResponseVo;
|
||||
import org.mmga.clubs.utils.JwtUtils;
|
||||
import org.mmga.clubs.utils.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class UserService {
|
||||
private final UserDao userDao;
|
||||
private final AuthService authService;
|
||||
private final JwtUtils jwtUtils;
|
||||
private final VerifyCodeService verifyCodeService;
|
||||
private final File tempDir;
|
||||
private final AvatarDao avatarDao;
|
||||
private final File avatarDir;
|
||||
|
||||
@Autowired
|
||||
public UserService(UserDao userDao, AuthService authService, JwtUtils jwtUtils){
|
||||
public UserService(UserDao userDao, AuthService authService, JwtUtils jwtUtils, VerifyCodeService verifyCodeService, File tempDir, AvatarDao avatarDao, @Qualifier("avatarDir") File avatarDir){
|
||||
this.userDao = userDao;
|
||||
this.authService = authService;
|
||||
this.jwtUtils = jwtUtils;
|
||||
this.verifyCodeService = verifyCodeService;
|
||||
this.tempDir = tempDir;
|
||||
this.avatarDao = avatarDao;
|
||||
this.avatarDir = avatarDir;
|
||||
}
|
||||
|
||||
public BaseResponse<Boolean> login(UserLoginVo user, HttpServletResponse response) {
|
||||
if (verifyCodeService.invalid(user.key(), user.code())) {
|
||||
return BaseResponse.failed(403, "验证码错误");
|
||||
}
|
||||
UserVo userVo = userDao.getUser(user.username(), DigestUtils.sha1Hex(user.password()));
|
||||
User u = packageUser(userVo);
|
||||
if (response != null && u != null){
|
||||
@ -37,6 +70,9 @@ public class UserService {
|
||||
}
|
||||
|
||||
public BaseResponse<Boolean> createUser(UserRegVo user, HttpServletResponse response) {
|
||||
if (verifyCodeService.invalid(user.key(), user.code())) {
|
||||
return BaseResponse.failed(403, "验证码错误");
|
||||
}
|
||||
String username = user.username();
|
||||
if (userDao.countUser(username) > 0) {
|
||||
return BaseResponse.failed(409, "用户已存在");
|
||||
@ -67,6 +103,7 @@ public class UserService {
|
||||
User result = new User();
|
||||
result.setId(vo.id());
|
||||
result.setName(vo.name());
|
||||
result.setAvatar(vo.avatar());
|
||||
result.setAuth(authService.getAuthById(vo.auth()));
|
||||
return result;
|
||||
}
|
||||
@ -118,4 +155,76 @@ public class UserService {
|
||||
userDao.changeAuth(userChangeAuthVo.id(), userChangeAuthVo.authId());
|
||||
return BaseResponse.success(true);
|
||||
}
|
||||
|
||||
public BaseResponse<Boolean> changeAvatar(String avatarOperationCode, int uid) {
|
||||
UserAvatarOperationVo byOperationCode = avatarDao.findByOperationCode(avatarOperationCode);
|
||||
if (byOperationCode == null) {
|
||||
return BaseResponse.failed(404, "操作码错误!");
|
||||
}
|
||||
int operationUserId = byOperationCode.userId();
|
||||
if (operationUserId != uid && !userHasPermission(uid, 4)) {
|
||||
return BaseResponse.failed(403, "权限不足");
|
||||
}
|
||||
avatarDao.delete(byOperationCode);
|
||||
String sha = byOperationCode.avatarSHA();
|
||||
userDao.changeAvatar(operationUserId, sha);
|
||||
File file = new File(avatarDir, sha);
|
||||
File tmpFile = new File(tempDir, avatarOperationCode);
|
||||
if (!tmpFile.exists()){
|
||||
return BaseResponse.failed(500, "无法找到已上传的头像信息");
|
||||
}
|
||||
if (!file.exists()){
|
||||
if (!tmpFile.renameTo(file)) {
|
||||
log.error("移动头像文件失败!");
|
||||
}
|
||||
}else {
|
||||
if (!tmpFile.delete()) {
|
||||
log.error("无法删除临时头像文件");
|
||||
}
|
||||
}
|
||||
return BaseResponse.success(true);
|
||||
}
|
||||
|
||||
public BaseResponse<VerifyResponseVo> getVerifyCode() {
|
||||
Base64.Encoder mimeEncoder = Base64.getMimeEncoder();
|
||||
VerifyImgVo verifyImgVo = verifyCodeService.generateVerifyCode();
|
||||
String s = mimeEncoder.encodeToString(verifyImgVo.img());
|
||||
return BaseResponse.success(new VerifyResponseVo(s, verifyImgVo.code()));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public BaseResponse<String> changeAvatar(MultipartFile file, int userId) {
|
||||
InputStream inputStream = file.getInputStream();
|
||||
DigestInputStream sha1 = new DigestInputStream(inputStream, MessageDigest.getInstance("SHA1"));
|
||||
String operationCode;
|
||||
while (true) {
|
||||
operationCode = StringUtils.generatorRandomString(32);
|
||||
File tmpAvatarImg = new File(tempDir, operationCode);
|
||||
if (tmpAvatarImg.exists()) {
|
||||
continue;
|
||||
}
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(tmpAvatarImg);
|
||||
StreamUtils.copy(sha1, fileOutputStream);
|
||||
fileOutputStream.close();
|
||||
sha1.close();
|
||||
break;
|
||||
}
|
||||
String sha1Result = Hex.encodeHexString(sha1.getMessageDigest().digest());
|
||||
UserAvatarOperationVo userAvatarOperationVo = new UserAvatarOperationVo(operationCode, sha1Result, userId, new Date());
|
||||
avatarDao.save(userAvatarOperationVo);
|
||||
return BaseResponse.success(operationCode);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void getAvatar(HttpServletResponse response, String sha1) {
|
||||
File avatar = new File(avatarDir, sha1);
|
||||
if (avatar.exists()){
|
||||
FileInputStream fileInputStream = new FileInputStream(avatar);
|
||||
response.setContentType("image/png");
|
||||
ServletOutputStream outputStream = response.getOutputStream();
|
||||
StreamUtils.copy(fileInputStream, outputStream);
|
||||
outputStream.close();
|
||||
fileInputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
70
src/main/java/org/mmga/clubs/service/VerifyCodeService.java
Normal file
70
src/main/java/org/mmga/clubs/service/VerifyCodeService.java
Normal file
@ -0,0 +1,70 @@
|
||||
package org.mmga.clubs.service;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.mmga.clubs.dao.redis.VerifyDao;
|
||||
import org.mmga.clubs.entities.verify.VerifyImgVo;
|
||||
import org.mmga.clubs.entities.verify.VerifyVo;
|
||||
import org.mmga.clubs.utils.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Date;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.mmga.clubs.utils.StringUtils.generatorRandomString;
|
||||
|
||||
@Service
|
||||
public class VerifyCodeService {
|
||||
private final VerifyDao verifyDao;
|
||||
|
||||
@Autowired
|
||||
public VerifyCodeService(VerifyDao verifyDao) {
|
||||
this.verifyDao = verifyDao;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public VerifyImgVo generateVerifyCode() {
|
||||
int width = 100;
|
||||
int height = 40;
|
||||
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics g = image.createGraphics();
|
||||
Font font = new Font("宋体",Font.BOLD,25);
|
||||
g.setFont(font);
|
||||
StringBuilder code = new StringBuilder();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int index = new Random().nextInt(StringUtils.source.length() - 1);
|
||||
char myCode = StringUtils.source.charAt(index);
|
||||
Random random = new Random();
|
||||
g.setColor(new Color(20+random.nextInt(120),20+random.nextInt(120),20+random.nextInt(120)));
|
||||
g.drawString(myCode+"",15+i*20,20+new Random().nextInt(10));
|
||||
for (int j = 0; j < 5; j++) {
|
||||
g.drawLine(random.nextInt(width),random.nextInt(height),random.nextInt(width),random.nextInt(height));
|
||||
}
|
||||
code.append(myCode);
|
||||
}
|
||||
g.dispose();
|
||||
String s = generatorRandomString(32);
|
||||
String codeString = code.toString();
|
||||
verifyDao.save(new VerifyVo(s, codeString, new Date()));
|
||||
try(ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()){
|
||||
ImageIO.write(image, "png", byteArrayOutputStream);
|
||||
return new VerifyImgVo(byteArrayOutputStream.toByteArray(), s);
|
||||
}
|
||||
}
|
||||
public boolean invalid(String code, String ans){
|
||||
if (code == null || ans == null){
|
||||
return true;
|
||||
}
|
||||
VerifyVo byKey = verifyDao.getByKey(code);
|
||||
verifyDao.deleteById(code);
|
||||
if (byKey == null){
|
||||
return true;
|
||||
}
|
||||
return !byKey.answer().equalsIgnoreCase(ans);
|
||||
}
|
||||
}
|
||||
|
15
src/main/java/org/mmga/clubs/utils/StringUtils.java
Normal file
15
src/main/java/org/mmga/clubs/utils/StringUtils.java
Normal file
@ -0,0 +1,15 @@
|
||||
package org.mmga.clubs.utils;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class StringUtils {
|
||||
public final static String source = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
public static String generatorRandomString(int length) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
int index = new Random().nextInt(source.length() - 1);
|
||||
builder.append(source.charAt(index));
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
@ -18,6 +18,9 @@
|
||||
<update id="changeAuth">
|
||||
update `user` set `auth` = #{authId} where `id` = #{id};
|
||||
</update>
|
||||
<update id="changeAvatar">
|
||||
update `user` set `avatar` = #{sha} where `id` = #{id};
|
||||
</update>
|
||||
<select id="getUser" resultType="org.mmga.clubs.entities.user.UserVo">
|
||||
select * from `user` where `name` = #{username} and `password` = #{password};
|
||||
</select>
|
||||
|
Loading…
x
Reference in New Issue
Block a user