|
| 1 | +import { CourseBase } from '@/models/CourseBase' |
| 2 | +import { CourseTodoInfo } from '@/models/CourseTodoInfo' |
| 3 | +import { Semester, Term } from '@/models/shared' |
| 4 | +import { CourseSpider } from '@/spiders/CourseSpider' |
| 5 | +import { ExamSpider } from '@/spiders/ExamSpider' |
| 6 | +import { GradeSpider } from '@/spiders/GradeSpider' |
| 7 | +import { XzzdSpider } from '@/spiders/XzzdSpider' |
| 8 | +import { toSemester } from '@/utils/stringUtils' |
| 9 | +import { CourseCombined, DataOrigin } from '../models/CourseCombined' |
| 10 | + |
| 11 | +/**批量获取并解析所有上游数据的服务。 */ |
| 12 | +export class RenewService { |
| 13 | + courseSpider = new CourseSpider() |
| 14 | + examSpider = new ExamSpider() |
| 15 | + gradeSpider = new GradeSpider() |
| 16 | + xxzdSpider = new XzzdSpider() |
| 17 | + |
| 18 | + /**立即重新获取所有数据并解析。若任何步骤发生致命错误将reject。 */ |
| 19 | + async renewAll(): Promise<CourseCombined[]> { |
| 20 | + /** 对term做按位交,合并两个Semester */ |
| 21 | + function combineSemester(sem1: Semester, sem2: Semester): Semester { |
| 22 | + const { year: year1, term: term1 } = sem1, |
| 23 | + { year: year2, term: term2 } = sem2 |
| 24 | + if (year1 !== year2) throw new Error('尝试跨学年合并学期') |
| 25 | + if (term1 === Term.Short || term2 === Term.Short) |
| 26 | + //如果含短学期,总是返回短学期 |
| 27 | + return { year: year1, term: Term.Short } |
| 28 | + const term = term1 & term2 |
| 29 | + if (term === 0) throw new Error('欲合并的学期无交集') |
| 30 | + return { year: year1, term } |
| 31 | + } |
| 32 | + /** 返回一个函数。该函数将`append`与接收到的首个参数进行课程信息合并。直接修改接收到的参数,不返回值。 */ |
| 33 | + function combineF( |
| 34 | + append: Omit<CourseCombined, 'origin'>, |
| 35 | + origin: DataOrigin, |
| 36 | + ): (base: CourseCombined) => void { |
| 37 | + return (base) => { |
| 38 | + //合并课程信息。除了semester取区间较短者(秋 总覆盖 秋冬),其它全部后出现者覆盖 |
| 39 | + const { semester: appendSemester } = append, |
| 40 | + { semester: baseSemester, origin: baseOrigin } = base |
| 41 | + if (!baseOrigin.includes(origin)) baseOrigin.push(origin) |
| 42 | + Object.assign(base, { |
| 43 | + ...append, |
| 44 | + semester: combineSemester(baseSemester, appendSemester), |
| 45 | + }) |
| 46 | + } |
| 47 | + } |
| 48 | + const [courses, exams, grades, todos] = await Promise.all([ |
| 49 | + this.courseSpider.getAllCourses(), |
| 50 | + this.examSpider.getAllExams(), |
| 51 | + this.gradeSpider.getAllGrades(), |
| 52 | + this.xxzdSpider.getTodos(), |
| 53 | + ]) |
| 54 | + const r: Map<CourseBase['id'], CourseCombined> = new Map() |
| 55 | + courses.forEach((item) => |
| 56 | + r.ensure( |
| 57 | + item.id, |
| 58 | + () => ({ ...item, origin: ['class'] }), |
| 59 | + combineF(item, 'class'), |
| 60 | + ), |
| 61 | + ) |
| 62 | + exams.forEach((item) => |
| 63 | + r.ensure( |
| 64 | + item.id, |
| 65 | + () => ({ ...item, origin: ['exam'] }), |
| 66 | + combineF(item, 'exam'), |
| 67 | + ), |
| 68 | + ) |
| 69 | + grades.forEach((item) => |
| 70 | + r.ensure( |
| 71 | + item.id, |
| 72 | + () => ({ ...item, origin: ['grade'] }), |
| 73 | + combineF(item, 'grade'), |
| 74 | + ), |
| 75 | + ) |
| 76 | + todos.forEach((item) => |
| 77 | + r.ensure( |
| 78 | + item.courseCode, |
| 79 | + () => { |
| 80 | + return { |
| 81 | + semester: toSemester(item.courseCode), |
| 82 | + id: item.courseCode, |
| 83 | + name: item.courseName, |
| 84 | + todos: [item], |
| 85 | + origin: ['xzzdTodo'], |
| 86 | + } |
| 87 | + }, |
| 88 | + (prevItem: CourseCombined & Partial<CourseTodoInfo>) => { |
| 89 | + if (!prevItem.todos) { |
| 90 | + prevItem.todos = [] |
| 91 | + prevItem.origin.push('xzzdTodo') |
| 92 | + } |
| 93 | + prevItem.todos.push(item) |
| 94 | + }, |
| 95 | + ), |
| 96 | + ) |
| 97 | + const coll = new Intl.Collator('zh-Hans-CN', { |
| 98 | + usage: 'sort', |
| 99 | + sensitivity: 'variant', |
| 100 | + }) |
| 101 | + return [...r.values()].sort((a, b) => coll.compare(a.id, b.id)) |
| 102 | + } |
| 103 | +} |
0 commit comments