美文网首页初见
WunianEdu系统开发-底层微服务搭建

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

作者: 勿念及时雨 | 来源:发表于2020-05-19 00:43 被阅读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);
        }
    }
    

    相关文章

      网友评论

        本文标题:WunianEdu系统开发-底层微服务搭建

        本文链接:https://www.haomeiwen.com/subject/gqwmohtx.html