初见

WunianEdu系统开发-底层微服务搭建

2020-05-19  本文已影响0人  勿念及时雨

数据库搭建

创建数据库wunian_edu,创建多张数据库表并插入一些数据。建表SQL如下:

CREATE TABLE `edu_chapter` (
  `id` char(19) NOT NULL COMMENT '章节ID',
  `course_id` char(19) NOT NULL COMMENT '课程ID',
  `title` varchar(50) NOT NULL COMMENT '章节名称',
  `sort` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '显示排序',
  `gmt_create` datetime NOT NULL COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_course_id` (`course_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='课程';

CREATE TABLE `edu_course` (
  `id` char(19) NOT NULL COMMENT '课程ID',
  `teacher_id` char(19) NOT NULL COMMENT '课程讲师ID',
  `subject_id` char(19) NOT NULL COMMENT '课程专业ID',
  `subject_parent_id` char(19) NOT NULL COMMENT '课程专业父级ID',
  `title` varchar(50) NOT NULL COMMENT '课程标题',
  `price` decimal(10,4) unsigned NOT NULL DEFAULT '0.0000' COMMENT '课程销售价格,设置为0则可免费观看',
  `lesson_num` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '总课时',
  `cover` varchar(255) CHARACTER SET utf8 NOT NULL COMMENT '课程封面图片路径',
  `buy_count` bigint(10) unsigned NOT NULL DEFAULT '0' COMMENT '销售数量',
  `view_count` bigint(10) unsigned NOT NULL DEFAULT '0' COMMENT '浏览数量',
  `version` bigint(20) unsigned NOT NULL DEFAULT '1' COMMENT '乐观锁',
  `status` varchar(10) NOT NULL DEFAULT 'Draft' COMMENT '课程状态 Draft未发布  Normal已发布',
  `gmt_create` datetime NOT NULL COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_title` (`title`),
  KEY `idx_subject_id` (`subject_id`),
  KEY `idx_teacher_id` (`teacher_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='课程';

CREATE TABLE `edu_course_description` (
  `id` char(19) NOT NULL COMMENT '课程ID',
  `description` text COMMENT '课程简介',
  `gmt_create` datetime NOT NULL COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='课程简介';

CREATE TABLE `edu_subject` (
  `id` char(19) NOT NULL COMMENT '课程类别ID',
  `title` varchar(10) NOT NULL COMMENT '类别名称',
  `parent_id` char(19) NOT NULL DEFAULT '0' COMMENT '父ID',
  `sort` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '排序字段',
  `gmt_create` datetime NOT NULL COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_parent_id` (`parent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='课程科目';

CREATE TABLE `edu_teacher` (
  `id` char(19) NOT NULL COMMENT '讲师ID',
  `name` varchar(20) NOT NULL COMMENT '讲师姓名',
  `intro` varchar(255) NOT NULL COMMENT '讲师资历,一句话说明讲师',
  `career` text COMMENT '讲师简介',
  `level` int(10) unsigned NOT NULL COMMENT '头衔 1高级讲师 2首席讲师',
  `avatar` varchar(255) DEFAULT NULL COMMENT '讲师头像',
  `sort` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '排序',
  `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',
  `gmt_create` datetime NOT NULL COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='讲师';

CREATE TABLE `edu_video` (
  `id` char(19) NOT NULL COMMENT '视频ID',
  `course_id` char(19) NOT NULL COMMENT '课程ID',
  `chapter_id` char(19) NOT NULL COMMENT '章节ID',
  `title` varchar(50) NOT NULL COMMENT '节点名称',
  `video_source_id` varchar(100) DEFAULT NULL COMMENT '云端视频资源',
  `video_original_name` varchar(100) DEFAULT NULL COMMENT '原始文件名称',
  `sort` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '排序字段',
  `play_count` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '播放次数',
  `is_free` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '是否可以试听:0收费 1免费',
  `duration` float NOT NULL DEFAULT '0' COMMENT '视频时长(秒)',
  `status` varchar(20) NOT NULL DEFAULT '' COMMENT '视频状态',
  `size` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '视频源文件大小(字节)',
  `version` bigint(20) unsigned NOT NULL DEFAULT '1' COMMENT '乐观锁',
  `gmt_create` datetime NOT NULL COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_course_id` (`course_id`),
  KEY `idx_chapter_id` (`chapter_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='课程视频';

创建项目

使用IDEA创建SpringBoot项目,项目创建完成后,项目结构如下图 所示。


项目结构

完善代码架构的编写

使用MyBatis-Plus代码自动生成工具来生成基本的CRUD业务代码,只需要一个配置方法,基本业务代码全部搞定。
自动生成代码配置类如下:

//自动生成代码
public class CodeGenerator {

    public static void main(String[] args) {

        //模块名
        String moduleName="edu";
        //1、 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        //规则的配置
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath=System.getProperty("user.dir"); //获取当前项目的路径,这里指的是wunian_edu_api
        gc.setOutputDir(projectPath+"/wunian-edu-edu/src/main/java");//输出目录
        gc.setAuthor("wunian"); //配置文档注释@Author
        gc.setOpen(false);//生成代码后不打开文件夹
        gc.setFileOverride(false);//不覆盖之前生成的文件
        gc.setServiceName("%sService");//把Service接口的首字母I去掉 IUserService  ->UserService
        gc.setIdType(IdType.ID_WORKER_STR);//主键策略
        gc.setDateType(DateType.ONLY_DATE);//日期类型
        gc.setSwagger2(true);//自动开启Swagger配置
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/wunian_"+moduleName+"?characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(moduleName);
        pc.setParent("com.wunian");
        pc.setController("controller");//controller层包名
        pc.setService("service");//service层包名
        pc.setEntity("pojo");//实体类层包名
        pc.setMapper("mapper");//dao层的包名
        mpg.setPackageInfo(pc);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        //要生成哪张表对应的类(重要,以后要改配置就改这里)
        strategy.setInclude(moduleName+"_\\w*");//所有edu开头的表都自动生成
        strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表生成到实体类的策略,下划线转驼峰
        strategy.setTablePrefix(pc.getModuleName()+"_");//表前缀 edu这个前缀不生成在类中 edu_teacher->Teacher

        strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库列名生成到实体类属性的策略,下划线转驼峰
        strategy.setEntityLombokModel(true);//自动生成lombok注解

        strategy.setLogicDeleteFieldName("is_deleted");//配置逻辑删除字段
        strategy.setEntityBooleanColumnRemoveIsPrefix(true);//去掉布尔值的is_前缀

        //自动填充策略 创建时间和更新时间
        TableFill gmt_create=new TableFill("gmt_create", FieldFill.INSERT);
        TableFill gmt_modified=new TableFill("gmt_modified",FieldFill.INSERT_UPDATE);

        ArrayList<TableFill> tableFills=new ArrayList<>();
        tableFills.add(gmt_create);
        tableFills.add(gmt_modified);
        strategy.setTableFillList(tableFills);

        //乐观锁
        strategy.setVersionFieldName("version");//配置乐观锁字段
        strategy.setRestControllerStyle(true);//restful风格的 api
        strategy.setControllerMappingHyphenStyle(true);//使用_连接驼峰  /user/hello_world
        mpg.setStrategy(strategy);

        //2、执行代码生成器
        mpg.execute();
    }
}

配置Swagger进行测试

在edu模块中添加Swagger配置类,代码如下:

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket webApiConfig(){
        //过滤掉 admin 下的请求
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")
                .apiInfo(webApiInfo())
                .select()
                .paths(Predicates.not(PathSelectors.regex("/admin/.*")))
                .paths(Predicates.not(PathSelectors.regex("/error.*")))
                .build();
    }

    @Bean
    public Docket adminApiConfig(){
        //只选取 /admin 下的请求
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("adminApi")
                .apiInfo(adminApiInfo())
                .select()
                .paths(Predicates.and(PathSelectors.regex("/admin/.*")))
                .build();
    }

    private ApiInfo webApiInfo(){
        return new ApiInfoBuilder()
                .title("网站-课程中心API文档")
                .description("本文档描述了课程中心微服务接口定义")
                .version("1.0")
                .contact(new Contact("Coding", "http://icodingedu.com", "24736743@qq.com"))
                .build();
    }

    private ApiInfo adminApiInfo(){
        return new ApiInfoBuilder()
                .title("后台管理系统-课程中心API文档")
                .description("本文档描述了后台管理系统课程中心微服务接口定义")
                .version("1.0")
                .contact(new Contact("Coding", "http://icodingedu.com", "24736743@qq.com"))
                .build();
    }
}

注意编写后端接口时,Swagger注释应该一并写好,尽量不要返工,一次性搞定接口文档的问题最好。

封装接口统一返回数据格式

真正的微服务是提供给多端使用的,项目中我们会将响应的数据封装成JSON返回,一般会将所有接口的数据格式统一,这样可以使前端对数据的操作更一致、轻松。
一般情况下,统一返回数据格式并不固定,只要能描述清楚返回的数据状态以及要返回的具体数据就行。但是一般会包含状态码、返回消息、数据这些内容。
例如,我们的系统要求返回的基本数据格式如下:
列表

{ "success": true, "code": 20000, "message": "成功", "data": { "items": [ { "id": "1", "name": "coding", "intro": "coding老师有点帅" } ] }
}

分页

{ "success": true, "code": 20000, "message": "成功", "data": { "total": 17, "rows": [ { "id": "1", "name": "coding", "intro": "coding老师有点帅" } ] } }

无返回数据

{ "success": true, "code": 20000, "message": "成功", "data": {} }

失败

{ "success": false, "code": 20001, "message": "失败", "data": {} }

因此,我们可以定义统一返回结果格式为:

{ 
  "success": 布尔, //响应是否成功 
  "code": 数字, //响应码 
  "message": 字符串, //返回消息 
  "data": HashMap //返回数据,放在键值对中 
}

在common模块中定义VO对象R,代码如下:

//无论什么接口,返回值永远是R
@Data
@ApiModel(value = "全局的统一返回结果")
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<>();

    public R(){}

    //ok
    public static R ok(){
        R r=new R();
        r.setSuccess(ResultCodeEnum.SUCCESS.getSuccess());
        r.setCode(ResultCodeEnum.SUCCESS.getCode());
        r.setMessage(ResultCodeEnum.SUCCESS.getMessage());
        return r;
    }
    //error
    public static R error(){
        R r=new R();
        r.setSuccess(ResultCodeEnum.UNKNOW_REASON.getSuccess());
        r.setCode(ResultCodeEnum.UNKNOW_REASON.getCode());
        r.setMessage(ResultCodeEnum.UNKNOW_REASON.getMessage());
        return r;
    }
    //自定义错误码
    public static R setResult(ResultCodeEnum resultCodeEnum){
        R r=new R();
        r.setSuccess(resultCodeEnum.getSuccess());
        r.setCode(resultCodeEnum.getCode());
        r.setMessage(resultCodeEnum.getMessage());
        return r;
    }
    //以下方法用于链式编程
    public R success(Boolean success){
        this.setSuccess(success);
        return this;
    }
    public R message(String message){
        this.setMessage(message);
        return this;
    }
    public R code(Integer code){
        this.setCode(code);
        return this;
    }
    public R data(String key,Object value){
        this.data.put(key, value);
        return this;
    }
    public R data(Map<String,Object> map){
        this.setData(map);
        return this;
    }
}

分页查询和条件查询

service继承IService,查询字段可以单独定义为一个Query类,在service实现类中使用QueryWrapper对象来实现动态SQL,在实现类中可以使用baseMapper.selectPage(pageParam,queryWrapper)方法来实现分页查询和条件查询。
TeacherQuery类代码如下:

@ApiModel(value = "Teacher查询对象",description = "讲师查询对象封装")
@Data
public class TeacherQuery implements Serializable {

    private static final long serializableUID=1L;

    @ApiModelProperty(value = "讲师名称,模糊查询")
    private String name;

    @ApiModelProperty(value = "头衔 1 高级讲师 2 顶级讲师")
    private Integer level;

    @ApiModelProperty(value = "入驻时间开始")
    private String begin;//String 类型,前端传递的数据不需要做类型转换

    @ApiModelProperty(value = "入驻时间结束")
    private String end;
}

TeacherServiceImpl类代码如下:

@Service
public class TeacherServiceImpl extends ServiceImpl<TeacherMapper, Teacher> implements TeacherService {
    //MP是让我们可以通过面向对象的方式编写sql
    //pageParam 分页  teacherQuery 条件查询
    @Override
    public void pageQuery(Page<Teacher> pageParam, TeacherQuery teacherQuery) {
        QueryWrapper<Teacher> queryWrapper=new QueryWrapper<>();
        queryWrapper.orderByAsc("sort");
        if(teacherQuery==null){
            baseMapper.selectPage(pageParam,queryWrapper);
            return;
        }
        String name=teacherQuery.getName();
        Integer level=teacherQuery.getLevel();
        String begin=teacherQuery.getBegin();
        String end=teacherQuery.getEnd();

        if(!StringUtils.isEmpty(name)){
            queryWrapper.like("name",name);//模糊匹配
        }
        if(!StringUtils.isEmpty(level)){
            queryWrapper.eq("level",level);//等于
        }
        if(!StringUtils.isEmpty(begin)){
            queryWrapper.ge("gmt_create",begin);//大于开始时间
        }
        if(!StringUtils.isEmpty(end)){
            queryWrapper.le("gmt_create",end);//小于结束时间
        }
        baseMapper.selectPage(pageParam,queryWrapper);
    }
}
上一篇下一篇

猜你喜欢

热点阅读