feat:初始化 -融骅

This commit is contained in:
2023-10-17 09:15:30 +08:00
parent c9ff84e6a2
commit 405e152b38
1190 changed files with 138344 additions and 455 deletions

32
serve/src/app.module.ts Normal file
View 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 {}

View 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);
}
}

View 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 {}

View 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;
}
}

View 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;
}

View File

@@ -0,0 +1,6 @@
import { PartialType } from '@nestjs/swagger';
import { CreateArticleManageDto } from './create-article_manage.dto';
export class UpdateArticleManageDto extends PartialType(
CreateArticleManageDto,
) {}

View 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
}

View 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;
}

View File

@@ -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 {}

View File

@@ -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);
// }
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1 @@
export class CreateAssessmentEvaluationDto {}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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[];
};
}

View File

@@ -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;
}

View File

@@ -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[];
};
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View 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;
}

View File

@@ -0,0 +1,6 @@
import { PartialType } from '@nestjs/swagger';
import { CreateAssessmentEvaluationDto } from './create-assessment-evaluation.dto';
export class UpdateAssessmentEvaluationDto extends PartialType(
CreateAssessmentEvaluationDto,
) {}

View File

@@ -0,0 +1 @@
export class AssessmentEvaluation {}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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,
],
};

View File

@@ -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,
],
};

View File

@@ -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,
],
};

View File

@@ -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`;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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 });
}
}

View File

@@ -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}`);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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],
);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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(',')})`);
}
}

View 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);

View 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);
},
);

View 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;
},
);

View 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;
}

View File

@@ -0,0 +1,7 @@
import { ApiProperty } from '@nestjs/swagger';
export class UpdateDto {
@ApiProperty({ description: '更新人' })
// @IsNotEmpty({ message: '获取当前登录人信息异常' })
updater: string;
}

View 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;
}

View File

@@ -0,0 +1,7 @@
import { PrimaryGeneratedColumn } from 'typeorm';
import { BaseEntity } from './base.entity';
export abstract class IncrementIdEntity extends BaseEntity {
@PrimaryGeneratedColumn('increment')
id: number;
}

View 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;
}

View 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;
}

View 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);
}
}

View 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' })));
}
}

View 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;
}
}
}

View 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;
}

View 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'),
});

View 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);
}
}

View 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
View 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