提交 9c6d7531 作者: 方治民

feat: springboot v3 + openapi v3 等相关适配完成

上级 ccb648b8
...@@ -30,11 +30,11 @@ dependencies { ...@@ -30,11 +30,11 @@ dependencies {
// Optional: Doc // Optional: Doc
implementation project(":basic-common:doc") implementation project(":basic-common:doc")
implementation "com.github.xiaoymin:knife4j-spring-boot-starter:${knife4jVersion}" implementation "com.github.xiaoymin:knife4j-openapi3-jakarta-spring-boot-starter:${knife4jOpen3Version}"
// Optional: Auth // Optional: Auth
implementation project(":basic-auth") implementation project(":basic-auth")
implementation "cn.dev33:sa-token-spring-boot-starter:${saTokenVersion}" implementation "cn.dev33:sa-token-spring-boot3-starter:${saTokenVersion}"
// Optional: WebSocket && STOMP 依赖 Auth + Redis 模块 // Optional: WebSocket && STOMP 依赖 Auth + Redis 模块
implementation project(":basic-websocket") implementation project(":basic-websocket")
...@@ -59,6 +59,6 @@ dependencies { ...@@ -59,6 +59,6 @@ dependencies {
implementation "cn.hutool:hutool-core:${hutoolVersion}" implementation "cn.hutool:hutool-core:${hutoolVersion}"
implementation "cn.hutool:hutool-extra:${hutoolVersion}" implementation "cn.hutool:hutool-extra:${hutoolVersion}"
// https://github.com/vladmihalcea/hibernate-types // https://github.com/vladmihalcea/hypersistence-utils
implementation "com.vladmihalcea:hibernate-types-55:${hibernateTypesVersion}" implementation "io.hypersistence:hypersistence-utils-hibernate-60:${hibernateTypesVersion}"
} }
/* (C) 2021 YiRing, Inc. */ /* (C) 2021 YiRing, Inc. */
package com.yiring.app.config; package com.yiring.app.config;
import cn.dev33.satoken.exception.*;
import com.yiring.common.core.I18n; import com.yiring.common.core.I18n;
import com.yiring.common.core.Result; import com.yiring.common.core.Result;
import com.yiring.common.core.Status; import com.yiring.common.core.Status;
import com.yiring.common.exception.BusinessException;
import com.yiring.common.exception.FailStatusException;
import javax.validation.ConstraintViolationException;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.ClientAbortException; import org.springframework.core.annotation.Order;
import org.aspectj.bridge.AbortException;
import org.hibernate.validator.internal.engine.ConstraintViolationImpl;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/** /**
* 全局错误处理 * 全局错误处理
...@@ -33,130 +19,14 @@ import org.springframework.web.bind.annotation.ResponseStatus; ...@@ -33,130 +19,14 @@ import org.springframework.web.bind.annotation.ResponseStatus;
* 2017年11月30日 上午11:36:31 * 2017年11月30日 上午11:36:31
*/ */
@Slf4j @Slf4j
@ControllerAdvice @Order
@ResponseBody @RestControllerAdvice
@RequiredArgsConstructor @RequiredArgsConstructor
public class GlobalExceptionHandler { public class GlobalExceptionHandler {
final I18n i18n; final I18n i18n;
/** /**
* 参数校验异常
*
* @param e 异常信息
* @return 统一的校验失败信息 {@link Status#EXPECTATION_FAILED
*/
@ExceptionHandler(
{ BindException.class, MethodArgumentNotValidException.class, ConstraintViolationException.class }
)
public Result<String> validFailHandler(Exception e) {
String details = null;
if (e instanceof ConstraintViolationException) {
details = ((ConstraintViolationException) e).getConstraintViolations().iterator().next().getMessage();
} else {
BindingResult result = null;
if (e instanceof MethodArgumentNotValidException) {
result = ((MethodArgumentNotValidException) e).getBindingResult();
} else if (e instanceof BindException) {
result = ((BindException) e).getBindingResult();
}
if (result != null) {
ObjectError error = result.getAllErrors().iterator().next();
if (error instanceof FieldError fieldError) {
// TODO: 可以优化成提取 @ApiModelProperty value 中文描述
// 构建明确的字段错误提示, 例如: id 不能为 null, 如果自己填写了 message 则不追加 field 字段前缀
ConstraintViolationImpl<?> violation = error.unwrap(ConstraintViolationImpl.class);
String template = violation.getMessageTemplate();
String prefix = "";
// 如果是模板字符串, 则在消息前添加字段提示
if (template.contains("{") && template.contains("}")) {
prefix = fieldError.getField() + " ";
}
details = prefix + i18n.get(fieldError);
} else {
details = i18n.get(error);
}
}
}
return Result.no(Status.EXPECTATION_FAILED, details);
}
/**
* 不支持的HttpMethod异常
*
* @param e 异常信息
* @return 异常信息反馈 {@link Status#METHOD_NOT_ALLOWED
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Result<String> httpRequestMethodNotSupportedErrorHandler(Exception e) {
return Result.no(Status.METHOD_NOT_ALLOWED, e.getMessage());
}
/**
* 未登录异常(鉴权失败)
*
* @return 异常信息反馈 {@link Status#UNAUTHORIZED
*/
@ExceptionHandler(NotLoginException.class)
public Result<String> unauthorizedErrorHandler() {
return Result.no(Status.UNAUTHORIZED);
}
/**
* 1. 二级认证失败异常
* 2. 角色条件不满足
* 3. 权限条件不满足
* 4. HTTP Basic 验证不通过
* 5. 用户被禁止访问该服务
* 6. API 被禁用
*
* @return 异常信息反馈 {@link Status#FORBIDDEN
*/
@ExceptionHandler(
{
// https://sa-token.dev33.cn/doc.html#/up/safe-auth
NotSafeException.class,
// https://sa-token.dev33.cn/doc.html#/use/at-check
NotRoleException.class,
NotPermissionException.class,
// https://sa-token.dev33.cn/doc.html#/up/basic-auth
NotBasicAuthException.class,
// https://sa-token.dev33.cn/doc.html#/up/disable
DisableServiceException.class,
ApiDisabledException.class,
}
)
public Result<String> forbiddenHandler() {
return Result.no(Status.FORBIDDEN);
}
/**
* 自定义业务异常
*/
@ExceptionHandler(BusinessException.class)
public Result<String> businessExceptionHandler(BusinessException e) {
return Result.no(Status.BAD_REQUEST, e.getCode(), e.getMessage(), null);
}
/**
* 失败状态异常
*/
@ExceptionHandler(FailStatusException.class)
public Result<String> failStatusExceptionHandler(FailStatusException e) {
return Result.no(e.getStatus(), e.getMessage());
}
/**
* 取消请求异常(忽略)
*/
@ExceptionHandler({ ClientAbortException.class, AbortException.class, HttpMessageNotWritableException.class })
public void ignoreExceptionHandler() {}
/**
* 其他异常 * 其他异常
* *
* @param e 异常信息 * @param e 异常信息
......
...@@ -3,12 +3,12 @@ package com.yiring.app.domain.user; ...@@ -3,12 +3,12 @@ package com.yiring.app.domain.user;
import com.yiring.auth.domain.user.User; import com.yiring.auth.domain.user.User;
import com.yiring.common.domain.BasicEntity; import com.yiring.common.domain.BasicEntity;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import lombok.*; import lombok.*;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
import lombok.experimental.FieldNameConstants; import lombok.experimental.FieldNameConstants;
......
...@@ -2,11 +2,13 @@ ...@@ -2,11 +2,13 @@
package com.yiring.app.vo.user; package com.yiring.app.vo.user;
import com.yiring.common.jackson.MappingSerialize; import com.yiring.common.jackson.MappingSerialize;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import lombok.*; import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
/** /**
...@@ -15,9 +17,8 @@ import lombok.experimental.FieldDefaults; ...@@ -15,9 +17,8 @@ import lombok.experimental.FieldDefaults;
* 2022/7/13 11:36 * 2022/7/13 11:36
*/ */
@ApiModel("UserExtensionVo") @Schema(name = "UserExtensionVo", description = "用户扩展信息")
@Data @Data
@Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
...@@ -26,10 +27,13 @@ public class UserExtensionVo implements Serializable { ...@@ -26,10 +27,13 @@ public class UserExtensionVo implements Serializable {
@Serial @Serial
private static final long serialVersionUID = -2251567849918281906L; private static final long serialVersionUID = -2251567849918281906L;
@ApiModelProperty(value = "性别", example = "男") @Schema(description = "性别", example = "男", allowableValues = { "男", "女" }, type = "string")
@MappingSerialize(mapping = "0:女,1:男") @MappingSerialize(mapping = "0:女,1:男")
Integer gender; Integer gender;
@ApiModelProperty(value = "年龄", example = "18") @Schema(description = "年龄", example = "18")
Integer age; Integer age;
@Schema(description = "简介", example = "Hi")
String introduction;
} }
...@@ -10,6 +10,7 @@ import com.yiring.app.domain.user.UserExtensionRepository; ...@@ -10,6 +10,7 @@ import com.yiring.app.domain.user.UserExtensionRepository;
import com.yiring.app.vo.user.UserExtensionVo; import com.yiring.app.vo.user.UserExtensionVo;
import com.yiring.auth.domain.user.User; import com.yiring.auth.domain.user.User;
import com.yiring.auth.util.Auths; import com.yiring.auth.util.Auths;
import com.yiring.common.annotation.DownloadResponse;
import com.yiring.common.core.I18n; import com.yiring.common.core.I18n;
import com.yiring.common.core.Result; import com.yiring.common.core.Result;
import com.yiring.common.core.Status; import com.yiring.common.core.Status;
...@@ -19,16 +20,17 @@ import com.yiring.common.util.Commons; ...@@ -19,16 +20,17 @@ import com.yiring.common.util.Commons;
import com.yiring.common.util.FileUtils; import com.yiring.common.util.FileUtils;
import com.yiring.common.validation.group.Group; import com.yiring.common.validation.group.Group;
import com.yiring.common.vo.PageVo; import com.yiring.common.vo.PageVo;
import io.swagger.annotations.Api; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import javax.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.data.domain.Example; import org.springframework.data.domain.Example;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
...@@ -43,9 +45,8 @@ import org.springframework.web.bind.annotation.*; ...@@ -43,9 +45,8 @@ import org.springframework.web.bind.annotation.*;
@Slf4j @Slf4j
@Validated @Validated
@SuppressWarnings({ "deprecation" })
@ApiSupport(order = 0) @ApiSupport(order = 0)
@Api(tags = "示例", description = "Example") @Tag(name = "示例", description = "Example")
@RequestMapping("/example/") @RequestMapping("/example/")
@RestController @RestController
@RequiredArgsConstructor @RequiredArgsConstructor
...@@ -55,7 +56,7 @@ public class ExampleController { ...@@ -55,7 +56,7 @@ public class ExampleController {
final Auths auths; final Auths auths;
final UserExtensionRepository userExtensionRepository; final UserExtensionRepository userExtensionRepository;
@ApiOperation("Hello World") @Operation(summary = "Hello World")
@GetMapping @GetMapping
public Result<String> hello() { public Result<String> hello() {
return Result.ok("example.hello"); return Result.ok("example.hello");
...@@ -64,16 +65,16 @@ public class ExampleController { ...@@ -64,16 +65,16 @@ public class ExampleController {
/** /**
* 测试失败自定义状态信息输出 * 测试失败自定义状态信息输出
*/ */
@ApiOperation("测试失败") @Operation(summary = "测试失败")
@GetMapping("fail") @GetMapping("fail")
public Result<String> fail() { public Result<String> fail() {
throw BusinessException.i18n("Code.1"); throw BusinessException.i18n("Code.1");
} }
@SaCheckLogin @SaCheckLogin
@ApiOperation("分页条件查询") @Operation(summary = "分页条件查询")
@GetMapping("page") @GetMapping("page")
public Result<PageVo<String>> page(@Validated PageParam param) { public Result<PageVo<String>> page(@ParameterObject @Validated PageParam param) {
log.debug("PageParam: {}", param); log.debug("PageParam: {}", param);
String text = i18n.get("example.hello"); String text = i18n.get("example.hello");
...@@ -82,7 +83,7 @@ public class ExampleController { ...@@ -82,7 +83,7 @@ public class ExampleController {
return Result.ok(vo); return Result.ok(vo);
} }
@ApiOperation(value = "JSON 传参") @Operation(summary = "JSON 传参")
@PostMapping("json") @PostMapping("json")
public Result<PageVo<String>> json( public Result<PageVo<String>> json(
@RequestBody(required = false) @Validated(Group.Optional.class) PageParam param @RequestBody(required = false) @Validated(Group.Optional.class) PageParam param
...@@ -91,9 +92,10 @@ public class ExampleController { ...@@ -91,9 +92,10 @@ public class ExampleController {
} }
@SaIgnore @SaIgnore
@DownloadResponse
@SneakyThrows(IOException.class) @SneakyThrows(IOException.class)
@ApiOperation(value = "文件下载", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) @Operation(summary = "文件下载")
@GetMapping("download") @GetMapping(value = "download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public void download(HttpServletResponse response) { public void download(HttpServletResponse response) {
ClassPathResource resource = new ClassPathResource("static/cat.jpg"); ClassPathResource resource = new ClassPathResource("static/cat.jpg");
FileUtils.download(response, resource.getFile()); FileUtils.download(response, resource.getFile());
...@@ -101,7 +103,7 @@ public class ExampleController { ...@@ -101,7 +103,7 @@ public class ExampleController {
@SaCheckSafe @SaCheckSafe
@SaCheckLogin @SaCheckLogin
@ApiOperation("查询用户属性") @Operation(summary = "查询用户属性")
@GetMapping("findUserExtensionInfo") @GetMapping("findUserExtensionInfo")
public Result<UserExtensionVo> findUserExtensionInfo() { public Result<UserExtensionVo> findUserExtensionInfo() {
User user = auths.getLoginUser(); User user = auths.getLoginUser();
......
...@@ -19,3 +19,5 @@ logging: ...@@ -19,3 +19,5 @@ logging:
springfox.documentation.spring.web.readers.operation.CachingOperationNameGenerator: WARN springfox.documentation.spring.web.readers.operation.CachingOperationNameGenerator: WARN
# 关闭接口扫描 ApiListingReferenceScanner 日志 # 关闭接口扫描 ApiListingReferenceScanner 日志
springfox.documentation.spring.web.scanners.ApiListingReferenceScanner: WARN springfox.documentation.spring.web.scanners.ApiListingReferenceScanner: WARN
# https://github.com/spring-projects/spring-framework/issues/29612
org.springframework.core.LocalVariableTableParameterNameDiscoverer: ERROR
...@@ -12,19 +12,34 @@ spring: ...@@ -12,19 +12,34 @@ spring:
username: ${env.extra.username} username: ${env.extra.username}
password: ${env.extra.password} password: ${env.extra.password}
jpa: jpa:
database-platform: org.hibernate.dialect.PostgreSQL10Dialect database-platform: org.hibernate.dialect.PostgreSQLDialect
open-in-view: true open-in-view: true
hibernate: hibernate:
ddl-auto: update ddl-auto: update
show-sql: true show-sql: false
properties: properties:
hibernate: hibernate:
format_sql: true format_sql: true
redis: # https://github.com/spring-projects/spring-data-jpa/issues/2717
database: 5 # https://hibernate.atlassian.net/browse/HHH-15827
host: ${env.host} jakarta:
port: 6379 persistence:
password: ${env.extra.password} sharedCache:
mode: UNSPECIFIED
data:
redis:
database: 5
port: 6379
host: ${env.host}
password: ${env.extra.password}
springdoc:
default-consumes-media-type: "application/x-www-form-urlencoded"
default-produces-media-type: "application/json"
default-flat-param-object: true
override-with-generic-response: false
api-docs:
resolve-schema-properties: true
# knife4j # knife4j
knife4j: knife4j:
......
...@@ -3,3 +3,16 @@ ...@@ -3,3 +3,16 @@
example.hello=\uD83D\uDE0E Hello World example.hello=\uD83D\uDE0E Hello World
# \u4E1A\u52A1\u72B6\u6001\u7801\u9519\u8BEF\u6D88\u606F # \u4E1A\u52A1\u72B6\u6001\u7801\u9519\u8BEF\u6D88\u606F
Code.1=\u5931\u8D25 Code.1=\u5931\u8D25
Code.1000=\u7528\u6237\u4E0D\u5B58\u5728
Code.1001=\u6743\u9650\u6807\u8BC6\u91CD\u590D
Code.1002=\u89D2\u8272\u6807\u8BC6\u91CD\u590D
Code.10000=\u4E8C\u7EA7\u8BA4\u8BC1\u6821\u9A8C\u5931\u8D25
Code.10001=\u89D2\u8272\u6743\u9650\u4E0D\u8DB3
Code.10002=\u6743\u9650\u4E0D\u8DB3
Code.10003=\u672A\u6388\u6743
Code.100000=\u7528\u6237\u540D\u5DF2\u5B58\u5728
Code.100001=\u624B\u673A\u53F7\u5DF2\u5B58\u5728
Code.100002=\u90AE\u7BB1\u5DF2\u5B58\u5728
Code.100003=\u8D26\u53F7\u5BC6\u7801\u9519\u8BEF
Code.100004=\u7528\u6237\u88AB\u7981\u7528, \u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
Code.100005=\u7528\u6237\u88AB\u7981\u6B62\u767B\u5F55, \u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
...@@ -3,3 +3,16 @@ ...@@ -3,3 +3,16 @@
example.hello=\uD83D\uDE0E Hello World example.hello=\uD83D\uDE0E Hello World
# \u4E1A\u52A1\u72B6\u6001\u7801\u9519\u8BEF\u6D88\u606F # \u4E1A\u52A1\u72B6\u6001\u7801\u9519\u8BEF\u6D88\u606F
Code.1=\u5931\u8D25 Code.1=\u5931\u8D25
Code.1000=\u7528\u6237\u4E0D\u5B58\u5728
Code.1001=\u6743\u9650\u6807\u8BC6\u91CD\u590D
Code.1002=\u89D2\u8272\u6807\u8BC6\u91CD\u590D
Code.10000=\u4E8C\u7EA7\u8BA4\u8BC1\u6821\u9A8C\u5931\u8D25
Code.10001=\u89D2\u8272\u6743\u9650\u4E0D\u8DB3
Code.10002=\u6743\u9650\u4E0D\u8DB3
Code.10003=\u672A\u6388\u6743
Code.100000=\u7528\u6237\u540D\u5DF2\u5B58\u5728
Code.100001=\u624B\u673A\u53F7\u5DF2\u5B58\u5728
Code.100002=\u90AE\u7BB1\u5DF2\u5B58\u5728
Code.100003=\u8D26\u53F7\u5BC6\u7801\u9519\u8BEF
Code.100004=\u7528\u6237\u88AB\u7981\u7528, \u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
Code.100005=\u7528\u6237\u88AB\u7981\u6B62\u767B\u5F55, \u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
javax.validation.constraints.AssertFalse.message=\u53EA\u80FD\u4E3A false jakarta.validation.constraints.AssertFalse.message=\u53EA\u80FD\u4E3Afalse
javax.validation.constraints.AssertTrue.message=\u53EA\u80FD\u4E3A true jakarta.validation.constraints.AssertTrue.message=\u53EA\u80FD\u4E3Atrue
javax.validation.constraints.DecimalMax.message=\u5FC5\u987B\u5C0F\u4E8E${inclusive == true ? '\u6216\u7B49\u4E8E' : ''}{value} jakarta.validation.constraints.DecimalMax.message=\u5FC5\u987B\u5C0F\u4E8E${inclusive == true ? '\u6216\u7B49\u4E8E' : ''}{value}
javax.validation.constraints.DecimalMin.message=\u5FC5\u987B\u5927\u4E8E${inclusive == true ? '\u6216\u7B49\u4E8E' : ''}{value} jakarta.validation.constraints.DecimalMin.message=\u5FC5\u987B\u5927\u4E8E${inclusive == true ? '\u6216\u7B49\u4E8E' : ''}{value}
javax.validation.constraints.Digits.message=\u6570\u5B57\u7684\u503C\u8D85\u51FA\u4E86\u5141\u8BB8\u8303\u56F4(\u53EA\u5141\u8BB8\u5728{integer}\u4F4D\u6574\u6570\u548C{fraction}\u4F4D\u5C0F\u6570\u8303\u56F4\u5185) jakarta.validation.constraints.Digits.message=\u6570\u5B57\u7684\u503C\u8D85\u51FA\u4E86\u5141\u8BB8\u8303\u56F4(\u53EA\u5141\u8BB8\u5728{integer}\u4F4D\u6574\u6570\u548C{fraction}\u4F4D\u5C0F\u6570\u8303\u56F4\u5185)
javax.validation.constraints.Email.message=\u4E0D\u662F\u4E00\u4E2A\u5408\u6CD5\u7684\u7535\u5B50\u90AE\u4EF6\u5730\u5740 jakarta.validation.constraints.Email.message=\u4E0D\u662F\u4E00\u4E2A\u5408\u6CD5\u7684\u7535\u5B50\u90AE\u4EF6\u5730\u5740
javax.validation.constraints.Future.message=\u9700\u8981\u662F\u4E00\u4E2A\u5C06\u6765\u7684\u65F6\u95F4 jakarta.validation.constraints.Future.message=\u9700\u8981\u662F\u4E00\u4E2A\u5C06\u6765\u7684\u65F6\u95F4
javax.validation.constraints.FutureOrPresent.message=\u9700\u8981\u662F\u4E00\u4E2A\u5C06\u6765\u6216\u73B0\u5728\u7684\u65F6\u95F4 jakarta.validation.constraints.FutureOrPresent.message=\u9700\u8981\u662F\u4E00\u4E2A\u5C06\u6765\u6216\u73B0\u5728\u7684\u65F6\u95F4
javax.validation.constraints.Max.message=\u6700\u5927\u4E0D\u80FD\u8D85\u8FC7{value} jakarta.validation.constraints.Max.message=\u6700\u5927\u4E0D\u80FD\u8D85\u8FC7{value}
javax.validation.constraints.Min.message=\u6700\u5C0F\u4E0D\u80FD\u5C0F\u4E8E{value} jakarta.validation.constraints.Min.message=\u6700\u5C0F\u4E0D\u80FD\u5C0F\u4E8E{value}
javax.validation.constraints.Negative.message=\u5FC5\u987B\u662F\u8D1F\u6570 jakarta.validation.constraints.Negative.message=\u5FC5\u987B\u662F\u8D1F\u6570
javax.validation.constraints.NegativeOrZero.message=\u5FC5\u987B\u662F\u8D1F\u6570\u6216\u96F6 jakarta.validation.constraints.NegativeOrZero.message=\u5FC5\u987B\u662F\u8D1F\u6570\u6216\u96F6
javax.validation.constraints.NotBlank.message=\u4E0D\u80FD\u4E3A\u7A7A jakarta.validation.constraints.NotBlank.message=\u4E0D\u80FD\u4E3A\u7A7A
javax.validation.constraints.NotEmpty.message=\u4E0D\u80FD\u4E3A\u7A7A jakarta.validation.constraints.NotEmpty.message=\u4E0D\u80FD\u4E3A\u7A7A
javax.validation.constraints.NotNull.message=\u4E0D\u80FD\u4E3A null jakarta.validation.constraints.NotNull.message=\u4E0D\u80FD\u4E3Anull
javax.validation.constraints.Null.message=\u5FC5\u987B\u4E3A null jakarta.validation.constraints.Null.message=\u5FC5\u987B\u4E3Anull
javax.validation.constraints.Past.message=\u9700\u8981\u662F\u4E00\u4E2A\u8FC7\u53BB\u7684\u65F6\u95F4 jakarta.validation.constraints.Past.message=\u9700\u8981\u662F\u4E00\u4E2A\u8FC7\u53BB\u7684\u65F6\u95F4
javax.validation.constraints.PastOrPresent.message=\u9700\u8981\u662F\u4E00\u4E2A\u8FC7\u53BB\u6216\u73B0\u5728\u7684\u65F6\u95F4 jakarta.validation.constraints.PastOrPresent.message=\u9700\u8981\u662F\u4E00\u4E2A\u8FC7\u53BB\u6216\u73B0\u5728\u7684\u65F6\u95F4
javax.validation.constraints.Pattern.message=\u9700\u8981\u5339\u914D\u6B63\u5219\u8868\u8FBE\u5F0F"{regexp}" jakarta.validation.constraints.Pattern.message=\u9700\u8981\u5339\u914D\u6B63\u5219\u8868\u8FBE\u5F0F"{regexp}"
javax.validation.constraints.Positive.message=\u5FC5\u987B\u662F\u6B63\u6570 jakarta.validation.constraints.Positive.message=\u5FC5\u987B\u662F\u6B63\u6570
javax.validation.constraints.PositiveOrZero.message=\u5FC5\u987B\u662F\u6B63\u6570\u6216\u96F6 jakarta.validation.constraints.PositiveOrZero.message=\u5FC5\u987B\u662F\u6B63\u6570\u6216\u96F6
javax.validation.constraints.Size.message=\u4E2A\u6570\u5FC5\u987B\u5728{min}\u548C{max}\u4E4B\u95F4 jakarta.validation.constraints.Size.message=\u4E2A\u6570\u5FC5\u987B\u5728{min}\u548C{max}\u4E4B\u95F4
org.hibernate.validator.constraints.CreditCardNumber.message=\u4E0D\u5408\u6CD5\u7684\u4FE1\u7528\u5361\u53F7\u7801 org.hibernate.validator.constraints.CreditCardNumber.message=\u4E0D\u5408\u6CD5\u7684\u4FE1\u7528\u5361\u53F7\u7801
org.hibernate.validator.constraints.Currency.message=\u4E0D\u5408\u6CD5\u7684\u8D27\u5E01 (\u5FC5\u987B\u662F{value}\u5176\u4E2D\u4E4B\u4E00) org.hibernate.validator.constraints.Currency.message=\u4E0D\u5408\u6CD5\u7684\u8D27\u5E01 (\u5FC5\u987B\u662F{value}\u5176\u4E2D\u4E4B\u4E00)
org.hibernate.validator.constraints.EAN.message=\u4E0D\u5408\u6CD5\u7684{type}\u6761\u5F62\u7801 org.hibernate.validator.constraints.EAN.message=\u4E0D\u5408\u6CD5\u7684{type}\u6761\u5F62\u7801
......
...@@ -9,11 +9,13 @@ dependencies { ...@@ -9,11 +9,13 @@ dependencies {
implementation fileTree(dir: project.rootDir.getPath() + '\\libs', includes: ['*jar']) implementation fileTree(dir: project.rootDir.getPath() + '\\libs', includes: ['*jar'])
// swagger(knife4j) // swagger(knife4j)
implementation "com.github.xiaoymin:knife4j-spring-boot-starter:${knife4jVersion}" implementation "com.github.xiaoymin:knife4j-openapi3-jakarta-spring-boot-starter:${knife4jOpen3Version}"
// sa-token // sa-token
implementation "cn.dev33:sa-token-spring-boot-starter:${saTokenVersion}" implementation "cn.dev33:sa-token-spring-boot3-starter:${saTokenVersion}"
implementation "cn.dev33:sa-token-dao-redis-jackson:${saTokenVersion}" implementation "cn.dev33:sa-token-dao-redis-jackson:${saTokenVersion}"
// Sa-Token 整合 Redis (使用 jackson 序列化方式)
implementation 'org.apache.commons:commons-pool2'
// fastjson // fastjson
implementation "com.alibaba.fastjson2:fastjson2:${fastJsonVersion}" implementation "com.alibaba.fastjson2:fastjson2:${fastJsonVersion}"
...@@ -21,8 +23,11 @@ dependencies { ...@@ -21,8 +23,11 @@ dependencies {
// hutool-core // hutool-core
implementation "cn.hutool:hutool-core:${hutoolVersion}" implementation "cn.hutool:hutool-core:${hutoolVersion}"
// https://github.com/vladmihalcea/hibernate-types // https://github.com/vladmihalcea/hypersistence-utils
// hibernate-types-55 // hypersistence-utils-hibernate-60
implementation "com.vladmihalcea:hibernate-types-55:${hibernateTypesVersion}" implementation "io.hypersistence:hypersistence-utils-hibernate-60:${hibernateTypesVersion}"
// https://mvnrepository.com/artifact/org.jetbrains/annotations
implementation "org.jetbrains:annotations:${jetbrainsAnnotationsVersion}"
} }
/* (C) 2022 YiRing, Inc. */
package com.yiring.auth.annotation;
import cn.dev33.satoken.annotation.SaIgnore;
import java.lang.annotation.*;
/**
* 忽略登录校验
* 与 @SaCheckLogin 相对
*
* @author Jim
* @version 0.1
* 2022/4/7 15:21
* @deprecated 已过期,请使用 @SaIgnore
*/
@SuppressWarnings({ "unused" })
@Deprecated
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@SaIgnore
public @interface AuthIgnore {
}
/* (C) 2021 YiRing, Inc. */
package com.yiring.auth.config;
import cn.dev33.satoken.exception.*;
import com.yiring.common.core.Result;
import com.yiring.common.core.Status;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 授权异常拦截处理
*
* @author Jim
* @version 0.1
* 2023/1/12 14:06
*/
@Slf4j
@Order(0)
@RestControllerAdvice
@RequiredArgsConstructor
public class AuthExceptionHandler {
/**
* 未登录异常(鉴权失败)
*
* @return 异常信息反馈 {@link Status#UNAUTHORIZED
*/
@ExceptionHandler(NotLoginException.class)
public Result<String> unauthorizedErrorHandler() {
return Result.no(Status.UNAUTHORIZED);
}
/**
* 1. 二级认证失败异常
* 2. 角色条件不满足
* 3. 权限条件不满足
* 4. HTTP Basic 验证不通过
* 5. 用户被禁止访问该服务
* 6. API 被禁用
*
* @return 异常信息反馈 {@link Status#FORBIDDEN
*/
@ExceptionHandler(
{
// https://sa-token.dev33.cn/doc.html#/up/safe-auth
NotSafeException.class,
// https://sa-token.dev33.cn/doc.html#/use/at-check
NotRoleException.class,
NotPermissionException.class,
// https://sa-token.dev33.cn/doc.html#/up/basic-auth
NotBasicAuthException.class,
// https://sa-token.dev33.cn/doc.html#/up/disable
DisableServiceException.class,
ApiDisabledException.class,
}
)
public Result<String> forbiddenHandler(Exception e) {
if (e instanceof NotSafeException) {
return Result.no(Status.FORBIDDEN, "Code.10000");
}
if (e instanceof NotRoleException) {
return Result.no(Status.FORBIDDEN, "Code.10001");
}
if (e instanceof NotPermissionException) {
return Result.no(Status.FORBIDDEN, "Code.10002");
}
if (e instanceof NotBasicAuthException) {
return Result.no(Status.FORBIDDEN, "Code.10003");
}
log.warn(e.getMessage(), e);
return Result.no(Status.FORBIDDEN);
}
}
...@@ -12,7 +12,7 @@ import java.util.Objects; ...@@ -12,7 +12,7 @@ import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.Resource; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
...@@ -24,10 +24,10 @@ import org.springframework.stereotype.Component; ...@@ -24,10 +24,10 @@ import org.springframework.stereotype.Component;
*/ */
@Component @Component
@RequiredArgsConstructor
public class StpInterfaceImpl implements StpInterface { public class StpInterfaceImpl implements StpInterface {
@Resource final UserRepository userRepository;
UserRepository userRepository;
@Override @Override
public List<String> getPermissionList(Object loginId, String loginType) { public List<String> getPermissionList(Object loginId, String loginType) {
...@@ -50,6 +50,7 @@ public class StpInterfaceImpl implements StpInterface { ...@@ -50,6 +50,7 @@ public class StpInterfaceImpl implements StpInterface {
/** /**
* 根据 id 获取用户信息 * 根据 id 获取用户信息
*
* @param loginId 登录 ID * @param loginId 登录 ID
* @return 用户信息 * @return 用户信息
*/ */
...@@ -57,7 +58,7 @@ public class StpInterfaceImpl implements StpInterface { ...@@ -57,7 +58,7 @@ public class StpInterfaceImpl implements StpInterface {
String id = Objects.toString(loginId); String id = Objects.toString(loginId);
Optional<User> optional = userRepository.findById(id); Optional<User> optional = userRepository.findById(id);
if (optional.isEmpty()) { if (optional.isEmpty()) {
throw Status.NOT_FOUND.exception("用户不存在"); throw Status.NOT_FOUND.exception("Code.1000");
} }
return optional.get(); return optional.get();
......
...@@ -6,9 +6,10 @@ import static com.yiring.auth.domain.permission.Permission.TABLE_NAME; ...@@ -6,9 +6,10 @@ import static com.yiring.auth.domain.permission.Permission.TABLE_NAME;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.yiring.common.domain.BasicEntity; import com.yiring.common.domain.BasicEntity;
import io.hypersistence.utils.hibernate.type.json.JsonType;
import jakarta.persistence.*;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import javax.persistence.*;
import lombok.*; import lombok.*;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
import lombok.experimental.FieldNameConstants; import lombok.experimental.FieldNameConstants;
...@@ -99,7 +100,7 @@ public class Permission extends BasicEntity implements Serializable { ...@@ -99,7 +100,7 @@ public class Permission extends BasicEntity implements Serializable {
* 可用于扩展一些前端可能用到的路由参数 * 可用于扩展一些前端可能用到的路由参数
*/ */
@Comment("扩展元数据信息") @Comment("扩展元数据信息")
@org.hibernate.annotations.Type(type = "json") @org.hibernate.annotations.Type(JsonType.class)
@Column(columnDefinition = "json") @Column(columnDefinition = "json")
JSONObject meta; JSONObject meta;
...@@ -123,6 +124,7 @@ public class Permission extends BasicEntity implements Serializable { ...@@ -123,6 +124,7 @@ public class Permission extends BasicEntity implements Serializable {
/** /**
* 获取权限的元数据信息,通常是根据前端所需来输出,可自定义调整 * 获取权限的元数据信息,通常是根据前端所需来输出,可自定义调整
*
* @return JSON 格式 Meta 元数据 * @return JSON 格式 Meta 元数据
*/ */
public JSONObject getMetaJson() { public JSONObject getMetaJson() {
......
...@@ -8,11 +8,11 @@ import com.fasterxml.jackson.annotation.JsonIgnore; ...@@ -8,11 +8,11 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.yiring.auth.domain.permission.Permission; import com.yiring.auth.domain.permission.Permission;
import com.yiring.auth.domain.user.User; import com.yiring.auth.domain.user.User;
import com.yiring.common.domain.BasicEntity; import com.yiring.common.domain.BasicEntity;
import jakarta.persistence.*;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import javax.persistence.*;
import lombok.*; import lombok.*;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
import lombok.experimental.FieldNameConstants; import lombok.experimental.FieldNameConstants;
......
...@@ -4,12 +4,12 @@ package com.yiring.auth.domain.user; ...@@ -4,12 +4,12 @@ package com.yiring.auth.domain.user;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.yiring.auth.domain.role.Role; import com.yiring.auth.domain.role.Role;
import com.yiring.common.domain.BasicEntity; import com.yiring.common.domain.BasicEntity;
import jakarta.persistence.*;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import javax.persistence.*;
import lombok.*; import lombok.*;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
import lombok.experimental.FieldNameConstants; import lombok.experimental.FieldNameConstants;
......
/* (C) 2021 YiRing, Inc. */ /* (C) 2021 YiRing, Inc. */
package com.yiring.auth.param.auth; package com.yiring.auth.param.auth;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.annotations.ApiModelProperty; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import javax.validation.constraints.NotEmpty;
import lombok.*; import lombok.*;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
...@@ -16,7 +16,7 @@ import lombok.experimental.FieldDefaults; ...@@ -16,7 +16,7 @@ import lombok.experimental.FieldDefaults;
* @version 0.1 * @version 0.1
* 2019/5/28 22:11 * 2019/5/28 22:11
*/ */
@ApiModel("LoginParam") @Schema(name = "LoginParam")
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
...@@ -27,11 +27,11 @@ public class LoginParam implements Serializable { ...@@ -27,11 +27,11 @@ public class LoginParam implements Serializable {
@Serial @Serial
private static final long serialVersionUID = -8690942241103456895L; private static final long serialVersionUID = -8690942241103456895L;
@ApiModelProperty(value = "账号(支持用户名/手机号/邮箱)", example = "admin", required = true) @Parameter(description = "账号(支持用户名/手机号/邮箱)", example = "admin")
@NotEmpty(message = "账号不能为空") @NotEmpty(message = "账号不能为空")
String account; String account;
@ApiModelProperty(value = "密码", example = "123456", required = true) @Parameter(description = "密码", example = "123456")
@NotEmpty(message = "密码不能为空") @NotEmpty(message = "密码不能为空")
String password; String password;
} }
/* (C) 2021 YiRing, Inc. */ /* (C) 2021 YiRing, Inc. */
package com.yiring.auth.param.auth; package com.yiring.auth.param.auth;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.annotations.ApiModelProperty; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Pattern;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import lombok.*; import lombok.*;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
...@@ -17,7 +17,7 @@ import lombok.experimental.FieldDefaults; ...@@ -17,7 +17,7 @@ import lombok.experimental.FieldDefaults;
* @version 0.1 * @version 0.1
* 2019/5/28 22:11 * 2019/5/28 22:11
*/ */
@ApiModel("RegisterParam") @Schema(name = "RegisterParam")
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
...@@ -28,32 +28,32 @@ public class RegisterParam implements Serializable { ...@@ -28,32 +28,32 @@ public class RegisterParam implements Serializable {
@Serial @Serial
private static final long serialVersionUID = -8690942241103456895L; private static final long serialVersionUID = -8690942241103456895L;
@ApiModelProperty(value = "用户名", example = "admin", required = true) @Parameter(description = "用户名", example = "admin")
@NotEmpty(message = "用户名不能为空") @NotEmpty(message = "用户名不能为空")
String username; String username;
@ApiModelProperty(value = "密码", example = "123456", required = true) @Parameter(description = "密码", example = "123456")
@NotEmpty(message = "密码不能为空") @NotEmpty(message = "密码不能为空")
String password; String password;
@ApiModelProperty(value = "真实姓名", example = "管理员", required = true) @Parameter(description = "真实姓名", example = "管理员")
@NotEmpty(message = "真实姓名不能为空") @NotEmpty(message = "真实姓名不能为空")
String realName; String realName;
@ApiModelProperty(value = "手机号", example = "13012345678", required = true) @Parameter(description = "手机号", example = "13012345678")
@NotEmpty(message = "手机号不能为空") @NotEmpty(message = "手机号不能为空")
@Pattern(regexp = "^1\\d{10}$", message = "手机号码格式不正确") @Pattern(regexp = "^1\\d{10}$", message = "手机号码格式不正确")
String mobile; String mobile;
@ApiModelProperty(value = "头像", example = "https://s1.ax1x.com/2022/03/30/qggJH0.jpg") @Parameter(description = "头像", example = "https://s1.ax1x.com/2022/03/30/qggJH0.jpg")
String avatar; String avatar;
@ApiModelProperty(value = "邮箱", example = "developer@yiring.com") @Parameter(description = "邮箱", example = "developer@yiring.com")
String email; String email;
@ApiModelProperty(value = "简介", example = "平台管理员") @Parameter(description = "简介", example = "平台管理员")
String introduction; String introduction;
@ApiModelProperty(value = "是否启用", example = "true") @Parameter(description = "是否启用", example = "true")
Boolean enable; Boolean enable;
} }
/* (C) 2021 YiRing, Inc. */ /* (C) 2021 YiRing, Inc. */
package com.yiring.auth.param.auth; package com.yiring.auth.param.auth;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.annotations.ApiModelProperty; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import javax.validation.constraints.NotEmpty;
import lombok.*; import lombok.*;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
...@@ -16,7 +16,7 @@ import lombok.experimental.FieldDefaults; ...@@ -16,7 +16,7 @@ import lombok.experimental.FieldDefaults;
* @version 0.1 * @version 0.1
* 2019/5/28 22:11 * 2019/5/28 22:11
*/ */
@ApiModel("SafeParam") @Schema(name = "SafeParam")
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
...@@ -27,7 +27,7 @@ public class SafeParam implements Serializable { ...@@ -27,7 +27,7 @@ public class SafeParam implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 9106494470582579138L; private static final long serialVersionUID = 9106494470582579138L;
@ApiModelProperty(value = "密码", example = "123456", required = true) @Parameter(description = "密码", example = "123456")
@NotEmpty(message = "密码不能为空") @NotEmpty(message = "密码不能为空")
String password; String password;
} }
...@@ -4,13 +4,13 @@ package com.yiring.auth.param.permission; ...@@ -4,13 +4,13 @@ package com.yiring.auth.param.permission;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.yiring.auth.domain.permission.Permission; import com.yiring.auth.domain.permission.Permission;
import com.yiring.common.validation.group.Group; import com.yiring.common.validation.group.Group;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.annotations.ApiModelProperty; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import lombok.*; import lombok.*;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
...@@ -23,7 +23,7 @@ import lombok.experimental.FieldDefaults; ...@@ -23,7 +23,7 @@ import lombok.experimental.FieldDefaults;
*/ */
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
@ApiModel("PermissionParam") @Schema(name = "PermissionParam")
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
...@@ -34,47 +34,47 @@ public class PermissionParam implements Serializable { ...@@ -34,47 +34,47 @@ public class PermissionParam implements Serializable {
@Serial @Serial
private static final long serialVersionUID = -6781934969837655538L; private static final long serialVersionUID = -6781934969837655538L;
@ApiModelProperty(value = "id", example = "1") @Parameter(description = "id", example = "1")
@NotBlank(groups = { Group.Edit.class }) @NotBlank(groups = { Group.Edit.class })
String id; String id;
@ApiModelProperty(value = "权限类型", example = "MENU", required = true) @Parameter(description = "权限类型", example = "MENU")
@NotNull(message = "权限类型不能为空") @NotNull(message = "权限类型不能为空")
Permission.Type type; Permission.Type type;
@ApiModelProperty(value = "序号", example = "1") @Parameter(description = "序号", example = "1")
Integer serial; Integer serial;
@ApiModelProperty(value = "标识", example = "Dashboard", required = true) @Parameter(description = "标识", example = "Dashboard")
@NotEmpty(message = "权限标识不能为空") @NotEmpty(message = "权限标识不能为空")
String uid; String uid;
@ApiModelProperty(value = "名称", example = "Dashboard", required = true) @Parameter(description = "名称", example = "Dashboard")
@NotEmpty(message = "权限名称不能为空") @NotEmpty(message = "权限名称不能为空")
String name; String name;
@ApiModelProperty(value = "路径", example = "/dashboard") @Parameter(description = "路径", example = "/dashboard")
String path; String path;
@ApiModelProperty(value = "重定向", example = "/dashboard/workbench") @Parameter(description = "重定向", example = "/dashboard/workbench")
String redirect; String redirect;
@ApiModelProperty(value = "组件", example = "LAYOUT") @Parameter(description = "组件", example = "LAYOUT")
String component; String component;
@ApiModelProperty(value = "图标", example = "ion:grid-outline") @Parameter(description = "图标", example = "ion:grid-outline")
String icon; String icon;
@ApiModelProperty(value = "是否隐藏", example = "false") @Parameter(description = "是否隐藏", example = "false")
Boolean hidden; Boolean hidden;
@ApiModelProperty(value = "是否启用", example = "true") @Parameter(description = "是否启用", example = "true")
Boolean enable; Boolean enable;
@ApiModelProperty(value = "父级ID", example = "0") @Parameter(description = "父级ID", example = "0")
@Builder.Default @Builder.Default
String pid = "0"; String pid = "0";
@ApiModelProperty(value = "元数据", example = "{\"title\": \"routes.dashboard.dashboard\"}") @Parameter(description = "元数据", example = "{\"title\": \"routes.dashboard.dashboard\"}")
String meta; String meta;
} }
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
package com.yiring.auth.param.role; package com.yiring.auth.param.role;
import com.yiring.common.validation.group.Group; import com.yiring.common.validation.group.Group;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.annotations.ApiModelProperty; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import lombok.*; import lombok.*;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
...@@ -19,7 +19,7 @@ import lombok.experimental.FieldDefaults; ...@@ -19,7 +19,7 @@ import lombok.experimental.FieldDefaults;
* 2022/3/25 17:09 * 2022/3/25 17:09
*/ */
@ApiModel("RoleParam") @Schema(name = "RoleParam")
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
...@@ -30,15 +30,15 @@ public class RoleParam implements Serializable { ...@@ -30,15 +30,15 @@ public class RoleParam implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 6572751635422870217L; private static final long serialVersionUID = 6572751635422870217L;
@ApiModelProperty(value = "id", example = "1") @Parameter(description = "id", example = "1")
@NotBlank(groups = { Group.Edit.class }) @NotBlank(groups = { Group.Edit.class })
String id; String id;
@ApiModelProperty(value = "标识", example = "admin", required = true) @Parameter(description = "标识", example = "admin")
@NotEmpty(message = "角色标识不能为空") @NotEmpty(message = "角色标识不能为空")
String uid; String uid;
@ApiModelProperty(value = "名称", example = "管理员", required = true) @Parameter(description = "名称", example = "管理员")
@NotEmpty(message = "角色名称不能为空") @NotEmpty(message = "角色名称不能为空")
String name; String name;
} }
...@@ -9,8 +9,8 @@ import com.yiring.common.core.Status; ...@@ -9,8 +9,8 @@ import com.yiring.common.core.Status;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import javax.annotation.Resource;
import lombok.NonNull; import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
...@@ -23,10 +23,10 @@ import org.springframework.stereotype.Component; ...@@ -23,10 +23,10 @@ import org.springframework.stereotype.Component;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Component @Component
@RequiredArgsConstructor
public class Auths { public class Auths {
@Resource final UserRepository userRepository;
UserRepository userRepository;
/** /**
* 管理员角色标识 * 管理员角色标识
...@@ -36,6 +36,7 @@ public class Auths { ...@@ -36,6 +36,7 @@ public class Auths {
/** /**
* 根据 Token 获取用户信息 * 根据 Token 获取用户信息
* 如果用户未登录或校验失败会抛出 NotLoginException {@link Status#UNAUTHORIZED} * 如果用户未登录或校验失败会抛出 NotLoginException {@link Status#UNAUTHORIZED}
*
* @param token token * @param token token
* @return 用户信息 * @return 用户信息
*/ */
...@@ -70,6 +71,7 @@ public class Auths { ...@@ -70,6 +71,7 @@ public class Auths {
/** /**
* 踢出这个用户 id 所有登录状态(可能有多人重复登录了一个账号的情况) * 踢出这个用户 id 所有登录状态(可能有多人重复登录了一个账号的情况)
*
* @param userId 用户 id * @param userId 用户 id
*/ */
public void logoutAll(String userId) { public void logoutAll(String userId) {
...@@ -81,6 +83,7 @@ public class Auths { ...@@ -81,6 +83,7 @@ public class Auths {
/** /**
* 判断用户是否为超级管理员 * 判断用户是否为超级管理员
*
* @param userId 用户 ID * @param userId 用户 ID
* @return 是否为管理员 * @return 是否为管理员
*/ */
...@@ -91,6 +94,7 @@ public class Auths { ...@@ -91,6 +94,7 @@ public class Auths {
/** /**
* 检查用户是否为管理员(检查用户是否拥有包含 admin 字符的角色) * 检查用户是否为管理员(检查用户是否拥有包含 admin 字符的角色)
*
* @param user 用户 * @param user 用户
* @return 是否为管理员 * @return 是否为管理员
*/ */
...@@ -104,6 +108,7 @@ public class Auths { ...@@ -104,6 +108,7 @@ public class Auths {
/** /**
* 检查当前登录用户是否为管理员 * 检查当前登录用户是否为管理员
* {@link this.isAdmin} * {@link this.isAdmin}
*
* @return 是否为管理员 * @return 是否为管理员
*/ */
public boolean checkLoginUserIsAdmin() { public boolean checkLoginUserIsAdmin() {
......
/* (C) 2022 YiRing, Inc. */ /* (C) 2022 YiRing, Inc. */
package com.yiring.auth.util; package com.yiring.auth.util;
import com.sun.istack.Nullable;
import com.yiring.auth.domain.permission.Permission; import com.yiring.auth.domain.permission.Permission;
import com.yiring.auth.domain.role.Role; import com.yiring.auth.domain.role.Role;
import com.yiring.auth.vo.permission.MenuVo; import com.yiring.auth.vo.permission.MenuVo;
import com.yiring.auth.vo.permission.PermissionVo; import com.yiring.auth.vo.permission.PermissionVo;
import com.yiring.auth.vo.role.RoleVo; import com.yiring.auth.vo.role.RoleVo;
import com.yiring.common.util.Commons; import com.yiring.common.util.Commons;
import jakarta.annotation.Nullable;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
...@@ -28,6 +28,7 @@ public class Permissions { ...@@ -28,6 +28,7 @@ public class Permissions {
/** /**
* 将角色集合转换成 Vo 集合 * 将角色集合转换成 Vo 集合
*
* @param roles 角色集合 * @param roles 角色集合
* @return vos * @return vos
*/ */
...@@ -40,6 +41,7 @@ public class Permissions { ...@@ -40,6 +41,7 @@ public class Permissions {
/** /**
* 将权限集合转换成菜单树 * 将权限集合转换成菜单树
*
* @param permissions 权限集合 * @param permissions 权限集合
* @return 菜单树 * @return 菜单树
*/ */
...@@ -75,6 +77,7 @@ public class Permissions { ...@@ -75,6 +77,7 @@ public class Permissions {
/** /**
* 菜单树递归排序 * 菜单树递归排序
*
* @param menus 菜单集合 * @param menus 菜单集合
* @return 排序后的菜单集合 * @return 排序后的菜单集合
*/ */
...@@ -88,7 +91,7 @@ public class Permissions { ...@@ -88,7 +91,7 @@ public class Permissions {
) )
) )
.peek(item -> { .peek(item -> {
if (!Commons.isNullOrEmpty(item.getChildren())) { if (Commons.notEmpty(item.getChildren())) {
item.setChildren(sortMenuTreeVo(item.getChildren())); item.setChildren(sortMenuTreeVo(item.getChildren()));
} }
}) })
...@@ -97,6 +100,7 @@ public class Permissions { ...@@ -97,6 +100,7 @@ public class Permissions {
/** /**
* 将权限集合转换成 Vo 集合 * 将权限集合转换成 Vo 集合
*
* @param permissions 权限集合 * @param permissions 权限集合
* @return vos * @return vos
*/ */
...@@ -114,6 +118,7 @@ public class Permissions { ...@@ -114,6 +118,7 @@ public class Permissions {
/** /**
* 提取角色集合含有的权限去重结果 * 提取角色集合含有的权限去重结果
*
* @param roles 角色集合 * @param roles 角色集合
* @return 权限集合 * @return 权限集合
*/ */
...@@ -130,8 +135,9 @@ public class Permissions { ...@@ -130,8 +135,9 @@ public class Permissions {
/** /**
* 根据 pid 构建树状权限集合 * 根据 pid 构建树状权限集合
*
* @param permissions 权限集合 * @param permissions 权限集合
* @param pid 权限父级 ID * @param pid 权限父级 ID
* @return 树状权限集合 * @return 树状权限集合
*/ */
public List<PermissionVo> toTree(List<Permission> permissions, @NonNull String pid) { public List<PermissionVo> toTree(List<Permission> permissions, @NonNull String pid) {
......
/* (C) 2021 YiRing, Inc. */ /* (C) 2021 YiRing, Inc. */
package com.yiring.auth.vo.auth; package com.yiring.auth.vo.auth;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import lombok.*; import lombok.*;
...@@ -16,7 +15,7 @@ import lombok.experimental.FieldDefaults; ...@@ -16,7 +15,7 @@ import lombok.experimental.FieldDefaults;
* 2019/5/28 22:11 * 2019/5/28 22:11
*/ */
@ApiModel("LoginVo") @Schema(name = "LoginVo")
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
...@@ -27,9 +26,9 @@ public class LoginVo implements Serializable { ...@@ -27,9 +26,9 @@ public class LoginVo implements Serializable {
@Serial @Serial
private static final long serialVersionUID = -8690942241103456896L; private static final long serialVersionUID = -8690942241103456896L;
@ApiModelProperty(value = "用户 ID", example = "1") @Schema(description = "用户 ID", example = "1")
String userId; String userId;
@ApiModelProperty(value = "token", example = "c68ca9c8c04b4a59afeafd2fb7c04741") @Schema(description = "token", example = "c68ca9c8c04b4a59afeafd2fb7c04741")
String token; String token;
} }
...@@ -4,8 +4,7 @@ package com.yiring.auth.vo.permission; ...@@ -4,8 +4,7 @@ package com.yiring.auth.vo.permission;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
...@@ -21,7 +20,7 @@ import lombok.experimental.FieldDefaults; ...@@ -21,7 +20,7 @@ import lombok.experimental.FieldDefaults;
*/ */
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
@ApiModel("MenuVo") @Schema(name = "MenuVo")
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
...@@ -38,24 +37,24 @@ public class MenuVo implements Serializable { ...@@ -38,24 +37,24 @@ public class MenuVo implements Serializable {
@JsonIgnore @JsonIgnore
String pid; String pid;
@ApiModelProperty(value = "唯一标识", example = "Dashboard") @Schema(description = "唯一标识", example = "Dashboard")
String uid; String uid;
@ApiModelProperty(value = "名称", example = "Dashboard") @Schema(description = "名称", example = "Dashboard")
String name; String name;
@ApiModelProperty(value = "路径", example = "/dashboard") @Schema(description = "路径", example = "/dashboard")
String path; String path;
@ApiModelProperty(value = "重定向", example = "/dashboard/workbench") @Schema(description = "重定向", example = "/dashboard/workbench")
String redirect; String redirect;
@ApiModelProperty(value = "组件", example = "LAYOUT") @Schema(description = "组件", example = "LAYOUT")
String component; String component;
@ApiModelProperty(value = "元数据", example = "{\"title\": \"routes.dashboard.dashboard\"}") @Schema(description = "元数据", example = "{\"title\": \"routes.dashboard.dashboard\"}")
JSONObject meta; JSONObject meta;
@ApiModelProperty(value = "子权限") @Schema(description = "子权限")
List<MenuVo> children; List<MenuVo> children;
} }
...@@ -4,8 +4,8 @@ package com.yiring.auth.vo.permission; ...@@ -4,8 +4,8 @@ package com.yiring.auth.vo.permission;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.yiring.auth.domain.permission.Permission; import com.yiring.auth.domain.permission.Permission;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.annotations.ApiModelProperty; import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
...@@ -14,13 +14,14 @@ import lombok.experimental.FieldDefaults; ...@@ -14,13 +14,14 @@ import lombok.experimental.FieldDefaults;
/** /**
* 权限输出类 * 权限输出类
*
* @author Jim * @author Jim
* @version 0.1 * @version 0.1
* 2022/3/25 17:09 * 2022/3/25 17:09
*/ */
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
@ApiModel("PermissionVo") @Schema(name = "PermissionVo")
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
...@@ -31,42 +32,42 @@ public class PermissionVo implements Serializable { ...@@ -31,42 +32,42 @@ public class PermissionVo implements Serializable {
@Serial @Serial
private static final long serialVersionUID = -9139328772148985141L; private static final long serialVersionUID = -9139328772148985141L;
@ApiModelProperty(value = "主键", example = "1") @Parameter(description = "主键", example = "1")
String id; String id;
@ApiModelProperty(value = "权限类型", example = "MENU") @Parameter(description = "权限类型", example = "MENU")
Permission.Type type; Permission.Type type;
@ApiModelProperty(value = "序号", example = "1") @Parameter(description = "序号", example = "1")
Integer serial; Integer serial;
@ApiModelProperty(value = "标识", example = "home") @Parameter(description = "标识", example = "home")
String uid; String uid;
@ApiModelProperty(value = "名称", example = "主页") @Parameter(description = "名称", example = "主页")
String name; String name;
@ApiModelProperty(value = "路径", example = "/") @Parameter(description = "路径", example = "/")
String path; String path;
@ApiModelProperty(value = "组件", example = "/home") @Parameter(description = "组件", example = "/home")
String component; String component;
@ApiModelProperty(value = "图标", example = "menu") @Parameter(description = "图标", example = "menu")
String icon; String icon;
@ApiModelProperty(value = "是否隐藏", example = "false") @Parameter(description = "是否隐藏", example = "false")
Boolean hidden; Boolean hidden;
@ApiModelProperty(value = "是否启用", example = "true") @Parameter(description = "是否启用", example = "true")
Boolean enable; Boolean enable;
@ApiModelProperty(value = "父级ID", example = "0") @Parameter(description = "父级ID", example = "0")
String pid; String pid;
@ApiModelProperty(value = "元数据", example = "{\"title\": \"routes.dashboard.dashboard\"}") @Parameter(description = "元数据", example = "{\"title\": \"routes.dashboard.dashboard\"}")
JSONObject meta; JSONObject meta;
@ApiModelProperty(value = "子权限") @Parameter(description = "子权限")
List<PermissionVo> children; List<PermissionVo> children;
} }
...@@ -2,8 +2,7 @@ ...@@ -2,8 +2,7 @@
package com.yiring.auth.vo.role; package com.yiring.auth.vo.role;
import com.yiring.auth.vo.permission.PermissionVo; import com.yiring.auth.vo.permission.PermissionVo;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
...@@ -12,12 +11,13 @@ import lombok.experimental.FieldDefaults; ...@@ -12,12 +11,13 @@ import lombok.experimental.FieldDefaults;
/** /**
* 角色响应类 * 角色响应类
*
* @author Jim * @author Jim
* @version 0.1 * @version 0.1
* 2022/3/25 17:09 * 2022/3/25 17:09
*/ */
@ApiModel("RoleVo") @Schema(name = "RoleVo")
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
...@@ -28,15 +28,15 @@ public class RoleVo implements Serializable { ...@@ -28,15 +28,15 @@ public class RoleVo implements Serializable {
@Serial @Serial
private static final long serialVersionUID = -9154497137563970840L; private static final long serialVersionUID = -9154497137563970840L;
@ApiModelProperty(value = "主键", example = "1") @Schema(description = "主键", example = "1")
String id; String id;
@ApiModelProperty(value = "标识", example = "admin") @Schema(description = "标识", example = "admin")
String uid; String uid;
@ApiModelProperty(value = "名称", example = "系统管理员") @Schema(description = "名称", example = "系统管理员")
String name; String name;
@ApiModelProperty("权限") @Schema(description = "权限")
List<PermissionVo> permissions; List<PermissionVo> permissions;
} }
...@@ -2,8 +2,7 @@ ...@@ -2,8 +2,7 @@
package com.yiring.auth.vo.user; package com.yiring.auth.vo.user;
import com.yiring.auth.vo.role.RoleVo; import com.yiring.auth.vo.role.RoleVo;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -13,11 +12,12 @@ import lombok.experimental.FieldDefaults; ...@@ -13,11 +12,12 @@ import lombok.experimental.FieldDefaults;
/** /**
* 用户信息 * 用户信息
*
* @author ifzm * @author ifzm
* 2022/03/03 10:35 * 2022/03/03 10:35
**/ **/
@ApiModel("UserInfo") @Schema(name = "UserInfo")
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
...@@ -28,28 +28,28 @@ public class UserInfoVo implements Serializable { ...@@ -28,28 +28,28 @@ public class UserInfoVo implements Serializable {
@Serial @Serial
private static final long serialVersionUID = -5319037883240327088L; private static final long serialVersionUID = -5319037883240327088L;
@ApiModelProperty(value = "主键", example = "1") @Schema(description = "主键", example = "1")
String userId; String userId;
@ApiModelProperty(value = "手机号", example = "15616260195") @Schema(description = "手机号", example = "15616260195")
String mobile; String mobile;
@ApiModelProperty(value = "真实姓名", example = "超级用户") @Schema(description = "真实姓名", example = "超级用户")
String realName; String realName;
@ApiModelProperty(value = "用户名", example = "admin") @Schema(description = "用户名", example = "admin")
String username; String username;
@ApiModelProperty(value = "介绍", example = "系统管理员") @Schema(description = "介绍", example = "系统管理员")
String desc; String desc;
@ApiModelProperty(value = "头像", example = "https://s1.ax1x.com/2022/03/30/qggJH0.jpg") @Schema(description = "头像", example = "https://s1.ax1x.com/2022/03/30/qggJH0.jpg")
String avatar; String avatar;
@ApiModelProperty("角色") @Schema(description = "角色")
@Builder.Default @Builder.Default
List<RoleVo> roles = new ArrayList<>(0); List<RoleVo> roles = new ArrayList<>(0);
@ApiModelProperty(value = "用户主页", example = "/dashboard/workbench") @Schema(description = "用户主页", example = "/dashboard/workbench")
String homePath; String homePath;
} }
...@@ -2,8 +2,7 @@ ...@@ -2,8 +2,7 @@
package com.yiring.auth.vo.user; package com.yiring.auth.vo.user;
import com.yiring.auth.vo.role.RoleVo; import com.yiring.auth.vo.role.RoleVo;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -13,11 +12,12 @@ import lombok.experimental.FieldDefaults; ...@@ -13,11 +12,12 @@ import lombok.experimental.FieldDefaults;
/** /**
* 用户信息 * 用户信息
*
* @author ifzm * @author ifzm
* 2022/03/03 10:35 * 2022/03/03 10:35
**/ **/
@ApiModel("UserInfo") @Schema(name = "UserInfo")
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
...@@ -28,25 +28,25 @@ public class UserMenuListVo implements Serializable { ...@@ -28,25 +28,25 @@ public class UserMenuListVo implements Serializable {
@Serial @Serial
private static final long serialVersionUID = -5319037883240327088L; private static final long serialVersionUID = -5319037883240327088L;
@ApiModelProperty(value = "主键", example = "1") @Schema(description = "主键", example = "1")
String userId; String userId;
@ApiModelProperty(value = "真实姓名", example = "超级用户") @Schema(description = "真实姓名", example = "超级用户")
String realName; String realName;
@ApiModelProperty(value = "用户名", example = "admin") @Schema(description = "用户名", example = "admin")
String username; String username;
@ApiModelProperty(value = "介绍", example = "系统管理员") @Schema(description = "介绍", example = "系统管理员")
String desc; String desc;
@ApiModelProperty(value = "头像", example = "https://s1.ax1x.com/2022/03/30/qggJH0.jpg") @Schema(description = "头像", example = "https://s1.ax1x.com/2022/03/30/qggJH0.jpg")
String avatar; String avatar;
@ApiModelProperty("角色") @Schema(description = "角色")
@Builder.Default @Builder.Default
List<RoleVo> roles = new ArrayList<>(0); List<RoleVo> roles = new ArrayList<>(0);
@ApiModelProperty(value = "用户主页", example = "/dashboard/workbench") @Schema(description = "用户主页", example = "/dashboard/workbench")
String homePath; String homePath;
} }
/* (C) 2022 YiRing, Inc. */ /* (C) 2022 YiRing, Inc. */
package com.yiring.auth.vo.user; package com.yiring.auth.vo.user;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDateTime; import java.time.LocalDateTime;
...@@ -11,11 +10,12 @@ import lombok.experimental.FieldDefaults; ...@@ -11,11 +10,12 @@ import lombok.experimental.FieldDefaults;
/** /**
* 用户信息 * 用户信息
*
* @author ifzm * @author ifzm
* 2022/03/03 10:35 * 2022/03/03 10:35
**/ **/
@ApiModel("UserVo") @Schema(name = "UserVo")
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
...@@ -26,39 +26,39 @@ public class UserVo implements Serializable { ...@@ -26,39 +26,39 @@ public class UserVo implements Serializable {
@Serial @Serial
private static final long serialVersionUID = -2184378273450466835L; private static final long serialVersionUID = -2184378273450466835L;
@ApiModelProperty(value = "主键", example = "1") @Schema(description = "主键", example = "1")
String id; String id;
@ApiModelProperty(value = "真实姓名", example = "超级用户") @Schema(description = "真实姓名", example = "超级用户")
String realName; String realName;
@ApiModelProperty(value = "用户名", example = "admin") @Schema(description = "用户名", example = "admin")
String username; String username;
@ApiModelProperty(value = "手机号", example = "13012345678") @Schema(description = "手机号", example = "13012345678")
String mobile; String mobile;
@ApiModelProperty(value = "邮箱", example = "developer@yiring.com") @Schema(description = "邮箱", example = "developer@yiring.com")
String email; String email;
@ApiModelProperty(value = "职称", example = "系统管理员") @Schema(description = "职称", example = "系统管理员")
String title; String title;
@ApiModelProperty(value = "头像", example = "https://s1.ax1x.com/2022/03/30/qggJH0.jpg") @Schema(description = "头像", example = "https://s1.ax1x.com/2022/03/30/qggJH0.jpg")
String avatar; String avatar;
@ApiModelProperty(value = "是否启用", example = "true") @Schema(description = "是否启用", example = "true")
Boolean enabled; Boolean enabled;
@ApiModelProperty(value = "是否删除", example = "false") @Schema(description = "是否删除", example = "false")
Boolean deleted; Boolean deleted;
@ApiModelProperty(value = "最后登录IP地址", example = "127.0.0.1") @Schema(description = "最后登录IP地址", example = "127.0.0.1")
String lastLoginIp; String lastLoginIp;
@ApiModelProperty(value = "最后登录时间", example = "2022-10-24 10:24:00") @Schema(description = "最后登录时间", example = "2022-10-24 10:24:00")
LocalDateTime lastLoginTime; LocalDateTime lastLoginTime;
@ApiModelProperty(value = "最后登录时间", example = "2022-01-01 00:00:00") @Schema(description = "最后登录时间", example = "2022-01-01 00:00:00")
LocalDateTime createTime; LocalDateTime createTime;
} }
...@@ -5,7 +5,6 @@ import cn.dev33.satoken.annotation.SaCheckLogin; ...@@ -5,7 +5,6 @@ import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.secure.SaSecureUtil; import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.yiring.auth.domain.user.User; import com.yiring.auth.domain.user.User;
import com.yiring.auth.domain.user.UserRepository; import com.yiring.auth.domain.user.UserRepository;
import com.yiring.auth.param.auth.LoginParam; import com.yiring.auth.param.auth.LoginParam;
...@@ -16,12 +15,15 @@ import com.yiring.auth.vo.auth.LoginVo; ...@@ -16,12 +15,15 @@ import com.yiring.auth.vo.auth.LoginVo;
import com.yiring.common.core.Result; import com.yiring.common.core.Result;
import com.yiring.common.exception.BusinessException; import com.yiring.common.exception.BusinessException;
import com.yiring.common.util.Commons; import com.yiring.common.util.Commons;
import io.swagger.annotations.Api; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.extensions.Extension;
import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import javax.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.data.domain.Example; import org.springframework.data.domain.Example;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
...@@ -39,9 +41,11 @@ import org.springframework.web.bind.annotation.RestController; ...@@ -39,9 +41,11 @@ import org.springframework.web.bind.annotation.RestController;
@Slf4j @Slf4j
@Validated @Validated
@SuppressWarnings({ "all" }) @Tag(
@ApiSupport(order = -99) name = "Auth",
@Api(tags = "身份认证", description = "Auth") description = "身份认证",
extensions = { @Extension(properties = { @ExtensionProperty(name = "x-order", value = "-9999") }) }
)
@RestController @RestController
@RequestMapping("/auth/") @RequestMapping("/auth/")
@RequiredArgsConstructor @RequiredArgsConstructor
...@@ -50,9 +54,9 @@ public class AuthController { ...@@ -50,9 +54,9 @@ public class AuthController {
final Auths auths; final Auths auths;
final UserRepository userRepository; final UserRepository userRepository;
@ApiOperation(value = "注册") @Operation(summary = "注册")
@PostMapping("register") @PostMapping(value = "register")
public Result<String> register(@Validated RegisterParam param) { public Result<String> register(@ParameterObject @Validated RegisterParam param) {
// 检查用户名是否存在 // 检查用户名是否存在
long count = userRepository.count(Example.of(User.builder().username(param.getUsername()).build())); long count = userRepository.count(Example.of(User.builder().username(param.getUsername()).build()));
if (count > 0) { if (count > 0) {
...@@ -88,9 +92,9 @@ public class AuthController { ...@@ -88,9 +92,9 @@ public class AuthController {
return Result.ok(); return Result.ok();
} }
@ApiOperation(value = "登录") @Operation(summary = "登录")
@PostMapping("login") @PostMapping("login")
public Result<LoginVo> login(@Validated LoginParam param, HttpServletRequest request) { public Result<LoginVo> login(@ParameterObject @Validated LoginParam param, HttpServletRequest request) {
// 查询用户信息是否匹配 // 查询用户信息是否匹配
User user = userRepository.findByAccount(param.getAccount()); User user = userRepository.findByAccount(param.getAccount());
if (user == null) { if (user == null) {
...@@ -126,13 +130,13 @@ public class AuthController { ...@@ -126,13 +130,13 @@ public class AuthController {
return Result.ok(vo); return Result.ok(vo);
} }
@ApiOperation(value = "检查登录") @Operation(summary = "检查登录")
@GetMapping("valid") @GetMapping("valid")
public Result<Boolean> valid() { public Result<Boolean> valid() {
return Result.ok(StpUtil.isLogin()); return Result.ok(StpUtil.isLogin());
} }
@ApiOperation(value = "登出") @Operation(summary = "登出")
@GetMapping("logout") @GetMapping("logout")
public Result<String> logout() { public Result<String> logout() {
StpUtil.logout(); StpUtil.logout();
...@@ -144,13 +148,12 @@ public class AuthController { ...@@ -144,13 +148,12 @@ public class AuthController {
* 默认安全时间: 120s * 默认安全时间: 120s
* *
* @param param 用户密码 * @param param 用户密码
* @return * @link { <a href="https://sa-token.dev33.cn/doc.html#/up/safe-auth">...</a> }
* @link { https://sa-token.dev33.cn/doc.html#/up/safe-auth }
*/ */
@SaCheckLogin @SaCheckLogin
@ApiOperation(value = "安全校验") @Operation(summary = "安全验证")
@GetMapping("safe") @GetMapping("safe")
public Result<String> safe(@Validated SafeParam param) { public Result<String> safe(@ParameterObject @Validated SafeParam param) {
User user = auths.getLoginUser(); User user = auths.getLoginUser();
if (SaSecureUtil.sha256(param.getPassword()).equals(user.getPassword())) { if (SaSecureUtil.sha256(param.getPassword()).equals(user.getPassword())) {
StpUtil.openSafe(120); StpUtil.openSafe(120);
......
...@@ -4,7 +4,6 @@ package com.yiring.auth.web.sys.permission; ...@@ -4,7 +4,6 @@ package com.yiring.auth.web.sys.permission;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONValidator; import com.alibaba.fastjson2.JSONValidator;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.yiring.auth.domain.permission.Permission; import com.yiring.auth.domain.permission.Permission;
import com.yiring.auth.domain.permission.PermissionRepository; import com.yiring.auth.domain.permission.PermissionRepository;
import com.yiring.auth.param.permission.PermissionParam; import com.yiring.auth.param.permission.PermissionParam;
...@@ -18,13 +17,16 @@ import com.yiring.common.param.PageParam; ...@@ -18,13 +17,16 @@ import com.yiring.common.param.PageParam;
import com.yiring.common.param.PidParam; import com.yiring.common.param.PidParam;
import com.yiring.common.validation.group.Group; import com.yiring.common.validation.group.Group;
import com.yiring.common.vo.PageVo; import com.yiring.common.vo.PageVo;
import io.swagger.annotations.Api; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.extensions.Extension;
import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Example; import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
...@@ -44,9 +46,11 @@ import org.springframework.web.bind.annotation.RestController; ...@@ -44,9 +46,11 @@ import org.springframework.web.bind.annotation.RestController;
@Slf4j @Slf4j
@Validated @Validated
@SuppressWarnings({ "deprecation" }) @Tag(
@ApiSupport(order = -97) name = "权限管理",
@Api(tags = "权限管理", description = "Permission") description = "Permission",
extensions = { @Extension(properties = { @ExtensionProperty(name = "x-order", value = "-99") }) }
)
@RestController @RestController
@RequestMapping("/sys/permission/") @RequestMapping("/sys/permission/")
@RequiredArgsConstructor @RequiredArgsConstructor
...@@ -54,9 +58,9 @@ public class PermissionController { ...@@ -54,9 +58,9 @@ public class PermissionController {
final PermissionRepository permissionRepository; final PermissionRepository permissionRepository;
@ApiOperation(value = "新增") @Operation(summary = "新增")
@PostMapping("add") @PostMapping("add")
public Result<String> add(@Validated({ Group.Add.class }) PermissionParam param) { public Result<String> add(@ParameterObject @Validated({ Group.Add.class }) PermissionParam param) {
if (has(param.getUid())) { if (has(param.getUid())) {
throw BusinessException.i18n("Code.1001"); throw BusinessException.i18n("Code.1001");
} }
...@@ -66,9 +70,9 @@ public class PermissionController { ...@@ -66,9 +70,9 @@ public class PermissionController {
return Result.ok(); return Result.ok();
} }
@ApiOperation(value = "修改") @Operation(summary = "修改")
@PostMapping("modify") @PostMapping("modify")
public Result<String> modify(@Validated({ Group.Edit.class }) PermissionParam param) { public Result<String> modify(@ParameterObject @Validated({ Group.Edit.class }) PermissionParam param) {
Optional<Permission> optional = permissionRepository.findById(param.getId()); Optional<Permission> optional = permissionRepository.findById(param.getId());
if (optional.isEmpty()) { if (optional.isEmpty()) {
throw Status.NOT_FOUND.exception(); throw Status.NOT_FOUND.exception();
...@@ -86,9 +90,9 @@ public class PermissionController { ...@@ -86,9 +90,9 @@ public class PermissionController {
return Result.ok(); return Result.ok();
} }
@ApiOperation(value = "删除") @Operation(summary = "删除")
@PostMapping("deleted") @PostMapping("deleted")
public Result<String> deleted(@Validated IdParam param) { public Result<String> deleted(@ParameterObject @Validated IdParam param) {
Optional<Permission> optional = permissionRepository.findById(param.getId()); Optional<Permission> optional = permissionRepository.findById(param.getId());
if (optional.isEmpty()) { if (optional.isEmpty()) {
throw Status.NOT_FOUND.exception(); throw Status.NOT_FOUND.exception();
...@@ -99,9 +103,9 @@ public class PermissionController { ...@@ -99,9 +103,9 @@ public class PermissionController {
return Result.ok(); return Result.ok();
} }
@ApiOperation(value = "查询") @Operation(summary = "查询")
@GetMapping("find") @GetMapping("find")
public Result<PermissionVo> find(@Validated IdParam param) { public Result<PermissionVo> find(@ParameterObject @Validated IdParam param) {
Optional<Permission> optional = permissionRepository.findById(param.getId()); Optional<Permission> optional = permissionRepository.findById(param.getId());
if (optional.isEmpty()) { if (optional.isEmpty()) {
throw Status.NOT_FOUND.exception(); throw Status.NOT_FOUND.exception();
...@@ -114,18 +118,18 @@ public class PermissionController { ...@@ -114,18 +118,18 @@ public class PermissionController {
return Result.ok(vo); return Result.ok(vo);
} }
@ApiOperation(value = "分页查询") @Operation(summary = "分页查询")
@GetMapping("page") @GetMapping("page")
public Result<PageVo<PermissionVo>> page(@Validated PageParam param) { public Result<PageVo<PermissionVo>> page(@ParameterObject @Validated PageParam param) {
Page<Permission> page = permissionRepository.findAll(PageParam.toPageable(param)); Page<Permission> page = permissionRepository.findAll(PageParam.toPageable(param));
List<PermissionVo> data = Permissions.toPermissionVos(page.toList()); List<PermissionVo> data = Permissions.toPermissionVos(page.toList());
PageVo<PermissionVo> vo = PageVo.build(data, page.getTotalElements()); PageVo<PermissionVo> vo = PageVo.build(data, page.getTotalElements());
return Result.ok(vo); return Result.ok(vo);
} }
@ApiOperation(value = "树结构查询") @Operation(summary = "树结构查询")
@GetMapping(value = "tree") @GetMapping(value = "tree")
public Result<ArrayList<PermissionVo>> tree(@Validated(Group.Optional.class) PidParam param) { public Result<ArrayList<PermissionVo>> tree(@ParameterObject @Validated(Group.Optional.class) PidParam param) {
List<Permission> permissions = permissionRepository.findAll(); List<Permission> permissions = permissionRepository.findAll();
List<PermissionVo> vos = Permissions.toTree( List<PermissionVo> vos = Permissions.toTree(
permissions, permissions,
......
/* (C) 2022 YiRing, Inc. */ /* (C) 2022 YiRing, Inc. */
package com.yiring.auth.web.sys.role; package com.yiring.auth.web.sys.role;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.yiring.auth.domain.permission.Permission; import com.yiring.auth.domain.permission.Permission;
import com.yiring.auth.domain.permission.PermissionRepository; import com.yiring.auth.domain.permission.PermissionRepository;
import com.yiring.auth.domain.role.Role; import com.yiring.auth.domain.role.Role;
...@@ -17,12 +16,15 @@ import com.yiring.common.param.IdsParam; ...@@ -17,12 +16,15 @@ import com.yiring.common.param.IdsParam;
import com.yiring.common.param.PageParam; import com.yiring.common.param.PageParam;
import com.yiring.common.validation.group.Group; import com.yiring.common.validation.group.Group;
import com.yiring.common.vo.PageVo; import com.yiring.common.vo.PageVo;
import io.swagger.annotations.Api; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.extensions.Extension;
import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.io.Serializable; import java.io.Serializable;
import java.util.*; import java.util.*;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Example; import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
...@@ -42,9 +44,11 @@ import org.springframework.web.bind.annotation.RestController; ...@@ -42,9 +44,11 @@ import org.springframework.web.bind.annotation.RestController;
@Slf4j @Slf4j
@Validated @Validated
@SuppressWarnings({ "deprecation" }) @Tag(
@ApiSupport(order = -96) name = "角色管理",
@Api(tags = "角色管理", description = "Role") description = "Role",
extensions = { @Extension(properties = { @ExtensionProperty(name = "x-order", value = "-98") }) }
)
@RestController @RestController
@RequestMapping("/sys/role/") @RequestMapping("/sys/role/")
@RequiredArgsConstructor @RequiredArgsConstructor
...@@ -53,9 +57,9 @@ public class RoleController { ...@@ -53,9 +57,9 @@ public class RoleController {
final RoleRepository roleRepository; final RoleRepository roleRepository;
final PermissionRepository permissionRepository; final PermissionRepository permissionRepository;
@ApiOperation(value = "新增") @Operation(summary = "新增")
@PostMapping("add") @PostMapping("add")
public Result<String> add(@Validated({ Group.Add.class }) RoleParam param) { public Result<String> add(@ParameterObject @Validated({ Group.Add.class }) RoleParam param) {
if (has(param.getUid())) { if (has(param.getUid())) {
throw BusinessException.i18n("Code.1002"); throw BusinessException.i18n("Code.1002");
} }
...@@ -66,9 +70,9 @@ public class RoleController { ...@@ -66,9 +70,9 @@ public class RoleController {
return Result.ok(); return Result.ok();
} }
@ApiOperation(value = "修改") @Operation(summary = "修改")
@PostMapping("modify") @PostMapping("modify")
public Result<String> modify(@Validated({ Group.Edit.class }) RoleParam param) { public Result<String> modify(@ParameterObject @Validated({ Group.Edit.class }) RoleParam param) {
Optional<Role> optional = roleRepository.findById(param.getId()); Optional<Role> optional = roleRepository.findById(param.getId());
if (optional.isEmpty()) { if (optional.isEmpty()) {
throw Status.NOT_FOUND.exception(); throw Status.NOT_FOUND.exception();
...@@ -87,9 +91,12 @@ public class RoleController { ...@@ -87,9 +91,12 @@ public class RoleController {
return Result.ok(); return Result.ok();
} }
@ApiOperation(value = "分配权限") @Operation(summary = "分配权限")
@PostMapping("assign") @PostMapping("assign")
public Result<String> assign(@Validated IdParam idParam, @Validated IdsParam idsParam) { public Result<String> assign(
@ParameterObject @Validated IdParam idParam,
@ParameterObject @Validated IdsParam idsParam
) {
Optional<Role> optional = roleRepository.findById(idParam.getId()); Optional<Role> optional = roleRepository.findById(idParam.getId());
if (optional.isEmpty()) { if (optional.isEmpty()) {
throw Status.NOT_FOUND.exception(); throw Status.NOT_FOUND.exception();
...@@ -105,17 +112,17 @@ public class RoleController { ...@@ -105,17 +112,17 @@ public class RoleController {
return Result.ok(); return Result.ok();
} }
@ApiOperation(value = "删除") @Operation(summary = "删除")
@PostMapping("deleted") @PostMapping("deleted")
public Result<String> deleted(@Validated IdsParam param) { public Result<String> deleted(@ParameterObject @Validated IdsParam param) {
List<Role> roles = roleRepository.findAllById(param.toIds()); List<Role> roles = roleRepository.findAllById(param.toIds());
roleRepository.deleteAll(roles); roleRepository.deleteAll(roles);
return Result.ok(); return Result.ok();
} }
@ApiOperation(value = "查询") @Operation(summary = "查询")
@GetMapping("find") @GetMapping("find")
public Result<RoleVo> find(@Validated IdParam param) { public Result<RoleVo> find(@ParameterObject @Validated IdParam param) {
Optional<Role> optional = roleRepository.findById(param.getId()); Optional<Role> optional = roleRepository.findById(param.getId());
if (optional.isEmpty()) { if (optional.isEmpty()) {
throw Status.NOT_FOUND.exception(); throw Status.NOT_FOUND.exception();
...@@ -128,16 +135,16 @@ public class RoleController { ...@@ -128,16 +135,16 @@ public class RoleController {
return Result.ok(vo); return Result.ok(vo);
} }
@ApiOperation(value = "分页查询") @Operation(summary = "分页查询")
@GetMapping("page") @GetMapping("page")
public Result<PageVo<RoleVo>> page(@Validated PageParam param) { public Result<PageVo<RoleVo>> page(@ParameterObject @Validated PageParam param) {
Page<Role> page = roleRepository.findAll(PageParam.toPageable(param)); Page<Role> page = roleRepository.findAll(PageParam.toPageable(param));
List<RoleVo> data = new ArrayList<>(Permissions.toRoleVos(page.toSet())); List<RoleVo> data = new ArrayList<>(Permissions.toRoleVos(page.toSet()));
PageVo<RoleVo> vo = PageVo.build(data, page.getTotalElements()); PageVo<RoleVo> vo = PageVo.build(data, page.getTotalElements());
return Result.ok(vo); return Result.ok(vo);
} }
@ApiOperation(value = "选项查询") @Operation(summary = "选项查询")
@GetMapping("selector") @GetMapping("selector")
public Result<ArrayList<RoleVo>> selector() { public Result<ArrayList<RoleVo>> selector() {
List<Role> roles = roleRepository.findAll(); List<Role> roles = roleRepository.findAll();
......
/* (C) 2022 YiRing, Inc. */ /* (C) 2022 YiRing, Inc. */
package com.yiring.auth.web.sys.user; package com.yiring.auth.web.sys.user;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.yiring.auth.domain.role.Role; import com.yiring.auth.domain.role.Role;
import com.yiring.auth.domain.role.RoleRepository; import com.yiring.auth.domain.role.RoleRepository;
import com.yiring.auth.domain.user.User; import com.yiring.auth.domain.user.User;
...@@ -14,13 +13,16 @@ import com.yiring.common.param.IdsParam; ...@@ -14,13 +13,16 @@ import com.yiring.common.param.IdsParam;
import com.yiring.common.param.PageParam; import com.yiring.common.param.PageParam;
import com.yiring.common.util.Commons; import com.yiring.common.util.Commons;
import com.yiring.common.vo.PageVo; import com.yiring.common.vo.PageVo;
import io.swagger.annotations.Api; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.extensions.Extension;
import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.io.Serializable; import java.io.Serializable;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
...@@ -38,9 +40,11 @@ import org.springframework.web.bind.annotation.RestController; ...@@ -38,9 +40,11 @@ import org.springframework.web.bind.annotation.RestController;
@Slf4j @Slf4j
@Validated @Validated
@SuppressWarnings({ "deprecation" }) @Tag(
@ApiSupport(order = -95) name = "用户管理",
@Api(tags = "用户管理", description = "User") description = "User",
extensions = { @Extension(properties = { @ExtensionProperty(name = "x-order", value = "-97") }) }
)
@RestController @RestController
@RequestMapping("/sys/user/") @RequestMapping("/sys/user/")
@RequiredArgsConstructor @RequiredArgsConstructor
...@@ -49,9 +53,12 @@ public class UserController { ...@@ -49,9 +53,12 @@ public class UserController {
final UserRepository userRepository; final UserRepository userRepository;
final RoleRepository roleRepository; final RoleRepository roleRepository;
@ApiOperation(value = "分配角色") @Operation(summary = "分配角色")
@PostMapping("assign") @PostMapping("assign")
public Result<String> assign(@Validated IdParam idParam, @Validated IdsParam idsParam) { public Result<String> assign(
@ParameterObject @Validated IdParam idParam,
@ParameterObject @Validated IdsParam idsParam
) {
Optional<User> optional = userRepository.findById(idParam.getId()); Optional<User> optional = userRepository.findById(idParam.getId());
if (optional.isEmpty()) { if (optional.isEmpty()) {
throw Status.NOT_FOUND.exception(); throw Status.NOT_FOUND.exception();
...@@ -67,9 +74,9 @@ public class UserController { ...@@ -67,9 +74,9 @@ public class UserController {
return Result.ok(); return Result.ok();
} }
@ApiOperation(value = "分页查询") @Operation(summary = "分页查询")
@GetMapping("page") @GetMapping("page")
public Result<PageVo<UserVo>> page(@Validated PageParam param) { public Result<PageVo<UserVo>> page(@ParameterObject @Validated PageParam param) {
Page<User> page = userRepository.findAll(PageParam.toPageable(param)); Page<User> page = userRepository.findAll(PageParam.toPageable(param));
List<UserVo> data = page.get().map(user -> Commons.transform(user, UserVo.class)).collect(Collectors.toList()); List<UserVo> data = page.get().map(user -> Commons.transform(user, UserVo.class)).collect(Collectors.toList());
......
/* (C) 2022 YiRing, Inc. */ /* (C) 2022 YiRing, Inc. */
package com.yiring.auth.web.user; package com.yiring.auth.web.user;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.yiring.auth.domain.permission.Permission; import com.yiring.auth.domain.permission.Permission;
import com.yiring.auth.domain.user.User; import com.yiring.auth.domain.user.User;
import com.yiring.auth.util.Auths; import com.yiring.auth.util.Auths;
...@@ -9,8 +8,10 @@ import com.yiring.auth.util.Permissions; ...@@ -9,8 +8,10 @@ import com.yiring.auth.util.Permissions;
import com.yiring.auth.vo.permission.MenuVo; import com.yiring.auth.vo.permission.MenuVo;
import com.yiring.auth.vo.user.UserInfoVo; import com.yiring.auth.vo.user.UserInfoVo;
import com.yiring.common.core.Result; import com.yiring.common.core.Result;
import io.swagger.annotations.Api; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.extensions.Extension;
import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
...@@ -31,9 +32,11 @@ import org.springframework.web.bind.annotation.RestController; ...@@ -31,9 +32,11 @@ import org.springframework.web.bind.annotation.RestController;
@Slf4j @Slf4j
@Validated @Validated
@SuppressWarnings({ "deprecation" }) @Tag(
@ApiSupport(order = -95) name = "用户信息",
@Api(tags = "用户信息", description = "UserView") description = "UserView",
extensions = { @Extension(properties = { @ExtensionProperty(name = "x-order", value = "-9998") }) }
)
@RestController @RestController
@RequestMapping("/user/") @RequestMapping("/user/")
@RequiredArgsConstructor @RequiredArgsConstructor
...@@ -41,7 +44,7 @@ public class UserViewController { ...@@ -41,7 +44,7 @@ public class UserViewController {
final Auths auths; final Auths auths;
@ApiOperation(value = "获取登录用户信息") @Operation(summary = "获取登录用户信息")
@GetMapping("getUserInfo") @GetMapping("getUserInfo")
public Result<UserInfoVo> getUserInfo() { public Result<UserInfoVo> getUserInfo() {
User user = auths.getLoginUser(); User user = auths.getLoginUser();
...@@ -57,7 +60,7 @@ public class UserViewController { ...@@ -57,7 +60,7 @@ public class UserViewController {
return Result.ok(userInfoVo); return Result.ok(userInfoVo);
} }
@ApiOperation(value = "获取用户菜单") @Operation(summary = "获取用户菜单")
@GetMapping("getMenuList") @GetMapping("getMenuList")
public Result<ArrayList<MenuVo>> getMenuList() { public Result<ArrayList<MenuVo>> getMenuList() {
User user = auths.getLoginUser(); User user = auths.getLoginUser();
...@@ -70,7 +73,7 @@ public class UserViewController { ...@@ -70,7 +73,7 @@ public class UserViewController {
return Result.ok((ArrayList<MenuVo>) vos); return Result.ok((ArrayList<MenuVo>) vos);
} }
@ApiOperation(value = "获取用户权限") @Operation(summary = "获取用户权限")
@GetMapping("getPermCode") @GetMapping("getPermCode")
public Result<ArrayList<String>> getPermCode() { public Result<ArrayList<String>> getPermCode() {
User user = auths.getLoginUser(); User user = auths.getLoginUser();
......
# Sa-Token配置 # Sa-Token配置
sa-token: sa-token:
# token名称 (同时也是cookie名称) # token名称 (同时也是cookie名称)
token-name: Authorization token-name: App-Token
# token有效期,单位s 默认30天, -1代表永不过期 # token有效期,单位s 默认30天, -1代表永不过期
timeout: 2592000 timeout: 2592000
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒 # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
......
Code.1000=\u7528\u6237\u4E0D\u5B58\u5728
Code.1001=\u6743\u9650\u6807\u8BC6\u91CD\u590D Code.1001=\u6743\u9650\u6807\u8BC6\u91CD\u590D
Code.1002=\u89D2\u8272\u6807\u8BC6\u91CD\u590D Code.1002=\u89D2\u8272\u6807\u8BC6\u91CD\u590D
Code.10000=\u4E8C\u7EA7\u8BA4\u8BC1\u6821\u9A8C\u5931\u8D25
Code.10001=\u89D2\u8272\u6743\u9650\u4E0D\u8DB3
Code.10002=\u6743\u9650\u4E0D\u8DB3
Code.10003=\u672A\u6388\u6743
Code.100000=\u7528\u6237\u540D\u5DF2\u5B58\u5728 Code.100000=\u7528\u6237\u540D\u5DF2\u5B58\u5728
Code.100001=\u624B\u673A\u53F7\u5DF2\u5B58\u5728 Code.100001=\u624B\u673A\u53F7\u5DF2\u5B58\u5728
Code.100002=\u90AE\u7BB1\u5DF2\u5B58\u5728 Code.100002=\u90AE\u7BB1\u5DF2\u5B58\u5728
......
Code.1000=\u7528\u6237\u4E0D\u5B58\u5728
Code.1001=\u6743\u9650\u6807\u8BC6\u91CD\u590D Code.1001=\u6743\u9650\u6807\u8BC6\u91CD\u590D
Code.1002=\u89D2\u8272\u6807\u8BC6\u91CD\u590D Code.1002=\u89D2\u8272\u6807\u8BC6\u91CD\u590D
Code.10000=\u4E8C\u7EA7\u8BA4\u8BC1\u6821\u9A8C\u5931\u8D25
Code.10001=\u89D2\u8272\u6743\u9650\u4E0D\u8DB3
Code.10002=\u6743\u9650\u4E0D\u8DB3
Code.10003=\u672A\u6388\u6743
Code.100000=\u7528\u6237\u540D\u5DF2\u5B58\u5728 Code.100000=\u7528\u6237\u540D\u5DF2\u5B58\u5728
Code.100001=\u624B\u673A\u53F7\u5DF2\u5B58\u5728 Code.100001=\u624B\u673A\u53F7\u5DF2\u5B58\u5728
Code.100002=\u90AE\u7BB1\u5DF2\u5B58\u5728 Code.100002=\u90AE\u7BB1\u5DF2\u5B58\u5728
......
Status.OK=OK Status.OK=\u6210\u529F
Status.NON_AUTHORITATIVE_INFORMATION=\u8BA4\u8BC1\u5931\u8D25 Status.NON_AUTHORITATIVE_INFORMATION=\u8BA4\u8BC1\u5931\u8D25
Status.BAD_REQUEST=\u8BF7\u6C42\u5931\u8D25 Status.BAD_REQUEST=\u8BF7\u6C42\u5931\u8D25
Status.UNAUTHORIZED=\u51ED\u8BC1\u8FC7\u671F Status.UNAUTHORIZED=\u51ED\u8BC1\u8FC7\u671F
......
...@@ -11,7 +11,7 @@ dependencies { ...@@ -11,7 +11,7 @@ dependencies {
implementation fileTree(dir: project.rootDir.getPath() + '\\libs', includes: ['*jar']) implementation fileTree(dir: project.rootDir.getPath() + '\\libs', includes: ['*jar'])
// swagger(knife4j) // swagger(knife4j)
implementation "com.github.xiaoymin:knife4j-spring-boot-starter:${knife4jVersion}" implementation "com.github.xiaoymin:knife4j-openapi3-jakarta-spring-boot-starter:${knife4jOpen3Version}"
// hutool-extra // hutool-extra
implementation "cn.hutool:hutool-extra:${hutoolVersion}" implementation "cn.hutool:hutool-extra:${hutoolVersion}"
...@@ -22,9 +22,9 @@ dependencies { ...@@ -22,9 +22,9 @@ dependencies {
// JTS 几何对象操作库 // JTS 几何对象操作库
implementation "org.locationtech.jts:jts-core:${jtsVersion}" implementation "org.locationtech.jts:jts-core:${jtsVersion}"
// https://github.com/vladmihalcea/hibernate-types // https://github.com/vladmihalcea/hypersistence-utils
// hibernate-types-55 // hypersistence-utils-hibernate-60
implementation "com.vladmihalcea:hibernate-types-55:${hibernateTypesVersion}" implementation "io.hypersistence:hypersistence-utils-hibernate-60:${hibernateTypesVersion}"
// https://mvnrepository.com/artifact/org.n52.jackson/jackson-datatype-jts/1.2.10 // https://mvnrepository.com/artifact/org.n52.jackson/jackson-datatype-jts/1.2.10
implementation("org.n52.jackson:jackson-datatype-jts:1.2.10") { implementation("org.n52.jackson:jackson-datatype-jts:1.2.10") {
......
/* (C) 2023 YiRing, Inc. */
package com.yiring.common.annotation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import java.lang.annotation.*;
import org.springframework.core.annotation.AliasFor;
import org.springframework.http.MediaType;
/**
* 下载响应注解
*
* @author Jim
* @version 0.1
* 2023/1/12 11:22
*/
@SuppressWarnings({ "unused" })
@Target({ ElementType.METHOD, ElementType.TYPE, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@ApiResponse
public @interface DownloadResponse {
@AliasFor(annotation = ApiResponse.class)
String responseCode() default "200";
@AliasFor(annotation = ApiResponse.class)
String description() default "OK";
@AliasFor(annotation = ApiResponse.class)
Content content() default @Content(
schema = @Schema(type = "string", format = "binary"),
mediaType = MediaType.APPLICATION_OCTET_STREAM_VALUE
);
}
/* (C) 2021 YiRing, Inc. */ /* (C) 2021 YiRing, Inc. */
package com.yiring.common.aspect; package com.yiring.common.aspect;
import cn.hutool.extra.servlet.ServletUtil; import cn.hutool.extra.servlet.JakartaServletUtil;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter; import com.alibaba.fastjson2.JSONWriter;
import com.yiring.common.constant.DateFormatter; import com.yiring.common.constant.DateFormatter;
import com.yiring.common.core.Result; import com.yiring.common.core.Result;
import com.yiring.common.util.Commons; import com.yiring.common.util.Commons;
import jakarta.servlet.http.HttpServletRequest;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Around;
...@@ -36,9 +36,9 @@ public class RequestAspect { ...@@ -36,9 +36,9 @@ public class RequestAspect {
Boolean debug; Boolean debug;
/** /**
* 白名单 * 白名单(忽略)
*/ */
List<String> WHITE_LIST = List.of("/swagger-resources", "/error"); List<String> IGNORE_LIST = List.of("/swagger-resources", "/error", "/v3/api-docs");
@Pointcut( @Pointcut(
"@annotation(org.springframework.web.bind.annotation.RequestMapping) || @annotation(org.springframework.web.bind.annotation.PostMapping) || @annotation(org.springframework.web.bind.annotation.GetMapping) || @annotation(org.springframework.web.bind.annotation.PutMapping) || @annotation(org.springframework.web.bind.annotation.DeleteMapping) || @annotation(org.springframework.web.bind.annotation.PatchMapping) || @annotation(org.springframework.web.bind.annotation.ExceptionHandler)" "@annotation(org.springframework.web.bind.annotation.RequestMapping) || @annotation(org.springframework.web.bind.annotation.PostMapping) || @annotation(org.springframework.web.bind.annotation.GetMapping) || @annotation(org.springframework.web.bind.annotation.PutMapping) || @annotation(org.springframework.web.bind.annotation.DeleteMapping) || @annotation(org.springframework.web.bind.annotation.PatchMapping) || @annotation(org.springframework.web.bind.annotation.ExceptionHandler)"
...@@ -49,8 +49,10 @@ public class RequestAspect { ...@@ -49,8 +49,10 @@ public class RequestAspect {
public Object around(ProceedingJoinPoint point) throws Throwable { public Object around(ProceedingJoinPoint point) throws Throwable {
HttpServletRequest request = getRequest(); HttpServletRequest request = getRequest();
// 放行白名单 // 放行白名单
if (WHITE_LIST.contains(request.getServletPath())) { for (String path : IGNORE_LIST) {
return point.proceed(); if (request.getServletPath().startsWith(path)) {
return point.proceed();
}
} }
// 计算接口执行耗时 // 计算接口执行耗时
...@@ -66,10 +68,13 @@ public class RequestAspect { ...@@ -66,10 +68,13 @@ public class RequestAspect {
String extra = ""; String extra = "";
if (Boolean.TRUE.equals(debug)) { if (Boolean.TRUE.equals(debug)) {
String headers = JSONObject.toJSONString( String headers = JSONObject.toJSONString(
ServletUtil.getHeaderMap(request), JakartaServletUtil.getHeaderMap(request),
JSONWriter.Feature.PrettyFormat
);
String params = JSONObject.toJSONString(
JakartaServletUtil.getParamMap(request),
JSONWriter.Feature.PrettyFormat JSONWriter.Feature.PrettyFormat
); );
String params = JSONObject.toJSONString(ServletUtil.getParamMap(request), JSONWriter.Feature.PrettyFormat);
extra += String.format("\nHeaders: %s", headers); extra += String.format("\nHeaders: %s", headers);
extra += String.format("\nParams: %s", params); extra += String.format("\nParams: %s", params);
if (result instanceof Result) { if (result instanceof Result) {
......
/* (C) 2023 YiRing, Inc. */
package com.yiring.common.config;
import com.yiring.common.core.I18n;
import com.yiring.common.core.Result;
import com.yiring.common.core.Status;
import com.yiring.common.exception.BusinessException;
import com.yiring.common.exception.FailStatusException;
import jakarta.validation.ConstraintViolationException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.ClientAbortException;
import org.aspectj.bridge.AbortException;
import org.hibernate.validator.internal.engine.ConstraintViolationImpl;
import org.springframework.core.annotation.Order;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 核心异常拦截处理
*
* @author Jim
* @version 0.1
* 2023/1/12 14:06
*/
@Slf4j
@Order(0)
@RestControllerAdvice
@RequiredArgsConstructor
public class CoreExceptionHandler {
final I18n i18n;
/**
* 参数校验异常
*
* @param e 异常信息
* @return 统一的校验失败信息 {@link Status#EXPECTATION_FAILED
*/
@ExceptionHandler(
{ BindException.class, MethodArgumentNotValidException.class, ConstraintViolationException.class }
)
public Result<String> validFailHandler(Exception e) {
String details = null;
if (e instanceof ConstraintViolationException) {
details = ((ConstraintViolationException) e).getConstraintViolations().iterator().next().getMessage();
} else {
BindingResult result = null;
if (e instanceof MethodArgumentNotValidException) {
result = ((MethodArgumentNotValidException) e).getBindingResult();
} else if (e instanceof BindException) {
result = ((BindException) e).getBindingResult();
}
if (result != null) {
ObjectError error = result.getAllErrors().iterator().next();
if (error instanceof FieldError fieldError) {
// 构建明确的字段错误提示, 例如: id 不能为 null, 如果自己填写了 message 则不追加 field 字段前缀
ConstraintViolationImpl<?> violation = error.unwrap(ConstraintViolationImpl.class);
String template = violation.getMessageTemplate();
String prefix = "";
// 如果是模板字符串, 则在消息前添加字段提示
if (template.contains("{") && template.contains("}")) {
prefix = "参数" + fieldError.getField();
}
details = prefix + i18n.get(fieldError);
} else {
details = i18n.get(error);
}
}
}
return Result.no(Status.EXPECTATION_FAILED, details);
}
/**
* 参数读取解析失败异常
* eg: 例如参数使用 RequestBody 接收,但是传了个空字符串,导致解析失败
*
* @param e 异常
* @return 校验失败信息 {@link Status#EXPECTATION_FAILED
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
public Result<String> httpMessageNotReadableExceptionHandler(Exception e) {
log.warn(e.getMessage(), e);
return Result.no(Status.EXPECTATION_FAILED);
}
/**
* 不支持的HttpMethod异常
*
* @param e 异常信息
* @return 异常信息反馈 {@link Status#METHOD_NOT_ALLOWED
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Result<String> httpRequestMethodNotSupportedErrorHandler(Exception e) {
return Result.no(Status.METHOD_NOT_ALLOWED, e.getMessage());
}
/**
* 自定义业务异常
*/
@ExceptionHandler(BusinessException.class)
public Result<String> businessExceptionHandler(BusinessException e) {
return Result.no(e.getStatus(), e.getMessage());
}
/**
* 失败状态异常
*/
@ExceptionHandler(FailStatusException.class)
public Result<String> failStatusExceptionHandler(FailStatusException e) {
return Result.no(e.getStatus(), e.getMessage());
}
/**
* 取消请求异常(忽略)
*/
@ExceptionHandler({ ClientAbortException.class, AbortException.class, HttpMessageNotWritableException.class })
public void ignoreExceptionHandler() {}
}
...@@ -4,7 +4,7 @@ package com.yiring.common.config; ...@@ -4,7 +4,7 @@ package com.yiring.common.config;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import javax.annotation.Resource; import lombok.RequiredArgsConstructor;
import org.n52.jackson.datatype.jts.JtsModule; import org.n52.jackson.datatype.jts.JtsModule;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -18,10 +18,10 @@ import org.springframework.context.annotation.Configuration; ...@@ -18,10 +18,10 @@ import org.springframework.context.annotation.Configuration;
*/ */
@Configuration @Configuration
@RequiredArgsConstructor
public class JacksonConfig { public class JacksonConfig {
@Resource final JavaTimeModule javaTimeModule;
JavaTimeModule javaTimeModule;
@Bean @Bean
public ObjectMapper objectMapper() { public ObjectMapper objectMapper() {
......
/* (C) 2021 YiRing, Inc. */ /* (C) 2021 YiRing, Inc. */
package com.yiring.common.core; package com.yiring.common.core;
import cn.hutool.core.convert.Convert;
import cn.hutool.extra.spring.SpringUtil; import cn.hutool.extra.spring.SpringUtil;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.Objects;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.PropertyKey; import org.jetbrains.annotations.PropertyKey;
/** /**
...@@ -23,13 +23,11 @@ import org.jetbrains.annotations.PropertyKey; ...@@ -23,13 +23,11 @@ import org.jetbrains.annotations.PropertyKey;
*/ */
@SuppressWarnings({ "unchecked", "unused" }) @SuppressWarnings({ "unchecked", "unused" })
@ApiModel("Result")
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
@Slf4j
@Data @Data
@Builder @Builder
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public class Result<T extends Serializable> implements Serializable { public class Result<T> implements Serializable {
@Serial @Serial
private static final long serialVersionUID = -4802543396830024571L; private static final long serialVersionUID = -4802543396830024571L;
...@@ -46,49 +44,49 @@ public class Result<T extends Serializable> implements Serializable { ...@@ -46,49 +44,49 @@ public class Result<T extends Serializable> implements Serializable {
/** /**
* 接口响应时间 * 接口响应时间
*/ */
@ApiModelProperty(value = "响应时间", example = "2021-01-01 00:00:00") @Schema(description = "响应时间", example = "2021-01-01 00:00:00")
String timestamp; String timestamp;
/** /**
* 接口耗时(单位:秒)通常在调试阶段出现 * 接口耗时(单位:秒)通常在调试阶段出现
*/ */
@ApiModelProperty(value = "耗时", example = "0.001s") @Schema(description = "耗时", example = "0.001s")
String times; String times;
/** /**
* 响应状态码 * 响应状态码
*/ */
@ApiModelProperty(value = "状态码", example = "200") @Schema(description = "状态码", example = "200", defaultValue = "200")
Integer status; Integer status;
/** /**
* 业务标识码 * 响应消息
*/ */
@ApiModelProperty(value = "业务标识码", example = "0") @Schema(description = "消息", example = "OK", defaultValue = "OK")
Integer code; String message;
/** /**
* 响应消息 * 业务标识码
*/ */
@ApiModelProperty(value = "消息", example = "OK") @Schema(description = "业务标识码", example = "0", nullable = true)
String message; Integer code;
/** /**
* 详细信息,通常为参数校验结果或自定义消息 * 详细信息,通常为参数校验结果或自定义消息
*/ */
@ApiModelProperty(value = "详细信息", example = "Details message") @Schema(description = "详细信息", nullable = true)
String details; String details;
/** /**
* 异常信息,通常在出现服务器错误时会出现该异常 * 异常信息,通常在出现服务器错误时会出现该异常
*/ */
@ApiModelProperty(value = "异常信息", notes = "出现错误时会出现该字段", example = "Error message") @Schema(description = "异常信息", nullable = true)
String error; String error;
/** /**
* 响应内容 * 响应内容
*/ */
@ApiModelProperty("内容") @Schema(description = "内容")
T body; T body;
/** /**
...@@ -97,7 +95,7 @@ public class Result<T extends Serializable> implements Serializable { ...@@ -97,7 +95,7 @@ public class Result<T extends Serializable> implements Serializable {
* @return Result * @return Result
* @see com.yiring.common.core.Status * @see com.yiring.common.core.Status
*/ */
public static <T extends Serializable> Result<T> ok() { public static <T> Result<T> ok() {
return (Result<T>) Result.builder().status(Status.OK.value()).message(t(Status.OK.getReasonPhrase())).build(); return (Result<T>) Result.builder().status(Status.OK.value()).message(t(Status.OK.getReasonPhrase())).build();
} }
...@@ -108,9 +106,7 @@ public class Result<T extends Serializable> implements Serializable { ...@@ -108,9 +106,7 @@ public class Result<T extends Serializable> implements Serializable {
* @return Result * @return Result
* @see com.yiring.common.core.Status * @see com.yiring.common.core.Status
*/ */
public static <T extends Serializable> Result<T> ok( public static <T> Result<T> ok(@PropertyKey(resourceBundle = I18n.RESOURCE_BUNDLE) String body) {
@PropertyKey(resourceBundle = I18n.RESOURCE_BUNDLE) String body
) {
return (Result<T>) Result return (Result<T>) Result
.builder() .builder()
.status(Status.OK.value()) .status(Status.OK.value())
...@@ -125,7 +121,7 @@ public class Result<T extends Serializable> implements Serializable { ...@@ -125,7 +121,7 @@ public class Result<T extends Serializable> implements Serializable {
* @param body {@link Object} * @param body {@link Object}
* @return Result * @return Result
*/ */
public static <T extends Serializable> Result<T> ok(T body) { public static <T> Result<T> ok(T body) {
return (Result<T>) Result return (Result<T>) Result
.builder() .builder()
.status(Status.OK.value()) .status(Status.OK.value())
...@@ -140,7 +136,7 @@ public class Result<T extends Serializable> implements Serializable { ...@@ -140,7 +136,7 @@ public class Result<T extends Serializable> implements Serializable {
* @return Result * @return Result
* @see Status#BAD_REQUEST * @see Status#BAD_REQUEST
*/ */
public static <T extends Serializable> Result<T> no() { public static <T> Result<T> no() {
return no(Status.BAD_REQUEST); return no(Status.BAD_REQUEST);
} }
...@@ -150,9 +146,7 @@ public class Result<T extends Serializable> implements Serializable { ...@@ -150,9 +146,7 @@ public class Result<T extends Serializable> implements Serializable {
* @return Result * @return Result
* @see Status#BAD_REQUEST * @see Status#BAD_REQUEST
*/ */
public static <T extends Serializable> Result<T> no( public static <T> Result<T> no(@PropertyKey(resourceBundle = I18n.RESOURCE_BUNDLE) String details) {
@PropertyKey(resourceBundle = I18n.RESOURCE_BUNDLE) String details
) {
return no(Status.BAD_REQUEST, details); return no(Status.BAD_REQUEST, details);
} }
...@@ -162,7 +156,7 @@ public class Result<T extends Serializable> implements Serializable { ...@@ -162,7 +156,7 @@ public class Result<T extends Serializable> implements Serializable {
* @return Result * @return Result
* @see Status#BAD_REQUEST * @see Status#BAD_REQUEST
*/ */
public static <T extends Serializable> Result<T> no(Status status) { public static <T> Result<T> no(Status status) {
return no(status, null, null, null); return no(status, null, null, null);
} }
...@@ -172,10 +166,7 @@ public class Result<T extends Serializable> implements Serializable { ...@@ -172,10 +166,7 @@ public class Result<T extends Serializable> implements Serializable {
* @return Result * @return Result
* @see Status * @see Status
*/ */
public static <T extends Serializable> Result<T> no( public static <T> Result<T> no(Status status, @PropertyKey(resourceBundle = I18n.RESOURCE_BUNDLE) String details) {
Status status,
@PropertyKey(resourceBundle = I18n.RESOURCE_BUNDLE) String details
) {
return no(status, null, details, null); return no(status, null, details, null);
} }
...@@ -185,7 +176,7 @@ public class Result<T extends Serializable> implements Serializable { ...@@ -185,7 +176,7 @@ public class Result<T extends Serializable> implements Serializable {
* @return Result * @return Result
* @see Status * @see Status
*/ */
public static <T extends Serializable> Result<T> no(Status status, Throwable error) { public static <T> Result<T> no(Status status, Throwable error) {
return no(status, null, null, error); return no(status, null, null, error);
} }
...@@ -195,12 +186,22 @@ public class Result<T extends Serializable> implements Serializable { ...@@ -195,12 +186,22 @@ public class Result<T extends Serializable> implements Serializable {
* @return Result * @return Result
* @see Status * @see Status
*/ */
public static <T extends Serializable> Result<T> no( public static <T> Result<T> no(
Status status, Status status,
Integer code, Integer code,
@PropertyKey(resourceBundle = I18n.RESOURCE_BUNDLE) String details, @PropertyKey(resourceBundle = I18n.RESOURCE_BUNDLE) String details,
Throwable error Throwable error
) { ) {
if (Objects.isNull(code)) {
String prefix = "Code.";
if (details.startsWith(prefix)) {
String codeText = details.replace(prefix, "");
code = Convert.toInt(codeText);
} else {
code = -1;
}
}
Result<T> result = (Result<T>) Result Result<T> result = (Result<T>) Result
.builder() .builder()
.status(status.value()) .status(status.value())
......
/* (C) 2022 YiRing, Inc. */ /* (C) 2022 YiRing, Inc. */
package com.yiring.common.domain; package com.yiring.common.domain;
import com.vladmihalcea.hibernate.type.json.JsonBinaryType; import com.yiring.common.snowflake.SnowflakeId;
import com.vladmihalcea.hibernate.type.json.JsonType; import jakarta.persistence.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import javax.persistence.*;
import lombok.*; import lombok.*;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
import lombok.experimental.FieldNameConstants; import lombok.experimental.FieldNameConstants;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import org.hibernate.annotations.*; import org.hibernate.annotations.Comment;
import org.hibernate.snowflake.SnowflakeId; import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.UpdateTimestamp;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/** /**
* 基础表抽象类 * 基础表抽象类
...@@ -28,12 +30,8 @@ import org.hibernate.snowflake.SnowflakeId; ...@@ -28,12 +30,8 @@ import org.hibernate.snowflake.SnowflakeId;
@FieldNameConstants @FieldNameConstants
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
@SuperBuilder(toBuilder = true) @SuperBuilder(toBuilder = true)
@TypeDefs(
value = {
@TypeDef(name = "json", typeClass = JsonType.class), @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class),
}
)
@MappedSuperclass @MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BasicEntity { public abstract class BasicEntity {
@Comment("主键") @Comment("主键")
......
/* (C) 2022 YiRing, Inc. */ /* (C) 2022 YiRing, Inc. */
package com.yiring.common.exception; package com.yiring.common.exception;
import cn.hutool.core.convert.Convert;
import com.yiring.common.core.I18n; import com.yiring.common.core.I18n;
import com.yiring.common.core.Status;
import java.io.Serial; import java.io.Serial;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
import org.jetbrains.annotations.PropertyKey; import org.jetbrains.annotations.PropertyKey;
...@@ -22,7 +22,6 @@ import org.jetbrains.annotations.PropertyKey; ...@@ -22,7 +22,6 @@ import org.jetbrains.annotations.PropertyKey;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Data @Data
@AllArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public class BusinessException extends RuntimeException { public class BusinessException extends RuntimeException {
...@@ -30,33 +29,28 @@ public class BusinessException extends RuntimeException { ...@@ -30,33 +29,28 @@ public class BusinessException extends RuntimeException {
private static final long serialVersionUID = -4226669531686389671L; private static final long serialVersionUID = -4226669531686389671L;
/** /**
* 业务状态 * 状态码
*/ */
Integer code; Status status;
/** /**
* 业务状态异常消息 * 业务状态异常消息
*/ */
String message; String message;
public BusinessException(@PropertyKey(resourceBundle = I18n.RESOURCE_BUNDLE) String message) { public BusinessException(Status status, @PropertyKey(resourceBundle = I18n.RESOURCE_BUNDLE) String message) {
String prefix = "Code."; this.status = status;
if (message.startsWith(prefix)) {
String code = message.replaceAll(".*(\\d+).*", "$1");
this.code = Convert.toInt(code);
this.message = message;
} else {
this.code = -1;
this.message = "Unknown Error";
}
}
public BusinessException(int code, @PropertyKey(resourceBundle = I18n.RESOURCE_BUNDLE) String message) {
this.code = code;
this.message = message; this.message = message;
} }
public static BusinessException i18n(@PropertyKey(resourceBundle = I18n.RESOURCE_BUNDLE) String message) { public static BusinessException i18n(@PropertyKey(resourceBundle = I18n.RESOURCE_BUNDLE) String message) {
return new BusinessException(message); return new BusinessException(Status.BAD_REQUEST, message);
}
public static BusinessException i18n(
@NonNull Status status,
@PropertyKey(resourceBundle = I18n.RESOURCE_BUNDLE) String message
) {
return new BusinessException(status, message);
} }
} }
/* (C) 2021 YiRing, Inc. */ /* (C) 2021 YiRing, Inc. */
package com.yiring.common.param; package com.yiring.common.param;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.annotations.ApiModelProperty; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import javax.validation.constraints.NotBlank;
import lombok.*; import lombok.*;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
...@@ -16,7 +16,7 @@ import lombok.experimental.FieldDefaults; ...@@ -16,7 +16,7 @@ import lombok.experimental.FieldDefaults;
* @version 0.1 * @version 0.1
* 2019/5/28 22:11 * 2019/5/28 22:11
*/ */
@ApiModel(value = "IdParam", description = "公共的 ID 查询参数") @Schema(name = "IdParam", description = "公共的 ID 查询参数")
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
...@@ -27,7 +27,7 @@ public class IdParam implements Serializable { ...@@ -27,7 +27,7 @@ public class IdParam implements Serializable {
@Serial @Serial
private static final long serialVersionUID = -8690942241103456893L; private static final long serialVersionUID = -8690942241103456893L;
@ApiModelProperty(value = "id", example = "1", required = true) @Parameter(description = "id", example = "1")
@NotBlank @NotBlank
String id; String id;
} }
/* (C) 2021 YiRing, Inc. */ /* (C) 2021 YiRing, Inc. */
package com.yiring.common.param; package com.yiring.common.param;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.annotations.ApiModelProperty; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.validation.constraints.NotBlank;
import lombok.*; import lombok.*;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
...@@ -19,7 +19,7 @@ import lombok.experimental.FieldDefaults; ...@@ -19,7 +19,7 @@ import lombok.experimental.FieldDefaults;
* @version 0.1 * @version 0.1
* 2019/5/28 22:11 * 2019/5/28 22:11
*/ */
@ApiModel("IdsParam") @Schema(name = "IdsParam")
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
...@@ -30,7 +30,7 @@ public class IdsParam implements Serializable { ...@@ -30,7 +30,7 @@ public class IdsParam implements Serializable {
@Serial @Serial
private static final long serialVersionUID = -8379896695668632733L; private static final long serialVersionUID = -8379896695668632733L;
@ApiModelProperty(value = "ids 多个以逗号分割", example = "1,2", required = true) @Parameter(description = "ids 多个以逗号分割", example = "1,2")
@NotBlank @NotBlank
String ids; String ids;
......
/* (C) 2021 YiRing, Inc. */ /* (C) 2021 YiRing, Inc. */
package com.yiring.common.param; package com.yiring.common.param;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.annotations.ApiModelProperty; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import javax.validation.constraints.NotBlank;
import lombok.*; import lombok.*;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
...@@ -16,7 +16,7 @@ import lombok.experimental.FieldDefaults; ...@@ -16,7 +16,7 @@ import lombok.experimental.FieldDefaults;
* @version 0.1 * @version 0.1
* 2022/4/27 08:53 * 2022/4/27 08:53
*/ */
@ApiModel(value = "KeywordParam", description = "公共的关键字查询参数") @Schema(name = "KeywordParam", description = "公共的关键字查询参数")
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
...@@ -27,7 +27,7 @@ public class KeywordParam implements Serializable { ...@@ -27,7 +27,7 @@ public class KeywordParam implements Serializable {
@Serial @Serial
private static final long serialVersionUID = -8690942241103456894L; private static final long serialVersionUID = -8690942241103456894L;
@ApiModelProperty(value = "关键字", example = "hi") @Parameter(description = "关键字", example = "hi")
@NotBlank @NotBlank
String keyword; String keyword;
} }
/* (C) 2021 YiRing, Inc. */ /* (C) 2021 YiRing, Inc. */
package com.yiring.common.param; package com.yiring.common.param;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.annotations.ApiModelProperty; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.Objects; import java.util.Objects;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
...@@ -26,7 +26,7 @@ import org.springframework.data.domain.Sort; ...@@ -26,7 +26,7 @@ import org.springframework.data.domain.Sort;
* @author ifzm * @author ifzm
* @version 0.1 2019/3/10 16:29 * @version 0.1 2019/3/10 16:29
*/ */
@ApiModel(value = "PageParam", description = "公共的分页排序查询参数") @Schema(name = "PageParam", description = "公共的分页排序查询参数")
@Data @Data
@SuperBuilder @SuperBuilder
@NoArgsConstructor @NoArgsConstructor
...@@ -37,20 +37,22 @@ public class PageParam implements Serializable { ...@@ -37,20 +37,22 @@ public class PageParam implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 6103761701912769946L; private static final long serialVersionUID = 6103761701912769946L;
@ApiModelProperty(value = "分页条数", example = "10") @Parameter
@Schema(description = "分页条数", defaultValue = "10", example = "10", type = "integer")
@NotNull @NotNull
@Range(min = 1, max = 100) @Range(min = 1, max = 100)
Integer pageSize; Integer pageSize;
@ApiModelProperty(value = "当前页数", example = "1") @Parameter
@Schema(description = "当前页数", defaultValue = "1", example = "1", type = "integer")
@NotNull @NotNull
@Min(1) @Min(1)
Integer pageNo; Integer pageNo;
@ApiModelProperty(value = "排序字段", example = "id") @Schema(description = "排序字段", defaultValue = "id", example = "id")
String sortField; String sortField;
@ApiModelProperty(value = "排序方向(ASC|DESC)", example = "DESC") @Schema(description = "排序方向(ASC|DESC)", defaultValue = "DESC", example = "DESC")
Sort.Direction sortOrder; Sort.Direction sortOrder;
/** /**
......
/* (C) 2021 YiRing, Inc. */ /* (C) 2021 YiRing, Inc. */
package com.yiring.common.param; package com.yiring.common.param;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.annotations.ApiModelProperty; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import javax.validation.constraints.NotBlank;
import lombok.*; import lombok.*;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
...@@ -16,7 +16,7 @@ import lombok.experimental.FieldDefaults; ...@@ -16,7 +16,7 @@ import lombok.experimental.FieldDefaults;
* @version 0.1 * @version 0.1
* 2019/5/28 22:11 * 2019/5/28 22:11
*/ */
@ApiModel(value = "PidParam", description = "公共的父级 ID 查询参数") @Schema(name = "PidParam", description = "公共的父级 ID 查询参数")
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
...@@ -27,7 +27,7 @@ public class PidParam implements Serializable { ...@@ -27,7 +27,7 @@ public class PidParam implements Serializable {
@Serial @Serial
private static final long serialVersionUID = -8690942241103456893L; private static final long serialVersionUID = -8690942241103456893L;
@ApiModelProperty(value = "pid", example = "0", required = true) @Parameter(description = "pid", example = "0")
@NotBlank @NotBlank
String pid; String pid;
} }
/* (C) 2023 YiRing, Inc. */
package com.yiring.common.snowflake;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import java.io.Serializable;
import java.util.Properties;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.Type;
import org.springframework.stereotype.Component;
/**
* 基于雪花算法的 ID 生成器
* 生成 Long 类型
*
* @author ifzm
* @version 0.1
* 2020/1/14 16:18
*/
@Component
public class GenerateLongId implements IdentifierGenerator {
private Snowflake snowflake;
@Override
public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
return snowflake.nextId();
}
@Override
public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {
snowflake = IdUtil.getSnowflake();
}
}
/* (C) 2023 YiRing, Inc. */
package com.yiring.common.snowflake;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import java.io.Serializable;
import java.util.Properties;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.Type;
import org.springframework.stereotype.Component;
/**
* 基于雪花算法的 ID 生成器
* 生成 String 类型
*
* @author ifzm
* @version 0.1
* 2020/1/14 16:18
*/
@Component
public class GenerateStringId implements IdentifierGenerator {
private Snowflake snowflake;
@Override
public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
return snowflake.nextIdStr();
}
@Override
public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {
snowflake = IdUtil.getSnowflake();
}
}
/* (C) 2023 YiRing, Inc. */
package com.yiring.common.snowflake;
import lombok.experimental.UtilityClass;
/**
* 雪花 ID 生成器常量
*
* @author ifzm
* @version 0.1
* 2020/8/10 15:35
*/
@UtilityClass
public class SnowflakeId {
public final String GENERATOR = "snowflake";
public class Strategy {
public static final String STRING = "com.yiring.common.snowflake.GenerateStringId";
public static final String LONG = "com.yiring.common.snowflake.GenerateLongId";
}
}
...@@ -4,11 +4,11 @@ package com.yiring.common.validation; ...@@ -4,11 +4,11 @@ package com.yiring.common.validation;
import static java.lang.annotation.ElementType.*; import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
/** /**
* 枚举参数校验 * 枚举参数校验
......
/* (C) 2022 YiRing, Inc. */ /* (C) 2022 YiRing, Inc. */
package com.yiring.common.validation; package com.yiring.common.validation;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/** /**
* 枚举参数校验器 * 枚举参数校验器
......
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
package com.yiring.common.validation; package com.yiring.common.validation;
import com.yiring.common.util.Commons; import com.yiring.common.util.Commons;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.Validation;
import jakarta.validation.ValidatorFactory;
import java.util.Set; import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;
import lombok.Cleanup; import lombok.Cleanup;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
...@@ -34,7 +34,7 @@ public class ValidateUtil { ...@@ -34,7 +34,7 @@ public class ValidateUtil {
@Cleanup @Cleanup
ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Set<ConstraintViolation<T>> constraintViolations = factory.getValidator().validate(t, groups); Set<ConstraintViolation<T>> constraintViolations = factory.getValidator().validate(t, groups);
if (!Commons.isNullOrEmpty(constraintViolations)) { if (Commons.notEmpty(constraintViolations)) {
throw new ConstraintViolationException(constraintViolations); throw new ConstraintViolationException(constraintViolations);
} }
} }
......
/* (C) 2022 YiRing, Inc. */ /* (C) 2022 YiRing, Inc. */
package com.yiring.common.validation.group; package com.yiring.common.validation.group;
import javax.validation.groups.Default; import jakarta.validation.groups.Default;
/** /**
* validate group * validate group
......
/* (C) 2021 YiRing, Inc. */ /* (C) 2021 YiRing, Inc. */
package com.yiring.common.vo; package com.yiring.common.vo;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import lombok.*; import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
/** /**
...@@ -16,7 +18,7 @@ import lombok.experimental.FieldDefaults; ...@@ -16,7 +18,7 @@ import lombok.experimental.FieldDefaults;
* @version 0.1 * @version 0.1
* 2022/3/23 16:47 * 2022/3/23 16:47
*/ */
@ApiModel(value = "DataVo", description = "公共数据响应输出") @Schema(name = "DataVo", description = "公共数据响应输出")
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
...@@ -26,17 +28,18 @@ public class DataVo<T extends Serializable> implements Serializable { ...@@ -26,17 +28,18 @@ public class DataVo<T extends Serializable> implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 2472779197432240431L; private static final long serialVersionUID = 2472779197432240431L;
@ApiModelProperty(value = "数据") @Schema(description = "数据")
T data; T data;
/** /**
* 通常在带有时效性的数据查询时有用途(可选参数) * 通常在带有时效性的数据查询时有用途(可选参数)
*/ */
@ApiModelProperty(value = "数据最新时间") @Schema(description = "数据最新时间")
LocalDateTime latest; LocalDateTime latest;
/** /**
* 构建一个 DataVo * 构建一个 DataVo
*
* @param data 数据 * @param data 数据
* @return DataVo * @return DataVo
*/ */
...@@ -47,7 +50,8 @@ public class DataVo<T extends Serializable> implements Serializable { ...@@ -47,7 +50,8 @@ public class DataVo<T extends Serializable> implements Serializable {
/** /**
* 构建一个 DataVo * 构建一个 DataVo
* @param data 数据 *
* @param data 数据
* @param latest 数据最新时间 * @param latest 数据最新时间
* @return DataVo * @return DataVo
*/ */
......
/* (C) 2021 YiRing, Inc. */ /* (C) 2021 YiRing, Inc. */
package com.yiring.common.vo; package com.yiring.common.vo;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import lombok.*; import lombok.*;
...@@ -16,7 +15,7 @@ import lombok.experimental.FieldDefaults; ...@@ -16,7 +15,7 @@ import lombok.experimental.FieldDefaults;
* 2022/3/24 17:29 * 2022/3/24 17:29
*/ */
@ApiModel(value = "KeyValueVo", description = "键值对响应输出") @Schema(name = "KeyValueVo", description = "键值对响应输出")
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
...@@ -27,15 +26,15 @@ public class KeyValueVo implements Serializable { ...@@ -27,15 +26,15 @@ public class KeyValueVo implements Serializable {
@Serial @Serial
private static final long serialVersionUID = -5238793972067296346L; private static final long serialVersionUID = -5238793972067296346L;
@ApiModelProperty(value = "key", example = "key") @Schema(description = "key", example = "key")
String key; String key;
@ApiModelProperty(value = "value", example = "value") @Schema(description = "value", example = "value")
String value; String value;
/** /**
* 扩展字段,可用于文本输出 * 扩展字段,可用于文本输出
*/ */
@ApiModelProperty(value = "label", example = "label") @Schema(description = "label", example = "label")
String label; String label;
} }
...@@ -2,14 +2,16 @@ ...@@ -2,14 +2,16 @@
package com.yiring.common.vo; package com.yiring.common.vo;
import com.yiring.common.util.Commons; import com.yiring.common.util.Commons;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import lombok.*; import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
...@@ -19,7 +21,7 @@ import org.springframework.data.domain.Page; ...@@ -19,7 +21,7 @@ import org.springframework.data.domain.Page;
* @author ifzm * @author ifzm
* @version 0.1 2019/3/10 16:29 * @version 0.1 2019/3/10 16:29
*/ */
@ApiModel(value = "PageVo", description = "公共分页查询响应输出") @Schema(name = "PageVo", description = "公共分页查询响应输出")
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
...@@ -29,16 +31,16 @@ public class PageVo<T extends Serializable> implements Serializable { ...@@ -29,16 +31,16 @@ public class PageVo<T extends Serializable> implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 6103761701912769946L; private static final long serialVersionUID = 6103761701912769946L;
@ApiModelProperty(value = "数据", required = true) @Schema(description = "数据")
List<T> data; List<T> data;
@ApiModelProperty(value = "数据总数", example = "100", required = true) @Schema(description = "数据总数", example = "100")
Long total; Long total;
/** /**
* 通常在带有时效性的数据查询时有用途(可选参数) * 通常在带有时效性的数据查询时有用途(可选参数)
*/ */
@ApiModelProperty(value = "数据最新时间") @Schema(description = "数据最新时间")
LocalDateTime latest; LocalDateTime latest;
/** /**
......
dependencies { dependencies {
implementation project(":basic-common:core") implementation project(":basic-common:core")
implementation project(":basic-common:i18n")
implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-validation'
// swagger(knife4j) // swagger(knife4j)
implementation "com.github.xiaoymin:knife4j-spring-boot-starter:${knife4jVersion}" implementation "com.github.xiaoymin:knife4j-openapi3-jakarta-spring-boot-starter:${knife4jOpen3Version}"
// hutool-core // hutool-core
implementation "cn.hutool:hutool-core:${hutoolVersion}" implementation "cn.hutool:hutool-core:${hutoolVersion}"
......
/* (C) 2021 YiRing, Inc. */ /* (C) 2021 YiRing, Inc. */
package com.yiring.common.swagger; package com.yiring.common.swagger;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Console; import cn.hutool.core.lang.Console;
import cn.hutool.core.net.NetUtil; import cn.hutool.core.net.NetUtil;
import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver; import com.yiring.common.core.I18n;
import com.yiring.common.core.Status; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.annotations.Api; import io.swagger.v3.oas.models.ExternalDocumentation;
import java.util.Arrays; import io.swagger.v3.oas.models.OpenAPI;
import java.util.List; import io.swagger.v3.oas.models.info.Contact;
import java.util.function.Predicate; import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.tags.Tag;
import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.Resource; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.customizers.OpenApiCustomizer;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner; import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile; import org.springframework.context.annotation.Profile;
import org.springframework.web.bind.annotation.RequestMethod;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.ResponseMessage;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
/** /**
* Swagger Config * Swagger Config
...@@ -42,9 +33,8 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc; ...@@ -42,9 +33,8 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
@Slf4j @Slf4j
@Profile("!prod") @Profile("!prod")
@EnableSwagger2WebMvc
@Configuration @Configuration
@Import(BeanValidatorPluginsConfiguration.class) @RequiredArgsConstructor
public class SwaggerConfig implements CommandLineRunner { public class SwaggerConfig implements CommandLineRunner {
@Value("${spring.application.name}") @Value("${spring.application.name}")
...@@ -56,78 +46,110 @@ public class SwaggerConfig implements CommandLineRunner { ...@@ -56,78 +46,110 @@ public class SwaggerConfig implements CommandLineRunner {
@Value("${server.servlet.context-path}") @Value("${server.servlet.context-path}")
String path; String path;
@Resource final I18n i18n;
OpenApiExtensionResolver openApiExtensionResolver;
@Bean
public OpenAPI openAPI() {
Info info = new Info()
.title("API Doc")
.description(applicationName)
.version("1.0")
.contact(new Contact().name("© YiRing").url("https://yiring.com").email("developer@yiring.com"));
return new OpenAPI().info(info).externalDocs(new ExternalDocumentation());
}
@Bean(name = "api.any") @Bean(name = "api.any")
public Docket any() { public GroupedOpenApi any() {
return api("default", List.of(""), PathSelectors.any()); return api("default", List.of("com.yiring"), List.of("/**"), Collections.emptyList());
} }
@Bean(name = "api.auth") @Bean(name = "api.auth")
public Docket auth() { public GroupedOpenApi auth() {
return api("① Auth", List.of("com.yiring.auth.web"), Predicate.not(PathSelectors.ant(path + "/sys/**"))); return api("① Auth", List.of("com.yiring.auth.web"), List.of("/**"), List.of("/sys/**"));
} }
@Bean(name = "api.common") @Bean(name = "api.common")
public Docket common() { public GroupedOpenApi common() {
return api("② 公共", List.of("com.yiring.common.web", "com.yiring.app.web.common"), PathSelectors.any()); return api(
"② 公共",
List.of("com.yiring.common.web", "com.yiring.app.web.common"),
List.of("/**"),
Collections.emptyList()
);
} }
@Bean(name = "api.manage") @Bean(name = "api.manage")
public Docket manage() { public GroupedOpenApi manage() {
return api("③ 系统管理", List.of("com.yiring.auth.web.sys"), PathSelectors.any()); return api("③ 系统管理", List.of("com.yiring.auth.web.sys"), List.of("/**"), Collections.emptyList());
} }
@Bean(name = "api.example") @Bean(name = "api.example")
public Docket example() { public GroupedOpenApi example() {
return api("④ 示例", List.of("com.yiring.app.web.example"), PathSelectors.any()); return api("④ 示例", List.of("com.yiring.app.web.example"), List.of("/**"), Collections.emptyList());
} }
private Docket api(String group, List<String> basePackages, Predicate<String> paths) { @Bean
// 扫描多个包 public OpenApiCustomizer sortTagCustom() {
Predicate<RequestHandler> predicate = basePackages String order = "x-order";
.stream() return api -> {
.map(RequestHandlerSelectors::basePackage) List<Tag> tags = api
.reduce(Predicate::or) .getTags()
.orElse(RequestHandlerSelectors.none()); .stream()
.sorted(
return new Docket(DocumentationType.SWAGGER_2) Comparator.comparing(tag ->
.groupName(group) Convert.toInt(
.apiInfo(apiInfo()) Optional.ofNullable(tag.getExtensions()).orElseGet(HashMap::new).get(order),
.useDefaultResponseMessages(false) Integer.MAX_VALUE
.globalResponseMessage(RequestMethod.GET, buildGlobalResponseMessage()) )
.globalResponseMessage(RequestMethod.POST, buildGlobalResponseMessage()) )
.globalResponseMessage(RequestMethod.DELETE, buildGlobalResponseMessage()) )
.globalResponseMessage(RequestMethod.PUT, buildGlobalResponseMessage()) .peek(tag -> {
.select() Map<String, Object> extensions = tag.getExtensions();
.apis(RequestHandlerSelectors.withClassAnnotation(Api.class).and(predicate)) if (Objects.nonNull(extensions) && extensions.containsKey(order)) {
.paths(paths) extensions.put(order, Convert.toInt(extensions.get(order)));
.build() }
.extensions(openApiExtensionResolver.buildExtensions(group)); })
.toList();
api.setTags(tags);
};
} }
private ApiInfo apiInfo() { // @Bean
return new ApiInfoBuilder() // public OperationCustomizer addGlobalStatusResponse() {
.title("API Doc") // return (operation, handlerMethod) -> {
.description(applicationName) // ApiResponses responses = operation.getResponses();
.version("1.0") // for (Status status : Status.values()) {
.contact(new Contact("© YiRing", "https://yiring.com", "developer@yiring.com")) // if (Status.OK.equals(status)) {
// continue;
// }
//
// ApiResponse response = new ApiResponse()
// .description(i18n.get(status.getReasonPhrase()))
// .$ref("Response");
// responses.addApiResponse(String.valueOf(status.value()), response);
// }
//
// return operation;
// };
// }
private GroupedOpenApi api(
String group,
List<String> basePackages,
List<String> pathsToMatch,
List<String> pathsToExclude
) {
return GroupedOpenApi
.builder()
.group(group)
.packagesToScan(basePackages.toArray(new String[0]))
.addOpenApiMethodFilter(method -> method.isAnnotationPresent(Operation.class))
.pathsToMatch(pathsToMatch.toArray(new String[0]))
.pathsToExclude(pathsToExclude.toArray(new String[0]))
.build(); .build();
} }
/**
* 构建全局响应消息
*
* @return 所有自定义状态码消息
*/
private List<ResponseMessage> buildGlobalResponseMessage() {
return Arrays
.stream(Status.values())
.map(status -> new ResponseMessageBuilder().code(status.value()).message(status.getReasonPhrase()).build())
.collect(Collectors.toList());
}
@Override @Override
public void run(String... args) { public void run(String... args) {
String link = NetUtil String link = NetUtil
...@@ -135,6 +157,6 @@ public class SwaggerConfig implements CommandLineRunner { ...@@ -135,6 +157,6 @@ public class SwaggerConfig implements CommandLineRunner {
.stream() .stream()
.map(host -> "> http://" + host + ":" + port + path + "/doc.html") .map(host -> "> http://" + host + ":" + port + path + "/doc.html")
.collect(Collectors.joining("\n\t\t")); .collect(Collectors.joining("\n\t\t"));
Console.log("\n\t📖 API Doc (Swagger2): \n\t\t{}\n", link); Console.log("\n\t📖 API Doc (OpenAPI3): \n\t\t{}\n", link);
} }
} }
...@@ -7,6 +7,8 @@ import org.jetbrains.annotations.PropertyKey; ...@@ -7,6 +7,8 @@ import org.jetbrains.annotations.PropertyKey;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceResolvable; import org.springframework.context.MessageSourceResolvable;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
...@@ -18,6 +20,7 @@ import org.springframework.stereotype.Component; ...@@ -18,6 +20,7 @@ import org.springframework.stereotype.Component;
*/ */
@Slf4j @Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
public class I18n { public class I18n {
......
Status.OK=OK Status.OK=\u6210\u529F
Status.NON_AUTHORITATIVE_INFORMATION=\u8BA4\u8BC1\u5931\u8D25 Status.NON_AUTHORITATIVE_INFORMATION=\u8BA4\u8BC1\u5931\u8D25
Status.BAD_REQUEST=\u8BF7\u6C42\u5931\u8D25 Status.BAD_REQUEST=\u8BF7\u6C42\u5931\u8D25
Status.UNAUTHORIZED=\u51ED\u8BC1\u8FC7\u671F Status.UNAUTHORIZED=\u51ED\u8BC1\u8FC7\u671F
......
...@@ -6,7 +6,7 @@ dependencies { ...@@ -6,7 +6,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-validation'
// swagger(knife4j) // swagger(knife4j)
implementation "com.github.xiaoymin:knife4j-spring-boot-starter:${knife4jVersion}" implementation "com.github.xiaoymin:knife4j-openapi3-jakarta-spring-boot-starter:${knife4jOpen3Version}"
// minio // minio
implementation "io.minio:minio:${minioVersion}" implementation "io.minio:minio:${minioVersion}"
......
/* (C) 2022 YiRing, Inc. */ /* (C) 2022 YiRing, Inc. */
package com.yiring.common.param; package com.yiring.common.param;
import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.annotations.ApiModelProperty; import jakarta.validation.constraints.NotEmpty;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import javax.validation.constraints.NotEmpty;
import lombok.*; import lombok.*;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
...@@ -17,7 +16,7 @@ import lombok.experimental.FieldDefaults; ...@@ -17,7 +16,7 @@ import lombok.experimental.FieldDefaults;
* 2022/7/5 15:29 * 2022/7/5 15:29
*/ */
@ApiModel(value = "DownloadParam", description = "文件下载请求参数") @Schema(name = "DownloadParam", description = "文件下载请求参数")
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
...@@ -28,11 +27,11 @@ public class DownloadParam implements Serializable { ...@@ -28,11 +27,11 @@ public class DownloadParam implements Serializable {
@Serial @Serial
private static final long serialVersionUID = -8690942241103456899L; private static final long serialVersionUID = -8690942241103456899L;
@ApiModelProperty(value = "bucket", example = "public", required = true) @Schema(name = "bucket", example = "public")
@NotEmpty(message = "存储桶不能为空") @NotEmpty(message = "存储桶不能为空")
String bucket; String bucket;
@ApiModelProperty(value = "object", example = "cat.jpg", required = true) @Schema(name = "object", example = "cat.jpg")
@NotEmpty(message = "文件对象不能为空") @NotEmpty(message = "文件对象不能为空")
String object; String object;
} }
...@@ -3,7 +3,7 @@ package com.yiring.common.web; ...@@ -3,7 +3,7 @@ package com.yiring.common.web;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import com.github.xiaoymin.knife4j.annotations.ApiSupport; import com.yiring.common.annotation.DownloadResponse;
import com.yiring.common.core.Minio; import com.yiring.common.core.Minio;
import com.yiring.common.core.Result; import com.yiring.common.core.Result;
import com.yiring.common.core.Status; import com.yiring.common.core.Status;
...@@ -13,16 +13,18 @@ import com.yiring.common.util.FileUtils; ...@@ -13,16 +13,18 @@ import com.yiring.common.util.FileUtils;
import com.yiring.common.vo.ImageInfo; import com.yiring.common.vo.ImageInfo;
import io.minio.GetObjectResponse; import io.minio.GetObjectResponse;
import io.minio.StatObjectResponse; import io.minio.StatObjectResponse;
import io.swagger.annotations.Api; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.annotations.ApiImplicitParam; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.annotations.ApiParam; import io.swagger.v3.oas.annotations.extensions.Extension;
import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotBlank;
import java.sql.Timestamp; import java.sql.Timestamp;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders; import org.springdoc.core.annotations.ParameterObject;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
...@@ -36,9 +38,11 @@ import org.springframework.web.multipart.MultipartFile; ...@@ -36,9 +38,11 @@ import org.springframework.web.multipart.MultipartFile;
@Slf4j @Slf4j
@Validated @Validated
@SuppressWarnings({ "deprecation" }) @Tag(
@ApiSupport(order = -98) name = "文件管理",
@Api(tags = "文件管理", description = "file") description = "file",
extensions = { @Extension(properties = { @ExtensionProperty(name = "x-order", value = "-9997") }) }
)
@RequiredArgsConstructor @RequiredArgsConstructor
@RestController @RestController
@RequestMapping("/common/file/") @RequestMapping("/common/file/")
...@@ -50,9 +54,9 @@ public class MinioController { ...@@ -50,9 +54,9 @@ public class MinioController {
/** /**
* minio 上传文件,成功返回文件 url * minio 上传文件,成功返回文件 url
*/ */
@ApiOperation(value = "文件上传", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @Operation(summary = "文件上传")
@PostMapping(value = "upload", headers = HttpHeaders.CONTENT_TYPE + "=" + MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(value = "upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Result<String> upload(@ApiParam(value = "文件", required = true) @RequestPart("file") MultipartFile file) { public Result<String> upload(@Parameter(name = "文件", required = true) @RequestPart("file") MultipartFile file) {
try { try {
// 获取文件名信息 // 获取文件名信息
String filename = file.getOriginalFilename(); String filename = file.getOriginalFilename();
...@@ -76,13 +80,13 @@ public class MinioController { ...@@ -76,13 +80,13 @@ public class MinioController {
} }
} }
@ApiOperation(value = "Base64 图片上传") @Operation(summary = "Base64 图片上传")
@ApiImplicitParam( @Parameter(
name = "base64Image", name = "base64Image",
value = "Base64 图片信息", description = "Base64 图片信息",
example = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAALCAYAAABYpyyrAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAFiUAABYlAUlSJPAAAAATSURBVBhXYzAwNP6PjIeKgPF/ABj+RUX4hZfVAAAAAElFTkSuQmCC", example = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAALCAYAAABYpyyrAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAFiUAABYlAUlSJPAAAAATSURBVBhXYzAwNP6PjIeKgPF/ABj+RUX4hZfVAAAAAElFTkSuQmCC",
required = true, required = true,
paramType = "query" in = ParameterIn.QUERY
) )
@PostMapping(value = "uploadBase64Image") @PostMapping(value = "uploadBase64Image")
public Result<String> uploadBase64Image(@NotBlank(message = "图片 Base64 信息不能为空") String base64Image) { public Result<String> uploadBase64Image(@NotBlank(message = "图片 Base64 信息不能为空") String base64Image) {
...@@ -112,9 +116,10 @@ public class MinioController { ...@@ -112,9 +116,10 @@ public class MinioController {
* @param response HttpServletResponse * @param response HttpServletResponse
* @param param 请求参数 * @param param 请求参数
*/ */
@ApiOperation(value = "文件下载", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) @DownloadResponse
@GetMapping("download") @Operation(summary = "文件下载")
public void download(HttpServletResponse response, @Validated DownloadParam param) { @GetMapping(value = "download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public void download(HttpServletResponse response, @ParameterObject @Validated DownloadParam param) {
try { try {
StatObjectResponse statObject = minio.objectStat(param.getBucket(), param.getObject()); StatObjectResponse statObject = minio.objectStat(param.getBucket(), param.getObject());
GetObjectResponse object = minio.getObject(param.getBucket(), param.getObject()); GetObjectResponse object = minio.getObject(param.getBucket(), param.getObject());
......
...@@ -3,7 +3,7 @@ package com.yiring.common.config; ...@@ -3,7 +3,7 @@ package com.yiring.common.config;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.Duration; import java.time.Duration;
import javax.annotation.Resource; import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -25,10 +25,10 @@ import org.springframework.data.redis.serializer.StringRedisSerializer; ...@@ -25,10 +25,10 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
@EnableCaching @EnableCaching
@Configuration @Configuration
@RequiredArgsConstructor
public class RedisConfig { public class RedisConfig {
@Resource final ObjectMapper objectMapper;
ObjectMapper objectMapper;
@Bean @Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
......
...@@ -3,7 +3,7 @@ package com.yiring.common.core; ...@@ -3,7 +3,7 @@ package com.yiring.common.core;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.annotation.Resource; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
...@@ -18,10 +18,10 @@ import org.springframework.stereotype.Component; ...@@ -18,10 +18,10 @@ import org.springframework.stereotype.Component;
@SuppressWarnings({ "unused" }) @SuppressWarnings({ "unused" })
@Slf4j @Slf4j
@Component @Component
@RequiredArgsConstructor
public final class Redis { public final class Redis {
@Resource final RedisTemplate<String, Object> redisTemplate;
RedisTemplate<String, Object> redisTemplate;
/** /**
* 指定缓存失效时间 * 指定缓存失效时间
...@@ -83,7 +83,7 @@ public final class Redis { ...@@ -83,7 +83,7 @@ public final class Redis {
/** /**
* 普通缓存获取 * 普通缓存获取
* *
* @param key 键 * @param key
* @param type 类型 * @param type 类型
* @return 值 * @return 值
*/ */
...@@ -459,6 +459,7 @@ public final class Redis { ...@@ -459,6 +459,7 @@ public final class Redis {
/** /**
* 获取所有 key * 获取所有 key
*
* @param pattern 查询规则 * @param pattern 查询规则
* @return 所有匹配的 Key 集合 * @return 所有匹配的 Key 集合
*/ */
......
/* (C) 2021 YiRing, Inc. */ /* (C) 2021 YiRing, Inc. */
package com.yiring.common.util; package com.yiring.common.util;
import jakarta.servlet.http.HttpServletRequest;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.*; import java.util.*;
import javax.servlet.http.HttpServletRequest;
import lombok.NonNull; import lombok.NonNull;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
...@@ -21,7 +21,9 @@ import org.springframework.beans.BeanUtils; ...@@ -21,7 +21,9 @@ import org.springframework.beans.BeanUtils;
@UtilityClass @UtilityClass
public class Commons { public class Commons {
/** 代理 IP 请求头 */ /**
* 代理 IP 请求头
*/
private static final String[] HEADERS_TO_TRY = { private static final String[] HEADERS_TO_TRY = {
"X-Forwarded-For", "X-Forwarded-For",
"Proxy-Client-IP", "Proxy-Client-IP",
...@@ -69,8 +71,8 @@ public class Commons { ...@@ -69,8 +71,8 @@ public class Commons {
* @param collection 集合 * @param collection 集合
* @return 是否为空 * @return 是否为空
*/ */
public boolean isNullOrEmpty(Collection<?> collection) { public boolean notEmpty(Collection<?> collection) {
return collection == null || collection.isEmpty(); return collection != null && !collection.isEmpty();
} }
/** /**
...@@ -85,11 +87,12 @@ public class Commons { ...@@ -85,11 +87,12 @@ public class Commons {
/** /**
* 对象 Copy * 对象 Copy
* @param source 源对象 *
* @param type 目标类型 * @param source 源对象
* @param type 目标类型
* @param ignoreProperties 忽略属性 * @param ignoreProperties 忽略属性
* @param <T> 目标类型
* @return 目标对象 * @return 目标对象
* @param <T> 目标类型
*/ */
public <T> T transform(Object source, Class<T> type, String... ignoreProperties) { public <T> T transform(Object source, Class<T> type, String... ignoreProperties) {
try { try {
...@@ -106,11 +109,12 @@ public class Commons { ...@@ -106,11 +109,12 @@ public class Commons {
/** /**
* 将集合通过 BeanUtils 反射转换成指定类型集合 * 将集合通过 BeanUtils 反射转换成指定类型集合
* @param list 原始数据集合 *
* @param type 目标类型 * @param list 原始数据集合
* @param type 目标类型
* @param ignoreProperties 忽略属性 * @param ignoreProperties 忽略属性
* @param <T> 目标类型集合 * @param <T> 目标类型集合
* @param <S> 原类型集合 * @param <S> 原类型集合
* @return 目标集合 * @return 目标集合
*/ */
public <T, S> List<T> transform(@NonNull List<S> list, Class<T> type, String... ignoreProperties) { public <T, S> List<T> transform(@NonNull List<S> list, Class<T> type, String... ignoreProperties) {
...@@ -119,12 +123,13 @@ public class Commons { ...@@ -119,12 +123,13 @@ public class Commons {
/** /**
* 将集合通过 BeanUtils 反射转换成指定类型集合 * 将集合通过 BeanUtils 反射转换成指定类型集合
* @param list 原始数据集合 *
* @param type 目标类型 * @param list 原始数据集合
* @param fn 自定义处理函数 * @param type 目标类型
* @param fn 自定义处理函数
* @param ignoreProperties 忽略属性 * @param ignoreProperties 忽略属性
* @param <T> 目标类型集合 * @param <T> 目标类型集合
* @param <S> 原类型集合 * @param <S> 原类型集合
* @return 目标集合 * @return 目标集合
*/ */
public <T, S> List<T> transform( public <T, S> List<T> transform(
...@@ -162,6 +167,7 @@ public class Commons { ...@@ -162,6 +167,7 @@ public class Commons {
public interface CallbackFunction<S, T> { public interface CallbackFunction<S, T> {
/** /**
* 执行方法 * 执行方法
*
* @param s 源对象 * @param s 源对象
* @param t 目标对象 * @param t 目标对象
*/ */
......
...@@ -5,6 +5,7 @@ import cn.hutool.core.codec.Base64; ...@@ -5,6 +5,7 @@ import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileReader; import cn.hutool.core.io.file.FileReader;
import com.yiring.common.vo.ImageInfo; import com.yiring.common.vo.ImageInfo;
import jakarta.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
...@@ -15,7 +16,6 @@ import java.nio.charset.StandardCharsets; ...@@ -15,7 +16,6 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import org.apache.tomcat.util.http.fileupload.IOUtils; import org.apache.tomcat.util.http.fileupload.IOUtils;
......
plugins { plugins {
id 'java' id 'java'
// https://start.spring.io // https://start.spring.io
id 'org.springframework.boot' version '2.7.7' id 'org.springframework.boot' version '3.0.1'
id 'org.graalvm.buildtools.native' version '0.9.18'
// https://plugins.gradle.org/plugin/io.spring.dependency-management // https://plugins.gradle.org/plugin/io.spring.dependency-management
id 'io.spring.dependency-management' version '1.0.13.RELEASE' id 'io.spring.dependency-management' version '1.1.0'
// https://plugins.gradle.org/plugin/com.diffplug.spotless // https://plugins.gradle.org/plugin/com.diffplug.spotless
id "com.diffplug.spotless" version "6.3.0" id "com.diffplug.spotless" version "6.12.1"
} }
ext { ext {
// Spotless // Spotless
// https://www.npmjs.com/package/prettier // https://www.npmjs.com/package/prettier
prettierVersion = '2.8.1' prettierVersion = '2.8.2'
// https://www.npmjs.com/package/prettier-plugin-java // https://www.npmjs.com/package/prettier-plugin-java
prettierJavaVersion = '2.0.0' prettierJavaVersion = '2.0.0'
// SpringCloud // SpringCloud
// https://start.spring.io/ // https://start.spring.io/
springCloudVersion = '2021.0.4' springCloudVersion = '2022.0.0'
// Dependencies // Dependencies
// https://mvnrepository.com/artifact/com.github.xiaoymin/knife4j-spring-boot-starter // // https://mvnrepository.com/artifact/com.github.xiaoymin/knife4j-openapi3-jakarta-spring-boot-starter
knife4jVersion = '2.0.9' knife4jOpen3Version = '4.0.0'
// https://mvnrepository.com/artifact/io.swagger/swagger-annotations // https://mvnrepository.com/artifact/io.swagger/swagger-annotations
swaggerAnnotationsVersion = '1.6.9' swaggerAnnotationsVersion = '1.6.9'
// https://mvnrepository.com/artifact/cn.dev33/sa-token-spring-boot-starter // https://mvnrepository.com/artifact/cn.dev33/sa-token-spring-boot3-starter
saTokenVersion = '1.33.0' saTokenVersion = '1.34.0'
// https://mvnrepository.com/artifact/cn.hutool/hutool-all // https://mvnrepository.com/artifact/cn.hutool/hutool-all
hutoolVersion = '5.8.11' hutoolVersion = '5.8.11'
// https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2 // https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2
...@@ -35,11 +36,11 @@ ext { ...@@ -35,11 +36,11 @@ ext {
// https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp // https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
okhttpVersion = '4.10.0' okhttpVersion = '4.10.0'
// https://mvnrepository.com/artifact/io.minio/minio // https://mvnrepository.com/artifact/io.minio/minio
minioVersion = '8.4.6' minioVersion = '8.5.1'
// https://mvnrepository.com/artifact/com.vladmihalcea/hibernate-types-55 // https://mvnrepository.com/artifact/io.hypersistence/hypersistence-utils-hibernate-60
hibernateTypesVersion = '2.21.1' hibernateTypesVersion = '3.0.1'
// https://mvnrepository.com/artifact/org.hibernate/hibernate-spatial // https://mvnrepository.com/artifact/org.hibernate/hibernate-spatial
hibernateSpatialVersion = '5.6.14.Final' hibernateSpatialVersion = '6.1.6.Final'
// https://mvnrepository.com/artifact/org.locationtech.jts/jts-core // https://mvnrepository.com/artifact/org.locationtech.jts/jts-core
jtsVersion = '1.19.0' jtsVersion = '1.19.0'
// https://mvnrepository.com/artifact/com.github.liaochong/myexcel // https://mvnrepository.com/artifact/com.github.liaochong/myexcel
...@@ -55,6 +56,7 @@ allprojects { ...@@ -55,6 +56,7 @@ allprojects {
mavenLocal() mavenLocal()
// Nexus // Nexus
// maven { url 'http://10.111.102.83:8081/repository/aliyun-maven/' } // maven { url 'http://10.111.102.83:8081/repository/aliyun-maven/' }
maven { url 'https://mirrors.cloud.tencent.com/nexus/repository/maven-public/' }
maven { url 'https://maven.aliyun.com/repository/public' } maven { url 'https://maven.aliyun.com/repository/public' }
mavenCentral() mavenCentral()
} }
......
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
const scopes = fs
.readdirSync(path.resolve(__dirname, 'src'), { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name.replace(/s$/, ''))
// precomputed scope
const scopeComplete = execSync('git status --porcelain || true')
.toString()
.trim()
.split('\n')
.find((r) => ~r.indexOf('M src'))
?.replace(/(\/)/g, '%%')
?.match(/src%%((\w|-)*)/)?.[1]
?.replace(/s$/, '')
/** @type {import('cz-git').UserConfig} */
module.exports = {
ignores: [(commit) => commit.includes('init')],
extends: ['@commitlint/config-conventional'],
rules: {
'body-leading-blank': [2, 'always'],
'footer-leading-blank': [1, 'always'],
'header-max-length': [2, 'always', 108],
'subject-empty': [2, 'never'],
'type-empty': [2, 'never'],
'subject-case': [0],
'type-enum': [
2,
'always',
[
'feat',
'fix',
'perf',
'style',
'docs',
'test',
'refactor',
'build',
'ci',
'chore',
'revert',
'wip',
'workflow',
'types',
'release',
],
],
},
prompt: {
/** @use `yarn commit :f` */
alias: {
f: 'docs: fix typos',
r: 'docs: update README',
s: 'style: update code format',
b: 'build: bump dependencies',
c: 'chore: update config',
},
customScopesAlign: !scopeComplete ? 'top' : 'bottom',
defaultScope: scopeComplete,
scopes: [...scopes, 'mock'],
allowEmptyIssuePrefixs: false,
allowCustomIssuePrefixs: false,
// English
typesAppend: [
{ value: 'wip', name: 'wip: work in process' },
{ value: 'workflow', name: 'workflow: workflow improvements' },
{ value: 'types', name: 'types: type definition file changes' },
],
// 中英文对照版
// messages: {
// type: '选择你要提交的类型 :',
// scope: '选择一个提交范围 (可选):',
// customScope: '请输入自定义的提交范围 :',
// subject: '填写简短精炼的变更描述 :\n',
// body: '填写更加详细的变更描述 (可选)。使用 "|" 换行 :\n',
// breaking: '列举非兼容性重大的变更 (可选)。使用 "|" 换行 :\n',
// footerPrefixsSelect: '选择关联issue前缀 (可选):',
// customFooterPrefixs: '输入自定义issue前缀 :',
// footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
// confirmCommit: '是否提交或修改commit ?',
// },
// types: [
// { value: 'feat', name: 'feat: 新增功能' },
// { value: 'fix', name: 'fix: 修复缺陷' },
// { value: 'docs', name: 'docs: 文档变更' },
// { value: 'style', name: 'style: 代码格式' },
// { value: 'refactor', name: 'refactor: 代码重构' },
// { value: 'perf', name: 'perf: 性能优化' },
// { value: 'test', name: 'test: 添加疏漏测试或已有测试改动' },
// { value: 'build', name: 'build: 构建流程、外部依赖变更 (如升级 npm 包、修改打包配置等)' },
// { value: 'ci', name: 'ci: 修改 CI 配置、脚本' },
// { value: 'revert', name: 'revert: 回滚 commit' },
// { value: 'chore', name: 'chore: 对构建过程或辅助工具和库的更改 (不影响源文件、测试用例)' },
// { value: 'wip', name: 'wip: 正在开发中' },
// { value: 'workflow', name: 'workflow: 工作流程改进' },
// { value: 'types', name: 'types: 类型定义文件修改' },
// ],
// emptyScopesAlias: 'empty: 不填写',
// customScopesAlias: 'custom: 自定义',
},
}
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<!-- prettier-ignore --> <!-- prettier-ignore -->
- [SpringBoot v2.7.x](https://spring.io/projects/spring-boot) - [SpringBoot v3.0.x](https://spring.io/projects/spring-boot)
- [Lombok](https://projectlombok.org/) - [Lombok](https://projectlombok.org/)
- [Spring Web](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html) - [Spring Web](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html)
- [Spring Data Jpa](https://spring.io/projects/spring-data-jpa) - [Spring Data Jpa](https://spring.io/projects/spring-data-jpa)
......
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
{ {
"name": "basic-api-project", "name": "basic-api-boot",
"version": "0.0.1", "version": "0.0.1",
"scripts": { "scripts": {
"log": "conventional-changelog -p angular -i CHANGELOG.md -s" "log": "conventional-changelog -p angular -i CHANGELOG.md -s"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^17.4.0",
"@commitlint/config-conventional": "^17.4.0",
"commitizen": "^4.2.6",
"conventional-changelog-cli": "^2.2.2",
"cz-conventional-changelog": "^3.3.0", "cz-conventional-changelog": "^3.3.0",
"conventional-changelog-cli": "^2.2.2" "cz-customizable": "^7.0.0",
"cz-git": "^1.4.1"
}, },
"config": { "config": {
"commitizen": { "commitizen": {
"path": "./node_modules/cz-conventional-changelog" "path": "node_modules/cz-git"
} }
} }
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论