SpringBoot+Vue前后端分离管理系统03:后端

news2024/10/2 6:40:51

后端项目初始化 

1、创建一个springboot 2.7.8项目

2、导入依赖

<!-- web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mysql -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</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>

3、编写application.yaml

server:
  port: 9999

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/xdm?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    
logging:
  level:
    com: debug

MyBatisPlus代码生成

官网:代码生成器(新) | MyBatis-Plus

1、在test/java/com包下,编写代码生成器

public class CodeGenerator {
    public static void main(String[] args){
        String url = "jdbc:mysql://localhost:3306/xdm?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC";
        String username = "root";
        String password = "666666QIU";
        String moduleName = "sys";
        String mapperLocation = "src\\main\\resources\\mapper\\" + moduleName;
        String tables = "x_menu,x_role,x_role_menu,x_user,x_user_role";
        FastAutoGenerator.create(url, username, password)
                .globalConfig(builder -> {
                    builder.author("丘桔") // 设置作者
                            .outputDir("src\\main\\java"); // 指定输出目录
                })
                .packageConfig(builder -> {
                    builder.parent("com") // 设置父包名
                            .moduleName(moduleName) // 设置父包模块名
                            .pathInfo(Collections.singletonMap(OutputFile.xml, mapperLocation)); // 设置mapperXml生成路径
                })
                .strategyConfig(builder -> {
                    builder.addInclude(tables) // 设置需要生成的表名
                            .addTablePrefix("x_"); // 设置过滤表前缀
                })
                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .execute();
    }
}

2、给启动类加注解

@MapperScan("com.*.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(String message){
        return new Result<>(20000,message,null);
    }

    public static <T>Result<T> success(T data){
        return new Result<>(20000,"success",data);
    }

    public static <T>Result<T> success(String message, T data){
        return new Result<>(20000,message,data);
    }



    public static <T>Result<T> fail(){
        return new Result<>(20001,"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);
    }

    public static <T>Result<T> fail(T data){
        return new Result<>(20001,"fail",data);
    }

    public static <T>Result<T> fail(String message, T data){
        return new Result<>(20001,message,data);
    }
}

登录接口

接口属性
url/user/login
methodpost
请求参数username
password
返回参数{
    "code": 20000,
    "message": "success",
    "data": {
        "token": "user:71957608-08d0-4e29-b875-a497763f1db8"
    }
}

要做的事:用户访问/user/login请求的时候,判断用户名密码是否正确,如果正确,给前端返回token,并且将token和uuid存入redis中

1、导入redis依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.5.2</version>
        </dependency>

2、在application.yaml中,配置redis

spring:
  redis:
    host: localhost
    port: 6379

 3、编写redis配置类

@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;
    }
}

4、编写service层

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Autowired
    private RedisTemplate redisTemplate;

    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);    //密码不用存到redis中
            //存入key value,并且设置过期时间为30分钟
            redisTemplate.opsForValue().set(key,loginUser,30, TimeUnit.MINUTES);
            //返回数据
            HashMap<String, Object> data = new HashMap<>();
            data.put("token",key);
            return data;
        }else {
            return null;
        }
    }
}

5、编写controller层


@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserServiceImpl userService;
   
    @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);
        }else {
            return Result.fail(20002,"用户名或密码错误");
        }

    }
}

获取用户信息

接口属性
url/user/info?token=xxx
methodget
请求参数token
返回参数{
    "code": 20000,
    "message": "success",
    "data": {
        "roles": [
            "admin",
            "hr"
        ],
        "name": "admin",
        "avatar": "https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif"
    }
}

1、导入依赖

        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.7</version>
        </dependency>

2、编写UserMapper接口

public interface UserMapper extends BaseMapper<User> {
    public List<String> getRoleNameByUserId(Integer userId);
}

3、编写UserMapper.xml

<select id="getRoleNameByUserId" parameterType="Integer" resultType="String">
        select r.role_name
        from x_user_role ur
        left join x_role r
        on ur.id = r.role_id
        where ur.user_id = #{user_id};
</select>

4、编写Service层

    @Override
    public Map<String, Object> getUserInfo(String token) {

        //根据token,从redis中获取用户信息
        Object obj = redisTemplate.opsForValue().get(token);
        if(obj != null){
            User loginUser = JSON.parseObject(JSON.toJSONString(obj), User.class);
            Map<String, Object> data = new HashMap<>();
            data.put("name", loginUser.getUsername());
            data.put("avatar", loginUser.getAvatar());
            //角色
            List<String> roleList = this.baseMapper.getRoleNameByUserId(loginUser.getId());
            data.put("roles",roleList);
            return data;
        }else {
            return null;
        }
    }

5、编写Controller层

    @GetMapping("/info")
    public Result<Map<String, Object>> getUserInfo(@RequestParam("token") String token){
        //根据token获取用户信息
        Map<String, Object> data = userService.getUserInfo(token);
        if(data != null){
            return Result.success(data);
        }else {
            return Result.fail(20003,"登录信息无效,请重新登录");
        }
    }

注销接口

接口属性
url/user/logout
methodpost
请求参数X-Token(在Request Header中)
返回参数{
    "code": 20000,
    "message": "success",
    "data": null
}

注销直接在redis中,把token删掉即可

1、编写service层

    @Override
    public void logout(String token) {
        redisTemplate.delete(token);
    }

2、编写controller层

    @PostMapping("/logout")
    public Result<?> logout(@RequestHeader("X-Token") String token){
        userService.logout(token);
        return Result.success();
    }

前后端对接

1、更改src/api/user.js下的路径

2、更改 .env.development下的路径

3、删掉vue.config.js的第39行

4、运行之后会报错

原因:跨域(ip地址或者端口号不同),前端是localhost:8888,后端是localhost:9999

5、解决跨域问题

方法一:在每一个controller上加 @CrossOrigin 注解

方法二:写一个配置类,去做全局的跨域处理

@Configuration
public class MyCorsConfig {
    @Bean
    public CorsFilter corsFilter(){
        CorsConfiguration configuration = new CorsConfiguration();
        //允许的ip和端口,不能写*,不安全,并且cookie无法使用
        configuration.addAllowedOrigin("http://localhost:8888"); //允许这个localhost这个ip,8888这个端口访问后端
        configuration.setAllowCredentials(true);   //允许给后端传cookie
        configuration.addAllowedMethod("*");    //允许所有的请求方式访问后端
        configuration.addAllowedHeader("*");    //允许所有的请求头

        //添加映射路径,“/**”表示对所有的路径实行全局跨域访问权限的设置
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**",configuration);

        return new CorsFilter(source);
    }
}

用户管理页面布局

参考我之前写的前端部分:SpringBoot+Vue前后端分离管理系统02:前端_丘桔的博客-CSDN博客

用户列表查询接口

1、编写MyBatisPlus分页插件配置

@Configuration
public class MyPageConfig {
    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

2、编写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") Integer pageNo,
                                                   @RequestParam(value = "pageSize") Integer 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);
    }

用户列表查询对接后端

1、在src/api目录下,创建userManager.js

import request from '@/utils/request'

export default {
  getUserList(searchModel){
    return request({
      url: '/user/list',
      method: 'get',
      params: {
        pageNo: searchModel.pageNo,
        pageSize: searchModel.pageSize,
        username: searchModel.username,
        phone: searchModel.phone
      }
    })
  }
}

2、在src/views/sys/user.vue中,编写方法

  methods: {
    handleSizeChange(pageSize){
      this.searchModel.pageSize = pageSize
      this.getUserList()
    },
    handleCurrentChange(pageNo){
      this.searchModel.pageNo = pageNo
      this.getUserList()
    },
    getUserList(){
      userApi.getUserList(this.searchModel).then(reponse => {
        this.userList = reponse.data.rows
        this.total = reponse.data.total
      })
    }
  },
  created() {
    this.getUserList()
  }

表单提交

    @PostMapping("/addUser")
    public Result<?> addUser(@RequestBody User user){
        userService.save(user);
        return Result.success("新增用户成功");
    }

密码加密

1、导入依赖

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
        </dependency>

2、注册bean

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

3、增加用户的时候对密码加密

    @Autowired
    private PasswordEncoder passwordEncoder;
    @PostMapping("/addUser")
    public Result<?> addUser(@RequestBody User user){
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        userService.save(user);
        return Result.success("新增用户成功");
    }

4、修改登录逻辑

新增用户

    @PutMapping("/updateUser")
    public Result<?> updateUser(@RequestBody User user){
        user.setPassword(null);
        userService.updateById(user);   //这个方法中,如果某个字段值为空,那么该字段的值不会更新
        return Result.success("修改用户成功");
    }

    @GetMapping("/{id}")
    public Result<?> getUserById(@PathVariable("id") Integer id){
        User user = userService.getById(id);
        return Result.success(user);
    }

 

删除用户

1、配置application.yaml

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

2、编写controller

    @DeleteMapping("/{id}")
    public Result<?> DeleteById(@PathVariable("id") Integer id){
        userService.removeById(id);
        return Result.success("删除用户成功");
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/369946.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MIND——Modality independent neighbourhood descriptor 模态无关邻域描述符

参考&#xff1a;https://blog.mbedded.ninja/programming/signal-processing/image-processing/image-registration/modality-independent-neighbourhood-descriptor-mind/《MIND: Modality independent neighbourhood descriptor for multi-modal deformable registration》论…

【离线数仓-6-数据仓库开发ODS层设计要点】

离线数仓-6-数据仓库开发ODS层设计要点离线数仓-6-数据仓库开发ODS层1.数据仓库开发ODS层设计要点2.ODS层用户行为日志表1.hive中复杂结构体复习1.array2.map3.struct 复杂结构4.嵌套格式2.hive中针对复杂结构字符串的练习1.针对ods层为json格式数据的练习2.用户行为日志表的设…

Linux学习(5)nano与正确关机方法sync shutdown

目录 文书编辑器 nano 正确的关机方法: sync, shutdown, reboot, halt, poweroff, init 数据同步写入磁盘&#xff1a;sync 惯用的关机命令&#xff1a; shutdown 重新启动&#xff0c;关机&#xff1a; reboot, halt, poweroff 切换运行等级&#xff1a; init 以下内容转载…

linux编程之经典多级时间轮定时器(C语言版)

一. 多级时间轮实现框架 上图是5个时间轮级联的效果图。中间的大轮是工作轮&#xff0c;只有在它上的任务才会被执行&#xff1b;其他轮上的任务时间到后迁移到下一级轮上&#xff0c;他们最终都会迁移到工作轮上而被调度执行。 多级时间轮的原理也容易理解&#xff1a;就拿时…

技术分享| anyRTC回声消除算法进化

本文将从基础概念、经典算法、主要挑战&#xff0c;以及人工智能回声消除技术探索等方面&#xff0c;分享anyRTC在 AEC 技术方面的实践及效果。 一.什么是回声消除 回音消除一直是语音通信的难点&#xff0c;最早的回声消除是从电话兴起的时候就有了&#xff0c;电话机的硬件…

postgres 源码解析51 LWLock轻量锁--2

本篇将着重讲解LWLock涉及的主要API工作流程与实现原理&#xff0c;相关基础知识见回顾&#xff1a;postgres 源码解析50 LWLock轻量锁–1 API介绍 函数API功能CreateLWLocks分配LWLocks所需的内存并进行初始化LWLockNewTrancheId分配新的Tranche ID,供用户使用Extension模块…

Helm安装Harbor

一、介绍 1.1 Harbor Harbor 是由 VMware 公司为企业用户设计的 Registry Server 开源项目&#xff0c;包括了权限管理 (RBAC)、LDAP、审计、管理界面、自我注册、HA 等企业必需的功能&#xff0c;同时针对中国用户的特点&#xff0c;设计镜像复制和中文支持等功能。目前该项…

说说Java“锁“ 事

文章目录前言大厂面试题复盘 —— 并发编程高级面试解析从轻松的乐观锁和悲观锁开讲通过8种情况演示锁运行案例&#xff0c;看看我们到底锁的是什么公平锁和非公平锁可重入锁(又名递归锁)死锁及排查写锁(独占锁)/读锁(共享锁)自旋锁SpinLock无锁 -> 独占锁 -> 读写锁 -&g…

五种IO模型以及select多路转接IO模型

目录 一、典型IO模型 1.1 阻塞IO 1.2 非阻塞IO 1.3 信号驱动I0 1.4 IO多路转接 1.5 异步IO 多路转接的作用和意义 二、多路转接IO模型&#xff08;select&#xff09; 2.1 接口 2.2 接口当中的事件集合&#xff1a; fd_set 2.2 select使用事件集合&#xff08;位图&am…

ip公司和soc公司是什么?

IP 公司和 SoC 公司都是半导体行业的重要组成部分&#xff0c;但它们的角色和职责略有不同。IP&#xff08;Intellectual Property&#xff09;公司主要提供可重用的知识产权组件&#xff0c;也称为 IP 核或 IP 模块&#xff0c;这些组件可以在设计芯片的过程中被集成到芯片中。…

Git代码冲突-不同分支之间的代码冲突

1、解决思路在团队开发中&#xff0c;提交代码到Git仓库时经常会遇到代码冲突的问题。- 原因&#xff1a;多人对相同的文件进行了编辑&#xff0c;造成代码存在差异化- 解决方案&#xff1a;1. 使用工具或git命令对比不同分支代码的差异化2. 把不同分支中有效代码进行保留&…

[译文] 基于PostGIS3.1 生成格网数据

根据格网进行数据统计与分析是一种常用的方法&#xff0c;相比自然地理边界与行政管理边界而言&#xff0c;使用格网有如下特点&#xff1a;每个格网之间地位相等&#xff0c;没有上下级之分。每个格网的面积都相等。相邻两个格网单元中心点之间距离相等。适用于将数据从“空间…

ThreeJS加载公路GeoJson数据实现流光效果

threejs加载公路geojson数据,跟加载行政区域的原理一样,唯一不同的是geojson格式不一样,路线并不是连贯起来的,按照路段进行的拆分,在加载的时候问题不大,正常解析然后转墨卡托投影,但是在做流光效果时,需要对geojson进行重新组合. 实现效果:

Android:反编译apk踩坑/apktool/dex2jar/JDGUI

需求描述 想要反编译apk文件&#xff0c;搜到了这篇博客&#xff1a;Android APK反编译就这么简单 详解&#xff08;附图&#xff09;&#xff0c;非常有参考价值~但其中的工具下载链接都已404&#xff0c;而本杂鱼实际操作的过程中也出现了亿点点点点点点的问题&#xff0c;于…

电子技术——反馈对放大器极点的影响

电子技术——反馈对放大器极点的影响 放大器的频率响应和稳定性可以直接由其极点决定。因此我们将深入反馈对放大器极点的影响。 稳定性和极点位置 我们首先讨论稳定性和极点位置的关系。首先我们给出结论&#xff0c;对于任何一个稳定的放大器&#xff0c;其极点都处在 sss …

prometheus + alterManager + 飞书通知,实现服务宕机监控告警;实测可用

架构设计图 最终效果图 项目准备 xml依赖 <!-- 监控相关 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>io.…

Elasticsearch7.8.0版本进阶——段合并

目录一、段的概述1.1、段的概念1.2、段的缺点1.3、如何解决段数量暴增问题二、段合并的流程三、段合并的注意事项一、段的概述 1.1、段的概念 每一 段 本身都是一个倒排索引。 1.2、段的缺点 由于自动刷新流程每秒会创建一个新的段 &#xff0c;这样会导致短时间内的段数量…

interrupt多线程设计模式

1. 两阶段终止-interrupt Two Phase Termination 在一个线程T1中如何“优雅”终止线程T2&#xff1f;这里的【优雅】指的是给T2一个料理后事的机会。 错误思路 ● 使用线程对象的stop()方法停止线程&#xff08;强制杀死&#xff09; —— stop&#xff08;&#xff09;方法…

Linux内核的虚拟内存(MMU、页表结构)

前言&#xff1a;内存是程序得以运行的重要物质基础。如何在有限的内存空间运行较大的应用程序&#xff0c;曾是困扰人们的一个难题。为解决这个问题&#xff0c;人们设计了许多的方案&#xff0c;其中最成功的当属虚拟内存技术。Linux作为一个以通用为目的的现代大型操作系统&…

【git】Idea中git的使用

配置git 创建git仓库 不同颜色代表的含义 红色——未加入版本控制&#xff1b;绿色——已经加入控制暂未提交&#xff1b;蓝色——加入&#xff0c;已提交&#xff0c;有改动&#xff1b;白色——加入&#xff0c;已提交&#xff0c;无改动&#xff1b;灰色——版本控制已忽略文…