104 lines
4.0 KiB
TypeScript
104 lines
4.0 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
import { Repository } from 'typeorm';
|
|
import { StudentProfile } from '../entities/student-profile.entity';
|
|
import { StudentEnrollment } from '../entities/student-enrollment.entity';
|
|
import { ExamScore } from '../entities/exam-score.entity';
|
|
import { LearningRecord } from '../entities/learning-record.entity';
|
|
import { ResultArchive } from '../entities/result-archive.entity';
|
|
import { AttendanceRecord } from '../entities/attendance-record.entity';
|
|
import { Student } from '../entities/student.entity';
|
|
import { ARCHIVE_REPORT_CSS } from './archive-report.styles';
|
|
import { esc } from './archive-report.helpers';
|
|
import { buildCover, buildBasicInfo } from './archive-report.cover';
|
|
import { buildExamOverview, buildExamDetail } from './archive-report.exam';
|
|
import { buildAttendance } from './archive-report.attendance';
|
|
import { buildLearning, buildResult } from './archive-report.learning';
|
|
|
|
interface ReportData {
|
|
student: Student;
|
|
profile: StudentProfile | null;
|
|
enrollments: StudentEnrollment[];
|
|
exams: ExamScore[];
|
|
learnings: LearningRecord[];
|
|
result: ResultArchive | null;
|
|
attendances: AttendanceRecord[];
|
|
}
|
|
|
|
@Injectable()
|
|
export class ArchiveReportService {
|
|
constructor(
|
|
@InjectRepository(StudentProfile) private profileRepo: Repository<StudentProfile>,
|
|
@InjectRepository(StudentEnrollment) private enrollmentRepo: Repository<StudentEnrollment>,
|
|
@InjectRepository(ExamScore) private examRepo: Repository<ExamScore>,
|
|
@InjectRepository(LearningRecord) private learningRepo: Repository<LearningRecord>,
|
|
@InjectRepository(ResultArchive) private resultRepo: Repository<ResultArchive>,
|
|
@InjectRepository(AttendanceRecord) private attendanceRepo: Repository<AttendanceRecord>,
|
|
@InjectRepository(Student) private studentRepo: Repository<Student>,
|
|
) {}
|
|
|
|
async generateReportHtml(studentId: number): Promise<string> {
|
|
const [student, profile, enrollments, exams, learnings, result, attendances] =
|
|
await Promise.all([
|
|
this.studentRepo.findOne({ where: { id: studentId } }),
|
|
this.profileRepo.findOne({ where: { studentId } }),
|
|
this.enrollmentRepo.find({ where: { studentId }, order: { startDate: 'ASC' } }),
|
|
this.examRepo.find({ where: { studentId }, order: { examDate: 'ASC' } }),
|
|
this.learningRepo.find({ where: { studentId }, order: { recordDate: 'DESC' } }),
|
|
this.resultRepo.findOne({ where: { studentId } }),
|
|
this.attendanceRepo.find({ where: { studentId }, order: { attendanceDate: 'ASC' } }),
|
|
]);
|
|
|
|
if (!student) throw new Error('学生不存在');
|
|
|
|
return this.buildHtml({
|
|
student,
|
|
profile,
|
|
enrollments,
|
|
exams,
|
|
learnings,
|
|
result,
|
|
attendances,
|
|
});
|
|
}
|
|
|
|
private buildHtml(data: ReportData): string {
|
|
const { student, profile, enrollments, exams, learnings, result, attendances } = data;
|
|
const name = student.name;
|
|
const now = new Date().toLocaleDateString('zh-CN', {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric',
|
|
});
|
|
|
|
const sections = [
|
|
{
|
|
name: enrollments.length > 0 ? '基础信息与报读记录' : '基础信息',
|
|
html: buildBasicInfo(student, profile, enrollments, now),
|
|
},
|
|
{ name: '考试成绩总览', html: buildExamOverview(exams, now) },
|
|
{ name: '出勤记录', html: buildAttendance(attendances, now) },
|
|
{ name: '文化课考试成绩', html: buildExamDetail(exams, now) },
|
|
{ name: '学情记录', html: buildLearning(learnings, now) },
|
|
{ name: '录取归档', html: buildResult(result, now) },
|
|
].filter((section) => section.html.length > 0);
|
|
|
|
const cover = buildCover(
|
|
student,
|
|
profile,
|
|
enrollments,
|
|
now,
|
|
sections.map((section) => section.name),
|
|
);
|
|
|
|
return `<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head><meta charset="UTF-8"><title>学生档案报告 - ${esc(name)}</title>
|
|
<style>${ARCHIVE_REPORT_CSS}</style></head>
|
|
<body>
|
|
${cover}
|
|
${sections.map((section) => section.html).join('\n')}
|
|
</body></html>`;
|
|
}
|
|
}
|