|
@@ -204,6 +204,14 @@ async function handler(request, response) {
|
|
|
classes: ALLOWED_CLASSES.size, cloudFunctions
|
|
classes: ALLOWED_CLASSES.size, cloudFunctions
|
|
|
}});
|
|
}});
|
|
|
}
|
|
}
|
|
|
|
|
+ if (operation === 'logout') {
|
|
|
|
|
+ const sessionToken = String(input.sessionToken || '');
|
|
|
|
|
+ if (!sessionToken || sessionToken.length > 512) fail(400, '缺少有效的管理员会话');
|
|
|
|
|
+ const query = new Parse.Query('_Session'); query.equalTo('sessionToken', sessionToken); query.equalTo('user', context.current); query.limit(1);
|
|
|
|
|
+ const session = await query.first({ useMasterKey: true });
|
|
|
|
|
+ if (session) await session.destroy({ useMasterKey: true });
|
|
|
|
|
+ return response.json({ success: true, data: { revoked: Boolean(session) } });
|
|
|
|
|
+ }
|
|
|
if (operation === 'dashboard') {
|
|
if (operation === 'dashboard') {
|
|
|
const metrics = [
|
|
const metrics = [
|
|
|
['_User','用户总数'],['CourseBinding','课程绑定'],['CourseAppointment','课程预约'],['PracticeRecord','练习记录'],['LessonRecord','上课记录'],['DailyStudyRecord','每日学习'],['VocabularyWord','词库单词'],['CommonModel','内容记录']
|
|
['_User','用户总数'],['CourseBinding','课程绑定'],['CourseAppointment','课程预约'],['PracticeRecord','练习记录'],['LessonRecord','上课记录'],['DailyStudyRecord','每日学习'],['VocabularyWord','词库单词'],['CommonModel','内容记录']
|
|
@@ -1288,6 +1296,25 @@ async function handler(request, response) {
|
|
|
Chat_API: '目标缺少 ChatMsg/ChatGroup/ChatGroupUser 可删除、启停或授权的旧聊天记录'
|
|
Chat_API: '目标缺少 ChatMsg/ChatGroup/ChatGroupUser 可删除、启停或授权的旧聊天记录'
|
|
|
}; if (!Object.prototype.hasOwnProperty.call(blockers, action)) fail(400, '不支持的旧聊天动作'); fail(501, 'migration_blocked: ' + blockers[action]);
|
|
}; if (!Object.prototype.hasOwnProperty.call(blockers, action)) fail(400, '不支持的旧聊天动作'); fail(501, 'migration_blocked: ' + blockers[action]);
|
|
|
}
|
|
}
|
|
|
|
|
+ if (operation === 'legacyDesignClassBlocker') {
|
|
|
|
|
+ const action = String(input.action || ''); const blockers = {
|
|
|
|
|
+ ClassList: '目标 Schema 缺少 DesignTlpClass/Design_TlpClass 模板分类主表与历史分类数据',
|
|
|
|
|
+ ClassAdd: '目标无可定位或新建的模板分类数据类;DesignTlp 仅保留数字 classId 引用',
|
|
|
|
|
+ ClassAdd_Submit: '历史 DesignTlp 引用 classId=6,但分类 6 的名称、父级、排序和说明已丢失,不能伪造保存',
|
|
|
|
|
+ Class_API: '目标无可删除的模板分类记录,且直接处理 classId=6 会使既有 DesignTlp 引用悬空'
|
|
|
|
|
+ }; if (!Object.prototype.hasOwnProperty.call(blockers, action)) fail(400, '不支持的旧模板分类动作'); fail(501, 'migration_blocked: ' + blockers[action]);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (operation === 'legacyComBlocker') {
|
|
|
|
|
+ const action = String(input.action || ''); const blockers = {
|
|
|
|
|
+ SPwdCheck: '目标管理员会话没有旧 Manager.RandNumber 二级密码验证状态和业务消费者,不能伪造 SPWD Session',
|
|
|
|
|
+ Import: '目标与云函数运行时缺少旧 /Config/Import/{mode}.xml 导入定义',
|
|
|
|
|
+ Import_DownTlp: '缺少旧 XML 字段别名、隐藏字段、类型与默认值规则,无法等价生成 xlsx 模板',
|
|
|
|
|
+ Import_Submit: '云函数没有旧 XML+NPOI 动态表导入流水线,也不允许按客户端表名直接插入',
|
|
|
|
|
+ ImportForContent: '目标缺少旧动态模型附表结构与通用 xlsx 内容导入页',
|
|
|
|
|
+ ImportForContent_DownTlp: '目标无法从旧模型动态附表还原完整 xlsx 字段模板',
|
|
|
|
|
+ ImportForContent_Submit: '目标缺少动态模型附表批量写入流程,商品分支还缺少 ZL_Commodities 主表'
|
|
|
|
|
+ }; if (!Object.prototype.hasOwnProperty.call(blockers, action)) fail(400, '不支持的旧通用后台动作'); fail(501, 'migration_blocked: ' + blockers[action]);
|
|
|
|
|
+ }
|
|
|
if (operation === 'baikeEntries') {
|
|
if (operation === 'baikeEntries') {
|
|
|
const page = Math.max(1, Number(input.page) || 1); const pageSize = Math.min(100, Math.max(1, Number(input.pageSize) || 20)); const search = String(input.search || '').trim().toLowerCase(); const status = Number(input.status == null ? -100 : input.status); const elite = Number(input.elite == null ? -100 : input.elite); if (search.length > 100 || ![-100,0,1].includes(status) || ![-100,0,1].includes(elite)) fail(400, '百科搜索或状态筛选无效'); const fields = await schemaFor('Baike'); const query = new Parse.Query('Baike'); applyTenant(query, fields, context, input.companyId); query.limit(1000); let results = await query.find({ useMasterKey: true }); results = results.filter((entry) => (status === -100 || Number(entry.get('status')) === status) && (elite === -100 || Number(entry.get('elite')) === elite) && (!search || [entry.get('tittle'),entry.get('brief'),entry.get('contents'),entry.get('userName'),entry.get('classification')].some((value) => String(value || '').toLowerCase().includes(search)))); results.sort((left,right) => Number(right.get('id') || 0) - Number(left.get('id') || 0)); const total = results.length; results = results.slice((page - 1) * pageSize, page * pageSize); return response.json({ success: true, data: { className: 'Baike', page, pageSize, total, results: results.map(serializeObject) } });
|
|
const page = Math.max(1, Number(input.page) || 1); const pageSize = Math.min(100, Math.max(1, Number(input.pageSize) || 20)); const search = String(input.search || '').trim().toLowerCase(); const status = Number(input.status == null ? -100 : input.status); const elite = Number(input.elite == null ? -100 : input.elite); if (search.length > 100 || ![-100,0,1].includes(status) || ![-100,0,1].includes(elite)) fail(400, '百科搜索或状态筛选无效'); const fields = await schemaFor('Baike'); const query = new Parse.Query('Baike'); applyTenant(query, fields, context, input.companyId); query.limit(1000); let results = await query.find({ useMasterKey: true }); results = results.filter((entry) => (status === -100 || Number(entry.get('status')) === status) && (elite === -100 || Number(entry.get('elite')) === elite) && (!search || [entry.get('tittle'),entry.get('brief'),entry.get('contents'),entry.get('userName'),entry.get('classification')].some((value) => String(value || '').toLowerCase().includes(search)))); results.sort((left,right) => Number(right.get('id') || 0) - Number(left.get('id') || 0)); const total = results.length; results = results.slice((page - 1) * pageSize, page * pageSize); return response.json({ success: true, data: { className: 'Baike', page, pageSize, total, results: results.map(serializeObject) } });
|
|
|
}
|
|
}
|