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
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jGVAw7XV-1639140871439)(assets/image-20211209173504730.png)]
-
步骤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 {//成功状态码
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
-
步骤3:拷贝vo
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MrjVE9q6-1639140959027)(assets/image-20211209180305989.png)]
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
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OMB3OIN6-1639140959028)(assets/image-20211209182008606.png)]
-
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
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-as0gatW1-1639140959029)(assets/image-20211209173520216.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HbNNe76A-1639140959029)(assets/image-20211209172705158.png)]
-
步骤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:配置文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JuFoSTrx-1639140959030)(assets/image-20211209210153319.png)]
#端口号 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:拷贝全局跨域配置类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8eJnW4Rd-1639140959030)(assets/image-20211209210216000.png)]
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:启动类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C3BiGAZ2-1639140959031)(assets/image-20211209210256076.png)]
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
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DBdVmh4w-1639140959031)(assets/image-20211209174119016.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HydIz7b1-1639140959032)(assets/image-20211209174107226.png)]
-
步骤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
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j4fIx4Qw-1639140959032)(assets/image-20211209174441024.png)]
#端口号 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:编写启动类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KVe5B7rI-1639140959033)(assets/image-20211209174722341.png)]
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 配置类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-24TPh7pU-1639140959034)(assets/image-20211209210613871.png)]
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
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K05a484L-1639140959035)(assets/image-20211209213956918.png)]
1.4.2 安装axios
cnpm install axios --save
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CsKbt8Iv-1639140959035)(assets/image-20211209214409198.png)]
1.4.3 抽取axios
-
安装依赖
npm i element-ui --save
-
创建工具
src/utils/request.js
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eJ9rFNL8-1639140959036)(assets/image-20211209223549234.png)]
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
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k8ccGuB0-1639140959036)(assets/image-20211209224318391.png)]
// 导入工具 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
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SfIb66M7-1639140959037)(assets/image-20211209214524648.png)]
1.4.5 访问
http://localhost:8080/
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QEKX90bE-1639140959037)(assets/image-20211209214608757.png)]
1.4.6 修改入口页面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6WGuPdra-1639140959038)(assets/image-20211209214733329.png)]
<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>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ELLlofPb-1639140959038)(assets/image-20211209214845776.png)]
1.4.7 优化:拷贝编辑器配置文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WjJi9neO-1639140959039)(assets/image-20211209231239396.png)]
-
创建文件
.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": []
},
...
]
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eEuyihzy-1639140959039)(assets/image-20210424225553055.png)]
2.1.3 后端实现
-
步骤1:拷贝配置类(Swagger配置类)
-
步骤2:创建条件查询封装类:StudentVo
-
步骤3:编写Mapper
-
步骤4:编写service接口、实现类
-
步骤5:编写controller
-
步骤6:测试
-
步骤1:拷贝配置类(Swagger配置类)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YHTZAd3K-1639140959040)(assets/image-20211209180858291.png)]
-
步骤2:创建条件查询封装类:StudentVo
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fnqcgIS0-1639140959040)(assets/image-20211209181027088.png)]
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
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MjJ35lnP-1639140959040)(assets/image-20211209182043978.png)]
-
步骤4:编写service
-
接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lBg5rudQ-1639140959041)(assets/image-20211209182202194.png)]
-
实现类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ByucX9In-1639140959041)(assets/image-20211209182658186.png)]
-
-
步骤5:编写controller
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JxgVSW5c-1639140959042)(assets/image-20211209182839342.png)]
-
步骤6:测试
http://localhost:9000/student/condition/3/1 http://localhost:10010/student-service/student/condition/3/1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mgIN2hV5-1639140959042)(assets/image-20211209183047568.png)]
2.2 查询所有:前端
2.2.1 查询所有
-
需求:显示学生列表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DlHal5r0-1639140959043)(assets/image-20211209225712558.png)]
-
步骤:
- 步骤1:确定访问路径
- 步骤2:编写路由
- 步骤3:查询所有
-
步骤1:确定访问路径
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r60oZbAk-1639140959043)(assets/image-20211209224438810.png)]
-
步骤2:编写路由
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HNZ7MoCq-1639140959043)(assets/image-20211209225541221.png)]
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:查询所有
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eGK6nUNo-1639140959044)(assets/image-20211209225647075.png)]
<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 条件所有
-
需求:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KhMoHQjz-1639140959044)(assets/image-20211209230110276.png)]
-
步骤
- 步骤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 函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l0ZfOGXk-1639140959045)(assets/image-20211209230328582.png)]
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 分页所有
-
需求:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z752wR7U-1639140959045)(assets/image-20211209230709579.png)]
-
步骤
- 步骤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:编写跳转函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ASofHjdO-1639140959045)(assets/image-20211209230808720.png)]
go() { if(parseInt(this.pageInfo.pageNum) == this.pageInfo.pageNum) { this.conditionFn() } },