feat: change answer submission architecture

This commit is contained in:
2026-07-12 17:55:09 +03:30
parent f399ec4a04
commit 4e4731dbde
11 changed files with 238 additions and 87 deletions
@@ -38,17 +38,13 @@ export class CreateQuestionDTO {
})
content!: string;
@IsPositive({
message: t('validation.wrongField', {
field: '$t(questions.fields.order)',
}),
})
@IsNotEmpty({
message: t('validation.requiredField', {
field: '$t(questions.fields.order)',
}),
})
order!: number;
// @IsPositive({
// message: t('validation.wrongField', {
// field: '$t(questions.fields.order)',
// }),
// })
// @IsOptional()
// order?: number;
@IsPositive({
message: t('validation.wrongField', {
@@ -1,7 +1,7 @@
import { ConflictException, HttpStatus, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Question } from '../entities/question.entity';
import { QueryFailedError, Repository } from 'typeorm';
import { DataSource, QueryFailedError, Repository } from 'typeorm';
import { TestsService } from '@/modules/tests/providers/tests.service';
import { I18nService } from 'nestjs-i18n';
import { CreateQuestionDTO } from '../dtos/create-question.dto';
@@ -11,10 +11,16 @@ import { AppTimeoutException } from '@/common/exceptions/app-timeout.exception';
import { UpdateQuestionDTO } from '../dtos/update-question.dto';
import { validateMetadata } from '../helpers/validate-metadata.helper';
import { AppException } from '@/common/exceptions/app.exception';
import { Test } from '@/modules/tests/entities/test.entity';
@Injectable()
export class QuestionsCommandProvider {
constructor(
/**
* Inject DataSource
*/
private readonly dataSource: DataSource,
/**
* Inject Questions Repository
*/
@@ -35,13 +41,31 @@ export class QuestionsCommandProvider {
public async createOne(createQuestionDto: CreateQuestionDTO) {
const test = await this.testsService.adminGetOne(createQuestionDto.test);
let maxOrder: { max: string } | undefined;
try {
maxOrder = await this.questionsRepository
.createQueryBuilder('q')
.select('MAX(q.order)', 'max')
.where('q.testId = :testId', { testId: test.id })
.getRawOne();
} catch (err) {
throw new AppTimeoutException(err);
}
const newQuestion = this.questionsRepository.create({
...createQuestionDto,
test,
order: (Number(maxOrder?.max) || 0) + 1,
});
try {
await this.questionsRepository.save(newQuestion);
await this.dataSource.transaction(async (manager) => {
await manager.save(newQuestion);
await manager.increment(Test, { id: test.id }, 'questionsCount', 1);
});
return new AppResponse(
newQuestion,
this.i18nService.translate('common.messages.createSuccess', {
@@ -117,7 +141,10 @@ export class QuestionsCommandProvider {
let question: Question | null = null;
try {
question = await this.questionsRepository.findOneBy({ id });
question = await this.questionsRepository.findOne({
where: { id },
relations: { test: true },
});
} catch (err) {
throw new AppTimeoutException(err);
}
@@ -130,7 +157,16 @@ export class QuestionsCommandProvider {
);
try {
await this.questionsRepository.delete(question.id);
await this.dataSource.transaction(async (manager) => {
await manager.delete(Question, question.id);
await manager.decrement(
Test,
{ id: question.test.id },
'questionsCount',
1,
);
});
} catch (err) {
console.log(err);
throw new AppTimeoutException(err);
@@ -7,7 +7,7 @@ import { AppTimeoutException } from '@/common/exceptions/app-timeout.exception';
import { PaginatedResponse } from '@/common/responses';
import { InjectRepository } from '@nestjs/typeorm';
import { Question } from '../entities/question.entity';
import { Repository } from 'typeorm';
import { FindManyOptions, FindOneOptions, Repository } from 'typeorm';
import { TestsService } from '@/modules/tests/providers/tests.service';
import { AppException } from '@/common/exceptions/app.exception';
@@ -39,8 +39,6 @@ export class QuestionsQueryProvider {
});
}
console.log(selectedTest);
try {
const [questions, total] = await this.questionsRepository.findAndCount({
where: {
@@ -69,7 +67,19 @@ export class QuestionsQueryProvider {
}
}
public async findOneForAdmin(id: number) {
public async findByOptionsForAdmin(options: FindManyOptions<Question>) {
let questions: Array<Question> = [];
try {
questions = await this.questionsRepository.find(options);
} catch (err) {
throw new AppTimeoutException(err);
}
return questions;
}
public async findOneByIdForAdmin(id: number) {
let question: Question | null = null;
try {
@@ -91,4 +101,16 @@ export class QuestionsQueryProvider {
return question;
}
public async findOneForAdmin(options: FindOneOptions<Question>) {
let question: Question | null = null;
try {
question = await this.questionsRepository.findOne(options);
} catch (err) {
throw new AppTimeoutException(err);
}
return question;
}
}
@@ -4,6 +4,8 @@ import { FindQuestionsDTO } from '../dtos/find-questions.dto';
import { UpdateQuestionDTO } from '../dtos/update-question.dto';
import { QuestionsCommandProvider } from './questions-command.provider';
import { QuestionsQueryProvider } from './questions-query.provider';
import { FindManyOptions, FindOneOptions } from 'typeorm';
import { Question } from '../entities/question.entity';
@Injectable()
export class QuestionsService {
@@ -27,8 +29,16 @@ export class QuestionsService {
return await this.questionsQueryProvider.findAllForAdmin(query);
}
public async findOneForAdmin(id: number) {
return await this.questionsQueryProvider.findOneForAdmin(id);
public async findByOptionsForAdmin(options: FindManyOptions<Question>) {
return await this.questionsQueryProvider.findByOptionsForAdmin(options);
}
public async findOneByIdForAdmin(id: number) {
return await this.questionsQueryProvider.findOneByIdForAdmin(id);
}
public async findOneForAdmin(options: FindOneOptions<Question>) {
return await this.questionsQueryProvider.findOneForAdmin(options);
}
public async updateOne(id: number, updateQuestionDto: UpdateQuestionDTO) {