在线教育
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()
}
}