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

fix: include duration in word lesson history labels

彭峰 5 часов назад
Родитель
Сommit
842dc6500d

+ 1 - 1
projects/xiaoshu-admin/src/app/pages/lesson-records.component.scss

@@ -18,7 +18,7 @@
 .lesson-table .course-col { width: 15%; }
 .lesson-table .time-col { width: 138px; }
 .lesson-table .coach-col { width: 8%; }
-.lesson-table .class-col { width: 82px; }
+.lesson-table .class-col { width: 136px; }
 .lesson-table .review-col { width: 9%; }
 .lesson-table .pending-col { width: 62px; }
 .lesson-table .action-col { width: 92px; }

+ 7 - 1
scripts/cloud/lesson-records.js

@@ -31,7 +31,13 @@ function lessonHistoryFilters(input,args){const f={...(input.filters||{}),...inp
  for(const key of ['dateFrom','dateTo'])if(f[key]&&!/^\d{4}-\d{2}-\d{2}$/.test(f[key]))fail(400,'日期格式应为 YYYY-MM-DD');
  add('"actualAt">=($?::date::timestamp AT TIME ZONE \'Asia/Shanghai\')',f.dateFrom);add('"actualAt"<(($?::date+1)::timestamp AT TIME ZONE \'Asia/Shanghai\')',f.dateTo);
  return conditions.length?' WHERE '+conditions.join(' AND '):'';}
-function lessonHistoryRow(row){return {targetId:text(row.targetId||row.objectId),lessonId:row.objectId,generalId:number(row.generalId||row.id),appointmentId:text(row.yyds),studentId:number(row.xymz),studentName:row.studentNameResolved,coachId:number(row.jsmz),coachName:row.coachNameResolved,storeId:number(row.storeIdResolved),storeName:row.storeName,courseId:number(row.kcid),courseName:row.courseNameResolved,classTypeLabel:row.courseCategoryName||classType(row.kclx),lessonAt:dateIso(row.actualAt),score:number(row.pf),comment:text(row.pldp),coachComment:text(row.pldp),studentComment:text(row.pjnr),feedbackSummary:text(row.feedbackSummary||row.pldp),duplicateCount:number(row.duplicates),pendingReviews:number(row.pendingReviews),reviewCoaches:row.reviewCoaches||[],reviewCoachLabel:(row.reviewCoaches||[]).length>1?'多位陪练':row.reviewCoaches?.[0]?.name||'无待复习任务'};}
+function lessonHistoryClassType(row) {
+ const categoryKey=text(row.courseCategoryKey),categoryName=text(row.courseCategoryName);
+ const minutes=({'1':30,'2':60})[text(row.kclx)];
+ const isWord=categoryKey==='word'||categoryName==='单词课'||(!categoryKey&&!categoryName&&number(row.kcid)>0&&number(row.kcid)!==398);
+ return minutes&&isWord?'单词课 '+minutes+' 分钟':categoryName||classType(row.kclx);
+}
+function lessonHistoryRow(row){return {targetId:text(row.targetId||row.objectId),lessonId:row.objectId,generalId:number(row.generalId||row.id),appointmentId:text(row.yyds),studentId:number(row.xymz),studentName:row.studentNameResolved,coachId:number(row.jsmz),coachName:row.coachNameResolved,storeId:number(row.storeIdResolved),storeName:row.storeName,courseId:number(row.kcid),courseName:row.courseNameResolved,classTypeLabel:lessonHistoryClassType(row),lessonAt:dateIso(row.actualAt),score:number(row.pf),comment:text(row.pldp),coachComment:text(row.pldp),studentComment:text(row.pjnr),feedbackSummary:text(row.feedbackSummary||row.pldp),duplicateCount:number(row.duplicates),pendingReviews:number(row.pendingReviews),reviewCoaches:row.reviewCoaches||[],reviewCoachLabel:(row.reviewCoaches||[]).length>1?'多位陪练':row.reviewCoaches?.[0]?.name||'无待复习任务'};}
 async function lessonHistoryPage(context,input){
  requireRole(context,['ops-manager','ops-staff','ops-auditor']);const co=companyIdOf(context,input);if(!co)fail(400,'缺少帐套');
  const args=[co];if(context.storeId)input={...input,storeId:context.storeId,_storeId:context.storeId};const where=lessonHistoryFilters(input,args),cumulativeWhere=lessonHistoryFilters({studentId:input.studentId,studentName:input.studentName,search:input.search,_storeId:input._storeId},args),paging=pageInput(input);args.push(paging.pageSize,(paging.page-1)*paging.pageSize);

+ 17 - 1
scripts/tests/lesson-records.test.mjs

@@ -94,6 +94,22 @@ test('history projection preserves duplicate-account precedence and every filter
  await query(`INSERT INTO "_User"("objectId",company,"legacyUserId","legacyGroupId","identityType","realName","legacyUserData") VALUES ('zz-duplicate','c',20,1,'member','duplicate account','{"ParentUserID":40}'),('cross-tenant','d',20,1,'member','other tenant','{}')`);
  for(const input of [{},{studentId:20},{coachName:'原老师'},{dateFrom:'2026-10-01',dateTo:'2026-10-04'},{page:2,pageSize:10}]){
   const before=JSON.parse(JSON.stringify(await old.lessonHistoryPage(manager,input))),after=JSON.parse(JSON.stringify(await ctx.lessonHistoryPage(manager,input)));
-  delete before.refreshedAt;delete after.refreshedAt;assert.deepEqual(after,before);
+  delete before.refreshedAt;delete after.refreshedAt;
+  for(let index=0;index<after.items.length;index++){
+   assert.equal(after.items[index].classTypeLabel,before.items[index].classTypeLabel==='60 分钟'?'单词课 60 分钟':before.items[index].classTypeLabel);
+   delete before.items[index].classTypeLabel;delete after.items[index].classTypeLabel;
+  }
+  assert.deepEqual(after,before);
+ }
+});
+test('新旧单词课统一在课型后显示预约时长,并保留其他课型',()=>{
+ for(const kclx of ['1','2']){
+  const minutes=kclx==='1'?30:60;
+  for(const category of [{},{courseCategoryKey:'word',courseCategoryName:'单词课'}]){
+   assert.equal(ctx.lessonHistoryRow({kclx,kcid:'203',...category}).classTypeLabel,'单词课 '+minutes+' 分钟');
+  }
  }
+ assert.equal(ctx.lessonHistoryRow({kclx:'2',kcid:'0',courseCategoryKey:'high_writing',courseCategoryName:'高中语法写作课'}).classTypeLabel,'高中语法写作课');
+ assert.equal(ctx.lessonHistoryRow({kclx:'2',kcid:'398'}).classTypeLabel,'60 分钟');
+ assert.equal(ctx.lessonHistoryRow({kclx:'3',kcid:'405',courseCategoryKey:'trial',courseCategoryName:'体验课'}).classTypeLabel,'体验课');
 });