exam-history.js 2.4 KB

12345678910111213141516171819202122232425262728
  1. // Read each vocabulary attempt; old summaries may have overwritten earlier scores,
  2. // but DesignAnswer retains their original answer sheets. Never backfill on reads.
  3. function examHistoryItem(row) {
  4. const data=row.data||{};
  5. let answers;try { answers=typeof data.answer==='string'?JSON.parse(data.answer):data.answer; } catch { answers=null; }
  6. const valid=Array.isArray(answers)&&answers.length>0&&answers.every(a=>a && a.result != null && [0,1,2,'0','1','2'].includes(a.result));
  7. const count=key=>data[key]==null?NaN:Number(data[key]);
  8. const total=valid?answers.length:count('totalScore'),score=valid?answers.filter(a=>Number(a.result)===1).length:count('df');
  9. const wrong=valid?answers.filter(a=>Number(a.result)===0).length:count('wrong'),unknown=valid?answers.filter(a=>Number(a.result)===2).length:count('dontKnow');
  10. const available=[total,score,wrong,unknown].every(Number.isFinite)&&total>0&&Math.min(score,wrong,unknown)>=0&&score+wrong+unknown===total;
  11. return {GeneralID:row.key,testedAt:row.tested_at,askid:row.ask_id,resultAvailable:available,df:available?score:null,totalScore:available?total:null,wrong:available?wrong:null,dontKnow:available?unknown:null};
  12. }
  13. async function appExamHistory(input,current) {
  14. assertLessonTenant(current);
  15. const uid=ownLegacyId(current);if(!uid)fail(403,'无法确认学员身份');
  16. const page=Math.max(1,Math.floor(number(input.page,1))),pageSize=10;
  17. const rows=await Psql.query(`WITH attempts AS (
  18. SELECT 'answer:'||a.id::text AS key,a."objectId" AS object_id,a."askId"::text AS ask_id,COALESCE(a.cdate,a."createdAt") AS tested_at,to_jsonb(a) AS data,0 AS priority
  19. FROM "DesignAnswer" a WHERE a.company::text=ANY($1::text[]) AND a."userId"::text=$2 AND a."askId"::text IN ('1','2')
  20. UNION ALL
  21. SELECT CASE WHEN COALESCE(p.answerid::text,'') NOT IN ('','0') THEN 'answer:'||p.answerid::text ELSE 'profile:'||p."objectId" END,p."objectId",p.askid::text,COALESCE(p."updatedAt",p."createdAt"),to_jsonb(p),1
  22. FROM "AssessmentProfile" p WHERE p.company::text=ANY($1::text[]) AND p."userId"::text=$2
  23. ), unique_attempts AS (
  24. SELECT DISTINCT ON (key) * FROM attempts ORDER BY key,priority,tested_at DESC NULLS LAST,object_id
  25. ) SELECT * FROM unique_attempts ORDER BY tested_at DESC NULLS LAST,key DESC LIMIT $3 OFFSET $4`,
  26. [[DEFAULT_COMPANY_ID,'Company$'+DEFAULT_COMPANY_ID],String(uid),pageSize+1,(page-1)*pageSize]);
  27. return {items:rows.slice(0,pageSize).map(examHistoryItem),page,hasMore:rows.length>pageSize};
  28. }