springboot/vue前后端分离后台管理系统增删改查
springboot项目适合初学者
1.需求分析
一个音乐管理系统包括:
1.用户信息管理:该模块主要由管理员进行操作,将所有用户的用户名、密码、邮箱、创建时间以及用户状态列在一张表,管理员可以进行增加、删除(批量删除)、修改以及查询用户信息。
2.歌手信息管理:该模块主要包括歌手的姓名、昵称、性别、歌手照片、歌手出生日期、居住地址、歌手简介。具有增加、删除(逻辑删除、批量删除)、修改以及查询(分页查询、模糊查询)歌手信息等功能。
3:歌曲信息管理:该模块主要包括歌曲名、歌曲简介、发行封面、发行时间、歌词以及更新时间等。有增加、删除(逻辑删除、批量删除)、修改以及查询(分页查询、模糊查询)歌手信息等功能。
4.评论管理:该模块主要包括评论内容、评论类型、是否置顶推荐、评论时间,删除评论等功能。
(还有日志管理,权限管理等功能后续开发)
2.数据库开发
根据上述需求分析进行数据库设计
1.用户信息表 user
用户id | id | INT |
用户名 | name | VARCHAR(45) |
密码 | password | VARCHAR(45) |
邮箱 | VARCHAR(45) | |
手机号 | phone | CHAR(15) |
出生日期 | birth | DATETIME |
头像 | avator | VARCHAR(255) |
创建时间 | create_time | DATETIME |
更新时间 | update_time | DATETIME |
2.歌手信息表 singer
歌手id | id | INT |
歌手名 | name | VARCHAR(45) |
歌手昵称 | nickName | VARCHAR(45) |
歌手性别 | sex | TINYINT(4) |
歌手照片 | pic | VARCHAR(255) |
出生日期 | birth | DATETIME |
居住地址 | address | VARCHAR(45) |
歌手介绍 | introduction | VARCHAR(255) |
是否删除 | is_delete | VARCHAR(20) |
3.歌曲信息表 song
歌曲id | id | INT |
歌手id | singer_id | INT(10) |
歌曲名 | name | VARCHAR(45) |
歌曲简介 | introduction | VARCHAR(255) |
发行时间 | create_time | DATETIME |
更新时间 | update_time | DATETIME |
发行照片 | pic | VARCHAR(255) |
歌词 | lyric | TEXT |
歌曲地址 | url | VARCHAR(255) |
是否删除 | is_delete | VARCHAR(20) |
4.歌单表 song_list
歌单id | id | INT |
歌单标题 | title | VARCHAR(255) |
歌单图片 | pic | VARCHAR(255) |
歌单简介 | introduction | TEXT |
歌单风格 | style | VARCHAR(10) |
5. 歌曲歌单关联表
歌单id | id | INT(10) |
歌曲id | song_id | INT(10) |
歌单id | song_list_id | INT(10) |
6. 歌曲分类表 song_type
类型id | id | INT(10) |
歌曲id | song_id | INT(10) |
评论id | comment_id | INT(10) |
歌曲类型 | type | VARCHAR(255) |
7.评论表 comment
评论id | id | INT |
用户id | user_id | INT(10) |
歌曲id | song_id | VARCHAR(45) |
歌单id | song_list_id | INT(10) |
评论内容 | content | VARCHAR(255) |
创建时间 | create_time | DATETIME |
是否置顶 | is_top | INT(10) |
具体的sql如下代码所示:没有插入数据,自己随便插入一些就可以
CREATE DATABASE /*!32312 IF NOT EXISTS*/`t_music` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `t_music`;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(45) NOT NULL COMMENT '用户名',
`password` VARCHAR(45) NOT NULL COMMENT '密码',
`email` VARCHAR(45) COMMENT '邮箱',
`phone` CHAR(15) DEFAULT NULL COMMENT '手机号',
`birth` DATETIME DEFAULT NULL COMMENT '出生日期',
`avator` VARCHAR(255) DEFAULT NULL COMMENT '头像',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`update_time` DATETIME NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `name_UNIQUE` (`name`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT '用户信息表';
DROP TABLE IF EXISTS `singer`;
CREATE TABLE `singer` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(45) NOT NULL COMMENT '歌手名',
`nickName` VARCHAR(45) COMMENT '歌手昵称',
`sex` TINYINT(4) DEFAULT NULL COMMENT '歌手性别',
`pic` VARCHAR(255) DEFAULT NULL COMMENT '歌手照片',
`birth` DATETIME DEFAULT NULL COMMENT '出生日期',
`address` VARCHAR(45) DEFAULT NULL COMMENT '居住地址',
`introduction` VARCHAR(255) DEFAULT NULL COMMENT '歌手介绍',
`is_delete` VARCHAR(20) NOT NULL DEFAULT '0' COMMENT '是否逻辑删除 0-否,1-是',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=46 DEFAULT CHARSET=utf8 COMMENT '歌手信息表';
DROP TABLE IF EXISTS `song`;
CREATE TABLE `song` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`singer_id` INT(10) UNSIGNED NOT NULL,
`name` VARCHAR(45) NOT NULL COMMENT '歌曲名',
`introduction` VARCHAR(255) DEFAULT NULL COMMENT '歌曲简介',
`create_time` DATETIME NOT NULL COMMENT '发行时间',
`update_time` DATETIME NOT NULL COMMENT '更新时间',
`pic` VARCHAR(255) DEFAULT NULL COMMENT '发行照片',
`lyric` TEXT COMMENT '歌词',
`url` VARCHAR(255) NOT NULL COMMENT '歌曲地址',
`is_delete` VARCHAR(20) NOT NULL DEFAULT '0' COMMENT '是否逻辑删除 0-否,1-是',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=114 DEFAULT CHARSET=utf8 COMMENT '歌曲信息表';
DROP TABLE IF EXISTS `song_list`;
CREATE TABLE `song_list` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`title` VARCHAR(255) NOT NULL COMMENT '歌单标题',
`pic` VARCHAR(255) DEFAULT NULL COMMENT '歌单图片',
`introduction` TEXT COMMENT '歌单简介',
`style` VARCHAR(10) DEFAULT '无' COMMENT '歌单风格',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=42340354 DEFAULT CHARSET=utf8 COMMENT '歌单表';
DROP TABLE IF EXISTS `comment`;
CREATE TABLE `comment` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` INT(10) UNSIGNED NOT NULL COMMENT '用户id',
`song_id` INT(10) UNSIGNED DEFAULT NULL COMMENT '歌曲id',
`song_list_id` INT(10) UNSIGNED DEFAULT NULL COMMENT '歌单id',
`content` VARCHAR(255) DEFAULT NULL COMMENT '评论内容',
`create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
`is_top` INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT '是否置顶 0-未置顶,1-置顶',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=64 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `song_list_linked`;
CREATE TABLE `song_list_linked` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`song_id` INT(10) UNSIGNED NOT NULL,
`song_list_id` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=212 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `song_type`;
CREATE TABLE `song_type` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`song_id` INT(10) UNSIGNED DEFAULT NULL COMMENT '歌曲id',
`comment_id` INT(10) UNSIGNED DEFAULT NULL COMMENT '评论id',
`type` VARCHAR(255) DEFAULT NULL COMMENT '歌曲类型',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=64 DEFAULT CHARSET=utf8 COMMENT '歌曲类型表';
3. 歌手信息后台开发
通过代码生成器,整合swagger进行后台相关开发,主要目录如下
对应的pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>music</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>music</name>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.properties:
# 服务端口
server.port=8888
# 环境设置:dev、test、prod
spring.profiles.active=dev
# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/t_music?serverTimezone=GMT%2B8&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root
#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
#mybatis日志
#mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#mybatis-plus.mapper-locations=classpath:com/example/serviceedu/mapper/xml/*.xml
mybatis-plus.mapper-locations=classpath*:mapper/*.xml,classpath:mapper/**/*Mapper.xml
运行codegenerator代码生成器,生成相应的代码,比如生成singer相关的代码
CodeGenerator代码生成器代码如下:
package com.example.music.config;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class CodeGenerator {
/**
* <p>
* 读取控制台内容
* </p>
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotEmpty(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
//作者
gc.setAuthor("mozz");
//打开输出目录
gc.setOpen(false);
//xml开启 BaseResultMap
gc.setBaseResultMap(true);
//xml 开启BaseColumnList
gc.setBaseColumnList(true);
// 实体属性 Swagger2 注解
gc.setSwagger2(true);
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/t_music? useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia" + "/Shanghai");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.example.music")
.setEntity("entity")
.setMapper("mapper")
.setService("service")
.setServiceImpl("service.impl")
.setController("controller");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
// 如果模板引擎是 freemarker
String templatePath = "/templates/mapper.xml.ftl";
// 如果模板引擎是 velocity
// String templatePath = "/templates/mapper.xml.vm";
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会
return projectPath + "/src/main/resources/mapper/"
+ tableInfo.getEntityName() + "Mapper"
+ StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
//数据库表映射到实体的命名策略
strategy.setNaming(NamingStrategy.underline_to_camel);
//数据库表字段映射到实体的命名策略
strategy.setColumnNaming(NamingStrategy.no_change);
//lombok模型
strategy.setEntityLombokModel(true);
//生成 @RestController 控制器
strategy.setRestControllerStyle(true);
strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
strategy.setControllerMappingHyphenStyle(true);
//表前缀
// strategy.setTablePrefix("t_");
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
文件自动生成,随后进行代码开发,有些是通过mybaits-plus实现的。
整体目录如下:
返回结果接口及封装类
package com.example.music.commons;
public interface ResultCode {
public static Integer SUCCESS = 20000;
public static Integer ERROR = 20001;
}
package com.example.music.commons;
//import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
//统一返回结果的类
@Data
public class R {
@ApiModelProperty(value = "是否成功")
private boolean success;
@ApiModelProperty(value = "返回码")
private Integer code;
@ApiModelProperty(value = "返回消息")
private String message;
@ApiModelProperty(value = "返回数据")
private Map<String,Object> data = new HashMap<String,Object>();
//构造方法私有
private R(){};
//成功的静态方法
public static R ok(){
R r = new R();
r.setSuccess(true);
r.setCode(ResultCode.SUCCESS);
r.setMessage("成功");
return r;
}
//失败的静态方法
public static R error(){
R r = new R();
r.setSuccess(false);
r.setCode(ResultCode.ERROR);
r.setMessage("失败");
return r;
}
public R success(Boolean success){
this.setSuccess(success);
return this;
}
public R code(Integer code){
this.setCode(code);
return this;
}
public R message(String message){
this.setMessage(message);
return this;
}
public R data(String s,Object o){
this.data.put(s,o);
return this;
}
public R data(Map<String,Object> map){
this.setData(map);
return this;
}
}
swagger配置类
package com.example.music.config;
import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket webApiConfig(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("webApi")
.apiInfo(webApiInfo())
.select()
.paths(Predicates.not(PathSelectors.regex("/admin/.*")))
.paths(Predicates.not(PathSelectors.regex("/error.*")))
.build();
}
private ApiInfo webApiInfo(){
return new ApiInfoBuilder()
.title("API文档")
.description("接口定义")
.version("1.0")
.contact(new Contact("mozz", "http://www.baidu.com",
"1345656307@qq.com"))
.build();
}
}
异常类
package com.example.music.expection;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MusicExpection extends RuntimeException{
private Integer code; //状态码
private String msg; //异常信息
}
package com.example.music.expection;
import com.example.music.commons.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
@ControllerAdvice
@Slf4j
public class GlobalExpectionHandler {
//指定出现什么异常执行这个方法
@ExceptionHandler(Exception.class)
@ResponseBody //为了返回数据
public R error(Exception e){
e.printStackTrace();
return R.error().message("执行了全局异常");
}
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
@ResponseBody //为了返回数据
public R error(MethodArgumentTypeMismatchException e){
e.printStackTrace();
return R.error().message("执行了MethodArgumentTypeMismatchException异常");
}
//自定义异常
@ExceptionHandler(MusicExpection.class)
@ResponseBody //为了返回数据
public R error(MusicExpection e){
log.error(e.getMessage());
e.printStackTrace();
return R.error().code(e.getCode()).message(e.getMsg());
}
}
实体类
package com.example.music.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* <p>
*
* </p>
*
* @author mozz
* @since 2022-09-22
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="Singer对象", description="")
public class Singer implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private String name;
private String nickname;
private Integer sex;
private String pic;
private LocalDateTime birth;
private String address;
private String introduction;
private String isDelete;
}
创建一个vo目录,目录下创建一个PageResult实体,用来对分页进行封装
package com.example.music.vo;
import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ApiModel(description = "分页对象")
public class PageResult<T> {
private List<T> records; //分页列表
private Integer count; //总数
}
1.controller(包括分页查询,id查询,修改,删除,批量删除等功能)
package com.example.music.controller;
import com.example.music.commons.R;
import com.example.music.entity.Singer;
import com.example.music.service.ISingerService;
import com.example.music.vo.PageResult;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* <p>
* 前端控制器
* </p>
*
* @author mozz
* @since 2022-09-22
*/
@RestController
@RequestMapping("/music/singer")
@CrossOrigin
public class SingerController {
@Autowired
private ISingerService singerService;
/**
* 分页查询所有歌手信息
* @param page
* @param limit
* @return
*/
@ApiOperation(value = "分页查询所有歌手信息")
@GetMapping("lists/{page}/{limit}")
public R getAllSinger(@ApiParam(name = "page", value = "当前页码", required = true)
@PathVariable("page") Long page,
@ApiParam(name = "limit", value = "每页记录数", required = true)
@PathVariable("limit") Long limit){
page = (page-1)*limit;
PageResult<Singer> singers = singerService.getAllSinger(page,limit);
return R.ok().data("data",singers);
}
/**
* 关键字模糊查询
* @param keyword
* @return
*/
@ApiOperation(value = "关键字模糊查询")
@GetMapping("{keyword}/{page}/{limit}")
public R getSingerByKeyword(@PathVariable("keyword") String keyword,
@PathVariable("page") Long page,
@PathVariable("limit") Long limit){
page = (page-1)*limit;
PageResult<Singer> singers = singerService.getSingerByKeyword(keyword,page,limit);
return R.ok().data("data",singers);
}
/**
* 根据id查询歌手信息
* @param id
* @return
*/
@ApiOperation(value = "根据id查询")
@GetMapping("/info/{id}")
public R getSingerById(@PathVariable("id") Integer id){
Singer singer = singerService.getSingerById(id);
return R.ok().data("data",singer);
}
/**
* 根据id删除歌手信息
* @param id
* @return
*/
@ApiOperation(value = "根据id删除歌手信息")
@PostMapping("delete/{id}")
public R deleteSinger(@PathVariable("id") Integer id){
singerService.deleteSinger(id);
return R.ok();
}
/**
* 批量删除歌手信息
* @param ids
* @return
*/
@ApiOperation(value = "批量删除歌手信息")
@PostMapping("/batchDelete/{ids}")
public R batchDeleteSinger(@PathVariable("ids") Integer[] ids){
singerService.batchDeleteSinger(ids);
return R.ok();
}
/**
* 添加歌手信息
* @param singer
* @return
*/
@ApiOperation(value = "添加歌手信息")
@PostMapping("addSinger")
public R addSinger(@RequestBody Singer singer){
singerService.addSinger(singer);
return R.ok().data("data",singer);
}
/**
* 修改歌手信息
* @param singer
* @return
*/
@ApiOperation(value = "修改歌手信息")
@PostMapping("update")
public R updateSinger(@RequestBody Singer singer){
singerService.updateSinger(singer);
return R.ok().data("data",singer);
}
}
2.service
package com.example.music.service;
import com.example.music.entity.Singer;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.music.vo.PageResult;
import java.util.List;
/**
* <p>
* 服务类
* </p>
*
* @author mozz
* @since 2022-09-22
*/
public interface ISingerService extends IService<Singer> {
PageResult<Singer> getAllSinger(Long page, Long limit);
PageResult<Singer> getSingerByKeyword(String keyword,Long page, Long limit);
Singer getSingerById(Integer id);
void deleteSinger(Integer id);
void batchDeleteSinger(Integer[] ids);
void addSinger(Singer singer);
void updateSinger(Singer singer);
}
实现类
package com.example.music.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.music.entity.Singer;
import com.example.music.expection.MusicExpection;
import com.example.music.mapper.SingerMapper;
import com.example.music.service.ISingerService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.music.vo.PageResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* <p>
* 服务实现类
* </p>
*
* @author mozz
* @since 2022-09-22
*/
@Service
public class SingerServiceImpl extends ServiceImpl<SingerMapper, Singer> implements ISingerService {
@Resource
private SingerMapper singerMapper;
@Override
public PageResult<Singer> getAllSinger(Long page, Long limit) {
//获取所有歌手信息
List<Singer> singers = singerMapper.getAllSinger(page,limit);
PageResult<Singer> pageResult = new PageResult<>();
pageResult.setRecords(singers);
//总记录数
Integer count = singerMapper.selectList(null).size();
pageResult.setCount(count);
return pageResult;
}
@Override
public PageResult<Singer> getSingerByKeyword(String keyword,Long page, Long limit) {
List<Singer> singers = singerMapper.getSingerByKeyword(keyword,page,limit);
PageResult<Singer> result = new PageResult<>();
result.setRecords(singers);
QueryWrapper wrapper = new QueryWrapper();
wrapper.like("name",keyword);
Integer count = singerMapper.selectList(wrapper).size();
result.setCount(count);
return result;
}
@Override
public Singer getSingerById(Integer id) {
QueryWrapper<Singer> wrapper = new QueryWrapper();
wrapper.eq("id",id);
wrapper.eq("is_delete","0");
Singer singer = singerMapper.selectOne(wrapper);
return singer;
}
@Override
public void deleteSinger(Integer id) {
singerMapper.deleteSinger(id);
}
@Override
public void batchDeleteSinger(Integer[] ids) {
singerMapper.batchDeleteSinger(ids);
}
@Override
public void addSinger(Singer singer) {
//判断是否有该歌手信息
QueryWrapper<Singer> wrapper = new QueryWrapper<>();
wrapper.eq("is_delete","0");
List<Singer> singers = singerMapper.selectList(wrapper);
String name = singer.getName();
for(Singer item: singers){
if(name.equals(item.getName())){
throw new MusicExpection(20000,"该歌手信息已存在");
}
}
singerMapper.insert(singer);
}
@Override
public void updateSinger(Singer singer) {
QueryWrapper<Singer> wrapper = new QueryWrapper<>();
wrapper.eq("id",singer.getId());
singerMapper.update(singer, wrapper);
}
}
3.mapper
package com.example.music.mapper;
import com.example.music.entity.Singer;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* <p>
* Mapper 接口
* </p>
*
* @author mozz
* @since 2022-09-22
*/
public interface SingerMapper extends BaseMapper<Singer> {
List<Singer> getAllSinger(@Param("current") Long page, @Param("size") Long limit);
void deleteSinger(@Param("id") Integer id);
void batchDeleteSinger(Integer[] ids);
List<Singer> getSingerByKeyword(@Param("keyword")String keyword,
@Param("current")Long page,
@Param("size")Long limit);
}
4.xml(有几个sql没用mybatis-plus,mybatis-plus用的不太熟,所以自己写的)
<?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="com.example.music.mapper.SingerMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.example.music.entity.Singer">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="sex" property="sex" />
<result column="pic" property="pic" />
<result column="birth" property="birth" />
<result column="location" property="address" />
<result column="introduction" property="introduction" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, name, sex, pic, birth, location, introduction
</sql>
<select id="getAllSinger" resultType="com.example.music.entity.Singer">
select `id`,`name`,nickname,sex,pic,birth,address,introduction,is_delete
from singer where is_delete = '0' order by id ASC limit #{current},#{size}
</select>
<select id="getSingerByKeyword" resultType="com.example.music.entity.Singer">
select `id`,`name`,nickname,sex,pic,birth,address,introduction,is_delete
from singer where is_delete = '0' and `name` like concat ('%',#{keyword},'%')
order by id ASC limit #{current},#{size}
</select>
<update id="deleteSinger" parameterType="integer">
update singer set is_delete = '1' where id=#{id}
</update>
<update id="batchDeleteSinger">
update singer t set t.is_delete = '1' where
t.id in
<foreach item="ids" collection="array" index="index" open="("
separator="," close=")">
#{ids}
</foreach>
</update>
</mapper>
至此整个后台的增删改查全部完成,启动类不要忘记@MapperScan去映射mapper文件
通过swagger进行测试:
可以看到所有的接口都通过swagger展示出来,去对应的接口中进行测试即可,我这边通过测试都已经成功,接下来就进行前端页面联调开发了。
4.歌手信息前台开发:
本次项目使用的是vue-admin-template模板进行开发,通过nginx进行端口代理,nginx需要修改一下nginx.conf文件中的地址信息。(这个模板我自己改动过)
其中8888就是你yml或者properties文件中设置的端口号,music就是你统一的路径地址。
首先进行前后端联调,联调成功后具体页面如下:
出现该页面说明前后端联调成功,下面进行具体的开发。
(1)首先左侧菜单应该包含用户管理、歌手管理、歌曲管理以及评论管理
该部分主要通过前端路由实现,可以看到,设置好后页面效果如下:
随后就可以在对应的页面进行开发了,路由代码如下:
import Vue from 'vue'
import Router from 'vue-router'
// in development-env not use lazy-loading, because lazy-loading too many pages will cause webpack hot update too slow. so only in production use lazy-loading;
// detail: https://panjiachen.github.io/vue-element-admin-site/#/lazy-loading
Vue.use(Router)
/* Layout */
import Layout from '../views/layout/Layout'
/**
* hidden: true if `hidden:true` will not show in the sidebar(default is false)
* alwaysShow: true if set true, will always show the root menu, whatever its child routes length
* if not set alwaysShow, only more than one route under the children
* it will becomes nested mode, otherwise not show the root menu
* redirect: noredirect if `redirect:noredirect` will no redirect in the breadcrumb
* name:'router-name' the name is used by <keep-alive> (must set!!!)
* meta : {
title: 'title' the name show in submenu and breadcrumb (recommend set)
icon: 'svg-name' the icon show in the sidebar,
}
**/
export const constantRouterMap = [
{ path: '/login', component: () => import('@/views/login/index'), hidden: true },
{ path: '/404', component: () => import('@/views/404'), hidden: true },
{
path: '/',
component: Layout,
redirect: '/dashboard',
name: 'Dashboard',
hidden: true,
children: [{
path: 'dashboard',
component: () => import('@/views/dashboard/index')
}]
},
{
path: '/test',
component: Layout,
name: 'test',
alwaysShow:true,
meta: { title: '用户管理', icon: 'form' },
children: [
{
path: 'userList',
name: 'userList',
component: () => import('@/views/user/userList'),
meta: { title: '用户列表', icon: 'form' },
}
]
},
{
path: '/singer',
component: Layout,
name: 'singer',
alwaysShow:true,
meta: { title: '歌手管理', icon: 'form' },
children: [
{
path: 'singerList',
name: 'singerList',
component: () => import('@/views/singer/singerList'),
meta: { title: '歌手列表', icon: 'form' },
},
]
},
{
path: '/song',
component: Layout,
name: 'song',
alwaysShow:true,
meta: { title: '歌曲管理', icon: 'form' },
children: [
{
path: 'songList',
name: 'songList',
component: () => import('@/views/song/songList'),
meta: { title: '歌曲列表', icon: 'form' },
}
]
},
{
path: '/comment',
component: Layout,
name: 'comment',
alwaysShow:true,
meta: { title: '评论管理', icon: 'form' },
children: [
{
path: 'commentList',
name: 'commentList',
component: () => import('@/views/comment/commentList'),
meta: { title: '评论列表', icon: 'form' },
}
]
},
// {
// path: '/home',
// component: Layout,
// name: 'home',
// meta: { title: '首页', icon: 'form' },
// children: []
// },
{ path: '*', redirect: '/404', hidden: true }
]
export default new Router({
// mode: 'history', //后端支持可开
scrollBehavior: () => ({ y: 0 }),
routes: constantRouterMap
})
然后进入到歌手列表进行页面开发,具体如下
基础的增删改查就是这些了,然后就是如何从后台获取数据了。
前端请求后端接口全部统一写在了api目录下
singer.js文件
import request from '@/utils/request'
export default {
//查询所有歌手
getAllSingers(current, limit){
return request({
url: `/music/singer/lists/${current}/${limit}`,
method: 'get'
})
},
//批量删除
batchdeleteSinger(ids){
return request({
url:'/music/singer/batchDelete/'+ids,
method: 'post'
})
},
//id删除
deleteSinger(id){
return request({
url:'/music/singer/delete/'+id,
method: 'post'
})
},
//根据姓名模糊查询
getSingerByKeyword(keyword,current, limit){
return request({
url: `/music/singer/${keyword}/${current}/${limit}`,
method: 'get'
})
},
//添加歌手
addSinger(singer) {
return request({
url: `/music/singer/addSinger`,
method: 'post',
data: singer
})
},
// 修改歌手信息
updateSinger(singer){
return request({
url:`/music/singer/update`,
method: 'post',
data: singer
})
},
}
然后在singerList.vue中写页面,如下:
<template>
<div class="app-container">
<h3>歌手列表</h3>
<el-button type="danger" @click="batchdelete"
:disabled="this.sels.length === 0">批量删除</el-button>
<el-button type="primary" >新增</el-button>
<el-input
style="width:20%;margin-top: 10px;"
placeholder="请输入姓名模糊查询"
prefix-icon="el-icon-search"
clearable
v-model="keyword">
</el-input>
<el-button type="primary" icon="el-icon-search" @click="loadListData()">搜索</el-button>
<el-table
:data="tableData"
border
fit
highlight-current-row
style="width: 100%;margin-top: 20px;"
@selection-change="handleSelectionChange"
>
<el-table-column
type="selection"
border
width="55">
</el-table-column>
<el-table-column
label="姓名"
width="120">
<template slot-scope="scope">
<span style="margin-left: 10px">{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column
label="昵称"
width="120">
<template slot-scope="scope">
<span style="margin-left: 10px">{{ scope.row.nickname }}</span>
</template>
</el-table-column>
<el-table-column
label="性别"
width="80">
<template slot-scope="scope">
<span style="margin-left: 10px">{{ scope.row.sex == 0 ? '女' : '男' }}</span>
</template>
</el-table-column>
<el-table-column
label="出生日期"
prop="birth"
sortable
width="180">
<template slot-scope="scope">
<span style="margin-left: 10px">{{ scope.row.birth }}</span>
</template>
</el-table-column>
<el-table-column
label="地址"
width="120">
<template slot-scope="scope">
<span style="margin-left: 10px">{{ scope.row.address }}</span>
</template>
</el-table-column>
<el-table-column
label="简介"
width="180">
<template slot-scope="scope">
<span style="margin-left: 10px"><!-- {{ scope.row.introduction }} --></span>
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button
size="mini"
type="success" plain
@click="handleEdit(scope.$index, scope.row)">编辑</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div style="text-align: center;margin-top: 10px;">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="page"
:page-sizes="[5, 10, 15, 20]"
:page-size="limit"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</div>
</template>
<script>
import singer from '@/api/singer/singer'
export default {
data() {
return {
sels:[],
keyword:'',
tableData: [],
page: 1,//开始页
limit: 5, //每页记录数
total: 0, //总记录数
}
},
watch:{
keyword(newValue){
if(!newValue){
this.loadListData();
}
}
},
created() {
this.getList();
},
methods: {
handleEdit(index, row) {
console.log(index, row);
},
handleDelete(id) {
this.$confirm('此操作将永久删除,是否继续?','提示',{
confirmButtonText:'确定',
cancelButtonText: '取消',
type:'warning'
}).then(()=>{
singer.deleteSinger(id).then(res => {
if(res){
this.$message.success("删除成功")
this.getList()
}else{
this.$message.success("删除失败")
}
})
}).catch(() => {})
},
// this.manageCheckPlanDetailTableData 表格数据数组
handleSelectionChange(sels) {
this.sels = sels;
// console.log("选中的值",sels.map((item) => item.id));
},
handleSelectionChange(sels) {
this.sels = sels;
// console.log("选中的值",sels.map((item) => item.id));
},
handleCurrentChange(page) {
this.page = page;
// console.log("page:",this.page)
if(this.keyword){
this.searchInfo();
}else{
this.getList();
}
},
handleSizeChange(limit){
this.limit = limit;
if(this.keyword){
this.searchInfo();
}else{
this.getList();
}
},
loadListData() {
if (this.keyword) {
// 调用模糊查询接口
this.searchInfo();
} else{
// 调用全部接口
this.getList();
}
},
getList(){
singer.getAllSingers(this.page,this.limit).then(res => {
console.log(res.data.data.records)
this.tableData=res.data.data.records
this.total=res.data.data.count
})
},
searchInfo(){
singer.getSingerByKeyword(this.keyword,this.page,this.limit)
.then(res => {
console.log(res.data.data);
this.tableData=res.data.data.records
this.total=res.data.data.count
})
},
batchdelete(){
let ids = []
this.$confirm('此操作将永久删除,是否继续?','提示',{
confirmButtonText:'确定',
cancelButtonText: '取消',
type:'warning'
}).then(()=>{
let ids = this.sels.map((item) => item.id);
const _this = this
singer.batchdeleteSinger(ids).
then(function(resp){
_this.$message({
type: 'success',
message: '删除成功!'
});
_this.getList()
})
}).catch(() => {})
},
}
}
</script>
<style>
</style>
这时可以看到基本的信息功能差不多完成了。
还差一个新增和编辑,通过弹框实现,继续开发。
新增和编辑功能完成,整个的前端页面代码如下:
<template>
<div class="app-container">
<h3>歌手列表</h3>
<el-button type="danger" @click="batchdelete"
:disabled="this.sels.length === 0">批量删除</el-button>
<el-button type="primary" @click="addSinger">新增</el-button>
<el-input
style="width:20%;margin-top: 10px;"
placeholder="请输入姓名模糊查询"
prefix-icon="el-icon-search"
clearable
v-model="keyword">
</el-input>
<el-button type="primary" icon="el-icon-search" @click="loadListData()">搜索</el-button>
<el-table
:data="tableData"
border
fit
highlight-current-row
style="width: 100%;margin-top: 20px;"
@selection-change="handleSelectionChange"
>
<el-table-column
type="selection"
border
width="55">
</el-table-column>
<el-table-column
label="姓名"
width="120">
<template slot-scope="scope">
<span style="margin-left: 10px">{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column
label="昵称"
width="120">
<template slot-scope="scope">
<span style="margin-left: 10px">{{ scope.row.nickname }}</span>
</template>
</el-table-column>
<el-table-column
label="性别"
width="80">
<template slot-scope="scope">
<span style="margin-left: 10px">{{ scope.row.sex == 0 ? '女' : '男' }}</span>
</template>
</el-table-column>
<el-table-column
label="出生日期"
prop="birth"
sortable
width="180">
<template slot-scope="scope">
<span style="margin-left: 10px">{{ scope.row.birth }}</span>
</template>
</el-table-column>
<el-table-column
label="地址"
width="120">
<template slot-scope="scope">
<span style="margin-left: 10px">{{ scope.row.address }}</span>
</template>
</el-table-column>
<el-table-column
label="简介"
width="180">
<template slot-scope="scope">
<span style="margin-left: 10px"><!-- {{ scope.row.introduction }} --></span>
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button
size="mini"
type="success" plain
@click="handleEdit(scope.row.id)">编辑</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div style="text-align: center;margin-top: 10px;">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="page"
:page-sizes="[5, 10, 15, 20]"
:page-size="limit"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
<el-dialog title="歌手信息" :visible.sync="dialogTableVisible" >
<el-form :model="singer" :rules="rules" ref="singer">
<el-form-item label="姓名" :label-width="formLabelWidth" prop="name">
<el-input v-model="singer.name"></el-input>
</el-form-item>
<el-form-item label="昵称" :label-width="formLabelWidth" prop="sex">
<el-input v-model="singer.nickname" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="性别" :label-width="formLabelWidth">
<!-- <el-input v-model="singer.sex" autocomplete="off"></el-input> -->
<el-radio-group v-model="singer.sex">
<el-radio :label="1">男</el-radio>
<el-radio :label="0">女</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="出生日期" :label-width="formLabelWidth" prop="birth">
<el-input v-model="singer.birth" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="地址" :label-width="formLabelWidth">
<el-input v-model="singer.address" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="简介" :label-width="formLabelWidth">
<el-input v-model="singer.introduction" type="textarea" autocomplete="off">
</el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogTableVisible = false">取 消</el-button>
<el-button type="primary" @click="saveOrUpdate">确 定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import singer from '@/api/singer/singer'
export default {
data() {
return {
id:'',
singer:{
id:'',
name:'',
nickname:'',
sex:'',
birth:'',
address:'',
introduction:''
},
sels:[],
keyword:'',
dialogTableVisible:false,
dialogFormVisible:false,
tableData: [],
page: 1,//开始页
limit: 5, //每页记录数
total: 0, //总记录数
formLabelWidth: '100px',
rules: {
name: [
{ required: true, message: '请输入姓名', trigger: 'blur' }
],
sex: [
{ required: true, message: '性别不能为空', trigger: 'blur' }
],
birth: [
{ required: true, message: '出生日期不能为空', trigger: 'blur' }
]
}
}
},
watch:{
keyword(newValue){
if(!newValue){
this.loadListData();
}
}
},
created() {
this.getList();
},
methods: {
addSinger(){
this.dialogTableVisible=true;
this.singer.name = '';
this.singer.nickname = '';
this.singer.sex = '';
this.singer.birth='';
this.singer.address='';
this.singer.introduction='';
},
handleEdit(id) {
this.dialogTableVisible=true;
singer.getSingerById(id).then(res => {
console.log("id:",res.data.data)
this.singer = res.data.data
})
},
handleDelete(id) {
this.$confirm('此操作将永久删除,是否继续?','提示',{
confirmButtonText:'确定',
cancelButtonText: '取消',
type:'warning'
}).then(()=>{
singer.deleteSinger(id).then(res => {
if(res){
this.$message.success("删除成功")
this.getList()
}else{
this.$message.success("删除失败")
}
})
}).catch(() => {})
},
// this.manageCheckPlanDetailTableData 表格数据数组
handleSelectionChange(sels) {
this.sels = sels;
// console.log("选中的值",sels.map((item) => item.id));
},
handleSelectionChange(sels) {
this.sels = sels;
// console.log("选中的值",sels.map((item) => item.id));
},
handleCurrentChange(page) {
this.page = page;
// console.log("page:",this.page)
if(this.keyword){
this.searchInfo();
}else{
this.getList();
}
},
handleSizeChange(limit){
this.limit = limit;
if(this.keyword){
this.searchInfo();
}else{
this.getList();
}
},
loadListData() {
if (this.keyword) {
// 调用模糊查询接口
this.searchInfo();
} else{
// 调用全部接口
this.getList();
}
},
getList(){
singer.getAllSingers(this.page,this.limit).then(res => {
console.log(res.data.data.records)
this.tableData=res.data.data.records
this.total=res.data.data.count
})
},
searchInfo(){
singer.getSingerByKeyword(this.keyword,this.page,this.limit)
.then(res => {
console.log(res.data.data);
this.tableData=res.data.data.records
this.total=res.data.data.count
})
},
batchdelete(){
let ids = []
this.$confirm('此操作将永久删除,是否继续?','提示',{
confirmButtonText:'确定',
cancelButtonText: '取消',
type:'warning'
}).then(()=>{
let ids = this.sels.map((item) => item.id);
const _this = this
singer.batchdeleteSinger(ids).
then(function(resp){
_this.$message({
type: 'success',
message: '删除成功!'
});
_this.getList()
})
}).catch(() => {})
},
saveSinger(){
this.singer.id = this.id;
singer.addSinger(this.singer).then(res => {
console.log("data",res.data.data)
this.dialogTableVisible=false;
this.$message({
type: 'success',
message: '添加歌手成功!'
});
this.getList();
})
},
updateSinger(){
singer.updateSinger(this.singer).then(res => {
this.dialogTableVisible = false;
this.$message({
type: 'success',
message: '修改歌手信息成功!'
});
this.getList()
})
},
saveOrUpdate(){
if(!this.singer.id){
this.saveSinger()
}else{
this.updateSinger()
}
}
}
}
</script>
<style>
</style>
至此整个的springboot基础的前后端增删改查就完成了。
继续更:
由于增删改查完成,涉及到表单功能,比如新增与删除功能,需要对其进行一系列的校验,这边前端是通过设置rules规则完成校验,必要时配合正则表达式。同样后端也需要进行校验,后端采用JSR303校验
JSR303校验
首先引入以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
然后在controller层需要添加@Valid注解,并且传入BindingResult,比如修改歌手信息,歌手名不能为空,在实体类上添加相应的注解
package com.example.music.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* <p>
*
* </p>
*
* @author mozz
* @since 2022-09-22
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="Singer对象", description="")
public class Singer implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@NotBlank(message = "用户名不能为空")
private String name;
private String nickname;
@NotNull
private Integer sex;
private String pic;
@JsonFormat(pattern = "yyyy-MM-dd")
@NotNull(message = "用户出生日期不能为空")
private Date birth;
private String address;
private String introduction;
private String isDelete;
}
controller中:
/**
* 修改歌手信息
* @param singer
* @return
*/
@ApiOperation(value = "修改歌手信息")
@PostMapping("update")
public R updateSinger(@RequestBody @Valid Singer singer, BindingResult result){
if(result.hasErrors()){
Map<String,String> map = new HashMap<>();
result.getFieldErrors().forEach((item) -> {
String message = item.getDefaultMessage();
String field = item.getField();
map.put(field,message);
});
return R.error().data("data",map);
}
singerService.updateSinger(singer);
return R.ok().data("data",singer);
}
最后运行swagger验证:让姓名name为空查看结果
可以看到,返回的信息就是我们实体类上注解对应的message信息,同样的增加功能也需要进行对用的校验操作。至此校验完成。
更多推荐
所有评论(0)