lesson-study.js 3.3 KB

1234567891011121314151617181920212223242526272829303132
  1. async function upsertStudyRecord(input,current) {
  2. assertLessonTenant(current);
  3. const uid=await authorizeRequestedUser(current,input.uid),gid=String(input.orderId||input.id||'');
  4. const order=await orderDetailRow(gid,current);
  5. if(!order||number(order.szyh)!==uid||ownLegacyId(current)!==number(order.pl))fail(403,'仅预约老师可以保存课堂学习');
  6. const addon=parseObject(input.addon,'学习记录'),words=await validateStudyWords(addon.xxqs||[]);
  7. // The transaction checks lesson state after command replay, so a confirmed
  8. // request can be retried after concurrent completion without a second write.
  9. const binding=await lessonStudyBinding(addon,current,uid,{...order,dszt:10},words);
  10. const mode=String(input.saveMode||'legacy'),batchId=String(input.batchId||('legacy:'+binding.objectId));
  11. const key=mobileCommandKey(input)||('legacy:'+randomObjectId());
  12. try{return (await Psql.one('SELECT xs_lesson_study_save($1,$2,$3,$4,$5,$6::jsonb) result',[DEFAULT_COMPANY_ID,gid,current.id,ownLegacyId(current),key,JSON.stringify({mode,batchId,bindingId:binding.objectId,expectedVersion:input.expectedVersion??0,words})])).result;}
  13. catch(e){fail(409,String(e.message||e));}
  14. }
  15. async function appLessonStudy(input,current) {
  16. assertLessonTenant(current);
  17. const gid=String(input.orderId||''),order=await orderDetailRow(gid,current);
  18. if(!order)fail(404,'预约不存在');
  19. const batches=await Psql.query('SELECT b.*,cb.kcid AS "courseId",n."nodeName" AS "courseName" FROM "LessonStudyBatch" b LEFT JOIN "CourseBinding" cb ON cb.company=b.company AND cb."objectId"=b."bindingId" LEFT JOIN "Node" n ON n.company=cb.company AND n."nodeId"::text=cb.kcid WHERE b.company=$1 AND b."orderId"=$2 AND b.status<>\'discarded\' ORDER BY b."updatedAt",b."batchId"',[DEFAULT_COMPANY_ID,gid]);
  20. const record=await Psql.oneOrNone('SELECT d.*,c."generalId" AS "GeneralID" FROM "DailyStudyRecord" d JOIN "CommonModel" c ON c.company=d.company AND c."modelId"=56 AND c."itemId"=d.id WHERE d.company=$1 AND d.dsid::text=$2 AND d."recordScope"=\'lesson\'',[DEFAULT_COMPANY_ID,gid]);
  21. const nodes=await Psql.query('SELECT "nodeId","nodeName" FROM "Node" WHERE company=$1 AND "nodeId"::text=ANY($2::text[])',[DEFAULT_COMPANY_ID,[...new Set(batches.flatMap(b=>parseArray(b.words).map(w=>String(w.nodeId||''))))]]);
  22. const names=new Map(nodes.map(n=>[String(n.nodeId),n.nodeName]));
  23. return {orderId:gid,record:record?{...record,words:parseArray(record.xxqs)}:null,batches:batches.map(b=>({...b,words:parseArray(b.words).map(w=>({...w,nodeName:names.get(String(w.nodeId))||''}))})),pending:batches.filter(b=>b.status==='draft').length};
  24. }
  25. async function appReadingSource(input,current) {
  26. const detail=await recordDetailData({id:input.sourceStudyId,studentId:input.studentId},current);
  27. if(!detail?.record)fail(404,'学习记录不存在');
  28. const record=detail.record,order=record.dsid?await orderDetailRow(record.dsid,current):null;
  29. if(record.recordScope==='lesson'&&number(order?.dszt)<30)fail(409,'请先完成课程,再使用完整课堂词表生成阅读');
  30. const words=detail.words||[];
  31. return {sourceStudyId:String(record.GeneralID||record.generalId),sourceVersion:String(record.studyVersion||record.updatedAt||''),wordIds:words.map(w=>String(w.GeneralID)),wordList:words.map(w=>w.Title),studentId:number(record.userId??record.UserID)};
  32. }