一小时完成后台管理系统

基于ant design pro + springboot的后台管理系统

简介

在之前的Java快速学习找实习中曾经提到过,在学完springboot,理解从前端请求到后台响应的过程之后,可以尝试自己写一个后台管理系统,这次就来介绍如何快速创建一个管理系统,一小时完成基于单表增删改查的后台管理系统。
在实际的请求过程中,请求流程如下:

请求过程

在我们自己创建管理系统时,可以采用数据库,后台,前端的开发过程。

企业项目开发流程

需求分析==> 系统设计 ==>技术选型 ==> 初始化 ==>编写业务代码 ==> 测试 ==> review ==> 部署 ==> 上线
注 因为后台管理系统过于简单,一般不用需求分析和系统设计

技术选型:ant design pro + springboot + mybatisplus +mysql
下面直接进入初始化

初始化

后端

1.使用 idea 新建一个springboot 项目
2.根据技术选型引入依赖
<dependency>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

 <dependency>
     <groupId>com.baomidou</groupId>
     <artifactId>mybatis-plus-boot-starter</artifactId>
     <version>3.5.0</version>
 </dependency>

 <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
 </dependency>

 <dependency>
     <groupId>org.projectlombok</groupId>
     <artifactId>lombok</artifactId>
 </dependency>
3.运行项目

前端

1.创建 ant design pro 项目
npm i @ant-design/pro-cli -g
pro create myapp(项目名称)
2.下载依赖
cd myapp
npm install
3.执行项目
npm run start

详细操作过程见官网

数据库

库表设计应该在之前完成,但是对于单表的增删改查我们无需过多设计,只需考虑单表对应的实体类应该具有哪些属性,对应单表字段即可。这里提供一个例子,仅供参考。也是我后台管理系统用到的表。

create table t_book
(
    book_id          bigint                             not null comment '书籍id'
        primary key,
    book_name        varchar(100)                       not null comment '书籍名称',
    book_price       decimal(5, 2)                      null comment '书籍价格',
    book_count       int      default 0                 null comment '书籍数量',
    book_author      varchar(50)                        null comment '书籍作者',
    book_description varchar(1000)                      null comment '书籍简介',
    create_time      datetime default CURRENT_TIMESTAMP not null comment '创建时间',
    update_time      datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
    is_delete        tinyint  default 0                 not null comment '是否删除'
)
    comment '书籍表';

小技巧:可以尝试用chatgpt生成一些假数据添加到表中

编写业务代码

后端

正常情况下,我们需要写controller、service、mapper、entity,由于mybatisplus单表查询的优势,service、mapper、entity可以直接使用Mybatis-X插件生成
生成过程

下载
选择使用插件
实体类
生成完成
之后我们需要自己编写controller,只需要了解springboot基本注解即可。

示例代码
package com.wdlm.demo.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wdlm.demo.pojo.Book;
import com.wdlm.demo.request.book.AddBookRequest;
import com.wdlm.demo.request.book.DeleteBookRequest;
import com.wdlm.demo.request.book.QueryBookRequest;
import com.wdlm.demo.request.book.UpdateBookRequest;
import com.wdlm.demo.response.Response;
import com.wdlm.demo.service.BookService;
import com.wdlm.demo.vo.BookVO;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/book")
public class BookController {
    @Resource
    private BookService bookService;

    @PostMapping("/add")
    public Response<Long> addBook(@RequestBody AddBookRequest addBookRequest) {
        Book book = new Book();
        BeanUtils.copyProperties(addBookRequest,book);
        boolean isSave = bookService.save(book);
        if (isSave) {
            return new Response(1, book.getBookId(), "插入成功");
        } else {
            return new Response(-1, book.getBookId(), "插入失败");
        }
    }

    @PostMapping("/delete")
    public Response<Boolean> deleteBook(@RequestBody DeleteBookRequest deleteBookRequest) {
        Long bookId = deleteBookRequest.getBookId();
        boolean isDelete = bookService.removeById(bookId);
        if (isDelete) {
            return new Response(1, "删除成功");
        } else {
            return new Response(-1, "删除失败");
        }
    }

    @PostMapping("/update")
    public Response<Boolean> updateBook(@RequestBody UpdateBookRequest updateBookRequest) {
        Book book = new Book();
        BeanUtils.copyProperties(updateBookRequest,book);
        boolean isUpdate = bookService.updateById(book);
        if (isUpdate) {
            return new Response(1, "更新成功");
        } else {
            return new Response(-1, "更新失败");
        }
    }

    @GetMapping("/query")
    public Response<List<BookVO>> getBook(@RequestBody QueryBookRequest queryBookRequest) {
        String bookName = queryBookRequest.getBookName();
        BigDecimal bookPrice = queryBookRequest.getBookPrice();
        Integer bookCount = queryBookRequest.getBookCount();
        String bookAuthor = queryBookRequest.getBookAuthor();
        String bookDescription = queryBookRequest.getBookDescription();
        Integer current = queryBookRequest.getCurrent();
        Integer size = queryBookRequest.getSize();
        if (current == null) {
            current = 1;
        }
        if (size == null) {
            size = 5;
        }


        QueryWrapper<Book> queryWrapper = new QueryWrapper<>();
        if (bookName!= null) {
            queryWrapper.eq("book_name", bookName);
        }
        if (bookPrice!= null) {
            queryWrapper.eq("book_price", bookPrice);
        }
        if (bookCount!= null) {
            queryWrapper.eq("book_count", bookCount);
        }
        if (bookAuthor!= null) {
            queryWrapper.eq("book_author", bookAuthor);
        }
        if (bookDescription!= null) {
            queryWrapper.like("book_description", bookDescription);
        }

        IPage<Book> page = new Page<>(current,size);
        IPage<Book> bookPage = bookService.page(page, queryWrapper);

        if (bookPage == null) {
            return new Response(-1, null, "查无所有");
        }

        List<Book> list = bookPage.getRecords();

        List<BookVO> bookVOList = new ArrayList<>();
        for (Book book : list) {
            BookVO bookVO = new BookVO();
            BeanUtils.copyProperties(book, bookVO);
            bookVO.setBookId(String.valueOf(book.getBookId()));
            bookVOList.add(bookVO);
        }
        return new Response(1, bookVOList, "查询成功");
    }

}
规范写法:

1.对每个请求单独设置一个请求参数类
2.设置统一的响应类
3.设置统一的异常处理

前端

前端自动生成的代码中,已经包含了一个登录页面和一个查询表格,对于单表的增删改查来说已经足够了。我们需要做的是改造这两个界面,完成前后端联调。
ant design pro 提供了基于openapi,自动在前端生成后端接口的方式
首先需要后端提供接口文档

引入依赖

<dependency>
   <groupId>com.github.xiaoymin</groupId>
   <artifactId>knife4j-spring-boot-starter</artifactId>
   <version>3.0.3</version>
</dependency>

编写配置

package com.wdlm.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
@Profile({"dev", "test"})
public class Knife4jConfig {
    @Bean
    public Docket defaultApi2() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(new ApiInfoBuilder()
                        .title("接口文档")
                        .description("springboot-demo")
                        .version("1.0")
                        .build())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.wdlm.demo.controller"))
                .paths(PathSelectors.any())
                .paths(PathSelectors.regex("/error").negate())
                .build();
    }
}

启动后端项目,输入localhost:8888/api/doc.html,可以看到
接口文档
输入localhost:8888/api/v3/api-docs可以获得接口文档json数据。

前端设置

在config.ts中设置接口文档地址

openAPI: [
    {
      requestLibPath: "import { request } from '@umijs/max'",
      schemaPath: 'http://localhost:8888/api/v3/api-docs',
      projectName: 'back-end-demo',
    },
  ],

在package.json中执行
openapi
生成的接口在services目录下。
在这里插入图片描述

修改注册页面

参考官方文档:登录表单

修改查询表格

参考官方文档:高级表格

设置请求路径

之前requestErrorConfig中包含异常处理信息,可以在这里设置请求路径,并在app.tsx中导入,为了见名知意,将requestErrorConfig改为requestConfig
导入
修改
之所以设置withCredentials:true,是让前端携带cookie数据进行访问。注意,之后的项目启动采用npm run dev(不使用mock数据,直接请求后端)。

项目效果

登录
查询表格

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今天不coding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值