Files
gongxue-base/apps/server/src/archive/archive-report.service.ts

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