fix: 代码整合修复 - Entity类型、题库生成、评估流程等14项修复

This commit is contained in:
Developer
2026-05-14 09:55:07 +08:00
parent 122ab5e96f
commit 368eddfd75
17 changed files with 1666 additions and 115 deletions
@@ -0,0 +1,227 @@
import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, In } from 'typeorm';
import * as XLSX from 'xlsx';
import { AssessmentSession } from '../entities/assessment-session.entity';
import { AssessmentQuestion } from '../entities/assessment-question.entity';
import { AssessmentAnswer } from '../entities/assessment-answer.entity';
import { AssessmentCertificate } from '../entities/assessment-certificate.entity';
@Injectable()
export class ExportService {
private readonly logger = new Logger(ExportService.name);
constructor(
@InjectRepository(AssessmentSession)
private sessionRepository: Repository<AssessmentSession>,
@InjectRepository(AssessmentQuestion)
private questionRepository: Repository<AssessmentQuestion>,
@InjectRepository(AssessmentAnswer)
private answerRepository: Repository<AssessmentAnswer>,
@InjectRepository(AssessmentCertificate)
private certificateRepository: Repository<AssessmentCertificate>,
) {}
async exportToExcel(sessionId: string): Promise<Buffer> {
const session = await this.sessionRepository.findOne({
where: { id: sessionId },
relations: ['template', 'user'],
});
if (!session) {
throw new Error('Session not found');
}
const questions = await this.questionRepository.find({
where: { sessionId },
order: { order: 'ASC' },
});
const answers = await this.answerRepository.find({
where: { questionId: In(questions.map((q) => q.id)) },
});
const answerMap = new Map(answers.map((a) => [a.questionId, a]));
const summarySheet = [
['评估报告'],
['评估ID', session.id],
['用户', session.user?.name || session.userId],
['状态', session.status],
['最终分数', session.finalScore || '-'],
['原始分数', session.originalScore || '-'],
['评估模板', session.template?.name || session.templateJson?.name || '-'],
['开始时间', session.startedAt ? new Date(session.startedAt).toLocaleString() : '-'],
['完成时间', session.updatedAt ? new Date(session.updatedAt).toLocaleString() : '-'],
['总用时(秒)', session.totalTimeLimit],
[''],
['维度分数'],
...this.extractDimensionScores(session),
];
const questionRows = [
['题号', '题目内容', '用户回答', '分数', '反馈', '是否追问'],
];
for (let i = 0; i < questions.length; i++) {
const q = questions[i];
const a = answerMap.get(q.id);
questionRows.push([
(i + 1).toString(),
q.content || q.questionText || '',
a?.userAnswer || '',
a?.score?.toString() || '',
a?.feedback || '',
a?.isFollowUp ? '是' : '否',
]);
}
const wb = XLSX.utils.book_new();
const summaryWs = XLSX.utils.aoa_to_sheet(summarySheet);
XLSX.utils.book_append_sheet(wb, summaryWs, '摘要');
const questionWs = XLSX.utils.aoa_to_sheet(questionRows);
XLSX.utils.book_append_sheet(wb, questionWs, '题目详情');
if (session.finalReport) {
const reportSheet = this.wrapTextToLines(session.finalReport, 80);
const reportWs = XLSX.utils.aoa_to_sheet(reportSheet);
XLSX.utils.book_append_sheet(wb, reportWs, '评估报告');
}
const buffer = XLSX.write(wb, { bookType: 'xlsx', type: 'buffer' });
return Buffer.from(buffer);
}
private extractDimensionScores(session: AssessmentSession): any[][] {
const scores = session.templateJson?.dimensionScores || session.finalReport;
if (!scores) return [['未找到维度分数']];
if (typeof scores === 'string') {
return [['维度分数', scores]];
}
return Object.entries(scores).map(([key, value]) => [key, value]);
}
private wrapTextToLines(text: string, maxWidth: number): string[][] {
const lines: string[] = [];
const paragraphs = text.split('\n');
for (const paragraph of paragraphs) {
if (paragraph.trim() === '') {
lines.push('');
continue;
}
const words = paragraph.split(' ');
let currentLine = '';
for (const word of words) {
if ((currentLine + ' ' + word).trim().length <= maxWidth) {
currentLine = (currentLine + ' ' + word).trim();
} else {
if (currentLine) lines.push([currentLine]);
currentLine = word;
}
}
if (currentLine) lines.push([currentLine]);
}
return lines.map((l) => [l]);
}
async exportToPdf(sessionId: string): Promise<Buffer> {
const session = await this.sessionRepository.findOne({
where: { id: sessionId },
relations: ['template', 'user'],
});
if (!session) {
throw new Error('Session not found');
}
const certificate = await this.certificateRepository.findOne({
where: { sessionId },
});
const questions = await this.questionRepository.find({
where: { sessionId },
order: { order: 'ASC' },
});
const answers = await this.answerRepository.find({
where: { questionId: In(questions.map((q) => q.id)) },
});
const content = this.generatePdfContent(session, questions, answers, certificate);
return Buffer.from(content, 'utf-8');
}
private generatePdfContent(
session: AssessmentSession,
questions: AssessmentQuestion[],
answers: AssessmentAnswer[],
certificate: AssessmentCertificate | null,
): string {
const lines: string[] = [];
lines.push('='.repeat(60));
lines.push(' 人才评估报告');
lines.push('='.repeat(60));
lines.push('');
lines.push(`评估ID: ${session.id}`);
lines.push(`用户: ${session.user?.name || session.userId}`);
lines.push(`状态: ${session.status === 'COMPLETED' ? '已完成' : '进行中'}`);
lines.push(`最终分数: ${session.finalScore || '-'}`);
lines.push(`评估模板: ${session.template?.name || session.templateJson?.name || '-'}`);
lines.push(`评估时间: ${session.startedAt ? new Date(session.startedAt).toLocaleString() : '-'}`);
lines.push('');
if (certificate) {
lines.push('-'.repeat(60));
lines.push('证书信息');
lines.push('-'.repeat(60));
lines.push(`等级: ${certificate.level}`);
lines.push(`总分: ${certificate.totalScore}`);
lines.push(`是否通过: ${certificate.passed ? '是' : '否'}`);
lines.push(`颁发时间: ${certificate.issuedAt ? new Date(certificate.issuedAt).toLocaleString() : '-'}`);
lines.push('');
}
lines.push('-'.repeat(60));
lines.push('题目详情');
lines.push('-'.repeat(60));
const answerMap = new Map(answers.map((a) => [a.questionId, a]));
for (let i = 0; i < questions.length; i++) {
const q = questions[i];
const a = answerMap.get(q.id);
lines.push('');
lines.push(`${i + 1}题:`);
lines.push(` 题目: ${q.content || q.questionText || '-'}`);
lines.push(` 用户回答: ${a?.userAnswer || '-'}`);
lines.push(` 得分: ${a?.score ?? '-'}`);
lines.push(` 反馈: ${a?.feedback || '-'}`);
lines.push(` 追问: ${a?.isFollowUp ? '是' : '否'}`);
}
if (session.finalReport) {
lines.push('');
lines.push('-'.repeat(60));
lines.push('综合评估报告');
lines.push('-'.repeat(60));
lines.push(session.finalReport);
}
lines.push('');
lines.push('='.repeat(60));
lines.push(' 报告结束');
lines.push('='.repeat(60));
return lines.join('\n');
}
}