Browse Source

feat: 更新业务逻辑

master
xc-yjs 8 months ago
parent
commit
f3da251960
  1. 95
      nla-common/pom.xml
  2. 28
      nla-common/src/main/java/cn/nla/common/annotation/EncryptId.java
  3. 20
      nla-common/src/main/java/cn/nla/common/annotation/ResponseResult.java
  4. 23
      nla-common/src/main/java/cn/nla/common/config/EncryptIdValidator.java
  5. 82
      nla-common/src/main/java/cn/nla/common/config/SwaggerConfiguration.java
  6. 20
      nla-common/src/main/java/cn/nla/common/config/WebConfig.java
  7. 88
      nla-common/src/main/java/cn/nla/common/enums/BizCodeEnum.java
  8. 22
      nla-common/src/main/java/cn/nla/common/exception/BizException.java
  9. 124
      nla-common/src/main/java/cn/nla/common/exception/CustomExceptionHandler.java
  10. 35
      nla-common/src/main/java/cn/nla/common/interceptor/ResponseResultInterceptor.java
  11. 35
      nla-common/src/main/java/cn/nla/common/util/CheckUtil.java
  12. 92
      nla-common/src/main/java/cn/nla/common/util/CommonUtil.java
  13. 59
      nla-common/src/main/java/cn/nla/common/util/JsonData.java
  14. 20
      nla-user-service/pom.xml
  15. 2
      nla-user-service/src/main/java/cn/nla/user/UserApplication.java
  16. 48
      nla-user-service/src/main/java/cn/nla/user/config/CaptchaConfig.java
  17. 15
      nla-user-service/src/main/java/cn/nla/user/config/OSSConfig.java
  18. 22
      nla-user-service/src/main/java/cn/nla/user/controller/AddressController.java
  19. 23
      nla-user-service/src/main/java/cn/nla/user/controller/FileController.java
  20. 112
      nla-user-service/src/main/java/cn/nla/user/controller/NotifyController.java
  21. 6
      nla-user-service/src/main/java/cn/nla/user/goup/Save.java
  22. 7
      nla-user-service/src/main/java/cn/nla/user/goup/Update.java
  23. 32
      nla-user-service/src/main/java/cn/nla/user/model/StudentBean.java
  24. 12
      nla-user-service/src/main/java/cn/nla/user/model/TeacherBean.java
  25. 8
      nla-user-service/src/main/java/cn/nla/user/service/FileService.java
  26. 6
      nla-user-service/src/main/java/cn/nla/user/service/MailService.java
  27. 55
      nla-user-service/src/main/java/cn/nla/user/service/impl/FileServiceImpl.java
  28. 47
      nla-user-service/src/main/java/cn/nla/user/service/impl/MailServiceImpl.java
  29. 34
      nla-user-service/src/main/resources/application.yml
  30. 71
      nla-user-service/src/main/resources/logback.xml
  31. 26
      nla-user-service/src/test/java/AddressTest.java
  32. 6
      pom.xml

95
nla-common/pom.xml

@ -15,42 +15,63 @@
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--项⽬中添加 spring-boot-starter-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- 代码⾃动⽣成依赖 begin -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!-- velocity -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
<!-- 代码⾃动⽣成依赖 end-->
<!--swagger ui接⼝⽂档依赖-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
</dependency>
</dependencies>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--项⽬中添加 spring-boot-starter-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--如果spring-boot版本大于2.3.x,则需要手动引入依赖-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.1.Final</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- 代码⾃动⽣成依赖 begin -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!-- velocity -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
<!-- 代码⾃动⽣成依赖 end-->
<!--swagger ui接⼝⽂档依赖-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
</dependency>
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
</dependencies>
</project>

28
nla-common/src/main/java/cn/nla/common/annotation/EncryptId.java

@ -0,0 +1,28 @@
package cn.nla.common.annotation;
import cn.nla.common.config.EncryptIdValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 自定义加密校验
*/
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {EncryptIdValidator.class})
public @interface EncryptId {
// 默认错误消息
String message() default "加密id格式错误";
// 分组
Class<?>[] groups() default {};
// 负载
Class<? extends Payload>[] payload() default {};
}

20
nla-common/src/main/java/cn/nla/common/annotation/ResponseResult.java

@ -0,0 +1,20 @@
package cn.nla.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 该注解作业于类或者方法上将返回值对象包装返回体结构
*/
@Retention(RUNTIME)
@Target({TYPE, METHOD})
@Documented
public @interface ResponseResult {
}

23
nla-common/src/main/java/cn/nla/common/config/EncryptIdValidator.java

@ -0,0 +1,23 @@
package cn.nla.common.config;
import cn.nla.common.annotation.EncryptId;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 自定义加密校验的方法
*/
public class EncryptIdValidator implements ConstraintValidator<EncryptId, String> {
// 字符串的长度: 32~256,以字母a到f或数字0到9开头。
private static final Pattern PATTERN = Pattern.compile("^[a-f\\d]{32,256}$");
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
// 不为null才进行校验
if (value != null) {
Matcher matcher = PATTERN.matcher(value);
return matcher.find();
}
return true;
}
}

82
nla-common/src/main/java/cn/nla/common/config/SwaggerConfiguration.java

@ -0,0 +1,82 @@
package cn.nla.common.config;
import lombok.Data;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import springfox.documentation.builders.*;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.schema.ScalarType;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.List;
/**
* Swagger配置
**/
@Data
@Component
@EnableOpenApi
public class SwaggerConfiguration {
/**
* 对用户服务端的接口文档
*/
@Bean
public Docket userApiDoc() {
return new Docket(DocumentationType.OAS_30) // 版本3.0
.groupName("用户端接口文档")
.pathMapping("/")
//定义是否开启Swagger,false是关闭,可以通过变量去控制,线上关闭
.enable(true)
//配置文档的元信息
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("cn.nla"))
//正则匹配请求路径,并分配到当前项目组
.paths(PathSelectors.ant("/user/**"))
.build()
// 新版SwaggerUI3.0
.globalRequestParameters(globalRequestParameters())
.globalResponses(HttpMethod.GET, getGlobalResponseMessage())
.globalResponses(HttpMethod.POST, getGlobalResponseMessage());
}
/**
* 接口基本信息配置
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("电商平台")
.description("微服务接口文档")
.contact(new Contact("yuan", "http://localhost", "yuanjs625@163.com"))
.version("v1.0")
.build();
}
/**
* 配置全局通用参数
*/
private List<RequestParameter> globalRequestParameters() {
return List.of(new RequestParameterBuilder()
.name("token")
.description("登录令牌")
.in(ParameterType.HEADER)
.query(q -> q.model(m -> m.scalarModel(ScalarType.STRING)))
.required(false)
.build());
}
/**
* 生成通用的响应信息
*/
private List<Response> getGlobalResponseMessage() {
return List.of(new ResponseBuilder()
.code("4xx")
.description("请求错误,根据code和msg检查")
.build());
}
}

20
nla-common/src/main/java/cn/nla/common/config/WebConfig.java

@ -0,0 +1,20 @@
package cn.nla.common.config;
import cn.nla.common.interceptor.ResponseResultInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
/**
* 注册拦截器
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Resource
private ResponseResultInterceptor responseResultInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(responseResultInterceptor);
}
}

88
nla-common/src/main/java/cn/nla/common/enums/BizCodeEnum.java

@ -0,0 +1,88 @@
package cn.nla.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum BizCodeEnum {
/**
* 通用操作码
*/
OPS_REPEAT(110001,"重复操作"),
/**
* 系统相关
*/
SYS_LOGIN(210001,"登录过期,请重新登录"),
SYS_NO_TOKEN(210002,"token不存在,请重新登录"),
SYS_NO_USER(210003,"loginUser对象为空"),
/**
* 购物车
*/
CART_FAIL(220001,"添加购物车失败"),
/**
*验证码
*/
CODE_TO_ERROR(240001,"接收号码不合规"),
CODE_LIMITED(240002,"验证码发送过快"),
CODE_ERROR(240003,"验证码错误"),
CODE_CAPTCHA_ERROR(240101,"图形验证码错误"),
/**
* 账号
*/
ACCOUNT_REPEAT(250001,"账号已经存在"),
ACCOUNT_UNREGISTER(250002,"账号不存在"),
ACCOUNT_PWD_ERROR(250003,"账号或者密码错误"),
ACCOUNT_UNLOGIN(250004,"账号未登录"),
/**
* 优惠券
*/
COUPON_CONDITION_ERROR(270001,"优惠券条件错误"),
COUPON_UNAVAILABLE(270002,"没有可用的优惠券"),
COUPON_NO_EXITS(270003,"优惠券不存在"),
COUPON_NO_STOCK(270005,"优惠券库存不足"),
COUPON_OUT_OF_LIMIT(270006,"优惠券领取超过限制次数"),
COUPON_OUT_OF_TIME(270407,"优惠券不在领取时间范围"),
COUPON_GET_FAIL(270407,"优惠券领取失败"),
COUPON_RECORD_LOCK_FAIL(270409,"优惠券锁定失败"),
/**
* 订单
*/
ORDER_CONFIRM_COUPON_FAIL(280001,"创建订单-优惠券使用失败,不满足价格条件"),
ORDER_CONFIRM_PRICE_FAIL(280002,"创建订单-验价失败"),
ORDER_CONFIRM_LOCK_PRODUCT_FAIL(280003,"创建订单-商品库存不足锁定失败"),
ORDER_CONFIRM_ADD_STOCK_TASK_FAIL(280004,"创建订单-新增商品库存锁定任务"),
ORDER_CONFIRM_TOKEN_NOT_EXIST(280008,"订单令牌缺少"),
ORDER_CONFIRM_TOKEN_EQUAL_FAIL(280009,"订单令牌不正确"),
ORDER_CONFIRM_NOT_EXIST(280010,"订单不存在"),
ORDER_CONFIRM_CART_ITEM_NOT_EXIST(280011,"购物车商品项不存在"),
/**
* 收货地址
*/
ADDRESS_ADD_FAIL(290001,"新增收货地址失败"),
ADDRESS_DEL_FAIL(290002,"删除收货地址失败"),
ADDRESS_NO_EXITS(290003,"地址不存在"),
/**
* 支付
*/
PAY_ORDER_FAIL(300001,"创建支付订单失败"),
PAY_ORDER_CALLBACK_SIGN_FAIL(300002,"支付订单回调验证签失败"),
PAY_ORDER_CALLBACK_NOT_SUCCESS(300003,"创建支付订单失败"),
PAY_ORDER_NOT_EXIST(300005,"订单不存在"),
PAY_ORDER_STATE_ERROR(300006,"订单状态不正常"),
PAY_ORDER_PAY_TIMEOUT(300007,"订单支付超时"),
/**
* 流控操作
*/
CONTROL_FLOW(500101,"限流控制"),
CONTROL_DEGRADE(500201,"降级控制"),
CONTROL_AUTH(500301,"认证控制"),
/**
* 文件相关
*/
FILE_UPLOAD_USER_IMG_FAIL(600101,"用户头像文件上传失败");
private int code;
private String message;
}

22
nla-common/src/main/java/cn/nla/common/exception/BizException.java

@ -0,0 +1,22 @@
package cn.nla.common.exception;
import cn.nla.common.enums.BizCodeEnum;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class BizException extends RuntimeException {
private int code;
private String msg;
public BizException(int code, String msg){
super(msg);
this.code = code;
this.msg = msg;
}
public BizException(BizCodeEnum bizCodeEnum){
super(bizCodeEnum.getMessage());
this.code = bizCodeEnum.getCode();
this.msg = bizCodeEnum.getMessage();
}
}

124
nla-common/src/main/java/cn/nla/common/exception/CustomExceptionHandler.java

@ -0,0 +1,124 @@
package cn.nla.common.exception;
import cn.nla.common.annotation.ResponseResult;
import cn.nla.common.util.JsonData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
@Slf4j
@ControllerAdvice
public class CustomExceptionHandler implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (sra == null) {
return false;
}
HttpServletRequest request = sra.getRequest();
ResponseResult responseResultAnn = (ResponseResult) request.getAttribute("RESPONSE_RESULT_ANN");
return responseResultAnn != null;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter methodParameter,
MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass,
ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
log.info("into ResponseResult handle ....");
if (body instanceof JsonData) {
return body;
}
return JsonData.buildSuccess(body);
}
@ExceptionHandler(value = Exception.class)
@ResponseBody
public JsonData handle(Exception e) {
//是不是自定义异常
if (e instanceof BizException) {
BizException bizException = (BizException) e;
log.error("[业务异常]", e);
return JsonData.buildCodeAndMsg(bizException.getCode(), bizException.getMsg());
} else {
log.error("[系统异常]", e);
return JsonData.buildError("全局异常,未知错误");
}
}
/**
* 忽略参数异常处理器
*
* @param e 忽略参数异常
*/
//@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseBody
public JsonData parameterMissingExceptionHandler(MissingServletRequestParameterException e) {
log.error("参数异常", e);
return JsonData.buildError("请求参数[" + e.getParameterName() + "]不能为空");
}
/**
* 缺少请求体异常处理器
*
* @param e 缺少请求体异常
*/
//@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseBody
public JsonData parameterBodyMissingExceptionHandler(HttpMessageNotReadableException e) {
log.error("缺少请求体异常", e);
return JsonData.buildError("参数体不能为空");
}
/**
* 参数效验异常处理器
*
* @param e 参数验证异常
*/
//@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public JsonData parameterExceptionHandler(MethodArgumentNotValidException e) {
log.error("参数效验异常", e);
BindingResult bindingResult = e.getBindingResult();
StringBuilder sb = new StringBuilder("校验失败: ");
for (FieldError fieldError : bindingResult.getFieldErrors()) {
// sb.append(fieldError.getDefaultMessage()).append(", ");
sb.append("[").append(fieldError.getField()).append("]").append(fieldError.getDefaultMessage()).append(", ");
}
return JsonData.buildError(sb.toString());
}
/**
* 自定义校验
*
* @param e 参数验证异常
*/
@ExceptionHandler({ConstraintViolationException.class})
@ResponseBody
public JsonData handleConstraintViolationException(ConstraintViolationException e) {
log.error("自定义校验异常", e);
return JsonData.buildError(e.getMessage());
}
}

35
nla-common/src/main/java/cn/nla/common/interceptor/ResponseResultInterceptor.java

@ -0,0 +1,35 @@
package cn.nla.common.interceptor;
import cn.nla.common.annotation.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
/**
* 请求拦截封装响应对象
*/
@Slf4j
@Component
public class ResponseResultInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (handler instanceof HandlerMethod) {
final HandlerMethod handlerMethod = (HandlerMethod) handler;
final Class<?> clazz = handlerMethod.getBeanType();
final Method method = handlerMethod.getMethod();
if (clazz.isAnnotationPresent(ResponseResult.class)) {
// 设置此请求返回体,需要包装,往下传递,在ResponseBodyAdvice接口中进行解析
request.setAttribute("RESPONSE_RESULT_ANN", clazz.getAnnotation(ResponseResult.class));
} else if (method.isAnnotationPresent(ResponseResult.class)) {
// 设置此请求返回体,需要包装,往下传递,在ResponseBodyAdvice接口中进行解析
request.setAttribute("RESPONSE_RESULT_ANN", method.getAnnotation(ResponseResult.class));
}
}
return true;
}
}

35
nla-common/src/main/java/cn/nla/common/util/CheckUtil.java

@ -0,0 +1,35 @@
package cn.nla.common.util;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CheckUtil {
/**
* 邮箱正则
*/
private static final Pattern MAIL_PATTERN = Pattern.compile("^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$");
/**
* 手机号正则暂时未用
*/
private static final Pattern PHONE_PATTERN = Pattern.compile("^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$");
/**
* @param email 邮箱
*/
public static boolean isEmail(String email) {
if (null == email || "".equals(email)) {
return false;
}
Matcher m = MAIL_PATTERN.matcher(email);
return m.matches();
}
/**
* @param phone 手机号
*/
public static boolean isPhone(String phone) {
if (null == phone || "".equals(phone)) {
return false;
}
Matcher m = PHONE_PATTERN.matcher(phone);
return m.matches();
}
}

92
nla-common/src/main/java/cn/nla/common/util/CommonUtil.java

@ -0,0 +1,92 @@
package cn.nla.common.util;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.util.Random;
/**
* 工具类
*/
@Slf4j
public class CommonUtil {
/**
* 获取请求的ip地址
*/
public static String getIpAddr(HttpServletRequest request) {
String ipAddress;
try {
ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (ipAddress.equals("127.0.0.1")) {
// 根据网卡取本机配置的IP
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
log.error("getIpAddress error", e);
}
if (inet != null) {
ipAddress = inet.getHostAddress();
}
}
}
// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if (ipAddress != null && ipAddress.length() > 15) {
// "***.***.***.***".length()
// = 15
if (ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
} catch (Exception e) {
log.error(" getIpAddress Exception", e);
ipAddress = "";
}
return ipAddress;
}
/**
* MD5加密
*/
public static String MD5(String data) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100), 1, 3);
}
return sb.toString().toUpperCase();
} catch (Exception e) {
log.error("MD5 error", e);
}
return null;
}
/**
* 获取验证码随机数
*
* @param length 长度
*/
public static String getRandomCode(int length) {
String sources = "0123456789";
Random random = new Random();
StringBuilder sb = new StringBuilder();
for (int j = 0; j < length; j++) {
sb.append(sources.charAt(random.nextInt(9)));
}
return sb.toString();
}
}

59
nla-common/src/main/java/cn/nla/common/util/JsonData.java

@ -0,0 +1,59 @@
package cn.nla.common.util;
import cn.nla.common.enums.BizCodeEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class JsonData {
public static final String SUCCESS_MSG = "成功";
public static final Integer SUCCESS = 0;
public static final Integer FAIL = -1;
/**
* 状态码 0 表示成功
*/
private Integer code;
/**
* 数据
*/
private Object data;
/**
* 描述
*/
private String msg;
/**
* 成功不传入数据
*/
public static JsonData buildSuccess() {
return new JsonData(SUCCESS, null, SUCCESS_MSG);
}
/**
* 成功传入数据
*/
public static JsonData buildSuccess(Object data) {
return new JsonData(SUCCESS, data, SUCCESS_MSG);
}
/**
* 失败传入描述信息
*/
public static JsonData buildError(String msg) {
return new JsonData(FAIL, null, msg);
}
/**
* 自定义状态码和错误信息
*/
public static JsonData buildCodeAndMsg(int code, String msg) {
return new JsonData(code, null, msg);
}
/**
* 传入枚举返回信息
*/
public static JsonData buildResult(BizCodeEnum codeEnum) {
return JsonData.buildCodeAndMsg(codeEnum.getCode(), codeEnum.getMessage());
}
}

20
nla-user-service/pom.xml

@ -25,5 +25,25 @@
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 图形验证码-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>kaptcha-spring-boot-starter</artifactId>
<version>1.1.0</version>
</dependency>
<!--发送邮件-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!-- OSS-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>

2
nla-user-service/src/main/java/cn/nla/user/UserApplication.java

@ -3,9 +3,11 @@ package cn.nla.user;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@MapperScan("cn.nla.*.mapper")
@ComponentScan(basePackages = {"cn.nla.*"})
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);

48
nla-user-service/src/main/java/cn/nla/user/config/CaptchaConfig.java

@ -0,0 +1,48 @@
package cn.nla.user.config;
import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
@Configuration
public class CaptchaConfig {
/**
* 验证码配置 Kaptcha配置类名
*/
@Bean
@Qualifier("captchaProducer")
public DefaultKaptcha kaptcha() {
DefaultKaptcha kaptcha = new DefaultKaptcha();
Properties properties = new Properties();
// properties.setProperty(Constants.KAPTCHA_BORDER, "yes");
// properties.setProperty(Constants.KAPTCHA_BORDER_COLOR, "220,220,220");
// properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR, "38,29,12");
// properties.setProperty(Constants.KAPTCHA_IMAGE_WIDTH, "147");
// properties.setProperty(Constants.KAPTCHA_IMAGE_HEIGHT, "34");
// properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE, "25");
// properties.setProperty(Constants.KAPTCHA_SESSION_KEY, "code");
//验证码个数
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
// properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Courier");
//字体间隔
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "8");
//干扰线颜色
// properties.setProperty(Constants.KAPTCHA_NOISE_COLOR, "white");
//干扰实现类
properties.setProperty(Constants.KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
//图片样式
properties.setProperty(Constants.KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.WaterRipple");
//文字来源
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_STRING, "0123456789");
Config config = new Config(properties);
kaptcha.setConfig(config);
return kaptcha;
}
}

15
nla-user-service/src/main/java/cn/nla/user/config/OSSConfig.java

@ -0,0 +1,15 @@
package cn.nla.user.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@ConfigurationProperties(prefix = "aliyun.oss")
@Configuration
@Data
public class OSSConfig {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketname;
}

22
nla-user-service/src/main/java/cn/nla/user/controller/AddressController.java

@ -1,21 +1,35 @@
package cn.nla.user.controller;
import cn.nla.common.annotation.ResponseResult;
import org.springframework.web.bind.annotation.RequestMapping;
import cn.nla.user.service.AddressService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 电商-公司收发货地址表 前端控制器
* </p>
*
* @author YJs
* @since 2024-07-29
*/
@Slf4j
@ResponseResult
@RestController
@RequestMapping("/addressDO")
public class AddressController {
@Resource
private AddressService addressService;
@PostMapping("query")
@ResponseResult
public Object query(@RequestHeader Map<String, String> headers) {
log.info("请求参数:{}", headers.get("token"));
return List.of();
}
}

23
nla-user-service/src/main/java/cn/nla/user/controller/FileController.java

@ -0,0 +1,23 @@
package cn.nla.user.controller;
import cn.nla.common.util.JsonData;
import cn.nla.user.service.FileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
@RestController
@RequestMapping("/file/v1")
@Slf4j
public class FileController {
@Resource
private FileService fileService;
@PostMapping(value = "/uploadFile", name = "上传文件")
public JsonData uploadImage(MultipartFile file) {
return JsonData.buildSuccess(fileService.uploadUserHeadImg(file));
}
}

112
nla-user-service/src/main/java/cn/nla/user/controller/NotifyController.java

@ -0,0 +1,112 @@
package cn.nla.user.controller;
import cn.nla.common.enums.BizCodeEnum;
import cn.nla.common.util.CheckUtil;
import cn.nla.common.util.CommonUtil;
import cn.nla.common.util.JsonData;
import cn.nla.user.service.MailService;
import com.google.code.kaptcha.Producer;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
/**
*
*/
@RestController
@RequestMapping("/user/v1")
@Slf4j
public class NotifyController {
@Resource
private Producer captchaProducer;
@Resource
private MailService mailService;
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 图形验证码有效期10分钟
*/
private static final long CAPTCHA_CODE_EXPIRED = 60 * 1000 * 10;
/**
* 获取图形验证码
*
* @param request 请求参数
* @param response 响应
*/
@ApiOperation("获取图形验证码")
@GetMapping("captcha")
public void getCaptcha(HttpServletRequest request, HttpServletResponse response) {
String captchaText = captchaProducer.createText();
log.info("图形验证码:{}", captchaText);
redisTemplate.opsForValue().set(getCaptchaKey(request), captchaText, CAPTCHA_CODE_EXPIRED, TimeUnit.MILLISECONDS);
BufferedImage bufferedImage = captchaProducer.createImage(captchaText);
ServletOutputStream outputStream;
try {
outputStream = response.getOutputStream();
ImageIO.write(bufferedImage, "jpg", outputStream);
outputStream.flush();
outputStream.close();
} catch (IOException e) {
log.error("获取图形验证码异常", e);
}
}
@ApiOperation("获取图形验证码")
@GetMapping("send")
public JsonData sendCode(String from, String to) {
String cacheKey = String.format(from + "_%s", to);
String cacheValue = redisTemplate.opsForValue().get(cacheKey);
//如果不为空,则判断是否60秒内重复发送
if (StringUtils.isNotBlank(cacheValue)) {
long ttl = Long.parseLong(cacheValue.split("_")[1]);
//当前时间戳-验证码发送时间戳,如果小于60秒,则不给重复发送
if (System.currentTimeMillis() - ttl < 1000 * 60) {
log.info("重复发送验证码,时间间隔:{} 秒", (System.currentTimeMillis() - ttl) / 1000);
return JsonData.buildResult(BizCodeEnum.CODE_LIMITED);
}
}
//拼接验证码: 验证码+时间戳: 2322_324243232424324
String code = CommonUtil.getRandomCode(6);
String value = code + "_" + System.currentTimeMillis();
redisTemplate.opsForValue().set(cacheKey, value, CAPTCHA_CODE_EXPIRED, TimeUnit.MILLISECONDS);
if (CheckUtil.isEmail(to)) {
//邮箱验证码
mailService.sendMail(to, "电商验证码", code);
log.info("发送邮箱验证码[{}]成功!", code);
return JsonData.buildSuccess();
} else if (CheckUtil.isPhone(to)) {
//短信验证码
log.info("要发送短信验证码[{}]", code);
}
return JsonData.buildResult(BizCodeEnum.CODE_TO_ERROR);
}
/**
* 获取缓存的key
*/
private String getCaptchaKey(HttpServletRequest request) {
String ip = CommonUtil.getIpAddr(request);
String userAgent = request.getHeader("User-Agent");
String key = "user-service:captcha:" + CommonUtil.MD5(ip + userAgent);
log.info("ip:{}", ip);
log.info("userAgent:{}", userAgent);
log.info("key:{}", key);
return key;
}
}

6
nla-user-service/src/main/java/cn/nla/user/goup/Save.java

@ -0,0 +1,6 @@
package cn.nla.user.goup;
/**
* 保存
*/
public interface Save {
}

7
nla-user-service/src/main/java/cn/nla/user/goup/Update.java

@ -0,0 +1,7 @@
package cn.nla.user.goup;
/**
* 更新
*/
public interface Update {
}

32
nla-user-service/src/main/java/cn/nla/user/model/StudentBean.java

@ -0,0 +1,32 @@
package cn.nla.user.model;
import cn.nla.common.annotation.EncryptId;
import cn.nla.user.goup.Save;
import cn.nla.user.goup.Update;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.Valid;
import javax.validation.constraints.*;
import java.io.Serializable;
import java.util.List;
@Data
public class StudentBean implements Serializable {
@NotBlank(message = "用户名不能为空")
private String name;
@NotNull(groups = {Save.class, Update.class})
@Length(min = 6, max = 20, groups = {Save.class, Update.class})
private String account;
@Min(value = 18, message = "年龄不能小于18岁")
@NotNull(message = "年龄不能为空")
private Integer age;
@Pattern(regexp = "^((13[0-9])|(14[5,7,9])|(15([0-3]|[5-9]))|(166)|(17[0,1,3,5,6,7,8])|(18[0-9])|(19[8|9]))\\d{8}$", message = "手机号格式错误")
private String phoneNum;
@Email(message = "邮箱格式错误")
private String email;
@Valid
@NotNull(message = "任课老师不能为空")
@Size(min = 1, message = "至少有一个老师")
private List<TeacherBean> teacherBeans;
@EncryptId(message = "密码格式错误")
private String password;
}

12
nla-user-service/src/main/java/cn/nla/user/model/TeacherBean.java

@ -0,0 +1,12 @@
package cn.nla.user.model;
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
@Data
public class TeacherBean {
@NotEmpty(message = "老师姓名不能为空")
private String teacherName;
@Min(value = 1, message = "学科类型从1开始计算")
private int type;
}

8
nla-user-service/src/main/java/cn/nla/user/service/FileService.java

@ -0,0 +1,8 @@
package cn.nla.user.service;
import org.springframework.web.multipart.MultipartFile;
public interface FileService {
String uploadUserHeadImg(MultipartFile file);
}

6
nla-user-service/src/main/java/cn/nla/user/service/MailService.java

@ -0,0 +1,6 @@
package cn.nla.user.service;
public interface MailService {
void sendMail(String to, String subject, String content);
}

55
nla-user-service/src/main/java/cn/nla/user/service/impl/FileServiceImpl.java

@ -0,0 +1,55 @@
package cn.nla.user.service.impl;
import cn.nla.user.config.OSSConfig;
import cn.nla.user.service.FileService;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.PutObjectResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
@Service
@Slf4j
public class FileServiceImpl implements FileService {
@Resource
private OSSConfig ossConfig;
@Override
public String uploadUserHeadImg(MultipartFile file) {
String originalFilename = file.getOriginalFilename();
//获取相关配置
String bucketName = ossConfig.getBucketname();
String endpoint = ossConfig.getEndpoint();
String accessKeyId = ossConfig.getAccessKeyId();
String accessKeySecret = ossConfig.getAccessKeySecret();
//创建oss对象
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
//构建路径
LocalDateTime ldt = LocalDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String folder = dtf.format(ldt);
String fileName = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
assert originalFilename != null;
String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
//在oss上创建文件夹test路径
String newFileName = "test/" + folder + "/" + fileName + extension;
try {
PutObjectResult result = ossClient.putObject(bucketName, newFileName, file.getInputStream());
//返回访问路径
if (null != result) {
return "https://" + bucketName + "." + endpoint + "/" + newFileName;
}
} catch (Exception e) {
log.error("上传头像失败", e);
} finally {
// 关闭OSS服务
ossClient.shutdown();
}
return null;
}
}

47
nla-user-service/src/main/java/cn/nla/user/service/impl/MailServiceImpl.java

@ -0,0 +1,47 @@
package cn.nla.user.service.impl;
import cn.nla.user.service.MailService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 发送邮件
*/
@Service
@Slf4j
public class MailServiceImpl implements MailService {
/**
* springboot 提供的一个发送邮件的简单抽象直接注入即可
*/
@Resource
private JavaMailSender mailSender;
@Value("${spring.mail.from}")
private String from;
/**
* 发送邮件
*
* @param to 收件人
* @param subject 主题
* @param content 内容
*/
@Override
public void sendMail(String to, String subject, String content) {
//创建一个邮箱消息对象
SimpleMailMessage message = new SimpleMailMessage();
//配置邮箱发送人
message.setFrom(from);
//邮件的收件人
message.setTo(to);
//邮件的主题
message.setSubject(subject);
//邮件的内容
message.setText(content);
mailSender.send(message);
log.info("邮件发送成功:{}", message);
}
}

34
nla-user-service/src/main/resources/application.yml

@ -2,20 +2,40 @@ server:
port: 9001
spring:
application:
name: nla-user-service
name: nla-user-service
redis:
host: 127.0.0.1
port: 6379
database: 0
#数据库配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://117.72.43.105:3306/p_nla_user?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: Yuan625621105.
mail:
host: smtp.qq.com
username: 625621105@qq.com
password: iqaadvnrobajbfef
from: 625621105@qq.com
properties.mail.smtp.starttls.enable: true
properties.mail.smtp.starttls.required: true
properties.mail.smtp.ssl.enable: true
default-encoding: utf-8
#配置plus打印sql⽇志
mybatis-plus:
configuration:
log-impl:
org.apache.ibatis.logging.stdout.StdOutImpl
log-impl:
org.apache.ibatis.logging.stdout.StdOutImpl
#设置⽇志级别,ERROR/WARN/INFO/DEBUG,默认是INFO以上才显示
logging:
level:
root: INFO
#logging:
# level:
# root: INFO
#阿里云OSS配置
aliyun:
oss:
endpoint: oss-cn-beijing.aliyuncs.com
access-key-id: LTAI5tGbV7aVMVKSnoDwmFUY
access-key-secret: U4W5fsdxjAh5f3udizbYNFdUldD8pZ
bucketname: yzdan

71
nla-user-service/src/main/resources/logback.xml

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 自定义配置: 日志存放路径 -->
<property name="log.path" value="./logs/nla-user-service"/>
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n"/>
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 自定义配置: 系统模块日志级别控制 -->
<logger name="cn.nla" level="info"/>
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="warn"/>
<logger name="io.lettuce.core.protocol" level="ERROR"/>
<!--自定义配置: mapper打印-->
<logger name="cn.nla.user.mapper" level="DEBUG"/>
<root level="info">
<appender-ref ref="console"/>
</root>
<!--系统操作日志-->
<root level="info">
<appender-ref ref="file_info"/>
<appender-ref ref="file_error"/>
</root>
</configuration>

26
nla-user-service/src/test/java/AddressTest.java

@ -0,0 +1,26 @@
import cn.nla.user.UserApplication;
import cn.nla.user.model.AddressDO;
import cn.nla.user.service.AddressService;
import lombok.extern.slf4j.Slf4j;
import net.minidev.json.JSONArray;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = UserApplication.class)
@Slf4j
public class AddressTest {
@Resource
private AddressService addressService;
@Test
public void testAddressDetail() {
List<AddressDO> list = addressService.list();
log.info(JSONArray.toJSONString(list));
}
}

6
pom.xml

@ -85,6 +85,12 @@
<artifactId>springfox-boot-starter</artifactId>
<version>${springfox.boot.starter.version}</version>
</dependency>
<!-- OSS-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.10.2</version>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 代码库 -->

Loading…
Cancel
Save