前端
https://blog.csdn.net/m0_37613503/article/details/128961447
資料庫
1.使用者表
CREATE TABLE `x_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(100) DEFAULT NULL,
`email` varchar(50) DEFAULT NULL,
`phone` varchar(20) DEFAULT NULL,
`status` int(1) DEFAULT NULL,
`avatar` varchar(200) DEFAULT NULL,
`deleted` INT(1) DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
insert into `x_user` (`id`, `username`, `password`, `email`, `phone`, `status`, `avatar`, `deleted`) values('1','admin','123456','super@aliyun.com','18677778888','1','https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif','0');
insert into `x_user` (`id`, `username`, `password`, `email`, `phone`, `status`, `avatar`, `deleted`) values('2','zhangsan','123456','zhangsan@gmail.com','13966667777','1','https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif','0');
insert into `x_user` (`id`, `username`, `password`, `email`, `phone`, `status`, `avatar`, `deleted`) values('3','lisi','123456','lisi@gmail.com','13966667778','1','https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif','0');
insert into `x_user` (`id`, `username`, `password`, `email`, `phone`, `status`, `avatar`, `deleted`) values('4','wangwu','123456','wangwu@gmail.com','13966667772','1','https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif','0');
insert into `x_user` (`id`, `username`, `password`, `email`, `phone`, `status`, `avatar`, `deleted`) values('5','zhaoer','123456','zhaoer@gmail.com','13966667776','1','https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif','0');
insert into `x_user` (`id`, `username`, `password`, `email`, `phone`, `status`, `avatar`, `deleted`) values('6','songliu','123456','songliu@gmail.com','13966667771','1','https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif','0');
2.角色表
CREATE TABLE `x_role` (
`role_id` int(11) NOT NULL AUTO_INCREMENT,
`role_name` varchar(50) DEFAULT NULL,
`role_desc` varchar(100) DEFAULT NULL,
PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
insert into `x_role` (`role_id`, `role_name`, `role_desc`) values('1','admin','超級管理員');
insert into `x_role` (`role_id`, `role_name`, `role_desc`) values('2','hr','人事專員');
insert into `x_role` (`role_id`, `role_name`, `role_desc`) values('3','normal','普通員工');
3.選單表
CREATE TABLE `x_menu` (
`menu_id` int(11) NOT NULL AUTO_INCREMENT,
`component` varchar(100) DEFAULT NULL,
`path` varchar(100) DEFAULT NULL,
`redirect` varchar(100) DEFAULT NULL,
`name` varchar(100) DEFAULT NULL,
`title` varchar(100) DEFAULT NULL,
`icon` varchar(100) DEFAULT NULL,
`parent_id` int(11) DEFAULT NULL,
`is_leaf` varchar(1) DEFAULT NULL,
`hidden` tinyint(1) DEFAULT NULL,
PRIMARY KEY (`menu_id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4;
insert into `x_menu`(`menu_id`,`component`,`path`,`redirect`,`name`,`title`,`icon`,`parent_id`,`is_leaf`,`hidden`) values (1,'Layout','/user','/user/list','userManage','使用者管理','userManage',0,'N',0),(2,'user/user','list',NULL,'userList','使用者列表','userList',1,'Y',0),(3,'user/role','role',NULL,'roleList','角色列表','role',1,'Y',0),(4,'user/permission','permission',NULL,'permissionList','許可權列表','permission',1,'Y',0);
4.使用者角色對映表
CREATE TABLE `x_user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`role_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;
insert into `x_user_role` (`id`, `user_id`, `role_id`) values('1','1','1');
5.角色選單對映表
CREATE TABLE `x_role_menu` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role_id` int(11) DEFAULT NULL,
`menu_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;
後端
1、建立springboot專案2.6.13
2、pom依賴
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.2</version>
</dependency>
<!-- freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
3.yml
server:
port: 9999
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/xdb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT
redis:
port: 6379
host: localhost
logging:
level:
com.zhu: debug
2、Mybatis-plus程式碼生成
1.編寫程式碼生成器
package com.zhu;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Collections;
public class CodeGenerator {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/xdb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT";
String username = "root";
String password = "123456";
String moduleName = "sys";
String mapperLocation = "E:\\Intellij IDEA\\專案\\x-admin\\src\\main\\resources\\mapper\\"+ moduleName;
String tables = "x_user,x_menu,x_role,x_role_menu,x_user_role";
FastAutoGenerator.create(url, username, password)
.globalConfig(builder -> {
builder.author("zhutieyang") // 設定作者
//.enableSwagger() // 開啟 swagger 模式
.fileOverride() // 覆蓋已生成檔案
.outputDir("E:\\Intellij IDEA\\專案\\x-admin\\src\\main\\java"); // 指定輸出目錄
})
.packageConfig(builder -> {
builder.parent("com.zhu") // 設定父包名
.moduleName(moduleName) // 設定父包模組名
.pathInfo(Collections.singletonMap(OutputFile.xml, mapperLocation)); // 設定mapperXml生成路徑
})
.strategyConfig(builder -> {
builder.addInclude(tables) // 設定需要生成的表名
.addTablePrefix("x_"); // 設定過濾表字首
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,預設的是Velocity引擎模板
.execute();
}
}
2、啟動類加註解
因為自動生成的時候沒有@mapper
3、測試
3、公共響應類
package com.zhu.common;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
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);
}
}
4.登入相關介面
4.1、登入
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
package com.zhu.sys.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zhu.config.MyRedisConfig;
import com.zhu.sys.entity.User;
import com.zhu.sys.mapper.UserMapper;
import com.zhu.sys.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.omg.CORBA.TIMEOUT;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* <p>
* 服務實現類
* </p>
*
* @author zhutieyang
* @since 2024-04-06
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Autowired
private RedisTemplate redisTemplate;
@Override
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.baseMapper.selectOne(wrapper);
// 結果不為空,則生成token,並將使用者資訊存入redis
if(loginUser !=null){
// 暫時用UUID,終極方案是jwt
String key ="user:"+ UUID.randomUUID();
// 存入redis
loginUser.setPassword(null);
redisTemplate.opsForValue().set(key,loginUser,30, TimeUnit.MINUTES);
// 返回資料
Map<String, Object> data = new HashMap<>();
data.put("token",key);
return data;
}
return null;
}
}
4.1.2、整合redis
1.pom
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.yml
spring:
redis:
host: localhost
port: 6379
3.配置類
package com.zhu.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
@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 serializer = new Jackson2JsonRedisSerializer<Object>(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;
}
}
4.2、獲取使用者資訊
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>
4.3、登出
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);
}
5、跨域處理
記得開啟redis
package com.zhu.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class MyCorsConfig {
@Bean
public CorsFilter corsFilter(){
//新增CORS配置資訊
CorsConfiguration configuration = new CorsConfiguration();
//允許的域,不要寫*,否則cookie就無法使用了
configuration.addAllowedOrigin("http://localhost:8888");//這裡填寫請求的前端伺服器
//是否傳送Cookie資訊
configuration.setAllowCredentials(true);
//允許的請求方式
configuration.addAllowedMethod("*");
// 4)允許的頭資訊
configuration.addAllowedHeader("*");
// 新增對映路徑,我們攔截一切請求
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**",configuration);
//返回新的CorsFilter
return new CorsFilter(urlBasedCorsConfigurationSource);
}
}
6、使用者管理介面
6.1、查詢使用者列表
1、controller
@GetMapping("/list")
public Result<Map<String,Object>> getUserList(@RequestParam(value = "username",required = false) String username,
@RequestParam(value = "phone",required = false) String phone,
@RequestParam(value = "pageNo") Long pageNo,
@RequestParam(value = "pageSize") Long pageSize){
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(StringUtils.hasLength(username),User::getUsername,username);
wrapper.eq(StringUtils.hasLength(phone),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);
2、分頁攔截器
@Configuration
public class MpConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}