该项目是为“短视频小程序解决方案所提供的后台 Demo”,主要开发语言为 TypeScript,使用 Koa 框架进行搭建,其中使用了丰富的第三方开源库,如 routing-controllers、 class-validator 、 typeorm 、typedi 等。
配置文件
本项目使用
src/conf/config/moduleConfig.json
作为整个 Demo 服务的配置文件。开发者可以根据自身业务进行配置项的调整。{"port": 3000,"db": {"host":"localhost","username":"root","password":"vod@2020#DEMO","port":"3306","database":"short-video"},"secretId": "","secretKey": "","urlKey": "","wxAppConfig": {"appId": "","appSecret": "",}}
配置项 | 描述 |
port | 服务监听的端口号,默认为 3000 。 |
db | 数据库配置。 |
db.host | 数据库连接地址,默认为 localhost 。 |
db.username | 数据库用户名,默认为 root 。 |
db.password | 数据库密码,默认为 vod@2020#DEMO 。 |
db.port | 数据库端口号,默认为 3306 。 |
db.database | 数据库名,默认为 short-video 。 |
secretId | |
secretKey | |
urlKey | 播放 URL 防盗链 Key,用于生成视频防盗链。默认为空。 |
wxAppConfig | 微信小程序配置。 |
wxAppConfig.appId | 微信小程序开发者 AppId。 |
wxAppConfig.appSecret | 微信小程序开发者 AppSecret。 |
接口列表
HTTP 方法 | 接口路由 | 描述 |
POST | /ModifyPassword | 修改密码。 |
POST | /ModifyUserInfo | 修改用户信息。 |
POST | /GetUserInfo | 拉取用户资料。 |
POST | /GetUploadSign | 获取上传签名。 |
POST | /GetVideoList | 获取视频列表。 |
POST | /GetVideoListByUser | 根据用户获取视频列表。 |
POST | /DeleteVideo | 删除视频。 |
POST | /WXAMPGetVideoList | 获取发布到微信小程序的视频列表。 |
POST | /WXAMPLogin | 微信小程序用户登录/注册。 |
代码解读
目录结构
|-- package.json|-- src| |-- conf| |-- controller| |-- dao| |-- dto| |-- entity| |-- index.ts| |-- service| `-- util`-- tsconfig.json
文件/目录 | 说明 |
该文件定义了这个项目所需要的各种依赖模块,以及项目的运行 Script 和配置信息(例如名称、版本、许可证等元数据)。 | |
src | 源代码目录。 |
src/conf | 配置模块。 |
src/controller | 控制器模块。接口的路由和处理定义在此模块,是每个接口处理的入口。 |
src/dao | 数据访问模块。包含了对数据库的增删改查操作。 |
src/dto | 数据传输对象模块。封装了每个接口的请求参数和参数校验。 |
src/entity | 实体模块。封装了实体类与数据库表的映射关系(ORM)。 |
src/index.ts | 程序的入口文件。 |
src/service | 服务模块。封装了一系列服务类的方法。 |
src/util | 工具模块。 |
编译 TypeScript 代码的配置文件。 |
模块介绍
本 Demo 主要包含配置、控制器、数据传输对象、实体、服务、工具等多个模块, 下面将分别介绍这些模块以及实现方式。
程序入口 index.ts
index.ts
是本 Demo 的程序入口文件。包含以下作用:加载服务配置。
连接数据库。
通过 Koa 加载日志和解析
HTTP BODY
的中间件,注册控制器。启动
HTTP Server
,监听服务请求。说明
如果数据库连接出错,服务将启动失败。
配置模块 src/conf
模块文件 | 描述 |
整个 Demo 服务的配置文件。 | |
config.ts | 主要用于读取 moduleConfig.json 文件中的配置信息。 |
控制器模块 src/controller
该模块主要使用第三方库
routing-controllers
进行路由注册和请求处理,使用装饰器(decorator)代替 Koa
中传统的路由指定方式。Controller
类中的每个方法都对应一个业务接口,是接口处理的入口。模块文件 | 描述 |
wxampAccount.ts | 包含了 /WXAMPLogin 小程序账户相关的接口实现。 |
callback.ts | 包括了 /UploadCallback 、/AiContentReviewCallback 等回调相关的接口实现。 |
index.ts | 导出控制器类,以供程序入口文件( index.ts )进行注册。 |
media.ts | 包含了 /WXAMPGetVideoList 、/GetVideoListByUser 、/DeleteVideo 等媒资相关的接口实现。 |
upload.ts | 包含了 /GetUploadSign 上传相关的接口实现。 |
user.ts | 包含了 /ModifyUserInfo 、/GetUserInfo 等用户相关的接口实现。 |
play.ts | 包含了 /PlayVideo 等播放相关的接口实现。 |
下面以
src/controller/media.ts
中/WXAMPGetVideoList
接口为例进行说明。// 媒资相关的控制器@JsonController()export class MediaController {tokenService: TokenService;validatorService: ValidatorService;videoService: VideoService;constructor() {this.tokenService = Container.get(TokenService);this.validatorService = Container.get(ValidatorService);this.videoService = Container.get(VideoService);}/** 获取微信小程序视频列表*/@Post("/WXAMPGetVideoList")public async wxampGetVideoList(@Body({ validate: false }) dto: GetVideoListDTO) {let requestId = uuidv4();let ctx = new ServiceContext(requestId);try {// 校验接口参数await this.validatorService.Validate(ctx, dto);// 获取视频列表let condition = {UserId: "",Status: VideoStatus.PASS,WechatMiniProgramStatus: VideoStatus.PASS,Offset: dto.Offset,Limit: dto.Limit,};let listVideoResult = await this.videoService.SearchVideo(ctx, condition);return new Response(requestId, ErrorCode.OK, "OK", {VideoList: listVideoResult.VideoList,TotalCount: listVideoResult.TotalCount,});} catch (error) {logger.error(ctx, `getVideoList error:`, error);logger.info(ctx, `getVideoList dto:`, dto);logger.error(ctx, `get video list fail: ${error.Code}, ${error.Message}, error:`, error);return new Response(requestId, error.Code, error.Message, {});}}// 其余代码省略...}
MediaController
类通过装饰器(@JsonController
)进行装饰,确保每个处理方法都能以 JSON 字符串返回,并且 HTTP Response
的头部 Content-Type
被设置为 application/json
。MediaController
中定义了一个 wxampGetVideoList
方法,wxampGetVideoList
方法通过 @Post
, 指定 /WXAMPGetVideoList
接口路由到此方法进行处理。wxampGetVideoList
方法中定义了一个数据传输对象 GetVideoListDTO
,并通过 @Body({ validate: false })
装饰,此处将请求中的 HTTP Body
数据注入到 GetVideoListDTO
对象中。为了在校验非法的情况下使用自定义的回包,此处通过指定 validate: false
促使不在注入的同时进行参数校验,而是延迟到在 wxampGetVideoList
方法体中通过 Validate
方法进行显示校验。wxampGetVideoList
方法体中分别调用了Service
服务类中的方法进行接口参数校验、获取视频列表。此处的Service
服务类通过使用 typedi
进行注入和管理。wxampGetVideoList
方法返回 Response
对象, routing-controllers
将其转化为 JSON 字符串返回到请求客户端。数据传输对象模块 src/dto
该模块主要封装了各个接口的请求参数, 并且通过
class-validator
实现参数校验,避免大量重复的if/else
条件判断,提高代码可读性。下面将以 src/dto/media.ts
中 GetVideoListDTO
为例进行说明。// 获取视频列表的 DTOexport class GetVideoListDTO {@IsOptional()@Min(0, {message: "Offset must be greater than or equals to 0",context: {errorCode: ErrorCode.ParamValueLegal,},})Offset: number;@IsOptional()@Min(1, {message: "Limit must be greater than 1",context: {errorCode: ErrorCode.ParamValueLegal,},})@Max(100, {message: "Limit must be lower than 100",context: {errorCode: ErrorCode.ParamValueLegal,},})Limit: number;constructor(offset: number, limit: number) {this.Offset = offset;this.Limit = limit;}}
GetVideoListDTO
类包含了 Offset
和 Limit
字段以及对应的校验条件(通过装饰器进行指定)。对于
`Offset`
字段,通过 @Min
限制 Offset
的最短长度为 1,并且都定义了校验非法情况下的错误信息 message
和错误码 errorCode
。如果违反校验条件,
class-validator
将抛出 ValidationError
错误, ValidationError
的解析可以参考 ValidatorService
中的处理。class-validator
提供了丰富校验条件,具体用法请参考官方文档。实体模块 src/entity
模块文件 | 描述 |
index.ts | 用于导出实体类。 |
user.ts | 定义了 User 实体类,与数据库中 User 表一一对应。 |
video.ts | 定义了 Video 实体类,与数据库中 Video 表一一对应。 |
user_auth.ts | 定义了 UserAuth 实体类,与数据库中 UserAuth 表一一对应。 |
服务模块 src/service
该模块主要封装了一些业务处理的逻辑,作为一个完整的子功能提供给
Controller
类使用。例如 Token
的生成、上传签名的生成、参数校验错误的解析以及数据库相关的操作等。下面以 src/service/video.ts
中的 SearchVideo
方法为例进行说明。// 视频相关的服务类@Service()export class VideoService {videoDao: VideoDao;constructor() {this.videoDao = Container.get(VideoDao);}// 搜索视频public async SearchVideo(ctx: ServiceContext, condition: VideoListCondition) {let videos = await this.videoDao.List(ctx, condition);let totalCount = await this.videoDao.Count(ctx, condition);let result: SearchVideoResult = {VideoList: videos,TotalCount: totalCount,};return result;}// 其余代码省略...}
VideoService
类封装了与视频文件相关的业务处理方法,其中,SearchVideo
方法主要功能是根据搜索条件 condition
进行视频搜索。VideoService
类使用 @Service
进行装饰,确保其可以被纳入 typedi
进行管理。方法签名中有一个的
ServiceContext
上下文对象,后续可以根据业务需要增加一些 Service
类中需要用到的公共字段,避免了对全量的方法进行修改。方法中在调用出错时或业务逻辑不符合预期时,会抛出一个 自定义错误类
VodError
,主要作用是区分不同错误返回给 Controller
类,进而返回给请求客户端。数据访问模块 src/dao
该模块下主要封装了对于数据库的增删改查操作,用于返回业务逻辑需要的实体数据。例如根据条件获取视频列表,修改用户信息,查询用户信息,视频播放信息和用户信息聚合等相关操作。下面将以
src/dao/video.ts
中 VideoDao
为例进行说明。// 视频数据访问类@Service()export class VideoDao {// 保存视频public async Save(ctx: ServiceContext, video: Video) {try {const videoRepository: Repository<Video> = getManager().getRepository(Video);let v = videoRepository.save(video);logger.info(ctx, `video has been saved:`, v);return v;} catch (error) {logger.error(ctx, `save video fail:`, error);throw new VodError(ErrorCode.SaveVideoFail, "Fail to save video");}}// 其余代码省略...}
VideoDao
封装了对 Video
数据库进行的查询修改等操作,其中 Save
需要保存视频信息。工具模块 src/util
该模块下定义了一些工具方法、错误码以及一些公共类(包括
Response
、VodError
、Context
等),您可根据自身业务特点进行扩充。