配套视频地址:https://www.bilibili.com/video/BV1dG4y1T7yp/
创建springboot项目:2.7.8
pom依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.mysqlgroupId>
<artifactId>mysql-connector-jartifactId>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.5.2version>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-generatorartifactId>
<version>3.5.2version>
dependency>
<dependency>
<groupId>org.freemarkergroupId>
<artifactId>freemarkerartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
yml
server:
port: 9999
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql:///xdb
logging:
level:
com.lantu: debug
测试
编写代码生成器
public static void main(String[] args) {
String url = "jdbc:mysql:///xdb";
String username = "root";
String password = "123456";
String author = "laocai";
String outputDir = "D:\\tmp\\spring\\x-admin\\src\\main\\java";
String basePackage = "com.lantu";
String moduleName = "sys";
String mapperLocation = "D:\\tmp\\spring\\x-admin\\src\\main\\resources\\mapper\\" + moduleName;
String tableName = "x_user,x_menu,x_role,x_role_menu,x_user_role";
String tablePrefix = "x_";
FastAutoGenerator.create(url, username, password)
.globalConfig(builder -> {
builder.author(author) // 设置作者
.enableSwagger() // 开启 swagger 模式
//.fileOverride() // 覆盖已生成文件
.outputDir(outputDir); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent(basePackage) // 设置父包名
.moduleName(moduleName) // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.xml, mapperLocation)); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude(tableName) // 设置需要生成的表名
.addTablePrefix(tablePrefix); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
启动类加注解
@MapperScan("com.lantu.*.mapper")
测试
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
private Integer code;
private String message;
private T data;
public static<T> Result<T> success(){
return new Result<>(20000,"success",null);
}
public static<T> Result<T> success(T data){
return new Result<>(20000,"success",data);
}
public static<T> Result<T> success(T data, String message){
return new Result<>(20000,message,data);
}
public static<T> Result<T> success(String message){
return new Result<>(20000,message,null);
}
public static<T> Result<T> fail(){
return new Result<>(20001,"fail",null);
}
public static<T> Result<T> fail(Integer code){
return new Result<>(code,"fail",null);
}
public static<T> Result<T> fail(Integer code, String message){
return new Result<>(code,message,null);
}
public static<T> Result<T> fail( String message){
return new Result<>(20001,message,null);
}
}
接口属性 | 值 |
---|---|
url | /user/login |
method | post |
请求参数 | username password |
返回参数 | |
controller
@PostMapping("/login")
public Result<Map<String,Object>> login(@RequestBody User user){
Map<String,Object> data = userService.login(user);
if(data != null){
return Result.success(data);
}
return Result.fail(20002,"用户名或密码错误");
}
service
public Map<String, Object> login(User user) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper();
wrapper.eq(User::getUsername,user.getUsername());
wrapper.eq(User::getPassword,user.getPassword());
User loginUser = this.getOne(wrapper);
if(loginUser != null){
Map<String, Object> data = new HashMap<>();
String key = "user::" + UUID.randomUUID();
data.put("token", key); // 待优化,最终方案jwt
loginUser.setPassword(null);
redisTemplate.opsForValue().set(key,loginUser,30, TimeUnit.MINUTES);
return data;
}
return null;
}
整合redis
pom
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
yml
spring:
redis:
host: localhost
port: 6379
配置类
@Configuration
public class MyRedisConfig {
@Resource
private RedisConnectionFactory factory;
@Bean
public RedisTemplate redisTemplate(){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
redisTemplate.setValueSerializer(serializer);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
om.setTimeZone(TimeZone.getDefault());
om.configure(MapperFeature.USE_ANNOTATIONS, false);
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
om.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance ,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
om.setSerializationInclusion(JsonInclude.Include.NON_NULL);
serializer.setObjectMapper(om);
return redisTemplate;
}
}
接口属性 | 值 |
---|---|
url | /user/info?token=xxx |
method | get |
请求参数 | token |
返回参数 | |
controller
@GetMapping("/info")
public Result<?> getUserInfo(@Param("token") String token){
Map<String,Object> data = userService.getUserInfo(token);
if(data != null){
return Result.success(data);
}
return Result.fail(20003,"用户信息获取失败");
}
service
public Map<String, Object> getUserInfo(String token) {
// 从redis查询token
Object obj = redisTemplate.opsForValue().get(token);
// 反序列化
User user = JSON.parseObject(JSON.toJSONString(obj),User.class);
if(user != null){
Map<String, Object> data = new HashMap<>();
data.put("name",user.getUsername());
data.put("avatar",user.getAvatar());
List<String> roleList = this.getBaseMapper().getRoleNamesByUserId(user.getId());
data.put("roles", roleList);
return data;
}
return null;
}
mapper.xml
<select id="getRoleNamesByUserId" parameterType="Integer" resultType="String">
SELECT
b.role_name
FROM x_user_role a,x_role b
WHERE a.`user_id` = #{userId}
AND a.`role_id` = b.`role_id`
select>
接口属性 | 值 |
---|---|
url | /user/logout |
method | post |
请求参数 | |
返回参数 | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DwV5GD4c-1675952251553)(C:\Users\dacai\AppData\Roaming\Typora\typora-user-images\image-20230203171855151.png)] |
controller
@PostMapping("/logout")
public Result<?> logout(@RequestHeader("X-Token") String token){
userService.logout(token);
return Result.success("注销成功");
}
service
public void logout(String token) {
redisTemplate.delete(token);
}
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter(){
//1.添加CORS配置信息
CorsConfiguration config = new CorsConfiguration();
//1) 允许的域,不要写*,否则cookie就无法使用了
config.addAllowedOrigin("http://localhost:8888"); //这里填写请求的前端服务器
//2) 是否发送Cookie信息
config.setAllowCredentials(true);
//3) 允许的请求方式
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
// 4)允许的头信息
config.addAllowedHeader("*");
//2.添加映射路径,我们拦截一切请求
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/**", config);
//3.返回新的CorsFilter.
return new CorsFilter(configSource);
}
}
接口 | 说明 |
---|---|
查询用户列表 | 分页查询 |
新增用户 | |
根据id查询用户 | |
修改用户 | |
删除用户 | 逻辑删除 |
controller
@GetMapping("/list")
public Result<?> getUserListPage(@RequestParam(value = "username", required = false) String username,
@RequestParam(value = "phone", required = false) String phone,
@RequestParam("pageNo") Long pageNo,
@RequestParam("pageSize") Long pageSize) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper();
wrapper.eq(username != null, User::getUsername, username);
wrapper.eq(phone != null, User::getPhone, phone);
Page<User> page = new Page<>(pageNo, pageSize);
userService.page(page, wrapper);
Map<String, Object> data = new HashMap<>();
data.put("total", page.getTotal());
data.put("rows", page.getRecords());
return Result.success(data);
}
分页拦截器
@Configuration
public class MpConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
密码加密处理,用BCryptPasswordEncoder,涉及登录逻辑改动
此处不提供密码更新,大家自行扩展,可以去实现前端右上角菜单的个人信息功能
利用MyBatisPlus做逻辑删除处理
mybatis-plus:
global-config:
db-config:
logic-delete-field: delete
logic-delete-value: 1
logic-not-delete-value: 0