Просмотр исходного кода

fix(mobile): load appointment courses for booked student

彭峰 3 недель назад
Родитель
Сommit
be65fefd0e

+ 39 - 0
projects/xiaoshu-mobile/src/app/core/home-learning-modules.integration.spec.ts

@@ -13,12 +13,27 @@ import { SessionService } from './session.service';
 class LearningModulesApiService {
   lastScheduleParams: Record<string, unknown> = {};
   lastNodeParams: Record<string, unknown> = {};
+  lastAppointmentCourseParams: Record<string, unknown> = {};
   lastWordParams: Record<string, unknown> = {};
   lastLearningWordUpdate: Record<string, unknown> = {};
   lastMemoryParams: Record<string, unknown> = {};
   lastStandaloneLearningRecord: Record<string, unknown> = {};
 
   get<T>(action: string, params: Record<string, unknown> = {}): Observable<ApiEnvelope<T>> {
+    if (action === 'e_order_detail') {
+      return this.result<T>([{
+        GeneralID: 901,
+        szyh: 100,
+        HoneyName: '张雅茜',
+        kcid: 801,
+        yykcmc: '七年级上册',
+        dszt: 0,
+        dslx: 2,
+        bxrq: '2026-08-10',
+        sdsd: '19:00',
+        jffs: '线上',
+      }]);
+    }
     if (action === 'content_list' && Number(params['nid']) === 291) {
       return this.result<T>([
         { GeneralID: 201, CreateTime: '2026-08-01 09:30:00', con: '初中英语@Unit 1', xxqs: JSON.stringify([{ Title: 'apple' }]) },
@@ -39,11 +54,15 @@ class LearningModulesApiService {
       return this.result<T>([{ GeneralID: 401, df: 18, totalScore: 48, dontKnow: 20, wrong: 10 }]);
     }
     if (action === 'content_list' && Number(params['nid']) === 28) {
+      this.lastAppointmentCourseParams = params;
       return this.result<T>([
         { GeneralID: 801, kcid: 801, Title: '初中英语|七年级上册@同步词汇' },
         { GeneralID: 802, kcid: 801, Title2: '【初中英语】阅读训练' },
       ]);
     }
+    if (action === 'content_list' && Number(params['ModelID']) === 58) {
+      return this.result<T>([{ GeneralID: 701, yhid: 100, kcid: 801, syjd: '{}' }]);
+    }
     if (action === 'content_list' && Number(params['nid']) === 29) {
       this.lastScheduleParams = params;
       return this.result<T>([
@@ -663,6 +682,26 @@ describe('home learning modules', () => {
     expect(JSON.parse(String(standaloneRecord['addon']))).toEqual(jasmine.objectContaining({ UserID: 100, pl: 300, learned: 1, createReviewPlan: false }));
   }, 20000);
 
+  it('loads appointment courses and units for the appointment student when no student is selected in the session', async () => {
+    user.set({ userId: 300, userName: 'coach', honeyName: '张老师', groupId: 3 });
+    session.groupId.set(3);
+    session.student.set(null);
+    const harness = await RouterTestingHarness.create();
+
+    await harness.navigateByUrl('/pages/shop/get_order?id=901');
+    await harness.fixture.whenStable();
+    harness.fixture.detectChanges();
+
+    const api = TestBed.inject(ApiService) as unknown as LearningModulesApiService;
+    expect(session.student()?.userId).toBe(100);
+    expect(api.lastAppointmentCourseParams['myfield']).toBe('yhid=100');
+    expect(api.lastNodeParams['userId']).toBe(100);
+    const courseSelect = harness.routeNativeElement?.querySelector('.course-section select') as HTMLSelectElement;
+    expect(courseSelect.value).toBe('801');
+    expect(courseSelect.selectedOptions[0]?.textContent).toContain('七年级上册');
+    expect(harness.routeNativeElement?.querySelectorAll('.unit-grid article').length).toBe(2);
+  });
+
   it('counts words marked as known as completed and persists them for the student', async () => {
     user.set({ userId: 300, userName: 'coach', honeyName: '张老师', groupId: 3 });
     session.groupId.set(3);

+ 57 - 15
projects/xiaoshu-mobile/src/app/features/appointment/appointment-page.component.ts

@@ -103,6 +103,7 @@ export class AppointmentPageComponent implements OnInit {
 
   private applyOrder(order: SourceItem): void {
     this.order.set(order);
+    this.syncAppointmentStudent(order);
     this.selectedCourse = String(order['kcid'] ?? '');
     this.coachComment = String(order['pldp'] ?? '');
     this.loadPracticeRecord();
@@ -251,6 +252,7 @@ export class AppointmentPageComponent implements OnInit {
 
   private savePracticeSession(): void {
     const student = this.session.student();
+    const studentId = this.appointmentStudentId();
     const user = this.session.user();
     const existing = this.practiceRecord();
     const content: SourceItem = {
@@ -270,7 +272,7 @@ export class AppointmentPageComponent implements OnInit {
     const addon = {
       jsmz: user?.userId,
       kcid: this.selectedCourse,
-      xymz: student?.userId ?? this.order()['yhid'],
+      xymz: studentId,
       kclx: this.order()['dslx_t'],
       yyds: this.orderId,
       jffs: this.order()['jffs'],
@@ -329,19 +331,35 @@ export class AppointmentPageComponent implements OnInit {
   }
 
   private loadCourses(): void {
-    const student = this.session.student();
-    if (!student?.userId) return;
-    this.api.get<unknown>('content_list', { inputer: '', nid: 28, myfield: `yhid=${student.userId}`, orders: 'id=DESC', psize: 200 }).subscribe({
-      next: ({ result }) => this.courses.set(this.findList(result).map((item) => {
-        const parts = String(item['Title'] ?? '').split('|');
-        return { ...item, Title: parts[parts.length - 1] || '-', kcId: item['kcid'] };
-      })),
-      error: () => undefined,
+    const studentId = this.appointmentStudentId();
+    const currentCourse = this.currentCourseOption();
+    if (!studentId) {
+      this.courses.set(currentCourse ? [currentCourse] : []);
+      return;
+    }
+    this.api.get<unknown>('content_list', { inputer: '', nid: 28, myfield: `yhid=${studentId}`, orders: 'id=DESC', psize: 200 }).subscribe({
+      next: ({ result }) => {
+        const courses: SourceItem[] = this.findList(result).map((item): SourceItem => {
+          const parts = String(item['Title'] ?? '').split('|');
+          return { ...item, Title: parts[parts.length - 1] || '-', kcId: item['kcid'] };
+        });
+        if (currentCourse && !courses.some((course) => String(course['kcid'] ?? course['kcId'] ?? '') === this.selectedCourse)) {
+          courses.unshift(currentCourse);
+        }
+        this.courses.set(courses);
+      },
+      error: () => this.courses.set(currentCourse ? [currentCourse] : []),
     });
   }
 
   private loadUnits(): void {
-    this.api.get<unknown>('node_list', { pid: this.selectedCourse, ifunit: 1, userId: this.session.user()?.userId }).subscribe({
+    const studentId = this.appointmentStudentId();
+    if (!studentId) {
+      this.units.set([]);
+      this.platform.toast('预约记录缺少学员 ID,暂时无法加载学习单元', 'error');
+      return;
+    }
+    this.api.get<unknown>('node_list', { pid: this.selectedCourse, ifunit: 1, userId: studentId }).subscribe({
       next: ({ result }) => {
         this.units.set(this.findList(result));
         this.loadUnitProgress();
@@ -351,8 +369,9 @@ export class AppointmentPageComponent implements OnInit {
   }
 
   private loadUnitProgress(): void {
-    const owner = Number(this.session.user()?.groupId) === 3 ? this.session.student() : this.session.user();
-    this.api.post<unknown>('content_list', { ModelID: 58, myfield2: `yhid=${owner?.userId}|kcid=${this.selectedCourse}` }).subscribe({
+    const studentId = this.appointmentStudentId();
+    if (!studentId) return;
+    this.api.post<unknown>('content_list', { ModelID: 58, myfield2: `yhid=${studentId}|kcid=${this.selectedCourse}` }).subscribe({
       next: ({ result }) => {
         const record = this.findList(result)[0] ?? {};
         this.bindingRecord.set(record);
@@ -363,9 +382,9 @@ export class AppointmentPageComponent implements OnInit {
   }
 
   private loadLearningRecord(): void {
-    const student = this.session.student();
-    if (!student?.userId) return;
-    this.api.get<unknown>('content_list', { ModelID: 56, psize: 1, myfield: `dsid=${this.orderId}`, myfield2: `UserId=${student.userId}` }).subscribe({
+    const studentId = this.appointmentStudentId();
+    if (!studentId) return;
+    this.api.get<unknown>('content_list', { ModelID: 56, psize: 1, myfield: `dsid=${this.orderId}`, myfield2: `UserId=${studentId}` }).subscribe({
       next: ({ result }) => this.learningRecord.set(this.findList(result)[0] ?? {}),
       error: () => undefined,
     });
@@ -387,6 +406,29 @@ export class AppointmentPageComponent implements OnInit {
     return value && typeof value === 'object' && !Array.isArray(value) ? value as SourceItem : {};
   }
 
+  private appointmentStudentId(): number {
+    return Number(this.order()['szyh'] ?? this.order()['yhid'] ?? this.session.student()?.userId ?? 0);
+  }
+
+  private syncAppointmentStudent(order: SourceItem): void {
+    const studentId = Number(order['szyh'] ?? order['yhid'] ?? 0);
+    if (!studentId || this.session.student()?.userId === studentId) return;
+    this.session.setStudent({
+      userId: studentId,
+      honeyName: String(order['HoneyName'] ?? order['honeyName'] ?? order['honeyname'] ?? order['xymz_t'] ?? '学员'),
+      groupId: 1,
+    });
+  }
+
+  private currentCourseOption(): SourceItem | null {
+    if (!this.selectedCourse) return null;
+    return {
+      kcid: this.selectedCourse,
+      kcId: this.selectedCourse,
+      Title: this.field('course') || `课程 ${this.selectedCourse}`,
+    };
+  }
+
   private appointmentSnapshot(): SourceItem {
     const state = history.state as { appointmentSnapshot?: unknown } | null;
     if (state?.appointmentSnapshot && typeof state.appointmentSnapshot === 'object' && !Array.isArray(state.appointmentSnapshot)) {