// Read each vocabulary attempt; old summaries may have overwritten earlier scores, // but DesignAnswer retains their original answer sheets. Never backfill on reads. function examHistoryItem(row) { const data=row.data||{}; let answers;try { answers=typeof data.answer==='string'?JSON.parse(data.answer):data.answer; } catch { answers=null; } const valid=Array.isArray(answers)&&answers.length>0&&answers.every(a=>a && a.result != null && [0,1,2,'0','1','2'].includes(a.result)); const count=key=>data[key]==null?NaN:Number(data[key]); const total=valid?answers.length:count('totalScore'),score=valid?answers.filter(a=>Number(a.result)===1).length:count('df'); 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'); const available=[total,score,wrong,unknown].every(Number.isFinite)&&total>0&&Math.min(score,wrong,unknown)>=0&&score+wrong+unknown===total; 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}; } async function appExamHistory(input,current) { assertLessonTenant(current); const uid=ownLegacyId(current);if(!uid)fail(403,'无法确认学员身份'); const page=Math.max(1,Math.floor(number(input.page,1))),pageSize=10; const rows=await Psql.query(`WITH attempts AS ( 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 FROM "DesignAnswer" a WHERE a.company::text=ANY($1::text[]) AND a."userId"::text=$2 AND a."askId"::text IN ('1','2') UNION ALL 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 FROM "AssessmentProfile" p WHERE p.company::text=ANY($1::text[]) AND p."userId"::text=$2 ), unique_attempts AS ( SELECT DISTINCT ON (key) * FROM attempts ORDER BY key,priority,tested_at DESC NULLS LAST,object_id ) SELECT * FROM unique_attempts ORDER BY tested_at DESC NULLS LAST,key DESC LIMIT $3 OFFSET $4`, [[DEFAULT_COMPANY_ID,'Company$'+DEFAULT_COMPANY_ID],String(uid),pageSize+1,(page-1)*pageSize]); return {items:rows.slice(0,pageSize).map(examHistoryItem),page,hasMore:rows.length>pageSize}; }