|
|
@@ -17,7 +17,7 @@ const CLASS_LABELS = {
|
|
|
const ALLOWED_CLASSES = new Set([
|
|
|
'PageTemplate','PaperQuestions','PayPlat','Permission','PlatComp','App','DesignAnswer','DesignAsk','Attachment','DesignPage','DesignQuestion','DesignRes','Feedback','DesignScence','StudentAchieve','ContentArticle','DesignSiteInfo','Profile','AdInfo','AdZone','ARoleAuth','Baike','ExamSysQuestions','ContactInfo','VocabularyWord','AssessmentProfile','Agency','MemoryPracticeRecord','DeliveryCenter','CourseBinding','PracticeRecord','CourseAppointment','Account','SurveyItem','SurveyLog','LessonRecord','DailyStudyRecord','CommonModel','ContentPublish','Company','_Role','_User','CRMSAttr','Currency','Datadic','Datadiccategory','DesignTlp','DocModel','DocPermission','ExamClass','ExamSysPapers','ExamType','ExamPoint','ExTeacher','FontPicShape','FontPicShapeType','Grade','GradeCate','Group','GroupModel','GuestBar','Guestbook','Guestcate','MailTemp','Manager','MisProcedure','MisProLevel','MisSign','MisType','PageStyle','Model','ModelField','Node','NodeAuth','NodeModelTemplate','Product','PlatUserRole','Pub','PubTw','PubWTHD','PubZXDC','PublishNode','QuestionsKnowledge','Role','StoreProduct','SafeMobile','Search','SenTask','ServiceSeat','ShopFareTlp','ShopMoneyRegular','Special','StoreApplication','StoreStyle','SysCSSManage','SysHoliday','SysLog','Temp','ThirdPlatInfo','UserCredit','UserDummyPoint','UserFriendGroup','UserLevel','UserSIcon','UserUserPoint','UserExpDomP','UserExpHis'
|
|
|
]);
|
|
|
-const READ_ONLY_CLASSES = new Set(['_Role','Permission','PayPlat','ThirdPlatInfo']);
|
|
|
+const READ_ONLY_CLASSES = new Set(['_Role','Permission','PayPlat','ThirdPlatInfo','GuestBar']);
|
|
|
const SYSTEM_FIELDS = new Set(['objectId','createdAt','updatedAt','ACL','company','password','authData','sessionToken','legacyPasswordHash','legacyPasswordHashType','appPassword','newapiToken','fmodeApiToken','useMasterKey','adminPassword','randNumber','adminRoleKey','isAdmin','roles','role','isDisabled','legacyUserId','legacyGroupId','legacyUserData','groupId']);
|
|
|
const HIDDEN_FIELDS = new Set(['password','authData','sessionToken','legacyPasswordHash','legacyPasswordHashType','appPassword','newapiToken','fmodeApiToken','useMasterKey','adminPassword','randNumber','apiKey','appSecret','secret','token']);
|
|
|
const GENERIC_WRITE_TYPES = new Set(['String','Number','Boolean','Date','Pointer','Object','Array']);
|
|
|
@@ -28,7 +28,8 @@ const CLASS_SYSTEM_FIELDS = {
|
|
|
Model: new Set(['modelId','modelType','tableName','sourceKey','nodeId','fromModel','multiFlag','sysModel']),
|
|
|
ModelField: new Set(['fieldId','modelId','fieldName','fieldType','sourceKey','sysType','orderId']),
|
|
|
Guestbook: new Set(['gid','parentid','cateid','userid','status','sourceKey','gdate','ip']),
|
|
|
- Guestcate: new Set(['cateid','parentId','gtype','sourceKey','orderId'])
|
|
|
+ Guestcate: new Set(['cateid','parentId','gtype','sourceKey','orderId']),
|
|
|
+ GuestBar: new Set(['id','cateId','pid','replyId','replyUserId','cuser','cuname','cdate','status','sourceKey','orderFlag','postFlag'])
|
|
|
};
|
|
|
|
|
|
function inputOf(request) {
|
|
|
@@ -677,6 +678,46 @@ async function handler(request, response) {
|
|
|
for (const target of targets) await audit({ ...context, company: target.get('company') || context.company }, 'guest-category-' + action, 'Guestcate', target.id);
|
|
|
return response.json({ success: true, data: { action, updated: targets.length, results: action === 'delete' ? [] : targets.map(serializeObject) } });
|
|
|
}
|
|
|
+ if (operation === 'barBatch') {
|
|
|
+ const action = String(input.action || '');
|
|
|
+ const actions = ['audit','unaudit','recycle','recover','move','top-global','top-section','top-undo','elite','elite-undo','bottom','bottom-undo'];
|
|
|
+ if (!actions.includes(action)) fail(400, '不支持的贴吧帖子批量操作');
|
|
|
+ const requestedIds = Array.isArray(input.objectIds) ? input.objectIds : [input.objectId];
|
|
|
+ const objectIds = [...new Set(requestedIds.map((value) => String(value || '').trim()).filter(Boolean))];
|
|
|
+ if (!objectIds.length || objectIds.length > 100) fail(400, '每次请选择 1 至 100 条帖子');
|
|
|
+ const fields = await schemaFor('GuestBar');
|
|
|
+ const query = new Parse.Query('GuestBar'); applyTenant(query, fields, context, input.companyId); query.containedIn('objectId', objectIds); query.limit(objectIds.length);
|
|
|
+ const targets = await query.find({ useMasterKey: true });
|
|
|
+ if (targets.length !== objectIds.length) fail(404, '部分帖子不存在或不属于当前帐套');
|
|
|
+ let cateId = null;
|
|
|
+ if (action === 'move') {
|
|
|
+ cateId = Number(input.cateId);
|
|
|
+ if (!Number.isInteger(cateId) || cateId < 1) fail(400, '目标贴吧分类编号无效');
|
|
|
+ const companyIds = [...new Set(targets.map((target) => pointerId(target.get('company'))).filter(Boolean))];
|
|
|
+ if (companyIds.length !== 1) fail(400, '批量移动的帖子必须属于同一帐套');
|
|
|
+ const category = await Psql.oneOrNone('SELECT "objectId" FROM "Guestcate" WHERE "company"=$1 AND "cateid"=$2 AND COALESCE("gtype",0)=1 LIMIT 1', [companyIds[0], cateId]);
|
|
|
+ if (!category) fail(404, '目标贴吧分类不存在或不属于当前帐套');
|
|
|
+ }
|
|
|
+ for (const target of targets) {
|
|
|
+ if (action === 'audit' || action === 'recover') target.set('status', 99);
|
|
|
+ else if (action === 'unaudit') target.set('status', 0);
|
|
|
+ else if (action === 'recycle') target.set('status', -2);
|
|
|
+ else if (action === 'move') target.set('cateId', cateId);
|
|
|
+ else if (action === 'top-global') target.set('orderFlag', 2);
|
|
|
+ else if (action === 'top-section') target.set('orderFlag', 1);
|
|
|
+ else if (action === 'bottom') target.set('orderFlag', -1);
|
|
|
+ else if (action === 'top-undo' || action === 'bottom-undo') target.set('orderFlag', 0);
|
|
|
+ else {
|
|
|
+ const flags = String(target.get('postFlag') || '').split(',').map((value) => value.trim()).filter(Boolean);
|
|
|
+ const next = new Set(flags);
|
|
|
+ if (action === 'elite') next.add('Recommend'); else next.delete('Recommend');
|
|
|
+ target.set('postFlag', next.size ? ',' + [...next].join(',') + ',' : '');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ await Parse.Object.saveAll(targets, { useMasterKey: true });
|
|
|
+ for (const target of targets) await audit({ ...context, company: target.get('company') || context.company }, 'bar-' + action, 'GuestBar', target.id);
|
|
|
+ return response.json({ success: true, data: { action, updated: targets.length, cateId, results: targets.map(serializeObject) } });
|
|
|
+ }
|
|
|
if (operation === 'contentBatch') {
|
|
|
const action = String(input.action || '');
|
|
|
if (!['status','recycle','recover','move'].includes(action)) fail(400, '不支持的内容批量操作');
|