feat:初始化 -融骅
This commit is contained in:
32
serve/src/app.module.ts
Normal file
32
serve/src/app.module.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import MySQL from 'src/common/plugins/mysql';
|
||||
import { SystemModule } from './system/system.module';
|
||||
import { OnlineTeachingModule } from './online-teaching/online-teaching.module';
|
||||
import { OnlineCourseModule } from './online-course/online-course.module';
|
||||
import { AssessmentEvaluationModule } from './assessment-evaluation/assessment-evaluation.module';
|
||||
import { ResourceModule } from './resource/resource.module';
|
||||
import { EvaluationModule } from './evaluation/evaluation.module';
|
||||
import { InitializeModule } from './initialize/initialize.module';
|
||||
import { StatisticModule } from './statistic/statistic.module';
|
||||
import { ArticleManageModule } from './article_manage/article_manage.module';
|
||||
import { ManualsManage } from './manuals_manage/entities/manuals_manage.entity';
|
||||
import { MnaualsManageClassify } from './manuals_manage/entities/manuals_manage_classify.entity';
|
||||
import { ManualsManageModule } from './manuals_manage/manuals_manage.module';
|
||||
import { TrainModule } from './train/train.module';
|
||||
@Module({
|
||||
imports: [
|
||||
MySQL,
|
||||
SystemModule,
|
||||
OnlineTeachingModule,
|
||||
OnlineCourseModule,
|
||||
AssessmentEvaluationModule,
|
||||
ResourceModule,
|
||||
EvaluationModule,
|
||||
InitializeModule,
|
||||
StatisticModule,
|
||||
ArticleManageModule,
|
||||
ManualsManageModule,
|
||||
TrainModule,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
76
serve/src/article_manage/article_manage.controller.ts
Normal file
76
serve/src/article_manage/article_manage.controller.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
UseGuards,
|
||||
Request,
|
||||
} from '@nestjs/common';
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { NoAuthToken } from 'src/common/decorator/no-token.decorator';
|
||||
import { TokenData } from 'src/common/decorator/token-data.decorator';
|
||||
import { TokenDataEntity } from 'src/common/entities/token-data.entity';
|
||||
import { ArticleManageService } from './article_manage.service';
|
||||
import { CreateArticleManageDto } from './dto/create-article_manage.dto';
|
||||
import { UpdateArticleManageDto } from './dto/update-article_manage.dto';
|
||||
import { updatePortionArticle } from './dto/update-portion-article.dto';
|
||||
@ApiTags('文章管理')
|
||||
@Controller('article-manage')
|
||||
@NoAuthToken()
|
||||
export class ArticleManageController {
|
||||
constructor(private readonly articleManageService: ArticleManageService) {}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: '新建文章' })
|
||||
create(
|
||||
@Body() createArticleManageDto: CreateArticleManageDto,
|
||||
@Request() req,
|
||||
) {
|
||||
return this.articleManageService.create(createArticleManageDto, req);
|
||||
}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: '获取所有文章' })
|
||||
findAll(@Request() req) {
|
||||
return this.articleManageService.findAll(req);
|
||||
}
|
||||
|
||||
@Get('manuals/:id')
|
||||
@ApiOperation({ summary: '获取手册下的文章' })
|
||||
findManualsAll(@Param('id') id: string, @TokenData() token: TokenDataEntity) {
|
||||
return this.articleManageService.findManualsAll(id, token);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: '根据ID获取文章' })
|
||||
findOne(@Param('id') id: string, @Request() req) {
|
||||
return this.articleManageService.findOne(+id, req);
|
||||
}
|
||||
|
||||
@Patch()
|
||||
@ApiOperation({ summary: '编辑文章' })
|
||||
update(
|
||||
@Body() updateArticleManageDto: UpdateArticleManageDto,
|
||||
@Request() req,
|
||||
) {
|
||||
return this.articleManageService.update(updateArticleManageDto, req);
|
||||
}
|
||||
@Post('portion')
|
||||
@ApiOperation({ summary: '批量编辑文章' })
|
||||
updatePortion(@Body() data: updatePortionArticle, @Request() req) {
|
||||
return this.articleManageService.updatePortion(data, req);
|
||||
}
|
||||
@Post('sort')
|
||||
@ApiOperation({ summary: '文章排序' })
|
||||
sort(@Body() sortIdArray: number[]) {
|
||||
return this.articleManageService.sort(sortIdArray);
|
||||
}
|
||||
@Delete(':id')
|
||||
@ApiOperation({ summary: '删除文章' })
|
||||
remove(@Param('id') id: string, @Request() req) {
|
||||
return this.articleManageService.remove(+id, req);
|
||||
}
|
||||
}
|
||||
12
serve/src/article_manage/article_manage.module.ts
Normal file
12
serve/src/article_manage/article_manage.module.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ArticleManageService } from './article_manage.service';
|
||||
import { ArticleManageController } from './article_manage.controller';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { ArticleManage } from './entities/article_manage.entity';
|
||||
import { ManualsManage } from 'src/manuals_manage/entities/manuals_manage.entity';
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([ArticleManage, ManualsManage])],
|
||||
controllers: [ArticleManageController],
|
||||
providers: [ArticleManageService],
|
||||
})
|
||||
export class ArticleManageModule {}
|
||||
101
serve/src/article_manage/article_manage.service.ts
Normal file
101
serve/src/article_manage/article_manage.service.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { CreateArticleManageDto } from './dto/create-article_manage.dto';
|
||||
import { UpdateArticleManageDto } from './dto/update-article_manage.dto';
|
||||
import { ArticleManage } from './entities/article_manage.entity';
|
||||
import { getRepository } from 'typeorm';
|
||||
import * as jwt from 'jsonwebtoken';
|
||||
import { TokenDataEntity } from 'src/common/entities/token-data.entity';
|
||||
import { ManualsManage } from 'src/manuals_manage/entities/manuals_manage.entity';
|
||||
|
||||
const enum lookOrEditor {
|
||||
A = 'canView',
|
||||
B = 'canEditor',
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ArticleManageService {
|
||||
constructor(
|
||||
@InjectRepository(ArticleManage)
|
||||
private articleSql: Repository<ArticleManage>,
|
||||
@InjectRepository(ManualsManage)
|
||||
private manualsSql: Repository<ManualsManage>,
|
||||
) {}
|
||||
async create(articeData: CreateArticleManageDto, req) {
|
||||
for (const k in articeData) {
|
||||
const type = typeof articeData[k];
|
||||
if (type == 'object') {
|
||||
articeData[k] = JSON.stringify(articeData[k]);
|
||||
}
|
||||
}
|
||||
const artice: any = articeData;
|
||||
this.manualsSql.update(
|
||||
{ id: articeData.manuals },
|
||||
{ updateTime: new Date() },
|
||||
);
|
||||
return await this.articleSql.save(artice);
|
||||
}
|
||||
// 查询所有文章
|
||||
async findAll(req) {
|
||||
const result = await this.articleSql.findBy({ delFlag: 0 });
|
||||
return result;
|
||||
}
|
||||
async findManualsAll(id, token: TokenDataEntity) {
|
||||
const result = await this.articleSql.findBy({ delFlag: 0, manuals: id });
|
||||
return result;
|
||||
}
|
||||
async findOne(id: number, req) {
|
||||
const result = await this.articleSql.findOneBy({ id, delFlag: 0 });
|
||||
if (!result) {
|
||||
throw '未查询到此文章';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async update(updateDto: UpdateArticleManageDto, req) {
|
||||
const updateData: any = updateDto;
|
||||
for (const k in updateData) {
|
||||
const type = typeof updateData[k];
|
||||
if (type == 'object') {
|
||||
updateData[k] = JSON.stringify(updateData[k]);
|
||||
}
|
||||
}
|
||||
const res = await this.articleSql.save(updateData);
|
||||
this.manualsSql.update(
|
||||
{ id: updateDto.manuals },
|
||||
{ updateTime: new Date() },
|
||||
);
|
||||
this.manualsSql.update({ id: res.manuals }, { updateTime: new Date() });
|
||||
console.log(res);
|
||||
return res;
|
||||
}
|
||||
// 排序
|
||||
async sort(sortIdArray) {
|
||||
const formatData = sortIdArray.map((id, index) => {
|
||||
return {
|
||||
id,
|
||||
index: index + 1,
|
||||
};
|
||||
});
|
||||
// this.articleSql.findBy({ id: sortIdArray });
|
||||
const result = await this.articleSql.save(formatData);
|
||||
return result;
|
||||
}
|
||||
// 批量更新
|
||||
async updatePortion(data, req) {
|
||||
console.log(data);
|
||||
let result: any = await this.articleSql.update(data.id, data.data);
|
||||
result = await this.articleSql.findBy({
|
||||
manuals: data.manuals,
|
||||
delFlag: 0,
|
||||
});
|
||||
this.manualsSql.update({ id: data.manuals }, { updateTime: new Date() });
|
||||
return result;
|
||||
}
|
||||
remove(id: number, req) {
|
||||
const result = this.articleSql.update(id, { delFlag: 1 });
|
||||
return result;
|
||||
}
|
||||
}
|
||||
60
serve/src/article_manage/dto/create-article_manage.dto.ts
Normal file
60
serve/src/article_manage/dto/create-article_manage.dto.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { ApiBody, ApiParam, ApiProperty } from '@nestjs/swagger';
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
isBoolean,
|
||||
IsNumber,
|
||||
IsString,
|
||||
} from 'class-validator';
|
||||
export class CreateArticleManageDto {
|
||||
@ApiProperty({ description: '文章标题', example: '测试文章' })
|
||||
@IsString()
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ description: '文章说明', example: '这里是测试文章' })
|
||||
@IsString()
|
||||
explain: string;
|
||||
|
||||
@ApiProperty({ description: '文章内容', example: 'xxxx' })
|
||||
@IsString()
|
||||
content: string;
|
||||
|
||||
@ApiProperty({ description: '所属手册', example: '1' })
|
||||
@IsNumber()
|
||||
manuals: number;
|
||||
|
||||
@ApiProperty({
|
||||
description: '类型:可以是文档或者是分组,值为:article|group',
|
||||
example: 'article',
|
||||
})
|
||||
@IsString()
|
||||
type: string;
|
||||
|
||||
@ApiProperty({ description: '父级ID', example: '0' })
|
||||
@IsNumber()
|
||||
parentId: number;
|
||||
|
||||
@ApiProperty({ description: '资源是否可下载', example: true })
|
||||
@IsBoolean({ message: '请传递resourceAuthority参数为boolean值' })
|
||||
resourceAuthority: boolean;
|
||||
|
||||
@ApiProperty({ description: '在当前父级的序号', example: 1 })
|
||||
@IsNumber()
|
||||
index: number;
|
||||
|
||||
@ApiProperty({ description: '文章中所含视频数量', example: 1 })
|
||||
@IsNumber()
|
||||
video: number;
|
||||
|
||||
@ApiProperty({ description: '文章中所含视频数量', example: 1 })
|
||||
@IsNumber()
|
||||
image: number;
|
||||
|
||||
@ApiProperty({ description: '文章中所含模型数量', example: 1 })
|
||||
@IsNumber()
|
||||
model: number;
|
||||
|
||||
@ApiProperty({ description: '文章中所含音频数量', example: 1 })
|
||||
@IsNumber()
|
||||
audio: number;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { CreateArticleManageDto } from './create-article_manage.dto';
|
||||
|
||||
export class UpdateArticleManageDto extends PartialType(
|
||||
CreateArticleManageDto,
|
||||
) {}
|
||||
15
serve/src/article_manage/dto/update-portion-article.dto.ts
Normal file
15
serve/src/article_manage/dto/update-portion-article.dto.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsNumber, IsObject } from "class-validator";
|
||||
|
||||
export class updatePortionArticle {
|
||||
@ApiProperty({ description: '更新文章所属标题', example: '测试文章' })
|
||||
@IsNumber()
|
||||
manuals: number
|
||||
|
||||
@ApiProperty({ description: '更新文章的相关数据', example: { delFlag: 1 } })
|
||||
@IsObject()
|
||||
data
|
||||
|
||||
@ApiProperty({ description: '更新文章的ID,可以是数组,也可以是单个数字', example: [1, 2, 3] })
|
||||
id
|
||||
}
|
||||
41
serve/src/article_manage/entities/article_manage.entity.ts
Normal file
41
serve/src/article_manage/entities/article_manage.entity.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { isBtcAddress } from 'class-validator';
|
||||
import { IncrementIdEntity } from 'src/common/entities/increment-id.entity';
|
||||
import { Entity, Column } from 'typeorm';
|
||||
|
||||
@Entity()
|
||||
export class ArticleManage extends IncrementIdEntity {
|
||||
@Column()
|
||||
title: string;
|
||||
@Column({ default: '' })
|
||||
explain: string;
|
||||
// 文章内容
|
||||
@Column({ type: global.DB_IS_SQLITE ? 'text' : 'longtext' })
|
||||
content: string;
|
||||
// 所属手册
|
||||
@Column({ default: 0 })
|
||||
manuals: number;
|
||||
// 父级ID 文章可能是层层嵌套的
|
||||
@Column({ default: 0 })
|
||||
parentId: number;
|
||||
// 文档类型,是分组或者是文章
|
||||
@Column({ default: 0 })
|
||||
type: string;
|
||||
// 资源是否可下载
|
||||
@Column()
|
||||
resourceAuthority: boolean;
|
||||
// 在当前父级中的索引
|
||||
@Column({ default: 0 })
|
||||
index: number;
|
||||
// 图片数量
|
||||
@Column()
|
||||
image: number;
|
||||
// 视频数量
|
||||
@Column()
|
||||
video: number;
|
||||
// 音频数量
|
||||
@Column()
|
||||
audio: number;
|
||||
// 模型数量
|
||||
@Column()
|
||||
model: number;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AssessmentEvaluationService } from './service/assessment-evaluation.service';
|
||||
import { AssessmentEvaluationController } from 'src/assessment-evaluation/controller/assessment-evaluation.controller';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import QuestionModules from 'src/assessment-evaluation/modules/question-manager.modules';
|
||||
import ExamPaperModules from 'src/assessment-evaluation/modules/exam-paper-manager.modules';
|
||||
import ExamModules from 'src/assessment-evaluation/modules/exam-manager.modules';
|
||||
@Module({
|
||||
imports: [
|
||||
// 引入各部分Entity
|
||||
TypeOrmModule.forFeature([
|
||||
...QuestionModules.entitis,
|
||||
...ExamPaperModules.entitis,
|
||||
...ExamModules.entitis,
|
||||
]),
|
||||
],
|
||||
// 引入所有controller
|
||||
controllers: [
|
||||
AssessmentEvaluationController,
|
||||
...QuestionModules.controllers,
|
||||
...ExamPaperModules.controllers,
|
||||
...ExamModules.controllers,
|
||||
],
|
||||
// 引入所有service
|
||||
providers: [
|
||||
AssessmentEvaluationService,
|
||||
...QuestionModules.services,
|
||||
...ExamPaperModules.services,
|
||||
...ExamModules.services,
|
||||
],
|
||||
})
|
||||
export class AssessmentEvaluationModule {}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { NoAuthToken } from 'src/common/decorator/no-token.decorator';
|
||||
import { AssessmentEvaluationService } from '../service/assessment-evaluation.service';
|
||||
|
||||
@ApiTags('考核测评 - 主功能')
|
||||
@ApiBearerAuth()
|
||||
// @NoAuthToken()
|
||||
@Controller('assessmentEvaluation')
|
||||
export class AssessmentEvaluationController {
|
||||
constructor(
|
||||
private readonly assessmentEvaluationService: AssessmentEvaluationService,
|
||||
) {}
|
||||
// @Post()
|
||||
// async create(
|
||||
// @Body() createKnowledgePointDto: CreateKnowledgePointDto,
|
||||
// @TokenData() token: any,
|
||||
// ) {
|
||||
// createKnowledgePointDto.creator = createKnowledgePointDto.updater = token.userId;
|
||||
// return this.KnowledgePointService.create(createKnowledgePointDto);
|
||||
// }
|
||||
|
||||
// @Get()
|
||||
// async findAll() {
|
||||
// return this.KnowledgePointService.findAll();
|
||||
// }
|
||||
|
||||
// @Patch(':id')
|
||||
// async update(@Param('id') id: string, @Body() updateKnowledgePointDto: UpdateKnowledgePointDto) {
|
||||
// return this.KnowledgePointService.update(+id, updateKnowledgePointDto);
|
||||
// }
|
||||
|
||||
// @Delete(':id')
|
||||
// async remove(@Param('id') id: string) {
|
||||
// return this.KnowledgePointService.remove(+id);
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Param,
|
||||
Delete,
|
||||
Patch,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { CreateExamClassifyDto } from 'src/assessment-evaluation/dto/dtos-exam-manager/exam-classify/create-exam-classify.dto';
|
||||
import { UpdateExamClassifyDto } from 'src/assessment-evaluation/dto/dtos-exam-manager/exam-classify/update-exam-classify.dto';
|
||||
import { ExamClassifyService } from 'src/assessment-evaluation/service/service-exam-manager/exam-classify.service';
|
||||
import { TokenData } from 'src/common/decorator/token-data.decorator';
|
||||
|
||||
@ApiTags('考试管理 - 考试分类')
|
||||
@ApiBearerAuth()
|
||||
// @NoAuthToken()
|
||||
@Controller('assessmentEvaluation/ExamClassify')
|
||||
export class ExamClassifyController {
|
||||
constructor(private readonly ExamClassifyService: ExamClassifyService) {}
|
||||
|
||||
@Post()
|
||||
/* 用于创建新考试分类的函数。 */
|
||||
async create(
|
||||
@Body() CreateExamClassifyDto: CreateExamClassifyDto,
|
||||
@TokenData() token: any,
|
||||
) {
|
||||
CreateExamClassifyDto.creator = CreateExamClassifyDto.updater =
|
||||
token.userId;
|
||||
return this.ExamClassifyService.create(CreateExamClassifyDto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
/* 用于查找所有考试分类的函数。 */
|
||||
async findAll() {
|
||||
return this.ExamClassifyService.findAll();
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
/* 更新考试分类的功能。 */
|
||||
async update(
|
||||
@Param('id') id: string,
|
||||
@Body() UpdateExamClassifyDto: UpdateExamClassifyDto,
|
||||
) {
|
||||
return this.ExamClassifyService.update(+id, UpdateExamClassifyDto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
/* 删除考试分类的功能。 */
|
||||
async remove(@Param('id') id: string) {
|
||||
return this.ExamClassifyService.remove(+id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Param,
|
||||
Delete,
|
||||
Patch,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { TokenData } from 'src/common/decorator/token-data.decorator';
|
||||
import { ExamService } from 'src/assessment-evaluation/service/service-exam-manager/exam.service';
|
||||
import { UpdateExamDto } from 'src/assessment-evaluation/dto/dtos-exam-manager/exam/update-exam.dto';
|
||||
import { PagingExamDto } from 'src/assessment-evaluation/dto/dtos-exam-manager/exam/paging-exam.dto';
|
||||
import { CreateExamDto } from 'src/assessment-evaluation/dto/dtos-exam-manager/exam/create-exam.dto';
|
||||
import { StudentOnlineExamService } from 'src/assessment-evaluation/service/service-exam-manager/student-online-exam.service';
|
||||
import { CreateStudentOnlineExamDto } from 'src/assessment-evaluation/dto/dtos-exam-manager/student-online-exam/create-student-online-exam.dto';
|
||||
|
||||
@ApiTags('考试管理 - 考试')
|
||||
@ApiBearerAuth()
|
||||
// @NoAuthToken()
|
||||
@Controller('assessmentEvaluation/exam')
|
||||
export class ExamController {
|
||||
constructor(
|
||||
private readonly ExamService: ExamService,
|
||||
private readonly studentOnlineExamService: StudentOnlineExamService,
|
||||
) {}
|
||||
|
||||
@Post()
|
||||
// @NoAuthToken()
|
||||
/* 创建考试的功能。 */
|
||||
async create(
|
||||
@Body() body: { exam: CreateExamDto; students: Array<any> },
|
||||
@TokenData() token: any,
|
||||
) {
|
||||
body.exam.creator = body.exam.updater = token.userId;
|
||||
console.log(body);
|
||||
const newExam = await this.ExamService.create(body.exam);
|
||||
const studentsExamArr: CreateStudentOnlineExamDto[] = body.students.map(
|
||||
(item) => {
|
||||
return {
|
||||
userId: item,
|
||||
examId: newExam.id,
|
||||
mistakes: 0,
|
||||
examTimes: newExam.examTimes,
|
||||
creator: token.userId,
|
||||
updater: token.userId,
|
||||
delFlag: 0,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
return await this.studentOnlineExamService.create(studentsExamArr);
|
||||
}
|
||||
|
||||
@Get('item/:id')
|
||||
// @NoAuthToken()
|
||||
// 查找一个
|
||||
async findOne(@Param('id') id: string) {
|
||||
const exam = await this.ExamService.findOne(+id);
|
||||
const students = await this.studentOnlineExamService.findAllByExamId(+id);
|
||||
return { exam, students };
|
||||
}
|
||||
|
||||
@Get('paging')
|
||||
// @NoAuthToken()
|
||||
// 分页查询
|
||||
async paging(@Query() pagingInfo: PagingExamDto, @TokenData() token: any) {
|
||||
return this.ExamService.paging(pagingInfo, token);
|
||||
}
|
||||
|
||||
@Patch('updateIsAnonymous/:id')
|
||||
// 更新是否为匿名
|
||||
async updateIsAnonymous(
|
||||
@Param('id') id: string,
|
||||
@Body() body: { isAnonymous: number },
|
||||
// @TokenData() token: any,
|
||||
) {
|
||||
return this.ExamService.updateIsAnonymous(+id, body.isAnonymous);
|
||||
}
|
||||
|
||||
@Patch('item/:id')
|
||||
// 更新数据
|
||||
async update(
|
||||
@Param('id') id: string,
|
||||
@Body() body: { exam: UpdateExamDto; students: Array<any> },
|
||||
@TokenData() token: any,
|
||||
) {
|
||||
await this.studentOnlineExamService.removeByExamId(+id);
|
||||
const studentsExamArr: CreateStudentOnlineExamDto[] = body.students.map(
|
||||
(item) => {
|
||||
return {
|
||||
userId: item,
|
||||
examId: +id,
|
||||
mistakes: 0,
|
||||
delFlag: 0,
|
||||
examTimes: body.exam.examTimes,
|
||||
creator: token.userId,
|
||||
updater: token.userId,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
await this.studentOnlineExamService.create(studentsExamArr);
|
||||
return this.ExamService.update(+id, body.exam);
|
||||
}
|
||||
|
||||
@Delete()
|
||||
// 批量删除考试
|
||||
async removeAll(@Body() ids: number[]) {
|
||||
try {
|
||||
for (const item of ids) {
|
||||
await this.ExamService.remove(+item);
|
||||
await this.studentOnlineExamService.removeByExamId(+item);
|
||||
}
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Param,
|
||||
Delete,
|
||||
Patch,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
ApiTags,
|
||||
ApiBearerAuth,
|
||||
ApiProperty,
|
||||
ApiOperation,
|
||||
} from '@nestjs/swagger';
|
||||
import { ExamQuestionsResultService } from 'src/assessment-evaluation/service/service-exam-manager/exam-questions-result.service';
|
||||
import { ExamService } from 'src/assessment-evaluation/service/service-exam-manager/exam.service';
|
||||
import { OnlineExamHistoryService } from 'src/assessment-evaluation/service/service-exam-manager/online-exam-history.service';
|
||||
import { StudentOnlineExamService } from 'src/assessment-evaluation/service/service-exam-manager/student-online-exam.service';
|
||||
import { NoAuthToken } from 'src/common/decorator/no-token.decorator';
|
||||
import { TokenData } from 'src/common/decorator/token-data.decorator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
import { TokenDataEntity } from 'src/common/entities/token-data.entity';
|
||||
import { checkAnswer } from 'src/assessment-evaluation/controller/sim-test/mistake-again.controller';
|
||||
export class CreateOnlineExamHistoryDto extends CreateDto {
|
||||
@ApiProperty({ description: '试题名模糊查询', required: true })
|
||||
onlineExamId: number;
|
||||
|
||||
@ApiProperty({ description: '试题名模糊查询', required: false, default: 0 })
|
||||
isPracticeExam: number;
|
||||
}
|
||||
@ApiTags('考试管理 - 考试历史')
|
||||
@ApiBearerAuth()
|
||||
// @NoAuthToken()
|
||||
@Controller('assessmentEvaluation/OnlineExamHistory')
|
||||
export class OnlineExamHistoryController {
|
||||
constructor(
|
||||
private readonly OnlineExamHistoryService: OnlineExamHistoryService,
|
||||
private readonly StudentOnlineExamService: StudentOnlineExamService,
|
||||
private readonly ExamService: ExamService,
|
||||
private readonly ExamQuestionResultService: ExamQuestionsResultService,
|
||||
) {}
|
||||
@Patch('result/:historyId')
|
||||
@ApiOperation({ summary: '传入修改数组,修改题目result相关字段' })
|
||||
// 修改results字段
|
||||
async updateResults(
|
||||
@Param('historyId') historyId,
|
||||
@Body() body: { id: number; score: number; comment: string }[],
|
||||
) {
|
||||
console.log(body);
|
||||
const promiseList = [];
|
||||
body.forEach(async (item) => {
|
||||
promiseList.push(
|
||||
this.ExamQuestionResultService.update(item.id, {
|
||||
score: item.score,
|
||||
comment: item.comment,
|
||||
}),
|
||||
);
|
||||
});
|
||||
const res = await Promise.all(promiseList);
|
||||
|
||||
return await this.OnlineExamHistoryService.update(historyId, {
|
||||
isGradePaper: 1,
|
||||
});
|
||||
}
|
||||
@Post('submitExam/:id')
|
||||
// @NoAuthToken()
|
||||
// 查一个
|
||||
async findOne(
|
||||
@Param('id') historyId: string,
|
||||
@Body() answers: Array<any>,
|
||||
@TokenData() token: TokenDataEntity,
|
||||
) {
|
||||
console.log(historyId, answers, token);
|
||||
const historyItem = await this.OnlineExamHistoryService.findOneById(
|
||||
historyId,
|
||||
);
|
||||
if (
|
||||
historyItem != null &&
|
||||
historyItem.reportTime - new Date(historyItem.createTime).getTime() > 500
|
||||
) {
|
||||
console.log(
|
||||
historyItem.reportTime,
|
||||
new Date(historyItem.createTime).getTime(),
|
||||
);
|
||||
throw '本次考试已交卷过,请重新点击开始考试';
|
||||
}
|
||||
const { onlineExamInfo, exam, questions } =
|
||||
await this.OnlineExamHistoryService.findAllQuestionsAndExamInfo(
|
||||
+historyId,
|
||||
1,
|
||||
);
|
||||
// 更新记录中交卷时间
|
||||
this.OnlineExamHistoryService.update(+historyId, {
|
||||
reportTime: new Date().getTime(),
|
||||
isGradePaper: exam.gradePaperMode == 1 ? 0 : 1,
|
||||
});
|
||||
// 将答题记录保存至试题结果表中
|
||||
const questionsResult = questions.map((ques) => {
|
||||
let ans = answers.find((ans) => ans.questionId === ques.id);
|
||||
let userScore = 0;
|
||||
if (ans == null) {
|
||||
ans = [];
|
||||
} else {
|
||||
ans = ans.answer;
|
||||
const result = checkAnswer(ques, ans);
|
||||
console.log(result);
|
||||
// if (result.pass === true) {
|
||||
// userScore = ques.score;
|
||||
// } else {
|
||||
userScore = result.score;
|
||||
// }
|
||||
}
|
||||
return {
|
||||
historyId: +historyId,
|
||||
questionId: +ques.id,
|
||||
questionScore: ques.score,
|
||||
score: userScore,
|
||||
userAnswer: JSON.stringify(ans),
|
||||
creator: token.userId,
|
||||
updater: token.userId,
|
||||
};
|
||||
});
|
||||
await this.ExamQuestionResultService.removeAllByHistoryId(+historyId);
|
||||
const createRes = await this.ExamQuestionResultService.create(
|
||||
questionsResult,
|
||||
);
|
||||
const usertotalScore = questionsResult.reduce((prev, item) => {
|
||||
return prev + item.score;
|
||||
}, 0);
|
||||
|
||||
return {
|
||||
score: usertotalScore,
|
||||
paperScore: exam.totalScore,
|
||||
passPercent: exam.passPercent,
|
||||
gradePaperMode: exam.gradePaperMode,
|
||||
isPass:
|
||||
Math.round((exam.totalScore * exam.passPercent) / 100) <=
|
||||
usertotalScore,
|
||||
};
|
||||
}
|
||||
@Post('simtest')
|
||||
// 创建模拟考试
|
||||
async createSim(
|
||||
@Body() CreateOnlineExamHistoryDto: CreateOnlineExamHistoryDto,
|
||||
@TokenData() token: any,
|
||||
) {
|
||||
// 查询学员考试
|
||||
const onlineExamItem = await this.StudentOnlineExamService.findOneById(
|
||||
+CreateOnlineExamHistoryDto.onlineExamId,
|
||||
false,
|
||||
);
|
||||
if (!onlineExamItem) {
|
||||
throw '数据错误,没找到模拟考试';
|
||||
}
|
||||
const formDto = {
|
||||
onlineExamId: CreateOnlineExamHistoryDto.onlineExamId,
|
||||
examId: null,
|
||||
examPaperId: null,
|
||||
examDuration: 0,
|
||||
reportTime: 0,
|
||||
endTimestamp: 0,
|
||||
isAnonymous: 0,
|
||||
isGradePaper: 0,
|
||||
createTime: new Date(),
|
||||
creator: token.userId,
|
||||
updater: token.userId,
|
||||
isPracticeExam: CreateOnlineExamHistoryDto.isPracticeExam ?? 0,
|
||||
};
|
||||
formDto.examId = onlineExamItem.examId;
|
||||
|
||||
// console.log(studentExamTimes);
|
||||
// 查询考试
|
||||
const examItem = await this.ExamService.findOne(onlineExamItem.examId);
|
||||
formDto.examDuration = examItem.examInfo.examDuration;
|
||||
formDto.examPaperId = examItem.examInfo.examPaperId;
|
||||
const endTime = new Date().getTime() + formDto.examDuration * 60 * 1000;
|
||||
formDto.reportTime = formDto.createTime.getTime();
|
||||
formDto.endTimestamp = endTime;
|
||||
// 存入考试记录
|
||||
return await this.OnlineExamHistoryService.create(formDto);
|
||||
// console.log(onlineExamItem, examItem);
|
||||
// return false;
|
||||
}
|
||||
|
||||
@Post()
|
||||
// 创建考试
|
||||
async create(
|
||||
@Body() CreateOnlineExamHistoryDto: CreateOnlineExamHistoryDto,
|
||||
@TokenData() token: any,
|
||||
) {
|
||||
// 查询学员考试
|
||||
const onlineExamItem = await this.StudentOnlineExamService.findOneById(
|
||||
+CreateOnlineExamHistoryDto.onlineExamId,
|
||||
);
|
||||
const formDto = {
|
||||
onlineExamId: CreateOnlineExamHistoryDto.onlineExamId,
|
||||
examId: null,
|
||||
examPaperId: null,
|
||||
examDuration: 0,
|
||||
reportTime: 0,
|
||||
endTimestamp: 0,
|
||||
isAnonymous: 0,
|
||||
isGradePaper: 0,
|
||||
createTime: new Date(),
|
||||
creator: token.userId,
|
||||
updater: token.userId,
|
||||
isPracticeExam: CreateOnlineExamHistoryDto.isPracticeExam ?? 0,
|
||||
};
|
||||
if (!onlineExamItem) throw '该学员考试不存在';
|
||||
formDto.examId = onlineExamItem.examId;
|
||||
|
||||
// console.log(studentExamTimes);
|
||||
// 查询考试
|
||||
const examItem = await this.ExamService.findOne(onlineExamItem.examId);
|
||||
if (!examItem.examInfo) throw '该考试不存在';
|
||||
if (
|
||||
examItem.examInfo.delFlag != -1 &&
|
||||
new Date() > new Date(examItem.examInfo.examEndTime)
|
||||
) {
|
||||
throw '考试已结束';
|
||||
}
|
||||
const studentExamTimes = await this.StudentOnlineExamService.checkExamTimes(
|
||||
token.userId,
|
||||
CreateOnlineExamHistoryDto.onlineExamId,
|
||||
);
|
||||
if (Array.isArray(studentExamTimes) && studentExamTimes.length > 0) {
|
||||
if (studentExamTimes[0].times >= examItem.examInfo.examTimes) {
|
||||
throw '您已没有考试机会了';
|
||||
}
|
||||
}
|
||||
formDto.examDuration = examItem.examInfo.examDuration;
|
||||
formDto.examPaperId = examItem.examInfo.examPaperId;
|
||||
const endTime = new Date().getTime() + formDto.examDuration * 60 * 1000;
|
||||
formDto.reportTime = formDto.createTime.getTime();
|
||||
formDto.endTimestamp = endTime;
|
||||
// 存入考试记录
|
||||
return await this.OnlineExamHistoryService.create(formDto);
|
||||
// console.log(onlineExamItem, examItem);
|
||||
// return false;
|
||||
}
|
||||
@Get('examQuestionResult/:onlineExamId')
|
||||
@NoAuthToken()
|
||||
// 获取试题
|
||||
async getQuestionResult(@Param('onlineExamId') onlineExamId: number) {
|
||||
return this.OnlineExamHistoryService.findAllQuestionResultByHistoryId(
|
||||
onlineExamId,
|
||||
);
|
||||
}
|
||||
@Get(`questions/:id`)
|
||||
// 使用历史记录ID获取试题
|
||||
async findQuestionByHistoryId(@Param('id') id: string) {
|
||||
return this.OnlineExamHistoryService.findAllQuestionsAndExamInfo(+id);
|
||||
}
|
||||
@Get()
|
||||
// 获取所有考试
|
||||
async findAll() {
|
||||
return this.OnlineExamHistoryService.findAll();
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
// 更新
|
||||
async update(
|
||||
@Param('id') id: string,
|
||||
@Body() UpdateOnlineExamHistoryDto: any,
|
||||
) {
|
||||
return this.OnlineExamHistoryService.update(
|
||||
+id,
|
||||
UpdateOnlineExamHistoryDto,
|
||||
);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
// 删除考试
|
||||
async remove(@Param('id') id: string) {
|
||||
return this.OnlineExamHistoryService.remove(+id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Param,
|
||||
Delete,
|
||||
Patch,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
|
||||
import { NoAuthToken } from 'src/common/decorator/no-token.decorator';
|
||||
import { TokenData } from 'src/common/decorator/token-data.decorator';
|
||||
import { StudentOnlineExamService } from 'src/assessment-evaluation/service/service-exam-manager/student-online-exam.service';
|
||||
import { TokenDataEntity } from 'src/common/entities/token-data.entity';
|
||||
import { UpdateStudentOnlineExamDto } from 'src/assessment-evaluation/dto/dtos-exam-manager/student-online-exam/update-student-online-exam.dto';
|
||||
import { CreateStudentOnlineExamDto } from 'src/assessment-evaluation/dto/dtos-exam-manager/student-online-exam/create-student-online-exam.dto';
|
||||
import { PagingStudentOnlineExamDto } from 'src/assessment-evaluation/dto/dtos-exam-manager/student-online-exam/paging-student-online-exam.dto';
|
||||
@ApiTags('考试管理 - 学员在线考试')
|
||||
@ApiBearerAuth()
|
||||
// @NoAuthToken()
|
||||
@Controller('assessmentEvaluation/StudentOnlineExam')
|
||||
export class StudentOnlineExamController {
|
||||
constructor(
|
||||
private readonly StudentOnlineExamService: StudentOnlineExamService,
|
||||
) {}
|
||||
|
||||
@Post()
|
||||
// @NoAuthToken()
|
||||
// 分发考试给学生
|
||||
async create(
|
||||
@Body() CreateStudentOnlineExamDto: CreateStudentOnlineExamDto,
|
||||
@TokenData() token: any,
|
||||
) {
|
||||
CreateStudentOnlineExamDto.creator = CreateStudentOnlineExamDto.updater =
|
||||
token.userId;
|
||||
return this.StudentOnlineExamService.create(CreateStudentOnlineExamDto);
|
||||
}
|
||||
|
||||
@Get('paging')
|
||||
// @NoAuthToken()
|
||||
// 分页查询考试历史
|
||||
async paging(
|
||||
@Query() pagingInfo: PagingStudentOnlineExamDto,
|
||||
@TokenData() token: TokenDataEntity,
|
||||
) {
|
||||
console.log(token);
|
||||
return this.StudentOnlineExamService.paging(pagingInfo, token);
|
||||
}
|
||||
|
||||
@Get('pagingGrade')
|
||||
// @NoAuthToken()
|
||||
@ApiOperation({ summary: '分页查询人工判卷的考试列表' })
|
||||
// 分页查询人工判卷的考试列表
|
||||
async pagingGrade(
|
||||
@Query() pagingInfo: PagingStudentOnlineExamDto,
|
||||
@TokenData() token: TokenDataEntity,
|
||||
) {
|
||||
console.log(token);
|
||||
return this.StudentOnlineExamService.pagingGrade(pagingInfo, token);
|
||||
}
|
||||
@Get('pagingGradeDetails/:examId')
|
||||
@NoAuthToken()
|
||||
@ApiOperation({ summary: '分页查询需要人工判卷的学员考试列表' })
|
||||
// 分页查询需要人工判卷的学员考试列表
|
||||
async pagingGradeDetails(
|
||||
@Param('examId') examId: number,
|
||||
@Query() pagingInfo: PagingStudentOnlineExamDto,
|
||||
) {
|
||||
return this.StudentOnlineExamService.pagingGradeDetails(
|
||||
+examId,
|
||||
pagingInfo,
|
||||
);
|
||||
}
|
||||
@Get('lastExamHistory/:onlineExamId')
|
||||
@NoAuthToken()
|
||||
// 获取考试最后一次的答卷
|
||||
async getLastExamHistory(@Param('onlineExamId') onlineExamId: number) {
|
||||
return this.StudentOnlineExamService.findLastExam(onlineExamId);
|
||||
}
|
||||
|
||||
@Patch('item/:id')
|
||||
// 更新
|
||||
async update(
|
||||
@Param('id') id: string,
|
||||
@Body() UpdateStudentOnlineExamDto: UpdateStudentOnlineExamDto,
|
||||
) {
|
||||
return this.StudentOnlineExamService.update(
|
||||
+id,
|
||||
UpdateStudentOnlineExamDto,
|
||||
);
|
||||
}
|
||||
// 删除
|
||||
@Delete('item/:id')
|
||||
async remove(@Param('id') id: string) {
|
||||
return this.StudentOnlineExamService.remove(+id);
|
||||
}
|
||||
// 批量删除
|
||||
@Delete()
|
||||
async removeAll(@Body() ids: number[]) {
|
||||
try {
|
||||
for (const item of ids) {
|
||||
await this.StudentOnlineExamService.remove(+item);
|
||||
}
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Param,
|
||||
Delete,
|
||||
Patch,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { CreateExamPaperClassifyDto } from 'src/assessment-evaluation/dto/dtos-exam-paper-manager/exam-paper-classify/create-exam-paper-classify.dto';
|
||||
import { UpdateExamPaperClassifyDto } from 'src/assessment-evaluation/dto/dtos-exam-paper-manager/exam-paper-classify/update-exam-paper-classify.dto';
|
||||
import { ExamPaperClassifyService } from 'src/assessment-evaluation/service/service-exam-paper-manager/exam-paper-classify.service';
|
||||
import { TokenData } from 'src/common/decorator/token-data.decorator';
|
||||
|
||||
@ApiTags('试卷管理 - 试卷分类')
|
||||
@ApiBearerAuth()
|
||||
// @NoAuthToken()
|
||||
@Controller('assessmentEvaluation/ExamPaperClassify')
|
||||
export class ExamPaperClassifyController {
|
||||
constructor(
|
||||
private readonly ExamPaperClassifyService: ExamPaperClassifyService,
|
||||
) {}
|
||||
|
||||
/*
|
||||
创建试卷分类
|
||||
*/
|
||||
@Post()
|
||||
async create(
|
||||
@Body() createExamPaperClassifyDto: CreateExamPaperClassifyDto,
|
||||
@TokenData() token: any,
|
||||
) {
|
||||
createExamPaperClassifyDto.creator = createExamPaperClassifyDto.updater =
|
||||
token.userId;
|
||||
return this.ExamPaperClassifyService.create(createExamPaperClassifyDto);
|
||||
}
|
||||
/**
|
||||
* 获取所有试卷分类
|
||||
*/
|
||||
@Get()
|
||||
async findAll() {
|
||||
return this.ExamPaperClassifyService.findAll();
|
||||
}
|
||||
/*
|
||||
更新试卷分类
|
||||
*/
|
||||
@Patch(':id')
|
||||
async update(
|
||||
@Param('id') id: string,
|
||||
@Body() updateExamPaperClassifyDto: UpdateExamPaperClassifyDto,
|
||||
) {
|
||||
return this.ExamPaperClassifyService.update(
|
||||
+id,
|
||||
updateExamPaperClassifyDto,
|
||||
);
|
||||
}
|
||||
/*
|
||||
删除试卷分类
|
||||
*/
|
||||
@Delete(':id')
|
||||
async remove(@Param('id') id: string) {
|
||||
return this.ExamPaperClassifyService.remove(+id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Param,
|
||||
Delete,
|
||||
Patch,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
|
||||
import { NoAuthToken } from 'src/common/decorator/no-token.decorator';
|
||||
import { TokenData } from 'src/common/decorator/token-data.decorator';
|
||||
import { ExamPaperService } from 'src/assessment-evaluation/service/service-exam-paper-manager/exam-paper.service';
|
||||
import { PagingExamPaperDto } from 'src/assessment-evaluation/dto/dtos-exam-paper-manager/exam-paper/paging-exam-paper.dto';
|
||||
import { CreateExamPaperDto } from 'src/assessment-evaluation/dto/dtos-exam-paper-manager/exam-paper/create-exam-paper.dto';
|
||||
import { UpdateExamPaperDto } from 'src/assessment-evaluation/dto/dtos-exam-paper-manager/exam-paper/update-exam-paper.dto';
|
||||
import { QuestionsForPaperService } from 'src/assessment-evaluation/service/service-exam-paper-manager/questions-for-paper';
|
||||
import { TokenDataEntity } from 'src/common/entities/token-data.entity';
|
||||
|
||||
@ApiTags('试卷管理 - 试卷')
|
||||
@ApiBearerAuth()
|
||||
// @NoAuthToken()
|
||||
@Controller('assessmentEvaluation/exampaper')
|
||||
export class ExamPaperController {
|
||||
constructor(
|
||||
private readonly ExamPaperService: ExamPaperService,
|
||||
private readonly QuestionsForPaperService: QuestionsForPaperService,
|
||||
) {}
|
||||
@Get('questionsByPaper/:id')
|
||||
@NoAuthToken()
|
||||
/*
|
||||
使用试卷id获取对应试题
|
||||
*/
|
||||
async getQuestionsByPaperId(@Param('id') id: number) {
|
||||
return this.QuestionsForPaperService.getQuestionsByPaperId(id);
|
||||
}
|
||||
@Post('copy/:id')
|
||||
/*
|
||||
复制试卷
|
||||
*/
|
||||
async copyNewPaper(
|
||||
@Param('id') id: number,
|
||||
@Body() data,
|
||||
@TokenData() token: any,
|
||||
) {
|
||||
const oldPaperItem = await this.ExamPaperService.findOne(id);
|
||||
delete oldPaperItem['id'];
|
||||
delete oldPaperItem['createTime'];
|
||||
delete oldPaperItem['updateTime'];
|
||||
oldPaperItem.title = data.title;
|
||||
oldPaperItem.status = 0;
|
||||
oldPaperItem.creator = oldPaperItem.updater = token.userId;
|
||||
const res = await this.create(oldPaperItem, token);
|
||||
return res;
|
||||
}
|
||||
|
||||
@Post()
|
||||
/*
|
||||
创建试卷
|
||||
*/
|
||||
// @NoAuthToken()
|
||||
async create(
|
||||
@Body() CreateExamPaperDto: CreateExamPaperDto,
|
||||
@TokenData() token: any,
|
||||
) {
|
||||
CreateExamPaperDto.creator = CreateExamPaperDto.updater = token.userId;
|
||||
const newPaper = await this.ExamPaperService.create(CreateExamPaperDto);
|
||||
const { FixRes, RandRes } = await this.saveQuestionInfo(
|
||||
newPaper.id,
|
||||
token,
|
||||
CreateExamPaperDto.questionInfo,
|
||||
);
|
||||
return { newPaper, FixRes, RandRes };
|
||||
}
|
||||
/*
|
||||
保存试卷中试题的
|
||||
*/
|
||||
async saveQuestionInfo(paperId: any, token: any, questionInfo: any) {
|
||||
const { fixdQuestions, randQuestions } = questionInfo;
|
||||
const fixdNew = fixdQuestions.map((fixItem) => {
|
||||
return {
|
||||
examPaperId: paperId,
|
||||
questionId: fixItem.id,
|
||||
score: fixItem['itemScore'],
|
||||
isFixed: 1,
|
||||
type: fixItem['typeId'],
|
||||
updater: token.userId,
|
||||
creator: token.userId,
|
||||
};
|
||||
});
|
||||
const randNew = randQuestions.map((fixItem) => {
|
||||
return {
|
||||
examPaperId: paperId,
|
||||
questionId: fixItem.id,
|
||||
score: fixItem['itemScore'],
|
||||
isFixed: 0,
|
||||
type: fixItem['typeId'],
|
||||
updater: token.userId,
|
||||
creator: token.userId,
|
||||
};
|
||||
});
|
||||
const FixRes = await this.QuestionsForPaperService.create(fixdNew);
|
||||
const RandRes = await this.QuestionsForPaperService.create(randNew);
|
||||
return { FixRes, RandRes };
|
||||
}
|
||||
@Get('item/:id')
|
||||
// @NoAuthToken()
|
||||
/*
|
||||
获取试卷详情
|
||||
*/
|
||||
async findOne(@Param('id') id: string) {
|
||||
return this.ExamPaperService.findOne(+id);
|
||||
}
|
||||
@Get('checkQuote/:id')
|
||||
@NoAuthToken()
|
||||
@ApiOperation({ summary: '检查试卷被哪些考试引用' })
|
||||
/*
|
||||
检查试卷被哪些考试引用
|
||||
*/
|
||||
async checkQuote(@Param('id') id: string) {
|
||||
return this.ExamPaperService.checkPaperHasQuote(+id);
|
||||
}
|
||||
|
||||
@Get('paging')
|
||||
// @NoAuthToken()
|
||||
/*
|
||||
分页查询试卷
|
||||
*/
|
||||
async paging(
|
||||
@Query() PagingExamPaperDto: PagingExamPaperDto,
|
||||
@TokenData() token: TokenDataEntity,
|
||||
) {
|
||||
return this.ExamPaperService.paging(PagingExamPaperDto, token);
|
||||
}
|
||||
|
||||
@Patch('item/:id')
|
||||
/*
|
||||
更新试卷
|
||||
*/
|
||||
async update(
|
||||
@Param('id') id: string,
|
||||
@Body() UpdateExamPaperDto: UpdateExamPaperDto,
|
||||
@TokenData() token,
|
||||
) {
|
||||
console.log(UpdateExamPaperDto);
|
||||
if ('questionInfo' in UpdateExamPaperDto) {
|
||||
await this.ExamPaperService.removeAllQuestionByPaperId(+id);
|
||||
const { FixRes, RandRes } = await this.saveQuestionInfo(
|
||||
+id,
|
||||
token,
|
||||
UpdateExamPaperDto.questionInfo,
|
||||
);
|
||||
delete UpdateExamPaperDto.questionInfo;
|
||||
}
|
||||
const newPaper = await this.ExamPaperService.update(
|
||||
+id,
|
||||
UpdateExamPaperDto,
|
||||
);
|
||||
return { newPaper };
|
||||
}
|
||||
|
||||
/*
|
||||
删除试卷
|
||||
*/
|
||||
@Delete('item/:id')
|
||||
async remove(@Param('id') id: string) {
|
||||
return this.ExamPaperService.remove(+id);
|
||||
}
|
||||
|
||||
/*
|
||||
批量删除试卷
|
||||
*/
|
||||
@Delete()
|
||||
async removeAll(@Body() ids: number[]) {
|
||||
try {
|
||||
for (const item of ids) {
|
||||
await this.ExamPaperService.remove(+item);
|
||||
}
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
修改ID
|
||||
*/
|
||||
@Patch('putchAllClassifyId')
|
||||
async putchAllClassifyId(
|
||||
@Body() body: { ids: number[]; classifyId: number },
|
||||
) {
|
||||
console.log('================>', body);
|
||||
const { ids, classifyId } = body;
|
||||
if (!ids) return '必须传入id';
|
||||
if (!classifyId) return '必须传入classifyId';
|
||||
try {
|
||||
await this.ExamPaperService.batchMovePaperToClassify(ids, classifyId);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Param,
|
||||
Delete,
|
||||
Patch,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
|
||||
import { TokenData } from 'src/common/decorator/token-data.decorator';
|
||||
import { CreateKnowledgePointDto } from 'src/assessment-evaluation/dto/dtos-question-manager/knowledge-point/create-knowledge-point.dto';
|
||||
import { UpdateKnowledgePointDto } from 'src/assessment-evaluation/dto/dtos-question-manager/knowledge-point/update-knowledge-point.dto';
|
||||
import { KnowledgePointService } from 'src/assessment-evaluation/service/service-question-manager/knowledge-point.service';
|
||||
|
||||
@ApiTags('试题管理 - 知识点管理')
|
||||
@ApiBearerAuth()
|
||||
// @NoAuthToken()
|
||||
@Controller('assessmentEvaluation/knowledgePoint')
|
||||
export class KnowledgePointController {
|
||||
constructor(private readonly KnowledgePointService: KnowledgePointService) {}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({
|
||||
summary: '新增知识点,会查询知识点是否存在,存在返回旧值,不存在返回新值',
|
||||
})
|
||||
/*
|
||||
新增知识点,会查询知识点是否存在,存在返回旧值,不存在返回新值
|
||||
*/
|
||||
async create(
|
||||
@Body() createKnowledgePointDto: CreateKnowledgePointDto,
|
||||
@TokenData() token: any,
|
||||
) {
|
||||
createKnowledgePointDto.creator = createKnowledgePointDto.updater =
|
||||
token.userId;
|
||||
return this.KnowledgePointService.create(createKnowledgePointDto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
/*
|
||||
获取所有知识点
|
||||
*/
|
||||
async findAll() {
|
||||
return this.KnowledgePointService.findAll();
|
||||
}
|
||||
|
||||
/*
|
||||
更新知识点
|
||||
*/
|
||||
@Patch(':id')
|
||||
async update(
|
||||
@Param('id') id: string,
|
||||
@Body() updateKnowledgePointDto: UpdateKnowledgePointDto,
|
||||
) {
|
||||
return this.KnowledgePointService.update(+id, updateKnowledgePointDto);
|
||||
}
|
||||
/*
|
||||
删除知识点
|
||||
*/
|
||||
@Delete(':id')
|
||||
async remove(@Param('id') id: string) {
|
||||
return this.KnowledgePointService.remove(+id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Param,
|
||||
Delete,
|
||||
Patch,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { TokenData } from 'src/common/decorator/token-data.decorator';
|
||||
import { CreateQuestionClassifyDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question-classify/create-question-classify.dto';
|
||||
import { UpdateQuestionClassifyDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question-classify/update-question-classify.dto';
|
||||
import { QuestionClassifyService } from 'src/assessment-evaluation/service/service-question-manager/question-classify.service';
|
||||
|
||||
@ApiTags('试题管理 - 试题分类')
|
||||
@ApiBearerAuth()
|
||||
// @NoAuthToken()
|
||||
@Controller('assessmentEvaluation/questionClassify')
|
||||
export class QuestionClassifyController {
|
||||
constructor(
|
||||
private readonly questionClassifyService: QuestionClassifyService,
|
||||
) {}
|
||||
|
||||
/*
|
||||
创建试题分类
|
||||
*/
|
||||
@Post()
|
||||
async create(
|
||||
@Body() createQuestionClassifyDto: CreateQuestionClassifyDto,
|
||||
@TokenData() token: any,
|
||||
) {
|
||||
createQuestionClassifyDto.creator = createQuestionClassifyDto.updater =
|
||||
token.userId;
|
||||
return this.questionClassifyService.create(createQuestionClassifyDto);
|
||||
}
|
||||
|
||||
/*
|
||||
获取所有试题分类
|
||||
*/
|
||||
@Get()
|
||||
async findAll() {
|
||||
return this.questionClassifyService.findAll();
|
||||
}
|
||||
|
||||
/*
|
||||
更新试题分类
|
||||
*/
|
||||
@Patch(':id')
|
||||
async update(
|
||||
@Param('id') id: string,
|
||||
@Body() updateQuestionClassifyDto: UpdateQuestionClassifyDto,
|
||||
) {
|
||||
return this.questionClassifyService.update(+id, updateQuestionClassifyDto);
|
||||
}
|
||||
|
||||
/*
|
||||
删除试题分类
|
||||
*/
|
||||
@Delete(':id')
|
||||
async remove(@Param('id') id: string) {
|
||||
return this.questionClassifyService.remove(+id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Param,
|
||||
Delete,
|
||||
Patch,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { TokenData } from 'src/common/decorator/token-data.decorator';
|
||||
import { CreateQuestionDifficultyLevelDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question-difficulty-level/create-question-difficulty-level.dto';
|
||||
import { UpdateQuestionDifficultyLevelDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question-difficulty-level/update-question-difficulty-level.dto';
|
||||
import { QuestionDifficultyLevelService } from 'src/assessment-evaluation/service/service-question-manager/question-difficulty-level.service';
|
||||
|
||||
@ApiTags('试题管理 - 难易程度')
|
||||
@ApiBearerAuth()
|
||||
@Controller('assessmentEvaluation/questionDifficultyLevel')
|
||||
export class QuestionDifficultyLevelController {
|
||||
constructor(
|
||||
private readonly questionDifficultyLevelService: QuestionDifficultyLevelService,
|
||||
) {}
|
||||
|
||||
/*
|
||||
创建试题难度
|
||||
*/
|
||||
@Post()
|
||||
async create(
|
||||
@Body() createquestionDifficultyLevelDto: CreateQuestionDifficultyLevelDto,
|
||||
@TokenData() token: any,
|
||||
) {
|
||||
createquestionDifficultyLevelDto.creator =
|
||||
createquestionDifficultyLevelDto.updater = token.userId;
|
||||
return this.questionDifficultyLevelService.create(
|
||||
createquestionDifficultyLevelDto,
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
获取所有试题难度
|
||||
*/
|
||||
@Get()
|
||||
async findAll() {
|
||||
return this.questionDifficultyLevelService.findAll();
|
||||
}
|
||||
|
||||
/*
|
||||
更新试题难度
|
||||
*/
|
||||
@Patch(':id')
|
||||
async update(
|
||||
@Param('id') id: string,
|
||||
@Body() updatequestionDifficultyLevelDto: UpdateQuestionDifficultyLevelDto,
|
||||
) {
|
||||
return this.questionDifficultyLevelService.update(
|
||||
+id,
|
||||
updatequestionDifficultyLevelDto,
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
删除试题难度
|
||||
*/
|
||||
@Delete(':id')
|
||||
async remove(@Param('id') id: string) {
|
||||
return this.questionDifficultyLevelService.remove(+id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Param,
|
||||
Delete,
|
||||
Patch,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { TokenData } from 'src/common/decorator/token-data.decorator';
|
||||
import { CreateQuestionTypeDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question-type/create-question-type.dto';
|
||||
import { UpdateQuestionTypeDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question-type/update-question-type.dto';
|
||||
import { QuestionTypeService } from 'src/assessment-evaluation/service/service-question-manager/question-type.service';
|
||||
|
||||
@ApiTags('试题管理 - 试题类型')
|
||||
@ApiBearerAuth()
|
||||
// @NoAuthToken()
|
||||
@Controller('assessmentEvaluation/questionType')
|
||||
export class QuestionTypeController {
|
||||
constructor(private readonly QuestionTypeService: QuestionTypeService) {}
|
||||
|
||||
/*
|
||||
创建试题类型
|
||||
*/
|
||||
@Post()
|
||||
async create(
|
||||
@Body() createQuestionTypeDto: CreateQuestionTypeDto,
|
||||
@TokenData() token: any,
|
||||
) {
|
||||
createQuestionTypeDto.creator = createQuestionTypeDto.updater =
|
||||
token.userId;
|
||||
return this.QuestionTypeService.create(createQuestionTypeDto);
|
||||
}
|
||||
/*
|
||||
查找所有试题类型
|
||||
*/
|
||||
@Get()
|
||||
async findAll() {
|
||||
return this.QuestionTypeService.findAll();
|
||||
}
|
||||
|
||||
/*
|
||||
更新试题类型
|
||||
*/
|
||||
@Patch(':id')
|
||||
async update(
|
||||
@Param('id') id: string,
|
||||
@Body() updateQuestionTypeDto: UpdateQuestionTypeDto,
|
||||
) {
|
||||
return this.QuestionTypeService.update(+id, updateQuestionTypeDto);
|
||||
}
|
||||
|
||||
/*
|
||||
删除试题类型
|
||||
*/
|
||||
@Delete(':id')
|
||||
async remove(@Param('id') id: string) {
|
||||
return this.QuestionTypeService.remove(+id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Param,
|
||||
Delete,
|
||||
Patch,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
|
||||
import { NoAuthToken } from 'src/common/decorator/no-token.decorator';
|
||||
import { TokenData } from 'src/common/decorator/token-data.decorator';
|
||||
import { CreateQuestionDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question/create-question.dto';
|
||||
import { PagingQuestionDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question/paging-question.dto';
|
||||
import { UpdateQuestionDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question/update-question.dto';
|
||||
import { QuestionDifficultyLevelService } from 'src/assessment-evaluation/service/service-question-manager/question-difficulty-level.service';
|
||||
import { QuestionTypeService } from 'src/assessment-evaluation/service/service-question-manager/question-type.service';
|
||||
import { QuestionService } from 'src/assessment-evaluation/service/service-question-manager/question.service';
|
||||
import { GetQuestionCountDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question/get-question-count.dto';
|
||||
import { KnowledgePointService } from 'src/assessment-evaluation/service/service-question-manager/knowledge-point.service';
|
||||
|
||||
@ApiTags('试题管理 - 试题')
|
||||
@ApiBearerAuth()
|
||||
// @NoAuthToken()
|
||||
@Controller('assessmentEvaluation/questions')
|
||||
export class QuestionController {
|
||||
constructor(
|
||||
private readonly QuestionService: QuestionService,
|
||||
private readonly QuestionDifficultyLevelService: QuestionDifficultyLevelService,
|
||||
private readonly QuestionTypeService: QuestionTypeService,
|
||||
private readonly KnowledgePointService: KnowledgePointService,
|
||||
) {}
|
||||
|
||||
@Post()
|
||||
// @NoAuthToken()
|
||||
/*
|
||||
创建试题
|
||||
*/
|
||||
async create(
|
||||
@Body() createQuestionDto: CreateQuestionDto,
|
||||
@TokenData() token: any,
|
||||
) {
|
||||
createQuestionDto.creator = createQuestionDto.updater = token.userId;
|
||||
return this.QuestionService.create(createQuestionDto);
|
||||
}
|
||||
|
||||
/*
|
||||
导入试题到某个分类下
|
||||
*/
|
||||
@Post('import/:classifyId')
|
||||
// @NoAuthToken()
|
||||
async importQuestions(
|
||||
@Param('classifyId') classifyId: number,
|
||||
@Body() questions: any[],
|
||||
@TokenData() token: any,
|
||||
) {
|
||||
const difficultys = await this.QuestionDifficultyLevelService.findAll();
|
||||
const types = await this.QuestionTypeService.findAll();
|
||||
// return this.QuestionService.create(createQuestionDto);
|
||||
const formatedQuestion: CreateQuestionDto[] = [];
|
||||
for (let ind = 0; ind < questions.length; ind++) {
|
||||
const item = questions[ind];
|
||||
const type = types.find((t) => t.name === item.type);
|
||||
const difficulty = difficultys.find((d) => d.name === item.difficulty);
|
||||
let knowledge = null;
|
||||
if (item.knowledge && item.knowledge.trim() !== '') {
|
||||
knowledge = await this.KnowledgePointService.create({
|
||||
name: item.knowledge,
|
||||
creator: token.userId,
|
||||
updater: token.userId,
|
||||
});
|
||||
}
|
||||
const queTemp: CreateQuestionDto = {
|
||||
type: type.id,
|
||||
difficultyLevel: difficulty.id,
|
||||
classifyId,
|
||||
title: item.title,
|
||||
options: item.options,
|
||||
answer: item.answer,
|
||||
knowledgeId: knowledge ? knowledge.id : null,
|
||||
analysis: item.analysis,
|
||||
creator: token.userId,
|
||||
updater: token.userId,
|
||||
student: 0,
|
||||
};
|
||||
formatedQuestion.push(queTemp);
|
||||
}
|
||||
|
||||
return await this.QuestionService.importQuestions(formatedQuestion);
|
||||
}
|
||||
/*
|
||||
随机选题
|
||||
*/
|
||||
@Get('random')
|
||||
@NoAuthToken()
|
||||
@ApiOperation({ summary: '传入选题个数,随机从数据库中抽取对应题数' })
|
||||
async random(
|
||||
@Query()
|
||||
getQuestionCountDto: GetQuestionCountDto,
|
||||
) {
|
||||
console.log('===================>', getQuestionCountDto);
|
||||
return this.QuestionService.random(getQuestionCountDto);
|
||||
}
|
||||
|
||||
/*
|
||||
导出某个分类下的试题,没有传入分类ID则导出所有试题
|
||||
*/
|
||||
@Get('export/:classifyId?')
|
||||
@NoAuthToken()
|
||||
async exportQuestion(
|
||||
@Param('classifyId')
|
||||
classifyId: number,
|
||||
) {
|
||||
return this.QuestionService.exportAllQuestion(+classifyId);
|
||||
}
|
||||
/*
|
||||
获取所有试题
|
||||
*/
|
||||
@Get()
|
||||
async findAll() {
|
||||
return this.QuestionService.findAll();
|
||||
}
|
||||
|
||||
/*
|
||||
获取单个试题
|
||||
*/
|
||||
@Get('item/:id')
|
||||
// @NoAuthToken()
|
||||
async findOne(@Param('id') id: string) {
|
||||
return this.QuestionService.findOne(+id);
|
||||
}
|
||||
|
||||
/*
|
||||
获取试题数量,可以排除某些试题ID
|
||||
*/
|
||||
@Get('count')
|
||||
@NoAuthToken()
|
||||
async getCount(@Query() getQuestionCountDto: GetQuestionCountDto) {
|
||||
return this.QuestionService.getCount(getQuestionCountDto);
|
||||
}
|
||||
|
||||
/*
|
||||
批量查询试题信息
|
||||
*/
|
||||
@Post('items')
|
||||
@NoAuthToken()
|
||||
@ApiOperation({ summary: '传入[试题ID,试题ID...]批量查询试题信息' })
|
||||
async getQuestions(@Body() ids: number[]) {
|
||||
return this.QuestionService.getQuestions(ids);
|
||||
}
|
||||
|
||||
/*
|
||||
分页获取试题
|
||||
*/
|
||||
@Get('paging')
|
||||
@NoAuthToken()
|
||||
async paging(@Query() pagingInfo: PagingQuestionDto) {
|
||||
console.log(pagingInfo);
|
||||
return this.QuestionService.paging(pagingInfo);
|
||||
}
|
||||
|
||||
/*
|
||||
获取试题难度和试题类型
|
||||
*/
|
||||
@Get('queryParams')
|
||||
async getQueryParams() {
|
||||
const difficultyLevel = await this.QuestionDifficultyLevelService.findAll();
|
||||
const questionType = await this.QuestionTypeService.findAll();
|
||||
return { difficultyLevel, questionType };
|
||||
}
|
||||
|
||||
/*
|
||||
更新试题数据
|
||||
*/
|
||||
@Patch('item/studentCanUse/:id')
|
||||
async studentCanUse(
|
||||
@Param('id') id: string,
|
||||
@Body() body: { student: number },
|
||||
) {
|
||||
return this.QuestionService.updateStudentCanUse(+id, body);
|
||||
}
|
||||
/*
|
||||
更新试题数据
|
||||
*/
|
||||
@Patch('item/:id')
|
||||
async update(
|
||||
@Param('id') id: string,
|
||||
@Body() updateQuestionDto: UpdateQuestionDto,
|
||||
) {
|
||||
return this.QuestionService.update(+id, updateQuestionDto);
|
||||
}
|
||||
/*
|
||||
根据ID删除试题
|
||||
*/
|
||||
@Delete('item/:id')
|
||||
async remove(@Param('id') id: string) {
|
||||
return this.QuestionService.remove(+id);
|
||||
}
|
||||
/* 批量删除试题 */
|
||||
@Delete()
|
||||
async removeAll(@Body() ids: number[]) {
|
||||
try {
|
||||
for (const item of ids) {
|
||||
await this.QuestionService.remove(+item);
|
||||
}
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
import { Controller, Get, Body, Param, Patch, Query } from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
|
||||
import { TokenData } from 'src/common/decorator/token-data.decorator';
|
||||
import { PagingExamPaperDto } from 'src/assessment-evaluation/dto/dtos-exam-paper-manager/exam-paper/paging-exam-paper.dto';
|
||||
import { QuestionsForPaperService } from 'src/assessment-evaluation/service/service-exam-paper-manager/questions-for-paper';
|
||||
import { TokenDataEntity } from 'src/common/entities/token-data.entity';
|
||||
import { ExamQuestionsResultService } from 'src/assessment-evaluation/service/service-exam-manager/exam-questions-result.service';
|
||||
|
||||
@ApiTags('考核测评 - 错题巩固')
|
||||
@ApiBearerAuth()
|
||||
// @NoAuthToken()
|
||||
@Controller('assessmentEvaluation/mistake')
|
||||
export class MistakeAgainController {
|
||||
constructor(
|
||||
private readonly QuestionsForPaperService: QuestionsForPaperService,
|
||||
private readonly ExamQuestionsResultService: ExamQuestionsResultService,
|
||||
) {}
|
||||
|
||||
/*
|
||||
根据分类查询所有试题
|
||||
*/
|
||||
@Get('questions/:classifyId')
|
||||
// @NoAuthToken()
|
||||
@ApiOperation({ summary: '根据分类查询所有试题' })
|
||||
async getQuestionAndExamInfoByClassifyId(
|
||||
@Param('classifyId') classifyId: number,
|
||||
@Query() querys: { showUserAnswer: number },
|
||||
@TokenData() token: TokenDataEntity,
|
||||
) {
|
||||
if (!querys.showUserAnswer) querys.showUserAnswer = 1;
|
||||
return this.ExamQuestionsResultService.getAllMistakeQuestionsByClassifyId(
|
||||
+classifyId,
|
||||
token,
|
||||
+querys.showUserAnswer,
|
||||
);
|
||||
}
|
||||
/*
|
||||
分页获取类型错题巩固列表
|
||||
*/
|
||||
@Get('paging')
|
||||
// @NoAuthToken()
|
||||
@ApiOperation({ summary: '分页获取类型错题巩固列表' })
|
||||
async pagingMistakeList(
|
||||
@Query() PagingExamPaperDto: PagingExamPaperDto,
|
||||
@TokenData() token: TokenDataEntity,
|
||||
) {
|
||||
return this.QuestionsForPaperService.pagingMistakeQuestion(
|
||||
PagingExamPaperDto,
|
||||
token,
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
根据分类查最后一次考试ID
|
||||
*/
|
||||
@Get('lastExamHistory/:classifyId')
|
||||
// @NoAuthToken()
|
||||
@ApiOperation({ summary: '根据分类查最后一次考试ID' })
|
||||
async getLastOnlineExamHistoryInfoByQuestionClassifyId(
|
||||
@Param('classifyId') classifyId: number,
|
||||
@TokenData() token: TokenDataEntity,
|
||||
) {
|
||||
return this.ExamQuestionsResultService.getLastOnlineExamHistoryInfoByQuestionClassifyId(
|
||||
token,
|
||||
+classifyId,
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
将错题得分改为试题分数
|
||||
*/
|
||||
@Patch('submitMistakes/:classifyId')
|
||||
// @NoAuthToken()
|
||||
@ApiOperation({ summary: '将错题得分改为试题分数' })
|
||||
async submitMistakes(
|
||||
@Param('classifyId') classifyId: number,
|
||||
@Body() answers: Array<any>,
|
||||
@TokenData() token: TokenDataEntity,
|
||||
) {
|
||||
console.log(answers, token);
|
||||
const questions =
|
||||
await this.ExamQuestionsResultService.getAllMistakeQuestionsByClassifyId(
|
||||
+classifyId,
|
||||
token,
|
||||
1,
|
||||
);
|
||||
const res = {
|
||||
pass: 0,
|
||||
faild: 0,
|
||||
};
|
||||
questions.forEach((quesItem) => {
|
||||
const ansItem = answers.find((ans) => ans.questionId === quesItem.id);
|
||||
if (ansItem) {
|
||||
const isPass = checkAnswer(quesItem, ansItem.answer);
|
||||
console.log(isPass);
|
||||
if (isPass.pass) {
|
||||
res.pass++;
|
||||
this.ExamQuestionsResultService.updateMiskateScore(ansItem.resultId);
|
||||
} else {
|
||||
res.faild++;
|
||||
}
|
||||
} else {
|
||||
res.faild++;
|
||||
}
|
||||
});
|
||||
|
||||
// this.OnlineExamHistoryController.checkAnswer()
|
||||
// this.ExamQuestionsResultService.updateMiskateScore()
|
||||
return res;
|
||||
// return true;
|
||||
}
|
||||
}
|
||||
// 包含率算法
|
||||
export function jaccard(ans, uans) {
|
||||
const ansSet = new Set(ans.split(''));
|
||||
const uAnsSet = new Set(uans.split(''));
|
||||
const uAnsHasAns = new Set([...ansSet].filter((x) => uAnsSet.has(x)));
|
||||
return uAnsHasAns.size / ansSet.size;
|
||||
}
|
||||
/*
|
||||
判断是否答对
|
||||
*/
|
||||
export function checkAnswer(question, userAnswer) {
|
||||
// 用户答案
|
||||
let answer = userAnswer;
|
||||
if (!answer) {
|
||||
return { pass: false, score: 0 };
|
||||
}
|
||||
let oneOptionScore;
|
||||
let checkdOptions = null;
|
||||
// 正确答案
|
||||
try {
|
||||
checkdOptions = JSON.parse(question.answer);
|
||||
} catch (error) {
|
||||
checkdOptions = null;
|
||||
}
|
||||
|
||||
if (question.element === 'textarea') {
|
||||
answer = userAnswer[0];
|
||||
// checkdOptions = JSON.parse(question.options);
|
||||
// oneOptionScore = Math.ceil(question.score / checkdOptions.length);
|
||||
}
|
||||
if (question.element === 'input') {
|
||||
checkdOptions = JSON.parse(question.options);
|
||||
}
|
||||
let isTrue;
|
||||
if (question.element === 'textarea') {
|
||||
isTrue = { pass: false, score: 0 };
|
||||
// for (let ind = 0; ind < checkdOptions.length; ind++) {
|
||||
// const element = checkdOptions[ind];
|
||||
// if (answer.indexOf(element) !== -1) {
|
||||
// isTrue.score += oneOptionScore;
|
||||
// if (isTrue.score >= question.score) {
|
||||
// isTrue.score = question.score;
|
||||
// isTrue.pass = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
const questionScore = question.score;
|
||||
let questionAnswer = '';
|
||||
try {
|
||||
questionAnswer = JSON.parse(question.answer);
|
||||
if (questionAnswer.length > 0) {
|
||||
const percent = jaccard(questionAnswer[0], answer);
|
||||
isTrue.score = Math.floor(questionScore * percent);
|
||||
if (percent === 1) {
|
||||
isTrue.pass = true;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('-?>>>>>>>>>>>>>>>>>>>>>>', error);
|
||||
}
|
||||
} else {
|
||||
isTrue = { pass: true, score: question.score };
|
||||
for (let ind = 0; ind < checkdOptions.length; ind++) {
|
||||
const element = checkdOptions[ind];
|
||||
if (answer.indexOf(element) == -1) {
|
||||
isTrue = { pass: false, score: 0 };
|
||||
}
|
||||
|
||||
// 填空题必须按顺序
|
||||
if (question.element === 'input') {
|
||||
if (element !== answer[ind]) {
|
||||
isTrue = { pass: false, score: 0 };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(answer, checkdOptions);
|
||||
return isTrue;
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Param,
|
||||
Delete,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { TokenData } from 'src/common/decorator/token-data.decorator';
|
||||
import { ExamPaperService } from 'src/assessment-evaluation/service/service-exam-paper-manager/exam-paper.service';
|
||||
import { PagingExamPaperDto } from 'src/assessment-evaluation/dto/dtos-exam-paper-manager/exam-paper/paging-exam-paper.dto';
|
||||
import { CreateExamPaperDto } from 'src/assessment-evaluation/dto/dtos-exam-paper-manager/exam-paper/create-exam-paper.dto';
|
||||
import { QuestionsForPaperService } from 'src/assessment-evaluation/service/service-exam-paper-manager/questions-for-paper';
|
||||
import { TokenDataEntity } from 'src/common/entities/token-data.entity';
|
||||
import { ExamService } from 'src/assessment-evaluation/service/service-exam-manager/exam.service';
|
||||
import { StudentOnlineExamService } from 'src/assessment-evaluation/service/service-exam-manager/student-online-exam.service';
|
||||
|
||||
@ApiTags('模拟考试')
|
||||
@ApiBearerAuth()
|
||||
// @NoAuthToken()
|
||||
@Controller('assessmentEvaluation/simtest')
|
||||
export class SimTestController {
|
||||
constructor(
|
||||
private readonly ExamPaperService: ExamPaperService,
|
||||
private readonly QuestionsForPaperService: QuestionsForPaperService,
|
||||
private readonly ExamService: ExamService,
|
||||
private readonly StudentOnlineExam: StudentOnlineExamService,
|
||||
) {}
|
||||
|
||||
/*
|
||||
分页获取模拟考试
|
||||
*/
|
||||
@Get('paging')
|
||||
// @NoAuthToken()
|
||||
async paging(
|
||||
@Query() PagingExamPaperDto: PagingExamPaperDto,
|
||||
@TokenData() token: TokenDataEntity,
|
||||
) {
|
||||
return this.ExamPaperService.pagingSim(PagingExamPaperDto, token, -1);
|
||||
}
|
||||
|
||||
/*
|
||||
创建模拟考试
|
||||
*/
|
||||
@Post()
|
||||
// @NoAuthToken()
|
||||
async create(
|
||||
@Body() CreateExamPaperDto: CreateExamPaperDto,
|
||||
@TokenData() token: any,
|
||||
) {
|
||||
CreateExamPaperDto.creator = CreateExamPaperDto.updater = token.userId;
|
||||
CreateExamPaperDto['delFlag'] = -1;
|
||||
const newPaper = await this.ExamPaperService.create(CreateExamPaperDto);
|
||||
await this.saveQuestionInfo(
|
||||
newPaper.id,
|
||||
token,
|
||||
CreateExamPaperDto.questionInfo,
|
||||
);
|
||||
// 创建完成试卷创建考试
|
||||
const now = new Date();
|
||||
const exam = await this.ExamService.create({
|
||||
title: CreateExamPaperDto.title,
|
||||
classifyId: -1,
|
||||
examDesc: '',
|
||||
examStartTime: now,
|
||||
examEndTime: now,
|
||||
examDuration: CreateExamPaperDto.paperDuration,
|
||||
examTimes: -1,
|
||||
passPercent: CreateExamPaperDto.passPercent,
|
||||
questionsIsRandomSort: 0,
|
||||
optionsIsRandomSort: 0,
|
||||
successTips: '恭喜您,考试通过',
|
||||
failedTips: '您本次考试未通过',
|
||||
waitingTips: '请等待人工评分',
|
||||
examPaperId: newPaper.id,
|
||||
examMode: 0,
|
||||
totalScore: CreateExamPaperDto.totalScore,
|
||||
gradePaperMode: 0,
|
||||
examinerId: token.userId,
|
||||
joinMode: 0,
|
||||
delFlag: -1,
|
||||
joinPassword: '',
|
||||
creator: token.userId,
|
||||
updater: token.userId,
|
||||
});
|
||||
console.log(exam);
|
||||
const studentOnlineExam = await this.StudentOnlineExam.createOne({
|
||||
userId: token.userId,
|
||||
examId: exam.id,
|
||||
mistakes: 0,
|
||||
examTimes: 0,
|
||||
delFlag: -1,
|
||||
creator: token.userId,
|
||||
updater: token.userId,
|
||||
});
|
||||
return { newPaper, exam, studentOnlineExam };
|
||||
}
|
||||
/*
|
||||
保存试题信息
|
||||
*/
|
||||
async saveQuestionInfo(paperId: any, token: any, questionInfo: any) {
|
||||
const { fixdQuestions, randQuestions } = questionInfo;
|
||||
const fixdNew = fixdQuestions.map((fixItem) => {
|
||||
return {
|
||||
examPaperId: paperId,
|
||||
questionId: fixItem.id,
|
||||
score: fixItem['itemScore'],
|
||||
isFixed: 1,
|
||||
type: fixItem['typeId'],
|
||||
updater: token.userId,
|
||||
creator: token.userId,
|
||||
};
|
||||
});
|
||||
const randNew = randQuestions.map((fixItem) => {
|
||||
return {
|
||||
examPaperId: paperId,
|
||||
questionId: fixItem.id,
|
||||
score: fixItem['itemScore'],
|
||||
isFixed: 0,
|
||||
type: fixItem['typeId'],
|
||||
updater: token.userId,
|
||||
creator: token.userId,
|
||||
};
|
||||
});
|
||||
const FixRes = await this.QuestionsForPaperService.create(fixdNew);
|
||||
const RandRes = await this.QuestionsForPaperService.create(randNew);
|
||||
return { FixRes, RandRes };
|
||||
}
|
||||
|
||||
/*
|
||||
删除模拟考试
|
||||
*/
|
||||
@Delete('item/:id')
|
||||
async remove(@Param('id') id: string) {
|
||||
await this.ExamPaperService.remove(+id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export class CreateAssessmentEvaluationDto {}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
export class CreateExamClassifyDto extends CreateDto {
|
||||
@ApiProperty({ description: '考试分类名' })
|
||||
@IsNotEmpty({ message: '考试分类名不能为空' })
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ description: '上级考试分类', default: 0 })
|
||||
@IsNotEmpty({ message: '上级考试分类不能为空' })
|
||||
pid: number;
|
||||
|
||||
@ApiProperty({ description: '考试分类层级', default: 1 })
|
||||
@IsNotEmpty({ message: '层级不能为空' })
|
||||
level: number;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
export class UpdateExamClassifyDto extends CreateDto {
|
||||
@ApiProperty({ description: '考试分类名' })
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ description: '上级考试分类', default: 0 })
|
||||
pid: number;
|
||||
|
||||
@ApiProperty({ description: '考试分类层级', default: 1 })
|
||||
level: number;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
/**
|
||||
* 考试表
|
||||
*/
|
||||
export class CreateExamDto extends CreateDto {
|
||||
@ApiProperty({ description: '考试名称' })
|
||||
@IsNotEmpty({ message: '考试名称不能为空' })
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ description: '考试分类' })
|
||||
@IsNotEmpty({ message: '考试分类不能为空' })
|
||||
classifyId: number;
|
||||
|
||||
@ApiProperty({ description: '考试描述' })
|
||||
examDesc: string;
|
||||
|
||||
@ApiProperty({ description: '考试开始时间' })
|
||||
@IsNotEmpty({ message: '考试开始时间不能为空' })
|
||||
examStartTime: Date;
|
||||
|
||||
@ApiProperty({ description: '考试结束时间' })
|
||||
@IsNotEmpty({ message: '考试结束时间不能为空' })
|
||||
examEndTime: Date;
|
||||
|
||||
@ApiProperty({ description: '考试时长', default: 120 })
|
||||
@IsNotEmpty({ message: '考试时长不能为空' })
|
||||
examDuration: number;
|
||||
|
||||
@ApiProperty({ description: '考试机会', default: 1 })
|
||||
@IsNotEmpty({ message: '考试时长不能为空' })
|
||||
examTimes: number;
|
||||
|
||||
@ApiProperty({ description: '及格分占比', default: 1 })
|
||||
@IsNotEmpty({ message: '及格分占比不能为空' })
|
||||
passPercent: number;
|
||||
|
||||
@ApiProperty({ description: '试题乱序', default: 0 })
|
||||
questionsIsRandomSort: number;
|
||||
|
||||
@ApiProperty({ description: '选项乱序', default: 0 })
|
||||
optionsIsRandomSort: number;
|
||||
|
||||
@ApiProperty({ description: '及格提示信息', default: '考试成功通过' })
|
||||
@IsNotEmpty({ message: '及格提示信息不能为空' })
|
||||
successTips: string;
|
||||
|
||||
@ApiProperty({ description: '不及格提示信息', default: '您没有通过本次考试' })
|
||||
@IsNotEmpty({ message: '不及格提示信息不能为空' })
|
||||
failedTips: string;
|
||||
|
||||
@ApiProperty({ description: '等待人工评卷信息', default: '等待老师人工评分' })
|
||||
@IsNotEmpty({ message: '等待人工评卷信息不能为空' })
|
||||
waitingTips: string;
|
||||
|
||||
@ApiProperty({ description: '试卷ID', default: 0 })
|
||||
@IsNotEmpty({ message: '试卷ID不能为空' })
|
||||
examPaperId: number;
|
||||
|
||||
@ApiProperty({ description: '考试模式(0整卷,1逐题)', default: 0 })
|
||||
@IsNotEmpty({ message: '考试模式不能为空' })
|
||||
examMode: number;
|
||||
|
||||
@ApiProperty({ description: '总分', default: 100 })
|
||||
@IsNotEmpty({ message: '总分不能为空' })
|
||||
totalScore: number;
|
||||
|
||||
@ApiProperty({ description: '判卷方式(0机器 1人工)', default: 0 })
|
||||
@IsNotEmpty({ message: '总分不能为空' })
|
||||
gradePaperMode: number;
|
||||
|
||||
@ApiProperty({ description: '判卷人Id', default: 'SYSTEM' })
|
||||
examinerId: string;
|
||||
|
||||
@ApiProperty({ description: '参加方式(预留不做)', default: 'SYSTEM' })
|
||||
joinMode: number;
|
||||
|
||||
@ApiProperty({ description: '参加考试口令6位', default: '123456' })
|
||||
joinPassword: string;
|
||||
|
||||
@ApiProperty({ description: '是否删除', default: 0 })
|
||||
delFlag: number;
|
||||
// @Column({ comment: '试卷状态(0待开考,1已开考,2已结束等', default: 0 })
|
||||
// @IsNotEmpty({ message: '试卷状态不能为空' })
|
||||
// status: number;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { PaginationDTO } from 'src/assessment-evaluation/dto/pagination.dto';
|
||||
|
||||
export class PagingExamDto extends PaginationDTO {
|
||||
@ApiProperty({ description: '试题分类ID', required: false })
|
||||
classifyId: number;
|
||||
|
||||
@ApiProperty({ description: '试题名模糊查询', required: false })
|
||||
title: number;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
/**
|
||||
* 考试表
|
||||
*/
|
||||
export class UpdateExamDto extends CreateDto {
|
||||
@ApiProperty({ description: '考试名称' })
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ description: '考试分类' })
|
||||
classifyId: number;
|
||||
|
||||
@ApiProperty({ description: '考试描述' })
|
||||
examDesc: string;
|
||||
|
||||
@ApiProperty({ description: '考试开始时间' })
|
||||
examStartTime: Date;
|
||||
|
||||
@ApiProperty({ description: '考试结束时间' })
|
||||
examEndTime: Date;
|
||||
|
||||
@ApiProperty({ description: '考试时长', default: 120 })
|
||||
examDuration: number;
|
||||
|
||||
@ApiProperty({ description: '考试机会', default: 1 })
|
||||
examTimes: number;
|
||||
|
||||
@ApiProperty({ description: '及格分占比', default: 1 })
|
||||
passPercent: number;
|
||||
|
||||
@ApiProperty({ description: '试题乱序', default: 0 })
|
||||
questionsIsRandomSort: number;
|
||||
|
||||
@ApiProperty({ description: '选项乱序', default: 0 })
|
||||
optionsIsRandomSort: number;
|
||||
|
||||
@ApiProperty({ description: '及格提示信息', default: '考试成功通过' })
|
||||
successTips: string;
|
||||
|
||||
@ApiProperty({ description: '不及格提示信息', default: '您没有通过本次考试' })
|
||||
failedTips: string;
|
||||
|
||||
@ApiProperty({ description: '等待人工评卷信息', default: '等待老师人工评分' })
|
||||
waitingTips: string;
|
||||
|
||||
@ApiProperty({ description: '试卷ID', default: 0 })
|
||||
examPaperId: number;
|
||||
|
||||
@ApiProperty({ description: '考试模式(0整卷,1逐题)', default: 0 })
|
||||
examMode: number;
|
||||
|
||||
@ApiProperty({ description: '总分', default: 100 })
|
||||
totalScore: number;
|
||||
|
||||
@ApiProperty({ description: '判卷方式(0机器 1人工)', default: 0 })
|
||||
gradePaperMode: number;
|
||||
|
||||
@ApiProperty({ description: '是否匿名判卷', default: 'SYSTEM' })
|
||||
isAnonymous: number;
|
||||
|
||||
@ApiProperty({ description: '判卷人Id', default: 'SYSTEM' })
|
||||
examinerId: string;
|
||||
|
||||
@ApiProperty({ description: '参加方式(预留不做)', default: 'SYSTEM' })
|
||||
joinMode: number;
|
||||
|
||||
@ApiProperty({ description: '参加考试口令6位', default: '123456' })
|
||||
joinPassword: string;
|
||||
|
||||
// @Column({ comment: '试卷状态(0待开考,1已开考,2已结束等', default: 0 })
|
||||
// @IsNotEmpty({ message: '试卷状态不能为空' })
|
||||
// status: number;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
export class CreateStudentOnlineExamDto extends CreateDto {
|
||||
@ApiProperty({ description: '学员ID' })
|
||||
@IsNotEmpty({ message: '学员Id不能为空' })
|
||||
userId: string;
|
||||
|
||||
@ApiProperty({ description: '考试Id', default: 0 })
|
||||
@IsNotEmpty({ message: '考试ID不能为空' })
|
||||
examId: number;
|
||||
|
||||
@ApiProperty({ description: '错题数', default: 0 })
|
||||
mistakes: number;
|
||||
|
||||
@ApiProperty({ description: '考试允许次数', default: 3 })
|
||||
examTimes: number;
|
||||
|
||||
@ApiProperty({ description: '是否删除', default: 0 })
|
||||
delFlag: number;
|
||||
|
||||
// @ApiProperty({ description: '我已考试次数', default: 0 })
|
||||
// myExamTimes: number;
|
||||
|
||||
// @ApiProperty({ description: '对于学员的考试状态', default: 1 })
|
||||
// status: number;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { PaginationDTO } from 'src/assessment-evaluation/dto/pagination.dto';
|
||||
|
||||
export class PagingStudentOnlineExamDto extends PaginationDTO {
|
||||
@ApiProperty({ description: '试题名模糊查询', required: false })
|
||||
title: number;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
export class UpdateStudentOnlineExamDto extends CreateDto {
|
||||
@ApiProperty({ description: '学员ID' })
|
||||
userId: string;
|
||||
|
||||
@ApiProperty({ description: '考试Id', default: 0 })
|
||||
examId: number;
|
||||
|
||||
@ApiProperty({ description: '错题数', default: 0 })
|
||||
mistakes: number;
|
||||
|
||||
@ApiProperty({ description: '考试允许次数', default: 3 })
|
||||
examTimes: number;
|
||||
|
||||
// @ApiProperty({ description: '我已考试次数', default: 0 })
|
||||
// myExamTimes: number;
|
||||
|
||||
// @ApiProperty({ description: '对于学员的考试状态', default: 1 })
|
||||
// status: number;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
export class CreateExamPaperClassifyDto extends CreateDto {
|
||||
@ApiProperty({ description: '试卷分类名' })
|
||||
@IsNotEmpty({ message: '试卷分类名不能为空' })
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ description: '上级试卷分类', default: 0 })
|
||||
@IsNotEmpty({ message: '上级试卷分类不能为空' })
|
||||
pid: number;
|
||||
|
||||
@ApiProperty({ description: '试卷分类层级', default: 1 })
|
||||
@IsNotEmpty({ message: '层级不能为空' })
|
||||
level: number;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
export class UpdateExamPaperClassifyDto extends CreateDto {
|
||||
@ApiProperty({ description: '试卷分类名' })
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ description: '上级试卷分类', default: 0 })
|
||||
pid: number;
|
||||
|
||||
@ApiProperty({ description: '试卷分类层级', default: 1 })
|
||||
level: number;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
import { Question } from 'src/assessment-evaluation/entities/entities-question-manager/question.entity';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
export class CreateExamPaperDto extends CreateDto {
|
||||
@ApiProperty({ description: '试卷名称' })
|
||||
@IsNotEmpty({ message: '试卷名称不能为空' })
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ description: '试卷描述' })
|
||||
paperDesc: string;
|
||||
|
||||
@ApiProperty({ description: '答题时长' })
|
||||
@IsNotEmpty({ message: '答题时长不能为空' })
|
||||
paperDuration: number;
|
||||
|
||||
@ApiProperty({ description: '试卷总分' })
|
||||
@IsNotEmpty({ message: '试卷总分不能为空' })
|
||||
totalScore: number;
|
||||
|
||||
@ApiProperty({ description: '试题数' })
|
||||
@IsNotEmpty({ message: '试题数不能为空' })
|
||||
questionCount: number;
|
||||
|
||||
@ApiProperty({ description: '试卷分类' })
|
||||
@IsNotEmpty({ message: '试卷分类不能为空' })
|
||||
classifyId: number;
|
||||
|
||||
@ApiProperty({ description: '及格分比例' })
|
||||
passPercent: number;
|
||||
|
||||
@ApiProperty({ description: '试题是否乱序', default: 0 })
|
||||
questionsIsRandomSort: number;
|
||||
|
||||
@ApiProperty({ description: '选项是否乱序', default: 0 })
|
||||
optionsIsRandomSort: number;
|
||||
|
||||
@ApiProperty({ description: '试卷状态 0关闭 1开启', default: 0 })
|
||||
status: number;
|
||||
|
||||
@ApiProperty({ description: '分享试卷 0关闭 1开启', default: 0 })
|
||||
share: number;
|
||||
|
||||
@ApiProperty({ description: '是否为模拟考试 (0、1)', default: 0 })
|
||||
isPracticeExam: number;
|
||||
|
||||
@ApiProperty({ description: '试卷中试题信息', default: 0 })
|
||||
questionInfo: {
|
||||
fixdQuestions: Question[];
|
||||
randQuestions: Question[];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsJSON, IsNotEmpty } from 'class-validator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
import { PaginationDTO } from 'src/assessment-evaluation/dto/pagination.dto';
|
||||
|
||||
export class PagingExamPaperDto extends PaginationDTO {
|
||||
@ApiProperty({ description: '试题分类ID', required: false })
|
||||
classifyId: number;
|
||||
|
||||
@ApiProperty({ description: '是否为模拟考试', required: false })
|
||||
isPractive: number;
|
||||
|
||||
@ApiProperty({ description: '试题名模糊查询', required: false })
|
||||
title: number;
|
||||
|
||||
@ApiProperty({ description: '是否为已开启试卷', required: false })
|
||||
status: number;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
import { Question } from 'src/assessment-evaluation/entities/entities-question-manager/question.entity';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
export class UpdateExamPaperDto extends CreateDto {
|
||||
@ApiProperty({ description: '试卷名称', required: false })
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ description: '试卷描述', required: false })
|
||||
paperDesc: string;
|
||||
|
||||
@ApiProperty({ description: '答题时长', required: false })
|
||||
paperDuration: number;
|
||||
|
||||
@ApiProperty({ description: '试卷总分', required: false })
|
||||
totalScore: number;
|
||||
|
||||
@ApiProperty({ description: '试题数', required: false })
|
||||
questionCount: number;
|
||||
|
||||
@ApiProperty({ description: '试卷分类', required: false })
|
||||
classifyId: number;
|
||||
|
||||
@ApiProperty({ description: '及格分比例', required: false })
|
||||
passPercent: number;
|
||||
|
||||
@ApiProperty({ description: '试题是否乱序', default: 0, required: false })
|
||||
questionsIsRandomSort: number;
|
||||
|
||||
@ApiProperty({ description: '选项是否乱序', default: 0, required: false })
|
||||
optionsIsRandomSort: number;
|
||||
|
||||
@ApiProperty({
|
||||
description: '试卷状态 0关闭 1开启',
|
||||
default: 0,
|
||||
required: false,
|
||||
})
|
||||
status: number;
|
||||
|
||||
@ApiProperty({
|
||||
description: '分享试卷 0关闭 1开启',
|
||||
default: 0,
|
||||
required: false,
|
||||
})
|
||||
share: number;
|
||||
|
||||
@ApiProperty({
|
||||
description: '是否为模拟考试 (0、1)',
|
||||
default: 0,
|
||||
required: false,
|
||||
})
|
||||
isPracticeExam: number;
|
||||
|
||||
@ApiProperty({ description: '试卷中试题信息', default: 0, required: false })
|
||||
questionInfo: {
|
||||
fixdQuestions: Question[];
|
||||
randQuestions: Question[];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
export class CreateQuestionForPaperDto extends CreateDto {
|
||||
@ApiProperty({ description: '试卷Id' })
|
||||
@IsNotEmpty({ message: '试卷ID不能为空' })
|
||||
examPaperId: number;
|
||||
|
||||
@ApiProperty({ description: '试题Id' })
|
||||
@IsNotEmpty({ message: '试题Id不能为空' })
|
||||
questionId: string;
|
||||
|
||||
@ApiProperty({ description: '分值', default: 0 })
|
||||
@IsNotEmpty({ message: '分值不能为空' })
|
||||
score: number;
|
||||
|
||||
@ApiProperty({ description: '试题类型', default: 0 })
|
||||
@IsNotEmpty({ message: '试题类型不能为空' })
|
||||
type: number;
|
||||
|
||||
@ApiProperty({ description: '试题是否为随机抽取', default: 0 })
|
||||
@IsNotEmpty({ message: '试题是否为随机抽取不能为空' })
|
||||
isFixed: number;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
export class CreateKnowledgePointDto extends CreateDto {
|
||||
@ApiProperty({ description: '知识点名' })
|
||||
@IsNotEmpty({ message: '知识点名不能为空' })
|
||||
name: string;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
export class UpdateKnowledgePointDto extends CreateDto {
|
||||
@ApiProperty({ description: '知识点ID' })
|
||||
@IsNotEmpty({ message: '知识点ID不能为空' })
|
||||
id: number;
|
||||
|
||||
@ApiProperty({ description: '知识点名' })
|
||||
@IsNotEmpty({ message: '知识点名不能为空' })
|
||||
name: string;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
export class CreateQuestionClassifyDto extends CreateDto {
|
||||
@ApiProperty({ description: '试题分类名' })
|
||||
@IsNotEmpty({ message: '试题分类名不能为空' })
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ description: '上级试题分类', default: 0 })
|
||||
@IsNotEmpty({ message: '上级试题分类不能为空' })
|
||||
pid: number;
|
||||
|
||||
@ApiProperty({ description: '试题分类层级', default: 1 })
|
||||
@IsNotEmpty({ message: '层级不能为空' })
|
||||
level: number;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
export class UpdateQuestionClassifyDto extends CreateDto {
|
||||
@ApiProperty({ description: '试卷分类名' })
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ description: '上级试题分类', default: 0 })
|
||||
pid: number;
|
||||
|
||||
@ApiProperty({ description: '试题分类层级', default: 1 })
|
||||
level: number;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
export class CreateQuestionDifficultyLevelDto extends CreateDto {
|
||||
@ApiProperty({ description: '难易程度名' })
|
||||
@IsNotEmpty({ message: '难易程度名不能为空' })
|
||||
name: string;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
export class UpdateQuestionDifficultyLevelDto extends CreateDto {
|
||||
@ApiProperty({ description: '难易程度Id' })
|
||||
@IsNotEmpty({ message: '难易程度ID不能为空' })
|
||||
id: number;
|
||||
|
||||
@ApiProperty({ description: '难易程度名' })
|
||||
@IsNotEmpty({ message: '难易程度名不能为空' })
|
||||
name: string;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
export class CreateQuestionTypeDto extends CreateDto {
|
||||
@ApiProperty({ description: '试题类型' })
|
||||
@IsNotEmpty({ message: '试题类型名不能为空' })
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ description: '元素类型' })
|
||||
element: string;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
export class UpdateQuestionTypeDto extends CreateDto {
|
||||
@ApiProperty({ description: '试题类型Id' })
|
||||
@IsNotEmpty({ message: '试题类型ID不能为空' })
|
||||
id: number;
|
||||
|
||||
@ApiProperty({ description: '试题类型名' })
|
||||
@IsNotEmpty({ message: '试题类型名不能为空' })
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ description: '元素类型' })
|
||||
element: string;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsJSON, IsNotEmpty } from 'class-validator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
export class CreateQuestionDto extends CreateDto {
|
||||
@ApiProperty({ description: '试题类型' })
|
||||
@IsNotEmpty({ message: '试题类型名不能为空' })
|
||||
type: number;
|
||||
|
||||
@ApiProperty({ description: '难易程度' })
|
||||
@IsNotEmpty({ message: '难易程度不能为空' })
|
||||
difficultyLevel: number;
|
||||
|
||||
@ApiProperty({ description: '试题分类' })
|
||||
@IsNotEmpty({ message: '试题分类不能为空' })
|
||||
classifyId: number;
|
||||
|
||||
@ApiProperty({ description: '试题题目' })
|
||||
@IsNotEmpty({ message: '试题题目不能为空' })
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ description: '试题选项' })
|
||||
// @IsNotEmpty({ message: '试题选项不能为空' })
|
||||
@IsJSON({ message: '试题选项应为JSON格式' })
|
||||
options: string;
|
||||
|
||||
@ApiProperty({ description: '正确答案' })
|
||||
// @IsNotEmpty({ message: '正确答案不能为空' })
|
||||
@IsJSON({ message: '正确答案应为JSON格式' })
|
||||
answer: string;
|
||||
|
||||
@ApiProperty({ description: '知识点ID' })
|
||||
knowledgeId: number;
|
||||
|
||||
@ApiProperty({ description: '答案解析' })
|
||||
analysis: string;
|
||||
|
||||
@ApiProperty({ description: '是否允许学员使用' })
|
||||
student: number;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class GetQuestionCountDto {
|
||||
@ApiProperty({ description: '试题分类ID', required: false })
|
||||
classifyId: number;
|
||||
@ApiProperty({ description: '试题类型ID', required: false })
|
||||
type: number;
|
||||
@ApiProperty({ description: '试题难度ID', required: false })
|
||||
difficultyLevel: number;
|
||||
@ApiProperty({ description: '随机数量', required: false })
|
||||
count: number;
|
||||
@ApiProperty({ description: '排除ID', required: false })
|
||||
excludeIds: number[];
|
||||
@ApiProperty({ description: '学生可选', required: false, default: 0 })
|
||||
student: number;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsJSON, IsNotEmpty } from 'class-validator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
import { PaginationDTO } from 'src/assessment-evaluation/dto/pagination.dto';
|
||||
|
||||
export class PagingQuestionDto extends PaginationDTO {
|
||||
@ApiProperty({ description: '试题分类ID', required: false })
|
||||
classifyId: number;
|
||||
@ApiProperty({ description: '试题类型ID', required: false })
|
||||
type: number;
|
||||
@ApiProperty({ description: '试题难度ID', required: false })
|
||||
difficultyLevel: number;
|
||||
@ApiProperty({ description: '试题名模糊查询', required: false })
|
||||
title: number;
|
||||
@ApiProperty({ description: '试题名模糊查询', required: false })
|
||||
excludeIds: number[];
|
||||
@ApiProperty({ description: '学生可选试题', required: false, default: 0 })
|
||||
student: number;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsJSON } from 'class-validator';
|
||||
import { CreateDto } from 'src/common/dto/create.dto';
|
||||
|
||||
export class UpdateQuestionDto extends CreateDto {
|
||||
@ApiProperty({ description: '试题类型' })
|
||||
type: number;
|
||||
|
||||
@ApiProperty({ description: '难易程度' })
|
||||
difficultyLevel: number;
|
||||
|
||||
@ApiProperty({ description: '试题分类' })
|
||||
classifyId: number;
|
||||
|
||||
@ApiProperty({ description: '试题题目' })
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ description: '试题选项' })
|
||||
@IsJSON({ message: '试题选项应为JSON格式' })
|
||||
options: string;
|
||||
|
||||
@ApiProperty({ description: '正确答案' })
|
||||
@IsJSON({ message: '试题选项应为JSON格式' })
|
||||
answer: string;
|
||||
|
||||
@ApiProperty({ description: '知识点ID' })
|
||||
knowledgeId: number;
|
||||
|
||||
@ApiProperty({ description: '答案解析' })
|
||||
analysis: string;
|
||||
@ApiProperty({ description: '是否允许学员使用' })
|
||||
student: number;
|
||||
}
|
||||
38
serve/src/assessment-evaluation/dto/pagination.dto.ts
Normal file
38
serve/src/assessment-evaluation/dto/pagination.dto.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsOptional, Matches, Min, MIN } from 'class-validator';
|
||||
// 自定义的校验规则
|
||||
|
||||
export class PaginationDTO {
|
||||
/**
|
||||
* 第几页
|
||||
* @example 1
|
||||
*/
|
||||
@IsOptional()
|
||||
// @Min(0,{message:"page不能小于0"})
|
||||
@ApiProperty({ description: 'currentPage', default: 1 })
|
||||
readonly currentPage?: number;
|
||||
|
||||
/**
|
||||
* 每页数据条数
|
||||
* @example 10
|
||||
*/
|
||||
@IsOptional()
|
||||
// @Min(0,{message:"pageSize不能小于0"})
|
||||
@ApiProperty({ description: 'pageSize', default: 10 })
|
||||
readonly pageSize?: number;
|
||||
|
||||
/**
|
||||
* 总页数
|
||||
* @example 10
|
||||
*/
|
||||
pages: number;
|
||||
|
||||
/**
|
||||
* 总条数
|
||||
* @example 100
|
||||
*/
|
||||
total: number;
|
||||
|
||||
// 数据
|
||||
records: any;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { CreateAssessmentEvaluationDto } from './create-assessment-evaluation.dto';
|
||||
|
||||
export class UpdateAssessmentEvaluationDto extends PartialType(
|
||||
CreateAssessmentEvaluationDto,
|
||||
) {}
|
||||
@@ -0,0 +1 @@
|
||||
export class AssessmentEvaluation {}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { IncrementIdEntity } from 'src/common/entities/increment-id.entity';
|
||||
import { Column, Entity } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 考试分类表(树形结构)
|
||||
*/
|
||||
@Entity()
|
||||
export class ExamClassify extends IncrementIdEntity {
|
||||
@Column({ comment: '分类名' })
|
||||
name: string;
|
||||
|
||||
@Column({ comment: '父类id' })
|
||||
pid: number;
|
||||
|
||||
@Column({ comment: '层级' })
|
||||
level: number;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { IncrementIdEntity } from 'src/common/entities/increment-id.entity';
|
||||
import { Column, Entity } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 考试试题结果存储
|
||||
*/
|
||||
@Entity()
|
||||
export class ExamQuestionsResult extends IncrementIdEntity {
|
||||
@Column({ name: 'history_id', comment: '考试记录Id' })
|
||||
historyId: number;
|
||||
|
||||
@Column({ name: 'question_id', comment: '试题ID' })
|
||||
questionId: number;
|
||||
|
||||
@Column({ name: 'question_score', comment: '该题分值' })
|
||||
questionScore: number;
|
||||
|
||||
@Column({ comment: '我的得分' })
|
||||
score: number;
|
||||
|
||||
@Column({ name: 'mistake_score', comment: '错题得分', default: 0 })
|
||||
mistakeScore: number;
|
||||
|
||||
@Column({ name: 'user_answer', comment: '我的答案' })
|
||||
userAnswer: string;
|
||||
|
||||
@Column({ comment: '老师的评语', default: null, type: 'longtext' })
|
||||
comment: string;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import { IncrementIdEntity } from 'src/common/entities/increment-id.entity';
|
||||
import { Column, Entity } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 考试表
|
||||
*/
|
||||
@Entity()
|
||||
export class Exam extends IncrementIdEntity {
|
||||
@Column({ comment: '考试名称' })
|
||||
title: string;
|
||||
|
||||
@Column({ name: 'classify_id', comment: '考试分类' })
|
||||
classifyId: number;
|
||||
|
||||
@Column({ name: 'exam_desc', comment: '考试描述' })
|
||||
examDesc: string;
|
||||
|
||||
@Column({ name: 'exam_start_time', comment: '考试开始时间' })
|
||||
examStartTime: Date;
|
||||
|
||||
@Column({ name: 'exam_end_time', comment: '考试结束时间' })
|
||||
examEndTime: Date;
|
||||
|
||||
@Column({ name: 'exam_duration', comment: '答题时长' })
|
||||
examDuration: number;
|
||||
|
||||
@Column({ name: 'exam_times', comment: '考试次数' })
|
||||
examTimes: number;
|
||||
|
||||
@Column({ name: 'pass_percent', comment: '及格分占总分百分比' })
|
||||
passPercent: number;
|
||||
|
||||
@Column({
|
||||
name: 'questions_is_random_sort',
|
||||
comment: '试题是否乱序',
|
||||
default: 0,
|
||||
})
|
||||
questionsIsRandomSort: number;
|
||||
|
||||
@Column({
|
||||
name: 'options_is_random_sort',
|
||||
comment: '选项是否乱序',
|
||||
default: 0,
|
||||
})
|
||||
optionsIsRandomSort: number;
|
||||
|
||||
@Column({ name: 'success_tips', comment: '及格提示消息' })
|
||||
successTips: string;
|
||||
|
||||
@Column({ name: 'failed_tips', comment: '不及格提示消息' })
|
||||
failedTips: string;
|
||||
|
||||
@Column({ name: 'waiting_tips', comment: '等待人工评卷提示消息' })
|
||||
waitingTips: string;
|
||||
|
||||
@Column({ name: 'exam_paper_id', comment: '所选试卷ID' })
|
||||
examPaperId: number;
|
||||
|
||||
@Column({ name: 'exam_mode', comment: '试卷模式(0整卷、1逐题)' })
|
||||
examMode: number;
|
||||
|
||||
@Column({ name: 'total_score', comment: '总分' })
|
||||
totalScore: number;
|
||||
|
||||
@Column({ name: 'grade_paper_mode', comment: '阅卷方式(0机器、1人工)' })
|
||||
gradePaperMode: number;
|
||||
|
||||
@Column({
|
||||
name: 'is_anonymous',
|
||||
comment: '是否匿名(0否 1是)',
|
||||
default: 0,
|
||||
})
|
||||
isAnonymous: number;
|
||||
|
||||
@Column({ name: 'examiner_id', comment: '判卷人ID' })
|
||||
examinerId: string;
|
||||
|
||||
@Column({
|
||||
name: 'join_mode',
|
||||
comment: '参加方式(暂时不做的功能 0账号密码、1口令)',
|
||||
default: 0,
|
||||
})
|
||||
joinMode: number;
|
||||
|
||||
@Column({
|
||||
name: 'join_password',
|
||||
comment: '参加口令6位(暂时不做的功能)',
|
||||
default: null,
|
||||
})
|
||||
joinPassword: string;
|
||||
|
||||
@Column({ comment: '试卷状态(0待开考,1已开考,2已结束等)', default: 0 })
|
||||
status: number;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { IncrementIdEntity } from 'src/common/entities/increment-id.entity';
|
||||
import { Column, Entity } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 在线考试历史
|
||||
*/
|
||||
@Entity()
|
||||
export class OnlineExamHistory extends IncrementIdEntity {
|
||||
@Column({ name: 'online_exam_id', comment: '对应在线考试ID' })
|
||||
onlineExamId: number;
|
||||
|
||||
@Column({ name: 'exam_id', comment: '发布的考试ID' })
|
||||
examId: number;
|
||||
|
||||
@Column({ name: 'exam_paper_id', comment: '考试对应试卷ID' })
|
||||
examPaperId: number;
|
||||
|
||||
@Column({ name: 'exam_duration', comment: '考试用时' })
|
||||
examDuration: number;
|
||||
|
||||
@Column({
|
||||
name: 'report_time',
|
||||
comment: '考试交卷时间',
|
||||
type: 'bigint',
|
||||
default: null,
|
||||
})
|
||||
reportTime: number;
|
||||
|
||||
@Column({
|
||||
name: 'end_timestamp',
|
||||
comment: '考试结束的时间戳',
|
||||
type: 'bigint',
|
||||
default: null,
|
||||
})
|
||||
endTimestamp: number;
|
||||
|
||||
@Column({ name: 'is_anonymous', comment: '是否匿名判卷', default: 0 })
|
||||
isAnonymous: number;
|
||||
@Column({
|
||||
name: 'is_practice_exam',
|
||||
comment: '是否为模拟考试 (0、1)',
|
||||
default: 0,
|
||||
})
|
||||
isPracticeExam: number;
|
||||
|
||||
@Column({ name: 'is_grade_paper', comment: '是否已判卷', default: 0 })
|
||||
isGradePaper: number;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { IncrementIdEntity } from 'src/common/entities/increment-id.entity';
|
||||
import { Column, Entity } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 学员在线考试表
|
||||
*/
|
||||
@Entity()
|
||||
export class StudentOnlineExam extends IncrementIdEntity {
|
||||
@Column({ name: 'user_id', comment: '学员Id' })
|
||||
userId: string;
|
||||
|
||||
@Column({ name: 'exam_id', comment: '发布的考试ID' })
|
||||
examId: number;
|
||||
|
||||
@Column({ comment: '错题数', default: 0 })
|
||||
mistakes: number;
|
||||
|
||||
@Column({ name: 'exam_times', comment: '考试允许次数' })
|
||||
examTimes: number;
|
||||
|
||||
// @Column({ comment: '对于学员的考试状态', default: 0 })
|
||||
// status: number;
|
||||
// @Column({ name: 'my_exam_times', comment: '我已考试的次数', default: 0 })
|
||||
// myExamTimes: number;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { IncrementIdEntity } from 'src/common/entities/increment-id.entity';
|
||||
import { Column, Entity } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 试题分类表(树形结构)
|
||||
*/
|
||||
@Entity()
|
||||
export class ExamPaperClassify extends IncrementIdEntity {
|
||||
@Column({ comment: '分类名' })
|
||||
name: string;
|
||||
|
||||
@Column({ comment: '父类id', default: 0 })
|
||||
pid: number;
|
||||
|
||||
@Column({ comment: '层级', default: 1 })
|
||||
level: number;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { IncrementIdEntity } from 'src/common/entities/increment-id.entity';
|
||||
import { Column, Entity } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 试卷表
|
||||
*/
|
||||
@Entity()
|
||||
export class ExamPaper extends IncrementIdEntity {
|
||||
@Column({ comment: '试卷名称' })
|
||||
title: string;
|
||||
|
||||
@Column({ name: 'paper_desc', comment: '试卷描述' })
|
||||
paperDesc: string;
|
||||
|
||||
@Column({ name: 'paper_duration', comment: '答题时长' })
|
||||
paperDuration: number;
|
||||
|
||||
@Column({ name: 'total_score', comment: '总分' })
|
||||
totalScore: number;
|
||||
|
||||
@Column({ name: 'question_count', comment: '试题数' })
|
||||
questionCount: number;
|
||||
|
||||
@Column({ name: 'pass_percent', comment: '及格分占总分百分比' })
|
||||
passPercent: number;
|
||||
|
||||
@Column({ name: 'classify_id', comment: '试卷分类' })
|
||||
classifyId: number;
|
||||
|
||||
@Column({
|
||||
name: 'questions_is_random_sort',
|
||||
comment: '试题是否乱序',
|
||||
default: 0,
|
||||
})
|
||||
questionsIsRandomSort: number;
|
||||
|
||||
@Column({
|
||||
name: 'options_is_random_sort',
|
||||
comment: '选项是否乱序',
|
||||
default: 0,
|
||||
})
|
||||
optionsIsRandomSort: number;
|
||||
|
||||
@Column({ comment: '试卷状态(0关闭,1开启)default', default: 0 })
|
||||
status: number;
|
||||
|
||||
@Column({ comment: '分享试卷(0关闭,1开启)default', default: 0 })
|
||||
share: number;
|
||||
|
||||
@Column({ name: 'is_practice_exam', comment: '是否为模拟考试', default: 0 })
|
||||
isPracticeExam: number;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { IncrementIdEntity } from 'src/common/entities/increment-id.entity';
|
||||
import { Column, Entity } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 试卷中所有试题
|
||||
*/
|
||||
@Entity()
|
||||
export class QuestionsForPaper extends IncrementIdEntity {
|
||||
@Column({ name: 'exam_paper_id', comment: '考试对应试卷ID' })
|
||||
examPaperId: number;
|
||||
|
||||
@Column({ name: 'question_id', comment: '试题ID(对应Question表)' })
|
||||
questionId: number;
|
||||
|
||||
@Column({ comment: '该题分值' })
|
||||
score: number;
|
||||
|
||||
@Column({ comment: '题类型' })
|
||||
type: number;
|
||||
|
||||
@Column({ name: 'is_fixed', comment: '固定选题、随机选题' })
|
||||
isFixed: number;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { IncrementIdEntity } from 'src/common/entities/increment-id.entity';
|
||||
import { Column, Entity } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 知识点表
|
||||
*/
|
||||
@Entity()
|
||||
export class KnowledgePoint extends IncrementIdEntity {
|
||||
@Column({ comment: '知识点' })
|
||||
name: string;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { IncrementIdEntity } from 'src/common/entities/increment-id.entity';
|
||||
import { Column, Entity } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 试题分类表(树形结构)
|
||||
*/
|
||||
@Entity()
|
||||
export class QuestionClassify extends IncrementIdEntity {
|
||||
@Column({ comment: '分类名' })
|
||||
name: string;
|
||||
|
||||
@Column({ comment: '父类id', default: 0 })
|
||||
pid: number;
|
||||
|
||||
@Column({ comment: '层级', default: 1 })
|
||||
level: number;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { IncrementIdEntity } from 'src/common/entities/increment-id.entity';
|
||||
import { Column, Entity } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 试题难易等级表
|
||||
*/
|
||||
@Entity()
|
||||
export class QuestionDifficultyLevel extends IncrementIdEntity {
|
||||
@Column({ comment: '类型名(单选多选判断填空简答)' })
|
||||
name: string;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { IncrementIdEntity } from 'src/common/entities/increment-id.entity';
|
||||
import { Column, Entity } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 试题类型表(单选、多选、判断、填空、简答)
|
||||
*/
|
||||
@Entity()
|
||||
export class QuestionType extends IncrementIdEntity {
|
||||
@Column({ comment: '类型名(单选多选判断填空简答)' })
|
||||
name: string;
|
||||
|
||||
@Column({
|
||||
comment: '元素类型(radio、checkbox、radio*2、input、textarea、[video])',
|
||||
})
|
||||
element: string;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { IncrementIdEntity } from 'src/common/entities/increment-id.entity';
|
||||
import { Column, Entity } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 试题表
|
||||
*/
|
||||
@Entity()
|
||||
export class Question extends IncrementIdEntity {
|
||||
@Column({ comment: '试题类型(对应QuestionType表)' })
|
||||
type: number;
|
||||
|
||||
@Column({
|
||||
name: 'difficulty_level',
|
||||
comment: '难易程度(对应QuestionDifficultyLevel',
|
||||
})
|
||||
difficultyLevel: number;
|
||||
|
||||
@Column({ name: 'classify_id', comment: '试题分类', default: null })
|
||||
classifyId: number;
|
||||
|
||||
@Column({ name: 'knowledge_id', comment: '知识点ID', default: null })
|
||||
knowledgeId: number;
|
||||
|
||||
@Column({ comment: '试题题目' })
|
||||
title: string;
|
||||
|
||||
@Column({ comment: '选项(json)', type: 'longtext' })
|
||||
options: string;
|
||||
|
||||
@Column({ comment: '正确答案(json)', type: 'longtext' })
|
||||
answer: string;
|
||||
|
||||
@Column({ comment: '答案解析', type: 'longtext' })
|
||||
analysis: string;
|
||||
|
||||
@Column({ comment: '是否允许学员使用', default: 0 })
|
||||
student: number;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { ExamClassifyController } from '../controller/exam-manager/exam-classify.controller';
|
||||
import { ExamController } from '../controller/exam-manager/exam.controller';
|
||||
import { OnlineExamHistoryController } from '../controller/exam-manager/online-exam-history.controller';
|
||||
import { StudentOnlineExamController } from '../controller/exam-manager/student-online-exam.controller';
|
||||
import { SimTestController } from '../controller/sim-test/sim-test.controller';
|
||||
import { ExamClassify } from '../entities/entities-exam-manager/exam-classify.entity';
|
||||
import { ExamQuestionsResult } from '../entities/entities-exam-manager/exam-questions-result.entity';
|
||||
import { Exam } from '../entities/entities-exam-manager/exam.entity';
|
||||
import { OnlineExamHistory } from '../entities/entities-exam-manager/online-exam-history.entity';
|
||||
import { StudentOnlineExam } from '../entities/entities-exam-manager/student-online-exam.entity';
|
||||
import { ExamClassifyService } from '../service/service-exam-manager/exam-classify.service';
|
||||
import { ExamQuestionsResultService } from '../service/service-exam-manager/exam-questions-result.service';
|
||||
import { ExamService } from '../service/service-exam-manager/exam.service';
|
||||
import { OnlineExamHistoryService } from '../service/service-exam-manager/online-exam-history.service';
|
||||
import { StudentOnlineExamService } from '../service/service-exam-manager/student-online-exam.service';
|
||||
|
||||
export default {
|
||||
entitis: [
|
||||
ExamClassify,
|
||||
Exam,
|
||||
StudentOnlineExam,
|
||||
OnlineExamHistory,
|
||||
ExamQuestionsResult,
|
||||
],
|
||||
controllers: [
|
||||
ExamClassifyController,
|
||||
ExamController,
|
||||
StudentOnlineExamController,
|
||||
OnlineExamHistoryController,
|
||||
SimTestController,
|
||||
],
|
||||
services: [
|
||||
ExamClassifyService,
|
||||
ExamService,
|
||||
StudentOnlineExamService,
|
||||
OnlineExamHistoryService,
|
||||
ExamQuestionsResultService,
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ExamPaperClassifyController } from 'src/assessment-evaluation/controller/exam-paper-manager/exam-paper-classify.controller';
|
||||
import { ExamPaperClassify } from 'src/assessment-evaluation/entities/entities-exam-paper-manager/exam-paper-classify.entity';
|
||||
import { ExamPaperClassifyService } from 'src/assessment-evaluation/service/service-exam-paper-manager/exam-paper-classify.service';
|
||||
import { ExamPaperController } from '../controller/exam-paper-manager/exam-paper.controller';
|
||||
import { MistakeAgainController } from '../controller/sim-test/mistake-again.controller';
|
||||
import { ExamPaper } from '../entities/entities-exam-paper-manager/exam-paper.entity';
|
||||
import { QuestionsForPaper } from '../entities/entities-exam-paper-manager/questions-for-paper.entity';
|
||||
import { ExamPaperService } from '../service/service-exam-paper-manager/exam-paper.service';
|
||||
import { QuestionsForPaperService } from '../service/service-exam-paper-manager/questions-for-paper';
|
||||
|
||||
export default {
|
||||
entitis: [ExamPaperClassify, ExamPaper, QuestionsForPaper],
|
||||
controllers: [
|
||||
ExamPaperClassifyController,
|
||||
ExamPaperController,
|
||||
MistakeAgainController,
|
||||
],
|
||||
services: [
|
||||
ExamPaperClassifyService,
|
||||
ExamPaperService,
|
||||
QuestionsForPaperService,
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
import { QuestionClassify } from 'src/assessment-evaluation/entities/entities-question-manager/question-classify.entity';
|
||||
import { QuestionClassifyController } from 'src/assessment-evaluation/controller/question-manager//question-classify.controller';
|
||||
import { KnowledgePoint } from 'src/assessment-evaluation/entities/entities-question-manager/knowledge-point.entity';
|
||||
import { QuestionType } from 'src/assessment-evaluation/entities/entities-question-manager/question-type.entity';
|
||||
import { QuestionDifficultyLevel } from 'src/assessment-evaluation/entities/entities-question-manager/question-difficulty-level.entity';
|
||||
import { Question } from 'src/assessment-evaluation/entities/entities-question-manager/question.entity';
|
||||
import { KnowledgePointService } from 'src/assessment-evaluation/service/service-question-manager/knowledge-point.service';
|
||||
import { KnowledgePointController } from 'src/assessment-evaluation/controller/question-manager//knowledge-point.controller';
|
||||
import { QuestionTypeController } from 'src/assessment-evaluation/controller/question-manager/question-type.controller';
|
||||
import { QuestionDifficultyLevelController } from 'src/assessment-evaluation/controller/question-manager//question-difficulty-level.controller';
|
||||
import { QuestionController } from 'src/assessment-evaluation/controller/question-manager//question.controller';
|
||||
import { QuestionTypeService } from 'src/assessment-evaluation/service/service-question-manager/question-type.service';
|
||||
import { QuestionClassifyService } from 'src/assessment-evaluation/service/service-question-manager/question-classify.service';
|
||||
import { QuestionDifficultyLevelService } from 'src/assessment-evaluation/service/service-question-manager/question-difficulty-level.service';
|
||||
import { QuestionService } from 'src/assessment-evaluation/service/service-question-manager/question.service';
|
||||
|
||||
export default {
|
||||
entitis: [
|
||||
QuestionClassify,
|
||||
Question,
|
||||
KnowledgePoint,
|
||||
QuestionType,
|
||||
QuestionDifficultyLevel,
|
||||
QuestionDifficultyLevel,
|
||||
],
|
||||
controllers: [
|
||||
QuestionController,
|
||||
QuestionClassifyController,
|
||||
KnowledgePointController,
|
||||
QuestionTypeController,
|
||||
QuestionDifficultyLevelController,
|
||||
],
|
||||
services: [
|
||||
QuestionClassifyService,
|
||||
KnowledgePointService,
|
||||
QuestionTypeService,
|
||||
QuestionDifficultyLevelService,
|
||||
QuestionService,
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { CreateAssessmentEvaluationDto } from '../dto/create-assessment-evaluation.dto';
|
||||
import { UpdateAssessmentEvaluationDto } from '../dto/update-assessment-evaluation.dto';
|
||||
import { QuestionDifficultyLevel } from 'src/assessment-evaluation/entities/entities-question-manager/question-difficulty-level.entity';
|
||||
import { QuestionType } from 'src/assessment-evaluation/entities/entities-question-manager/question-type.entity';
|
||||
|
||||
@Injectable()
|
||||
export class AssessmentEvaluationService {
|
||||
constructor(
|
||||
@InjectRepository(QuestionType)
|
||||
private questionTypeRepository: Repository<QuestionType>,
|
||||
@InjectRepository(QuestionDifficultyLevel)
|
||||
private questionDifficultyLevelRepository: Repository<QuestionDifficultyLevel>,
|
||||
) {}
|
||||
create(createAssessmentEvaluationDto: CreateAssessmentEvaluationDto) {
|
||||
return 'This action adds a new assessmentEvaluation';
|
||||
}
|
||||
|
||||
findAll() {
|
||||
return `This action returns all assessmentEvaluation`;
|
||||
}
|
||||
|
||||
findOne(id: number) {
|
||||
return `This action returns a #${id} assessmentEvaluation`;
|
||||
}
|
||||
|
||||
update(
|
||||
id: number,
|
||||
updateAssessmentEvaluationDto: UpdateAssessmentEvaluationDto,
|
||||
) {
|
||||
return `This action updates a #${id} assessmentEvaluation`;
|
||||
}
|
||||
|
||||
remove(id: number) {
|
||||
return `This action removes a #${id} assessmentEvaluation`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { CreateExamClassifyDto } from 'src/assessment-evaluation/dto/dtos-exam-manager/exam-classify/create-exam-classify.dto';
|
||||
import { UpdateExamClassifyDto } from 'src/assessment-evaluation/dto/dtos-exam-manager/exam-classify/update-exam-classify.dto';
|
||||
import { ExamClassify } from 'src/assessment-evaluation/entities/entities-exam-manager/exam-classify.entity';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
@Injectable()
|
||||
export class ExamClassifyService {
|
||||
constructor(
|
||||
@InjectRepository(ExamClassify)
|
||||
private ExamClassifyRepository: Repository<ExamClassify>,
|
||||
) {}
|
||||
/*
|
||||
创建考试分类
|
||||
*/
|
||||
async create(CreateExamClassifyDto: CreateExamClassifyDto) {
|
||||
await this.ExamClassifyRepository.insert(CreateExamClassifyDto);
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
查找所有分类
|
||||
*/
|
||||
async findAll() {
|
||||
return this.ExamClassifyRepository.find({
|
||||
select: ['id', 'name', 'pid', 'level'],
|
||||
where: { delFlag: 0 },
|
||||
});
|
||||
}
|
||||
/*
|
||||
更新分类
|
||||
*/
|
||||
async update(id: number, UpdateExamClassifyDto: UpdateExamClassifyDto) {
|
||||
return this.ExamClassifyRepository.update(id, UpdateExamClassifyDto);
|
||||
}
|
||||
/*
|
||||
删除父类型同时删除子类型
|
||||
*/
|
||||
async remove(id: number) {
|
||||
if (isNaN(+id) || !id) throw '缺少id';
|
||||
await this.ExamClassifyRepository.update(id, { delFlag: 1 });
|
||||
const children = await this.ExamClassifyRepository.find({
|
||||
where: { pid: id },
|
||||
});
|
||||
for (let index = 0; index < children.length; index++) {
|
||||
const element = children[index];
|
||||
await this.remove(element.id);
|
||||
}
|
||||
// await this.ExamPaperClassifyRepository.update(item.id, { delFlag: 1 });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { ExamQuestionsResult } from 'src/assessment-evaluation/entities/entities-exam-manager/exam-questions-result.entity';
|
||||
import { TokenDataEntity } from 'src/common/entities/token-data.entity';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
@Injectable()
|
||||
export class ExamQuestionsResultService {
|
||||
constructor(
|
||||
@InjectRepository(ExamQuestionsResult)
|
||||
private ExamQuestionsResultRepository: Repository<ExamQuestionsResult>,
|
||||
) {}
|
||||
|
||||
/*
|
||||
创建试题答卷答案
|
||||
*/
|
||||
async create(CreateExamQuestionsResultDto: any) {
|
||||
await this.ExamQuestionsResultRepository.insert(
|
||||
CreateExamQuestionsResultDto,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
获取所有试题答案
|
||||
*/
|
||||
async findAll() {
|
||||
return this.ExamQuestionsResultRepository.find({
|
||||
where: { delFlag: 0 },
|
||||
});
|
||||
}
|
||||
/*
|
||||
更新试题答案
|
||||
*/
|
||||
async update(id: number, UpdateExamQuestionsResultDto: any) {
|
||||
return this.ExamQuestionsResultRepository.update(
|
||||
id,
|
||||
UpdateExamQuestionsResultDto,
|
||||
);
|
||||
}
|
||||
/*
|
||||
删除父类型同时删除子类型
|
||||
*/
|
||||
async removeAllByHistoryId(id: number) {
|
||||
const rows = await this.ExamQuestionsResultRepository.query(
|
||||
`delete from exam_questions_result where del_flag=0 and history_id=${+id}`,
|
||||
);
|
||||
return rows;
|
||||
}
|
||||
/*
|
||||
根据ID删除试题答案
|
||||
*/
|
||||
async remove(id: number) {
|
||||
await this.ExamQuestionsResultRepository.update(id, { delFlag: 1 });
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
根据试题分类ID获取所有错题
|
||||
*/
|
||||
async getAllMistakeQuestionsByClassifyId(
|
||||
classifyId: number,
|
||||
token: TokenDataEntity,
|
||||
showUserAnswer: number,
|
||||
) {
|
||||
const { userId } = token;
|
||||
// eqr.user_answer as userAnswer,
|
||||
let sql = `
|
||||
select
|
||||
eqr.creator c,
|
||||
eqr.question_id as id,
|
||||
eqr.id as resultId,
|
||||
q.type as typeId,
|
||||
q.classify_id as classifyId,
|
||||
qt.name as type,
|
||||
qt.element as element,
|
||||
q.title as title,
|
||||
q.options as options,
|
||||
q.answer as answer,
|
||||
q.analysis as analysis,
|
||||
eqr.comment as teacherComment,`;
|
||||
if (showUserAnswer == 1) {
|
||||
sql += 'eqr.user_answer as userAnswer,';
|
||||
}
|
||||
sql += `
|
||||
eqr.question_score as score,
|
||||
eqr.score as userScore,
|
||||
q.knowledge_id as knowledgeId,
|
||||
q.difficulty_level as difficultyLevelId
|
||||
from exam_questions_result eqr
|
||||
-- 按试题去重
|
||||
inner join (
|
||||
select max(eqr.id) as rid from exam_questions_result eqr
|
||||
group by eqr.question_id,eqr.creator
|
||||
) hidt on eqr.id = hidt.rid
|
||||
left join question q on q.id= eqr.question_id
|
||||
left join question_type qt on q.type=qt.id
|
||||
left join online_exam_history oeh on oeh.id=eqr.history_id
|
||||
left join exam e on oeh.exam_id = e.id
|
||||
-- 检测所有试题对应的考试考了多少次了
|
||||
left join (
|
||||
select eqr.question_id qid,group_concat(oeh.id) oehids ,count(oeh.id) count,soe.user_id uid
|
||||
from exam_questions_result eqr
|
||||
left join online_exam_history oeh on oeh.id = eqr.history_id
|
||||
left join student_online_exam soe on soe.id = oeh.online_exam_id
|
||||
group by eqr.question_id,oeh.online_exam_id
|
||||
) countTable on countTable.qid = q.id and countTable.uid = eqr.creator
|
||||
where
|
||||
eqr.del_flag <=0
|
||||
and q.del_flag <= 0
|
||||
and eqr.mistake_score <eqr.question_score
|
||||
and eqr.score < eqr.question_score
|
||||
-- 考试结束判断,考试时间过了或考试次数用尽 考试次数小于允许考试的次数则显示
|
||||
and (e.exam_end_time <now() or e.exam_times<= countTable.count)
|
||||
and eqr.creator = ?
|
||||
and q.classify_id = ?
|
||||
order by q.type,eqr.question_id,eqr.id `;
|
||||
return await this.ExamQuestionsResultRepository.query(sql, [
|
||||
userId,
|
||||
classifyId,
|
||||
]);
|
||||
}
|
||||
async updateMiskateScore(id: number) {
|
||||
this.ExamQuestionsResultRepository.query(
|
||||
`update exam_questions_result eqr set eqr.mistake_score = eqr.question_score where eqr.id =?`,
|
||||
[+id],
|
||||
);
|
||||
}
|
||||
async getLastOnlineExamHistoryInfoByQuestionClassifyId(token, classify) {
|
||||
let sql = `select oeh.id,oeh.online_exam_id,oeh.exam_id ,oeh.exam_paper_id from online_exam_history oeh
|
||||
inner join (
|
||||
select qfp.id,qfp.exam_paper_id from questions_for_paper qfp inner join (
|
||||
select eqr.question_id as qid,
|
||||
q.classify_id as classifyId,
|
||||
qc.name as classifyName
|
||||
from (
|
||||
select eqr.question_id from exam_questions_result eqr
|
||||
where eqr.score<eqr.question_score
|
||||
and eqr.mistake_score <eqr.question_score
|
||||
and eqr.creator='${token.userId}'
|
||||
group by eqr.question_id
|
||||
order by eqr.create_time desc
|
||||
) eqr
|
||||
left join question q on q.id = eqr.question_id
|
||||
left join question_classify qc on qc.id = q.classify_id
|
||||
where q.classify_id = ${classify}
|
||||
group by q.classify_id
|
||||
|
||||
) hq on hq.qid=qfp.question_id
|
||||
) qfphistory on qfphistory.exam_paper_id=oeh.exam_paper_id
|
||||
where oeh.creator = '${token.userId}' order by oeh.id desc limit 1`;
|
||||
sql += '';
|
||||
return this.ExamQuestionsResultRepository.query(sql);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Like, Repository } from 'typeorm';
|
||||
import { Exam } from 'src/assessment-evaluation/entities/entities-exam-manager/exam.entity';
|
||||
import { UpdateExamDto } from 'src/assessment-evaluation/dto/dtos-exam-manager/exam/update-exam.dto';
|
||||
import { PagingExamDto } from 'src/assessment-evaluation/dto/dtos-exam-manager/exam/paging-exam.dto';
|
||||
import { CreateExamDto } from 'src/assessment-evaluation/dto/dtos-exam-manager/exam/create-exam.dto';
|
||||
import { ExamPaper } from 'src/assessment-evaluation/entities/entities-exam-paper-manager/exam-paper.entity';
|
||||
import { User } from 'src/system/entities/user.entity';
|
||||
// import { ExamPaperClassify } from 'src/assessment-evaluation/entities/entities-exam-paper-manager/exam-paper-classify.entity';
|
||||
|
||||
@Injectable()
|
||||
export class ExamService {
|
||||
constructor(
|
||||
@InjectRepository(Exam)
|
||||
private ExamRep: Repository<Exam>, // @InjectRepository(ExamPaperClassify) // private ExamPaperClassifyRep: Repository<ExamPaperClassify>,
|
||||
@InjectRepository(ExamPaper)
|
||||
private ExamPaperRep: Repository<ExamPaper>, // @InjectRepository(ExamPaperClassify) // private ExamPaperClassifyRep: Repository<ExamPaperClassify>,
|
||||
) {}
|
||||
|
||||
/*
|
||||
创建试卷
|
||||
*/
|
||||
async create(CreateExamPaperDto: CreateExamDto) {
|
||||
// const hasDifficultyLevel = await this.ExamPaperClassifyRep.findOneBy({
|
||||
// delFlag: 0,
|
||||
// id: createQuestionDto.difficultyLevel,
|
||||
// });
|
||||
// if (!hasDifficultyLevel) {
|
||||
// throw '[classifyId]字段 - 类型不存在';
|
||||
// }
|
||||
|
||||
return await this.ExamRep.save(CreateExamPaperDto);
|
||||
}
|
||||
|
||||
/*
|
||||
根据ID查找试卷
|
||||
*/
|
||||
async findOne(id: number) {
|
||||
const examInfo = await this.ExamRep.createQueryBuilder('exam')
|
||||
.leftJoinAndMapOne(
|
||||
'exam.userinfo',
|
||||
User,
|
||||
'user',
|
||||
'user.id=exam.examinerId',
|
||||
)
|
||||
.andWhere({ id })
|
||||
.getOne();
|
||||
|
||||
const paperInExam = await this.ExamPaperRep.findOneBy({
|
||||
id: examInfo.examPaperId,
|
||||
});
|
||||
return { examInfo, paperInExam };
|
||||
}
|
||||
/*
|
||||
分页查询考试信息
|
||||
*/
|
||||
async paging(pagingInfo: PagingExamDto, token: any) {
|
||||
const { currentPage, pageSize, classifyId, title } = pagingInfo;
|
||||
type PagingParamsMap = {
|
||||
delFlag: number;
|
||||
classifyId: number | null;
|
||||
title: any | null;
|
||||
type: number | null;
|
||||
difficultyLevel: number | null;
|
||||
creator: string | null;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
let whereMap: PagingParamsMap = {
|
||||
delFlag: 0,
|
||||
classifyId: null,
|
||||
title: null,
|
||||
type: null,
|
||||
difficultyLevel: null,
|
||||
creator: null,
|
||||
};
|
||||
// console.log(pagingInfo, whereMap);
|
||||
try {
|
||||
let sql = `select
|
||||
e.id as id,
|
||||
e.title as title,
|
||||
e.classify_id as classifyId,
|
||||
e.exam_desc as examDesc,
|
||||
e.exam_duration as examDuration,
|
||||
e.exam_times as examTimes,
|
||||
e.pass_percent as passPercent,
|
||||
e.questions_is_random_sort as questionsIsRandomSort,
|
||||
e.options_is_random_sort as optionsIsRandomSort,
|
||||
e.success_tips as successTips,
|
||||
e.failed_tips as failedTips,
|
||||
e.waiting_tips as waitingTips,
|
||||
e.exam_paper_id as examPaperId,
|
||||
e.exam_mode as examMode,
|
||||
e.total_score as totalScore,
|
||||
e.grade_paper_mode as gradePaperMode,
|
||||
e.examiner_id as examinerId,
|
||||
e.join_mode as joinMode,
|
||||
e.join_password as joinPassword,
|
||||
ec.name as classify,
|
||||
ep.title as examPaperTitle,
|
||||
date_format(e.exam_start_time,'%Y-%m-%d %H:%i:%S') as examStartTime ,
|
||||
date_format(e.exam_end_time ,'%Y-%m-%d %H:%i:%S') as examEndTime ,
|
||||
(case
|
||||
when now()>=e.exam_start_time and now()<=e.exam_end_time
|
||||
then 1 when now()<e.exam_start_time then 0
|
||||
when now()>e.exam_end_time then 2
|
||||
end) as status,
|
||||
u.name as creator
|
||||
from
|
||||
exam e
|
||||
left join exam_classify ec on
|
||||
ec.id = e.classify_id
|
||||
left join exam_paper ep on ep.id=e.exam_paper_id left join user u on u.id=e.creator
|
||||
where
|
||||
e.del_flag = 0 `;
|
||||
if (classifyId) {
|
||||
sql += ` AND e.classify_id = ${classifyId}`;
|
||||
whereMap.classifyId = classifyId;
|
||||
}
|
||||
if (title) {
|
||||
sql += ` AND e.title LIKE '%${title}%'`;
|
||||
whereMap.title = Like(`%${title}%`);
|
||||
}
|
||||
if (token?.userId && token?.roleId !== 1) {
|
||||
sql += ` AND e.creator ='${token.userId}'`;
|
||||
whereMap.creator = token.userId;
|
||||
}
|
||||
sql += `
|
||||
order by
|
||||
e.id desc limit ?,?`;
|
||||
const getPagingQuestions = await this.ExamRep.query(sql, [
|
||||
+(currentPage - 1) * pageSize,
|
||||
+pageSize,
|
||||
]);
|
||||
// console.log(sql);
|
||||
const total = await this.ExamRep.countBy(whereMap);
|
||||
return { total, currentPage, pageSize, data: getPagingQuestions };
|
||||
} catch (err) {
|
||||
throw '分页查询失败,错误详情:' + err;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
更新某个考试
|
||||
*/
|
||||
async update(id: number, UpdateExamPaperDto: UpdateExamDto) {
|
||||
return this.ExamRep.update(id, UpdateExamPaperDto);
|
||||
}
|
||||
|
||||
/*
|
||||
更新是否匿名
|
||||
*/
|
||||
async updateIsAnonymous(id: number, isAnonymous: number) {
|
||||
return this.ExamRep.update(id, { isAnonymous: isAnonymous });
|
||||
}
|
||||
|
||||
/*
|
||||
删除某个考试
|
||||
*/
|
||||
async remove(id: number) {
|
||||
await this.ExamRep.update(id, { delFlag: 1 });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Exam } from 'src/assessment-evaluation/entities/entities-exam-manager/exam.entity';
|
||||
import { OnlineExamHistory } from 'src/assessment-evaluation/entities/entities-exam-manager/online-exam-history.entity';
|
||||
import { ExamPaper } from 'src/assessment-evaluation/entities/entities-exam-paper-manager/exam-paper.entity';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
@Injectable()
|
||||
export class OnlineExamHistoryService {
|
||||
constructor(
|
||||
@InjectRepository(OnlineExamHistory)
|
||||
private OnlineExamHistory: Repository<OnlineExamHistory>,
|
||||
@InjectRepository(Exam)
|
||||
private ExamRep: Repository<Exam>,
|
||||
@InjectRepository(ExamPaper)
|
||||
private ExamPaper: Repository<ExamPaper>,
|
||||
) {}
|
||||
|
||||
/*
|
||||
使用考试历史ID查找所有考试结果
|
||||
*/
|
||||
async findAllQuestionResultByHistoryId(id) {
|
||||
const onlineExamInfo = await this.OnlineExamHistory.findOneBy({ id });
|
||||
if (!onlineExamInfo) {
|
||||
throw '学员考试不存在';
|
||||
}
|
||||
const exam = await this.ExamRep.findOneBy({ id: onlineExamInfo.examId });
|
||||
if (!exam) {
|
||||
throw '考试不存在';
|
||||
}
|
||||
const { examPaperId, examId, onlineExamId } = onlineExamInfo;
|
||||
let sql = `select
|
||||
q.id as id,
|
||||
q.type as typeId,
|
||||
q.difficulty_level as difficultyLevelId,
|
||||
qdl.name as difficultyLevel,
|
||||
q.classify_id as classifyId,
|
||||
qc.name as classify,
|
||||
qt.name as type,
|
||||
qt.element as element,
|
||||
q.knowledge_id as knowledgeId,
|
||||
q.title as title,
|
||||
q.options as options,
|
||||
q.answer as answer ,
|
||||
q.analysis as analysis ,
|
||||
eqr.question_score as score,
|
||||
eqr.score as userScore,
|
||||
eqr.user_answer as userAnswer,
|
||||
eqr.comment as teacherComment,
|
||||
eqr.id as resultId
|
||||
from question q
|
||||
left join question_type qt on
|
||||
qt.id = q.type
|
||||
left join question_classify qc on
|
||||
qc.id = q.classify_id
|
||||
left join question_difficulty_level qdl on
|
||||
q.difficulty_level = qdl.id
|
||||
inner join questions_for_paper qfp on qfp.del_flag = 0 and qfp.exam_paper_id = ${examPaperId} and q.id = qfp.question_id
|
||||
inner join exam_questions_result eqr on eqr.question_id = q.id and eqr.history_id = ${id}
|
||||
order by q.type asc `;
|
||||
|
||||
sql += ``;
|
||||
const questions = await this.OnlineExamHistory.query(sql);
|
||||
return { onlineExamInfo, exam, questions };
|
||||
}
|
||||
async create(CreateOnlineExamHistoryDto: any) {
|
||||
return await this.OnlineExamHistory.save(CreateOnlineExamHistoryDto);
|
||||
}
|
||||
/**
|
||||
* 根据考试记录返回考试信息和试题
|
||||
* @param id 考试记录ID
|
||||
* @param isPracticeExam 是否为模拟考试,0否1是,模拟考试将会返回答案和解析
|
||||
*/
|
||||
async findAllQuestionsAndExamInfo(id: number, isPracticeExam = 0) {
|
||||
const onlineExamInfo = await this.OnlineExamHistory.findOneBy({ id });
|
||||
if (!onlineExamInfo) {
|
||||
throw '学员考试不存在';
|
||||
}
|
||||
const exam = await this.ExamRep.findOneBy({ id: onlineExamInfo.examId });
|
||||
if (!exam) {
|
||||
throw '考试不存在';
|
||||
}
|
||||
if (new Date().getTime() >= onlineExamInfo.endTimestamp) {
|
||||
throw '已超过规定答题时间';
|
||||
}
|
||||
|
||||
const { examPaperId, examId, onlineExamId } = onlineExamInfo;
|
||||
const paper = await this.ExamPaper.findOneBy({ id: examPaperId });
|
||||
let sql = `select
|
||||
q.id as id,
|
||||
q.type as typeId,
|
||||
q.difficulty_level as difficultyLevelId,
|
||||
qdl.name as difficultyLevel,
|
||||
q.classify_id as classifyId,
|
||||
qc.name as classify,
|
||||
qt.name as type,
|
||||
qt.element as element,
|
||||
q.knowledge_id as knowledgeId,
|
||||
q.title as title,
|
||||
`;
|
||||
if (isPracticeExam) {
|
||||
sql += `
|
||||
q.answer as answer ,
|
||||
q.analysis as analysis ,
|
||||
`;
|
||||
}
|
||||
sql += `
|
||||
q.options as options,
|
||||
qfp.score as score
|
||||
from question q
|
||||
left join question_type qt on
|
||||
qt.id = q.type
|
||||
left join question_classify qc on
|
||||
qc.id = q.classify_id
|
||||
left join question_difficulty_level qdl on
|
||||
q.difficulty_level = qdl.id
|
||||
inner join questions_for_paper qfp on qfp.exam_paper_id = ${examPaperId} and qfp.del_flag = 0 and q.id = qfp.question_id order by q.type asc `;
|
||||
const questions = await this.OnlineExamHistory.query(sql);
|
||||
return {
|
||||
onlineExamInfo,
|
||||
exam,
|
||||
questions,
|
||||
paper,
|
||||
now: new Date().getTime(),
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
查找所有考试历史记录
|
||||
*/
|
||||
async findAll() {
|
||||
return this.OnlineExamHistory.find({
|
||||
where: { delFlag: 0 },
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
根据考试历史ID获取详情
|
||||
*/
|
||||
async findOneById(historyId) {
|
||||
return this.OnlineExamHistory.findOneBy({
|
||||
id: historyId,
|
||||
});
|
||||
}
|
||||
async update(id: number, UpdateOnlineExamHistoryDto: any) {
|
||||
return this.OnlineExamHistory.update(id, UpdateOnlineExamHistoryDto);
|
||||
}
|
||||
// 删除父类型同时删除子类型
|
||||
async remove(id: number) {
|
||||
if (isNaN(+id) || !id) throw '缺少id';
|
||||
await this.OnlineExamHistory.update(id, { delFlag: 1 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,400 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { CreateStudentOnlineExamDto } from 'src/assessment-evaluation/dto/dtos-exam-manager/student-online-exam/create-student-online-exam.dto';
|
||||
import { UpdateStudentOnlineExamDto } from 'src/assessment-evaluation/dto/dtos-exam-manager/student-online-exam/update-student-online-exam.dto';
|
||||
import { Repository } from 'typeorm';
|
||||
import { StudentOnlineExam } from 'src/assessment-evaluation/entities/entities-exam-manager/student-online-exam.entity';
|
||||
import { PagingStudentOnlineExamDto } from 'src/assessment-evaluation/dto/dtos-exam-manager/student-online-exam/paging-student-online-exam.dto';
|
||||
import { TokenDataEntity } from 'src/common/entities/token-data.entity';
|
||||
|
||||
@Injectable()
|
||||
export class StudentOnlineExamService {
|
||||
constructor(
|
||||
@InjectRepository(StudentOnlineExam)
|
||||
private StudentOnlineExam: Repository<StudentOnlineExam>,
|
||||
) {}
|
||||
/*
|
||||
分发考试给一个学生
|
||||
*/
|
||||
async createOne(CreateStudentOnlineExamDto: CreateStudentOnlineExamDto) {
|
||||
return await this.StudentOnlineExam.save(CreateStudentOnlineExamDto);
|
||||
}
|
||||
/*
|
||||
批量分发考试给学生
|
||||
*/
|
||||
async create(CreateStudentOnlineExamDto: CreateStudentOnlineExamDto | any[]) {
|
||||
await this.StudentOnlineExam.insert(CreateStudentOnlineExamDto);
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
获取考试最后一次历史记录
|
||||
*/
|
||||
async findLastExam(id) {
|
||||
const lastArr = await this.StudentOnlineExam.query(
|
||||
`select * from online_exam_history oeh where online_exam_id = ${id} order by oeh.create_time desc limit 1`,
|
||||
);
|
||||
return lastArr?.length > 0 ? lastArr[0] : null;
|
||||
}
|
||||
/*
|
||||
查询某个考试历史
|
||||
*/
|
||||
async findOneById(id, checkDel = true) {
|
||||
const where: any = {
|
||||
id,
|
||||
};
|
||||
if (checkDel) {
|
||||
where.delFlag = 0;
|
||||
}
|
||||
const item = await this.StudentOnlineExam.findOneBy(where);
|
||||
return item;
|
||||
}
|
||||
|
||||
/*
|
||||
查找所有考试历史
|
||||
*/
|
||||
async findAll() {
|
||||
return this.StudentOnlineExam.find({
|
||||
where: { delFlag: 0 },
|
||||
});
|
||||
}
|
||||
/*
|
||||
根据考试ID获取考试历史
|
||||
*/
|
||||
async findAllByExamId(examId) {
|
||||
return this.StudentOnlineExam.query(
|
||||
`select soe.user_id as id,u.name as name,u.org_id as orgId from student_online_exam soe left join user u on u.id=soe.user_id where soe.exam_id = ${examId} and soe.del_flag = 0`,
|
||||
);
|
||||
}
|
||||
/**
|
||||
* 根据用户和在线考试ID查询用户已考试次数
|
||||
* @param userId 用户ID
|
||||
* @param onlineExamId 在线考试ID
|
||||
* @returns {}
|
||||
*/
|
||||
async checkExamTimes(userId: string, onlineExamId: number) {
|
||||
return this.StudentOnlineExam.query(
|
||||
`select count(1) as times,creator as userId,online_exam_id from online_exam_history oeh where creator = '${userId}' and online_exam_id =${onlineExamId} group by creator,online_exam_id`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询人工判卷所有考试
|
||||
* @param pagingInfo 分页信息
|
||||
* @param token 用户token信息需包含userId
|
||||
* @returns
|
||||
*/
|
||||
async pagingGradeDetails(
|
||||
examId: number,
|
||||
pagingInfo: PagingStudentOnlineExamDto,
|
||||
) {
|
||||
const { currentPage, pageSize, title } = pagingInfo;
|
||||
|
||||
// console.log(pagingInfo, whereMap);
|
||||
try {
|
||||
let sql = `select oeh.exam_id as examId, oeh.exam_paper_id as paperId,
|
||||
oeh.creator as userId,max(oeh.id) as historyId ,
|
||||
date_format(max(oeh.create_time), '%Y-%m-%d %H:%i:%S') as createTime ,
|
||||
FROM_UNIXTIME(max(oeh.report_time)/1000,'%Y-%m-%d %H:%i:%s') as reportTime,
|
||||
(max(oeh.report_time) - unix_timestamp(max(oeh.create_time))*1000) as myDuration,
|
||||
max(oeh.report_time) as reportTimestamp,
|
||||
oeh.is_grade_paper as isGradePaper,
|
||||
scoreSum.userScore as score,
|
||||
e.pass_percent as passPercent,
|
||||
e.total_score as totalScore,
|
||||
round(e.pass_percent *e.total_score /100) as passScore,
|
||||
u.username as username,
|
||||
u.name as realname,
|
||||
e.exam_end_time <now() as isOver
|
||||
from online_exam_history oeh
|
||||
inner join (
|
||||
select max(id) as id from online_exam_history oeh2 group by oeh2.exam_id ,oeh2.creator
|
||||
) oehk on oehk.id = oeh.id
|
||||
left join exam e on e.id = oeh.exam_id
|
||||
left join (
|
||||
select sum(eqr.score) as userScore,eqr.history_id as historyId ,count(1) as reportCount
|
||||
from exam_questions_result eqr where eqr.del_flag = 0 group by eqr.history_id
|
||||
) as scoreSum on scoreSum.historyId=oeh.id
|
||||
left join user u on u.id = oeh.creator
|
||||
where e.del_flag =0 and scoreSum.reportCount>0 and oeh.exam_id =${examId}
|
||||
`;
|
||||
// and e.grade_paper_mode=1
|
||||
let sqlCount = `
|
||||
select count(1) as count from (select max(oeh.id) from online_exam_history oeh
|
||||
left join exam e on e.id = oeh.exam_id
|
||||
left join user u on u.id = oeh.creator
|
||||
where e.del_flag =0 and oeh.exam_id =${examId}
|
||||
`;
|
||||
// and e.grade_paper_mode=1
|
||||
if (title) {
|
||||
sql += ` AND u.username LIKE '%${title}%'`;
|
||||
sqlCount += ` AND u.username LIKE '%${title}%'`;
|
||||
}
|
||||
|
||||
sqlCount += ` group by oeh.exam_id ,oeh.creator) a`;
|
||||
|
||||
let total = await this.StudentOnlineExam.query(sqlCount);
|
||||
total = total.length > 0 ? +total[0].count : 0;
|
||||
if (!currentPage) {
|
||||
sql += ` group by oeh.exam_id ,oeh.creator order by oeh.create_time desc limit 0,${total}`;
|
||||
} else {
|
||||
sql += ` group by oeh.exam_id ,oeh.creator order by oeh.create_time desc limit ?,?`;
|
||||
}
|
||||
const getPagingQuestions = await this.StudentOnlineExam.query(sql, [
|
||||
+(currentPage - 1) * pageSize,
|
||||
+pageSize,
|
||||
]);
|
||||
// console.log(sql);
|
||||
|
||||
return { total, currentPage, pageSize, data: getPagingQuestions };
|
||||
} catch (err) {
|
||||
throw '分页查询失败,错误详情:' + err;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 分页查询人工判卷所有考试
|
||||
* @param pagingInfo 分页信息
|
||||
* @param token 用户token信息需包含userId
|
||||
* @returns
|
||||
*/
|
||||
async pagingGrade(
|
||||
pagingInfo: PagingStudentOnlineExamDto,
|
||||
token: TokenDataEntity,
|
||||
) {
|
||||
const { currentPage, pageSize, title } = pagingInfo;
|
||||
const { userId, roleId } = token;
|
||||
|
||||
// console.log(pagingInfo, whereMap);
|
||||
try {
|
||||
let sql = `select
|
||||
count(1) as shouldCount,
|
||||
ifnull(finishCount.count,0) as finishCount,
|
||||
ifnull(gradedCount.count,0) as gradedCount,
|
||||
ifnull(finishCount.count,0) - ifnull(gradedCount.count,0) as ungradedCount,
|
||||
soe.exam_id as examId,
|
||||
e.grade_paper_mode as gradeMode,
|
||||
soe.create_time as createTime,
|
||||
e.title as title,
|
||||
date_format(e.exam_start_time,'%Y-%m-%d %H:%i:%S') as examStartTime,
|
||||
date_format(e.exam_end_time ,'%Y-%m-%d %H:%i:%S') as examEndTime,
|
||||
e.total_score as totalScore,
|
||||
e.is_anonymous as isAnonymous,
|
||||
round(e.total_score*e.pass_percent /100) as passScore
|
||||
from student_online_exam soe
|
||||
left join exam e on e.id = soe.exam_id
|
||||
left join (
|
||||
select count(oehx.examId) as count,oehx.examId
|
||||
from (
|
||||
select oeh.creator as userId,oeh.exam_id as examId ,max(oeh.id) oid,min(oeh.is_grade_paper) as gra
|
||||
from online_exam_history oeh
|
||||
inner join ( select max(oeh2.id) ids from online_exam_history oeh2 group by oeh2.creator , oeh2.exam_id) c on c.ids = oeh.id
|
||||
group by oeh.creator , oeh.exam_id
|
||||
) oehx
|
||||
where (select count(1) from exam_questions_result eqr where eqr.history_id = oehx.oid)>0
|
||||
group by oehx.examId
|
||||
) as finishCount on finishCount.examId=soe.exam_id
|
||||
left join (
|
||||
select count(1) as count,oehx.examId
|
||||
from (
|
||||
select oeh.creator as userId,oeh.exam_id as examId ,min(oeh.is_grade_paper) as gra
|
||||
from online_exam_history oeh
|
||||
inner join ( select max(oeh2.id) ids from online_exam_history oeh2 group by oeh2.creator , oeh2.exam_id) c on c.ids = oeh.id
|
||||
group by oeh.creator , oeh.exam_id
|
||||
) oehx
|
||||
where oehx.gra>0
|
||||
group by oehx.examId
|
||||
) as gradedCount on gradedCount.examId=soe.exam_id
|
||||
where e.del_flag=0`; // and e.grade_paper_mode =1
|
||||
let sqlCount = `
|
||||
select count(1) as count from (select e.id from exam e where e.del_flag = 0 `; //and e.grade_paper_mode =1
|
||||
if (title) {
|
||||
sql += ` AND e.title LIKE '%${title}%'`;
|
||||
sqlCount += ` AND e.title LIKE '%${title}%'`;
|
||||
}
|
||||
if (userId) {
|
||||
// && roleId == 3
|
||||
sql += ` AND e.examiner_id = '${userId}'`;
|
||||
sqlCount += ` AND e.examiner_id = '${userId}'`;
|
||||
}
|
||||
if (pagingInfo['year']) {
|
||||
sql += ` and date_format(soe.create_time,'%Y') = date_format('${pagingInfo['year']}','%Y') `;
|
||||
sqlCount += ` and date_format(e.create_time,'%Y') = date_format('${pagingInfo['year']}','%Y') `;
|
||||
}
|
||||
sql += ` group by soe.exam_id order by
|
||||
e.create_time desc limit ?,?`;
|
||||
sqlCount += ` order by
|
||||
e.create_time desc ) a`;
|
||||
|
||||
const getPagingQuestions = await this.StudentOnlineExam.query(sql, [
|
||||
+(currentPage - 1) * pageSize,
|
||||
+pageSize,
|
||||
]);
|
||||
// console.log(sql);
|
||||
let total = await this.StudentOnlineExam.query(sqlCount);
|
||||
total = total.length > 0 ? +total[0].count : 0;
|
||||
return { total, currentPage, pageSize, data: getPagingQuestions };
|
||||
} catch (err) {
|
||||
throw '分页查询失败,错误详情:' + err;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
分页查找考试记录
|
||||
*/
|
||||
async paging(pagingInfo: PagingStudentOnlineExamDto, token: TokenDataEntity) {
|
||||
const { currentPage, pageSize, title } = pagingInfo;
|
||||
const { userId, roleId } = token;
|
||||
type PagingParamsMap = {
|
||||
delFlag: number;
|
||||
title: any | null;
|
||||
userId: string | null;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
let whereMap: PagingParamsMap = {
|
||||
delFlag: 0,
|
||||
title: null,
|
||||
userId: null,
|
||||
};
|
||||
// console.log(pagingInfo, whereMap);
|
||||
/**
|
||||
* 别删 - - 有用
|
||||
*/
|
||||
try {
|
||||
// let sql = `select
|
||||
// soe.id as id,
|
||||
// soe.user_id as userId,
|
||||
// e.title as title,
|
||||
// ec.name as classify,
|
||||
// e.exam_desc as examDesc,
|
||||
// e.total_score as totalScore,
|
||||
// e.pass_percent as passPercent,
|
||||
// round(e.pass_percent*e.total_score/100) as successScore,
|
||||
// 0 as myScore,
|
||||
// e.exam_duration as examDuration,
|
||||
// soe.mistakes as mistakes ,
|
||||
// e.exam_times as examTimes,
|
||||
// ifnull(oeh2.times,0) as myTimes,
|
||||
// 0 as myDuration,
|
||||
// date_format(e.exam_start_time,'%Y-%m-%d %H:%i:%S') as examStartTime ,
|
||||
// date_format(e.exam_end_time ,'%Y-%m-%d %H:%i:%S') as examEndTime ,
|
||||
// (case
|
||||
// when now()>=e.exam_start_time and now()<=e.exam_end_time
|
||||
// then 1 when now()<e.exam_start_time then 0
|
||||
// when now()>e.exam_end_time then 2
|
||||
// end) as status
|
||||
// from student_online_exam soe
|
||||
// left join exam e on soe.exam_id = e.id
|
||||
// left join exam_classify ec on ec.id=e.classify_id
|
||||
// left join (select count(1) as times,oeh.creator as userId,oeh.online_exam_id from online_exam_history oeh group by oeh.creator,oeh.online_exam_id ) oeh2
|
||||
// on oeh2.userId = soe.user_id and oeh2.online_exam_id = soe.id
|
||||
// where
|
||||
// soe.del_flag = 0 `;
|
||||
let sql = `
|
||||
select
|
||||
soe.id as id,
|
||||
soe.user_id as userId,
|
||||
e.title as title,
|
||||
ec.name as classify,
|
||||
e.exam_desc as examDesc,
|
||||
e.exam_paper_id as paperId,
|
||||
e.total_score as totalScore,
|
||||
e.pass_percent as passPercent,
|
||||
round(e.pass_percent*e.total_score/100) as successScore,
|
||||
ifnull(scoreTemp.userScore,0) as myScore,
|
||||
e.exam_duration as examDuration,
|
||||
ifnull(mistakTable2.mistakes,0) as mistakmistakeses ,
|
||||
e.exam_times as examTimes,
|
||||
ifnull(oeh2.times,0) as myTimes,
|
||||
ifnull(durationTable.duration,0) as myDuration,
|
||||
date_format(e.exam_start_time,'%Y-%m-%d %H:%i:%S') as examStartTime ,
|
||||
date_format(e.exam_end_time ,'%Y-%m-%d %H:%i:%S') as examEndTime ,
|
||||
(case
|
||||
when now()>=e.exam_start_time and now()<=e.exam_end_time
|
||||
then 1 when now()<e.exam_start_time then 0
|
||||
when now()>e.exam_end_time then 2
|
||||
end) as status
|
||||
from student_online_exam soe
|
||||
left join exam e on soe.exam_id = e.id
|
||||
left join exam_classify ec on ec.id=e.classify_id
|
||||
left join (select count(1) as times,oeh.creator as userId,oeh.online_exam_id
|
||||
from online_exam_history oeh group by oeh.creator,oeh.online_exam_id ) oeh2
|
||||
on oeh2.userId = soe.user_id and oeh2.online_exam_id = soe.id
|
||||
|
||||
left join (select oeh2.online_exam_id as online_exam_id,oeh2.create_time ct ,ifnull(scoreSum.userScore,0)as userScore
|
||||
from online_exam_history oeh2 left join (
|
||||
select sum(eqr.score) as userScore,eqr.history_id as historyId
|
||||
from exam_questions_result eqr where eqr.del_flag = 0 group by eqr.history_id
|
||||
) as scoreSum on
|
||||
scoreSum.historyId = oeh2.id inner join
|
||||
(select max(oehx.create_time) mtime ,oehx.online_exam_id onlineId,max(oehx.id) nid from online_exam_history oehx group by oehx.online_exam_id ) stb
|
||||
on stb.nid = oeh2.id where oeh2.is_grade_paper =1) as scoreTemp on scoreTemp.online_exam_id = soe.id
|
||||
|
||||
left join (select oeh2.online_exam_id as online_exam_id ,ifnull(mistakeTable.mistakes,0)as mistakes
|
||||
from online_exam_history oeh2 left join (
|
||||
select count(1) as mistakes,eqr2.history_id as historyId from exam_questions_result eqr2
|
||||
where eqr2.score< eqr2.question_score and eqr2.del_flag = 0
|
||||
group by eqr2.history_id
|
||||
) as mistakeTable on
|
||||
mistakeTable.historyId = oeh2.id
|
||||
inner join (select max(oehx.create_time) mtime ,oehx.online_exam_id onlineId,max(oehx.id) nid from online_exam_history oehx group by oehx.online_exam_id ) stb
|
||||
on stb.nid = oeh2.id where oeh2.is_grade_paper =1) as mistakTable2 on mistakTable2.online_exam_id = soe.id
|
||||
|
||||
left join (select (oeh.report_time - unix_timestamp(oeh.create_time)*1000)+0 as duration,oeh.online_exam_id from online_exam_history oeh
|
||||
inner join (select max(oehx.create_time) mtime ,oehx.online_exam_id onlineId,max(oehx.id) nid from online_exam_history oehx group by oehx.online_exam_id ) stb
|
||||
on stb.nid = oeh.id) as durationTable on durationTable.online_exam_id = soe.id
|
||||
|
||||
where soe.del_flag = 0 and e.classify_id!=-1 `;
|
||||
let sqlCount = `
|
||||
select count(1) as count from (select
|
||||
soe.id as id,
|
||||
soe.user_id as userId,
|
||||
e.title as title
|
||||
from student_online_exam soe
|
||||
left join exam e on soe.exam_id = e.id
|
||||
where soe.del_flag =0`;
|
||||
if (title) {
|
||||
sql += ` AND e.title LIKE '%${title}%'`;
|
||||
sqlCount += ` AND e.title LIKE '%${title}%'`;
|
||||
}
|
||||
if (userId) {
|
||||
// && roleId == 3
|
||||
sql += ` AND soe.user_id = '${userId}'`;
|
||||
sqlCount += ` AND soe.user_id = '${userId}'`;
|
||||
}
|
||||
sql += `
|
||||
order by
|
||||
soe.id desc limit ?,?`;
|
||||
sqlCount += ` order by
|
||||
soe.id desc) a`;
|
||||
|
||||
const getPagingQuestions = await this.StudentOnlineExam.query(sql, [
|
||||
+(currentPage - 1) * pageSize,
|
||||
+pageSize,
|
||||
]);
|
||||
// console.log(sql);
|
||||
let total = await this.StudentOnlineExam.query(sqlCount);
|
||||
total = total.length > 0 ? +total[0].count : 0;
|
||||
return { total, currentPage, pageSize, data: getPagingQuestions };
|
||||
} catch (err) {
|
||||
throw '分页查询失败,错误详情:' + err;
|
||||
}
|
||||
}
|
||||
async update(
|
||||
id: number,
|
||||
UpdateStudentOnlineExamDto: UpdateStudentOnlineExamDto,
|
||||
) {
|
||||
return this.StudentOnlineExam.update(id, UpdateStudentOnlineExamDto);
|
||||
}
|
||||
/*
|
||||
删除父类型同时删除子类型
|
||||
*/
|
||||
async remove(id: number) {
|
||||
if (isNaN(+id) || !id) throw '缺少id';
|
||||
await this.StudentOnlineExam.update(id, { delFlag: 1 });
|
||||
return true;
|
||||
}
|
||||
async removeByExamId(examid: number) {
|
||||
return await this.StudentOnlineExam.query(`
|
||||
update student_online_exam set del_flag=1 where exam_id=${examid}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { CreateExamPaperClassifyDto } from 'src/assessment-evaluation/dto/dtos-exam-paper-manager/exam-paper-classify/create-exam-paper-classify.dto';
|
||||
import { UpdateExamPaperClassifyDto } from 'src/assessment-evaluation/dto/dtos-exam-paper-manager/exam-paper-classify/update-exam-paper-classify.dto';
|
||||
import { ExamPaperClassify } from 'src/assessment-evaluation/entities/entities-exam-paper-manager/exam-paper-classify.entity';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
@Injectable()
|
||||
export class ExamPaperClassifyService {
|
||||
constructor(
|
||||
@InjectRepository(ExamPaperClassify)
|
||||
private ExamPaperClassifyRepository: Repository<ExamPaperClassify>,
|
||||
) {}
|
||||
/*
|
||||
创建试卷分类
|
||||
*/
|
||||
async create(createExamPaperClassifyDto: CreateExamPaperClassifyDto) {
|
||||
await this.ExamPaperClassifyRepository.insert(createExamPaperClassifyDto);
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
获取所有试卷分类
|
||||
*/
|
||||
async findAll() {
|
||||
return this.ExamPaperClassifyRepository.find({
|
||||
select: ['id', 'name', 'pid', 'level'],
|
||||
where: { delFlag: 0 },
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
更新试卷分类
|
||||
*/
|
||||
async update(
|
||||
id: number,
|
||||
updateExamPaperClassifyDto: UpdateExamPaperClassifyDto,
|
||||
) {
|
||||
return this.ExamPaperClassifyRepository.update(
|
||||
id,
|
||||
updateExamPaperClassifyDto,
|
||||
);
|
||||
}
|
||||
/*
|
||||
删除父类型同时删除子类型
|
||||
*/
|
||||
async remove(id: number) {
|
||||
if (isNaN(+id) || !id) throw '缺少id';
|
||||
await this.ExamPaperClassifyRepository.update(id, { delFlag: 1 });
|
||||
const children = await this.ExamPaperClassifyRepository.find({
|
||||
where: { pid: id },
|
||||
});
|
||||
for (let index = 0; index < children.length; index++) {
|
||||
const element = children[index];
|
||||
await this.remove(element.id);
|
||||
}
|
||||
// await this.ExamPaperClassifyRepository.update(item.id, { delFlag: 1 });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,357 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Like, Repository } from 'typeorm';
|
||||
import { ExamPaper } from 'src/assessment-evaluation/entities/entities-exam-paper-manager/exam-paper.entity';
|
||||
import { PagingExamPaperDto } from 'src/assessment-evaluation/dto/dtos-exam-paper-manager/exam-paper/paging-exam-paper.dto';
|
||||
import { UpdateExamPaperDto } from 'src/assessment-evaluation/dto/dtos-exam-paper-manager/exam-paper/update-exam-paper.dto';
|
||||
import { CreateExamPaperDto } from 'src/assessment-evaluation/dto/dtos-exam-paper-manager/exam-paper/create-exam-paper.dto';
|
||||
import { QuestionsForPaper } from 'src/assessment-evaluation/entities/entities-exam-paper-manager/questions-for-paper.entity';
|
||||
import { TokenDataEntity } from 'src/common/entities/token-data.entity';
|
||||
import { Exam } from 'src/assessment-evaluation/entities/entities-exam-manager/exam.entity';
|
||||
// import { ExamPaperClassify } from 'src/assessment-evaluation/entities/entities-exam-paper-manager/exam-paper-classify.entity';
|
||||
|
||||
@Injectable()
|
||||
export class ExamPaperService {
|
||||
constructor(
|
||||
@InjectRepository(ExamPaper)
|
||||
private ExamPaperRep: Repository<ExamPaper>, // @InjectRepository(ExamPaperClassify) // private ExamPaperClassifyRep: Repository<ExamPaperClassify>,
|
||||
@InjectRepository(QuestionsForPaper)
|
||||
private QuestionsForPaper: Repository<QuestionsForPaper>, // @InjectRepository(ExamPaperClassify) // private ExamPaperClassifyRep: Repository<ExamPaperClassify>,
|
||||
@InjectRepository(Exam)
|
||||
private ExamRep: Repository<Exam>, // @InjectRepository(ExamPaperClassify) // private ExamPaperClassifyRep: Repository<ExamPaperClassify>,
|
||||
) {}
|
||||
|
||||
/*
|
||||
创建试卷
|
||||
*/
|
||||
async create(CreateExamPaperDto: any) {
|
||||
// const hasDifficultyLevel = await this.ExamPaperClassifyRep.findOneBy({
|
||||
// delFlag: 0,
|
||||
// id: createQuestionDto.difficultyLevel,
|
||||
// });
|
||||
// if (!hasDifficultyLevel) {
|
||||
// throw '[classifyId]字段 - 类型不存在';
|
||||
// }
|
||||
|
||||
return await this.ExamPaperRep.save(CreateExamPaperDto);
|
||||
}
|
||||
/*
|
||||
获取某个试卷
|
||||
*/
|
||||
async findOne(id: number) {
|
||||
const paperInfo = await this.ExamPaperRep.findOneBy({ id });
|
||||
let sql = `select
|
||||
q.*,
|
||||
qfp.exam_paper_id as examPaperId,
|
||||
qfp.type as typeId,
|
||||
qt.element as element,
|
||||
qt.name as type,
|
||||
qfp.score as itemScore,
|
||||
qfp.is_fixed as isFixed
|
||||
from
|
||||
questions_for_paper qfp
|
||||
left join question q on
|
||||
q.id = qfp.question_id
|
||||
left join question_type qt on
|
||||
qt.id = qfp.type
|
||||
where
|
||||
qfp.del_flag = 0`;
|
||||
if (paperInfo.id) {
|
||||
sql += ` and qfp.exam_paper_id=${paperInfo.id}`;
|
||||
}
|
||||
const paperQuestionsInfo = await this.QuestionsForPaper.query(sql);
|
||||
const fixdQuestions = paperQuestionsInfo.filter(
|
||||
(item) => item.isFixed == 1,
|
||||
);
|
||||
const randQuestions = paperQuestionsInfo.filter(
|
||||
(item) => item.isFixed == 0,
|
||||
);
|
||||
// paperInfo['questionInfo'] = {
|
||||
// fixdQuestions,
|
||||
// randQuestions,
|
||||
// };
|
||||
const item: CreateExamPaperDto = {
|
||||
...paperInfo,
|
||||
questionInfo: {
|
||||
fixdQuestions,
|
||||
randQuestions,
|
||||
},
|
||||
};
|
||||
return item;
|
||||
}
|
||||
/*
|
||||
分页查询模拟考试
|
||||
*/
|
||||
async pagingSim(
|
||||
pagingInfo: PagingExamPaperDto,
|
||||
token: TokenDataEntity,
|
||||
delFlag = -1,
|
||||
) {
|
||||
const { currentPage, pageSize, classifyId, title, isPractive, status } =
|
||||
pagingInfo;
|
||||
type PagingParamsMap = {
|
||||
delFlag: number;
|
||||
classifyId: number | null;
|
||||
title: any | null;
|
||||
type: number | null;
|
||||
difficultyLevel: number | null;
|
||||
isPracticeExam: number;
|
||||
status: number | null;
|
||||
creator: string | null;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
let whereMap: PagingParamsMap = {
|
||||
delFlag: delFlag,
|
||||
classifyId: null,
|
||||
title: null,
|
||||
type: null,
|
||||
difficultyLevel: null,
|
||||
isPracticeExam: 0,
|
||||
status: null,
|
||||
creator: null,
|
||||
};
|
||||
// console.log(pagingInfo, whereMap);
|
||||
try {
|
||||
let sql = `select
|
||||
ep.id as id,
|
||||
ep.title as title,
|
||||
ep.paper_desc as paperDesc,
|
||||
ep.paper_duration as duration,
|
||||
ep.total_score as totalScore ,
|
||||
ep.pass_percent as passPercent,
|
||||
ep.questions_is_random_sort as questionsIsRandomSort,
|
||||
ep.options_is_random_sort as optionsIsRandomSort,
|
||||
ep.question_count as questionCount,
|
||||
ep.status as status,
|
||||
ep.is_practice_exam as isPracticeExam,
|
||||
users.name as creator ,
|
||||
ep.creator as creatorId,
|
||||
ep.classify_id as classifyId,
|
||||
qc.name as classify,
|
||||
ex.id as exam_id,
|
||||
soe.id as studentOnlineExamId,
|
||||
ex.del_flag as exDel,
|
||||
ifnull(durationTable.duration,0) as myDuration,
|
||||
ifnull(mistakTable2.mistakes,0) as mistakes ,
|
||||
ifnull(scoreTemp.userScore,0) as myScore,
|
||||
date_format(ep.create_time, '%Y-%m-%d %H:%i:%S') as createTime
|
||||
from
|
||||
exam_paper ep
|
||||
left join user users on
|
||||
ep.creator = users.id
|
||||
left join exam_paper_classify qc on
|
||||
ep.classify_id = qc.id
|
||||
left join exam ex on ex.exam_paper_id = ep.id
|
||||
left join student_online_exam soe on soe.exam_id = ex.id
|
||||
|
||||
left join (select oeh2.online_exam_id as online_exam_id,oeh2.create_time ct ,ifnull(scoreSum.userScore,0)as userScore
|
||||
from online_exam_history oeh2 left join (
|
||||
select sum(eqr.score) as userScore,eqr.history_id as historyId
|
||||
from exam_questions_result eqr where eqr.del_flag = 0 group by eqr.history_id
|
||||
) as scoreSum on
|
||||
scoreSum.historyId = oeh2.id inner join
|
||||
(select max(oehx.create_time) mtime ,oehx.online_exam_id onlineId,max(oehx.id) nid from online_exam_history oehx group by oehx.online_exam_id ) stb
|
||||
on stb.nid = oeh2.id where oeh2.is_grade_paper =1) as scoreTemp on scoreTemp.online_exam_id = soe.id
|
||||
|
||||
left join (select oeh2.online_exam_id as online_exam_id ,ifnull(mistakeTable.mistakes,0)as mistakes
|
||||
from online_exam_history oeh2 left join (
|
||||
select count(1) as mistakes,eqr2.history_id as historyId from exam_questions_result eqr2
|
||||
where eqr2.score< eqr2.question_score and eqr2.del_flag = 0
|
||||
group by eqr2.history_id
|
||||
) as mistakeTable on
|
||||
mistakeTable.historyId = oeh2.id
|
||||
inner join (select max(oehx.create_time) mtime ,oehx.online_exam_id onlineId,max(oehx.id) nid from online_exam_history oehx group by oehx.online_exam_id ) stb
|
||||
on stb.nid = oeh2.id where oeh2.is_grade_paper =1) as mistakTable2 on mistakTable2.online_exam_id = soe.id
|
||||
|
||||
left join (select (oeh.report_time - unix_timestamp(oeh.create_time)*1000)+0 as duration,oeh.online_exam_id from online_exam_history oeh
|
||||
inner join (select max(oehx.create_time) mtime ,oehx.online_exam_id onlineId,max(oehx.id) nid from online_exam_history oehx group by oehx.online_exam_id ) stb
|
||||
on stb.nid = oeh.id) as durationTable on durationTable.online_exam_id = soe.id
|
||||
where
|
||||
ep.del_flag = ${delFlag} `;
|
||||
if (isPractive) {
|
||||
sql += ` AND ep.is_practice_exam=${isPractive} AND ep.creator = '${token.userId}'`;
|
||||
whereMap.isPracticeExam = isPractive;
|
||||
whereMap.creator = token.userId;
|
||||
} else {
|
||||
sql += ` AND ep.is_practice_exam =0`;
|
||||
}
|
||||
if (status != null) {
|
||||
sql += ` AND ep.status=${status}`;
|
||||
whereMap.status = status;
|
||||
}
|
||||
if (classifyId) {
|
||||
sql += ` AND ep.classify_id = ${classifyId}`;
|
||||
whereMap.classifyId = classifyId;
|
||||
}
|
||||
if (title) {
|
||||
sql += ` AND ep.title LIKE '%${title}%'`;
|
||||
whereMap.title = Like(`%${title}%`);
|
||||
}
|
||||
sql += `
|
||||
order by
|
||||
ep.id desc limit ?,?`;
|
||||
const getPagingQuestions = await this.ExamPaperRep.query(sql, [
|
||||
+(currentPage - 1) * pageSize,
|
||||
+pageSize,
|
||||
]);
|
||||
// console.log(sql);
|
||||
const total = await this.ExamPaperRep.countBy(whereMap);
|
||||
return { total, currentPage, pageSize, data: getPagingQuestions };
|
||||
} catch (err) {
|
||||
throw '分页查询失败,错误详情:' + err;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
分页查询
|
||||
*/
|
||||
async paging(
|
||||
pagingInfo: PagingExamPaperDto,
|
||||
token: TokenDataEntity,
|
||||
delFlag = 0,
|
||||
) {
|
||||
const { currentPage, pageSize, classifyId, title, isPractive, status } =
|
||||
pagingInfo;
|
||||
type PagingParamsMap = {
|
||||
delFlag: number;
|
||||
classifyId: number | null;
|
||||
title: any | null;
|
||||
type: number | null;
|
||||
difficultyLevel: number | null;
|
||||
isPracticeExam: number;
|
||||
status: number | null;
|
||||
creator: string | null;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
let whereMap: PagingParamsMap = {
|
||||
delFlag: delFlag,
|
||||
classifyId: null,
|
||||
title: null,
|
||||
type: null,
|
||||
difficultyLevel: null,
|
||||
isPracticeExam: 0,
|
||||
status: null,
|
||||
creator: null,
|
||||
};
|
||||
// console.log(pagingInfo, whereMap);
|
||||
try {
|
||||
let sql = `select
|
||||
exam.id as id,
|
||||
exam.title as title,
|
||||
exam.paper_desc as paperDesc,
|
||||
exam.share as share,
|
||||
exam.paper_duration as duration,
|
||||
exam.total_score as totalScore ,
|
||||
exam.pass_percent as passPercent,
|
||||
exam.questions_is_random_sort as questionsIsRandomSort,
|
||||
exam.options_is_random_sort as optionsIsRandomSort,
|
||||
exam.question_count as questionCount,
|
||||
exam.status as status,
|
||||
exam.is_practice_exam as isPracticeExam,
|
||||
users.name as creator ,
|
||||
exam.creator as creatorId,
|
||||
exam.classify_id as classifyId,
|
||||
qc.name as classify,
|
||||
date_format(exam.create_time, '%Y-%m-%d %H:%i:%S') as createTime
|
||||
from
|
||||
exam_paper exam
|
||||
left join user users on
|
||||
exam.creator = users.id
|
||||
left join exam_paper_classify qc on
|
||||
exam.classify_id = qc.id
|
||||
where
|
||||
exam.del_flag = ${delFlag} `;
|
||||
if (isPractive) {
|
||||
sql += ` AND exam.is_practice_exam=${isPractive} AND exam.creator = '${token.userId}'`;
|
||||
whereMap.isPracticeExam = isPractive;
|
||||
whereMap.creator = token.userId;
|
||||
} else {
|
||||
sql += ` AND exam.is_practice_exam =0`;
|
||||
}
|
||||
if (status != null) {
|
||||
sql += ` AND exam.status=${status}`;
|
||||
whereMap.status = status;
|
||||
}
|
||||
if (classifyId) {
|
||||
sql += ` AND exam.classify_id = ${classifyId}`;
|
||||
whereMap.classifyId = classifyId;
|
||||
}
|
||||
if (title) {
|
||||
sql += ` AND exam.title LIKE '%${title}%'`;
|
||||
whereMap.title = Like(`%${title}%`);
|
||||
}
|
||||
sql += `
|
||||
order by
|
||||
exam.id desc limit ?,?`;
|
||||
const getPagingQuestions = await this.ExamPaperRep.query(sql, [
|
||||
+(currentPage - 1) * pageSize,
|
||||
+pageSize,
|
||||
]);
|
||||
// console.log(sql);
|
||||
const total = await this.ExamPaperRep.countBy(whereMap);
|
||||
return { total, currentPage, pageSize, data: getPagingQuestions };
|
||||
} catch (err) {
|
||||
throw '分页查询失败,错误详情:' + err;
|
||||
}
|
||||
}
|
||||
/*
|
||||
更新试卷
|
||||
*/
|
||||
async update(id: number, UpdateExamPaperDto: UpdateExamPaperDto) {
|
||||
await this.ExamPaperRep.update(id, UpdateExamPaperDto);
|
||||
const item = await this.ExamPaperRep.findOneBy({ delFlag: 0, id });
|
||||
return item;
|
||||
}
|
||||
|
||||
/*
|
||||
批量移动试卷到分类中
|
||||
*/
|
||||
async batchMovePaperToClassify(ids: number[], classifyId: number) {
|
||||
return await this.ExamPaperRep.query(`
|
||||
update exam_paper set classify_id=${classifyId} where id in (${ids.join(
|
||||
',',
|
||||
)})
|
||||
`);
|
||||
}
|
||||
/**
|
||||
* 检查试卷被哪些考试引用
|
||||
* @param paperId 试卷ID
|
||||
* @returns 引用改试卷的考试列表
|
||||
*/
|
||||
async checkPaperHasQuote(paperId: number) {
|
||||
return await this.ExamPaperRep.query(
|
||||
`select e.title as title,(case
|
||||
when now()>=e.exam_start_time and now()<=e.exam_end_time
|
||||
then 1 when now()<e.exam_start_time then 0
|
||||
when now()>e.exam_end_time then 2
|
||||
end) as status from exam e where e.exam_paper_id = ? and e.del_flag =0 `,
|
||||
[paperId],
|
||||
);
|
||||
}
|
||||
async removeAllQuestionByPaperId(paperId: number) {
|
||||
const rows = await this.ExamPaperRep.query(
|
||||
`update questions_for_paper set del_flag=1 where del_flag=0 and exam_paper_id=${paperId}`,
|
||||
);
|
||||
return rows;
|
||||
}
|
||||
async remove(id: number) {
|
||||
await this.ExamPaperRep.query(`delete from exam_paper where id = ${id}`);
|
||||
const exam = await this.ExamRep.findOneBy({
|
||||
examPaperId: id,
|
||||
delFlag: -1,
|
||||
classifyId: -1,
|
||||
});
|
||||
if (exam) {
|
||||
await this.ExamPaperRep.query(
|
||||
`delete from student_online_exam soe where soe.exam_id = ${exam.id} and soe.del_flag =-1`,
|
||||
);
|
||||
await this.ExamPaperRep.query(
|
||||
`delete from exam where exam_paper_id = ${id} and classify_id = -1 and del_flag = -1`,
|
||||
);
|
||||
}
|
||||
const rows = await this.removeAllQuestionByPaperId(id);
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { PagingStudentOnlineExamDto } from 'src/assessment-evaluation/dto/dtos-exam-manager/student-online-exam/paging-student-online-exam.dto';
|
||||
import { QuestionsForPaper } from 'src/assessment-evaluation/entities/entities-exam-paper-manager/questions-for-paper.entity';
|
||||
import { TokenDataEntity } from 'src/common/entities/token-data.entity';
|
||||
import { Repository } from 'typeorm';
|
||||
import { QuestionTypeService } from '../service-question-manager/question-type.service';
|
||||
|
||||
@Injectable()
|
||||
export class QuestionsForPaperService {
|
||||
constructor(
|
||||
@InjectRepository(QuestionsForPaper)
|
||||
private QuestionsForPaperRepository: Repository<QuestionsForPaper>,
|
||||
private readonly QuestionTypeService: QuestionTypeService,
|
||||
) {}
|
||||
|
||||
async create(createQuestionsForPaperDto: any) {
|
||||
await this.QuestionsForPaperRepository.insert(createQuestionsForPaperDto);
|
||||
return true;
|
||||
}
|
||||
|
||||
async findAll() {
|
||||
return this.QuestionsForPaperRepository.find({
|
||||
where: { delFlag: 0 },
|
||||
});
|
||||
}
|
||||
async pagingMistakeQuestion(
|
||||
pagingInfo: PagingStudentOnlineExamDto,
|
||||
token: TokenDataEntity,
|
||||
) {
|
||||
const { currentPage, pageSize, title } = pagingInfo;
|
||||
const { userId } = token;
|
||||
let countSql = `
|
||||
select
|
||||
q.classify_id as classifyId,
|
||||
qc.name as classify
|
||||
from
|
||||
exam_questions_result eqr
|
||||
left join question q on
|
||||
q.id = eqr.question_id
|
||||
left join question_classify qc on q.classify_id = qc.id
|
||||
where
|
||||
eqr.del_flag <=0 and q.del_flag <= 0
|
||||
and eqr.score < eqr.question_score and eqr.mistake_score < eqr.question_score
|
||||
`;
|
||||
let sql = `
|
||||
select qcc.classify,qcc.classifyId,group_concat(qcc.typeId,':',qcc.count) as times from
|
||||
(
|
||||
|
||||
select ifnull(count(1),0) as count,
|
||||
axx.classify,axx.type,axx.classifyId,axx.typeId
|
||||
from (
|
||||
|
||||
select
|
||||
qc.name as classify,
|
||||
qt.name as type,
|
||||
q.classify_id as classifyId,
|
||||
q.type as typeId
|
||||
from
|
||||
exam_questions_result eqr
|
||||
left join question q on
|
||||
q.id = eqr.question_id
|
||||
right join question_type qt on qt.id = q.type
|
||||
left join question_classify qc on qc.id = q.classify_id
|
||||
-- 使用试题ID查询resultID,去重
|
||||
inner join (
|
||||
select max(eqr.id) as rid from exam_questions_result eqr
|
||||
left join online_exam_history oeh on oeh.id=eqr.history_id
|
||||
where oeh.is_grade_paper =1
|
||||
group by eqr.question_id,eqr.creator
|
||||
) hidt on eqr.id = hidt.rid
|
||||
left join online_exam_history oeh on oeh.id=eqr.history_id
|
||||
left join exam e on oeh.exam_id = e.id
|
||||
-- 获取所有试题对应考试的已考次数
|
||||
left join (
|
||||
select eqr.question_id qid,group_concat(oeh.id) oehids ,count(oeh.id) count,soe.user_id uid
|
||||
from exam_questions_result eqr
|
||||
left join online_exam_history oeh on oeh.id = eqr.history_id
|
||||
left join student_online_exam soe on soe.id = oeh.online_exam_id
|
||||
group by eqr.question_id,oeh.online_exam_id
|
||||
) countTable on countTable.qid = q.id and countTable.uid = eqr.creator
|
||||
where
|
||||
eqr.del_flag <=0
|
||||
and q.del_flag <= 0
|
||||
and (e.exam_end_time <now() or e.exam_times<= countTable.count)
|
||||
and eqr.score < eqr.question_score and eqr.mistake_score < eqr.question_score
|
||||
`;
|
||||
if (userId) {
|
||||
sql += `
|
||||
and eqr.creator ='${userId}' `;
|
||||
countSql += ` and eqr.creator ='${userId}'
|
||||
`;
|
||||
}
|
||||
sql += `
|
||||
group by eqr.question_id) axx group by
|
||||
axx.classifyId ,axx.typeId order by axx.classifyId ,axx.typeId
|
||||
) qcc `;
|
||||
if (title) {
|
||||
sql += `
|
||||
where qcc.classify like '%${title}%' `;
|
||||
countSql += `and qc.name like '%${title}%' `;
|
||||
}
|
||||
sql += `
|
||||
group by qcc.classifyId order by qcc.classifyId desc limit ?,?
|
||||
`;
|
||||
countSql += `group by q.classify_id order by q.classify_id limit ?,?`;
|
||||
const list = await this.QuestionsForPaperRepository.query(sql, [
|
||||
+(currentPage - 1) * pageSize,
|
||||
+pageSize,
|
||||
]);
|
||||
const count = await this.QuestionsForPaperRepository.query(countSql, [
|
||||
+(currentPage - 1) * pageSize,
|
||||
+pageSize,
|
||||
]);
|
||||
const types = await this.QuestionTypeService.findAll();
|
||||
return { total: count.length, currentPage, pageSize, data: list, types };
|
||||
}
|
||||
/**
|
||||
* 根据试卷id获取试卷与试题的关系列表
|
||||
*/
|
||||
async getAllPapersQuestions(id: number) {
|
||||
return this.QuestionsForPaperRepository.find({
|
||||
where: { delFlag: 0, examPaperId: id },
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 根据试卷id复制所有试题关系到新试卷id
|
||||
* @param id 试卷id
|
||||
* @param nid 复制到的新的试卷id
|
||||
* @param uid 用户id
|
||||
* @returns 影响行数
|
||||
*/
|
||||
async copyPapersQuestionToPaper(id: number, nid: number, uid: string) {
|
||||
const sql = `insert
|
||||
into
|
||||
questions_for_paper(version,exam_paper_id,creator,updater,question_id,score,type,is_fixed)
|
||||
select
|
||||
1 as version,
|
||||
${nid} as exam_paper_id,
|
||||
${uid} as creator,
|
||||
${uid} as updater,
|
||||
qfp.question_id as question_id,
|
||||
qfp.score as score,
|
||||
qfp.type as type,
|
||||
qfp.is_fixed as is_fixed
|
||||
from
|
||||
questions_for_paper qfp
|
||||
where
|
||||
qfp.del_flag = 0
|
||||
and qfp.exam_paper_id = ${id}`;
|
||||
|
||||
return await this.QuestionsForPaperRepository.query(sql);
|
||||
}
|
||||
// 删除父类型同时删除子类型
|
||||
async remove(id: number) {
|
||||
if (isNaN(+id) || !id) throw '缺少id';
|
||||
await this.QuestionsForPaperRepository.update(id, { delFlag: 1 });
|
||||
return true;
|
||||
}
|
||||
async getQuestionsByPaperId(id: number) {
|
||||
return await this.QuestionsForPaperRepository.query(
|
||||
`
|
||||
select
|
||||
qq.id as id,
|
||||
qq.type as typeId,
|
||||
qq.title as title,
|
||||
users.name as creator ,
|
||||
qq.creator as creatorId,
|
||||
qq.classify_id as classifyId,
|
||||
qc.name as classify,
|
||||
qq.options as options,
|
||||
date_format(qq.create_time, '%Y-%m-%d') as createTime,
|
||||
qq.answer as answer,
|
||||
types.name as type,
|
||||
types.element as element,
|
||||
qq.difficulty_level as difficultyLevelId,
|
||||
levels.name as difficultyLevel,
|
||||
qq.knowledge_id as knowledgeId,
|
||||
knows.name as knowledgePoint,
|
||||
qfp.score score
|
||||
from
|
||||
question qq
|
||||
inner join (select qfp.id as id,qfp.question_id as questionId,qfp.score score from questions_for_paper qfp
|
||||
where qfp.exam_paper_id =? and qfp.del_flag =0) qfp on qfp.questionId = qq.id
|
||||
left join question_type types on
|
||||
qq.type = types.id
|
||||
left join question_difficulty_level levels on
|
||||
qq.difficulty_level = levels.id
|
||||
left join knowledge_point knows on
|
||||
qq.knowledge_id = knows.id
|
||||
left join user users on qq.creator = users.id
|
||||
left join question_classify qc on qq.classify_id = qc.id
|
||||
|
||||
order by qq.type`,
|
||||
[+id],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { CreateKnowledgePointDto } from '../../dto/dtos-question-manager/knowledge-point/create-knowledge-point.dto';
|
||||
import { UpdateKnowledgePointDto } from '../../dto/dtos-question-manager/knowledge-point/update-knowledge-point.dto';
|
||||
import { KnowledgePoint } from '../../entities/entities-question-manager/knowledge-point.entity';
|
||||
|
||||
@Injectable()
|
||||
export class KnowledgePointService {
|
||||
constructor(
|
||||
@InjectRepository(KnowledgePoint)
|
||||
private knowledgePointRepository: Repository<KnowledgePoint>,
|
||||
) {}
|
||||
|
||||
/*
|
||||
创建知识点
|
||||
*/
|
||||
async create(createKnowledgePointDto: CreateKnowledgePointDto) {
|
||||
const knowledgeItem = await this.knowledgePointRepository.findOne({
|
||||
where: { name: createKnowledgePointDto.name, delFlag: 0 },
|
||||
});
|
||||
if (!knowledgeItem) {
|
||||
return await this.knowledgePointRepository.save(createKnowledgePointDto);
|
||||
}
|
||||
return knowledgeItem;
|
||||
}
|
||||
/*
|
||||
查找所有知识点
|
||||
*/
|
||||
async findAll() {
|
||||
return this.knowledgePointRepository.find({
|
||||
select: ['id', 'name'],
|
||||
where: { delFlag: 0 },
|
||||
});
|
||||
}
|
||||
/*
|
||||
更新知识点
|
||||
*/
|
||||
async update(id: number, updateKnowledgePointDto: UpdateKnowledgePointDto) {
|
||||
return this.knowledgePointRepository.update(id, updateKnowledgePointDto);
|
||||
}
|
||||
/*
|
||||
删除知识点
|
||||
*/
|
||||
async remove(id: number) {
|
||||
await this.knowledgePointRepository.update(id, { delFlag: 1 });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { CreateQuestionClassifyDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question-classify/create-question-classify.dto';
|
||||
import { UpdateQuestionClassifyDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question-classify/update-question-classify.dto';
|
||||
import { QuestionClassify } from 'src/assessment-evaluation/entities/entities-question-manager/question-classify.entity';
|
||||
|
||||
@Injectable()
|
||||
export class QuestionClassifyService {
|
||||
constructor(
|
||||
@InjectRepository(QuestionClassify)
|
||||
private questionClassifyRepository: Repository<QuestionClassify>,
|
||||
) {}
|
||||
/**
|
||||
* 创建试题分类
|
||||
* @param createQuestionClassifyDto
|
||||
* @returns
|
||||
*/
|
||||
async create(createQuestionClassifyDto: CreateQuestionClassifyDto) {
|
||||
await this.questionClassifyRepository.insert(createQuestionClassifyDto);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* 获取所有试题分类
|
||||
* @returns
|
||||
*/
|
||||
async findAll() {
|
||||
return this.questionClassifyRepository.find({
|
||||
select: ['id', 'name', 'pid', 'level'],
|
||||
where: { delFlag: 0 },
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 更新试题分类
|
||||
* @param id
|
||||
* @param updateQuestionClassifyDto
|
||||
* @returns
|
||||
*/
|
||||
async update(
|
||||
id: number,
|
||||
updateQuestionClassifyDto: UpdateQuestionClassifyDto,
|
||||
) {
|
||||
return this.questionClassifyRepository.update(
|
||||
id,
|
||||
updateQuestionClassifyDto,
|
||||
);
|
||||
}
|
||||
/**
|
||||
* 删除父类型同时删除子类型
|
||||
* @param id
|
||||
* @returns
|
||||
*/
|
||||
async remove(id: number) {
|
||||
if (isNaN(+id) || !id) throw '缺少id';
|
||||
await this.questionClassifyRepository.update(id, { delFlag: 1 });
|
||||
const children = await this.questionClassifyRepository.find({
|
||||
where: { pid: id },
|
||||
});
|
||||
for (let index = 0; index < children.length; index++) {
|
||||
const element = children[index];
|
||||
await this.remove(element.id);
|
||||
}
|
||||
// await this.questionClassifyRepository.update(item.id, { delFlag: 1 });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { CreateQuestionDifficultyLevelDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question-difficulty-level/create-question-difficulty-level.dto';
|
||||
import { UpdateQuestionDifficultyLevelDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question-difficulty-level/update-question-difficulty-level.dto';
|
||||
import { QuestionDifficultyLevel } from 'src/assessment-evaluation/entities/entities-question-manager/question-difficulty-level.entity';
|
||||
|
||||
@Injectable()
|
||||
export class QuestionDifficultyLevelService {
|
||||
constructor(
|
||||
@InjectRepository(QuestionDifficultyLevel)
|
||||
private questionDifficultyLevelRepository: Repository<QuestionDifficultyLevel>,
|
||||
) {}
|
||||
/*
|
||||
创建难易程度
|
||||
*/
|
||||
async create(
|
||||
createQuestionDifficultyLevelDto: CreateQuestionDifficultyLevelDto,
|
||||
) {
|
||||
if (
|
||||
await this.questionDifficultyLevelRepository.findOne({
|
||||
where: { delFlag: 0, name: createQuestionDifficultyLevelDto.name },
|
||||
})
|
||||
) {
|
||||
throw '难易程度已存在';
|
||||
}
|
||||
return await this.questionDifficultyLevelRepository.save(
|
||||
createQuestionDifficultyLevelDto,
|
||||
);
|
||||
}
|
||||
/*
|
||||
获取所有试题难度
|
||||
*/
|
||||
async findAll() {
|
||||
return this.questionDifficultyLevelRepository.find({
|
||||
select: ['id', 'name'],
|
||||
where: { delFlag: 0 },
|
||||
});
|
||||
}
|
||||
/*
|
||||
更新试题难度
|
||||
*/
|
||||
async update(
|
||||
id: number,
|
||||
updateQuestionDifficultyLevelDto: UpdateQuestionDifficultyLevelDto,
|
||||
) {
|
||||
return this.questionDifficultyLevelRepository.update(
|
||||
id,
|
||||
updateQuestionDifficultyLevelDto,
|
||||
);
|
||||
}
|
||||
/*
|
||||
删除试题难度
|
||||
*/
|
||||
async remove(id: number) {
|
||||
await this.questionDifficultyLevelRepository.update(id, { delFlag: 1 });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { CreateQuestionTypeDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question-type/create-question-type.dto';
|
||||
import { UpdateQuestionTypeDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question-type/update-question-type.dto';
|
||||
import { QuestionType } from 'src/assessment-evaluation/entities/entities-question-manager/question-type.entity';
|
||||
|
||||
@Injectable()
|
||||
export class QuestionTypeService {
|
||||
constructor(
|
||||
@InjectRepository(QuestionType)
|
||||
private questionTypeRepository: Repository<QuestionType>,
|
||||
) {}
|
||||
|
||||
/*
|
||||
创建试题类型
|
||||
*/
|
||||
async create(createQuestionTypeDto: CreateQuestionTypeDto) {
|
||||
await this.questionTypeRepository.insert(createQuestionTypeDto);
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
查找所有试题类型
|
||||
*/
|
||||
async findAll() {
|
||||
return this.questionTypeRepository.find({
|
||||
select: ['id', 'name', 'element'],
|
||||
where: { delFlag: 0 },
|
||||
});
|
||||
}
|
||||
/*
|
||||
更新试题类型
|
||||
*/
|
||||
async update(id: number, updateQuestionTypeDto: UpdateQuestionTypeDto) {
|
||||
return this.questionTypeRepository.update(id, updateQuestionTypeDto);
|
||||
}
|
||||
/*
|
||||
删除试题类型
|
||||
*/
|
||||
async remove(id: number) {
|
||||
await this.questionTypeRepository.update(id, { delFlag: 1 });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { In, Like, Not, Repository } from 'typeorm';
|
||||
import { CreateQuestionDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question/create-question.dto';
|
||||
import { PagingQuestionDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question/paging-question.dto';
|
||||
import { UpdateQuestionDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question/update-question.dto';
|
||||
import { KnowledgePoint } from 'src/assessment-evaluation/entities/entities-question-manager/knowledge-point.entity';
|
||||
import { QuestionDifficultyLevel } from 'src/assessment-evaluation/entities/entities-question-manager/question-difficulty-level.entity';
|
||||
import { QuestionType } from 'src/assessment-evaluation/entities/entities-question-manager/question-type.entity';
|
||||
import { Question } from 'src/assessment-evaluation/entities/entities-question-manager/question.entity';
|
||||
import { GetQuestionCountDto } from 'src/assessment-evaluation/dto/dtos-question-manager/question/get-question-count.dto';
|
||||
|
||||
@Injectable()
|
||||
export class QuestionService {
|
||||
constructor(
|
||||
@InjectRepository(Question)
|
||||
private QuestionRepository: Repository<Question>,
|
||||
@InjectRepository(QuestionType)
|
||||
private QuestionTypeRepository: Repository<QuestionType>,
|
||||
@InjectRepository(QuestionDifficultyLevel)
|
||||
private QuestionDifficultyLevelRepository: Repository<QuestionDifficultyLevel>,
|
||||
@InjectRepository(KnowledgePoint)
|
||||
private KnowledgePointRepository: Repository<KnowledgePoint>,
|
||||
) {}
|
||||
|
||||
/*
|
||||
导入试题
|
||||
*/
|
||||
async importQuestions(questions: CreateQuestionDto[]) {
|
||||
return await this.QuestionRepository.insert(questions);
|
||||
}
|
||||
|
||||
/* 导出试题 */
|
||||
async exportAllQuestion(classifyId: number) {
|
||||
const querys = this.QuestionRepository.createQueryBuilder('q')
|
||||
.leftJoin(QuestionType, 'qt', 'qt.id=q.type')
|
||||
.leftJoin(QuestionDifficultyLevel, 'qdl', 'qdl.id = q.difficulty_level')
|
||||
.leftJoin(KnowledgePoint, 'kp', 'kp.id=q.knowledge_id')
|
||||
.select(
|
||||
`
|
||||
q.title as '题目',
|
||||
qt.name as '类型',
|
||||
qdl.name as '难易程度',
|
||||
kp.name as '知识点',
|
||||
q.options as '选项',
|
||||
q.answer as '答案',
|
||||
q.analysis as '答案解析'
|
||||
`,
|
||||
)
|
||||
.where('q.del_flag=0');
|
||||
if (classifyId) {
|
||||
querys.andWhere('q.classify_id = ' + classifyId);
|
||||
}
|
||||
|
||||
return await querys.getRawMany();
|
||||
}
|
||||
|
||||
/* 创建试题 */
|
||||
async create(createQuestionDto: CreateQuestionDto) {
|
||||
// 是否含有改type类型
|
||||
const hasType = await this.QuestionTypeRepository.findOneBy({
|
||||
delFlag: 0,
|
||||
id: createQuestionDto.type,
|
||||
});
|
||||
if (!hasType) {
|
||||
console.log(hasType);
|
||||
throw '[type]字段 - 试题类型不存在';
|
||||
}
|
||||
const hasDifficultyLevel =
|
||||
await this.QuestionDifficultyLevelRepository.findOneBy({
|
||||
delFlag: 0,
|
||||
id: createQuestionDto.difficultyLevel,
|
||||
});
|
||||
if (!hasDifficultyLevel) {
|
||||
throw '[difficultyLevel]字段 - 难易程度不存在';
|
||||
}
|
||||
const hasKnowledgePoint = await this.KnowledgePointRepository.findOneBy({
|
||||
delFlag: 0,
|
||||
id: createQuestionDto.knowledgeId,
|
||||
});
|
||||
if (!hasKnowledgePoint) {
|
||||
// throw '[knowledgeId]字段 - 知识点不存在';
|
||||
createQuestionDto.knowledgeId = null;
|
||||
}
|
||||
await this.QuestionRepository.insert(createQuestionDto);
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
获取一道试题
|
||||
*/
|
||||
async findOne(id: number) {
|
||||
return await this.QuestionRepository.findOneBy({ id });
|
||||
}
|
||||
/*
|
||||
随机抽题
|
||||
*/
|
||||
async random(GetQuestionCountDto: GetQuestionCountDto) {
|
||||
let sql = `select u.*,u.type as typeId,
|
||||
qt.name as type,
|
||||
qt.element as element
|
||||
from question u
|
||||
inner join (select id from question where del_flag=0`;
|
||||
if (GetQuestionCountDto.type) {
|
||||
sql += ` AND type=${GetQuestionCountDto.type}`;
|
||||
}
|
||||
if (GetQuestionCountDto.classifyId) {
|
||||
sql += ` AND classify_id=${GetQuestionCountDto.classifyId}`;
|
||||
}
|
||||
if (GetQuestionCountDto.difficultyLevel) {
|
||||
sql += ` AND difficulty_level=${GetQuestionCountDto.difficultyLevel}`;
|
||||
}
|
||||
if (GetQuestionCountDto.student) {
|
||||
sql += ` AND student = ${GetQuestionCountDto.student}`;
|
||||
}
|
||||
if (GetQuestionCountDto.excludeIds) {
|
||||
sql += ` AND id not in (${GetQuestionCountDto.excludeIds.join(',')})`;
|
||||
}
|
||||
sql += ` order by RAND() limit ${GetQuestionCountDto.count}) as t on t.id=u.id left join question_type qt on qt.id=u.type`;
|
||||
return await this.QuestionRepository.query(sql);
|
||||
}
|
||||
/*
|
||||
查找所有试题
|
||||
*/
|
||||
async findAll() {
|
||||
// return this.QuestionRepository.find({
|
||||
// select: ['id', 'type', 'difficultyLevel', 'title', 'options', 'answer', 'analysis', 'knowledgeId'],
|
||||
// where: { delFlag: 0 },
|
||||
// });
|
||||
return await this.QuestionRepository.createQueryBuilder('questions')
|
||||
.where({ delFlag: 0 })
|
||||
.leftJoinAndSelect(QuestionType, 'types', 'questions.type = types.id')
|
||||
.leftJoinAndSelect(
|
||||
QuestionDifficultyLevel,
|
||||
'levels',
|
||||
'questions.difficultyLevel = levels.id',
|
||||
)
|
||||
.leftJoinAndSelect(
|
||||
KnowledgePoint,
|
||||
'knows',
|
||||
'questions.knowledgeId = knows.id',
|
||||
)
|
||||
.select(
|
||||
`
|
||||
questions.id as id,
|
||||
questions.id as questions_id,
|
||||
questions.type as typeId,
|
||||
types.name as type,
|
||||
questions.difficultyLevel as difficultyLevelId,
|
||||
levels.name as difficultyLevel,
|
||||
questions.knowledgeId as knowledgeId,
|
||||
knows.name as knowledgePoint
|
||||
`,
|
||||
)
|
||||
.getRawMany();
|
||||
}
|
||||
|
||||
/*
|
||||
分页查找所有试题
|
||||
*/
|
||||
async paging(pagingInfo: PagingQuestionDto) {
|
||||
const {
|
||||
currentPage,
|
||||
pageSize,
|
||||
classifyId,
|
||||
title,
|
||||
type,
|
||||
difficultyLevel,
|
||||
excludeIds,
|
||||
student,
|
||||
} = pagingInfo;
|
||||
type PagingParamsMap = {
|
||||
delFlag: number;
|
||||
classifyId: number | null;
|
||||
title: any | null;
|
||||
type: number | null;
|
||||
difficultyLevel: number | null;
|
||||
id: any | null;
|
||||
student: number | null;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
let whereMap: PagingParamsMap = {
|
||||
delFlag: 0,
|
||||
classifyId: null,
|
||||
title: null,
|
||||
type: null,
|
||||
difficultyLevel: null,
|
||||
id: null,
|
||||
student: null,
|
||||
};
|
||||
console.log(pagingInfo, whereMap);
|
||||
try {
|
||||
let sql = `select
|
||||
questions.id as id,
|
||||
questions.type as typeId,
|
||||
questions.title as title,
|
||||
questions.student as student,
|
||||
users.name as creator ,
|
||||
questions.creator as creatorId,
|
||||
questions.classify_id as classifyId,
|
||||
qc.name as classify,
|
||||
questions.options as options,
|
||||
date_format(questions.create_time, '%Y-%m-%d') as createTime,
|
||||
questions.answer as answer,
|
||||
types.name as type,
|
||||
types.element as element,
|
||||
questions.difficulty_level as difficultyLevelId,
|
||||
levels.name as difficultyLevel,
|
||||
questions.knowledge_id as knowledgeId,
|
||||
knows.name as knowledgePoint
|
||||
from
|
||||
question questions
|
||||
left join question_type types on
|
||||
questions.type = types.id
|
||||
left join question_difficulty_level levels on
|
||||
questions.difficulty_level = levels.id
|
||||
left join knowledge_point knows on
|
||||
questions.knowledge_id = knows.id
|
||||
left join user users on questions.creator = users.id
|
||||
left join question_classify qc on questions.classify_id = qc.id
|
||||
where
|
||||
questions.del_flag = 0 `;
|
||||
if (classifyId) {
|
||||
sql += ` AND questions.classify_id = ${classifyId}`;
|
||||
whereMap.classifyId = classifyId;
|
||||
}
|
||||
if (title) {
|
||||
sql += ` AND questions.title LIKE '%${title}%'`;
|
||||
whereMap.title = Like(`%${title}%`);
|
||||
}
|
||||
if (difficultyLevel) {
|
||||
sql += ` AND questions.difficulty_level = ${difficultyLevel}`;
|
||||
whereMap.difficultyLevel = difficultyLevel;
|
||||
}
|
||||
if (type) {
|
||||
sql += ` AND questions.type = ${type}`;
|
||||
whereMap.type = type;
|
||||
}
|
||||
console.log('excludeIds', excludeIds);
|
||||
if (excludeIds) {
|
||||
sql += ` AND questions.id not in (${excludeIds.join(',')})`;
|
||||
whereMap.id = Not(In(excludeIds));
|
||||
}
|
||||
if (student) {
|
||||
sql += ` AND questions.student = ${student}`;
|
||||
whereMap.student = student;
|
||||
}
|
||||
sql += `
|
||||
order by
|
||||
questions.id desc limit ?,?`;
|
||||
const getPagingQuestions = await this.QuestionRepository.query(sql, [
|
||||
+(currentPage - 1) * pageSize,
|
||||
+pageSize,
|
||||
]);
|
||||
// console.log(sql);
|
||||
const total = await this.QuestionRepository.countBy(whereMap);
|
||||
return { total, currentPage, pageSize, data: getPagingQuestions };
|
||||
} catch (err) {
|
||||
throw '分页查询失败,错误详情:' + err;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
更新试题
|
||||
*/
|
||||
async updateStudentCanUse(id: number, updateQuestionDto: any) {
|
||||
return this.QuestionRepository.update(id, updateQuestionDto);
|
||||
}
|
||||
/*
|
||||
更新试题
|
||||
*/
|
||||
async update(id: number, updateQuestionDto: UpdateQuestionDto) {
|
||||
console.log('<===============>', typeof updateQuestionDto.knowledgeId);
|
||||
return this.QuestionRepository.update(id, updateQuestionDto);
|
||||
}
|
||||
|
||||
/*
|
||||
删除试题
|
||||
*/
|
||||
async remove(id: number) {
|
||||
await this.QuestionRepository.update(id, { delFlag: 1 });
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
根据条件获取试题数
|
||||
*/
|
||||
async getCount(GetQuestionCountDto: GetQuestionCountDto) {
|
||||
// return await this.QuestionRepository.countBy({
|
||||
// delFlag: 0,
|
||||
// ...GetQuestionCountDto,
|
||||
// });
|
||||
let sql = `SELECT COUNT(1) AS cnt FROM question Question WHERE Question.del_flag = 0`;
|
||||
if (GetQuestionCountDto.classifyId) {
|
||||
sql += ` AND Question.classify_id = ${GetQuestionCountDto.classifyId}`;
|
||||
}
|
||||
if (GetQuestionCountDto.type) {
|
||||
sql += ` AND Question.type = ${GetQuestionCountDto.type}`;
|
||||
}
|
||||
if (GetQuestionCountDto.difficultyLevel) {
|
||||
sql += ` AND Question.difficulty_level = ${GetQuestionCountDto.difficultyLevel}`;
|
||||
}
|
||||
if (GetQuestionCountDto.student) {
|
||||
sql += ` AND Question.student = ${GetQuestionCountDto.student}`;
|
||||
}
|
||||
if (GetQuestionCountDto.excludeIds) {
|
||||
sql += ` AND Question.id not in (${GetQuestionCountDto.excludeIds.join(
|
||||
',',
|
||||
)})`;
|
||||
}
|
||||
const res = await this.QuestionRepository.query(sql);
|
||||
return res[0].cnt;
|
||||
}
|
||||
|
||||
/*
|
||||
根据ID获取试题详情
|
||||
*/
|
||||
async getQuestions(ids: number[]) {
|
||||
return await this.QuestionRepository.query(`
|
||||
select * from question where id in (${ids.join(',')})`);
|
||||
}
|
||||
}
|
||||
4
serve/src/common/decorator/no-token.decorator.ts
Normal file
4
serve/src/common/decorator/no-token.decorator.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { SetMetadata } from '@nestjs/common';
|
||||
|
||||
export const IS_NO_AUTH_TOKEN = '_IS_NO_AUTH_TOKEN_';
|
||||
export const NoAuthToken = () => SetMetadata(IS_NO_AUTH_TOKEN, true);
|
||||
8
serve/src/common/decorator/query-page-info.decorator.ts
Normal file
8
serve/src/common/decorator/query-page-info.decorator.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
||||
import { PageInfoEntity } from '../entities/page-info.entity';
|
||||
|
||||
export const PageInfo = createParamDecorator(
|
||||
(data: unknown, ctx: ExecutionContext) => {
|
||||
return new PageInfoEntity(ctx.switchToHttp().getRequest().query);
|
||||
},
|
||||
);
|
||||
8
serve/src/common/decorator/token-data.decorator.ts
Normal file
8
serve/src/common/decorator/token-data.decorator.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
||||
|
||||
export const TokenData = createParamDecorator(
|
||||
(data: unknown, ctx: ExecutionContext) => {
|
||||
const request = ctx.switchToHttp().getRequest();
|
||||
return request.user;
|
||||
},
|
||||
);
|
||||
8
serve/src/common/dto/create.dto.ts
Normal file
8
serve/src/common/dto/create.dto.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { UpdateDto } from './update.dto';
|
||||
|
||||
export class CreateDto extends UpdateDto {
|
||||
@ApiProperty({ description: '创建人' })
|
||||
// @IsNotEmpty({ message: '获取当前登录人信息异常' })
|
||||
creator: string;
|
||||
}
|
||||
7
serve/src/common/dto/update.dto.ts
Normal file
7
serve/src/common/dto/update.dto.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class UpdateDto {
|
||||
@ApiProperty({ description: '更新人' })
|
||||
// @IsNotEmpty({ message: '获取当前登录人信息异常' })
|
||||
updater: string;
|
||||
}
|
||||
37
serve/src/common/entities/base.entity.ts
Normal file
37
serve/src/common/entities/base.entity.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import {
|
||||
Column,
|
||||
UpdateDateColumn,
|
||||
CreateDateColumn,
|
||||
VersionColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
export abstract class BaseEntity {
|
||||
@CreateDateColumn({ name: 'create_time', comment: '创建时间' })
|
||||
createTime: Date;
|
||||
|
||||
@Column({ comment: '创建人', default: 'SYSTEM' })
|
||||
creator: string;
|
||||
|
||||
@UpdateDateColumn({
|
||||
name: 'update_time',
|
||||
comment: '更新时间',
|
||||
})
|
||||
updateTime: Date;
|
||||
|
||||
@Column({ comment: '更新人', default: 'SYSTEM' })
|
||||
updater: string;
|
||||
|
||||
@Column({
|
||||
default: 0,
|
||||
select: false,
|
||||
name: 'del_flag',
|
||||
comment: '是否删除:0否/1是',
|
||||
})
|
||||
delFlag: number;
|
||||
|
||||
@Column({ nullable: true, comment: '备注说明' })
|
||||
remarks: string;
|
||||
|
||||
@VersionColumn({ select: false, comment: '更新版本' })
|
||||
version: number;
|
||||
}
|
||||
7
serve/src/common/entities/increment-id.entity.ts
Normal file
7
serve/src/common/entities/increment-id.entity.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { PrimaryGeneratedColumn } from 'typeorm';
|
||||
import { BaseEntity } from './base.entity';
|
||||
|
||||
export abstract class IncrementIdEntity extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment')
|
||||
id: number;
|
||||
}
|
||||
30
serve/src/common/entities/page-info.entity.ts
Normal file
30
serve/src/common/entities/page-info.entity.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
/**
|
||||
* 分页信息
|
||||
*/
|
||||
export class PageInfoEntity {
|
||||
constructor({ current, size }) {
|
||||
this.size = +size || 10;
|
||||
this.current = +current || 1;
|
||||
this.start = (this.current - 1) * this.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前页
|
||||
*/
|
||||
@ApiProperty({ description: '当前页' })
|
||||
current: number;
|
||||
|
||||
/**
|
||||
* 每页条数
|
||||
*/
|
||||
@ApiProperty({ description: '每页条数' })
|
||||
size: number;
|
||||
|
||||
/**
|
||||
* 起始页
|
||||
*/
|
||||
@ApiProperty({ description: '起始页' })
|
||||
start: number;
|
||||
}
|
||||
15
serve/src/common/entities/token-data.entity.ts
Normal file
15
serve/src/common/entities/token-data.entity.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class TokenDataEntity {
|
||||
@ApiProperty({ description: '用户 ID' })
|
||||
userId: string;
|
||||
|
||||
@ApiProperty({ description: '角色 ID' })
|
||||
roleId: number;
|
||||
|
||||
@ApiProperty({ description: '基础角色' })
|
||||
baseRole: number;
|
||||
|
||||
@ApiProperty({ description: '组织 ID' })
|
||||
orgId: number;
|
||||
}
|
||||
41
serve/src/common/filters/global-exception.filter.ts
Normal file
41
serve/src/common/filters/global-exception.filter.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import {
|
||||
ExceptionFilter,
|
||||
Catch,
|
||||
ArgumentsHost,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import { HttpAdapterHost } from '@nestjs/core';
|
||||
|
||||
@Catch()
|
||||
export class GlobalExceptionsFilter implements ExceptionFilter {
|
||||
constructor(private readonly httpAdapterHost: HttpAdapterHost) {}
|
||||
|
||||
catch(exception: any, host: ArgumentsHost): void {
|
||||
console.log(exception);
|
||||
|
||||
const { httpAdapter } = this.httpAdapterHost;
|
||||
|
||||
const ctx = host.switchToHttp();
|
||||
|
||||
const httpStatus =
|
||||
exception instanceof HttpException
|
||||
? exception.getStatus()
|
||||
: HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
|
||||
const responseBody = {
|
||||
code: httpStatus,
|
||||
timestamp: new Date().toISOString(),
|
||||
msg: exception.sql
|
||||
? `SQL Error: ${exception.sqlMessage}`
|
||||
: Array.isArray(exception?.response?.message)
|
||||
? exception?.response?.message[0]
|
||||
: exception, //'系统错误,请联系管理员',
|
||||
path: httpAdapter.getRequestUrl(ctx.getRequest()),
|
||||
};
|
||||
|
||||
console.error(responseBody);
|
||||
|
||||
httpAdapter.reply(ctx.getResponse(), responseBody, httpStatus);
|
||||
}
|
||||
}
|
||||
24
serve/src/common/interceptor/response.interceptor.ts
Normal file
24
serve/src/common/interceptor/response.interceptor.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import {
|
||||
Injectable,
|
||||
CallHandler,
|
||||
ExecutionContext,
|
||||
NestInterceptor,
|
||||
} from '@nestjs/common';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
interface ResponseData<T> {
|
||||
data: T;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ResponseInterceptor<T> implements NestInterceptor {
|
||||
intercept(
|
||||
context: ExecutionContext,
|
||||
next: CallHandler<any>,
|
||||
): Observable<any> | Promise<Observable<ResponseData<T>>> {
|
||||
return next
|
||||
.handle()
|
||||
.pipe(map((data) => ({ data, code: 0, msg: 'success' })));
|
||||
}
|
||||
}
|
||||
133
serve/src/common/plugins/custom-type-orm-logger.ts
Normal file
133
serve/src/common/plugins/custom-type-orm-logger.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import { Logger as TypeOrmLogger } from 'typeorm';
|
||||
import { LoggerOptions as TypeOrmLoggerOptions } from 'typeorm/logger/LoggerOptions';
|
||||
import { Logger } from '@nestjs/common';
|
||||
|
||||
/**
|
||||
* https://github.com/nestjs/nest/issues/3657
|
||||
*/
|
||||
export class CustomTypeOrmLoggerr implements TypeOrmLogger {
|
||||
static ForConnection(connectionName: string, options: TypeOrmLoggerOptions) {
|
||||
const logger = new Logger(`TypeORM[${connectionName}]`);
|
||||
return new CustomTypeOrmLoggerr(logger, options);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly _logger: Logger,
|
||||
private readonly _options: TypeOrmLoggerOptions,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Logs query and parameters used in it.
|
||||
*/
|
||||
logQuery(query: string, parameters?: any[]) {
|
||||
if (
|
||||
this._options === 'all' ||
|
||||
this._options === true ||
|
||||
(this._options instanceof Array && this._options.indexOf('query') !== -1)
|
||||
) {
|
||||
const sql =
|
||||
query +
|
||||
(parameters && parameters.length
|
||||
? ' -- PARAMETERS: ' + this.stringifyParams(parameters)
|
||||
: '');
|
||||
this._logger.log('query' + ': ' + sql);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs query that is failed.
|
||||
*/
|
||||
logQueryError(error: string, query: string, parameters?: any[]) {
|
||||
if (
|
||||
this._options === 'all' ||
|
||||
this._options === true ||
|
||||
(this._options instanceof Array && this._options.indexOf('error') !== -1)
|
||||
) {
|
||||
const sql =
|
||||
query +
|
||||
(parameters && parameters.length
|
||||
? ' -- PARAMETERS: ' + this.stringifyParams(parameters)
|
||||
: '');
|
||||
this._logger.log(`query failed: ` + sql);
|
||||
this._logger.log(`error:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs query that is slow.
|
||||
*/
|
||||
logQuerySlow(time: number, query: string, parameters?: any[]) {
|
||||
const sql =
|
||||
query +
|
||||
(parameters && parameters.length
|
||||
? ' -- PARAMETERS: ' + this.stringifyParams(parameters)
|
||||
: '');
|
||||
this._logger.log(`query is slow: ` + sql);
|
||||
this._logger.log(`execution time: ` + time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs events from the schema build process.
|
||||
*/
|
||||
logSchemaBuild(message: string) {
|
||||
if (
|
||||
this._options === 'all' ||
|
||||
(this._options instanceof Array && this._options.indexOf('schema') !== -1)
|
||||
) {
|
||||
this._logger.log(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs events from the migrations run process.
|
||||
*/
|
||||
logMigration(message: string) {
|
||||
this._logger.log(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform logging using given logger, or by default to the this._logger.
|
||||
* Log has its own level and message.
|
||||
*/
|
||||
log(level: 'log' | 'info' | 'warn', message: any) {
|
||||
switch (level) {
|
||||
case 'log':
|
||||
if (
|
||||
this._options === 'all' ||
|
||||
(this._options instanceof Array &&
|
||||
this._options.indexOf('log') !== -1)
|
||||
)
|
||||
this._logger.log(message);
|
||||
break;
|
||||
case 'info':
|
||||
if (
|
||||
this._options === 'all' ||
|
||||
(this._options instanceof Array &&
|
||||
this._options.indexOf('info') !== -1)
|
||||
)
|
||||
this._logger.debug(message);
|
||||
break;
|
||||
case 'warn':
|
||||
if (
|
||||
this._options === 'all' ||
|
||||
(this._options instanceof Array &&
|
||||
this._options.indexOf('warn') !== -1)
|
||||
)
|
||||
this._logger.warn(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts parameters to a string.
|
||||
* Sometimes parameters can have circular objects and therefor we are handle this case too.
|
||||
*/
|
||||
protected stringifyParams(parameters: any[]) {
|
||||
try {
|
||||
return JSON.stringify(parameters);
|
||||
} catch (error) {
|
||||
// most probably circular objects in parameters
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
}
|
||||
15
serve/src/common/plugins/index.ts
Normal file
15
serve/src/common/plugins/index.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||
import staticAssets from './static-assets';
|
||||
import swagger from './swagger';
|
||||
|
||||
export function initPlugins(app: NestExpressApplication) {
|
||||
swagger(app);
|
||||
app.enableCors({
|
||||
origin: '*',
|
||||
methods: '*',
|
||||
allowedHeaders: '*',
|
||||
});
|
||||
staticAssets(app);
|
||||
|
||||
return app;
|
||||
}
|
||||
9
serve/src/common/plugins/mysql.ts
Normal file
9
serve/src/common/plugins/mysql.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import env from 'src/env';
|
||||
import { CustomTypeOrmLoggerr } from './custom-type-orm-logger';
|
||||
|
||||
export default TypeOrmModule.forRoot({
|
||||
...env.mysql,
|
||||
type: 'mysql',
|
||||
logger: CustomTypeOrmLoggerr.ForConnection('default', 'all'),
|
||||
});
|
||||
9
serve/src/common/plugins/static-assets.ts
Normal file
9
serve/src/common/plugins/static-assets.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||
import env from 'src/env';
|
||||
|
||||
export default function (app: NestExpressApplication) {
|
||||
for (const key in env.staticAssets) {
|
||||
const { path, ...option } = env.staticAssets[key];
|
||||
app.useStaticAssets(path, option);
|
||||
}
|
||||
}
|
||||
12
serve/src/common/plugins/swagger.ts
Normal file
12
serve/src/common/plugins/swagger.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
||||
import env from 'src/env';
|
||||
|
||||
export default function (app) {
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle(env.swagger.title)
|
||||
.setVersion(env.swagger.version)
|
||||
.addBearerAuth({ type: 'http', scheme: 'bearer', bearerFormat: 'JWT' })
|
||||
.build();
|
||||
const document = SwaggerModule.createDocument(app, config);
|
||||
SwaggerModule.setup(env.swagger.path, app, document);
|
||||
}
|
||||
43
serve/src/env.ts
Normal file
43
serve/src/env.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { join } from 'path';
|
||||
|
||||
const IS_DEV = process.argv.includes('ENV=DEV');
|
||||
const BASE_PATH = join(__dirname, '..');
|
||||
|
||||
export default {
|
||||
IS_DEV,
|
||||
BASE_PATH,
|
||||
|
||||
serve: {
|
||||
port: 3000,
|
||||
},
|
||||
staticAssets: {
|
||||
resource: {
|
||||
path: join(BASE_PATH, 'RESOURCE_BUCKET'),
|
||||
prefix: '/file-bucket',
|
||||
},
|
||||
|
||||
front: {
|
||||
path: join(BASE_PATH, 'front'),
|
||||
},
|
||||
textBookFront: {
|
||||
path: join(BASE_PATH, 'tf'),
|
||||
prefix: '/text-book',
|
||||
},
|
||||
},
|
||||
|
||||
mysql: {
|
||||
host: 'localhost',
|
||||
port: 3306,
|
||||
username: 'root',
|
||||
password: '123456',
|
||||
database: 'huai_li',
|
||||
synchronize: true,
|
||||
autoLoadEntities: true,
|
||||
},
|
||||
|
||||
swagger: {
|
||||
title: '接口文档',
|
||||
version: '1.0',
|
||||
path: 'api-docs',
|
||||
},
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user