commit 385b377b10d04ac0aab115d9a2c76c5135ef9db5 Author: forely <1605769034@qq.com> Date: Tue Mar 18 19:24:33 2025 +0800 基本实现登录注册 diff --git a/software_teamwork/README.md b/software_teamwork/README.md new file mode 100644 index 0000000..f321b36 --- /dev/null +++ b/software_teamwork/README.md @@ -0,0 +1,2 @@ +# software_teamwork + diff --git a/software_teamwork/作业截图及文件/第三次_项目任务书/珞珈岛-项目任务书v1.docx b/software_teamwork/作业截图及文件/第三次_项目任务书/珞珈岛-项目任务书v1.docx new file mode 100644 index 0000000..152a237 Binary files /dev/null and b/software_teamwork/作业截图及文件/第三次_项目任务书/珞珈岛-项目任务书v1.docx differ diff --git a/software_teamwork/作业截图及文件/第二次_第二周课后实践练习/前端/待办列表-李泽童.png b/software_teamwork/作业截图及文件/第二次_第二周课后实践练习/前端/待办列表-李泽童.png new file mode 100644 index 0000000..6b2572d Binary files /dev/null and b/software_teamwork/作业截图及文件/第二次_第二周课后实践练习/前端/待办列表-李泽童.png differ diff --git a/software_teamwork/作业截图及文件/第二次_第二周课后实践练习/后端/id查找成功.png b/software_teamwork/作业截图及文件/第二次_第二周课后实践练习/后端/id查找成功.png new file mode 100644 index 0000000..a23f4ae Binary files /dev/null and b/software_teamwork/作业截图及文件/第二次_第二周课后实践练习/后端/id查找成功.png differ diff --git a/software_teamwork/作业截图及文件/第二次_第二周课后实践练习/后端/数据库可视化.png b/software_teamwork/作业截图及文件/第二次_第二周课后实践练习/后端/数据库可视化.png new file mode 100644 index 0000000..7d446e8 Binary files /dev/null and b/software_teamwork/作业截图及文件/第二次_第二周课后实践练习/后端/数据库可视化.png differ diff --git a/software_teamwork/作业截图及文件/第二次_第二周课后实践练习/后端/注册请求成功.png b/software_teamwork/作业截图及文件/第二次_第二周课后实践练习/后端/注册请求成功.png new file mode 100644 index 0000000..128e6fe Binary files /dev/null and b/software_teamwork/作业截图及文件/第二次_第二周课后实践练习/后端/注册请求成功.png differ diff --git a/software_teamwork/作业截图及文件/第二次_第二周课后实践练习/后端/用户名查找成功.png b/software_teamwork/作业截图及文件/第二次_第二周课后实践练习/后端/用户名查找成功.png new file mode 100644 index 0000000..dbdb555 Binary files /dev/null and b/software_teamwork/作业截图及文件/第二次_第二周课后实践练习/后端/用户名查找成功.png differ diff --git a/software_teamwork/作业截图及文件/第四次_AI辅助项目选题和获取需求/chat with ai.docx b/software_teamwork/作业截图及文件/第四次_AI辅助项目选题和获取需求/chat with ai.docx new file mode 100644 index 0000000..dbc75c4 Binary files /dev/null and b/software_teamwork/作业截图及文件/第四次_AI辅助项目选题和获取需求/chat with ai.docx differ diff --git a/software_teamwork/作业截图及文件/第四次_AI辅助项目选题和获取需求/chat with deepseek.png b/software_teamwork/作业截图及文件/第四次_AI辅助项目选题和获取需求/chat with deepseek.png new file mode 100644 index 0000000..6409874 Binary files /dev/null and b/software_teamwork/作业截图及文件/第四次_AI辅助项目选题和获取需求/chat with deepseek.png differ diff --git a/software_teamwork/作业截图及文件/第四次_AI辅助项目选题和获取需求/珞珈岛-项目任务书v2.docx b/software_teamwork/作业截图及文件/第四次_AI辅助项目选题和获取需求/珞珈岛-项目任务书v2.docx new file mode 100644 index 0000000..a0c4d26 Binary files /dev/null and b/software_teamwork/作业截图及文件/第四次_AI辅助项目选题和获取需求/珞珈岛-项目任务书v2.docx differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/.gitignore b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/compiler.xml b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/compiler.xml new file mode 100644 index 0000000..8b9a28a --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/compiler.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/dataSources.xml b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/dataSources.xml new file mode 100644 index 0000000..2cdd411 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/dataSources.xml @@ -0,0 +1,31 @@ + + + + + mysql.8 + true + true + com.mysql.cj.jdbc.Driver + jdbc:mysql://192.168.59.129:3306/luojia_channel?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai + + + + + + $ProjectFileDir$ + + + redis + true + true + jdbc.RedisDriver + jdbc:redis://192.168.59.129:6379/0 + + + + + + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/encodings.xml b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/encodings.xml new file mode 100644 index 0000000..e845c72 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/encodings.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/jarRepositories.xml b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/jarRepositories.xml new file mode 100644 index 0000000..abb532a --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/misc.xml b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/misc.xml new file mode 100644 index 0000000..fdc35ea --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/settings.json b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/settings.json new file mode 100644 index 0000000..152dce8 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/settings.json @@ -0,0 +1,17 @@ + +{ + "luatools_path": "~/go/bin", + "dto_dir": "./ngx_conf/dto", + "swagger.docs.path": "./docs", + "swagger.excludes": "./client", + "swagger.file.type": "json", + "swagger.main.lua.path": "./main.lua", + "swagger.name": "swagger", + "swagger.search_dirs": "./ngx_conf,./config/cn/online", + "validator_dir": "./ngx_conf/validator", + "yapi.config.file": "docs/swagger-yapi.json", + "yapi.config.mode": "mergin", + "yapi.config.server": "https://api.yapi.net", + "yapi.config.token": "xxxxxxxxx" +} + \ No newline at end of file diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/sqldialects.xml b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/sqldialects.xml new file mode 100644 index 0000000..36966c2 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/sqldialects.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/uiDesigner.xml b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/vcs.xml b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/vcs.xml new file mode 100644 index 0000000..288b36b --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/pom.xml b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/pom.xml new file mode 100644 index 0000000..cc28f5e --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + + com.luojia + luojia_channel + 1.0.0 + + + common + + + + + + org.redisson + redisson-spring-boot-starter + + + + + org.apache.commons + commons-lang3 + 3.14.0 + + + com.alibaba + transmittable-thread-local + 2.13.0 + + + + io.jsonwebtoken + jjwt-api + 0.11.5 + + + io.jsonwebtoken + jjwt-impl + 0.11.5 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.11.5 + runtime + + + + org.springframework + spring-web + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/advice/GlobalExceptionHandler.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/advice/GlobalExceptionHandler.java new file mode 100644 index 0000000..7034f3b --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/advice/GlobalExceptionHandler.java @@ -0,0 +1,22 @@ +package com.luojia_channel.common.advice; + + +import com.luojia_channel.common.domain.Result; +import com.luojia_channel.common.exception.BaseException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +// 全局异常处理 +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + // 处理自定义业务异常 + @ExceptionHandler(BaseException.class) + public Object handleBadRequestException(BaseException e) { + log.error("自定义异常 -> {} , 异常原因:{} ",e.getClass().getName(), e.getMessage()); + log.debug("", e); + return Result.fail(e.getErrorCode(), e.getErrorMessage()); + } + +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/config/MybatisConfig.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/config/MybatisConfig.java new file mode 100644 index 0000000..5c2b3bf --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/config/MybatisConfig.java @@ -0,0 +1,7 @@ +package com.luojia_channel.common.config; + +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MybatisConfig { +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/config/RedisConfig.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/config/RedisConfig.java new file mode 100644 index 0000000..4502db3 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/config/RedisConfig.java @@ -0,0 +1,45 @@ +package com.luojia_channel.common.config; + +import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +public class RedisConfig { + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + // 设置key和value的序列化规则 + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new GenericFastJsonRedisSerializer()); + redisTemplate.setHashKeySerializer(new StringRedisSerializer()); + redisTemplate.setHashValueSerializer(new GenericFastJsonRedisSerializer()); + + return redisTemplate; + } + + // Redisson分布式锁 + @Value("${spring.data.redis.host}") + private String host; + @Value("${spring.data.redis.port}") + private String port; + @Value("${spring.data.redis.password}") + private String password; + @Bean + public RedissonClient redissonClient(){ + //配置 + Config config=new Config(); + config.useSingleServer().setAddress("redis://"+host+":"+port).setPassword(password); + //创建对并且返回 + return Redisson.create(config); + } +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/config/WebMvcConfig.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/config/WebMvcConfig.java new file mode 100644 index 0000000..c1ea840 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/config/WebMvcConfig.java @@ -0,0 +1,30 @@ +package com.luojia_channel.common.config; + +import com.luojia_channel.common.interceptor.AuthInterceptor; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +@RequiredArgsConstructor +public class WebMvcConfig implements WebMvcConfigurer { + private final AuthInterceptor authInterceptor; + @Override + public void addInterceptors(InterceptorRegistry registry) { + // 登录拦截器 + registry.addInterceptor(authInterceptor) + .excludePathPatterns("/user/login", + "/user/register" + ); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/constants/RedisConstant.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/constants/RedisConstant.java new file mode 100644 index 0000000..2f86484 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/constants/RedisConstant.java @@ -0,0 +1,8 @@ +package com.luojia_channel.common.constants; + +public class RedisConstant { + // redis存储的refreshToken前缀 + public static final String REFRESH_TOKEN_PREFIX = "refresh_token:"; + // redis存储的黑名单前缀 + public static final String BLACKLIST_PREFIX = "blacklist:"; +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/domain/Result.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/domain/Result.java new file mode 100644 index 0000000..5c754b5 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/domain/Result.java @@ -0,0 +1,28 @@ +package com.luojia_channel.common.domain; + +import lombok.Data; + +// 统一返回前端的结果 +@Data +public class Result { + private int code; + private String msg; + private T data; + public Result(int code, String msg, T data) { + this.code = code; + this.msg = msg; + this.data = data; + } + public static Result success(T data) { + return new Result<>(200, "success", data); + } + public static Result success() { + return new Result<>(200, "success", null); + } + public static Result fail(String msg) { + return new Result<>(500, msg, null); + } + public static Result fail(int code, String msg) { + return new Result<>(code, msg, null); + } +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/domain/UserDTO.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/domain/UserDTO.java new file mode 100644 index 0000000..3d2feeb --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/domain/UserDTO.java @@ -0,0 +1,17 @@ +package com.luojia_channel.common.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UserDTO { + private Long userId; + private String username; + private String accessToken; + private String refreshToken; +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/exception/BaseException.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/exception/BaseException.java new file mode 100644 index 0000000..76fc30a --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/exception/BaseException.java @@ -0,0 +1,15 @@ +package com.luojia_channel.common.exception; + +import lombok.Getter; + +@Getter +public class BaseException extends RuntimeException { + private final int errorCode; + private final String errorMessage; + + public BaseException(int errorCode, String errorMessage) { + super(errorMessage); + this.errorCode = errorCode; + this.errorMessage = errorMessage; + } +} \ No newline at end of file diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/exception/UserException.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/exception/UserException.java new file mode 100644 index 0000000..b243e3e --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/exception/UserException.java @@ -0,0 +1,7 @@ +package com.luojia_channel.common.exception; + +public class UserException extends BaseException{ + public UserException(String msg){ + super(500, msg); + } +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/interceptor/AuthInterceptor.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/interceptor/AuthInterceptor.java new file mode 100644 index 0000000..c937694 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/interceptor/AuthInterceptor.java @@ -0,0 +1,50 @@ +package com.luojia_channel.common.interceptor; + +import com.alibaba.fastjson.JSON; +import com.luojia_channel.common.domain.Result; +import com.luojia_channel.common.domain.UserDTO; +import com.luojia_channel.common.exception.UserException; +import com.luojia_channel.common.utils.JWTUtil; +import com.luojia_channel.common.utils.UserContext; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +@Component +@RequiredArgsConstructor +public class AuthInterceptor implements HandlerInterceptor { + + private final JWTUtil jwtUtil; + + @Override + public boolean preHandle(HttpServletRequest request, + HttpServletResponse response, + Object handler) throws Exception { + // 获取请求中的双Token + String accessToken = request.getHeader("Authorization"); + String refreshToken = request.getHeader("X-Refresh-Token"); + try { + // 验证Token并处理自动刷新 + UserDTO user = jwtUtil.checkLogin(accessToken, refreshToken); + // 将新Token写入响应头 + if (user.getAccessToken() != null) { + response.setHeader("New-Access-Token", JWTUtil.TOKEN_PREFIX + user.getAccessToken()); + response.setHeader("New-Refresh-Token", user.getRefreshToken()); + } + UserContext.setUser(user); + return true; + } catch (UserException ex) { + // Token验证失败处理 + response.setContentType("application/json"); + response.setCharacterEncoding("UTF-8"); + response.setStatus(HttpStatus.UNAUTHORIZED.value()); + response.getWriter().write( + JSON.toJSONString(Result.fail(ex.getMessage())) + ); + return false; + } + } +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/utils/JWTUtil.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/utils/JWTUtil.java new file mode 100644 index 0000000..e3c134a --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/utils/JWTUtil.java @@ -0,0 +1,179 @@ +package com.luojia_channel.common.utils; + +import com.alibaba.fastjson.JSON; +import com.luojia_channel.common.domain.UserDTO; +import com.luojia_channel.common.exception.UserException; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static com.luojia_channel.common.constants.RedisConstant.BLACKLIST_PREFIX; +import static com.luojia_channel.common.constants.RedisConstant.REFRESH_TOKEN_PREFIX; + +@Slf4j +@Component +@RequiredArgsConstructor +public final class JWTUtil { + + private static final long ACCESS_EXPIRATION = 60 * 60 * 1000; //一小时 + private static final long REFRESH_EXPIRATION = 60 * 60 * 24 * 15 * 1000; //15天 + private static final Long NEED_REFRESH_TTL = 7L; + private static final String USER_ID_KEY = "userId"; + private static final String USER_NAME_KEY = "username"; + public static final String TOKEN_PREFIX = "Bearer "; + public static final String ISS = "luojiachannel"; + public static final String SECRET = "SecretKey5464Created2435By54377Forely02345239354893543157956476525685754352976546564766315468763584576"; + + private final RedisUtil redisUtil; + + /** + * 生成accessToken + * @param userInfo + * @return + */ + public String generateAccessToken(UserDTO userInfo) { + Map customerUserMap = new HashMap<>(); + customerUserMap.put(USER_ID_KEY, userInfo.getUserId()); + customerUserMap.put(USER_NAME_KEY, userInfo.getUsername()); + String jwtToken = Jwts.builder() + .signWith(SignatureAlgorithm.HS512, SECRET) + .setIssuedAt(new Date()) + .setIssuer(ISS) + .setSubject(JSON.toJSONString(customerUserMap)) + .setExpiration(new Date(System.currentTimeMillis() + ACCESS_EXPIRATION)) + .compact(); + return jwtToken; + } + + /** + * 生成refreshToken + * @param userInfo + * @return + */ + public String generateRefreshToken(UserDTO userInfo) { + Map customerUserMap = new HashMap<>(); + customerUserMap.put(USER_ID_KEY, userInfo.getUserId()); + customerUserMap.put(USER_NAME_KEY, userInfo.getUsername()); + String jwtToken = Jwts.builder() + .signWith(SignatureAlgorithm.HS512, SECRET) + .setIssuedAt(new Date()) + .setIssuer(ISS) + .setSubject(JSON.toJSONString(customerUserMap)) + .setExpiration(new Date(System.currentTimeMillis() + REFRESH_EXPIRATION)) + .compact(); + return jwtToken; + } + + /** + * 解析token获得用户 + * @param accessToken + * @return + */ + public UserDTO parseJwtToken(String accessToken) { + if (!StringUtils.hasText(accessToken) || !accessToken.startsWith(TOKEN_PREFIX)) { + throw new UserException("Token格式错误"); + } + String rawAccessToken = accessToken.replace(TOKEN_PREFIX, ""); + try { + Claims claims = parseToken(rawAccessToken); + Date expiration = claims.getExpiration(); + if (expiration.after(new Date())) { + String subject = claims.getSubject(); + return JSON.parseObject(subject, UserDTO.class); + } + } catch (ExpiredJwtException ignored) { + } catch (Exception ex) { + log.error("JWT Token解析失败,请检查", ex); + } + return null; + } + + /** + * 检查登录 + * @param accessToken + * @param refreshToken + * @return + */ + public UserDTO checkLogin(String accessToken, String refreshToken) { + if (!StringUtils.hasText(accessToken) || !accessToken.startsWith(TOKEN_PREFIX)) { + throw new UserException("Token格式错误"); + } + String rawAccessToken = accessToken.replace(TOKEN_PREFIX, ""); + // 在黑名单中 + if (redisUtil.hasKey(BLACKLIST_PREFIX + rawAccessToken)) { + throw new UserException("Token已失效"); + } + try { + Claims claims = parseToken(rawAccessToken); + UserDTO user = extractUserFromClaims(claims); + if (needRefresh(claims)) { + return refreshTokens(user, refreshToken); + } + return user; + } catch (ExpiredJwtException ex) { + // accessToken过期,尝试用refreshToken刷新 + // 注意,这里不能直接用过期时间与现在时间做差判断,因为过期会抛异常 + return handleExpiredToken(ex, refreshToken); + } catch (Exception ex) { + throw new UserException("Token无效"); + } + } + + private boolean needRefresh(Claims claims) { + long remaining = claims.getExpiration().getTime() - System.currentTimeMillis(); + return remaining < 30 * 60 * 1000; // 剩余30分钟刷新 + } + + private UserDTO refreshTokens(UserDTO user, String refreshToken) { + String redisKey = REFRESH_TOKEN_PREFIX + user.getUserId(); + String storedRefreshToken = redisUtil.get(redisKey, String.class); + if (!refreshToken.equals(storedRefreshToken)) { + throw new UserException("refreshToken无效"); + } + // 生成新token并更新redis + String newAccessToken = generateAccessToken(user); + String newRefreshToken = generateRefreshToken(user); + // 惰性刷新refreshToken + Long ttl = redisUtil.getExpire(redisKey, TimeUnit.DAYS); + if(ttl < NEED_REFRESH_TTL) + redisUtil.set(redisKey, newRefreshToken, REFRESH_EXPIRATION, TimeUnit.MILLISECONDS); + user.setAccessToken(newAccessToken); + + return user; + } + + private UserDTO handleExpiredToken(ExpiredJwtException ex, String refreshToken) { + // 从过期token中提取用户信息 + UserDTO user = extractUserFromClaims(ex.getClaims()); + // 刷新双 Token + return refreshTokens(user, refreshToken); + } + + private Claims parseToken(String token) { + return Jwts.parser() + .setSigningKey(SECRET) + .parseClaimsJws(token) + .getBody(); + } + + private UserDTO extractUserFromClaims(Claims claims) { + String subject = claims.getSubject(); + return JSON.parseObject(subject, UserDTO.class); + } + + public long getRemainingTime(String rawToken){ + Claims claims = parseToken(rawToken); + return claims.getExpiration().getTime()-System.currentTimeMillis(); + } + +} \ No newline at end of file diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/utils/RedisUtil.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/utils/RedisUtil.java new file mode 100644 index 0000000..25f0160 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/utils/RedisUtil.java @@ -0,0 +1,168 @@ +package com.luojia_channel.common.utils; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ZSetOperations; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Component +@RequiredArgsConstructor +public class RedisUtil { + + private final RedisTemplate redisTemplate; + private static final Long DEFAULT_TIMEOUT = 30000L; + private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.MILLISECONDS; + + /** + * redis基础操作 + */ + + public T get(String key, Class type) { + Object value = redisTemplate.opsForValue().get(key); + return value != null ? type.cast(value) : null; + } + + public void set(String key, Object value) { + redisTemplate.opsForValue().set(key, value, DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT); + } + + public void set(String key, Object value, long timeout, TimeUnit unit) { + redisTemplate.opsForValue().set(key, value, timeout, unit); + } + + public void expire(String key, long time, TimeUnit timeUnit) { + redisTemplate.expire(key, time, timeUnit); + } + + public void delete(String key) { + redisTemplate.delete(key); + } + + public boolean hasKey(String key) { + Boolean result = redisTemplate.hasKey(key); + return result != null; + } + + public Long getExpire(String key, TimeUnit timeUnit){ + return redisTemplate.getExpire(key, timeUnit); + } + + /** + * set操作 + */ + private List convertSetToList(Set set) { + if(set == null || set.isEmpty()){ + return Collections.emptyList(); + } + return set.stream().map(obj -> (T) obj).collect(Collectors.toList()); + } + + public void sAdd(String key, Object... values) { + redisTemplate.opsForSet().add(key, values); + } + + public void sRemove(String key, Object... values) { + redisTemplate.opsForSet().remove(key, values); + } + + public List sGet(String key) { + Set members = redisTemplate.opsForSet().members(key); + return convertSetToList(members); + } + + public Boolean sIsMember(String key, Object value){ + return redisTemplate.opsForSet().isMember(key, value); + } + + public List sCommon(String key, String otherKey){ + Set intersect = redisTemplate.opsForSet().intersect(key, otherKey); + return convertSetToList(intersect); + } + + + /** + * zSet操作 + */ + // 带分数的结果包装类 + @Data + @AllArgsConstructor + public static class ZSetItem { + private T value; + private Double score; + } + private List> convertTuples(Set> tuples) { + if(tuples == null || tuples.isEmpty()){ + return Collections.emptyList(); + } + return tuples.stream() + .map(tuple -> new ZSetItem( + (T) tuple.getValue(), + tuple.getScore() != null ? tuple.getScore() : 0.0 + )) + .collect(Collectors.toList()); + } + + // 添加元素 + public Boolean zAdd(String key, Object value, double score) { + return redisTemplate.opsForZSet().add(key, value, score); + } + + public Boolean zAdd(String key, Object value, double score, long timeout, TimeUnit unit) { + Boolean result = redisTemplate.opsForZSet().add(key, value, score); + if (Boolean.TRUE.equals(result)) { + redisTemplate.expire(key, timeout, unit); + } + return result; + } + + // 获取范围 + public List zRange(String key, long start, long end) { + Set values = redisTemplate.opsForZSet().range(key, start, end); + return convertSetToList(values); + } + + public List zRevRange(String key, long start, long end) { + Set values = redisTemplate.opsForZSet().reverseRange(key, start, end); + return convertSetToList(values); + } + + // 带分数查询(用于热度计算) + public List> zRangeWithScores(String key, long start, long end) { + Set> typedTuples = redisTemplate.opsForZSet().rangeWithScores(key, start, end); + return convertTuples(typedTuples); + } + + // 增减分数(用于点赞数统计) + public Double zIncrScore(String key, Object value, double delta) { + return redisTemplate.opsForZSet().incrementScore(key, value, delta); + } + + // 删除元素 + public Long zRemove(String key, Object... values) { + return redisTemplate.opsForZSet().remove(key, values); + } + + // 获取排名(用于热榜) + public Long zRank(String key, Object value) { + return redisTemplate.opsForZSet().rank(key, value); + } + + public Long zRevRank(String key, Object value) { + return redisTemplate.opsForZSet().reverseRank(key, value); + } + + // 获取集合大小 + public Long zCard(String key) { + return redisTemplate.opsForZSet().zCard(key); + } + + +} \ No newline at end of file diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/utils/UserContext.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/utils/UserContext.java new file mode 100644 index 0000000..279ff91 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/src/main/java/com/luojia_channel/common/utils/UserContext.java @@ -0,0 +1,41 @@ +package com.luojia_channel.common.utils; + +import com.alibaba.ttl.TransmittableThreadLocal; +import com.luojia_channel.common.domain.UserDTO; + +import java.util.Optional; + +// 用户上下文 +public final class UserContext { + + private static final ThreadLocal USER_THREAD_LOCAL = new TransmittableThreadLocal<>(); + public static void setUser(UserDTO user) { + USER_THREAD_LOCAL.set(user); + } + + public static Long getUserId() { + UserDTO userInfoDTO = USER_THREAD_LOCAL.get(); + return Optional.ofNullable(userInfoDTO).map(UserDTO::getUserId).orElse(null); + } + + + public static String getUsername() { + UserDTO userInfoDTO = USER_THREAD_LOCAL.get(); + return Optional.ofNullable(userInfoDTO).map(UserDTO::getUsername).orElse(null); + } + + + public static String getAccessToken() { + UserDTO userInfoDTO = USER_THREAD_LOCAL.get(); + return Optional.ofNullable(userInfoDTO).map(UserDTO::getAccessToken).orElse(null); + } + + public static String getRefreshToken() { + UserDTO userInfoDTO = USER_THREAD_LOCAL.get(); + return Optional.ofNullable(userInfoDTO).map(UserDTO::getRefreshToken).orElse(null); + } + + public static void removeUser() { + USER_THREAD_LOCAL.remove(); + } +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/advice/GlobalExceptionHandler.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/advice/GlobalExceptionHandler.class new file mode 100644 index 0000000..e387a87 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/advice/GlobalExceptionHandler.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/config/MybatisConfig.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/config/MybatisConfig.class new file mode 100644 index 0000000..c239e22 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/config/MybatisConfig.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/config/RedisConfig.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/config/RedisConfig.class new file mode 100644 index 0000000..10d6bc4 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/config/RedisConfig.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/config/WebMvcConfig.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/config/WebMvcConfig.class new file mode 100644 index 0000000..3ccb3b2 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/config/WebMvcConfig.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/constants/RedisConstant.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/constants/RedisConstant.class new file mode 100644 index 0000000..8fe2bfe Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/constants/RedisConstant.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/domain/Result.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/domain/Result.class new file mode 100644 index 0000000..ea2fa90 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/domain/Result.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/domain/UserDTO$UserDTOBuilder.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/domain/UserDTO$UserDTOBuilder.class new file mode 100644 index 0000000..6ff591a Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/domain/UserDTO$UserDTOBuilder.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/domain/UserDTO.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/domain/UserDTO.class new file mode 100644 index 0000000..0a7c2b0 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/domain/UserDTO.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/exception/BaseException.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/exception/BaseException.class new file mode 100644 index 0000000..6146da1 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/exception/BaseException.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/exception/UserException.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/exception/UserException.class new file mode 100644 index 0000000..d98b1d1 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/exception/UserException.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/interceptor/AuthInterceptor.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/interceptor/AuthInterceptor.class new file mode 100644 index 0000000..aa34b06 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/interceptor/AuthInterceptor.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/utils/JWTUtil.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/utils/JWTUtil.class new file mode 100644 index 0000000..2051a33 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/utils/JWTUtil.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/utils/RedisUtil$ZSetItem.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/utils/RedisUtil$ZSetItem.class new file mode 100644 index 0000000..8be740e Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/utils/RedisUtil$ZSetItem.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/utils/RedisUtil.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/utils/RedisUtil.class new file mode 100644 index 0000000..4b9fa2d Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/utils/RedisUtil.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/utils/UserContext.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/utils/UserContext.class new file mode 100644 index 0000000..da05ecd Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/classes/com/luojia_channel/common/utils/UserContext.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/common-1.0.0.jar b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/common-1.0.0.jar new file mode 100644 index 0000000..15cb0ec Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/common-1.0.0.jar differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/common-1.0.0.jar.original b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/common-1.0.0.jar.original new file mode 100644 index 0000000..15239eb Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/common-1.0.0.jar.original differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/maven-archiver/pom.properties b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/maven-archiver/pom.properties new file mode 100644 index 0000000..189506b --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/maven-archiver/pom.properties @@ -0,0 +1,3 @@ +artifactId=common +groupId=com.luojia +version=1.0.0 diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..017e0fe --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,11 @@ +com\luojia\luojia_channel\advice\GlobalExceptionHandler.class +com\luojia\luojia_channel\domain\UserDTO.class +com\luojia\luojia_channel\utils\UserContext.class +com\luojia\luojia_channel\utils\JWTUtil.class +com\luojia\luojia_channel\config\RedisConfig.class +com\luojia\luojia_channel\utils\RedisUtil$ZSetItem.class +com\luojia\luojia_channel\domain\UserDTO$UserDTOBuilder.class +com\luojia\luojia_channel\exception\BaseException.class +com\luojia\luojia_channel\utils\RedisUtil.class +com\luojia\luojia_channel\domain\Result.class +com\luojia\luojia_channel\exception\UserException.class diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..4ae004b --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,9 @@ +D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\advice\GlobalExceptionHandler.java +D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\config\RedisConfig.java +D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\domain\Result.java +D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\domain\UserDTO.java +D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\exception\BaseException.java +D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\exception\UserException.java +D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\utils\JWTUtil.java +D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\utils\RedisUtil.java +D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\utils\UserContext.java diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/pom.xml b/software_teamwork/珞珈岛-项目相关文件/luojia_island/pom.xml new file mode 100644 index 0000000..6b6c0ab --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/pom.xml @@ -0,0 +1,108 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.4.3 + + + + com.luojia + luojia_channel + pom + 1.0.0 + + common + service + + + + 17 + 2024.0.0 + 3.5.7 + 3.29.0 + 8.0.33 + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + com.baomidou + mybatis-plus-spring-boot3-starter + ${mybatis-plus.version} + + + + + org.redisson + redisson-spring-boot-starter + ${redisson.version} + + + + + mysql + mysql-connector-java + ${mysql.version} + + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.projectlombok + lombok + provided + + + + + com.alibaba + fastjson + 1.2.83 + + + + + cn.hutool + hutool-all + 5.8.24 + + + + + org.springframework.boot + spring-boot-starter-security + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/pom.xml b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/pom.xml new file mode 100644 index 0000000..808083b --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + + com.luojia + luojia_channel + 1.0.0 + + + service + + + + + com.luojia + common + 1.0.0 + + + + + com.baomidou + mybatis-plus-spring-boot3-starter + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + org.springframework.boot + spring-boot-starter-amqp + + + + + mysql + mysql-connector-java + + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/LuojiaChannelApplication.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/LuojiaChannelApplication.java new file mode 100644 index 0000000..9e5e09d --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/LuojiaChannelApplication.java @@ -0,0 +1,13 @@ +package com.luojia_channel; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class LuojiaChannelApplication { + + public static void main(String[] args) { + SpringApplication.run(LuojiaChannelApplication.class, args); + } + +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/constant/UserConstant.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/constant/UserConstant.java new file mode 100644 index 0000000..eb57d5a --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/constant/UserConstant.java @@ -0,0 +1,13 @@ +package com.luojia_channel.modules.user.constant; + +public class UserConstant { + // 匿名用户前缀 + public static final String ANONYMOUS_USER_PREFIX = "luojia_"; + // 匿名用户key前缀 + public static final String ANONYMOUS_KEY_PREFIX = "anon:user:"; + // 过期时间 + public static final long EXPIRE_TIME_MINUTES = 30L; + // 过期时间 + public static final long EXPIRE_TIME = 15L; + +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/controller/UserInfoController.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/controller/UserInfoController.java new file mode 100644 index 0000000..be109f5 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/controller/UserInfoController.java @@ -0,0 +1,11 @@ +package com.luojia_channel.modules.user.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/user/info") +@RequiredArgsConstructor +public class UserInfoController { +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/controller/UserLoginController.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/controller/UserLoginController.java new file mode 100644 index 0000000..af6edb5 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/controller/UserLoginController.java @@ -0,0 +1,37 @@ +package com.luojia_channel.modules.user.controller; + +import com.luojia_channel.common.domain.Result; +import com.luojia_channel.common.domain.UserDTO; +import com.luojia_channel.modules.user.dto.UserLoginDTO; +import com.luojia_channel.modules.user.dto.UserRegisterDTO; +import com.luojia_channel.modules.user.service.UserLoginService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/user") +@RequiredArgsConstructor +public class UserLoginController { + private final UserLoginService userLoginService; + @PostMapping("/login") + public Result login(@RequestBody UserLoginDTO userLoginDTO){ + return Result.success(userLoginService.login(userLoginDTO)); + } + + @PostMapping("/register") + public Result register(@RequestBody UserRegisterDTO userRegisterDTO){ + return Result.success(userLoginService.register(userRegisterDTO)); + } + + @PostMapping("/logout") + public Result logout(@RequestParam String accessToken){ + userLoginService.logout(accessToken); + return Result.success(); + } + + @PostMapping("/hello") + public Result hello(){ + return Result.success("hello"); + } + +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/dto/UserLoginDTO.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/dto/UserLoginDTO.java new file mode 100644 index 0000000..e544b75 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/dto/UserLoginDTO.java @@ -0,0 +1,11 @@ +package com.luojia_channel.modules.user.dto; + +import lombok.Data; + +@Data +public class UserLoginDTO { + // 用户标志,支持学号,手机号,邮箱 + private String userFlag; + + private String password; +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/dto/UserRegisterDTO.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/dto/UserRegisterDTO.java new file mode 100644 index 0000000..fd149cc --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/dto/UserRegisterDTO.java @@ -0,0 +1,16 @@ +package com.luojia_channel.modules.user.dto; + +import lombok.Data; + +@Data +public class UserRegisterDTO { + private String username; + + private String password; + + private String phone; + + private String email; + + private String studentId; +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/entity/User.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/entity/User.java new file mode 100644 index 0000000..d49d1b4 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/entity/User.java @@ -0,0 +1,79 @@ +package com.luojia_channel.modules.user.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +@Data +@TableName("user") +public class User implements Serializable { + + @TableId(value = "id", type = IdType.AUTO) + private Long id; + /** + * 用户名 + */ + private String username; + + /** + * 实名 + */ + private String realName; + + /** + * 密码 + */ + private String password; + + /** + * 注册手机号 + */ + private String phone; + + /** + * 邮箱 + */ + private String email; + + /** + * 学号 + */ + private String studentId; + + /** + * 性别(0未知,1男,2女) + */ + private Integer gender; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + private LocalDateTime updateTime; + + /** + * 使用状态(1正常 2冻结) + */ + private Integer status; + + /** + * 身份(1普通用户 2管理员 3超级管理员) + */ + private Integer role; + + /** + * 用户积分 + */ + private Integer integral; + + /** + * 所属学院 + */ + private String college; + +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/mapper/UserMapper.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/mapper/UserMapper.java new file mode 100644 index 0000000..fdd724e --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/mapper/UserMapper.java @@ -0,0 +1,10 @@ +package com.luojia_channel.modules.user.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.luojia_channel.modules.user.entity.User; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface UserMapper extends BaseMapper { + +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/service/UserInfoService.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/service/UserInfoService.java new file mode 100644 index 0000000..6982d38 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/service/UserInfoService.java @@ -0,0 +1,7 @@ +package com.luojia_channel.modules.user.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.luojia_channel.modules.user.entity.User; + +public interface UserInfoService extends IService { +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/service/UserLoginService.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/service/UserLoginService.java new file mode 100644 index 0000000..9330f11 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/service/UserLoginService.java @@ -0,0 +1,18 @@ +package com.luojia_channel.modules.user.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.luojia_channel.common.domain.UserDTO; +import com.luojia_channel.modules.user.dto.UserLoginDTO; +import com.luojia_channel.modules.user.dto.UserRegisterDTO; +import com.luojia_channel.modules.user.entity.User; + +public interface UserLoginService extends IService { + + UserDTO login(UserLoginDTO userLoginDTO); + + UserDTO checkLogin(String accessToken, String refreshToken); + + void logout(String accessToken); + + UserDTO register(UserRegisterDTO userRegisterDTO); +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/service/impl/UserInfoServiceImpl.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/service/impl/UserInfoServiceImpl.java new file mode 100644 index 0000000..d72f795 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/service/impl/UserInfoServiceImpl.java @@ -0,0 +1,13 @@ +package com.luojia_channel.modules.user.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.luojia_channel.modules.user.entity.User; +import com.luojia_channel.modules.user.mapper.UserMapper; +import com.luojia_channel.modules.user.service.UserInfoService; + +import org.springframework.stereotype.Service; + +@Service +public class UserInfoServiceImpl extends ServiceImpl implements UserInfoService { + +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/service/impl/UserLoginServiceImpl.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/service/impl/UserLoginServiceImpl.java new file mode 100644 index 0000000..30169fe --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/service/impl/UserLoginServiceImpl.java @@ -0,0 +1,164 @@ +package com.luojia_channel.modules.user.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.luojia_channel.common.domain.UserDTO; +import com.luojia_channel.common.exception.UserException; +import com.luojia_channel.common.utils.UserContext; +import com.luojia_channel.modules.user.dto.UserLoginDTO; +import com.luojia_channel.modules.user.dto.UserRegisterDTO; +import com.luojia_channel.modules.user.entity.User; +import com.luojia_channel.modules.user.mapper.UserMapper; +import com.luojia_channel.modules.user.service.UserLoginService; +import com.luojia_channel.common.utils.JWTUtil; +import com.luojia_channel.common.utils.RedisUtil; +import com.luojia_channel.modules.user.utils.ValidateParameterUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.concurrent.TimeUnit; + +import static com.luojia_channel.common.constants.RedisConstant.BLACKLIST_PREFIX; +import static com.luojia_channel.common.constants.RedisConstant.REFRESH_TOKEN_PREFIX; +import static com.luojia_channel.common.utils.JWTUtil.TOKEN_PREFIX; +import static com.luojia_channel.modules.user.constant.UserConstant.*; + +@Service +@RequiredArgsConstructor +public class UserLoginServiceImpl extends ServiceImpl implements UserLoginService { + + private final UserMapper userMapper; + private final RedisUtil redisUtil; + private final JWTUtil jwtUtil; + private final ValidateParameterUtil validateParameterUtil; + private final PasswordEncoder passwordEncoder; + + /** + * 根据用户标志获得用户学号,用于登录 + * @param userFlag + * @return + */ + private User getUserByFlag(String userFlag) { + if (StrUtil.isBlank(userFlag)) { + throw new UserException("用户标识不能为空"); + } + // 使用正则表达式判断类型,之前直接判断长度,虽然不合法的数据在数据库中仍然查不到 + boolean isEmail = userFlag.contains("@"); + boolean isPhone = !isEmail && userFlag.matches(ValidateParameterUtil.PHONE_REGEX); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + if (isEmail) { + wrapper.eq(User::getEmail, userFlag); + } else if (isPhone) { + wrapper.eq(User::getPhone, userFlag); + } else { + // 默认学号登录 + wrapper.eq(User::getStudentId, userFlag); + } + User user = userMapper.selectOne(wrapper); + if (user == null) { + throw new UserException("用户不存在"); + } + return user; + } + + /** + * 生成并存储双token + * @param userDTO + */ + private void generateTokens(UserDTO userDTO){ + String accessToken = jwtUtil.generateAccessToken(userDTO); + String refreshToken = jwtUtil.generateRefreshToken(userDTO); + userDTO.setAccessToken(accessToken); + userDTO.setRefreshToken(refreshToken); + //存储refreshToken到redis + String key = "refresh_token:" + userDTO.getUserId(); + redisUtil.set(key, refreshToken, EXPIRE_TIME, TimeUnit.DAYS); + } + + /** + * 用户登录,支持学号,手机,邮箱登录 + * 双token登录,支持无感刷新,即时失效 + * @param userLoginDTO + * @return + */ + @Override + public UserDTO login(UserLoginDTO userLoginDTO) { + String userFlag = userLoginDTO.getUserFlag(); + String password = userLoginDTO.getPassword(); + User user = getUserByFlag(userFlag); + if (!passwordEncoder.matches(password, user.getPassword())) { + throw new UserException("密码错误"); + } + UserDTO userDTO = UserDTO.builder() + .userId(user.getId()) + .username(user.getUsername()) + .build(); + generateTokens(userDTO); + return userDTO; + } + + /** + * 检查登录 + * @param accessToken + * @param refreshToken + * @return + */ + @Override + public UserDTO checkLogin(String accessToken, String refreshToken) { + return jwtUtil.checkLogin(accessToken, refreshToken); + } + + /** + * 登出 + * @param accessToken + */ + @Override + public void logout(String accessToken) { + Long userId = UserContext.getUserId(); + // 删除refreshToken + String refreshKey = REFRESH_TOKEN_PREFIX + userId; + redisUtil.delete(refreshKey); + // 将accessToken加入黑名单 + if (StrUtil.isNotBlank(accessToken)) { + String rawToken = accessToken.replace(TOKEN_PREFIX, ""); + long expire = jwtUtil.getRemainingTime(rawToken); + redisUtil.set(BLACKLIST_PREFIX + rawToken, "1", expire, TimeUnit.MILLISECONDS); + } + } + + /** + * 用户注册,支持手机,邮箱,学号三选一注册,注册后分发token直接登录 + * TODO 若未来想实现验证码登录可使用UUID生成六位验证码,然后存入redis,再发送验证码 + * @param userRegisterDTO + * @return + */ + @Override + @Transactional(rollbackFor = Exception.class) + public UserDTO register(UserRegisterDTO userRegisterDTO) { + // 校验注册参数 + validateParameterUtil.validateUser(userRegisterDTO); + User user = BeanUtil.copyProperties(userRegisterDTO, User.class); + // 加密 + String encodedPassword = passwordEncoder.encode(userRegisterDTO.getPassword()); + user.setPassword(encodedPassword); + user.setCreateTime(LocalDateTime.now()); + user.setUpdateTime(LocalDateTime.now()); + save(user); + UserDTO userDTO = UserDTO.builder() + .userId(user.getId()) + .username(user.getUsername()) + .build(); + generateTokens(userDTO); + return userDTO; + } +} + + + + diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/utils/AnonymousUserUtil.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/utils/AnonymousUserUtil.java new file mode 100644 index 0000000..ca1f438 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/utils/AnonymousUserUtil.java @@ -0,0 +1,62 @@ +package com.luojia_channel.modules.user.utils; + +import cn.hutool.core.lang.UUID; +import com.luojia_channel.common.domain.UserDTO; +import com.luojia_channel.common.exception.UserException; +import com.luojia_channel.common.utils.RedisUtil; +import lombok.RequiredArgsConstructor; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +import static com.luojia_channel.modules.user.constant.UserConstant.*; + +/** + * 这是生成匿名用户的工具类,用于前端展示,并支持后端校验 + */ +@Component +@RequiredArgsConstructor +public class AnonymousUserUtil { + private final RedisUtil redisUtil; + private final RedissonClient redissonClient; + + /** + * 生成匿名用户id + * @param userDTO + * @return + */ + public String generateAnonymousId(UserDTO userDTO){ + String anonymousId = ANONYMOUS_USER_PREFIX + UUID.randomUUID().toString().replace("-", ""); + String cacheKey = ANONYMOUS_KEY_PREFIX + anonymousId; + String lockKey = ANONYMOUS_KEY_PREFIX + userDTO.getUserId(); + RLock lock = redissonClient.getLock(lockKey); + boolean tryLock = lock.tryLock(); + if(!tryLock){ + throw new UserException("匿名频繁,请稍后重试"); + } + try { + redisUtil.set(cacheKey, userDTO, EXPIRE_TIME_MINUTES, TimeUnit.MINUTES); + }finally { + lock.unlock(); + } + return anonymousId; + } + + /** + * 解析匿名用户 + * @param anonymousId + * @return + */ + public UserDTO getRealUser(String anonymousId){ + String cacheKey = ANONYMOUS_KEY_PREFIX + anonymousId; + UserDTO user = redisUtil.get(cacheKey, UserDTO.class); + if(user == null){ + throw new UserException("匿名用户不存在或已过期"); + } + return user; + } + + +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/utils/ValidateParameterUtil.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/utils/ValidateParameterUtil.java new file mode 100644 index 0000000..6be875a --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/java/com/luojia_channel/modules/user/utils/ValidateParameterUtil.java @@ -0,0 +1,98 @@ +package com.luojia_channel.modules.user.utils; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.luojia_channel.common.exception.UserException; +import com.luojia_channel.modules.user.dto.UserRegisterDTO; +import com.luojia_channel.modules.user.entity.User; +import com.luojia_channel.modules.user.mapper.UserMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class ValidateParameterUtil { + // 参数校验正则表达式,学号校验不一定正确 + public static final String PHONE_REGEX = "^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\\d{8}$"; + public static final String EMAIL_REGEX = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$"; + public static final String STUDENTID_REGEX = "^(?:\\d{12,13}|[A-Z]{2}\\d{8,10})$"; + public static final String PASSWORD_REGEX = "^\\w{4,32}$"; + private final UserMapper userMapper; + + /** + * 用户手机号,邮箱,学号三选一注册的参数校验 + * 可能需要前端在用户选择注册方式时只暴露出一个文本框 + * @param userRegisterDTO + */ + public void validateUser(UserRegisterDTO userRegisterDTO) { + String username = userRegisterDTO.getUsername(); + String password = userRegisterDTO.getPassword(); + String phone = userRegisterDTO.getPhone(); + String email = userRegisterDTO.getEmail(); + String studentId = userRegisterDTO.getStudentId(); + if (StrUtil.isBlank(username)){ + throw new UserException("用户名不能为空"); + } + if (StrUtil.isBlank(password)){ + throw new UserException("密码不能为空"); + } + + // 前端做了校验后端还要校验,保证选择一种注册方式 + int cnt = 0; + if(StrUtil.isNotBlank(phone)) cnt++; + if(StrUtil.isNotBlank(email)) cnt++; + if(StrUtil.isNotBlank(studentId)) cnt++; + if (cnt == 0) { + throw new UserException("必须填写手机号、邮箱或学号其中一种注册方式"); + } + if (cnt > 1) { + throw new UserException("只能选择一种注册方式(手机/邮箱/学号)"); + } + // 格式校验 + validateFormats(userRegisterDTO); + } + + + // 格式校验,未来更改用户信息时可能使用 + // TODO 实际上,用户更改信息校验时数据库查询的不是是否存在,而是是否等于要修改的用户 + public void validateFormats(UserRegisterDTO userRegisterDTO) { + String username = userRegisterDTO.getUsername(); + String password = userRegisterDTO.getPassword(); + String phone = userRegisterDTO.getPhone(); + String email = userRegisterDTO.getEmail(); + String studentId = userRegisterDTO.getStudentId(); + // 仅对非空字段做格式校验 + if (userMapper.exists(Wrappers.lambdaQuery() + .eq(User::getUsername, username))) { + throw new UserException("用户名已存在"); + } + if(!password.matches(PASSWORD_REGEX)){ + throw new UserException("密码格式错误"); + } + if(StrUtil.isNotBlank(phone)){ + if(!phone.matches(PHONE_REGEX)) + throw new UserException("手机号格式错误"); + if (userMapper.exists(Wrappers.lambdaQuery() + .eq(User::getPhone, phone))) { + throw new UserException("手机已存在"); + } + } + if(StrUtil.isNotBlank(email)){ + if(!email.matches(EMAIL_REGEX)) + throw new UserException("邮箱格式错误"); + if (userMapper.exists(Wrappers.lambdaQuery() + .eq(User::getEmail, email))) { + throw new UserException("邮箱已存在"); + } + } + if(StrUtil.isNotBlank(studentId)){ + if(!studentId.matches(STUDENTID_REGEX)) + throw new UserException("学号格式错误"); + if (userMapper.exists(Wrappers.lambdaQuery() + .eq(User::getStudentId, studentId))) { + throw new UserException("学号已存在"); + } + } + } + +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/resources/application-dev.yaml b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/resources/application-dev.yaml new file mode 100644 index 0000000..09c5e58 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/resources/application-dev.yaml @@ -0,0 +1 @@ +lj: diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/resources/application-local.yaml b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/resources/application-local.yaml new file mode 100644 index 0000000..e06fb6c --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/resources/application-local.yaml @@ -0,0 +1,14 @@ +# 本地开发环境 +lj: + db: + host: 192.168.59.129 + password: Forely123! + redis: + host: 192.168.59.129 + port: 6379 + password: Forely123! + rabbitmq: + host: 192.168.59.129 + port: 5672 + username: admin + password: Forely123! diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/resources/application.yaml b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/resources/application.yaml new file mode 100644 index 0000000..9b4645d --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/resources/application.yaml @@ -0,0 +1,51 @@ +server: + port: 8081 +spring: + application: + name: service + profiles: + active: local + # 数据库 + datasource: + url: jdbc:mysql://${lj.db.host}:3306/luojia_channel?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai + driver-class-name: com.mysql.cj.jdbc.Driver + username: root + password: ${lj.db.password} + # redis配置 + data: + redis: + host: ${lj.redis.host} + port: ${lj.redis.port} + password: ${lj.redis.password} + lettuce: + pool: + max-active: 10 + max-idle: 10 + min-idle: 1 + time-between-eviction-runs: 10s + # rabbitmq配置 + rabbitmq: + host: ${lj.rabbitmq.host} + port: ${lj.rabbitmq.port} + username: ${lj.rabbitmq.username} + password: ${lj.rabbitmq.password} + virtual-host: / + listener: + simple: + acknowledge-mode: manual + concurrency: 5 + max-concurrency: 10 + prefetch: 1 + +mybatis-plus: + configuration: + default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler + global-config: + db-config: + update-strategy: not_null + id-type: auto + mapper-locations: classpath*:mapper/**/*.xml + type-aliases-package: com.luojia.luojia_channel.modules.*.entity + + + diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/resources/db/luojia_channel.sql b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/resources/db/luojia_channel.sql new file mode 100644 index 0000000..94bfe34 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/resources/db/luojia_channel.sql @@ -0,0 +1,21 @@ + +CREATE TABLE `user` ( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID', + `username` VARCHAR(50) NOT NULL COMMENT '用户名', + `real_name` VARCHAR(50) COMMENT '实名', + `password` VARCHAR(100) NOT NULL COMMENT '密码', + `phone` VARCHAR(20) UNIQUE COMMENT '注册手机号', + `email` VARCHAR(100) UNIQUE COMMENT '邮箱', + `student_id` VARCHAR(20) UNIQUE COMMENT '学号', + `gender` TINYINT DEFAULT 0 COMMENT '性别(0未知,1男,2女)', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `status` INT DEFAULT 1 COMMENT '状态(1正常 2冻结)', + `role` INT DEFAULT 1 COMMENT '身份(1普通用户 2管理员 3超级管理员)', + `integral` INT DEFAULT 0 COMMENT '用户积分', + `college` VARCHAR(100) COMMENT '所属学院', + INDEX `idx_username` (`username`), + UNIQUE INDEX `uk_phone` (`phone`), + UNIQUE INDEX `uk_email` (`email`), + UNIQUE INDEX `uk_student_id` (`student_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; \ No newline at end of file diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/resources/mapper/user/UserMapper.xml b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/resources/mapper/user/UserMapper.xml new file mode 100644 index 0000000..52fee41 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/main/resources/mapper/user/UserMapper.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/test/java/com/luojia/luojia_channel/LuojiaChannelApplicationTests.java b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/test/java/com/luojia/luojia_channel/LuojiaChannelApplicationTests.java new file mode 100644 index 0000000..79abf19 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/src/test/java/com/luojia/luojia_channel/LuojiaChannelApplicationTests.java @@ -0,0 +1,14 @@ +package com.luojia.luojia_channel; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class LuojiaChannelApplicationTests { + + @Test + void contextLoads() { + + } + +} diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/application-dev.yaml b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/application-dev.yaml new file mode 100644 index 0000000..09c5e58 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/application-dev.yaml @@ -0,0 +1 @@ +lj: diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/application-local.yaml b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/application-local.yaml new file mode 100644 index 0000000..e06fb6c --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/application-local.yaml @@ -0,0 +1,14 @@ +# 本地开发环境 +lj: + db: + host: 192.168.59.129 + password: Forely123! + redis: + host: 192.168.59.129 + port: 6379 + password: Forely123! + rabbitmq: + host: 192.168.59.129 + port: 5672 + username: admin + password: Forely123! diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/application.yaml b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/application.yaml new file mode 100644 index 0000000..9b4645d --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/application.yaml @@ -0,0 +1,51 @@ +server: + port: 8081 +spring: + application: + name: service + profiles: + active: local + # 数据库 + datasource: + url: jdbc:mysql://${lj.db.host}:3306/luojia_channel?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai + driver-class-name: com.mysql.cj.jdbc.Driver + username: root + password: ${lj.db.password} + # redis配置 + data: + redis: + host: ${lj.redis.host} + port: ${lj.redis.port} + password: ${lj.redis.password} + lettuce: + pool: + max-active: 10 + max-idle: 10 + min-idle: 1 + time-between-eviction-runs: 10s + # rabbitmq配置 + rabbitmq: + host: ${lj.rabbitmq.host} + port: ${lj.rabbitmq.port} + username: ${lj.rabbitmq.username} + password: ${lj.rabbitmq.password} + virtual-host: / + listener: + simple: + acknowledge-mode: manual + concurrency: 5 + max-concurrency: 10 + prefetch: 1 + +mybatis-plus: + configuration: + default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler + global-config: + db-config: + update-strategy: not_null + id-type: auto + mapper-locations: classpath*:mapper/**/*.xml + type-aliases-package: com.luojia.luojia_channel.modules.*.entity + + + diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/LuojiaChannelApplication.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/LuojiaChannelApplication.class new file mode 100644 index 0000000..dbe2f8a Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/LuojiaChannelApplication.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/constant/UserConstant.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/constant/UserConstant.class new file mode 100644 index 0000000..e26cf5a Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/constant/UserConstant.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/controller/UserInfoController.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/controller/UserInfoController.class new file mode 100644 index 0000000..97e0f50 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/controller/UserInfoController.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/controller/UserLoginController.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/controller/UserLoginController.class new file mode 100644 index 0000000..cd089e4 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/controller/UserLoginController.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/dto/UserLoginDTO.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/dto/UserLoginDTO.class new file mode 100644 index 0000000..50769ed Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/dto/UserLoginDTO.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/dto/UserRegisterDTO.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/dto/UserRegisterDTO.class new file mode 100644 index 0000000..8898e1f Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/dto/UserRegisterDTO.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/entity/User.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/entity/User.class new file mode 100644 index 0000000..9ba01c0 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/entity/User.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/mapper/UserMapper.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/mapper/UserMapper.class new file mode 100644 index 0000000..2d727d7 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/mapper/UserMapper.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/service/UserInfoService.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/service/UserInfoService.class new file mode 100644 index 0000000..5f195b1 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/service/UserInfoService.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/service/UserLoginService.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/service/UserLoginService.class new file mode 100644 index 0000000..bd925f4 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/service/UserLoginService.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/service/impl/UserInfoServiceImpl.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/service/impl/UserInfoServiceImpl.class new file mode 100644 index 0000000..ff6e50a Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/service/impl/UserInfoServiceImpl.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/service/impl/UserLoginServiceImpl.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/service/impl/UserLoginServiceImpl.class new file mode 100644 index 0000000..439e1f2 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/service/impl/UserLoginServiceImpl.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/utils/AnonymousUserUtil.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/utils/AnonymousUserUtil.class new file mode 100644 index 0000000..e5ccb7d Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/utils/AnonymousUserUtil.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/utils/ValidateParameterUtil.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/utils/ValidateParameterUtil.class new file mode 100644 index 0000000..f425e6c Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/com/luojia_channel/modules/user/utils/ValidateParameterUtil.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/db/luojia_channel.sql b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/db/luojia_channel.sql new file mode 100644 index 0000000..94bfe34 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/db/luojia_channel.sql @@ -0,0 +1,21 @@ + +CREATE TABLE `user` ( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID', + `username` VARCHAR(50) NOT NULL COMMENT '用户名', + `real_name` VARCHAR(50) COMMENT '实名', + `password` VARCHAR(100) NOT NULL COMMENT '密码', + `phone` VARCHAR(20) UNIQUE COMMENT '注册手机号', + `email` VARCHAR(100) UNIQUE COMMENT '邮箱', + `student_id` VARCHAR(20) UNIQUE COMMENT '学号', + `gender` TINYINT DEFAULT 0 COMMENT '性别(0未知,1男,2女)', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `status` INT DEFAULT 1 COMMENT '状态(1正常 2冻结)', + `role` INT DEFAULT 1 COMMENT '身份(1普通用户 2管理员 3超级管理员)', + `integral` INT DEFAULT 0 COMMENT '用户积分', + `college` VARCHAR(100) COMMENT '所属学院', + INDEX `idx_username` (`username`), + UNIQUE INDEX `uk_phone` (`phone`), + UNIQUE INDEX `uk_email` (`email`), + UNIQUE INDEX `uk_student_id` (`student_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; \ No newline at end of file diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/mapper/user/UserMapper.xml b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/mapper/user/UserMapper.xml new file mode 100644 index 0000000..52fee41 --- /dev/null +++ b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/classes/mapper/user/UserMapper.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/test-classes/com/luojia/luojia_channel/LuojiaChannelApplicationTests.class b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/test-classes/com/luojia/luojia_channel/LuojiaChannelApplicationTests.class new file mode 100644 index 0000000..f5626a9 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/luojia_island/service/target/test-classes/com/luojia/luojia_channel/LuojiaChannelApplicationTests.class differ diff --git a/software_teamwork/珞珈岛-项目相关文件/珞珈岛-项目任务书v1.docx b/software_teamwork/珞珈岛-项目相关文件/珞珈岛-项目任务书v1.docx new file mode 100644 index 0000000..152a237 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/珞珈岛-项目任务书v1.docx differ diff --git a/software_teamwork/珞珈岛-项目相关文件/珞珈岛-项目任务书v2.docx b/software_teamwork/珞珈岛-项目相关文件/珞珈岛-项目任务书v2.docx new file mode 100644 index 0000000..a0c4d26 Binary files /dev/null and b/software_teamwork/珞珈岛-项目相关文件/珞珈岛-项目任务书v2.docx differ