|
@@ -360,6 +360,13 @@ async function sqlPage(className, input, filters = []) {
|
|
|
const rows = await Psql.query('SELECT * FROM "' + className + '" WHERE ' + where + ' ORDER BY "updatedAt" DESC LIMIT $' + (values.length + 1) + ' OFFSET $' + (values.length + 2), rowValues); const total = number(countRow.total);
|
|
const rows = await Psql.query('SELECT * FROM "' + className + '" WHERE ' + where + ' ORDER BY "updatedAt" DESC LIMIT $' + (values.length + 1) + ' OFFSET $' + (values.length + 2), rowValues); const total = number(countRow.total);
|
|
|
return { rows: rows.filter(isVisible).map((row) => legacyAliases(row, className)), page: { itemCount: total, pageCount: Math.ceil(total / paging.size), pageIndex: paging.index, pageSize: paging.size } };
|
|
return { rows: rows.filter(isVisible).map((row) => legacyAliases(row, className)), page: { itemCount: total, pageCount: Math.ceil(total / paging.size), pageIndex: paging.index, pageSize: paging.size } };
|
|
|
}
|
|
}
|
|
|
|
|
+async function nodePage(input) {
|
|
|
|
|
+ const paging = pageInput(input); const values = [DEFAULT_COMPANY_ID]; const clauses = ['"company"=$1']; if (input.pid !== undefined && input.pid !== '') { values.push(String(number(input.pid))); clauses.push('CAST("parentId" AS text)=$' + values.length); } const where = clauses.join(' AND '); const countRow = await Psql.one('SELECT COUNT(*)::int AS total FROM "Node" WHERE ' + where,values); const rows = await Psql.query('SELECT * FROM "Node" WHERE ' + where + ' ORDER BY CASE WHEN CAST("orderId" AS text) ~ \'^-?[0-9]+$\' THEN CAST("orderId" AS numeric) ELSE 0 END ASC, CASE WHEN CAST("nodeId" AS text) ~ \'^-?[0-9]+$\' THEN CAST("nodeId" AS numeric) ELSE 0 END ASC LIMIT $' + (values.length + 1) + ' OFFSET $' + (values.length + 2),values.concat([paging.size,(paging.index - 1) * paging.size])); const total = number(countRow.total); return { rows:rows.filter(isVisible).map((row) => legacyAliases(row,'Node')),page:{itemCount:total,pageCount:Math.ceil(total / paging.size),pageIndex:paging.index,pageSize:paging.size} };
|
|
|
|
|
+}
|
|
|
|
|
+async function nodeUnitPage(input,current) {
|
|
|
|
|
+ const uid = await authorizeRequestedUser(current,input.userId || input.uid); const paging = pageInput(input); const parentId = number(input.pid); if (!parentId) fail(400,'缺少课程栏目 ID'); const countRow = await Psql.one('SELECT COUNT(*)::int AS total FROM "Node" WHERE "company"=$1 AND CAST("parentId" AS text)=$2',[DEFAULT_COMPANY_ID,String(parentId)]); const rows = await Psql.query('SELECT n.*,COALESCE(w.total,0)::int AS "total",COALESCE(l.learned,0)::int AS "yx_word" FROM "Node" n LEFT JOIN LATERAL (SELECT COUNT(*)::int AS total FROM "CommonModel" c WHERE c."company"=$1 AND CAST(c."modelId" AS text)=\'52\' AND CAST(c."nodeId" AS text)=CAST(n."nodeId" AS text)) w ON TRUE LEFT JOIN LATERAL (SELECT COUNT(DISTINCT CAST(p."scid" AS text))::int AS learned FROM "PracticeRecord" p JOIN "CommonModel" c ON c."company"=$1 AND CAST(c."modelId" AS text)=\'52\' AND CAST(c."generalId" AS text)=CAST(p."scid" AS text) AND CAST(c."nodeId" AS text)=CAST(n."nodeId" AS text) WHERE p."company"=$1 AND CAST(p."yhid" AS text)=$3 AND COALESCE(CAST(p."xxcs" AS numeric),0)>0) l ON TRUE WHERE n."company"=$1 AND CAST(n."parentId" AS text)=$2 ORDER BY CASE WHEN CAST(n."orderId" AS text) ~ \'^-?[0-9]+$\' THEN CAST(n."orderId" AS numeric) ELSE 0 END ASC,CASE WHEN CAST(n."nodeId" AS text) ~ \'^-?[0-9]+$\' THEN CAST(n."nodeId" AS numeric) ELSE 0 END ASC LIMIT $4 OFFSET $5',[DEFAULT_COMPANY_ID,String(parentId),String(uid),paging.size,(paging.index - 1) * paging.size]); const total = number(countRow.total); return { rows:rows.map((source) => { const row = legacyAliases(source,'Node'); row.total = number(source.total); row.yx_word = number(source.yx_word); row.process = row.total ? Math.round(row.yx_word / row.total * 100) : 0; return row; }),page:{itemCount:total,pageCount:Math.ceil(total / paging.size),pageIndex:paging.index,pageSize:paging.size} };
|
|
|
|
|
+}
|
|
|
|
|
+function legacyAppRow(source) { if (!source) return null; const row = legacyAliases(source,'App'); row.ver = number(firstValue(row.index,String(row.version || '').replace(/\D/g,''))); row.nver = String(row.version || ''); row.intro = String(firstValue(row.changelog,row.desc,'') || ''); row.path = String(firstValue(row.downUrl,row.apkUrl,'') || ''); row.size = null; row.sizeUnavailable = true; return row; }
|
|
|
function parseArray(value) { if (Array.isArray(value)) return value; if (typeof value !== 'string' || !value.trim()) return []; try { const parsed = JSON.parse(value); return Array.isArray(parsed) ? parsed : []; } catch (_) { return []; } }
|
|
function parseArray(value) { if (Array.isArray(value)) return value; if (typeof value !== 'string' || !value.trim()) return []; try { const parsed = JSON.parse(value); return Array.isArray(parsed) ? parsed : []; } catch (_) { return []; } }
|
|
|
function largePageInput(input) { return { index: Math.max(1, number(input.cpage || input.page, 1)), size: Math.min(500, Math.max(1, number(input.psize || input.pageSize, 20))) }; }
|
|
function largePageInput(input) { return { index: Math.max(1, number(input.cpage || input.page, 1)), size: Math.min(500, Math.max(1, number(input.psize || input.pageSize, 20))) }; }
|
|
|
function ownLegacyId(current) { return number(current && (current.get('legacyUserId') || current.get('userid') || current.get('num'))); }
|
|
function ownLegacyId(current) { return number(current && (current.get('legacyUserId') || current.get('userid') || current.get('num'))); }
|
|
@@ -520,19 +527,19 @@ async function handler(request, response) {
|
|
|
}
|
|
}
|
|
|
if (action === 'user_register') { const registered = await registerUser(input); return response.json(envelope(registered.result,registered.addon)); }
|
|
if (action === 'user_register') { const registered = await registerUser(input); return response.json(envelope(registered.result,registered.addon)); }
|
|
|
if (action === 'user_info_name') { const username = String(input.uname || input.name || '').trim(); if (!username) return response.status(400).json(reject('缺少用户名')); const rows = await Psql.query('SELECT "objectId" FROM "_User" WHERE "company" = $1 AND "username" = $2 LIMIT 1', [DEFAULT_COMPANY_ID, username]); const found = rows[0]; return found ? response.json(envelope({ objectId: found.objectId })) : response.status(404).json(reject('用户不存在')); }
|
|
if (action === 'user_info_name') { const username = String(input.uname || input.name || '').trim(); if (!username) return response.status(400).json(reject('缺少用户名')); const rows = await Psql.query('SELECT "objectId" FROM "_User" WHERE "company" = $1 AND "username" = $2 LIMIT 1', [DEFAULT_COMPANY_ID, username]); const found = rows[0]; return found ? response.json(envelope({ objectId: found.objectId })) : response.status(404).json(reject('用户不存在')); }
|
|
|
- const publicRead = PUBLIC_READ.has(action) && !contentQueryRequiresAuth(action, input); const current = await currentUser(request, !publicRead); if (contentQueryRequiresAuth(action, input) && current) await authorizeContentAccess(current, input);
|
|
|
|
|
|
|
+ const privateNodeProgress = action === 'node_list' && number(input.ifunit) === 1; const publicRead = PUBLIC_READ.has(action) && !contentQueryRequiresAuth(action, input) && !privateNodeProgress; const current = await currentUser(request, !publicRead); if (contentQueryRequiresAuth(action, input) && current) await authorizeContentAccess(current, input);
|
|
|
if (action === 'user_get') { const requestedId = number(input.uid || ownLegacyId(current)); let target = current; if (requestedId && requestedId !== ownLegacyId(current)) { await authorizeTeamUser(current,requestedId); target = await legacyUserObject(requestedId); if (!target) return response.status(404).json(reject('用户不存在')); } return response.json(envelope(legacyUser(target),legacyUserAddon(target))); }
|
|
if (action === 'user_get') { const requestedId = number(input.uid || ownLegacyId(current)); let target = current; if (requestedId && requestedId !== ownLegacyId(current)) { await authorizeTeamUser(current,requestedId); target = await legacyUserObject(requestedId); if (!target) return response.status(404).json(reject('用户不存在')); } return response.json(envelope(legacyUser(target),legacyUserAddon(target))); }
|
|
|
if (action === 'user_list') { const result = await teamUserPage(input,current); return response.json(envelope(result.rows,undefined,result.page)); }
|
|
if (action === 'user_list') { const result = await teamUserPage(input,current); return response.json(envelope(result.rows,undefined,result.page)); }
|
|
|
if (action === 'user_dept') return response.json(envelope(await teamStats(input,current)));
|
|
if (action === 'user_dept') return response.json(envelope(await teamStats(input,current)));
|
|
|
if (action === 'e_user_list') { const result = await coachStudentPage(input,current); return response.json(envelope(result.rows,undefined,result.page)); }
|
|
if (action === 'e_user_list') { const result = await coachStudentPage(input,current); return response.json(envelope(result.rows,undefined,result.page)); }
|
|
|
if (action === 'user_update') { const updated = await updateUserProfile(input,current); return response.json(envelope(updated.result,updated.addon)); }
|
|
if (action === 'user_update') { const updated = await updateUserProfile(input,current); return response.json(envelope(updated.result,updated.addon)); }
|
|
|
- if (action === 'app_update') { const filters = input.platform ? [{ field: 'platform', value: input.platform }] : []; const result = await sqlPage('App', { page: 1, pageSize: 1 }, filters); return response.json(envelope(result.rows[0] || null)); }
|
|
|
|
|
|
|
+ if (action === 'app_update') { const result = await sqlPage('App', { page: 1, pageSize: 1 }); return response.json(envelope(legacyAppRow(result.rows[0]))); }
|
|
|
if (action === 'content_list' || action === 'content_list_llk') { const result = await contentPage(input, !current); return response.json(envelope(result.rows, undefined, result.page)); }
|
|
if (action === 'content_list' || action === 'content_list_llk') { const result = await contentPage(input, !current); return response.json(envelope(result.rows, undefined, result.page)); }
|
|
|
if (action === 'content_get') { const id = String(input.id || input.gid || input.generalId || ''); if (!id) return response.status(400).json(reject('缺少内容 ID')); const identity = /^\d+$/.test(id) ? { generalId: id } : { objectId: id }; const base = await contentPage({ page: 1, pageSize: 1, ...identity }); let detail = base.rows[0]; if (!detail) return response.status(404).json(reject('内容不存在')); const detailModel = number(detail.modelId || detail.ModelID); if (CONTENT_ADDONS[detailModel]) detail = (await contentPage({ page: 1, pageSize: 1, modelId: detailModel, ...identity })).rows[0] || detail; if (PRIVATE_CONTENT_MODELS.has(detailModel)) { if (!current) return response.status(401).json(reject('该内容详情需要登录')); await authorizeContentDetail(current, detail); } return response.json(envelope(detail)); }
|
|
if (action === 'content_get') { const id = String(input.id || input.gid || input.generalId || ''); if (!id) return response.status(400).json(reject('缺少内容 ID')); const identity = /^\d+$/.test(id) ? { generalId: id } : { objectId: id }; const base = await contentPage({ page: 1, pageSize: 1, ...identity }); let detail = base.rows[0]; if (!detail) return response.status(404).json(reject('内容不存在')); const detailModel = number(detail.modelId || detail.ModelID); if (CONTENT_ADDONS[detailModel]) detail = (await contentPage({ page: 1, pageSize: 1, modelId: detailModel, ...identity })).rows[0] || detail; if (PRIVATE_CONTENT_MODELS.has(detailModel)) { if (!current) return response.status(401).json(reject('该内容详情需要登录')); await authorizeContentDetail(current, detail); } return response.json(envelope(detail)); }
|
|
|
if (action === 'content_uphis') { const resolved = await resolveContent(input, true); if (!resolved.object) return response.status(404).json(reject('内容不存在')); resolved.object.increment('hits', Math.max(1, number(input.num, 1))); await resolved.object.save(null, { useMasterKey: true }); return response.json(envelope({ hits: resolved.object.get('hits') })); }
|
|
if (action === 'content_uphis') { const resolved = await resolveContent(input, true); if (!resolved.object) return response.status(404).json(reject('内容不存在')); resolved.object.increment('hits', Math.max(1, number(input.num, 1))); await resolved.object.save(null, { useMasterKey: true }); return response.json(envelope({ hits: resolved.object.get('hits') })); }
|
|
|
if (action === 'content_add') return response.json(envelope(await createContentPair(input, current)));
|
|
if (action === 'content_add') return response.json(envelope(await createContentPair(input, current)));
|
|
|
if (action === 'content_update') return response.json(envelope(await updateContentPair(input, current)));
|
|
if (action === 'content_update') return response.json(envelope(await updateContentPair(input, current)));
|
|
|
- if (action === 'node_list') { const filters = input.pid !== undefined ? [{ field: 'parentId', value: number(input.pid) }] : []; const result = await sqlPage('Node', input, filters); return response.json(envelope(result.rows, undefined, result.page)); }
|
|
|
|
|
|
|
+ if (action === 'node_list') { const result = privateNodeProgress ? await nodeUnitPage(input,current) : await nodePage(input); return response.json(envelope(result.rows, undefined, result.page)); }
|
|
|
if (action === 'node_get') { const result = await sqlPage('Node', { page: 1, pageSize: 1 }, [{ field: 'nodeId', value: number(input.id || input.nid) }]); return result.rows[0] ? response.json(envelope(result.rows[0])) : response.status(404).json(reject('栏目不存在')); }
|
|
if (action === 'node_get') { const result = await sqlPage('Node', { page: 1, pageSize: 1 }, [{ field: 'nodeId', value: number(input.id || input.nid) }]); return result.rows[0] ? response.json(envelope(result.rows[0])) : response.status(404).json(reject('栏目不存在')); }
|
|
|
const legacyId = ownLegacyId(current);
|
|
const legacyId = ownLegacyId(current);
|
|
|
if (action === 'content_add_zt') return response.json(envelope(await createStudyContent(input, current)));
|
|
if (action === 'content_add_zt') return response.json(envelope(await createStudyContent(input, current)));
|