第一个页面展示讲师数据,以及增删改操作(数据回显等)
数据库表是edu_teacher
文章图片
service_edu模块
先代码生成器生成
然后在controller里@Autowired注入service,调用方法来实(@RestController,@RequestMapping在类上)
写application.properties配置文件
创建启动类@SpringBootApplication,内容为SpringApplication.run(当前类.class,args)
创建一个配置类@Configuration,加上@MapperScan("")扫描路径
细节
1.返回的json数据的时间和我们的时间差八小时,需要在配置文件设置
#返回json的全局时间格式 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.jackson.time-zone=GMT+8
2.需要获取路径中的值,要在xxxMapping("{xxx}")
在方法的参数里用@PathVariable来获取值
3.浏览器测试只能测get提交,其他方式比如post,delete得用测试工具比如swagger测
4.分页查询需要在路径里输入当前页和每页记录数,用@PathVariable来获取
条件组合查询带分页功能
先创建一个vo类,用来封装前端得到的条件。然后通过对象的方式传递给后端的接口
文章图片
在对象前加@RequestBody会将条件都封装进一个json对象里来传递,而不加则是一个个单独的数据,required=false表示可以不填写,但是这种注解的方式必须是post提交。
//条件查询并分页,以对象形式得到条件
@PostMapping("pageTeacherCondition/{current}/{limit}")//用RequestBody传递json对象到对象中,需要使用post
public R pageTeacherCondition(@PathVariable long current,
@PathVariable long limit,
@RequestBody(required = false) TeacherQuery teacherQuery) {//创建page对象
Page pageTeacher = new Page<>(current, limit);
//构建条件
QueryWrapper wrapper = new QueryWrapper<>();
//多条件组合查询
//动态sql,判断条件值是否为空,不为空就拼接条件
String name = teacherQuery.getName();
Integer level = teacherQuery.getLevel();
String begin = teacherQuery.getBegin();
String end = teacherQuery.getEnd();
if (!StringUtils.isEmpty(name)) {
wrapper.like("name", name);
}
if (!StringUtils.isEmpty(level)) {
wrapper.eq("level", level);
}
if (!StringUtils.isEmpty(begin)) {
wrapper.ge("gmt_create", begin);
//大于等于
}
if (!StringUtils.isEmpty(end)) {
wrapper.le("gmt_create", end);
//小于等于
}//排序,按时间降序
wrapper.orderByDesc("gmt_create");
//调用方法查询并分页
teacherService.page(pageTeacher, wrapper);
long total = pageTeacher.getTotal();
//总记录数
List records = pageTeacher.getRecords();
//数据集合
return R.ok().data("total",total).data("rows",records);
}
在实际开发中,这些具体的代码应该写在serviceimpl里,controller里不写具体代码,只是调用service的方法。
前端部分
框架默认的登录页面的登录请求访问的是config里的dev.env.js里的BASE_API加上src的api的login.js里的login方法的url。
在后端EduLoginController里写登录以及用户信息的接口,再在前端的api的login.js里调用接口
完成后会出现一个问题,跨域问题(一个地址去访问另一个地址,协议、ip、端口号任一不同都是跨域)
解决方法:
在controller上加@CrossOrigin,可以允许跨域访问
之后会通过gataway网关来解决这个问题
添加路由(模仿):
{
//这里是讲师管理菜单(路由)
path: '/teacher',
component: Layout,
redirect: '/teacher/table',//默认是显示这个而不是save
name: '讲师管理',
meta: { title: '讲师管理', icon: 'example' },//icon是图标
children: [
{
path: 'table',
name: '讲师列表',
component: () => import('@/views/edu/teacher/list'),
meta: { title: '讲师列表', icon: 'table' }
},
{
path: 'save',
name: '添加讲师',
component: () => import('@/views/edu/teacher/save'),//引入路由对应的页面 ( @/相当于./)
meta: { title: '添加讲师', icon: 'tree' }
},
//隐藏路由,由修改按钮跳转
{
path:'edit/:id',
name:'EduTeacherEdit',
component:() => import('@/views/edu/teacher/save'),
meta: { title: '编辑讲师',noCache:true },//不缓存
hidden:true //隐藏路由
}
]
}
调用接口(类似):
export default{//显示课程分类列表
getSubjectList(){
return request({
url: `/eduservice/subject/getAllSubject`,//用的是piao
method: 'get',
})
}}
页面.vue引入api的js文件
import xxx from ''export default{data:{},
//data可以写为data(){return{}}created(){},methods:{}}
分页接口
//1.讲师列表(条件分页)
//current当前页,limit每页记录数,teacherQuery条件对象,通过对象获取类的全部参数
getTeacherListPage(current,limit,teacherQuery){
return request({
//方式一url: '/eduservice/teacher/pageTeacherCondition/'+current+"/"+limit,
url: `/eduservice/teacher/pageTeacherCondition/${current}/${limit}`,//用的是piao
method: 'post',
//teacherQuery条件对象,由于后端使用requestbody获取数据
//要用到data,data表示把对象转换成json传递到接口(不是requestbody直接用params:参数)
data:teacherQuery
})
}
第二个页面根据文件添加课程分类,以树形显示
实体:
public class EduSubject implements Serializable {private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "https://www.it610.com/article/课程类别ID")
@TableId(value = "https://www.it610.com/article/id", type = IdType.ID_WORKER_STR)
private String id;
@ApiModelProperty(value = "https://www.it610.com/article/类别名称")
private String title;
@ApiModelProperty(value = "https://www.it610.com/article/父ID")
private String parentId;
@ApiModelProperty(value = "https://www.it610.com/article/排序字段")
private Integer sort;
@ApiModelProperty(value = "https://www.it610.com/article/创建时间")
@TableField(fill = FieldFill.INSERT)//自动填充
private Date gmtCreate;
@ApiModelProperty(value = "https://www.it610.com/article/更新时间")
@TableField(fill = FieldFill.INSERT_UPDATE)//自动填充
private Date gmtModified;
}
//一级分类
@Data
public class OneSubject {
private String id;
private String title;
//一个一级分类里有多个二级分类
private List children = new ArrayList<>();
}
//二级分类
@Data
public class TwoSubject {private String id;
private String title;
}
根据文件添加课程分类
//添加课程分类
@Override
public void saveSubject(MultipartFile file,EduSubjectService subjectService) {try {
//文件输入流
InputStream in = file.getInputStream();
//读取Excel
EasyExcel.read(in, SubjectData.class,new SubjectExcelListener(subjectService)).sheet().doRead();
} catch (Exception e) {
e.printStackTrace();
}
}
得到数据库的课程列表
//课程分类列表
@Override
public List getAllOneTwoSubject() {
//查询所有一级分类 parent_id=0,eq表示等于
QueryWrapper wrapperOne = new QueryWrapper<>();
wrapperOne.eq("parent_id", "0");
List oneSubjectList = baseMapper.selectList(wrapperOne);
//查询所有二级分类,ne表示不等于
QueryWrapper wrapperTwo = new QueryWrapper<>();
wrapperTwo.ne("parent_id", "0");
List twoSubjectList = baseMapper.selectList(wrapperTwo);
//创建list集合存储最终封装数据
List finalSubjectList = new ArrayList<>();
//封装一级分类
//把查询出来的一级分类的集合遍历,得到每个一级分类对象,放到最终集合里
for (int i = 0;
i < oneSubjectList.size();
i++) {
//得到oneSubjectList里的每个edusubject对象
EduSubject eduSubject = oneSubjectList.get(i);
//把edusubject对象的值取出来放到oneSubject对象里面OneSubject oneSubject = new OneSubject();
//oneSubject.setId(eduSubject.getId());
//oneSubject.setTitle(eduSubject.getTitle());
//这个spring框架的工具作用和上面两行一样,但是当要设置的数据过多时,一个个写不现实用封装工具更便利
BeanUtils.copyProperties(eduSubject,oneSubject);
//多个Onesubject对象放到finalSubjectList集合里
finalSubjectList.add(oneSubject);
//封装二级分类
//把二级分类遍历放到一级分类集合中(二级分类的循环要放在一级分类的循环里)
List twoFinalSubjectList = new ArrayList<>();
//遍历二级分类list集合
for (int m = 0;
m < twoSubjectList.size();
m++) {
EduSubject tSubject = twoSubjectList.get(m);
//判断二级分类属于哪个一级分类
if (tSubject.getParentId().equals(eduSubject.getId())) {
TwoSubject twoSubject = new TwoSubject();
//把tsubject的值放到twosubject里,再放到twoFinalSubjectList里
BeanUtils.copyProperties(tSubject,twoSubject);
twoFinalSubjectList.add(twoSubject);
}}//把二级分类放到一级分类里
oneSubject.setChildren(twoFinalSubjectList);
}
return finalSubjectList;
}
eduvideocontroller里有一个删除小节同时删除视频
需要通过edu模块调用vod 模块的接口方法来实现
将两个服务都在nacos注册中心里注册,才能互相调用
依赖
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
org.springframework.cloud
spring-cloud-starter-alibaba-nacos-discovery
org.springframework.cloud
spring-cloud-starter-openfeign
# 服务端口 server.port=8001 # 服务名 spring.application.name=service-edu
# nacos服务地址 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
#配置熔断器,开启熔断机制 feign.hystrix.enabled=true # 设置hystrix超时时间,默认1000ms hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=6000
启动类上加
@EnableDiscoveryClient//nacos注册
当服务启动,nacos里就会有这个服务名(账号密码都是nacos,8848端口)
@EnableFeignClients//服务调用
在调用方启动类加上这个注解
然后
import java.util.List;
//调用的服务的名称,以及熔断后执行方法的类
@FeignClient(name="service-vod",fallback = VodClientDefeat.class)
@Component
public interface VodClient {//定义调用的方法的完全路径和方法
//根据视频id删除阿里云视频
@DeleteMapping("/eduvod/video/removeAlyVideo/{id}")
public R removeAlyVideo(@PathVariable("id") String id);
//删除章节的时候删除所属的多个视频
@DeleteMapping("/eduvod/video/delete-batch")
public R deleteBatch(@RequestParam("videoIdList") List videoIdList);
}
//当服务器故障导致熔断器起作用断开连接,就会执行这个实现类里的方法
@Component
public class VodClientDefeat implements VodClient{
@Override
public R removeAlyVideo(String id) {
return R.error().message("删除视频出错");
}@Override
public R deleteBatch(List videoIdList) {
return R.error().message("删除多个视频出错");
}
}
【数据库|谷粒学院之讲师管理模块】
推荐阅读
- 谷粒学院|谷粒学院day6 头像上传模块与课程分类模块
- opencv|opencv for android(二十三)(使用opencv人脸64点位实现人脸装饰物)
- 数据结构|二叉树的经典面试题(你值得拥有)
- java|写简洁java代码的小技巧
- android|Android 实现模拟地图定位功能
- 2022年【米哈游】 金三银四 三月内推开始啦!不加班福利好,200+个岗位任你挑选,赶快来看吧!
- 架构师|年后腾讯、阿里、滴滴后台面试题汇总总结 — (含答案)
- 如何写一份优秀的Java程序员简历
- 多线程控制 countDownLatch、CyclicBarrier、Semaphore 总结