2021SC@SDUSC
目录
引言
在教务系统中教师模块占据着重要的地位,主要涉及教师创建新课程,将新创建的课程(以公告形式)发布出去以便学生进行选择,以及添加学生等功能。本模块使用的框架是nestjs框架,使用DTO
(数据传输对象)模式来进行网络发送数据,此外本模块涉及教师信息以及课程信息,使用mongoose的Schema模块来进行数据结构的定义和管理。
代码分析
mongoose的安装
Mongoose为模型提供了一种直接的,基于scheme结构去定义你的数据模型。
使用npm来安装mongoose
$ npm install mongoose
使用Schema来定义数据模型
Mongoose 的一切始于 Schema。每个 schema 都会映射到一个 MongoDB collection ,并定义这个collection里的文档的构成。我们通过从mongoose中导入Schema,并创建Schema对象来定义教师和课程信息的数据模型。
import { Schema } from 'mongoose';
export const TeacherInfoSchema = new Schema(
{
//教工号
tid: String,
//教师姓名
tname: String,
//任课序号
cidList: [String],
},
{ versionKey: false },
);
我们定义了教师的教工号(String),教师姓名(String),任课序号(String数组)这些教师基本信息的数据模型。这里的每个属性的类型都会被转换为 在 blogSchema
里定义对应的 SchemaType。
import { Schema } from 'mongoose';
export const CourseInfoSchema = new Schema(
{
//课编号( 主键 )
cid: String,
//课序号( 可选 )
sequence: String,
//课程名
cname: String,
//任课老师教工号
tid: String,
//任课老师姓名
tname: String,
//插件功能表
plugins: [
{
pname: String,
pstatus: Boolean,
},
],
//学生列表
stuInfos: [
{
//学工号
sid: String,
//姓名
sname: String,
},
],
},
{ versionKey: false },
);
课程信息中我们定义了课程编号(String),课程序号(String),课程名(String),任课老师教工号(String),任课老师姓名(String),插件功能表(对象数组),学生课表(对象数组)这些属性来描述课程的信息。
Schema模块绑定collection
从schema.ts文件中导入CourseInfoSchema和TeacherInfoSchema模块,并分别在ACMODELINFO和ATMODELINFO中绑定这两个模块,并指定collection。
import { CourseInfoSchema } from '../schemas/couser-info.schema';
import { TeacherInfoSchema } from '../schemas/teacher-info.schema';
export const ACMODELINFO = {
name: 'courseInfo',
schema: CourseInfoSchema,
collection: 'courses',
};
export const ATMODELINFO = {
name: 'teacherInfo',
schema: TeacherInfoSchema,
collection: 'teachers',
};
其中CourseInfoSchema绑定courses的文档中,TeacherInfoSchema绑定teachers文档。
创建DTO对象
我们需要确定 DTO
(数据传输对象)模式。DTO
是一个对象,它定义了如何通过网络发送数据。我们可以通过使用 TypeScript 接口(Interface)或简单的类(Class)来定义 DTO 模式。
在教务系统中的数据传输的操作包括添加学生,发布公告和创建新课程。我们需要在dto对象中创建这些操作过程中需要使用的相关属性。
add-stu.dto
export class AddStuDto {
// @ApiProperty()
readonly tid: string;
readonly cid: string;
readonly stuInfos: [
{
sid: string;
sname: string;
},
];
}
在添加学生操作中需要指明要添加学生的课程号以及任课该课程的教师编号,这样能唯一的确定一门课程,此外还需要指明添加到该课程的学生的信息,需要一个列表来收集这些学生信息。因此我们在AddStuDto对象中定义了教师教工号tid,课程编号cid,学生信息列表stuInfos属性。
create-course.dto
import { ApiProperty } from '@nestjs/swagger';
export class CreateCourseDto {
//@ApiProperty({description: "任课老师学工号"})
readonly tid: string;
//@ApiProperty({description: "课序号"})
readonly sequence: string;
//@ApiProperty({description: "课程名"})
readonly cname: string;
}
在创建新课程的操作中需要指明所创建课程的序号,课程名称以及任课老师工号来标识该课程。
announce.dto
import { ApiProperty } from '@nestjs/swagger';
export class AnnounceDto {
//@ApiProperty({ description: '教工号' })
readonly tid: string;
//@ApiProperty({ description: '内容' })
readonly content: string;
//@ApiProperty({ description: '选中班级' })
readonly cid: [string];
}
在发布公告的操作中要指定所发送该公告的老师的教工号,同时还需要指明发布公告的内容以及发送给哪些班级的班号。
配置控制器
使用@Controller装饰器来指定请求路径。在AdminController类中的构造器中创建并注入adminService和studentSerice这两个服务器类。
import { Controller } from '@nestjs/common';
import { AdminService } from './admin.service';
import { CreateCourseDto } from './dto/create-course.dto';
import { AddStuDto } from './dto/add-stu.dto';
import { GrpcMethod } from '@nestjs/microservices';
import { StudentService } from '../student/student.service';
@Controller('api/admin')
export class AdminController {
constructor(
private adminService: AdminService,
private studentSerice: StudentService,
) {}
//开课
@GrpcMethod('AdminService', 'CreateCourse')
async createCourse(createCourseDto: CreateCourseDto) {
// 创建课程
// 将cid传递给网盘系统
return this.adminService.createCourse(createCourseDto);
}
//导入学生名单
@GrpcMethod('AdminService', 'AddStulist')
async addStulist(addStuDto: AddStuDto) {
await Promise.all([
this.adminService.addStulist(addStuDto),
this.studentSerice.updateStuCourse(addStuDto),
]);
return {
status: 200,
message: '执行成功!',
};
}
//显示所有课程
@GrpcMethod('AdminService', 'GetAllCourse')
async findAllCourse() {
return this.adminService.getAllCourse();
}
}
通过导入相关的DTO对象来实现在进行相关操作时的网络发送数据。在开课操作中将createCourseDto作为参数传给createCourse方法来完成开课过程中的数据传输。在导入学生名单操作中将addStuDto作为参数来完成添加学生名单的操作。其中await Promise.all([...])是使用异步请求来实现学生名单的添加和更新。
此外还定义了findAllCourse查询所有课程的函数,调用的是adminService服务类中的getAllCourse方法来查询课程。
admin.service服务类
相关模块的导入
服务类中实现教师功能的具体操作,除了一些基本装饰器外,该文件还导入了AddStuDto和CreateCourseDto这两个DTO对象以及之前定义的两个定义教师和课程信息数据类型模块,来具体实现添加学生和创建新课程的操作。
import { CreateCourseDto } from './dto/create-course.dto';
import { AddStuDto } from './dto/add-stu.dto';
import { ACMODELINFO, ATMODELINFO } from './contants/db.contants';
数据成员的注入
在该类的构造函数中创建并注入了两个模块,即courseModel课程模块以及teacherModel教师模块用于在后续的具体方法中使用。此外还注入了一个client用于进行网盘操作。
constructor(
@InjectModel(ACMODELINFO.name) private courseModel: Model<any>,
@InjectModel(ATMODELINFO.name) private teacherModel: Model<any>,
@Inject('NetDisk') private client: ClientGrpc,
) {}
使用数据成员client的getService方法获取网盘服务类
onModuleInit() {
this.netdiskService = this.client.getService('NetDiskService');
}
开课
首先先验证教师的教工号是否正确(该老师是否存在),找到后返回该教师的名称
查询到教师信息后通过courseModel模块来创建课程信息对象,其中课程的部分信息从课程创建的Dto对象中获取,此外还需要添加plugins属性,该属性使用对象数组,数组中的对象是整个系统中的其他模块,用于今后的项目整合。
再通过调用courseModel的insertMany方法将我们创建的课程信息对象插入到课程模块中并通过sendCidToNetdisk方法将创建课程的编号cid传给网盘系统来实现课程的开课操作。
async createCourse(createCourseDto: CreateCourseDto) {
//验证tid合法
let query1 = this.teacherModel.find();
query1.where({ tid: createCourseDto.tid });
const doc = await query1.findOne().exec();
if (!doc) {
//找不到老师
throw new GrpcException(status.NOT_FOUND, 'CANNOT FIND TEACHER');
}
let tname = '';
let cid = uuidv4();
try {
tname = doc.get('tname', String);
} catch {
return new GrpcException(status.NOT_FOUND, "CANNOT FIND TEACHER's NAME");
}
/* 创建课程信息对象并插入DB */
let createCinfo = new this.courseModel({
cid: cid,
sequence: createCourseDto.sequence,
cname: createCourseDto.cname,
tid: createCourseDto.tid,
tname: tname,
plugins: [
{ pname: '组队系统', pstatus: true },
{ pname: '答辩系统', pstatus: true },
{ pname: '公告系统', pstatus: true },
{ pname: '文件系统', pstatus: true },
{ pname: '题目系统', pstatus: true },
{ pname: '过程管理系统', pstatus: true },
],
});
let insertPromise = this.courseModel.insertMany([createCinfo]).catch(() => {
throw new GrpcException(status.UNKNOWN, 'CREATE COURSE FAILD');
});
/* 将创建的cid传给网盘系统 */
let sendPromise = this.sendCidToNetdisk(cid).catch(() => {
throw new GrpcException(status.UNKNOWN, 'NETDISK INIT FAILED');
});
await Promise.all([insertPromise, sendPromise]);
return {
status: 200,
message: 'SUCCEED',
};
}
使用网盘服务类netdiskService的makeRootDir方法将我们的cid传到网盘中。
async sendCidToNetdisk(cid: string) {
return this.netdiskService.makeRootDir({ cid: cid }).toPromise();
}
导入学生名单
先调用assertCourseExists方法来通过传入的cid判断课程是否存在
再调用courseModel的updateOne方法将课程号以及学生的信息列表添加到课程模块中来完成教师导入学生名单的操作
async addStulist(addStuDto: AddStuDto): Promise<void> {
await this.assertCourseExists(addStuDto.cid);
await this.courseModel.updateOne(
{
cid: addStuDto.cid,
},
{
stuInfos: addStuDto.stuInfos,
},
);
}
判断课程是否存在
调用courseModel的exists方法来判断课程号是否存在,若返回的变量是空的,那么则表示不存在该课程号的课程。
private async assertCourseExists(cid: string) {
const isExists = await this.courseModel.exists({
cid,
});
if (!isExists) {
throw new GrpcException(status.NOT_FOUND, '课程不存在');
}
}
admin.module模板
该模板文件主要负责整合和管理系统中所使用的model模块,如我们导入的MongooseModule中的ACMODELINFO和ATMODELINFO,ClientsModule网盘模块等。我们还将这教务功能的服务类导出,以便其他系统使用到我们这AdminModule时可以直接使用adminService服务类。
import { Module } from "@nestjs/common";
import { ClientsModule, Transport } from "@nestjs/microservices";
import { MongooseModule } from "@nestjs/mongoose";
import { join } from "path";
import { AnncModule } from "../annc/ann-system.module";
import { AnncService } from "../annc/ann-system.service";
import { StudentModule } from "../student/student.module";
import { AdminController } from "./admin.controller";
import { AdminService } from "./admin.service";
import { ACMODELINFO, ATMODELINFO } from "./contants/db.contants";
@Module({
imports:[
MongooseModule.forFeature([
ACMODELINFO,
ATMODELINFO,]),
ClientsModule.register([
{
name: "NetDisk",
transport: Transport.GRPC,
options: {
package: "netdisk",
protoPath: join(__dirname, "../../../proto/educationPlatform/netdisk.proto"),
url: "211.87.224.142:8089",
},
}
]),
StudentModule],
controllers: [AdminController],
providers: [AdminService],
exports: [AdminService]
})
export class AdminModule{}
总结
教务系统的教师功能主要负责创建和发布新课程,为课程导入学生名单这些操作。其中用到了Mongoose的Schema来定义课程和教师信息的数据模型,还用到了DTO对象来实现网络数据的传输。此外在创建新课程的操作用用到了网盘模块来将我们新课程的信息传给网盘。这次核心代码分析让我了解到管理数据模型的Mongoose工具,并进一步了解了nestjs框架的使用过程。