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, @InjectRepository(StudentEnrollment) private enrollmentRepo: Repository, @InjectRepository(ExamScore) private examRepo: Repository, @InjectRepository(LearningRecord) private learningRepo: Repository, @InjectRepository(ResultArchive) private resultRepo: Repository, @InjectRepository(AttendanceRecord) private attendanceRepo: Repository, @InjectRepository(Student) private studentRepo: Repository, ) {} async generateReportHtml(studentId: number): Promise { 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 ` 学生档案报告 - ${esc(name)} ${cover} ${sections.map((section) => section.html).join('\n')} `; } }