zoukankan      html  css  js  c++  java
  • springcloud项目实战之在线教育网站开发

    在线教育

    1、项目环境部署

    • 项目结构:
    • online_parent:根目录(父工程),管理四个子模块:
      • canal_client:canal数据库表同步模块(统计同步数据)
      • common:公共模块父节点
        • common_util:工具类模块,所有模块都可以依赖于它
        • service_base:service服务的base包,包含service服务的公共配置类,所有service模块依赖于它
        • spring_security:认证与授权模块,需要认证授权的service服务依赖于它
      • infrastructure:基础服务模块父节点
        • api_gateway:api网关服务
      • service:api接口服务父节点
        • service_edu:教学相关api接口服务
        • service_oss:阿里云oss api接口服务
        • service_acl:用户权限管理api接口服务(用户管理、角色管理和权限管理等)
        • service_cms:cms api接口服务
        • service_sms:短信api接口服务
        • service_trade:订单和支付相关api接口服务
        • service_statistics:统计报表api接口服务
        • service_ucenter:会员api接口服务
        • service_vod:视频点播api接口服务

    Ⅰ、父项目依赖online_parent

        <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.cxd</groupId>
        <artifactId>online_parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>online_parent</name>
        <description>online edu</description>
    
        <properties>
            <java.version>1.8</java.version>
            <mybatis-plus.version>3.3.1.tmp</mybatis-plus.version>
            <velocity.version>2.2</velocity.version>
            <swagger.version>2.7.0</swagger.version>
            <aliyun.oss.version>3.1.0</aliyun.oss.version>
            <jodatime.version>2.10.5</jodatime.version>
            <commons-fileupload.version>1.3.3</commons-fileupload.version>
            <commons-io.version>2.6</commons-io.version>
            <commons-lang.version>3.9</commons-lang.version>
            <httpclient.version>4.5.1</httpclient.version>
            <jwt.version>0.7.0</jwt.version>
            <aliyun-java-sdk-core.version>4.5.1</aliyun-java-sdk-core.version>
            <aliyun-java-sdk-vod.version>2.15.10</aliyun-java-sdk-vod.version>
            <aliyun-sdk-vod-upload.version>1.4.12</aliyun-sdk-vod-upload.version>
            <fastjson.version>1.2.28</fastjson.version>
            <gson.version>2.8.2</gson.version>
            <json.version>20170516</json.version>
            <commons-dbutils.version>1.7</commons-dbutils.version>
            <canal.client.version>1.1.0</canal.client.version>
            <docker.image.prefix>zx</docker.image.prefix>
            <alibaba.easyexcel.version>2.1.1</alibaba.easyexcel.version>
            <apache.xmlbeans.version>3.1.0</apache.xmlbeans.version>
        </properties>
    
        <dependencyManagement>
            <dependencies>
                <!--Spring Cloud-->
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>Hoxton.SR1</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
                <dependency>
                    <groupId>com.alibaba.cloud</groupId>
                    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                    <version>2.1.0.RELEASE</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
                <!--mybatis-plus 持久层-->
                <dependency>
                    <groupId>com.baomidou</groupId>
                    <artifactId>mybatis-plus-boot-starter</artifactId>
                    <version>${mybatis-plus.version}</version>
                </dependency>
                <dependency>
                    <groupId>com.baomidou</groupId>
                    <artifactId>mybatis-plus-generator</artifactId>
                    <version>${mybatis-plus.version}</version>
                </dependency>
                <!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 -->
                <dependency>
                    <groupId>org.apache.velocity</groupId>
                    <artifactId>velocity-engine-core</artifactId>
                    <version>${velocity.version}</version>
                </dependency>
                <!--swagger-->
                <dependency>
                    <groupId>io.springfox</groupId>
                    <artifactId>springfox-swagger2</artifactId>
                    <version>${swagger.version}</version>
                </dependency>
                <!--swagger ui-->
                <dependency>
                    <groupId>io.springfox</groupId>
                    <artifactId>springfox-swagger-ui</artifactId>
                    <version>${swagger.version}</version>
                </dependency>
                <!--aliyunOSS-->
                <dependency>
                    <groupId>com.aliyun.oss</groupId>
                    <artifactId>aliyun-sdk-oss</artifactId>
                    <version>${aliyun.oss.version}</version>
                </dependency>
                <!--日期时间工具-->
                <dependency>
                    <groupId>joda-time</groupId>
                    <artifactId>joda-time</artifactId>
                    <version>${jodatime.version}</version>
                </dependency>
                <!--文件上传-->
                <dependency>
                    <groupId>commons-fileupload</groupId>
                    <artifactId>commons-fileupload</artifactId>
                    <version>${commons-fileupload.version}</version>
                </dependency>
                <!--commons-io-->
                <dependency>
                    <groupId>commons-io</groupId>
                    <artifactId>commons-io</artifactId>
                    <version>${commons-io.version}</version>
                </dependency>
                <!--commons-lang3-->
                <dependency>
                    <groupId>org.apache.commons</groupId>
                    <artifactId>commons-lang3</artifactId>
                    <version>${commons-lang.version}</version>
                </dependency>
                <!--httpclient-->
                <dependency>
                    <groupId>org.apache.httpcomponents</groupId>
                    <artifactId>httpclient</artifactId>
                    <version>${httpclient.version}</version>
                </dependency>
                <!-- JWT -->
                <dependency>
                    <groupId>io.jsonwebtoken</groupId>
                    <artifactId>jjwt</artifactId>
                    <version>${jwt.version}</version>
                </dependency>
                <!--     aliyun-->
                <dependency>
                    <groupId>com.aliyun</groupId>
                    <artifactId>aliyun-java-sdk-core</artifactId>
                    <version>${aliyun-java-sdk-core.version}</version>
                </dependency>
                <dependency>
                    <groupId>com.aliyun</groupId>
                    <artifactId>aliyun-java-sdk-vod</artifactId>
                    <version>${aliyun-java-sdk-vod.version}</version>
                </dependency>
                <!--            <dependency>-->
                <!--                <groupId>com.aliyun</groupId>-->
                <!--                <artifactId>aliyun-sdk-vod-upload</artifactId>-->
                <!--                <version>${aliyun-sdk-vod-upload.version}</version>-->
                <!--            </dependency>-->
                <!--  json-->
                <dependency>
                    <groupId>com.alibaba</groupId>
                    <artifactId>fastjson</artifactId>
                    <version>${fastjson.version}</version>
                </dependency>
                <dependency>
                    <groupId>org.json</groupId>
                    <artifactId>json</artifactId>
                    <version>${json.version}</version>
                </dependency>
                <dependency>
                    <groupId>com.google.code.gson</groupId>
                    <artifactId>gson</artifactId>
                    <version>${gson.version}</version>
                </dependency>
    
                <dependency>
                    <groupId>com.alibaba</groupId>
                    <artifactId>easyexcel</artifactId>
                    <version>${alibaba.easyexcel.version}</version>
                </dependency>
                <dependency>
                    <groupId>org.apache.xmlbeans</groupId>
                    <artifactId>xmlbeans</artifactId>
                    <version>${apache.xmlbeans.version}</version>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    

    Ⅰ-Ⅱ、子工程依赖common

        <parent>
            <artifactId>online_parent</artifactId>
            <groupId>com.cxd</groupId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>common</artifactId>
        <packaging>pom</packaging>
        <modules>
            <module>service_base</module>
        </modules>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!--mybatis-plus-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
            </dependency>
            <!--lombok用来简化实体类:需要安装lombok插件-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
            <!--swagger-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
            </dependency>
            <!-- spring boot redis缓存引入 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
            <!-- lecttuce 缓存连接池-->
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-pool2</artifactId>
            </dependency>
        </dependencies>
    

    Ⅰ-Ⅱ-Ⅰ、子包依赖common_util

        <parent>
            <artifactId>common</artifactId>
            <groupId>com.cxd</groupId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>common_util</artifactId>
    
        <dependencies>
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
            </dependency>
    
            <dependency>
                <groupId>joda-time</groupId>
                <artifactId>joda-time</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.session</groupId>
                <artifactId>spring-session-data-redis</artifactId>
            </dependency>
    
            <!--httpclient-->
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpclient</artifactId>
            </dependency>
        </dependencies>
    

    Ⅰ-Ⅱ-Ⅱ、子包依赖service_base

        <parent>
            <artifactId>common</artifactId>
            <groupId>com.cxd</groupId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>service_base</artifactId>
    
        <dependencies>
            <dependency>
                <groupId>com.cxd</groupId>
                <artifactId>common_util</artifactId>
                <version>0.0.1-SNAPSHOT</version>
            </dependency>
        </dependencies>
    

    Ⅰ-Ⅲ、子工程依赖service

        <parent>
            <artifactId>online_parent</artifactId>
            <groupId>com.cxd</groupId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>service</artifactId>
        <packaging>pom</packaging>
        <modules>
            <module>service_edu</module>
        </modules>
    
        <dependencies>
            <!--服务容错-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            </dependency>
            <!--服务调用-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
            <!--服务注册-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>com.cxd</groupId>
                <artifactId>service_base</artifactId>
                <version>0.0.1-SNAPSHOT</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!--mybatis-plus-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-generator</artifactId>
            </dependency>
            <!--mysql-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            <!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 -->
            <dependency>
                <groupId>org.apache.velocity</groupId>
                <artifactId>velocity-engine-core</artifactId>
            </dependency>
            <!--swagger-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
            </dependency>
            <!--日期时间工具-->
            <dependency>
                <groupId>joda-time</groupId>
                <artifactId>joda-time</artifactId>
            </dependency>
            <!--lombok用来简化实体类:需要安装lombok插件-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
            <dependency>
                <groupId>commons-fileupload</groupId>
                <artifactId>commons-fileupload</artifactId>
            </dependency>
            <!--httpclient-->
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpclient</artifactId>
            </dependency>
            <!--commons-io-->
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
            </dependency>
            <!--json-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
            </dependency>
            <dependency>
                <groupId>org.json</groupId>
                <artifactId>json</artifactId>
            </dependency>
            <dependency>
                <groupId>com.google.code.gson</groupId>
                <artifactId>gson</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    

    Ⅰ-Ⅲ-Ⅰ、子包service_edu

        <parent>
            <artifactId>service</artifactId>
            <groupId>com.cxd</groupId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>service_edu</artifactId>
    
        <dependencies>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>easyexcel</artifactId>
            </dependency>
            <dependency>
                <groupId>org.apache.xmlbeans</groupId>
                <artifactId>xmlbeans</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <!-- 项目打包时会将java目录中的*.xml文件也进行打包 -->
            <resources>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>false</filtering>
                </resource>
            </resources>
        </build>
    

    2、edu模块接口开发

    • application.yml
    server:
      port: 8110 # 服务端口
    spring:
      profiles:
        active: dev
      application:
        name: service_edu # 服务名称
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver # mysql数据库连接
        url: jdbc:mysql://localhost:3306/db_guli_edu?serverTimezone=GMT%2B8
        username: root
        data-password: 123456
      jackson:
        date-format: yyyy-MM-dd HH:mm:ss
        time-zone: GMT+8
    
    # mybatis日志
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    

    Ⅰ、mp生成后端代码

    package com.cxd.service.edu.test;
    
    public class CodeGenerator {
        @Test
        public void genCode() {
            String prefix = "db_guli_";
            String moduleName = "edu";
            // 1、创建代码生成器
            AutoGenerator mpg = new AutoGenerator();
            // 2、全局配置
            GlobalConfig gc = new GlobalConfig();
            String projectPath = System.getProperty("user.dir");
            gc.setOutputDir(projectPath + "/src/main/java");
            gc.setAuthor("cxd");
            gc.setOpen(true); //生成后是否打开资源管理器
            gc.setFileOverride(true); //重新生成时文件是否覆盖
            gc.setServiceName("%sService"); //去掉Service接口的首字母I
            gc.setIdType(IdType.ASSIGN_ID); //主键策略
            gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
            gc.setSwagger2(true);//开启Swagger2模式
            mpg.setGlobalConfig(gc);
            // 3、数据源配置
            DataSourceConfig dsc = new DataSourceConfig();
            dsc.setUrl("jdbc:mysql://localhost:3306/" + prefix + moduleName + "?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");
            dsc.setDriverName("com.mysql.cj.jdbc.Driver");
            dsc.setUsername("root");
            dsc.setPassword("123456");
            dsc.setDbType(DbType.MYSQL);
            mpg.setDataSource(dsc);
            // 4、包配置
            PackageConfig pc = new PackageConfig();
            pc.setModuleName(moduleName); //模块名
            pc.setParent("com.cxd.service");
            pc.setController("controller");
            pc.setEntity("entity");
            pc.setService("service");
            pc.setMapper("mapper");
            mpg.setPackageInfo(pc);
            // 5、策略配置
            StrategyConfig strategy = new StrategyConfig();
            strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名
            strategy.setTablePrefix(moduleName + "_");//设置表前缀不生成
            strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射
            strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true)
            strategy.setLogicDeleteFieldName("is_deleted");//逻辑删除字段名
            strategy.setEntityBooleanColumnRemoveIsPrefix(true);//去掉布尔值的is_前缀
            //自动填充
            TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
            TableFill gmtModified = new TableFill("gmt_modified", FieldFill.INSERT_UPDATE);
            ArrayList<TableFill> tableFills = new ArrayList<>();
            tableFills.add(gmtCreate);
            tableFills.add(gmtModified);
            strategy.setTableFillList(tableFills);
            strategy.setRestControllerStyle(true); //restful api风格控制器
            strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符
            mpg.setStrategy(strategy);
    
            //设置BaseEntity
            strategy.setSuperEntityClass("BaseEntity");
            // 填写BaseEntity中的公共字段
            strategy.setSuperEntityColumns("id", "gmt_create", "gmt_modified");
            // 6、执行
            mpg.execute();
        }
    }
    

    Ⅱ、配置mp分页插件

    package com.cxd.service.base.config;
    
    @EnableTransactionManagement
    @Configuration
    @MapperScan("com.cxd.service.*.mapper")
    public class MybatisPlusConfig {
        /**
        * 分页插件
        * */
        @Bean
        public PaginationInterceptor paginationInterceptor(){
            return new PaginationInterceptor();
        }
    }
    
    

    Ⅲ、配置swagger生成接口文档([http://localhost:8110/swagger-ui.html#!/])

    package com.cxd.service.base.config;
    
    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {
        @Bean
        public Docket webApiConfig(){
            return new Docket(DocumentationType.SWAGGER_2)
                    .apiInfo(webApiInfo())
                    .groupName("webApi")
                    .select()
                    .paths(Predicates.and(PathSelectors.regex("/api/.*")))
                    .build();
        }
    
        @Bean
        public Docket adminApiConfig(){
            return new Docket(DocumentationType.SWAGGER_2)
                    .apiInfo(adminApiInfo())
                    .groupName("adminApi")
                    .select()
                    .paths(Predicates.and(PathSelectors.regex("/admin/.*")))
                    .build();
        }
    
        private ApiInfo webApiInfo(){
            return new ApiInfoBuilder()
                    .title("网站的API文档")
                    .description("api接口定义")
                    .version("1.0")
    //                .contact(new Contact("cxd","http://www.qq.com","243341364@126.com"))
                    .build();
        }
    
        private ApiInfo adminApiInfo(){
            return new ApiInfoBuilder()
                    .title("后台的API文档")
                    .description("后台api接口定义")
                    .version("1.0")
    //                .contact(new Contact("cxd","http://www.qq.com","243341364@126.com"))
                    .build();
        }
    }
    
    

    Ⅳ、编写BaseEntity类(其他实体类均继承此类)

    package com.cxd.service.base.model;
    
    @Data
    @EqualsAndHashCode(callSuper = false)
    @Accessors(chain = true)
    public class BaseEntity implements Serializable {
    
        private static final long serialVersionUID=1L;
    
        @ApiModelProperty(value = "讲师ID")
        @TableId(value = "id", type = IdType.ASSIGN_ID)
        private String id;
    
        @ApiModelProperty(value = "创建时间", example = "2020-11-11 8:00:00")
        @TableField(fill = FieldFill.INSERT)
        @JsonDeserialize(using = LocalDateTimeDeserializer.class)
        @JsonSerialize(using = LocalDateTimeSerializer.class)
        private LocalDateTime gmtCreate;
    
        @ApiModelProperty(value = "更新时间", example = "2020-11-11 8:00:00")
        @TableField(fill = FieldFill.INSERT_UPDATE)
        @JsonDeserialize(using = LocalDateTimeDeserializer.class)
        @JsonSerialize(using = LocalDateTimeSerializer.class)
        private LocalDateTime gmtModified;
    }
    
    

    Ⅴ、在common包下定义结果返回状态码以及统一返回结果

    • R.class
    @Data
    @NoArgsConstructor
    @ApiModel("全局统一返回结果")
    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 static R ok() {
            R r = new R();
            r.setSuccess(ResultCodeEnum.SUCCESS.getSuccess());
            r.setCode(ResultCodeEnum.SUCCESS.getCode());
            r.setMessage(ResultCodeEnum.SUCCESS.getMessage());
            return r;
        }
    
        public static R error() {
            R r = new R();
            r.setSuccess(ResultCodeEnum.UNKNOWN_REASON.getSuccess());
            r.setCode(ResultCodeEnum.UNKNOWN_REASON.getCode());
            r.setMessage(ResultCodeEnum.UNKNOWN_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.success(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;
        }
    }
    
    
    • ResultCodeEnum.class
    @Getter
    @ToString
    public enum ResultCodeEnum {
    
        SUCCESS(true, 20000, "成功"),
        UNKNOWN_REASON(false, 20001, "未知错误"),
    
        BAD_SQL_GRAMMAR(false, 21001, "sql语法错误"),
        JSON_PARSE_ERROR(false, 21002, "json解析异常"),
        PARAM_ERROR(false, 21003, "参数不正确"),
    
        FILE_UPLOAD_ERROR(false, 21004, "文件上传错误"),
        FILE_DELETE_ERROR(false, 21005, "文件刪除错误"),
        EXCEL_DATA_IMPORT_ERROR(false, 21006, "Excel数据导入错误"),
    
        VIDEO_UPLOAD_ALIYUN_ERROR(false, 22001, "视频上传至阿里云失败"),
        VIDEO_UPLOAD_TOMCAT_ERROR(false, 22002, "视频上传至业务服务器失败"),
        VIDEO_DELETE_ALIYUN_ERROR(false, 22003, "阿里云视频文件删除失败"),
        FETCH_VIDEO_UPLOADAUTH_ERROR(false, 22004, "获取上传地址和凭证失败"),
        REFRESH_VIDEO_UPLOADAUTH_ERROR(false, 22005, "刷新上传地址和凭证失败"),
        FETCH_PLAYAUTH_ERROR(false, 22006, "获取播放凭证失败"),
    
        URL_ENCODE_ERROR(false, 23001, "URL编码失败"),
        ILLEGAL_CALLBACK_REQUEST_ERROR(false, 23002, "非法回调请求"),
        FETCH_ACCESSTOKEN_FAILD(false, 23003, "获取accessToken失败"),
        FETCH_USERINFO_ERROR(false, 23004, "获取用户信息失败"),
        LOGIN_ERROR(false, 23005, "用户名或密码不正确"),
    
        COMMENT_EMPTY(false, 24006, "评论内容必须填写"),
    
        PAY_RUN(false, 25000, "支付中"),
        PAY_UNIFIEDORDER_ERROR(false, 25001, "统一下单错误"),
        PAY_ORDERQUERY_ERROR(false, 25002, "查询支付结果错误"),
    
        ORDER_EXIST_ERROR(false, 25003, "课程已购买"),
    
        GATEWAY_ERROR(false, 26000, "服务不能访问"),
    
        CODE_ERROR(false, 28000, "验证码错误"),
        LOGIN_DISABLED_ERROR(false, 28002, "该用户已被禁用"),
        REGISTER_MOBLE_ERROR(false, 28003, "手机号已被注册"),
        LOGIN_AUTH(false, 28004, "请先进行登录"),
        LOGIN_ACL(false, 28005, "没有权限"),
        SMS_SEND_ERROR(false, 28006, "短信发送失败"),
        SMS_SEND_ERROR_BUSINESS_LIMIT_CONTROL(false, 28007, "短信发送过于频繁");
    
        private final Boolean success;
    
        private final Integer code;
    
        private final String message;
    
        ResultCodeEnum(Boolean success, Integer code, String message) {
            this.success = success;
            this.code = code;
            this.message = message;
        }
    }
    
    

    Ⅵ、TeacherController实现CRUD

    /**
     * <p>
     * 讲师 前端控制器
     * </p>
     *
     * @author cxd
     * @since 2020-11-15
     */
    @Api(tags = "讲师管理")
    @RestController
    @RequestMapping("admin/edu/teacher")
    public class TeacherController {
    
        @Autowired
        private TeacherService teacherService;
    
    
        @ApiOperation(value = "查询所有老师列表")
        @GetMapping("list")
        public R listAll() {
            List<Teacher> list = teacherService.list();
            return R.ok().data("items", list);
        }
    
        @ApiOperation(value = "根据ID删除讲师", notes = "根据id删除,逻辑删除")
        @DeleteMapping("remove/{id}")
        public R removeById(@ApiParam("讲师ID") @PathVariable String id) {
            boolean result = teacherService.removeById(id);
            if (result) {
                return R.ok().message("删除成功");
            } else {
                return R.error().message("教师不存在");
            }
        }
    
        @ApiOperation("讲师分页列表")
        @GetMapping("list/{page}/{limit}")
        public R listPage(@ApiParam(value = "当前页码", required = true) @PathVariable long page,
                          @ApiParam(value = "每页数量", required = true) @PathVariable long limit,
                          @ApiParam("查询对象") TeacherQueryVo teacherQueryVo) {
            Page<Teacher> pageParam = new Page<>(page, limit);
            IPage<Teacher> pageModel = teacherService.selectPage(pageParam, teacherQueryVo);
            List<Teacher> records = pageModel.getRecords();
            long total = pageModel.getTotal();
            return R.ok().data("data", total).data("row", records);
        }
    
        @ApiOperation("新增导师")
        @PostMapping("save")
        public R save(@ApiParam(value = "讲师对象", required = true) @RequestBody Teacher teacher) {
            boolean result = teacherService.save(teacher);
            if (result){
                return R.ok().message("保存成功");
            }else{
                return R.error().message("操作失败");
            }
        }
    
        @ApiOperation("更新导师")
        @PutMapping("update")
        public R updateById(@ApiParam(value = "讲师对象",required = true) @RequestBody Teacher teacher) {
            boolean result = teacherService.updateById(teacher);
            if (result) {
                return R.ok().message("更新成功");
            } else {
                return R.error().message("数据不存在失败");
            }
        }
    
        @ApiOperation("根据id获取导师信息")
        @GetMapping("get/{id}")
        public R getById(@ApiParam(value = "导师对象",required = true) @PathVariable String id) {
            Teacher teacher = teacherService.getById(id);
            if (teacher != null) {
                return R.ok().data("item", teacher);
            } else {
                return R.error().message("数据不存在");
            }
        }
    
    

    Ⅶ、配置logback.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration scan="true" scanPeriod="10 seconds">
    
        <contextName>logback</contextName>
    
        <property name="log.path" value="./logger/edu"/>
    
        <!--控制台日志格式:彩色日志-->
        <!-- magenta:洋红 -->
        <!-- boldMagenta:粗红-->
        <!-- cyan:青色 -->
        <!-- white:白色 -->
        <!-- magenta:洋红 -->
        <property name="CONSOLE_LOG_PATTERN"
                  value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>
    
        <!--文件日志格式-->
        <property name="FILE_LOG_PATTERN"
                  value="%date{yyyy-MM-dd HH:mm:ss} |%-5level |%thread |%file:%line |%logger |%msg%n"/>
    
        <!--编码-->
        <property name="ENCODING" value="UTF-8"/>
    
        <!--输出到控制台-->
        <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                <!--日志级别-->
                <level>INFO</level>
            </filter>
            <encoder>
                <!--日志格式-->
                <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
                <!--日志字符集-->
                <charset>${ENCODING}</charset>
            </encoder>
        </appender>
    
        <!--输出到文件-->
        <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <!--日志过滤器:此日志文件只记录INFO级别的-->
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>INFO</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
            <!-- 正在记录的日志文件的路径及文件名 -->
            <file>${log.path}/log_info.log</file>
            <encoder>
                <pattern>${FILE_LOG_PATTERN}</pattern>
                <charset>${ENCODING}</charset>
            </encoder>
            <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!-- 每天日志归档路径以及格式 -->
                <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>500MB</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
                <!--日志文件保留天数-->
                <maxHistory>15</maxHistory>
            </rollingPolicy>
        </appender>
    
        <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <!-- 日志过滤器:此日志文件只记录WARN级别的 -->
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>WARN</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
            <!-- 正在记录的日志文件的路径及文件名 -->
            <file>${log.path}/log_warn.log</file>
            <encoder>
                <pattern>${FILE_LOG_PATTERN}</pattern>
                <charset>${ENCODING}</charset> <!-- 此处设置字符集 -->
            </encoder>
            <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>100MB</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
                <!--日志文件保留天数-->
                <maxHistory>15</maxHistory>
            </rollingPolicy>
        </appender>
    
        <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <!-- 日志过滤器:此日志文件只记录ERROR级别的 -->
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>ERROR</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
            <!-- 正在记录的日志文件的路径及文件名 -->
            <file>${log.path}/log_error.log</file>
            <encoder>
                <pattern>${FILE_LOG_PATTERN}</pattern>
                <charset>${ENCODING}</charset> <!-- 此处设置字符集 -->
            </encoder>
            <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>100MB</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
                <!--日志文件保留天数-->
                <maxHistory>15</maxHistory>
            </rollingPolicy>
        </appender>
    
        <!--开发环境-->
        <springProfile name="dev">
            <!--可以灵活设置此处,从而控制日志的输出-->
            <root level="DEBUG">
                <appender-ref ref="CONSOLE"/>
                <appender-ref ref="INFO_FILE"/>
                <appender-ref ref="WARN_FILE"/>
                <appender-ref ref="ERROR_FILE"/>
            </root>
        </springProfile>
    
        <!--生产环境-->
        <springProfile name="pro">
            <root level="ERROR">
                <appender-ref ref="ERROR_FILE"/>
            </root>
        </springProfile>
    </configuration>
    
    

    Ⅷ、service_base包中配置handler

    @Component
    @Slf4j
    public class CommonMetaObjectHandler implements MetaObjectHandler {
    
        @Override
        //默认填充字段
        public void insertFill(MetaObject metaObject) {
            log.info("start insert fill ....");
            this.strictInsertFill(metaObject, "gmtCreate", LocalDateTime.class, LocalDateTime.now());
            this.strictUpdateFill(metaObject, "gmtModified", LocalDateTime.class, LocalDateTime.now());
        }
    
        @Override
        public void updateFill(MetaObject metaObject) {
            log.info("start update fill ....");
            this.strictUpdateFill(metaObject, "gmtModified", LocalDateTime.class, LocalDateTime.now());
        }
    }
    
    

    Ⅸ、全局异常处理

    @Slf4j
    @ControllerAdvice
    public class GlobalExceptionHandler {
    
        //全局异常处理
        @ExceptionHandler(Exception.class)
        @ResponseBody
        public R error(Exception e){
            log.error(ExceptionUtils.getMessage(e));
            return R.error();
        }
    
        //指定异常处理
        @ExceptionHandler(BadSqlGrammarException.class)
        @ResponseBody
        public R error(BadSqlGrammarException e){
            log.error(ExceptionUtils.getMessage(e));
            return R.setResult(ResultCodeEnum.BAD_SQL_GRAMMAR);
        }
    
        @ExceptionHandler(HttpMessageNotReadableException.class)
        @ResponseBody
        public R error(HttpMessageNotReadableException e){
            log.error(ExceptionUtils.getMessage(e));
            return R.setResult(ResultCodeEnum.JSON_PARSE_ERROR);
        }
    }
    
    

    Ⅹ、配置logback日志

    • logback-spring.xml(默认日志的名字,必须是这个名字)
    <?xml version="1.0" encoding="UTF-8"?>
    <configuration  scan="true" scanPeriod="10 seconds">
    
        <contextName>logback</contextName>
        <property name="log.path" value="D:/project/helen/guli_log/edu" />
    
        <!--控制台日志格式:彩色日志-->
        <!-- magenta:洋红 -->
        <!-- boldMagenta:粗红-->
        <!-- cyan:青色 -->
        <!-- white:白色 -->
        <!-- magenta:洋红 -->
        <property name="CONSOLE_LOG_PATTERN"
                  value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>
    
        <!--文件日志格式-->
        <property name="FILE_LOG_PATTERN"
                  value="%date{yyyy-MM-dd HH:mm:ss} |%-5level |%thread |%file:%line |%logger |%msg%n" />
    
        <!--编码-->
        <property name="ENCODING"
                  value="UTF-8" />
    
        <!--输出到控制台-->
        <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                <!--日志级别-->
                <level>DEBUG</level>
            </filter>
            <encoder>
                <!--日志格式-->
                <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
                <!--日志字符集-->
                <charset>${ENCODING}</charset>
            </encoder>
        </appender>
    
        <!--输出到文件-->
        <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <!--日志过滤器:此日志文件只记录INFO级别的-->
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>INFO</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
            <!-- 正在记录的日志文件的路径及文件名 -->
            <file>${log.path}/log_info.log</file>
            <encoder>
                <pattern>${FILE_LOG_PATTERN}</pattern>
                <charset>${ENCODING}</charset>
            </encoder>
            <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!-- 每天日志归档路径以及格式 -->
                <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>100MB</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
                <!--日志文件保留天数-->
                <maxHistory>15</maxHistory>
            </rollingPolicy>
        </appender>
    
        <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <!-- 日志过滤器:此日志文件只记录WARN级别的 -->
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>WARN</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
            <!-- 正在记录的日志文件的路径及文件名 -->
            <file>${log.path}/log_warn.log</file>
            <encoder>
                <pattern>${FILE_LOG_PATTERN}</pattern>
                <charset>${ENCODING}</charset> <!-- 此处设置字符集 -->
            </encoder>
            <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>100MB</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
                <!--日志文件保留天数-->
                <maxHistory>15</maxHistory>
            </rollingPolicy>
        </appender>
    
        <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <!-- 日志过滤器:此日志文件只记录ERROR级别的 -->
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>ERROR</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
            <!-- 正在记录的日志文件的路径及文件名 -->
            <file>${log.path}/log_error.log</file>
            <encoder>
                <pattern>${FILE_LOG_PATTERN}</pattern>
                <charset>${ENCODING}</charset> <!-- 此处设置字符集 -->
            </encoder>
            <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>100MB</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
                <!--日志文件保留天数-->
                <maxHistory>15</maxHistory>
            </rollingPolicy>
        </appender>
    
        <!--开发环境-->
        <springProfile name="dev">
            <!--可以灵活设置此处,从而控制日志的输出-->
            <root level="DEBUG">
                <appender-ref ref="CONSOLE" />
                <appender-ref ref="INFO_FILE" />
                <appender-ref ref="WARN_FILE" />
                <appender-ref ref="ERROR_FILE" />
            </root>
        </springProfile>
    
        <!--生产环境-->
        <springProfile name="pro">
            <root level="ERROR">
                <appender-ref ref="ERROR_FILE" />
            </root>
        </springProfile>
    </configuration>
    
    

    类上添加注解 @Slf4j 修改异常输出语句 log.error(e.getMessage());

    2-1、edu模块前端开发

    • 导入前端文件到vscode

    1、页面模块

    1、定义路由模块

    src/router/index.js

    配置讲师管理相关路由

    2、定义api模块

    创建文件 src/api/teacher.js

    // @ 符号在build/webpack.base.conf.js 中配置 表示 'src' 路径
    import request from '@/utils/request'
    
    export default {
    
      list() {
        return request({
          url: '/admin/edu/teacher/list',
          method: 'get'
        })
      }
    }
    
    
    3、定义页面组件脚本

    src/views/teacher/list.vue

    <script>
    import teacherApi from '@/api/teacher'
    export default {
      // 定义数据模型
      data() {
        return {
          list: [] // 讲师列表
        }
      },
    
      // 页面渲染成功后获取数据
      created() {
        this.fetchData()
      },
    
      // 定义方法
      methods: {
        fetchData() {
          // 调用api
          teacherApi.list().then(response => {
            this.list = response.data.items
          })
        }
      }
    }
    </script>
    
    
    4、定义页面组件模板
    <!-- 表格 -->
    <el-table :data="list" border stripe>
        <el-table-column type="index" width="50"/>
        <el-table-column prop="name" label="名称" width="80" />
        <el-table-column label="头衔" width="90">
            <template slot-scope="scope">
              <el-tag v-if="scope.row.level === 1" type="success" size="mini">高级讲师</el-tag>
              <el-tag v-if="scope.row.level === 2" size="mini">首席讲师</el-tag>
            </template>
        </el-table-column>
        <el-table-column prop="intro" label="简介" />
        <el-table-column prop="sort" label="排序" width="60" />
        <el-table-column prop="joinDate" label="入驻时间" width="160" />
    </el-table>
    
    

    2、分页查询

    1、定义api模块

    src/api/teacher.js

    pageList(page, limit, searchObj) {
        return request({
            url: `/admin/edu/teacher/list/${page}/${limit}`,
            method: 'get',
            params: searchObj
        })
    }
    
    
    2、定义页面组件脚本

    src/views/teacher/list.vue,完善data定义

    data() {// 定义数据
        return {
            list: null, // 数据列表
            total: 0, // 总记录数
            page: 1, // 页码
            limit: 10, // 每页记录数
            searchObj: {}// 查询条件
        }
    }
    
    

    修改fetchData方法

    fetchData() {
        // 调用api
        teacherApi.pageList(this.page, this.limit, this.searchObj).then(response => {
            this.list = response.data.rows
            this.total = response.data.total
        })
    }
    
    
    3、定义页面组件模板

    在table组件下面添加分页组件

    <!-- 分页组件 -->
    <el-pagination
      :current-page="page"
      :total="total"
      :page-size="limit"
      :page-sizes="[5, 10, 20, 30, 40, 50, 100]"
      style="padding: 30px 0; text-align: center;"
      layout="total, sizes, prev, pager, next, jumper"
    />
    
    
    4、改变每页条数

    组件注册事件

    @size-change="changePageSize"
    
    

    定义事件脚本

    // 每页记录数改变,size:回调参数,表示当前选中的“每页条数”
    changePageSize(size) {
        this.limit = size
        this.fetchData()
    }
    
    
    5、翻页

    组件注册事件

    @current-change="changeCurrentPage"
    
    

    定义事件脚本

    // 改变页码,page:回调参数,表示当前选中的“页码”
    changeCurrentPage(page) {
        this.page = page
        this.fetchData()
    },  
    
    
    6、序号列
    <el-table-column
      label="#"
      width="50">
      <template slot-scope="scope">
        {{ (page - 1) * limit + scope.$index + 1 }}
      </template>
    </el-table-column>
    
    
    7、查询表单

    在table组件上面添加查询表单

    <!--查询表单-->
    <el-form :inline="true">
        <el-form-item>
            <el-input v-model="searchObj.name" placeholder="讲师"/>
        </el-form-item>
    
        <el-form-item>
            <el-select v-model="searchObj.level" clearable placeholder="头衔">
                <el-option value="1" label="高级讲师"/>
                <el-option value="2" label="首席讲师"/>
            </el-select>
        </el-form-item>
    
        <el-form-item label="入驻时间">
            <el-date-picker
                            v-model="searchObj.joinDateBegin"
                            placeholder="开始时间"
                            value-format="yyyy-MM-dd" />
        </el-form-item>
        <el-form-item label="-">
            <el-date-picker
                            v-model="searchObj.joinDateEnd"
                            placeholder="结束时间"
                            value-format="yyyy-MM-dd" />
        </el-form-item>
        <el-form-item>
            <el-button type="primary" icon="el-icon-search" @click="fetchData()">查询</el-button>
            <el-button type="default" @click="resetData()">清空</el-button>
        </el-form-item>
    </el-form>
    
    

    重置表单脚本

    // 重置表单
    resetData() {
        this.searchObj = {}
        this.fetchData()
    }
    
    

    3、数据删除

    1、定义api模块

    src/api/teacher.js

    removeById(id) {
        return request({
            url: `/admin/edu/teacher/remove/${id}`,
            method: 'delete'
        })
    }
    
    
    2、定义页面组件模板

    在table组件中添加删除列

    <el-table-column label="操作" width="200" align="center">
        <template slot-scope="scope">
            <el-button type="danger" size="mini" icon="el-icon-delete" @click="removeById(scope.row.id)">删除</el-button>
        </template>
    </el-table-column>
    
    
    3、定义页面组件脚本
    // 根据id删除数据
    removeById(id) {
        this.$confirm('此操作将永久删除该记录, 是否继续?', '提示', {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
        }).then(() => {
            return teacherApi.removeById(id)
        }).then((response) => {
            this.fetchData()
            this.$message.success(response.message)
        }).catch(error => {
            console.log('error', error)
            // 当取消时会进入catch语句:error = 'cancel'
            // 当后端服务抛出异常时:error = 'error'
            if (error === 'cancel') {
                this.$message.info('取消删除')
            }
        })
    }
    
    

    4、axios响应拦截器

    1、关于code===20000

    code!==20000的响应会被拦截,并转到 error=>{} 处理

    if (res.code !== 20000) {
        return Promise.reject('error')
    }
    
    
    2、关于response

    code===20000时放行,前端页面接收到response.data的值,而不是response

    if (res.code !== 20000) {
        return Promise.reject('error')
    } else {
        return response.data
    }
    
    

    5、新增讲师

    1、定义api模块

    src/api/teacher.js

    save(teacher) {
        return request({
            url: '/admin/edu/teacher/save',
            method: 'post',
            data: teacher
        })
    }
    
    
    2、定义页面组件脚本

    src/views/teacher/form.vue,完善data定义

    <script>
    export default {
      data() {
        return {
          // 初始化讲师默认数据
          teacher: {
            sort: 0,
            level: 1
          },
          saveBtnDisabled: false // 保存按钮是否禁用,防止表单重复提交
        }
      }
    }
    </script>
    
    
    3、定义页面组件模板

    src/views/teacher/form.vue

    <!-- 输入表单 -->
    <el-form label-width="120px">
        <el-form-item label="讲师名称">
            <el-input v-model="teacher.name" />
        </el-form-item>
        <el-form-item label="入驻时间">
            <el-date-picker v-model="teacher.joinDate" value-format="yyyy-MM-dd" />
        </el-form-item>
        <el-form-item label="讲师排序">
            <el-input-number v-model="teacher.sort" :min="0"/>
        </el-form-item>
        <el-form-item label="讲师头衔">
            <el-select v-model="teacher.level">
                <!--
                数据类型一定要和取出的json中的一致,否则没法回填
                因此,这里value使用动态绑定的值,保证其数据类型是number
                -->
                <el-option :value="1" label="高级讲师"/>
                <el-option :value="2" label="首席讲师"/>
            </el-select>
        </el-form-item>
        <el-form-item label="讲师简介">
            <el-input v-model="teacher.intro"/>
        </el-form-item>
        <el-form-item label="讲师资历">
           <el-input v-model="teacher.career" :rows="10" type="textarea"/>
        </el-form-item>
        <!-- 讲师头像:TODO -->
    
        <el-form-item>
            <el-button :disabled="saveBtnDisabled" type="primary" @click="saveOrUpdate()">保存</el-button>
        </el-form-item>
    </el-form>
    
    
    4、实现新增功能

    src/views/teacher/form.vue,引入teacher api模块:

    import teacherApi from '@/api/teacher'
    
    

    定义保存方法

    methods: {
    
      saveOrUpdate() {
        // 禁用保存按钮
        this.saveBtnDisabled = true
        this.saveData()
      },
    
      // 新增讲师
      saveData() {
        // debugger
        teacherApi.save(this.teacher).then(response => {
          this.$message({
            type: 'success',
            message: response.message
          })
          this.$router.push({ path: '/teacher' })
        })
      }
    }
    
    

    6、显示讲师信息

    1、定义api模块

    src/api/teacher.js

    getById(id) {
        return request({
            url: `/admin/edu/teacher/get/${id}`,
            method: 'get'
        })
    }
    
    
    2、定义页面组件脚本

    src/views/teacher/form.vue,methods中定义回显方法

    // 根据id查询记录
    fetchDataById(id) {
        teacherApi.getById(id).then(response => {
            this.teacher = response.data.item
        })
    }
    
    

    页面渲染成功获取数据

    因为已在路由中定义如下内容:path: 'edit/:id',因此可以使用 this.$route.params.id 获取路由中的id

    //页面渲染成功
    created() {
        if (this.$route.params.id) {
            this.fetchDataById(this.$route.params.id)
        }
    }
    
    
    3、定义页面组件模板

    src/views/teacher/list.vue,表格“操作”列中增加“修改”按钮

    <router-link :to="'/teacher/edit/'+scope.row.id">
        <el-button type="primary" size="mini" icon="el-icon-edit">修改</el-button>
    </router-link>
    
    

    7、更新讲师

    1、定义api模块

    src/api/teacher.js

    updateById(teacher) {
        return request({
            url: '/admin/edu/teacher/update',
            method: 'put',
            data: teacher
        })
    }
    
    
    2、定义页面组件脚本

    src/views/teacher/form.vue,methods中定义updateData

    // 根据id更新记录
    updateData() {
      // teacher数据的获取
      teacherApi.updateById(this.teacher).then(response => {
          this.$message({
            type: 'success',
            message: response.message
          })
          this.$router.push({ path: '/teacher' })
      })
    },
    
    

    完善saveOrUpdate方法

    saveOrUpdate() {
        // 禁用保存按钮
        this.saveBtnDisabled = true
        if (!this.teacher.id) {
            this.saveData()
        } else {
            this.updateData()
        }
    }
    
    

    8、组件重用问题

    问题:vue-router导航切换 时,如果两个路由都渲染同个组件,

    组件的生命周期方法(created或者mounted)不会再被调用, 组件会被重用,显示上一个路由渲染出来的自建

    解决方案:可以简单的在 router-view上加上一个唯一的key,来保证路由切换时都会重新触发生命周期方法,确保组件被重新初始化。

    修改 src/views/layout/components/AppMain.vue 文件如下:

    <router-view :key="key"></router-view>
    
    
    computed: {
        key() {
            return this.$route.name !== undefined? this.$route.name + +new Date(): this.$route + +new Date()
        }
     }
    
    

    3、oss模块开发

  • 相关阅读:
    zt 必看: 原来PCIe技术原理这么简单!
    zt linux:centos 解决SSH连接Linux超时自动断开
    idea总结和未来的想法
    linux一些技巧
    zt如何解决Verilog目前不支持数组型端口定义!
    高速设计学习-干货!高速串行Serdes均衡之FFE
    zt:tcpdump抓包对性能的影响
    zt:TCP 学习
    verdi使用
    IE 浏览器下 button元素自动触发click?
  • 原文地址:https://www.cnblogs.com/ed1s0n/p/14050626.html
Copyright © 2011-2022 走看看