1.环境搭建
1.1 架构分析
- 注册中心:Nacos
- 网关:Gateway
- 后端基础框架:ssm
- 前端:Vue + SPA
- Axios(request.js)
1.2 数据库环境
1.2.1 学生数据库
#学生数据库 CREATE DATABASE nacos_ssm_student; USE nacos_ssm_student; CREATE TABLE tb_city( c_id VARCHAR(32) PRIMARY KEY COMMENT '城市ID', city_name VARCHAR(20) COMMENT '城市名称' , parent_id VARCHAR(32) COMMENT '父ID' ); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('320000','江苏省','0'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140000','山西省','0'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('130000','河北省','0'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('320100','南京市','320000'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('320102','玄武区','320100'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('320103','白下区','320100'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('321300','宿迁市','320000'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('321322','沭阳县','321300'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('321323','泗阳县','321300'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140100','太原市','140000'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140106','迎泽区','140100'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140108','尖草坪区','140100'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140800','运城市','140000'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140823','闻喜县','140800'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140828','夏 县','140800'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('130100','石家庄市','130000'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('130127','高邑县','130100'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('130185','鹿泉市','130100'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('131000','廊坊市','130000'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('131003','广阳区','131000'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('131022','固安县','131000'); CREATE TABLE `tb_student` ( `s_id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '学生ID', `sname` VARCHAR(50) DEFAULT NULL COMMENT '姓名', `age` INT(11) DEFAULT NULL COMMENT '年龄', `birthday` DATETIME DEFAULT NULL COMMENT '生日', `gender` CHAR(1) DEFAULT NULL COMMENT '性别', `c_id` INT DEFAULT NULL, `city_ids` VARCHAR(32) DEFAULT NULL COMMENT '城市:320000,321300,321322' ); INSERT INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (1,'赵三33',21,'2001-01-17 00:00:00','1',1,'320000,321300,321322'); INSERT INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (2,'钱四444',1900,'2001-05-16 00:00:00','1',2,'320000,321300,321322'); INSERT INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (3,'孙五56',189,'2022-03-15 00:00:00','0',1,'320000,321300,321322'); INSERT INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (4,'张三',20,'2020-12-21 00:00:00','0',2,'320000,321300,321322'); INSERT INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (5,'xxx',18,'2020-12-21 00:00:00','0',2,'140000,140800,140823'); INSERT INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (6,'123',18,'2020-11-01 00:00:00','0',3,'130000,130100,130127'); INSERT INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (7,'xx',18,'2020-11-02 00:00:00','0',1,'130000,131000,131003');
1.2.2 用户数据库
# 用户数据库 CREATE DATABASE nacos_ssm_user; USE nacos_ssm_user; CREATE TABLE `tb_user` ( `u_id` VARCHAR(32) PRIMARY KEY NOT NULL COMMENT '用户编号', `user_name` VARCHAR(50) UNIQUE DEFAULT NULL COMMENT '用户名', `password` VARCHAR(32) DEFAULT NULL COMMENT '密码', `gender` BIT(1) DEFAULT NULL COMMENT '性别,1表示男,0表示女', `image` VARCHAR(50) UNIQUE DEFAULT NULL COMMENT '头像图片' ); INSERT INTO `tb_user`(`u_id`,`user_name`,`password`,`gender`,`image`) VALUES ('u001','jack','1234',1,'1.jpg'); INSERT INTO `tb_user`(`u_id`,`user_name`,`password`,`gender`,`image`) VALUES ('u002','rose','1234',0,'2.jpg'); INSERT INTO `tb_user`(`u_id`,`user_name`,`password`,`gender`,`image`) VALUES ('u003','tom','1234',1,'3.jpg');
1.2.3 班级数据库
# 班级数据库 CREATE DATABASE nacos_ssm_classes; USE nacos_ssm_classes; CREATE TABLE `tb_teacher` ( `tid` INT(11) NOT NULL PRIMARY KEY AUTO_INCREMENT, `tname` VARCHAR(50) DEFAULT NULL COMMENT '老师姓名', `type` INT(11) DEFAULT NULL COMMENT '老师类型:1.授课老师、2.助理老师、3.辅导员老师' ); INSERT INTO `tb_teacher`(`tid`,`tname`,`type`) VALUES (1,'梁桐老师',1); INSERT INTO `tb_teacher`(`tid`,`tname`,`type`) VALUES (2,'马坤老师',2); INSERT INTO `tb_teacher`(`tid`,`tname`,`type`) VALUES (3,'仲燕老师',3); INSERT INTO `tb_teacher`(`tid`,`tname`,`type`) VALUES (4,'袁新奇老师',1); INSERT INTO `tb_teacher`(`tid`,`tname`,`type`) VALUES (5,'任林达老师',2); INSERT INTO `tb_teacher`(`tid`,`tname`,`type`) VALUES (6,'王珊珊老师',3); CREATE TABLE `tb_class` ( `cid` INT(11) NOT NULL PRIMARY KEY AUTO_INCREMENT, `cname` VARCHAR(50) DEFAULT NULL COMMENT '班级名称', `teacher1_id` INT(11) DEFAULT NULL COMMENT '授课老师', `teacher2_id` INT(11) DEFAULT NULL COMMENT '助理老师', `teacher3_id` INT(11) DEFAULT NULL COMMENT '辅导员老师' ); INSERT INTO `tb_class`(`cid`,`cname`,`teacher1_id`,`teacher2_id`,`teacher3_id`) VALUES (1,'Java56',1,2,3); INSERT INTO `tb_class`(`cid`,`cname`,`teacher1_id`,`teacher2_id`,`teacher3_id`) VALUES (2,'Java78',1,2,3); INSERT INTO `tb_class`(`cid`,`cname`,`teacher1_id`,`teacher2_id`,`teacher3_id`) VALUES (3,'Java12',4,5,6); INSERT INTO `tb_class`(`cid`,`cname`,`teacher1_id`,`teacher2_id`,`teacher3_id`) VALUES (4,'Java34',4,5,6);
1.2.4 学生数据库
#学生数据库 CREATE DATABASE nacos_ssm_student; USE nacos_ssm_student; CREATE TABLE tb_city( c_id VARCHAR(32) PRIMARY KEY COMMENT '城市ID', city_name VARCHAR(20) COMMENT '城市名称' , parent_id VARCHAR(32) COMMENT '父ID' ); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('320000','江苏省','0'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140000','山西省','0'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('130000','河北省','0'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('320100','南京市','320000'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('320102','玄武区','320100'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('320103','白下区','320100'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('321300','宿迁市','320000'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('321322','沭阳县','321300'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('321323','泗阳县','321300'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140100','太原市','140000'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140106','迎泽区','140100'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140108','尖草坪区','140100'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140800','运城市','140000'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140823','闻喜县','140800'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140828','夏 县','140800'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('130100','石家庄市','130000'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('130127','高邑县','130100'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('130185','鹿泉市','130100'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('131000','廊坊市','130000'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('131003','广阳区','131000'); INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('131022','固安县','131000'); CREATE TABLE `tb_student` ( `s_id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '学生ID', `sname` VARCHAR(50) DEFAULT NULL COMMENT '姓名', `age` INT(11) DEFAULT NULL COMMENT '年龄', `birthday` DATETIME DEFAULT NULL COMMENT '生日', `gender` CHAR(1) DEFAULT NULL COMMENT '性别', `c_id` INT DEFAULT NULL, `city_ids` VARCHAR(32) DEFAULT NULL COMMENT '城市:320000,321300,321322' ); INSERT INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (1,'赵三33',21,'2001-01-17 00:00:00','1',1,'320000,321300,321322'); INSERT INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (2,'钱四444',1900,'2001-05-16 00:00:00','1',2,'320000,321300,321322'); INSERT INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (3,'孙五56',189,'2022-03-15 00:00:00','0',1,'320000,321300,321322'); INSERT INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (4,'张三',20,'2020-12-21 00:00:00','0',2,'320000,321300,321322'); INSERT INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (5,'xxx',18,'2020-12-21 00:00:00','0',2,'140000,140800,140823'); INSERT INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (6,'123',18,'2020-11-01 00:00:00','0',3,'130000,130100,130127'); INSERT INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (7,'xx',18,'2020-11-02 00:00:00','0',1,'130000,131000,131003');
1.3 后端环境
1.3.1 父项目
- 步骤:
- 步骤1:创建项目 nacos-ssm-student
- 步骤2:添加坐标
- 步骤1:创建项目
- 步骤2:添加坐标:
<!-- 1 确定spring boot的版本--> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version> </parent> <!--2 确定版本--> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> <spring-cloud-release.version>Hoxton.SR3</spring-cloud-release.version> <nacos.version>1.1.0</nacos.version> <alibaba.cloud.version>2.2.1.RELEASE</alibaba.cloud.version> <mybatis.starter.version>1.3.2</mybatis.starter.version> <mapper.starter.version>2.0.2</mapper.starter.version> <mysql.version>5.1.32</mysql.version> <pageHelper.starter.version>1.2.5</pageHelper.starter.version> <durid.starter.version>1.1.10</durid.starter.version> <swagger.version>2.7.0</swagger.version> <jwt.jjwt.version>0.9.0</jwt.jjwt.version> <jwt.joda.version>2.9.7</jwt.joda.version> <beanutils.version>1.9.3</beanutils.version> <student.version>1.0-SNAPSHOT</student.version> </properties> <!-- 3 锁定版本--> <dependencyManagement> <dependencies> <!-- sprig cloud--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud-release.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!--nacos --> <dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> <version>${nacos.version}</version> </dependency> <!--nacos cloud 发现 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>${alibaba.cloud.version}</version> </dependency> <!--nacos cloud 配置 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <version>${alibaba.cloud.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-sentinel --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> <version>${alibaba.cloud.version}</version> </dependency> <!-- mybatis启动器 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.starter.version}</version> </dependency> <!-- 通用Mapper启动器 --> <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper-spring-boot-starter</artifactId> <version>${mapper.starter.version}</version> </dependency> <!-- 分页助手启动器 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>${pageHelper.starter.version}</version> </dependency> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <!-- Druid连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>${durid.starter.version}</version> </dependency> <!--swagger2--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>${swagger.version}</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>${swagger.version}</version> </dependency> <!--jwt--> <!--JavaBean工具类,用于JavaBean数据封装--> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>${beanutils.version}</version> </dependency> <!--jwt工具--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>${jwt.jjwt.version}</version> </dependency> <!--joda 时间工具类 --> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> <version>${jwt.joda.version}</version> </dependency> <!-- 自定义项目 --> <dependency> <groupId>com.czxy</groupId> <artifactId>nacos-ssm-student-domain</artifactId> <version>${student.version}</version> </dependency> </dependencies> </dependencyManagement>
1.3.2 domain项目
- 步骤:
- 步骤1:创建项目:nacos-ssm-student-domain
- 步骤2:添加pom
- 步骤3:拷贝vo(BaseResult)
- 步骤4:拷贝JavaBean
- 步骤1:创建项目:nacos-ssm-student-domain
- 步骤2:添加pom
<dependencies> <!--jpa--> <dependency> <groupId>javax.persistence</groupId> <artifactId>javax.persistence-api</artifactId> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--jackson--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> </dependency> </dependencies>
- 步骤3:拷贝vo
package com.czxy.vo; import lombok.Getter; import java.util.HashMap; import java.util.Map; /** * @author 桐叔 * @email liangtong@itcast.cn */ @Getter public class BaseResult<T> { //成功状态码 public static final int OK = 20000; //失败状态码 public static final int ERROR = 0; //返回码 private Integer code; //返回消息 private String message; //存放数据 private T data; //其他数据 private Map<String,Object> other = new HashMap<>(); public BaseResult() { } public BaseResult(Integer code, String message) { this.code = code; this.message = message; } public BaseResult(Integer code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 快捷成功BaseResult对象 * @param message * @return */ public static BaseResult ok(String message){ return new BaseResult(BaseResult.OK , message); } public static BaseResult ok(String message, Object data){ return new BaseResult(BaseResult.OK , message, data ); } /** * 快捷失败BaseResult对象 * @param message * @return */ public static BaseResult error(String message){ return new BaseResult(BaseResult.ERROR , message); } /** * 自定义数据区域 * @param key * @param msg * @return */ public BaseResult append(String key , Object msg){ other.put(key , msg); return this; } }
- 步骤4:拷贝JavaBean
- City
package com.czxy.domain; import lombok.Data; import javax.persistence.Column; import javax.persistence.Id; import javax.persistence.Table; import java.io.Serializable; /** * (TbCity)实体类 * * @author 桐叔 */ @Table(name = "tb_city") @Data public class City implements Serializable { private static final long serialVersionUID = 660498290955870873L; /** * 城市ID */ @Id @Column(name = "c_id") private String cid; /** * 城市名称 */ @Column(name = "city_name") private String cityName; /** * 父ID */ @Column(name = "parent_id") private String parentId; }
- Classes
package com.czxy.domain; import lombok.Data; import javax.persistence.Column; import javax.persistence.Id; import javax.persistence.Table; /** * @author 桐叔 * @email liangtong@itcast.cn */ @Table(name = "tb_class") @Data public class Classes { @Id @Column(name = "cid") private Integer cid; @Column(name = "cname") private String cname; //班级名称 @Column(name = "teacher1_id") private Integer teacher1Id; //授课老师 @Column(name = "teacher2_id") private Integer teacher2Id; //助理老师 @Column(name = "teacher3_id") private Integer teacher3Id; //辅导员老师 }
- Course
package com.czxy.domain; import lombok.Data; import javax.persistence.Column; import javax.persistence.Id; import javax.persistence.Table; /** * @author 桐叔 * @email liangtong@itcast.cn */ @Table(name = "tb_course") @Data public class Course { @Id @Column(name = "c_id") private Integer cid; @Column(name = "cname") private String cname; @Column(name = "`desc`") private String desc; }
- Student
package com.czxy.domain; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import javax.persistence.Column; import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.Transient; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * @author 桐叔 * @email liangtong@itcast.cn */ @Table(name = "tb_student") @Data public class Student { @Id @Column(name = "s_id") private Integer sid; //学生ID @Column(name = "sname") private String sname; //姓名 @Column(name = "age") private Integer age; //年龄 @Column(name = "birthday") @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") private Date birthday; //生日 @Column(name = "gender") private String gender; //性别 @Column(name = "c_id") private Integer cid; //班级外键 @Column(name = "city_ids") private String cityIds; //城市 private List<City> cityList; //所选城市 @Transient private Classes classes; //班级对象 @Transient private Integer courseCount; //选课数 @Transient private List<Course> courseList = new ArrayList<>(); //选课详情 @Transient private List<Integer> courseIds = new ArrayList(); //选课id }
- Teacher
package com.czxy.domain; import lombok.Data; import javax.persistence.Column; import javax.persistence.Id; import javax.persistence.Table; /** * @author 桐叔 * @email liangtong@itcast.cn */ @Table(name = "tb_teacher") @Data public class Teacher { @Id @Column(name = "tid") private Integer tid; @Column(name = "tname") private String tname; //老师姓名 @Column(name = "type") private Integer type; //老师类型:1.授课老师、2.助理老师、3.辅导员老师 }
- User
package com.czxy.domain; import lombok.Data; import javax.persistence.Column; import javax.persistence.Id; import javax.persistence.Table; /** * @author 桐叔 * @email liangtong@itcast.cn */ @Table(name = "tb_user") @Data public class User { @Id @Column(name = "u_id") private String uid; @Column(name = "user_name") private String userName; private String password; private Integer gender; private String image; } /* CREATE TABLE `tb_user` ( `u_id` VARCHAR(32) PRIMARY KEY NOT NULL COMMENT '用户编号', `user_name` VARCHAR(50) UNIQUE DEFAULT NULL COMMENT '用户名', `password` VARCHAR(32) DEFAULT NULL COMMENT '密码', `gender` BIT(1) DEFAULT NULL COMMENT '性别,1表示男,0表示女', `image` VARCHAR(50) UNIQUE DEFAULT NULL COMMENT '头像图片' ); */
1.3.3 网关:10010
- 步骤:
- 步骤1:创建项目,nacos-ssm-student-gateway
- 步骤2:添加坐标
- 步骤3:配置文件 application.yml
- 步骤4:拷贝全局跨域配置类 GlobalGatewayCorsConfig
- 步骤5:启动类 GatewayApplication
- 步骤1:创建项目,nacos-ssm-student-gateway
- 步骤2:添加坐标
<dependencies> <!-- 网关 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!-- nacos 服务发现 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--JavaBean工具类,用于JavaBean数据封装--> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> </dependency> <!--jwt工具--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> </dependency> <!--joda 时间工具类 --> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> </dependency> </dependencies>
- 步骤3:配置文件
#端口号 server: port: 10010 spring: application: name: student-gateway cloud: nacos: discovery: server-addr: 127.0.0.1:8848 #nacos服务地址 gateway: discovery: locator: enabled: true #开启服务注册和发现的功能,自动创建router以服务名开头的请求路径转发到对应的服务 lowerCaseServiceId: true #将请求路径上的服务名配置为小写
- 步骤4:拷贝全局跨域配置类
package com.czxy.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.web.cors.reactive.CorsUtils; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; import reactor.core.publisher.Mono; /** * @author 桐叔 * @email liangtong@itcast.cn */ @Configuration public class GlobalGatewayCorsConfig { @Bean public WebFilter corsFilter2() { return (ServerWebExchange ctx, WebFilterChain chain) -> { ServerHttpRequest request = ctx.getRequest(); if (CorsUtils.isCorsRequest(request)) { HttpHeaders requestHeaders = request.getHeaders(); ServerHttpResponse response = ctx.getResponse(); HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod(); HttpHeaders headers = response.getHeaders(); headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin()); headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders()); if (requestMethod != null) { headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name()); } headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true"); headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*"); if (request.getMethod() == HttpMethod.OPTIONS) { response.setStatusCode(HttpStatus.OK); return Mono.empty(); } } return chain.filter(ctx); }; } }
- 步骤5:启动类
package com.czxy; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; /** * @author 桐叔 * @email liangtong@itcast.cn */ @SpringBootApplication @EnableDiscoveryClient public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } }
1.3.4 学生服务:9000
- 步骤:
- 步骤1:创建项目:nacos-ssm-student-service-student
- 步骤2:添加pom
- 步骤3:编写yml
- 步骤4:编写启动类,StudentServiceApplication
- 步骤5:拷贝 Swagger 配置类 ,Swagger2ConfigurationV3
- 步骤1:创建项目:nacos-ssm-student-service-student
- 步骤2:添加pom
<dependencies> <!--web起步依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- nacos 客户端 --> <dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> </dependency> <!-- nacos 服务发现 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- mybatis启动器 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <!-- 通用Mapper启动器 --> <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper-spring-boot-starter</artifactId> </dependency> <!-- 分页助手启动器 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> </dependency> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- Druid连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> </dependency> <!--swagger2--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> </dependency> <!-- feign 远程调用 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- 自定义项目 --> <dependency> <groupId>com.czxy</groupId> <artifactId>nacos-ssm-student-domain</artifactId> </dependency> </dependencies>
- 步骤3:编写yml
#端口号 server: port: 9000 spring: application: name: student-service #服务名 datasource: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/nacos_ssm_student?useUnicode=true&characterEncoding=utf8 username: root password: 1234 druid: #druid 连接池配置 initial-size: 1 #初始化连接池大小 min-idle: 1 #最小连接数 max-active: 20 #最大连接数 test-on-borrow: true #获取连接时候验证,会影响性能 cloud: nacos: discovery: server-addr: 127.0.0.1:8848 #nacos服务地址 sentinel: transport: dashboard: 127.0.0.1:8080
- 步骤4:编写启动类
package com.czxy.student; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; /** * @author 桐叔 * @email liangtong@itcast.cn */ @SpringBootApplication @EnableDiscoveryClient public class StudentServiceApplication { public static void main(String[] args) { SpringApplication.run(StudentServiceApplication.class, args ); } }
- 步骤5:拷贝 Swagger2ConfigurationV3 配置类
package com.czxy.student.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.*; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; import java.util.ArrayList; import java.util.List; /** * Swagger2 配置类, * 访问路径:swagger-ui.html * 自动注册: * 位置:resources/META-INF/spring.factories * 内容: * org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ * com.czxy.config.Swagger2Configuration */ @Configuration @EnableSwagger2 public class Swagger2ConfigurationV3 { @Bean public Docket createRestApi() { // 1 确定文档Swagger版本 Docket docket = new Docket(DocumentationType.SWAGGER_2); // 2 设置 api基本信息 docket.apiInfo(apiInfo()); // 3 设置自定义加载路径 docket = docket.select() .apis(RequestHandlerSelectors.basePackage("com.czxy")) .paths(PathSelectors.any()) .build(); //4 设置权限 docket.securitySchemes(securitySchemes()); docket.securityContexts(securityContexts()); return docket; } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("API") .description("基于swagger接口文档") .contact(new Contact("梁桐","http://www.javaliang.com","liangtong@itcast.cn")) .version("1.0") .build(); } private List<ApiKey> securitySchemes() { List<ApiKey> list = new ArrayList<>(); // name 为参数名 keyname是页面传值显示的 keyname, name在swagger鉴权中使用 list.add(new ApiKey("Authorization", "Authorization", "header")); return list; } private List<SecurityContext> securityContexts() { List<SecurityContext> list = new ArrayList<>(); list.add(SecurityContext.builder() .securityReferences(defaultAuth()) .forPaths(PathSelectors.regex("^(?!auth).*$")) .build()); return list; } private List<SecurityReference> defaultAuth() { AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; authorizationScopes[0] = authorizationScope; List<SecurityReference> list = new ArrayList(); list.add(new SecurityReference("Authorization", authorizationScopes)); return list; } }
1.4 前端环境
1.4.1 创建项目
vue create nacos-student-spa
1.4.2 安装axios
cnpm install axios --save
1.4.3 抽取axios
- 安装依赖
npm i element-ui --save
- 创建工具
src/utils/request.js
import axios from 'axios' import { MessageBox, Message } from 'element-ui' // 方式1:设置基本路径 // axios.defaults.baseURL='http://localhost:10010/api' // 方式2:create an axios instance,并设置基本路径 const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url // withCredentials: true, // send cookies when cross-domain requests timeout: 5000 // request timeout }) // request interceptor service.interceptors.request.use( config => { // 请求头中追加token let token = localStorage.getItem('token') if (token) { config.headers.Authorization = token } return config }, error => { // do something with request error console.log(error) // for debug return Promise.reject(error) } ) // response interceptor service.interceptors.response.use( response => { const res = response.data // if the custom code is not 20000, it is judged as an error. if (res.code !== 20000) { Message({ message: res.message || 'Error', type: 'error', duration: 5 * 1000 }) // ajax异常提示信息 (路径信息,数据) console.info(response.config, response.data ) return Promise.reject(new Error(res.message || 'Error')) } else { return res } }, error => { console.log('err' + error) // for debug if(error.response.status == 401) { MessageBox.confirm(error.response.data, '重新登录确认框', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => { // 删除token localStorage.removeItem('token') location.reload() }) } else { Message({ message: error.message, type: 'error', duration: 5 * 1000 }) } return Promise.reject(error) } ) export default service
- 使用工具,创建
src/api/student.js
// 导入工具 request.js import axios from '@/utils/request.js' // 编写功能方法 export function condition(studentPage, studentVo) { return axios.post(`/student-service/student/condition/${studentPage.pageSize}/${studentPage.pageNum}`, studentVo) }
1.4.4 启动
npm run serve
1.4.5 访问
http://localhost:8080/
1.4.6 修改入口页面
<template> <div> <router-link to="/classes_list">班级管理</router-link> | <router-link to="/student_list">学生管理</router-link> | <router-link to="/user_register">注册</router-link> | <router-link to="/user_login">登录</router-link> | <hr> <!-- 视图显示区域 --> <router-view/> </div> </template> <script> export default { } </script> <style> </style>
1.4.7 优化:拷贝编辑器配置文件
- 创建文件
.editorconfig
# editorconfig.org root = true [*] indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = false
2. 学生管理
2.1 查询所有:后端
2.1.1 需求
- 查询所有的学生,含条件查询、分页查询
2.1.2 接口
POST http://localhost:10010/student-service/student/condition/3/1 { "code": 20000, "message": "查询成功", "data": { "records": [ { "sid": 1, "sname": "赵三33", "age": 21, "birthday": "2001-01-17", "gender": "1", "cid": 1, "cityIds": "320000,321300,321322", "cityList": null, "classes": null, "courseCount": null, "courseList": [], "courseIds": [] }, ... ] }
2.1.3 后端实现
- 步骤1:拷贝配置类(Swagger配置类)
- 步骤2:创建条件查询封装类:StudentVo
- 步骤3:编写Mapper
- 步骤4:编写service接口、实现类
- 步骤5:编写controller
- 步骤6:测试
- 步骤1:拷贝配置类(Swagger配置类)
- 步骤2:创建条件查询封装类:StudentVo
package com.czxy.student.vo; import lombok.Data; /** * @author 桐叔 * @email liangtong@itcast.cn */ @Data public class StudentVo { private String cid; //班级 private String sname; //姓名 private String startAge; //开始年龄 private String endAge; //结束年龄 }
- 步骤3:编写Mapper
- 步骤4:编写service
- 接口
- 实现类
- 步骤5:编写controller
- 步骤6:测试
http://localhost:9000/student/condition/3/1 http://localhost:10010/student-service/student/condition/3/1
2.2 查询所欲:前端
2.2.1 查询所有
- 需求:显示学生列表
- 步骤:
- 步骤1:确定访问路径
- 步骤2:编写路由
- 步骤3:查询所有
- 步骤1:确定访问路径
- 步骤2:编写路由
import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) const routes = [ { path: '/student_list', name: '学生列表', component: () => import('../views/StudentList.vue') } ] const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes }) export default router
- 步骤3:查询所有
<template> <div> <router-link to="/student_add">添加学生</router-link> <!-- 查询列表 --> <table id="tid" border="1" width="800"> <tr> <td>学生ID</td> <td>班级ID</td> <td>学生姓名</td> <td>年龄</td> <td>生日</td> <td>性别</td> <td>操作</td> </tr> <tr v-for="(student,index) in pageInfo.list" :key="index"> <td>{{student.sid}}</td> <td>{{student.cid}}</td> <td>{{student.sname}}</td> <td>{{student.age}}</td> <td>{{student.birthday}}</td> <td>{{student.gender == 1 ? "男" : "女"}}</td> <td> <a>修改</a> <a>删除</a> </td> </tr> </table> </div> </template> <script> import {condition} from '@/api/student.js' export default { data() { return { pageInfo: { //分页对象 pageSize: 2, pageNum: 1 }, studentVo: { //条件表单对象 cid: '' }, } }, mounted() { // 查询所有 this.conditionFn() }, methods: { async conditionFn() { let { data } = await condition(this.pageInfo,this.studentVo) console.info(data) this.pageInfo = data }, }, } </script> <style> </style>
2.2.2 条件所有
- 需求:
- 步骤
- 步骤1:添加条件查询表单
- 步骤2:完善 conditionFn 函数
- 步骤1:添加条件查询表单
<!-- 查询条件 --> <table> <tr> <td>班级</td> <td> <select v-model="studentVo.cid"> <option value="">--选择班级--</option> <option value="1">Java12班</option> <option value="2">Java34班</option> <option value="3">Java56班</option> </select> </td> <td>姓名:</td> <td> <input type="text" placeholder="请输入姓名" v-model="studentVo.sname" size="10"> </td> <td>年龄:</td> <td> <input type="text" placeholder="请输入开始年龄" v-model="studentVo.startAge" size="10"> -- <input type="text" placeholder="请输入结束年龄" v-model="studentVo.endAge" size="10"> </td> <td><input type="button" value="查询" @click="conditionFn(1)"></td> </tr> </table>
- 步骤2:完善 conditionFn 函数
async conditionFn(pageNum) { if(pageNum) { this.pageInfo.pageNum = pageNum } let { data } = await condition(this.pageInfo,this.studentVo) console.info(data) this.pageInfo = data },
2.2.3 分页所有
- 需求:
- 步骤
- 步骤1:添加分页信息
- 步骤2:编写跳转函数
- 步骤1:添加分页信息
<!-- 分页条 --> <div id="pageId"> 每页 <select v-model="pageInfo.pageSize" @change="conditionFn(1)"> <option value="1">1</option> <option value="2">2</option> <option value="5">5</option> <option value="10">10</option> </select>条, <a href="#" v-for="index in pageInfo.pages" :key="index" @click.prevent="conditionFn(index)" >{{index}}</a> ,跳转到第 <input type="text" v-model="pageInfo.pageNum" size="5" @keydown.enter="go" />页 </div>
- 步骤2:编写跳转函数
go() { if(parseInt(this.pageInfo.pageNum) == this.pageInfo.pageNum) { this.conditionFn() } },