目录




项目名:nacos-nuxt-student-service-student
pom文件
yml文件
启动类
拷贝配置类
基本结构
pom文件
org.springframework.boot spring-boot-starter-web com.alibaba.nacos nacos-client com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery io.springfox springfox-swagger2 io.springfox springfox-swagger-ui org.springframework.cloud spring-cloud-starter-openfeign org.springframework.boot spring-boot-starter-test com.baomidou mybatis-plus-boot-starter mysql mysql-connector-java com.czxy nacos-nuxt-student-domain org.springframework.boot spring-boot-starter-data-redis org.springframework.boot spring-boot-starter-mail org.springframework.boot spring-boot-starter-amqp com.alibaba fastjson org.springframework.boot spring-boot-devtools true io.jsonwebtoken jjwt joda-time joda-time commons-beanutils commons-beanutils
yml文件
# 服务端口号 server: port: 9020 # 服务名 spring: application: name: student-service datasource: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/cloud_es_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服务地址 #开启log4j打印SQL语句 logging: level: com: czxy: student: mapper: debug # mp日志打印 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-value: 1 logic-not-delete-value: 0 sc: jwt: secret: sc@Login(Auth}*^31)&czxy% # 登录校验的密钥 pubKeyPath: D:/rsa/rsa.pub # 公钥地址 priKeyPath: D:/rsa/rsa.pri # 私钥地址 expire: 360 # 过期时间,单位分钟
启动类
package com.czxy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @author 桐叔
* @email liangtong@itcast.cn
* @description
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class StudentServiceApplication {
public static void main(String[] args) {
SpringApplication.run(StudentServiceApplication.class, args);
}
}
配置类

基本结构

项目名:nacos-nuxt-student-service-course

编写Vo
编写controller
编写service
编写Vo
package com.czxy.student.vo;
import lombok.Data;
/**
* @author 桐叔
* @email liangtong@itcast.cn
* @description
*/
@Data
public class StudentVo {
private String classesId; //班级
private String sname; //姓名
private String startAge; //开始年龄
private String endAge; //结束年龄
}
编写controller

@PostMapping("/condition/{size}/{current}")
public BaseResult condition(
@RequestBody StudentVo studentVo,
@PathVariable("size") Integer size,
@PathVariable("current") Integer current
) {
// 查询
Page page = tbStudentService.condition(studentVo, size, current);
// 返回
return BaseResult.ok("查询成功", page);
} 编写service
接口
Pagecondition(StudentVo studentVo, Integer size, Integer current);
实现类
@Override public Pagecondition(StudentVo studentVo, Integer size, Integer current) { //1 条件 QueryWrapper queryWrapper = new QueryWrapper<>(); if(StringUtils.isNotBlank(studentVo.getClassesId())) { queryWrapper.eq("c_id", studentVo.getClassesId()); } if(StringUtils.isNotBlank(studentVo.getSname())) { queryWrapper.like("sname", studentVo.getSname()); } if(StringUtils.isNotBlank(studentVo.getStartAge())) { queryWrapper.ge("age", studentVo.getStartAge()); } if(StringUtils.isNotBlank(studentVo.getEndAge())) { queryWrapper.le("age", studentVo.getEndAge()); } //2 分页 Page page = new Page<>(current, size); //3 查询 baseMapper.selectPage(page, queryWrapper); //4 关联 //5 返回 return page; }
列表
条件
分页
列表
添加 {{scope.row.gender == 1 ? '男': '女'}} 编辑 删除 {{student}} 男 女 {{course.cname}}
条件

添加 - 查询
分页

班级服务:查询详情
学生服务:
检查student对象,是否有班级对象
编写feign调用查询详情
修改service,给student填充班级信息
班级服务:查询详情

@GetMapping("/{cid}")
public BaseResult findById(@PathVariable("cid") Integer cid) {
// 查询详情
TbClass tbClass = tbClassesService.getById(cid);
// 返回
return BaseResult.ok("查询成功", tbClass);
} 学生服务:
检查student对象,是否有班级对象

编写feign调用查询详情

package com.czxy.student.feign;
import com.czxy.domain.TbClass;
import com.czxy.vo.BaseResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @author 桐叔
* @email liangtong@itcast.cn
* @description
*/
@FeignClient(value = "classes-service", path = "/classes")
public interface ClassesFeign {
// 必须有泛型,否则获得的数据不是TbClass而是Map
@GetMapping("/{cid}")
public BaseResult findById(@PathVariable("cid") Integer cid) ;
}
修改service,给student填充班级信息
@Override public Pagecondition(StudentVo studentVo, Integer size, Integer current) { //1 条件 QueryWrapper queryWrapper = new QueryWrapper<>(); if(StringUtils.isNotBlank(studentVo.getClassesId())) { queryWrapper.eq("c_id", studentVo.getClassesId()); } if(StringUtils.isNotBlank(studentVo.getSname())) { queryWrapper.like("sname", studentVo.getSname()); } if(StringUtils.isNotBlank(studentVo.getStartAge())) { queryWrapper.ge("age", studentVo.getStartAge()); } if(StringUtils.isNotBlank(studentVo.getEndAge())) { queryWrapper.le("age", studentVo.getEndAge()); } //2 分页 Page page = new Page<>(current, size); //3 查询 baseMapper.selectPage(page, queryWrapper); //4 关联 page.getRecords().forEach(student -> { // 4.1 处理对应班级信息 BaseResult classesBaseResult = classesFeign.findById(student.getCid()); TbClass tbClass = classesBaseResult.getData(); student.setTbClass(tbClass); }); //5 返回 return page; }

{{scope.row.tbClass ? scope.row.tbClass.cname : ''}}
课程服务:查找指定学生的选课
mapper:sql语句多表查询
service
controller
学生服务
编写CourseFeign
修改学生service,完善级联操作
==异常==:先完成添加,再完成查询时,会存在一个服务,编写2个feign情况,启动时会有异常。

课程服务:查找指定学生的选课
mapper:sql语句多表查询
package com.czxy.course.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.czxy.domain.TbCourse; import com.czxy.domain.TbStudent; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import java.util.List; /** * @author 桐叔 * @email liangtong@itcast.cn * @description */ @Mapper public interface TbCourseMapper extends BaseMapper{ /** * 查询指定学生的选课 * @author 桐叔 * @email liangtong@itcast.cn * @return */ @Select("SELECT c.* FROM tb_course c, tb_student_course sc WHERE c.c_id = sc.c_id AND sc.s_id = #{sid}") public List findAllByStudentId(@Param("sid") Integer sid); }
service
接口
package com.czxy.course.service; import com.baomidou.mybatisplus.extension.service.IService; import com.czxy.domain.TbCourse; import org.apache.ibatis.annotations.Param; import java.util.List; /** * @author 桐叔 * @email liangtong@itcast.cn * @description */ public interface TbCourseService extends IService{ public List findAllByStudentId(Integer sid); }
实现类
package com.czxy.course.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.czxy.course.mapper.TbCourseMapper; import com.czxy.course.service.TbCourseService; import com.czxy.domain.TbCourse; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; /** * @author 桐叔 * @email liangtong@itcast.cn * @description */ @Service @Transactional public class TbCourseServiceImpl extends ServiceImplimplements TbCourseService { @Override public List findAllByStudentId(Integer sid) { return baseMapper.findAllByStudentId(sid); } }
controller
package com.czxy.course.controller;
import com.czxy.course.service.TbCourseService;
import com.czxy.domain.TbCourse;
import com.czxy.vo.BaseResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* @author 桐叔
* @email liangtong@itcast.cn
* @description
*/
@RestController
@RequestMapping("/course")
public class TbCourseController {
@Resource
private TbCourseService tbCourseService;
@GetMapping("/student/{sid}")
public BaseResult> findAllByStudentId(@PathVariable("sid") Integer sid) {
// 查询
List courseList = tbCourseService.findAllByStudentId(sid);
// 返回
return BaseResult.ok("查询成功", courseList);
}
}
学生服务
编写CourseFeign

package com.czxy.student.feign;
import com.czxy.domain.TbStudentCourse;
import com.czxy.vo.BaseResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* @author 桐叔
* @email liangtong@itcast.cn
* @description
*/
//@FeignClient(value = "服务名", path = "controller类路径")
@FeignClient(value = "course-service", path = "/course")
public interface CourseFeign {
@GetMapping("/student/{sid}")
public BaseResult> findAllByStudentId(@PathVariable("sid") Integer sid);
}
修改学生service,完善级联操作
@Override public Pagecondition(StudentVo studentVo, Integer size, Integer current) { //1 条件 QueryWrapper queryWrapper = new QueryWrapper<>(); if(StringUtils.isNotBlank(studentVo.getClassesId())) { queryWrapper.eq("c_id", studentVo.getClassesId()); } if(StringUtils.isNotBlank(studentVo.getSname())) { queryWrapper.like("sname", studentVo.getSname()); } if(StringUtils.isNotBlank(studentVo.getStartAge())) { queryWrapper.ge("age", studentVo.getStartAge()); } if(StringUtils.isNotBlank(studentVo.getEndAge())) { queryWrapper.le("age", studentVo.getEndAge()); } //2 分页 Page page = new Page<>(current, size); //3 查询 baseMapper.selectPage(page, queryWrapper); //4 关联 page.getRecords().forEach(student -> { // 4.1 处理对应班级信息 BaseResult classesBaseResult = classesFeign.findById(student.getCid()); TbClass tbClass = classesBaseResult.getData(); student.setTbClass(tbClass); // 4.2 课程详情 BaseResult > courseListBaseResult = courseFeign.findAllByStudentId(student.getSid()); List
courseList = courseListBaseResult.getData(); student.setCourseList(courseList); student.setCourseCount(courseList.size()); }); //5 返回 return page; }

{{course.cname}}

1)后端实现:查询所有的班级

/**
* 查询所有
* @author 桐叔
* @email liangtong@itcast.cn
* @return
*/
@GetMapping
public BaseResult findAll() {
// 查询所有
List list = tbClassesService.list();
// 返回
return BaseResult.ok("查询成功", list);
}
2)前端实现:班级列表+表单
展示弹出框
填充表单
展示班级列表
展示弹出框
添加
填充表单
添加 {{student}} 男 女
展示班级列表
添加 {{student}} 男 女
3)后端实现:基本添加
编写service
编写controller
编写service
接口
实现类
@Service @Transactional public class TbStudentServiceImpl extends ServiceImplimplements TbStudentService { @Override public boolean addStudent(TbStudent tbStudent) { // 保存学生基本信息 int result = baseMapper.insert(tbStudent); return result == 1; } }
编写controller
package com.czxy.student.controller;
import com.czxy.domain.TbStudent;
import com.czxy.student.service.TbStudentService;
import com.czxy.vo.BaseResult;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* @author 桐叔
* @email liangtong@itcast.cn
* @description
*/
@RestController
@RequestMapping("/student")
public class TbStudentController {
@Resource
private TbStudentService tbStudentService;
@PostMapping
public BaseResult save(@RequestBody TbStudent tbStudent) {
// 添加
boolean result = tbStudentService.addStudent(tbStudent);
// 提示
if(result) {
return BaseResult.ok("添加成功");
}
return BaseResult.error("添加失败");
}
}
4)前端实现:基本添加
添加 {{student}} 男 女
1)后端实现

@GetMapping("/{parentId}")
public BaseResult findAllByParentId(@PathVariable("parentId") String parentId) {
//1 根据父id查询所有城市
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.eq("parent_id", parentId);
List list = tbCityService.list(queryWrapper);
//2 返回结果
return BaseResult.ok("查询成功", list);
}
2)前端实现
异步加载
完善添加
异步加载

cityProps: { //城市级联菜单的属性设置
lazy: true, //开启
async lazyLoad (node, resolve) { //加载数据
// 1. 如果 node.root == true 表示第一次加载,也就是省
let parentId = null
if(node.root == true) {
parentId = "0"
} else {
// 其他 - node.value可以获得当前节点的id的值,例如:省的id
parentId = node.value
}
console.info(node)
// 2. ajax 查询所有的城市
let { data:baseResult } = await _vue.$axios.get(`/student-service/city/${parentId}`)
// 处理查询结果,如果是县,表示是叶子
baseResult.data.forEach(city=>{
city.leaf = node.level >=2
})
// 通过调用resolve将子节点数据返回,通知组件数据加载完成
resolve(baseResult.data);
},
label: 'cityName',
value: 'cid'
}, 完善添加

添加 {{student}} 男 女
1)查询所有课程:后端实现

package com.czxy.course.controller;
import com.czxy.course.service.TbCourseService;
import com.czxy.domain.TbCourse;
import com.czxy.vo.BaseResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* @author 桐叔
* @email liangtong@itcast.cn
* @description
*/
@RestController
@RequestMapping("/course")
public class TbCourseController {
@Resource
private TbCourseService tbCourseService;
@GetMapping
public BaseResult findAll() {
List list = tbCourseService.list();
return BaseResult.ok("查询成功",list);
}
}
2)显示所有课程:前端实现

添加 {{student}} 男 女 {{course.cname}}
3)中间表:后端基本+添加

package com.czxy.course.controller;
import com.czxy.course.service.TbStudentCourseService;
import com.czxy.domain.TbStudentCourse;
import com.czxy.vo.BaseResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author 桐叔
* @email liangtong@itcast.cn
* @description
*/
@RestController
@RequestMapping("/stduentCourse")
public class TbStudentCourseController {
@Resource
private TbStudentCourseService tbStudentCourseService;
@PostMapping
public BaseResult save(@RequestBody TbStudentCourse tbStudentCourse) {
// 保存
boolean result = tbStudentCourseService.save(tbStudentCourse);
if(result) {
return BaseResult.ok("添加成功");
}
return BaseResult.error("添加失败");
}
}
4)修改后端添加(feign远程调用)
在学生服务中
定义选课feign
修改学生添加,保存选课信息
定义选课feign

package com.czxy.student.feign;
import com.czxy.domain.TbStudentCourse;
import com.czxy.vo.BaseResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* @author 桐叔
* @email liangtong@itcast.cn
* @description
*/
//@FeignClient(value = "服务名", path = "controller类路径")
@FeignClient(value = "course-service", path = "/stduentCourse")
public interface StudentCourseFeign {
@PostMapping
public BaseResult save(@RequestBody TbStudentCourse tbStudentCourse);
}
版本1:修改学生添加,保存选课信息

package com.czxy.student.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.czxy.domain.TbStudent; import com.czxy.domain.TbStudentCourse; import com.czxy.student.feign.StudentCourseFeign; import com.czxy.student.mapper.TbStudentMapper; import com.czxy.student.service.TbStudentService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; /** * @author 桐叔 * @email liangtong@itcast.cn * @description */ @Service @Transactional public class TbStudentServiceImpl extends ServiceImplimplements TbStudentService { @Resource private StudentCourseFeign studentCourseFeign; @Override public boolean addStudent(TbStudent tbStudent) { // 保存学生基本信息 int result = baseMapper.insert(tbStudent); // 保存选课信息 for (Integer courseId : tbStudent.getCourseIds()) { // 准备选课对象 TbStudentCourse tbStudentCourse = new TbStudentCourse(); tbStudentCourse.setCid(courseId); tbStudentCourse.setSid(tbStudent.getSid()); // TODO 保存 ,没有处理异常 studentCourseFeign.save(tbStudentCourse); } return result == 1; } }
版本2:修改学生添加,保存选课信息(课程有错,学生回滚)
package com.czxy.student.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.czxy.domain.TbStudent; import com.czxy.domain.TbStudentCourse; import com.czxy.student.feign.StudentCourseFeign; import com.czxy.student.mapper.TbStudentMapper; import com.czxy.student.service.TbStudentService; import com.czxy.vo.BaseResult; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; /** * @author 桐叔 * @email liangtong@itcast.cn * @description */ @Service @Transactional public class TbStudentServiceImpl extends ServiceImplimplements TbStudentService { @Resource private StudentCourseFeign studentCourseFeign; @Override public boolean addStudent(TbStudent tbStudent) { // 保存学生基本信息 int result = baseMapper.insert(tbStudent); // 记录操作结果 boolean flag = result == 1; // 保存选课信息 for (Integer courseId : tbStudent.getCourseIds()) { // 准备选课对象 TbStudentCourse tbStudentCourse = new TbStudentCourse(); tbStudentCourse.setCid(courseId); tbStudentCourse.setSid(tbStudent.getSid()); // 保存课程 正确 code == 20000 , 错误 code == 0 BaseResult baseResult = studentCourseFeign.save(tbStudentCourse); flag &= (baseResult.getCode() == 20000); } // 如果false抛异常 ,解决:课程有错,学生回滚 if(flag == false) { throw new RuntimeException("添加失败"); } //TODO 未解决问题:部分课程已经提交,需使用“分布式事务”解决 return flag; } }

1) 分析
基本数据:只要查询就可以回显(姓名、性别等)
关联数据:
班级:有数据直接回显
城市:需要一个==数组==,存放一组城市数据,目前有的数据为字符串1,2,3。将在前端处理数据。
选课:也需要一个数组,存在一组课程id数据。将在后端处理数据。
2)后端
编写controller
编写service
编写controller

@GetMapping("/{studentId}")
public BaseResult findById(@PathVariable("studentId") Integer studentId) {
// 查询
TbStudent tbStudent = tbStudentService.findById(studentId);
// 返回
return BaseResult.ok("查询详情", tbStudent);
} 编写service

TbStudent findById(Integer studentId);
@Override
public TbStudent findById(Integer studentId) {
//1 学生详情
TbStudent tbStudent = baseMapper.selectById(studentId);
//2 选课信息
BaseResult> tbCourseListBaseResult = courseFeign.findAllByStudentId(studentId);
List tbCourseList = tbCourseListBaseResult.getData();
tbStudent.setCourseList(tbCourseList);
tbStudent.setCourseIds(tbCourseList.stream().map(course->course.getCid()).collect(Collectors.toList()));
//3 返回
return tbStudent;
}
3)前端

openStudentDialog(studentId) {
// 查询所有的班级
this.findAllClasses()
// 查询所有的选课
this.findAllCourse()
// 修改前查询详情
if(studentId) {
this.findStudentById(studentId)
this.studentDialogTitle = '修改学生'
} else {
this.studentDialogTitle = '添加学生'
}
// 控制变量
this.dialogStudentVisible = true
},
async findStudentById(sid) {
let {data:baseResult} = await this.$axios.get(`/student-service/student/${sid}`)
// 获得结果
this.student = baseResult.data
// 处理数据:城市 "cityIds": "320000,321300,321322" --> cid : ["320000","321300","321322"]
this.student.cids = this.student.cityIds.split(',')
}
4)级联菜单回显
element ui的级联菜单先渲染组件时,如果有数据,将触发自动查询功能。
步骤:
定义变量,隐藏级联菜单
查询数据成功后,显示菜单
提交数据后,隐藏菜单
【待完善】,点击x后,没有处理
定义变量,隐藏级联菜单

查询数据成功后,显示菜单

提交数据后,隐藏菜单

【待完善】,点击x后,没有处理
1)学生服务
优化controller

修改service
编写feign
修改service

编写feign

2)课程服务

@DeleteMapping("/delete/student/{sid}")
public BaseResult deleteAllBySid(@PathVariable("sid") Integer sid) {
// 1 设置条件
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.eq("s_id", sid);
// 2 删除
boolean remove = tbStudentCourseService.remove(queryWrapper);
// 3 返回
if(remove) {
return BaseResult.ok("删除成功");
}
return BaseResult.error("删除失败");
}
3)修复前端
性别回显

添加级联显示
