1、修改shardingjdbc配置,

2、新增api版本控制,
3、新增用户角色关联
This commit is contained in:
zhuyijun 2022-09-24 00:11:25 +08:00
parent ee32e15a6e
commit 53e153e787
43 changed files with 749 additions and 187 deletions

View File

@ -129,11 +129,6 @@
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-micro-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<!-- JWT 依赖开始 -->
<!-- <dependency>-->
<!-- <groupId>io.jsonwebtoken</groupId>-->

View File

@ -90,5 +90,9 @@
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -51,14 +51,14 @@ public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
.csrf()
.disable()
//限制资源服务器作用范围为 "/user/**", "/demo/**"
.requestMatchers().antMatchers("/webjars/**", "/swagger-ui.html/**", "/swagger-resources/**", "/v2/api-docs/**", "/user/**", "/demo/**",
.requestMatchers().antMatchers("/v*/**,/webjars/**", "/swagger-ui.html/**", "/swagger-resources/**", "/v2/api-docs/**", "/v*/user/**", "/demo/**",
String.join(",", whiteListProperties.getAllowPaths()))
.and()
.formLogin().and()
.authorizeRequests()
.antMatchers("/webjars/**", "/swagger-ui.html/**", "/swagger-resources/**", "/v2/api-docs/**", String.join(",", whiteListProperties.getAllowPaths()))
.permitAll()
.antMatchers("/login", "/oauth/**", "/user/login", "/user/refresh/token").permitAll()
.antMatchers("/login", "/oauth/**", "/v*/user/login", "/v*/auth/refresh/token").permitAll()
//以下请求必须认证通过
.anyRequest()
.authenticated().and()

View File

@ -6,6 +6,7 @@ import cn.zyjblogs.server.user.service.AuthService;
import cn.zyjblogs.server.user.vo.OAuth2AccessTokenVo;
import cn.zyjblogs.starter.common.entity.response.ResponseObject;
import cn.zyjblogs.starter.common.entity.response.ResponseResult;
import cn.zyjblogs.starter.web.apiversion.ApiVersion;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
@ -21,24 +22,28 @@ import org.springframework.web.bind.annotation.RestController;
@Api(tags = {"token管理"})
@RestController
@RequiredArgsConstructor
@RequestMapping("/auth")
@ApiVersion(1)
@RequestMapping("/{v}/auth")
public class AuthController {
private final AuthService authService;
@ApiOperation(value = "刷新token", notes = "刷新token")
@PostMapping("/refresh/token")
@ApiVersion(1)
public ResponseObject<OAuth2AccessTokenVo> refreshToken(@RequestBody @Validated OAuth2AccessTokenDto oAuth2AccessTokenDto) {
return ResponseResult.success(authService.refreshToken(oAuth2AccessTokenDto));
}
@ApiOperation(value = "检测token", notes = "刷新token")
@PostMapping("/check/token")
@ApiVersion(1)
public ResponseObject<OAuth2AccessTokenVo> checkToken(@RequestBody @Validated OAuth2AccessTokenDto oAuth2AccessTokenDto) {
return ResponseResult.success(authService.checkToken(oAuth2AccessTokenDto));
}
@ApiOperation(value = "获取授权码", notes = "刷新token")
@PostMapping("/authorize")
@ApiVersion(1)
public void getAuthorizationCode(@RequestBody @Validated AuthorizationCodeDto authorizationCodeDto) {
authService.getAuthorizationCode(authorizationCodeDto);
}

View File

@ -5,9 +5,11 @@ import cn.zyjblogs.server.user.service.LoginService;
import cn.zyjblogs.server.user.vo.OAuth2AccessTokenVo;
import cn.zyjblogs.starter.common.entity.response.ResponseObject;
import cn.zyjblogs.starter.common.entity.response.ResponseResult;
import cn.zyjblogs.starter.web.apiversion.ApiVersion;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@ -20,17 +22,21 @@ import org.springframework.web.bind.annotation.RestController;
@Api(tags = {"登录认证管理"})
@RestController
@RequiredArgsConstructor
@RequestMapping("/user")
@Log4j2
@ApiVersion(1)
@RequestMapping("/{v}/user")
public class LoginController {
private final LoginService loginService;
@ApiOperation(value = "用户登录", notes = "用户登录")
@ApiVersion(1)
@PostMapping("/login")
public ResponseObject<OAuth2AccessTokenVo> login(@RequestBody @Validated UserLoginDto userLoginDto) {
return ResponseResult.success(loginService.login(userLoginDto));
}
@ApiOperation(value = "用户注销", notes = "用户注销")
@ApiVersion(1)
@PostMapping("/logout")
public void logout() {
loginService.logout();

View File

@ -1,6 +1,5 @@
package cn.zyjblogs.server.user.po;
import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
@ -14,7 +13,6 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.sql.Timestamp;
import java.time.LocalDateTime;
/**
@ -57,12 +55,6 @@ public class UserPo implements Serializable {
@TableField("status")
private Integer status;
@TableField("follow_num")
private Integer followNum;
@TableField("fans_num")
private Integer fansNum;
@TableField("deleted")
private Integer deleted;

View File

@ -29,6 +29,74 @@ spring:
namespace: ${spring.cloud.nacos.config.namespace}
group: public
shardingsphere:
datasource:
names: write-ds,read-ds-0
write-ds:
jdbc-url: jdbc:mysql://127.0.0.1:3306/zyjblogs_rbac?allowPublicKeyRetrieval=true&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai&useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
connectionTimeoutMilliseconds: 3000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
maintenanceIntervalMilliseconds: 30000\
hikari:
minimum-idle: 10
maximum-pool-size: 100
auto-commit: true
idle-timeout: 1800000
pool-name: DatebookHikariCP
max-lifetime: 1800000
connection-timeout: 60000
connection-test-query: SELECT 1
read-ds-0:
jdbc-url: jdbc:mysql://127.0.0.1:3306/zyjblogs_rbac?allowPublicKeyRetrieval=true&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai&useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
username: read
password: 123456
connectionTimeoutMilliseconds: 3000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
maintenanceIntervalMilliseconds: 30000
hikari:
minimum-idle: 10
maximum-pool-size: 100
auto-commit: true
idle-timeout: 1800000
pool-name: DatebookHikariCP
max-lifetime: 1800000
connection-timeout: 60000
connection-test-query: SELECT 1
sharding:
master-slave-rules:
master0:
master-data-source-name: write-ds
slave-data-source-names: read-ds-0
# rules:
# readwrite-splitting:
# data-sources:
# readwrite_ds:
# static-strategy:
# write-data-source-name: write-ds
# read-data-source-names:
# - read-ds-0
# # 负载均衡算法名称
# load-balancer-name: roundRobin
# load-balancers:
# # 一共两种一种是 RANDOM随机一种是 ROUND_ROBIN轮询
# roundRobin:
# type: ROUND_ROBIN
props:
sql:
show: true
logging:
config: classpath:logback-spring.xml

View File

@ -4,9 +4,11 @@ import cn.zyjblogs.server.role.dto.RolePageDto;
import cn.zyjblogs.server.role.po.RolePo;
import cn.zyjblogs.server.role.service.RoleService;
import cn.zyjblogs.server.role.vo.RoleVo;
import cn.zyjblogs.starter.common.entity.context.BaseContext;
import cn.zyjblogs.starter.common.entity.response.ResponseObject;
import cn.zyjblogs.starter.common.entity.response.ResponseResult;
import cn.zyjblogs.starter.common.utils.bean.BeanUtils;
import cn.zyjblogs.starter.web.apiversion.ApiVersion;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@ -25,8 +27,9 @@ import java.util.List;
*/
@Api(tags = {"角色管理"})
@RestController
@RequestMapping("/role")
@RequestMapping("/{v}/role")
@ResponseBody
@ApiVersion(1)
@RequiredArgsConstructor
public class RoleController {
private final RoleService roleService;
@ -39,7 +42,8 @@ public class RoleController {
* @author zhuyijun
* @date 2022/9/19 上午12:06
*/
@ApiOperation(value = "1", notes = "通过id查询用户")
@ApiOperation(value = "通过id查询用户", notes = "通过id查询用户")
@ApiVersion(1)
@GetMapping("/id")
public ResponseObject<RoleVo> findById(@RequestParam String id) {
RolePo byId = roleService.getById(id);
@ -53,7 +57,8 @@ public class RoleController {
* @author zhuyijun
* @date 2022/9/19 上午1:14
*/
@ApiOperation(value = "page=1&limit=10", notes = "分页查询")
@ApiOperation(value = "分页查询", notes = "分页查询")
@ApiVersion(1)
@GetMapping("/page")
public ResponseObject<List<RoleVo>> findPage(RolePageDto rolePageDto) {
IPage<RoleVo> page = roleService.findPage(rolePageDto);
@ -66,10 +71,11 @@ public class RoleController {
* @author zhuyijun
* @date 2022/9/22 下午11:33
*/
@ApiOperation(value = "", notes = "查询所有角色")
@ApiOperation(value = "查询当前租户下所有角色", notes = "查询所有角色")
@ApiVersion(1)
@GetMapping("/list")
public ResponseObject<List<RoleVo>> findList() {
return ResponseResult.success(roleService.findList());
return ResponseResult.success(roleService.findList(BaseContext.getTenantId()));
}
/**
@ -80,7 +86,8 @@ public class RoleController {
* @author zhuyijun
* @date 2022/9/19 上午1:14
*/
@ApiOperation(value = "1", notes = "通过角色id查询")
@ApiOperation(value = "通过角色id查询", notes = "通过角色id查询")
@ApiVersion(1)
@PostMapping("/findByIds")
public ResponseObject<List<RoleVo>> findByIds(List<String> ids) {
return ResponseResult.success(roleService.findByIds(ids));

View File

@ -1,66 +1,35 @@
package cn.zyjblogs.server.role.dto;
import cn.zyjblogs.starter.common.entity.constant.CommonConstant;
import cn.zyjblogs.starter.common.entity.dto.PageDto;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.AllArgsConstructor;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* @author zhuyijun
*/
@ApiModel(description = "角色分页查询")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RolePageDto extends PageDto implements Serializable {
private String id;
@ApiModelProperty(value = "名称")
private String name;
@ApiModelProperty(value = "状态")
private Integer status;
@ApiModelProperty(value = "类型")
private Integer roleType;
@ApiModelProperty(value = "是否删除")
private Integer deleted;
private String description;
private String createUserId;
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JSONField(format = CommonConstant.DATETIME_PATTERN)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = CommonConstant.DATETIME_PATTERN)
private LocalDateTime createTime;
private String editUserId;
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JSONField(format = CommonConstant.DATETIME_PATTERN)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = CommonConstant.DATETIME_PATTERN)
private LocalDateTime editTime;
private String tenantId;
@Builder
public RolePageDto(Integer page, Integer limit, String id, String name, Integer status, Integer deleted, String description, String createUserId, LocalDateTime createTime, String editUserId, LocalDateTime editTime, String tenantId) {
public RolePageDto(Integer page, Integer limit, String name, Integer status, Integer deleted) {
super(page, limit);
this.id = id;
this.name = name;
this.status = status;
this.deleted = deleted;
this.description = description;
this.createUserId = createUserId;
this.createTime = createTime;
this.editUserId = editUserId;
this.editTime = editTime;
this.tenantId = tenantId;
}
}

View File

@ -31,6 +31,8 @@ public class RolePo implements Serializable {
private String name;
@TableField("status")
private Integer status;
@TableField("role_type")
private Integer roleType;
@TableField("deleted")
private Integer deleted;

View File

@ -35,6 +35,6 @@ public interface RoleService extends IService<RolePo> {
*
* @return
*/
List<RoleVo> findList();
List<RoleVo> findList(String tenantId);
}

View File

@ -68,9 +68,9 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, RolePo> implements
* @date 2022/9/22 下午11:32
*/
@Override
public List<RoleVo> findList() {
public List<RoleVo> findList(String tenantId) {
LambdaQueryWrapper<RolePo> wrapper = Wrappers.lambdaQuery();
wrapper.eq(RolePo::getTenantId, BaseContext.getTenantId());
wrapper.eq(tenantId != null, RolePo::getTenantId, tenantId);
List<RolePo> rolePos = roleMapper.selectList(wrapper);
if (CollectionUtils.isEmpty(rolePos)) {
return new ArrayList<>();

View File

@ -1,12 +1,10 @@
package cn.zyjblogs.server.role.vo;
import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@ -18,40 +16,42 @@ import java.time.LocalDateTime;
/**
* @author zhuyijun
*/
@ApiModel(description = "角色实体类")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName("role")
public class RoleVo implements Serializable {
@TableId("id")
@ApiModelProperty(value = "主键id")
private String id;
@TableField("name")
@ApiModelProperty(value = "名称")
private String name;
@TableField("status")
@ApiModelProperty(value = "状态")
private Integer status;
@TableField("deleted")
@ApiModelProperty(value = "类型")
private Integer roleType;
@ApiModelProperty(value = "是否删除")
private Integer deleted;
@TableField("description")
@ApiModelProperty(value = "描述")
private String description;
@TableField("create_user_id")
@ApiModelProperty(value = "创建人")
private String createUserId;
@ApiModelProperty(value = "创建时间")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField("create_time")
private LocalDateTime createTime;
@TableField("edit_user_id")
@ApiModelProperty(value = "编辑人")
private String editUserId;
@ApiModelProperty(value = "编辑时间")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField("edit_time")
private LocalDateTime editTime;
@TableField("tenant_id")
@ApiModelProperty(value = "租户id")
private String tenantId;
}

View File

@ -5,8 +5,10 @@ import cn.zyjblogs.server.user.dto.UserPageDto;
import cn.zyjblogs.server.user.po.UserPo;
import cn.zyjblogs.server.user.service.UserService;
import cn.zyjblogs.server.user.vo.UserVo;
import cn.zyjblogs.starter.common.entity.response.HttpCode;
import cn.zyjblogs.starter.common.entity.response.ResponseObject;
import cn.zyjblogs.starter.common.entity.response.ResponseResult;
import cn.zyjblogs.starter.web.apiversion.ApiVersion;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@ -29,10 +31,11 @@ import java.util.List;
*/
@Api(tags = {"用户管理"})
@RestController
@RequestMapping("/user")
@RequestMapping("/{v}/user")
@RequiredArgsConstructor
@ResponseBody
@Log4j2
@ApiVersion(1)
public class UserController {
private final UserService userService;
@ -47,6 +50,7 @@ public class UserController {
*/
@ApiOperation(value = "", notes = "通过id查询用户")
@GetMapping("/id")
@ApiVersion(1)
public ResponseObject<UserPo> findById(@RequestParam String id) {
return ResponseResult.success(userService.getById(id));
}
@ -60,6 +64,7 @@ public class UserController {
*/
@ApiOperation(value = "", notes = "分页查询")
@GetMapping("/page")
@ApiVersion(1)
public ResponseObject<List<UserVo>> findPage(UserPageDto userPageDto) {
IPage<UserVo> page = userService.findPage(userPageDto);
return ResponseResult.success(page.getTotal(), page.getRecords());
@ -75,13 +80,18 @@ public class UserController {
*/
@ApiOperation(value = "", notes = "通过id集合查询用户")
@PostMapping("/findByIds")
@ApiVersion(1)
public ResponseObject<List<UserVo>> findByIds(List<String> ids) {
return ResponseResult.success(userService.findByIds(ids));
}
@ApiOperation(value = "", notes = "用户保存")
@PutMapping("/saveUser")
@ApiVersion(1)
public ResponseObject<String> saveUser(@RequestBody @Validated UserDto userDto) {
if (userService.checkUser(userDto)) {
return ResponseResult.error(HttpCode.BAD_REQUEST, "该账号已存在");
}
return userService.saveUser(userDto);
}
}

View File

@ -8,13 +8,16 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.Set;
/**
* @author zhuyijun
@ -25,48 +28,52 @@ import java.time.LocalDateTime;
@NoArgsConstructor
@Builder
public class UserDto {
@ApiModelProperty(value = "主键id")
private String id;
@ApiModelProperty(value = "账号(用户名)")
@NotBlank(message = "账号不能为空")
private String username;
@ApiModelProperty(value = "姓名")
private String name;
@ApiModelProperty(value = "年龄")
private Integer age;
private String avatar;
@ApiModelProperty(value = "手机号")
private String phone;
@ApiModelProperty(value = "邮箱")
private String email;
private String inviteUserId;
@ApiModelProperty(value = "状态")
private Integer status;
private Integer followNum;
private Integer fansNum;
@ApiModelProperty(value = "logo")
private String avatar;
@ApiModelProperty(value = "邀请人")
private String inviteUserId;
@ApiModelProperty(value = "是否删除 0未删除")
private Integer deleted;
@ApiModelProperty(value = "备注")
private String description;
@ApiModelProperty(value = "创建人")
private String createUserId;
@ApiModelProperty(value = "创建时间")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JSONField(format = CommonConstant.DATETIME_PATTERN)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = CommonConstant.DATETIME_PATTERN)
private LocalDateTime createTime;
@ApiModelProperty(value = "编辑人")
private String editUserId;
@ApiModelProperty(value = "编辑时间")
@JSONField(format = CommonConstant.DATETIME_PATTERN)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = CommonConstant.DATETIME_PATTERN)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime editTime;
@ApiModelProperty(value = "角色id集合")
@NotNull(message = "角色不能为空")
private Set<String> roleIds;
@ApiModelProperty(value = "租户id")
private String tenantId;
}

View File

@ -1,26 +1,32 @@
package cn.zyjblogs.server.user.dto;
import cn.zyjblogs.starter.common.entity.dto.PageDto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 分页查询
*
* @author zhuyijun
*/
@ApiModel(description = "用户查询dto")
@Data
@NoArgsConstructor
public class UserPageDto extends PageDto {
@ApiModelProperty(value = "账号(用户名)")
private String username;
@ApiModelProperty(value = "姓名")
private String name;
@ApiModelProperty(value = "年龄")
private Integer age;
@ApiModelProperty(value = "手机号")
private String phone;
@ApiModelProperty(value = "邮箱")
private String email;
@ApiModelProperty(value = "状态")
private Integer status;
@Builder

View File

@ -0,0 +1,31 @@
package cn.zyjblogs.server.user.dto;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author zhuyijun
*/
@ApiModel(description = "用户角色关联表")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName("user_role")
public class UserRoleDto {
@ApiModelProperty(value = "主键id")
private String id;
@ApiModelProperty(value = "用户id")
private String userId;
@ApiModelProperty(value = "角色id")
private String roleId;
@ApiModelProperty(value = "租户id")
private String tenantId;
}

View File

@ -0,0 +1,12 @@
package cn.zyjblogs.server.user.mapper;
import cn.zyjblogs.server.user.po.UserRolePo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* @author zhuyijun
*/
@Mapper
public interface UserRoleMapper extends BaseMapper<UserRolePo> {
}

View File

@ -57,12 +57,6 @@ public class UserPo implements Serializable {
@TableField("status")
private Integer status;
@TableField("follow_num")
private Integer followNum;
@TableField("fans_num")
private Integer fansNum;
@TableField("deleted")
private Integer deleted;

View File

@ -0,0 +1,28 @@
package cn.zyjblogs.server.user.po;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author zhuyijun
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName("user_role")
public class UserRolePo {
@TableId("id")
private String id;
@TableField("user_id")
private String userId;
@TableField("role_id")
private String roleId;
@TableField("tenant_id")
private String tenantId;
}

View File

@ -0,0 +1,54 @@
package cn.zyjblogs.server.user.service;
import cn.zyjblogs.server.user.po.UserRolePo;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.Collection;
import java.util.List;
/**
* @author zhuyijun
*/
public interface UserRoleService extends IService<UserRolePo> {
/**
* 保存或用户角色关联
*
* @param userId 用户id
* @param roleIds 角色id集合
* @param needClear 是否清除该用户旧关联
* @return boolean
* @author zhuyijun
* @date 2022/9/23 下午11:10
*/
boolean saveOrUpdate(String userId, Collection<String> roleIds, boolean needClear);
/**
* 通过用户id删除用户角色关联
*
* @param userId 用户id
* @return int
* @author zhuyijun
* @date 2022/9/23 下午10:56
*/
int deleteByUserId(String userId);
/**
* 通过用户id集合删除用户角色关联
*
* @param userIds 用户id集合
* @return int
* @author zhuyijun
* @date 2022/9/23 下午10:56
*/
int deleteByUserIds(Collection<String> userIds);
/**
* 通过用户id查询角色id
*
* @param userId 用户id
* @return int
* @author zhuyijun
* @date 2022/9/23 下午11:00
*/
List<String> findRoleListByUserId(String userId);
}

View File

@ -41,4 +41,14 @@ public interface UserService extends IService<UserPo> {
*/
ResponseObject<String> saveUser(@RequestBody UserDto userDto);
/**
* 检测账号是否存在
*
* @param userDto
* @return boolean
* @author zhuyijun
* @date 2022/9/23 下午5:41
*/
boolean checkUser(UserDto userDto);
}

View File

@ -0,0 +1,101 @@
package cn.zyjblogs.server.user.service.impl;
import cn.zyjblogs.server.user.mapper.UserRoleMapper;
import cn.zyjblogs.server.user.po.UserRolePo;
import cn.zyjblogs.server.user.service.UserRoleService;
import cn.zyjblogs.starter.common.entity.context.BaseContext;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author zhuyijun
*/
@Service
@RequiredArgsConstructor
public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRolePo> implements UserRoleService {
private final UserRoleMapper userRoleMapper;
/**
* 保存或用户角色关联
*
* @param userId 用户id
* @param roleIds 角色id集合
* @param needClear 是否清除该用户旧关联
* @author zhuyijun
* @date 2022/9/23 下午11:10
*/
@Transactional(rollbackFor = Exception.class)
@Override
public boolean saveOrUpdate(String userId, Collection<String> roleIds, boolean needClear) {
if (needClear) {
deleteByUserId(userId);
}
List<UserRolePo> userRolePos = new ArrayList<>(roleIds.size());
String tenantId = BaseContext.getTenantId();
roleIds.forEach(roleId -> userRolePos.add(UserRolePo.builder()
.roleId(roleId)
.userId(userId)
.tenantId(tenantId)
.build()));
return this.saveBatch(userRolePos);
}
/**
* 通过用户id集合删除用户角色关联表
*
* @param userId 用户id
* @author zhuyijun
* @date 2022/9/23 下午11:00
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int deleteByUserId(String userId) {
LambdaQueryWrapper<UserRolePo> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(UserRolePo::getUserId, userId)
.eq(UserRolePo::getTenantId, BaseContext.getTenantId());
return userRoleMapper.delete(queryWrapper);
}
/**
* 通过用户id集合删除用户角色关联表
*
* @param userIds 用户id集合
* @author zhuyijun
* @date 2022/9/23 下午11:00
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int deleteByUserIds(Collection<String> userIds) {
LambdaQueryWrapper<UserRolePo> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.in(UserRolePo::getUserId, userIds)
.eq(UserRolePo::getTenantId, BaseContext.getTenantId());
return userRoleMapper.delete(queryWrapper);
}
/**
* 通过用户id查询角色id集合
*
* @param userId 用户id
* @author zhuyijun
* @date 2022/9/23 下午11:00
*/
@Override
public List<String> findRoleListByUserId(String userId) {
LambdaQueryWrapper<UserRolePo> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.select(UserRolePo::getRoleId)
.eq(UserRolePo::getUserId, userId)
.eq(UserRolePo::getTenantId, BaseContext.getTenantId());
List<UserRolePo> userRolePos = userRoleMapper.selectList(queryWrapper);
return userRolePos.stream().map(UserRolePo::getRoleId).collect(Collectors.toList());
}
}

View File

@ -5,6 +5,7 @@ import cn.zyjblogs.server.user.dto.UserDto;
import cn.zyjblogs.server.user.dto.UserPageDto;
import cn.zyjblogs.server.user.mapper.UserMapper;
import cn.zyjblogs.server.user.po.UserPo;
import cn.zyjblogs.server.user.service.UserRoleService;
import cn.zyjblogs.server.user.service.UserService;
import cn.zyjblogs.server.user.vo.UserVo;
import cn.zyjblogs.starter.common.entity.context.BaseContext;
@ -17,6 +18,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.util.Collections;
@ -29,6 +31,7 @@ import java.util.List;
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, UserPo> implements UserService {
private final UserMapper userMapper;
private final UserRoleService userRoleService;
/**
* 分页查询
@ -68,16 +71,15 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserPo> implements
* @date 2022/9/22 下午6:04
*/
@Override
@Transactional(rollbackFor = Exception.class)
public ResponseObject<String> saveUser(UserDto userDto) {
if (checkUser(userDto)) {
return ResponseResult.error(HttpCode.BAD_REQUEST, "该账号已存在");
}
UserPo userPo = BeanUtils.map(userDto, UserPo.class);
int count = userMapper.insert(userPo);
if (count > 0) {
userRoleService.saveOrUpdate(userPo.getId(), userDto.getRoleIds(), false);
return ResponseResult.success("用户创建成功");
}
return ResponseResult.error(HttpCode.BAD_REQUEST, "用户创建是失败");
return ResponseResult.error(HttpCode.BAD_REQUEST, "用户创建成功");
}
/**
@ -86,6 +88,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserPo> implements
* @param userDto
* @return
*/
@Override
public boolean checkUser(UserDto userDto) {
return userMapper.checkUserCount(userDto) > 0;
}

View File

@ -0,0 +1,29 @@
package cn.zyjblogs.server.user.vo;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author zhuyijun
*/
@ApiModel(description = "用户角色关联表")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName("user_role")
public class UserRoleVo {
@ApiModelProperty(value = "主键id")
private String id;
@ApiModelProperty(value = "用户id")
private String userId;
@ApiModelProperty(value = "角色id")
private String roleId;
@ApiModelProperty(value = "租户id")
private String tenantId;
}

View File

@ -4,11 +4,14 @@ import cn.zyjblogs.starter.common.entity.constant.CommonConstant;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
import java.time.LocalDateTime;
@ -19,46 +22,50 @@ import java.time.LocalDateTime;
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ApiModel(description = "用户vo类")
public class UserVo implements Serializable {
@ApiModelProperty(value = "主键id")
private String id;
@ApiModelProperty(value = "账号(用户名)")
@NotBlank(message = "账号不能为空")
private String username;
@ApiModelProperty(value = "姓名")
private String name;
@ApiModelProperty(value = "年龄")
private Integer age;
private String avatar;
@ApiModelProperty(value = "手机号")
private String phone;
@ApiModelProperty(value = "邮箱")
private String email;
private String inviteUserId;
@ApiModelProperty(value = "状态")
private Integer status;
private Integer followNum;
private Integer fansNum;
@ApiModelProperty(value = "logo")
private String avatar;
@ApiModelProperty(value = "邀请人")
private String inviteUserId;
@ApiModelProperty(value = "是否删除 0未删除")
private Integer deleted;
@ApiModelProperty(value = "备注")
private String description;
@ApiModelProperty(value = "创建人")
private String createUserId;
@ApiModelProperty(value = "创建时间")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JSONField(format = CommonConstant.DATETIME_PATTERN)
private LocalDateTime createTime;
@ApiModelProperty(value = "编辑人")
private String editUserId;
@JsonSerialize(using = LocalDateTimeSerializer.class)
@ApiModelProperty(value = "编辑时间")
@JSONField(format = CommonConstant.DATETIME_PATTERN)
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime editTime;
@ApiModelProperty(value = "租户id")
private String tenantId;
}

View File

@ -28,6 +28,73 @@ spring:
password: ${spring.cloud.nacos.config.password}
namespace: ${spring.cloud.nacos.config.namespace}
group: public
shardingsphere:
datasource:
names: write-ds,read-ds-0
write-ds:
jdbc-url: jdbc:mysql://127.0.0.1:3306/zyjblogs_rbac?allowPublicKeyRetrieval=true&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai&useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
connectionTimeoutMilliseconds: 3000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
maintenanceIntervalMilliseconds: 30000\
hikari:
minimum-idle: 10
maximum-pool-size: 100
auto-commit: true
idle-timeout: 1800000
pool-name: DatebookHikariCP
max-lifetime: 1800000
connection-timeout: 60000
connection-test-query: SELECT 1
read-ds-0:
jdbc-url: jdbc:mysql://127.0.0.1:3306/zyjblogs_rbac?allowPublicKeyRetrieval=true&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai&useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
username: read
password: 123456
connectionTimeoutMilliseconds: 3000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
maintenanceIntervalMilliseconds: 30000
hikari:
minimum-idle: 10
maximum-pool-size: 100
auto-commit: true
idle-timeout: 1800000
pool-name: DatebookHikariCP
max-lifetime: 1800000
connection-timeout: 60000
connection-test-query: SELECT 1
sharding:
master-slave-rules:
master0:
master-data-source-name: write-ds
slave-data-source-names: read-ds-0
# rules:
# readwrite-splitting:
# data-sources:
# readwrite_ds:
# static-strategy:
# write-data-source-name: write-ds
# read-data-source-names:
# - read-ds-0
# # 负载均衡算法名称
# load-balancer-name: roundRobin
# load-balancers:
# # 一共两种一种是 RANDOM随机一种是 ROUND_ROBIN轮询
# roundRobin:
# type: ROUND_ROBIN
props:
sql:
show: true
logging:
config: classpath:logback-spring.xml
@ -36,4 +103,7 @@ security:
oauth2:
client:
client-id: ${spring.application.name}
client-secret: secret
client-secret: secret
resource:
id: resourceId

View File

@ -1,8 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.zyjblogs.server.role.mapper.RoleMapper">
<sql id="base_role">
id,name,status,role_type,deleted,description,
create_user_id,create_time,edit_user_id,
edit_time,tenant_id
</sql>
<select id="findPage" resultType="cn.zyjblogs.server.role.vo.RoleVo">
select * from role
select
<include refid="base_role"/>
from role
where deleted = 0
<if test="rolePageDto.username != null and rolePageDto.username!='' ">
and name concat('%',#{rolePageDto.name},'%')
@ -13,7 +20,7 @@
<if test="tenantId != null and tenantId != '' ">
and tenant_id = #{tenantId}
</if>
order by create_time desc
order by create_time desc,id desc
</select>

View File

@ -1,32 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.zyjblogs.server.user.mapper.UserMapper">
<resultMap id="userMap" type="cn.zyjblogs.server.user.vo.UserVo">
<id column="id" property="id" javaType="java.lang.String"/>
<result column="username" property="username" javaType="java.lang.String"/>
<result column="username" property="username" javaType="java.lang.String"/>
<result column="name" property="name" javaType="java.lang.String"/>
<result column="username" property="username" javaType="java.lang.String"/>
<result column="age" property="age" javaType="java.lang.Integer"/>
<result column="avatar" property="avatar" javaType="java.lang.String"/>
<result column="phone" property="phone" javaType="java.lang.String"/>
<result column="email" property="email" javaType="java.lang.String"/>
<result column="invite_user_id" property="inviteUserId" javaType="java.lang.String"/>
<result column="status" property="status" javaType="java.lang.Integer"/>
<result column="follow_num" property="followNum" javaType="java.lang.Integer"/>
<result column="fans_num" property="fansNum" javaType="java.lang.Integer"/>
<result column="deleted" property="deleted" javaType="java.lang.Integer"/>
<result column="description" property="description" javaType="java.lang.String"/>
<result column="create_user_id" property="createUserId" javaType="java.lang.String"/>
<result column="create_time" property="createTime" javaType="java.sql.Timestamp"/>
<result column="edit_user_id" property="editUserId" javaType="java.lang.String"/>
<result column="edit_time" property="editTime" javaType="java.sql.Timestamp"/>
<result column="tenant_id" property="tenantId" javaType="java.lang.String"/>
</resultMap>
<sql id="base_user_sql">
id,username,name,age,avatar,phone,
email,invite_user_id,status,follow_num,
fans_num,deleted,description,create_user_id,
email,invite_user_id,status,deleted,description,create_user_id,
create_time,edit_user_id,edit_time,tenant_id
</sql>
<select id="findUserByname" resultType="cn.zyjblogs.server.user.po.UserPo">
@ -47,9 +24,6 @@
<if test="userPageDto.username != null and userPageDto.username!='' ">
and username like concat('%',#{userPageDto.username},'%')
</if>
<if test="userPageDto.name != null and userPageDto.name!='' ">
and name like concat('%',#{name},'%')
</if>
<if test="userPageDto.phone != null and userPageDto.phone!='' ">
and phone like concat('%',#{phone},'%')
</if>
@ -62,16 +36,13 @@
<if test="tenantId != null and tenantId != '' ">
and tenant_id = #{tenantId}
</if>
order by create_time desc
order by create_time desc,id desc
</select>
<select id="checkUserCount" resultType="java.lang.Integer">
select count(1)
from users
<where>
and deleted = 0 and username = #{userDto.username}
<if test="userDto.name != null and userDto.name!='' ">
or name = #{userDto.name}
</if>
and deleted = 0 and ( username = #{userDto.username}
<if test="userDto.phone != null and userDto.phone!='' ">
or phone = #{userDto.phone}
</if>
@ -81,6 +52,7 @@
<if test="userDto.tenantId != null and userDto.tenantId != '' ">
and tenant_id = #{userDto.tenantId}
</if>
)
</where>
</select>
</mapper>

View File

@ -1,8 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.zyjblogs.server.role.mapper.RoleMapper">
<sql id="base_role">
id,name,status,role_type,deleted,description,
create_user_id,create_time,edit_user_id,
edit_time,tenant_id
</sql>
<select id="findPage" resultType="cn.zyjblogs.server.role.vo.RoleVo">
select * from role
select
<include refid="base_role"/>
from role
where deleted = 0
<if test="rolePageDto.username != null and rolePageDto.username!='' ">
and name concat('%',#{rolePageDto.name},'%')
@ -13,7 +20,7 @@
<if test="tenantId != null and tenantId != '' ">
and tenant_id = #{tenantId}
</if>
order by create_time desc
order by create_time desc,id desc
</select>

View File

@ -1,6 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.zyjblogs.server.user.mapper.UserMapper">
<sql id="base_user_sql">
id,username,name,age,avatar,phone,
email,invite_user_id,status,deleted,description,create_user_id,
create_time,edit_user_id,edit_time,tenant_id
</sql>
<select id="findUserByname" resultType="cn.zyjblogs.server.user.po.UserPo">
select *
from users
@ -33,16 +38,13 @@
<if test="tenantId != null and tenantId != '' ">
and tenant_id = #{tenantId}
</if>
order by create_time desc
order by create_time desc,id desc
</select>
<select id="checkUserCount" resultType="java.lang.Integer">
select count(1)
from users
<where>
and deleted = 0 and username = #{userDto.username}
<if test="userDto.name != null and userDto.name!='' ">
or name = #{userDto.name}
</if>
and deleted = 0 and (username = #{userDto.username}
<if test="userDto.phone != null and userDto.phone!='' ">
or phone = #{userDto.phone}
</if>
@ -52,6 +54,7 @@
<if test="userDto.tenantId != null and userDto.tenantId != '' ">
and tenant_id = #{userDto.tenantId}
</if>
)
</where>
</select>
</mapper>

View File

@ -2,6 +2,9 @@ package cn.zyjblogs.starter.common.exception;
import cn.zyjblogs.starter.common.entity.response.HttpCode;
/**
* @author zhuyijun
*/
public class AbstractBusinessException extends RuntimeException {
private static final long serialVersionUID = -6583471361241853199L;
/**
@ -23,7 +26,7 @@ public class AbstractBusinessException extends RuntimeException {
* 创建业务异常对象
*
* @param responseCode 错误码
* @param message 错误消息
* @param message 错误消息
*/
public AbstractBusinessException(HttpCode responseCode, String message) {
super(message);

View File

@ -0,0 +1,18 @@
package cn.zyjblogs.starter.common.exception;
import cn.zyjblogs.starter.common.entity.response.HttpCode;
/**
* 公共异常处理类
*
* @author zhuyijun
*/
public class CommonBusinessExcetion extends AbstractBusinessException {
public CommonBusinessExcetion() {
super();
}
public CommonBusinessExcetion(HttpCode responseCode, String message) {
super(responseCode, message);
}
}

View File

@ -26,7 +26,7 @@ public class ResourceAuthenticationEntryPoint implements AuthenticationEntryPoin
log.error("认证失败 {}", e.getMessage());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
OBJECT_MAPPER.writeValue(response.getOutputStream(),
ResponseResult.error(HttpCode.UNAUTHORIZED, "认证失败,token无效!"));
ResponseResult.error(HttpCode.UNAUTHORIZED, "认证失败!"));
}
}

View File

@ -31,7 +31,8 @@ public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(resourceId)
resources
.resourceId(resourceId)
// 验证令牌的服务
.tokenStore(tokenStore)
.stateless(true)

View File

@ -0,0 +1,20 @@
package cn.zyjblogs.starter.web.apiversion;
import org.springframework.web.bind.annotation.Mapping;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author zhuyijun
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface ApiVersion {
int value();
}

View File

@ -0,0 +1,49 @@
package cn.zyjblogs.starter.web.apiversion;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import javax.servlet.http.HttpServletRequest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author zhuyijun
*/
@Slf4j
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
private static final Pattern VERSION_PREFIX_PATTERN = Pattern.compile("v(\\d+)/");
private int apiVersion;
public ApiVersionCondition(int apiVersion) {
this.apiVersion = apiVersion;
}
@Override
public ApiVersionCondition combine(ApiVersionCondition other) {
return new ApiVersionCondition(other.getApiVersion());
}
@Override
public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());
if (m.find()) {
int version = Integer.parseInt(m.group(1));
if (version >= this.apiVersion) {
return this;
}
}
return null;
}
@Override
public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
return other.getApiVersion() - this.apiVersion;
}
public int getApiVersion() {
return this.apiVersion;
}
}

View File

@ -0,0 +1,38 @@
package cn.zyjblogs.starter.web.apiversion;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.lang.reflect.Method;
/**
* @author zhuyijun
*/
public class ApiVersionRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
@Override
protected boolean isHandler(Class<?> beanType) {
return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class);
}
@Override
protected RequestCondition<ApiVersionCondition> getCustomTypeCondition(Class<?> handlerType) {
ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
return createCondition(apiVersion);
}
@Override
protected RequestCondition<ApiVersionCondition> getCustomMethodCondition(Method method) {
ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
return createCondition(apiVersion);
}
private RequestCondition<ApiVersionCondition> createCondition(ApiVersion apiVersion) {
return apiVersion == null ? null : new ApiVersionCondition(apiVersion.value());
}
}

View File

@ -0,0 +1,18 @@
package cn.zyjblogs.starter.web.autoconfig;
import cn.zyjblogs.starter.web.apiversion.ApiVersionRequestMappingHandlerMapping;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
/**
* @author zhuyijun
*/
@Configuration
public class ApiVersionWebMvcAutoConfiguration extends WebMvcConfigurationSupport {
@Override
public RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return new ApiVersionRequestMappingHandlerMapping();
}
}

View File

@ -1,7 +1,6 @@
package cn.zyjblogs.starter.web.autoconfig;
import cn.zyjblogs.starter.web.hander.GlobalExceptionHandler;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -15,7 +14,8 @@ public class GlobalExceptionHandlerAutoConfiguration {
@ConditionalOnMissingBean(
name = {"globalExceptionHandler"}
)
public GlobalExceptionHandler globalExceptionHandler(){
public GlobalExceptionHandler globalExceptionHandler() {
return new GlobalExceptionHandler();
}
}

View File

@ -1,14 +1,10 @@
package cn.zyjblogs.starter.web.autoconfig;
import com.google.common.collect.Lists;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
@ -23,19 +19,18 @@ import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
import java.lang.reflect.Field;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author zhuyijun
*/
@EnableOpenApi
@Configuration
@Import(BeanValidatorPluginsConfiguration.class)
@ -80,6 +75,7 @@ public class Knife4jAutoConfigurationConfig {
@Bean(value = "defaultApi")
public Docket defaultApi() {
return new Docket(DocumentationType.SWAGGER_2)
.enable(true)
.directModelSubstitute(LocalDateTime.class, Date.class)
.directModelSubstitute(LocalDate.class, String.class)
.directModelSubstitute(LocalTime.class, String.class)

View File

@ -0,0 +1,19 @@
package cn.zyjblogs.starter.web.exception;
import cn.zyjblogs.starter.common.exception.AbstractFrameworkException;
/**
* 版本控制异常
*
* @author zhuyijun
*/
public class ApiVersionException extends AbstractFrameworkException {
public ApiVersionException(String message) {
super(message);
}
public ApiVersionException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -1,4 +1,5 @@
## Auto Configure
org.springframework.boot.cn.zyjblogs.starter.feign.autoconfigure.EnableAutoConfiguration=\
cn.zyjblogs.starter.web.autoconfig.GlobalExceptionHandlerAutoConfiguration,\
cn.zyjblogs.starter.web.autoconfig.Knife4jAutoConfigurationConfig
cn.zyjblogs.starter.web.autoconfig.Knife4jAutoConfigurationConfig,\
cn.zyjblogs.starter.web.autoconfig.ApiVersionWebMvcAutoConfiguration