|
|
@@ -2687,11 +2687,17 @@ function appOverviewCourseRow(source) {
|
|
|
function appOverviewStudyRow(row) {
|
|
|
if(!row)return null;return{objectId:String(row.objectId||''),GeneralID:number(row.generalId||row.id),ID:number(row.generalId||row.id),Title:String(row.title||row.con||'学习记录'),CreateTime:firstValue(row.createTime,row.createdAt),pl:row.pl,con:row.con,djq:row.djq,ygg:row.ygg,dqrq:row.dqrq,dsid:row.dsid,fxrl:row.fxrl,xxqs:row.xxqs,szmdid:row.szmdid,UserID:row.userId,userId:row.userId,learned:row.learned};
|
|
|
}
|
|
|
-async function appOverviewCompletedReadingMap(records,targetId) {
|
|
|
+async function appOverviewReadingMap(records,targetId) {
|
|
|
const studyIds=[...new Set(records.map((row)=>String(row&&row.GeneralID||'').trim()).filter(Boolean))];if(!studyIds.length)return new Map();
|
|
|
const learner=await legacyUserObject(targetId);if(!learner)return new Map();const ownerIds=[learner.id,'_User$'+learner.id];
|
|
|
- const rows=await Psql.query('SELECT DISTINCT ON (a."createOptions"#>>\'{params,sourceStudyId}\') a."createOptions"#>>\'{params,sourceStudyId}\' AS "sourceStudyId",a."objectId" AS "readingArticleId",a."title" AS "readingTitle",completed."completedAt" AS "readingCompletedAt" FROM "SurveyItem" a JOIN LATERAL (SELECT COALESCE(l."createdAt",l."updatedAt") AS "completedAt" FROM "SurveyLog" l WHERE CAST(l."surveyItem" AS text)=ANY(ARRAY[CAST(a."objectId" AS text),\'SurveyItem$\'||CAST(a."objectId" AS text)]) AND CAST(l."user" AS text)=ANY($1::text[]) ORDER BY COALESCE(l."createdAt",l."updatedAt") DESC,l."objectId" DESC LIMIT 1) completed ON TRUE WHERE CAST(a."user" AS text)=ANY($1::text[]) AND a."type"=\'reading\' AND COALESCE(a."isDeleted",FALSE)=FALSE AND a."createOptions"#>>\'{params,sourceStudyId}\'=ANY($2::text[]) ORDER BY a."createOptions"#>>\'{params,sourceStudyId}\',completed."completedAt" DESC,COALESCE(a."createdAt",a."updatedAt") DESC',[ownerIds,studyIds]);
|
|
|
- const map=new Map();for(const row of rows)map.set(String(row.sourceStudyId),{readingArticleId:String(row.readingArticleId||''),readingTitle:String(row.readingTitle||''),readingCompletedAt:row.readingCompletedAt});return map;
|
|
|
+ const [counts,completedRows]=await Promise.all([
|
|
|
+ Psql.query('SELECT a."createOptions"#>>\'{params,sourceStudyId}\' AS "sourceStudyId",COUNT(*)::int AS "readingArticleCount" FROM "SurveyItem" a WHERE CAST(a."user" AS text)=ANY($1::text[]) AND a."type"=\'reading\' AND COALESCE(a."isDeleted",FALSE)=FALSE AND a."createOptions"#>>\'{params,sourceStudyId}\'=ANY($2::text[]) GROUP BY a."createOptions"#>>\'{params,sourceStudyId}\'',[ownerIds,studyIds]),
|
|
|
+ Psql.query('SELECT DISTINCT ON (a."createOptions"#>>\'{params,sourceStudyId}\') a."createOptions"#>>\'{params,sourceStudyId}\' AS "sourceStudyId",a."objectId" AS "readingArticleId",a."title" AS "readingTitle",completed."completedAt" AS "readingCompletedAt" FROM "SurveyItem" a JOIN LATERAL (SELECT COALESCE(l."createdAt",l."updatedAt") AS "completedAt" FROM "SurveyLog" l WHERE CAST(l."surveyItem" AS text)=ANY(ARRAY[CAST(a."objectId" AS text),\'SurveyItem$\'||CAST(a."objectId" AS text)]) AND CAST(l."user" AS text)=ANY($1::text[]) ORDER BY COALESCE(l."createdAt",l."updatedAt") DESC,l."objectId" DESC LIMIT 1) completed ON TRUE WHERE CAST(a."user" AS text)=ANY($1::text[]) AND a."type"=\'reading\' AND COALESCE(a."isDeleted",FALSE)=FALSE AND a."createOptions"#>>\'{params,sourceStudyId}\'=ANY($2::text[]) ORDER BY a."createOptions"#>>\'{params,sourceStudyId}\',completed."completedAt" DESC,COALESCE(a."createdAt",a."updatedAt") DESC',[ownerIds,studyIds]),
|
|
|
+ ]);
|
|
|
+ const map=new Map();
|
|
|
+ for(const row of counts)map.set(String(row.sourceStudyId),{readingArticleCount:Number(row.readingArticleCount)||0});
|
|
|
+ for(const row of completedRows)map.set(String(row.sourceStudyId),{...map.get(String(row.sourceStudyId)),readingArticleId:String(row.readingArticleId||''),readingTitle:String(row.readingTitle||''),readingCompletedAt:row.readingCompletedAt});
|
|
|
+ return map;
|
|
|
}
|
|
|
async function appLearningTarget(current,requestedId) {
|
|
|
const ownId=ownLegacyId(current),targetId=number(requestedId||ownId);if(!targetId)fail(400,'缺少学员 ID');if(isAdminUser(current)||targetId===ownId)return targetId;
|
|
|
@@ -2762,11 +2768,11 @@ async function appLearningOverview(input,current) {
|
|
|
Psql.query('SELECT c."objectId",c."generalId",c."title",c."createTime",c."createdAt",b.*,b."objectId" AS "bindingObjectId",n."nodeName" AS "courseTitle" FROM "CourseBinding" b LEFT JOIN "CommonModel" c ON c."company"=$1 AND c."modelId"=58 AND c."itemId"=b."id" LEFT JOIN "Node" n ON n."company"=$1 AND n."nodeId"=CASE WHEN COALESCE(b."kcid",\'\')~\'^\\d+(?:\\.0+)?$\' THEN b."kcid"::numeric ELSE -1 END WHERE b."company"=$1 AND b."yhid"=$2 AND b."enrollmentStatus"=\'active\' AND c."status"=99 AND COALESCE(n."zstatus",99)=99 ORDER BY b."id" DESC',[DEFAULT_COMPANY_ID,String(targetId)]),
|
|
|
Psql.oneOrNone('SELECT c."objectId",c."generalId",c."title",c."createTime",c."createdAt",d."id",d."pl",d."con",d."djq",d."ygg",d."dqrq",d."dsid",d."fxrl",d."xxqs",d."szmdid",d."userId",d."learned" FROM "DailyStudyRecord" d LEFT JOIN "CommonModel" c ON c."company"=$1 AND c."modelId"=56 AND c."itemId"=d."id" WHERE d."company"=$1 AND d."userId"=$2 AND COALESCE(NULLIF(d."xxqs",\'\'),\'[]\')<>\'[]\' ORDER BY COALESCE(c."createTime",c."createdAt",d."updatedAt",d."createdAt") DESC LIMIT 1',[DEFAULT_COMPANY_ID,targetId])
|
|
|
]);
|
|
|
- const normalizedRecords=records.map(appOverviewStudyRow),normalizedLatestReading=appOverviewStudyRow(latestReading),completedReadingMap=await appOverviewCompletedReadingMap(normalizedLatestReading?[...normalizedRecords,normalizedLatestReading]:normalizedRecords,targetId),attachCompletedReading=(row)=>{if(!row)return row;const reading=completedReadingMap.get(String(row.GeneralID));return reading?{...row,...reading}:row;};
|
|
|
+ const normalizedRecords=records.map(appOverviewStudyRow),normalizedLatestReading=appOverviewStudyRow(latestReading),readingMap=await appOverviewReadingMap(normalizedLatestReading?[...normalizedRecords,normalizedLatestReading]:normalizedRecords,targetId),attachReading=(row)=>{if(!row)return row;const reading=readingMap.get(String(row.GeneralID));return reading?{...row,...reading}:row;};
|
|
|
const recordDateCounts={},scheduleDateCounts={},learnedDates=new Set();for(const row of dateRows){const date=appOverviewDateKey(row.date);if(date){recordDateCounts[date]=number(row.count);learnedDates.add(date);}}
|
|
|
const today=appOverviewToday();for(const row of scheduleGroups){const date=appOverviewDateKey(row.bxrq||row.yysj);if(!date||learnedDates.has(date))continue;const count=number(row.count),status=number(row.dszt),value=scheduleDateCounts[date]||{planned:0,missed:0};if(status<30&&date<today)value.missed+=count;else value.planned+=count;scheduleDateCounts[date]=value;}
|
|
|
const total=await Psql.one('SELECT COUNT(*)::int total FROM "DailyStudyRecord" d WHERE '+recordWhere.join(' AND '),recordValues);
|
|
|
- const live=await appLiveVersionSnapshot(targetId,['learning','courses','appointments','reading'],current,legacyLearningSync);return {page:mobilePageMeta(total.total,paging),...mobileSource(),records:normalizedRecords.map(attachCompletedReading),latestReadingRecord:attachCompletedReading(normalizedLatestReading),recordDateCounts,scheduleDateCounts,schedules:schedules.map(appOverviewScheduleRow),courses:courses.map(appOverviewCourseRow),legacyLearningSync,...live};
|
|
|
+ const live=await appLiveVersionSnapshot(targetId,['learning','courses','appointments','reading'],current,legacyLearningSync);return {page:mobilePageMeta(total.total,paging),...mobileSource(),records:normalizedRecords.map(attachReading),latestReadingRecord:attachReading(normalizedLatestReading),recordDateCounts,scheduleDateCounts,schedules:schedules.map(appOverviewScheduleRow),courses:courses.map(appOverviewCourseRow),legacyLearningSync,...live};
|
|
|
}
|
|
|
async function appStudentCompanionOverview(input,current,uid,p) {
|
|
|
const history=input.history===true,legacyAppointmentSync=await syncLegacyAppointments('szyh',uid,input.refresh===true);
|