谷粒学院,是一个B2C模式的职业技能在线教育系统,分为前台用户系统和后台运营平台。
权限:会员、系统运营
会员模块:门户、课程中心、用户中心、讲师、文章、问答
运营系统:会员管理、讲师管理、课程管理、文章资讯、问答管理、广告管理、统计分析、帮助中心、网站管理、短信管理、邮件管理、系统消息
插件功能:微信支付、微信登录、阿里云OSS、阿里云视频点播、阿里云短信
架构设计需要考虑的几个方面:
性能:主要考虑访问频率,每个用户每天的访问次数。项目初始阶段用户的访问量并不大,如果考虑做运营推广,可能会迎来服务器访问量骤增,因此要考虑分布式部署,引入缓存。
可扩展性:系统功能会随着用户量的增加以及多变的互联网用户需求不断地扩展,因此考虑到系统的可扩展性的要求需要使用微服务架构,引入消息中间件
高可用:系统一旦宕机,将会带来不可挽回的损失,因此必须做负载均衡,甚至是异地多活这类复 杂的方案。如果数据丢失,修复将会非常麻烦,只能靠人工逐条修复,这个很难接受,因此需要考 虑存储高可靠。我们需要考虑多种异常情况:机器故障、机房故障,针对机器故障,我们需要设计MySQL 同机房主备方案;针对机房故障,我们需要设计 MySQL 跨机房同步方案。
安全性:系统的信息有一定的隐私性,例如用户的个人身份信息,不包含强隐私(例如玉照、情感)的信息,因此使用账号密码管理、数据库访问权限控制即可。
@Autowired
private TeacherService teacherService;
//将数据库所有数据返回
@GetMapping
public List list(){
return teacherService.list(null);
}
//根据id逻辑删除数据
@DeleteMapping("{id}")
public boolean removeById(@PathVariable String id){
return teacherService.removeById(id);
}
乐观锁:
分页插件:
逻辑删除插件:
SQL执行性能分析插件:
项目中将响应封装成json格式返回。
统一返回的数据包含状态码、返回消息、数据这几部分内容。
实现步骤:
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
@ApiOperation(value = "分页讲师列表")
@GetMapping("{page}/{limit}")
public R pageList(
@ApiParam(name = "page", value = "当前页码", required = true)
@PathVariable Long page,
@ApiParam(name = "limit", value = "每页记录数", required = true)
@PathVariable Long limit){
Page pageParam = new Page<>(page, limit);
teacherService.page(pageParam, null);
List records = pageParam.getRecords();
long total = pageParam.getTotal();
return R.ok().data("total", total).data("rows", records);
}
@ApiModel(value = "Teacher查询对象", description = "讲师查询对象封装")
@Data
public class TeacherQuery implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "教师名称,模糊查询")
private String name;
@ApiModelProperty(value = "头衔 1高级讲师 2首席讲师")
private Integer level;
@ApiModelProperty(value = "查询开始时间", example = "2019-01-01 10:10:10")
private String begin;//注意,这里使用的是String类型,前端传过来的数据无需进行类型转换
@ApiModelProperty(value = "查询结束时间", example = "2019-12-01 10:10:10")
private String end;
}
接口:
package com.guli.edu.service;
public interface TeacherService extends IService {
void pageQuery(Page pageParam, TeacherQuery teacherQuery);
}
实现:
package com.guli.edu.service.impl;
@Service
public class TeacherServiceImpl extends ServiceImpl implements TeacherService {
@Override
public void pageQuery(Page pageParam, TeacherQuery teacherQuery) {
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.orderByAsc("sort");
if (teacherQuery == null){
baseMapper.selectPage(pageParam, queryWrapper);
return;
}
String name = teacherQuery.getName();
Integer level = teacherQuery.getLevel();
String begin = teacherQuery.getBegin();
String end = teacherQuery.getEnd();
if (!StringUtils.isEmpty(name)) {
queryWrapper.like("name", name);
}
if (!StringUtils.isEmpty(level) ) {
queryWrapper.eq("level", level);
}
if (!StringUtils.isEmpty(begin)) {
queryWrapper.ge("gmt_create", begin);
}
if (!StringUtils.isEmpty(end)) {
queryWrapper.le("gmt_create", end);
}
baseMapper.selectPage(pageParam, queryWrapper);
}
}
@ApiOperation(value = "分页讲师列表")
@GetMapping("{page}/{limit}")
public R pageQuery(
@ApiParam(name = "page", value = "当前页码", required = true)
@PathVariable Long page,
@ApiParam(name = "limit", value = "每页记录数", required = true) 456789
@PathVariable Long limit,
@ApiParam(name = "teacherQuery", value = "查询对象", required = false)
TeacherQuery teacherQuery){
Page pageParam = new Page<>(page, limit);
teacherService.pageQuery(pageParam, teacherQuery);
List records = pageParam.getRecords();
long total = pageParam.getTotal();
return R.ok().data("total", total).data("rows", records);
}
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("gmtCreate", new Date(), metaObject);
this.setFieldValByName("gmtModified", new Date(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("gmtModified", new Date(), metaObject);
}
}
@ApiModelProperty(value="创建时间")
@TableField(fill=FieldFill.INSERT)
private Date gmtCreate;
@ApiModelProperty(value="更新时间")
@TableField(fill=FieldFill.INSERT_UPDATE)
private Date gmtModified;
@ApiOperation(value = "新增讲师")
@PostMapping
public R save(
@ApiParam(name = "teacher", value = "讲师对象", required = true)
@RequestBody Teacher teacher){
teacherService.save(teacher);
return R.ok();
}
@ApiOperation(value = "根据ID查询讲师")
@GetMapping("{id}")
public R getById(
@ApiParam(name = "id", value = "讲师ID", required = true)
@PathVariable String id){
Teacher teacher = teacherService.getById(id);
return R.ok().data("item", teacher);
}
@ApiOperation(value = "根据ID修改讲师")
@PutMapping("{id}")
public R updateById(
@ApiParam(name = "id", value = "讲师ID", required = true)
@PathVariable String id,
@ApiParam(name = "teacher", value = "讲师对象", required = true)
@RequestBody Teacher teacher){
teacher.setId(id);
teacherService.updateById(teacher);
return R.ok();
}
/**
* 统一异常处理类
*/
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public R error(Exception e){
e.printStackTrace();
return R.error();
}
}
构建用户界面的渐进式框架
运行在服务端的JavaScript
主要实现讲师、课程、文章、评论的增删改查功能。
查询有三种:分页查询、条件查询、根据id查询(适用于页面回显)。
删除主要为逻辑删除。(在数据库表中多定义一个flag字段)
修改主要为普通的update的SQL语句。
增加主要为普通的insert的SQL语句。
在后台系统中我们需要实现讲师添加、删除以及讲师信息修改、查询功能,课程添加、删除、课程信息修改功能,文章添加、文章修改功能,评论添加、修改功能。
在前台系统中我们需要实现分页展示讲师列表功能、分页展示课程列表功能、根据课程获取对应评论功能、分页展示文章功能。
主要实现基于阿里云OSS的图片上传功能。
这个上传功能的实现很简单,通过创建上传文件流,然后获取文件名称,为上传文件命名:日期+UUID(唯一标识符),然后返回上传文件的url。
主要实现基于阿里云视频点播服务的视频添加和视频播放功能。
这个功能的实现也不难,主要通过阿里云视频点播服务中的sdk,将本地视频上传至阿里云,然后阿里云会为上传视频配置唯一id,获取id可以播放对应的视频。
主要实现基于登录注册功能。
首先在注册功能中,我们需要填写用户昵称、手机号、邮箱、密码,并将数据封装成RegisterVo类保存于数据库的ucenter表中。
其次在登录功能中,有两种登录方式,一种是根据用户手机号获取验证码登录,一种是微信登录。
下面重点讲常见的登录注册方式。
1.单一服务器模式
2.SSO模式(单点登录模式)
3.Token
4.JWT身份验证
JWT包括:JWT头、有效载荷、哈希签名。
5.OAuth2.0
6.微信登录如何实现?
基于微信支付sdk。
课程支付步骤: