項目介紹
項目概述
學成在線借鑑了MOOC
(大型開放式網絡課程,即MOOC
(massive open online courses))的設計思想,是一個提供IT職業課程在線學習的平臺,它爲即將和已經加入IT領域的技術人才提供在線學習服務,用戶通過在線學習、在線練習、在線考試等學習內容,最終掌握所學的IT技能,並能在工作中熟練應用。
功能模塊
當前市場的在線教育模式多種多樣,包括:B2C、C2C、B2B2C等業務模式,學成在線採用B2B2C業務模式,即向企業或個人提供在線教育平臺提供教學服務,老師和學生通過平臺完成整個教學和學習的過程,市場上類似的平臺有:網易雲課堂、騰訊課堂等,學成在線的特點是IT職業課程在線教學。
大模塊介紹:
功能模塊名稱 | 功能說明 |
---|---|
門戶 | 在首頁、活動頁、專題頁等頁面提供課程學習入口。 |
學習中心 | 學生登錄學習中心在線學習課程。 |
社交系統 | 社交系統爲老師和學生交流搭建溝通的平臺,包括:問答系統、評論系統、論壇等, 學生和老師通過問答系統提問問題、回答問題,通過評論系統對老師授課進行評論。 |
教學管理中心 | 教師登錄教學管理中心進行課程管理、資源管理、考試管理等教學活動。 |
系統管理中心 | 系統管理員登錄系統管理中心進行分類管理、運維管理等功能。 |
技術架構
學成在線採用當前流行的前後端分離架構開發,由用戶層、UI層、微服務層、數據層等部分組成,爲PC、App、H5等客戶端用戶提供服務。下圖是系統的技術架構圖:
各模塊說明:
序號 | 名稱 | 功能描述 |
---|---|---|
1 | 用戶層 | 用戶層描述了本系統所支持的用戶類型包括:pc用戶、app用戶、h5用戶。pc用戶通過 瀏覽器訪問系統、app用戶通過android、ios手機訪問系統,H5用戶通過h5頁面訪問系 統。 |
2 | CDN | CDN全稱Content Delivery Network,即內容分發網絡,本系統所有靜態資源全部通過 CDN加速來提高訪問速度。系統靜態資源包括:html頁面、js文件、css文件、image圖 片、pdf和ppt及doc教學文檔、video視頻等。 |
3 | 負載均衡 | 系統的CDN層、UI層、服務層及數據層均設置了負載均衡服務,上圖僅在UI層前邊標註 了負載均衡。每一層的負載均衡會根據系統的需求來確定負載均衡器的類型,系統支持 4層負載均衡+7層負載均衡結合的方式,4層負載均衡是指在網絡傳輸層進行流程轉發, 根據IP和端口進行轉發,7層負載均衡完成HTTP協議負載均衡及反向代理的功能,根據 url進行請求轉發。 |
4 | UI層 | UI層描述了系統向pc用戶、app用戶、h5用戶提供的產品界面。根據系統功能模塊特點 確定了UI層包括如下產品界面類型:1)面向pc用戶的門戶系統、學習中心繫統、教學 管理系統、系統管理中心。2)面向h5用戶的門戶系統、學習中心繫統。3)面向app 用戶的門戶系統、學習中心繫統未在上圖標註,在app項目中有詳細說明。 |
5 | 微服務層 | 微服務層將系統服務分類三類:前端服務、後端服務及系統服務。前端服務:主要爲學 習用戶提供學習服務。後端服務:主要爲管理用戶提供教學管理服務。系統服務:公 共服務,爲系統的所有微服務提供公共服務功能。服務網關:提供服務路由、負載均 衡、認證授權等服務。 |
6 | 數據層 | 數據層描述了系統的數據存儲的內容類型,持久化的業務數據使用MySQL和MongoDB 保存,其中MongoDB中主要保存系統日誌信息。消息隊列:存儲系統服務間通信的消 息,本身提供消息存取服務,與微服務層的系統服務連接。索引庫:存儲課程信息的索 引信息,本身提供索引維護及搜索的服務,與微服務層的系統服務連接。緩存:作爲系 統的緩存服務,存儲課程信息、分類信息、用戶信息等,與微服務層的所有服務連接。 文件存儲:提供系統靜態資源文件的分佈式存儲服務,文件存儲服務器作爲CDN服務器 的數據來源,CDN上的靜態資源將最終在文件存儲服務器上保存多份。流媒體服務: 作爲流媒體服務器,存儲所有的流媒體文件。 |
7 | 外部系統接口 | 1)微信、QQ、微博登錄接口,本系統和微信、QQ、微博系統對接,用戶輸入微信、 QQ、微博的賬號和密碼即可登錄本系統。2)微信、QQ、微博分享接口,本系統和微 信、QQ、微博系統對接,可直接將本系統的課程資源信息分享到微信、QQ、微博。 3)支付寶、微信、網銀支付接口,本系統提供支付寶、微信、網銀三種支付接口。 4)短信接口,本系統與第三方平臺對接短信發送接口。5)郵件接口,本系統需要連 接第三方的smpt郵件服務器對外發送電子郵件。6)微信公衆號,本系統與微信公衆號 平臺接口,用戶通過微信公衆號訪問H5頁面。7)點播、直播,前期視頻點播與直播採 用第三方服務方式,本系統與第三方點、直播服務對接,對外提供視頻點播與直播服 務。8)OSS存儲,前期靜態資源文件的存儲採用第三方服務方式,本系統與第三方提 供的OSS存儲服務對接,將系統的靜態資源文件存儲到第三方提供的OSS存儲服務器 上。9)CDN,本系統與第三方CDN服務對接,使用CDN加速服務來提高本系統的訪問 速度。 |
8 | DevOps | DevOps(英文Development和Operations的組合)是一組過程、方法與系統的統稱, 用於促進開發(應用程序/軟件工程)、技術運營和質量保障(QA)部門之間的溝通、 協作與整合。本項目供了許多開發、運營、維護支撐的系統,包括:Eureka服務治理 中心:提供服務治理服務,包括:服務註冊、服務獲取等。Spring Cloud Config服務 配置管理中心:提供服務配置管理服務,包括:配置文件更新、配置文件下發等。 Hystrix Dashboard服務熔斷監控:監控熔斷的請求響應時間、成功率等。Zipkin服務 追蹤監控:監控服務調用鏈路健康情況。Jenkins持續集成服務:提供系統持續集成服 務。Git/GitLab代碼管理服務:提供git代碼管理服務。ELK日誌分析服務:提供elk日誌分 析服務,包括系統運行日誌分析、告警服務。Docker容器化部署服務:將本系統所有 服務採用容器化部署方式。Maven項目管理工具:提供管理項目所有的Java包依賴、項 目工程打包服務。 |
技術棧
重點了解微服務技術棧
學成在線服務端基於Spring Boot構建,採用Spring Cloud微服務框架。
-
持久層:MySQL、MongoDB、Redis、ElasticSearch
-
數據訪問層:使用Spring Data JPA 、Mybatis、Spring Data Mongodb等
-
業務層:Spring IOC、Aop事務控制、Spring Task任務調度、Feign、Ribbon、Spring AMQP、Spring Data Redis等。
-
控制層:Spring MVC、FastJSON、RestTemplate、Spring Security Oauth2+JWT等
-
微服務治理:Eureka、Zuul、Hystrix、Spring Cloud Config等
CMS需求分析
什麼是CMS
CMS
(Content Management System)即內容管理系統,不同的項目對CMS
的定位不同,比如:一個在線教育網站,有些公司認爲CMS
系統是對所有的課程資源進行管理,而在早期網站剛開始盛行時很多公司的業務是網站製作,當時對CMS
的定位是創建網站,即對網站的頁面、圖片等靜態資源進行管理。
CMS有哪些類型
CMS
基本上分爲:針對後臺數據內容的管理、針對前端頁面的管理、針對樣式風格的管理等。比如:一個給企業做網站的公司,其CMS
系統主要是網站頁面管理及樣式風格的管理。
本項目CMS的定位是什麼
本項目作爲一個大型的在線教育平臺,對CMS
系統的定位是對各各網站(子站點)頁面的管理,主要管理由於運營需要而經常變動的頁面,從而實現根據運營需要快速進行頁面開發、上線的需求。
導入基礎工程與Nginx配置(省略)
CMS頁面管理需求
這些頁面的管理流程是什麼?
-
創建站點
一個網站有很多子站點,比如:學成在線有主門戶、學習中心、問答系統等子站點。具體的哪個頁面是歸屬於具體的站點,所以要管理頁面,先要管理頁面所屬的站點。
-
創建模板
頁面如何創建呢?比如電商網站的商品詳情頁面,每個頁面的內容佈局、板式是相同的,不同的只是內容,這個頁面的佈局、板式就是頁面模板,模板+數據就組成一個完整的頁面,最終要創建一個頁面文件需要先定義此頁面的模板,最終拿到頁面的數據再結合模板就拼裝成一個完整的頁面。
-
創建頁面
創建頁面是指填寫頁面的基本信息,如:頁面的名稱、頁面的url地址等。
-
頁面預覽
頁面預覽是頁面發佈前的一項工作,頁面預覽使用靜態化技術根據頁面模板和數據生成頁面內容,並通過瀏覽器預覽頁面。頁面發佈前進行頁面預覽的目是爲了保證頁面發佈後的正確性。
-
頁面發佈
使用計算機技術將頁面發送到頁面所在站點的服務器,頁面發佈成功就可以通過瀏覽器來訪問了。
本項目要實現什麼樣的功能?
-
頁面管理
管理員在後臺添加、修改、刪除頁面信息。
-
頁面預覽
管理員通過頁面預覽功能預覽頁面發佈後的效果。
-
頁面發佈
管理員通過頁面發佈功能將頁面發佈到遠程門戶服務器。頁面發佈成功,用戶即可在瀏覽器瀏覽到最新發布的頁面,整個頁面添加、發佈的過程由於軟件自動執行,無需人工登錄服務器操作。
CMS接口開發
工程導入(省略)
注意:
我這裏導入遇到一個坑,xc-framework-common
的依賴中spring-cloud-starter-feign
導入不成功,在Spring Boot 2.x
後,該依賴更名了,需要將該依賴修改爲:spring-cloud-starter-openfeign
(不需要輸入版本號)。
MongoDB安裝以及集合導入(省略)
請求與響應對象
接收請求參數對象
定義請求模型QueryPageRequest
,此模型作爲查詢條件類型
package com.xuecheng.framework.domain.cms.request;
import com.xuecheng.framework.model.request.RequestData;
import lombok.Data;
@Data
public class QueryPageRequest extends RequestData {
//站點id
private String siteId;
//頁面ID
private String pageId;
//頁面名稱
private String pageName;
//別名
private String pageAliase;
//模版id
private String templateId;
}
響應結果對象
package com.xuecheng.framework.model.response;
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class QueryResponseResult extends ResponseResult {
QueryResult queryResult;
public QueryResponseResult(ResultCode resultCode,QueryResult queryResult){
super(resultCode);
this.queryResult = queryResult;
}
}
API接口定義
在xc-service-api
中定義接口,進行接口的統一管理
package com.xuecheng.api.cms;
import com.xuecheng.framework.domain.cms.request.QueryPageRequest;
import com.xuecheng.framework.model.response.QueryResponseResult;
public interface CmsPageControllerApi {
QueryResponseResult findList(int page, int size, QueryPageRequest queryPageRequest);
}
API接口實現
創建xc-service-manage-cms
工程。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>xc-service-api</artifactId>
<groupId>com.xuecheng</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../xc-service-api/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>xc-service-manage-cms</artifactId>
<dependencies>
<dependency>
<groupId>com.xuecheng</groupId>
<artifactId>xc-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.xuecheng</groupId>
<artifactId>xc-framework-model</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.xuecheng</groupId>
<artifactId>xc-framework-utils</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.xuecheng</groupId>
<artifactId>xc-framework-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
</dependencies>
</project>
application.yml
server:
port: 31001
spring:
application:
name: xc‐service‐manage‐cms
data:
mongodb:
uri: mongodb://192.168.136.110:27017/
database: xc_cms
啓動類
package com.xuecheng.manage_cms;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@EntityScan("com.xuecheng.framework.domain.cms")
@ComponentScan(basePackages={"com.xuecheng.api"})
@ComponentScan(basePackages={"com.xuecheng.manage_cms"})
public class ManageCmsApplication {
public static void main(String[] args) {
SpringApplication.run(ManageCmsApplication.class, args);
}
}
Dao
package com.xuecheng.manage_cms.dao;
import com.xuecheng.framework.domain.cms.CmsPage;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface CmsPageRepository extends MongoRepository<CmsPage, String> {
}
Service
package com.xuecheng.manage_cms.service;
import com.xuecheng.framework.domain.cms.CmsPage;
import com.xuecheng.framework.domain.cms.request.QueryPageRequest;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.framework.model.response.QueryResponseResult;
import com.xuecheng.framework.model.response.QueryResult;
import com.xuecheng.manage_cms.dao.CmsPageRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class CmsPageService {
@Autowired
private CmsPageRepository cmsPageRepository;
public QueryResponseResult findList(int page, int size, QueryPageRequest queryPageRequest) {
if (page <= 0) {
page = 1;
}
// 頁碼從0開始
page = page - 1;
// 查詢
Page<CmsPage> pageResult = cmsPageRepository.findAll(PageRequest.of(page, size));
QueryResult<CmsPage> cmsPageQueryResult = new QueryResult<>();
cmsPageQueryResult.setList(pageResult.getContent());
cmsPageQueryResult.setTotal(pageResult.getTotalElements());
return new QueryResponseResult(CommonCode.SUCCESS, cmsPageQueryResult);
}
}
Controller
package com.xuecheng.manage_cms.controller;
import com.xuecheng.api.cms.CmsPageControllerApi;
import com.xuecheng.framework.domain.cms.request.QueryPageRequest;
import com.xuecheng.framework.model.response.QueryResponseResult;
import com.xuecheng.manage_cms.service.CmsPageService;
import org.springframework.beans.factory.annotation.Autowired;
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;
@RestController
@RequestMapping("cms/page")
public class CmsPageController implements CmsPageControllerApi {
@Autowired
private CmsPageService cmsPageService;
@Override
@GetMapping("list/{page}/{size}")
public QueryResponseResult findList(@PathVariable("page") int page,
@PathVariable("size") int size,
QueryPageRequest queryPageRequest) {
return cmsPageService.findList(page, size, queryPageRequest);
}
}
測試
Swagger
OpenAPI規範(OpenAPI Specification 簡稱OAS)是Linux基金會的一個項目,試圖通過定義一種用來描述API格式或API定義的語言,來規範RESTful服務開發過程,目前版本是V3.0,並且已經發布並開源在github上。
https://github.com/OAI/OpenAPI-Specification
Swagger是全球最大的OpenAPI規範(OAS)API開發工具框架,支持從設計和文檔到測試和部署的整個API生命週期的開發。
https://swagger.io/
Spring Boot 可以集成Swagger,生成Swagger接口,Spring Boot是Java領域的神器,它是Spring項目下快速構建項目的框架。
常用註解
-
@Api:修飾整個類,描述
Controller
的作用@ApiOperation
:描述一個類的一個方法,或者說一個接口。 -
@ApiParam:單個參數描述
-
@ApiModel:用對象來接收參數
-
@ApiModelProperty:用對象接收參數時,描述對象的一個字段
-
@ApiResponse:HTTP響應其中1個描述
-
@ApiResponses:HTTP響應整體描述
-
@ApiIgnore:使用該註解忽略這個API
-
@ApiError :發生錯誤返回的信息
-
@ApiImplicitParam:一個請求參數
-
@ApiImplicitParams:多個請求參數
@ApiImplicitParams參數介紹
屬性 | 取值 | 作用 |
---|---|---|
paramType | 查詢的參數類型 | |
path | 以地址的形式提交數據 | |
query | 直接跟參數完成自動映射賦值 | |
body | 以流的形式提交(POST請求) | |
header | 參數攜帶在request headers(請求頭) | |
form | 以form表單的形式提交(POST) | |
dataType | 參數的數據類型(只是用於展示) | |
Long | ||
String | ||
name | 接收參數名 | |
value | 該參數的描述 | |
required | true/false | 參數是否必填 |
defaultValue | 默認值 |
Swagger配置類
package com.xuecheng.api.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.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class Swagger2Configuration {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.xuecheng"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("學成網api文檔")
.description("學成網api文檔")
.version("1.0")
.build();
}
}
Swagger接口定義
修改CmsPageControllerApi
,使用Swagger
相關注解
package com.xuecheng.api.cms;
import com.xuecheng.framework.domain.cms.request.QueryPageRequest;
import com.xuecheng.framework.model.response.QueryResponseResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
@Api(value = "CMS頁面管理接口", description = "CMS頁面管理接口,提供頁面的增刪改查")
public interface CmsPageControllerApi {
@ApiOperation("分頁查詢頁面列表")
@ApiImplicitParams(
@ApiImplicitParam(name = "page", value = "頁碼", required = true, paramType = "path", dataType = "int"),
@ApiImplicitParam(name = "size", value = "每頁記錄數", required = true, paramType = "path", dataType = "int")
)
QueryResponseResult findList(int page, int size, QueryPageRequest queryPageRequest);
}
在QueryPageRequest
使用@ApiModelProperty
對屬性進行解析
package com.xuecheng.framework.domain.cms.request;
import com.xuecheng.framework.model.request.RequestData;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class QueryPageRequest extends RequestData {
//站點id
@ApiModelProperty("站點ID")
private String siteId;
//頁面ID
@ApiModelProperty("頁面ID")
private String pageId;
//頁面名稱
@ApiModelProperty("頁面名稱")
private String pageName;
//別名
@ApiModelProperty("頁面別名")
private String pageAliase;
//模版id
@ApiModelProperty("模板ID")
private String templateId;
}