从在线教育的火热到如今,在线教育的模式出现多种多样,包括:B2C、C2C、B2B2C等业务模式。学成在线采用B2B2C业务模式,即向企业或个人在线教育平台提供教学服务,老师和学生通过平台完成整个教学和学习的过程,市场上类似的平台有:网易云课堂、腾讯课堂等,学成在线的特点是IT职业课程在线教学。
学成在线包括在线教育平台、业务支持系统、基础服务来构建整个功能架构。
模块功能列表:
1.在线教育平台
功能模块名称 | 功能说明 |
---|---|
门户 | 在首页、活动页、专题页等页面提供课程学习入口。 |
教学管理平台 | 教学机构登录系统的入口,通过此来管理机构中的课程相关的数据。 |
运营平台 | 教师登录教学管理中心进行课程管理、资源管理、考试管理等教学活动。 |
2.业务系统支撑
功能模块名称 | 功能说明 |
---|---|
内容管理系统 | 内容管理中对教学机构的课程、课程计划、课程教师、课程营销数据进行管理 |
教学管理中心 | 对平台的入住教学机构数据进行管理和审核,以及课程中设计到的课程作业。 |
学习中心 | 对课程中的学员学习课程的记录数据来进行管理。 |
社交系统 | 论坛系统、问答系统、消息系统、评论系统等信息的管理 |
媒资管理 | 课程计划所关联的课程流媒体资源数据进行管理(直播、录播)。 |
3.基础服务
功能模块名称 | 功能说明 |
---|---|
系统管理 | 对学成在线后端服务提供系统的基础数据。 |
支付系统 | 管理收费课程的交易记录和订单数据。 |
文件服务 | 管理系统中的文件资源,包括课程图片、教师图片等。 |
验证码服务 | 生成系统中的验证码并通过短息服务发送验证码、校验验证码等。 |
统一认证服务 | 对系统中的所有用户资源进行管理,并提供服务中用户资源的认证功能 |
视频点直播 | 在课程学习时需要通过此服务来对视频资源进行播放。 |
剩下的流程图、UML我就不介绍了,直接开始实战
导入linux镜像,并使用docker容器开启组件:
启动nginx导入前端页面:
配置访问host
前端项目中,各个页面跳转都是由域名构成。为了方便需要修改本机host地址。
进入C:\Windows\System32\drivers\etc
目录,修改hosts文件。添加如下内容。温馨提示:做好备份
#门户网站域名
127.0.0.1 www.xuecheng.com
#企业管理后台
127.0.0.1 admin.xuecheng.com
#机构管理后台
127.0.0.1 manage.xuecheng.com
配置成功后,在浏览器打开http://admin.xuecheng.com即可进入企业管理后台页面
项目基于前后端分离的架构进行开发,前后端分离架构总体上包括前端和服务端,通常是多人协作开发
前后端分离开发基于HTTP+JSON交互
通过接口文档(API文档)定义规范
前后端按照文档定义请求及响应数据
这里我们使用YAPI接口文档管理
在写代码前,我们可以回想一下以前是怎么进行身份验证的?
没错 cookie 或session,但是对于前端来说,有移动端页面,h5页面,VUE页面,对于手机端页面可能就没有session,所以用这俩存登录信息,权限检验,就不行了
下面介绍一个新的身份校验工具——JWT
小实例:
首先导入依赖:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
测试类:
@Test
public void testCreateToken() {
//生成token
//1 准备数据
Map map = new HashMap();
map.put("id",1);
map.put("mobile","13800138000");
//2 使用JWT的工具类生成token
long now = System.currentTimeMillis();
String token = Jwts.builder()
.signWith(SignatureAlgorithm.HS512, "zjf") //指定加密算法
.setClaims(map) //写入数据
.setExpiration(new Date(now + 30000)) //失效时间
.compact();
System.out.println(token);
}
//解析token
/**
* SignatureException : token不合法
* ExpiredJwtException:token已过期
*/
@Test
public void testParseToken() {
String token = "...";//这里面换成上面测试打印的token
try {
Claims claims = Jwts.parser()
.setSigningKey("zjf")
.parseClaimsJws(token)
.getBody();
Object id = claims.get("id");
Object mobile = claims.get("mobile");
System.out.println(id + "--" + mobile);
}catch (ExpiredJwtException e) {
System.out.println("token已过期");
}catch (SignatureException e) {
System.out.println("token不合法");
}
}
基本介绍完毕, 下面完成登录功能:
package com.xuecheng.system.controller;
import com.xuecheng.commons.model.dto.LoginDto;
import com.xuecheng.commons.model.vo.ResponseResult;
import com.xuecheng.system.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LoginController {
@Autowired
private UserService userService;
@PostMapping("/login")
public ResponseResult organizationLogin(@RequestBody LoginDto loginDto){
return userService.organizationLogin(loginDto);
}
}
public interface UserService extends IService<User> {
ResponseResult organizationLogin(LoginDto loginDto);
}
serviceImpl层:
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public ResponseResult organizationLogin(LoginDto loginDto) {
if (StrUtil.isBlank(loginDto.getUsername())) {
throw new BusinessException(ErrorCode.DATAERROR);
}
if (StrUtil.isBlank(loginDto.getPassword())) {
throw new BusinessException(ErrorCode.DATAERROR);
}
if (StrUtil.isBlank(loginDto.getUtype())) {
throw new BusinessException(ErrorCode.DATAERROR);
}
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getPhone, loginDto.getUsername());
wrapper.eq(User::getUtype, loginDto.getUtype());
User user = userMapper.selectOne(wrapper);
if (BeanUtil.isEmpty(user)){
User u = new User();
u.setPhone(loginDto.getUsername());
u.setPassword(DigestUtil.md5Hex(loginDto.getPassword()));
u.setUtype(loginDto.getUtype());
userMapper.insert(u);
}
if (!StrUtil.equals(DigestUtil.md5Hex(loginDto.getPassword()), user.getPassword())) {
throw new BusinessException(ErrorCode.LOGINERROR);
}
HashMap<String, Object> map = new HashMap<>();
map.put("userId",user.getId());
map.put("companyId",user.getCompanyId());
map.put("companyName",user.getCompanyName());
String token = JwtUtils.createToken(map, 180);
LoginVo loginVo = new LoginVo();
loginVo.setAccess_token(token);
loginVo.setUsername(user.getPhone());
return ResponseResult.okResult(loginVo);
}
}
mapper层:
public interface UserMapper extends BaseMapper<User> {
}
网关就是系统的入口,封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、缓存、负载均衡、流量管控、路由转发等。学成在线也是通过网关介入所有请求,进行路由转发
package com.xuecheng.web.exception;
import com.xuecheng.commons.enums.ErrorCode;
import lombok.Data;
@Data
public class BusinessException extends RuntimeException{
private ErrorCode errorCode;
public BusinessException(ErrorCode errorCode) {
super(errorCode.getDesc());
this.errorCode = errorCode;
}
}
package com.xuecheng.web.exception;
import com.xuecheng.commons.enums.ErrorCode;
import com.xuecheng.commons.model.vo.ResponseResult;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalException {
@ExceptionHandler(RuntimeException.class)
public ResponseResult exception(RuntimeException e){
e.printStackTrace();
return ResponseResult.errorResult(ErrorCode.ERROR);
}
@ExceptionHandler(BusinessException.class)
public ResponseResult BusinessException(BusinessException e){
ErrorCode errorCode = e.getErrorCode();
return ResponseResult.errorResult(errorCode);
}
}
结果展示:
登录成功会进入商家页面
创建git,以天为单位作为分支,提交到gitee上,用以练习git操作
第一天任务完成