support-requests.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445
  1. // Both mobile roles submit category 22 to Guestbook. Keep legacy, untyped requests visible.
  2. async function supportRequestsPage(context, input) {
  3. requireRole(context, ['ops-manager', 'ops-staff', 'ops-auditor']);
  4. const company = companyIdOf(context, input);
  5. if (!company) fail(400, '请先选择帐套');
  6. const kind = text(input.kind), role = text(input.role), search = text(input.search);
  7. if (kind && !['feedback', 'leave', 'legacy'].includes(kind)) fail(400, '申请类型无效');
  8. if (role && !['student', 'teacher', 'other'].includes(role)) fail(400, '提交身份无效');
  9. const from = text(input.dateFrom), to = text(input.dateTo);
  10. for (const day of [from, to]) {
  11. if (day && (!/^\d{4}-\d{2}-\d{2}$/.test(day) || !Number.isFinite(Date.parse(day)) || new Date(day).toISOString().slice(0,10) !== day)) fail(400, '日期无效');
  12. }
  13. if (from && to && from > to) fail(400, '开始日期不能晚于结束日期');
  14. const page = Math.max(1, Math.floor(number(input.page, 1)));
  15. const pageSize = Math.min(100, Math.max(1, Math.floor(number(input.pageSize, 20))));
  16. const result = await Psql.one(`WITH requests AS (
  17. SELECT g."objectId" AS id, g.userid::text AS "userId", COALESCE(g.title,'') AS title,
  18. COALESCE(g.tcontent,'') AS content, COALESCE(g.gdate,g."createdAt") AS "submittedAt",
  19. COALESCE(NULLIF(u."realName",''),NULLIF(u.nickname,''),NULLIF(u.username,''),'账号未匹配') AS "accountName",
  20. CASE WHEN g.title LIKE '【休假申请】%' THEN 'leave'
  21. WHEN g.title LIKE '【意见反馈】%' THEN 'feedback' ELSE 'legacy' END AS kind,
  22. CASE WHEN COALESCE(to_jsonb(u)->>'identityType','')='coach' OR COALESCE(u."legacyGroupId"::text,u."legacyUserData"->>'GroupID')='3' THEN 'teacher'
  23. WHEN COALESCE(to_jsonb(u)->>'identityType','')='member' OR COALESCE(u."legacyGroupId"::text,u."legacyUserData"->>'GroupID')='1' THEN 'student'
  24. ELSE 'other' END AS role
  25. FROM "Guestbook" g
  26. LEFT JOIN LATERAL (SELECT u.* FROM "_User" u WHERE u.company=g.company
  27. AND COALESCE(u."legacyUserId"::text,u."legacyUserData"->>'UserID')=g.userid::text
  28. ORDER BY u."createdAt" DESC,u."objectId" LIMIT 1) u ON TRUE
  29. WHERE g.company=$1 AND COALESCE(g.parentid,0)=0 AND COALESCE(g.status,0)<>-2
  30. AND (g.cateid=22 OR g."sourceKey" LIKE 'cloud:guestbook_add:%')
  31. ), filtered AS (
  32. SELECT * FROM requests WHERE ($2='' OR kind=$2) AND ($3='' OR role=$3)
  33. AND ($4='' OR strpos(lower(title||' '||content||' '||"accountName"||' '||COALESCE("userId",'')),lower($4))>0)
  34. AND ($5='' OR "submittedAt">=NULLIF($5,'')::date::timestamp AT TIME ZONE 'Asia/Shanghai')
  35. AND ($6='' OR "submittedAt"<(NULLIF($6,'')::date+1)::timestamp AT TIME ZONE 'Asia/Shanghai')
  36. ), paged AS (SELECT * FROM filtered ORDER BY "submittedAt" DESC NULLS LAST,id DESC LIMIT $7 OFFSET $8)
  37. SELECT COUNT(*)::int AS total,
  38. COUNT(*) FILTER(WHERE kind='feedback')::int AS feedback,
  39. COUNT(*) FILTER(WHERE kind='leave')::int AS leave,
  40. COUNT(*) FILTER(WHERE kind='legacy')::int AS legacy,
  41. COALESCE((SELECT jsonb_agg(p ORDER BY p."submittedAt" DESC NULLS LAST,p.id DESC) FROM paged p),'[]'::jsonb) AS items
  42. FROM filtered`, [company,kind,role,search.slice(0,200),from,to,pageSize,(page-1)*pageSize]);
  43. return {items:result.items, total:result.total, page, pageSize,
  44. summary:{feedback:result.feedback,leave:result.leave,legacy:result.legacy},refreshedAt:new Date().toISOString()};
  45. }