| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- import {
- GROUP_ORDER,
- aggregateReportRecords,
- extractFillMonth,
- extractPeriod,
- formatDateTime,
- getAuditTime,
- getFillTime,
- getRejectReason,
- normalizeGroupName,
- } from '@/utils/aggregateReportRecords';
- import { NOTIFY_TZLX } from '@/utils/notificationFields';
- import { buildManageRoute } from '@/utils/manageRoutes';
- const pad2 = (n) => String(n).padStart(2, '0');
- const getCurrentPeriod = () => {
- const now = new Date();
- return `${now.getFullYear()}-${pad2(now.getMonth() + 1)}`;
- };
- const getPreviousPeriod = (period) => {
- const [year, month] = period.split('-').map(Number);
- const prevMonth = month === 1 ? 12 : month - 1;
- const prevYear = month === 1 ? year - 1 : year;
- return `${prevYear}-${pad2(prevMonth)}`;
- };
- /** 账期截止:次月 1 日 23:59:59 */
- const getPeriodDeadlineDate = (period) => {
- const [year, month] = period.split('-').map(Number);
- const nextMonth = month === 12 ? 1 : month + 1;
- const nextYear = month === 12 ? year + 1 : year;
- return new Date(nextYear, nextMonth - 1, 1, 23, 59, 59);
- };
- const toTimestamp = (val) => {
- if (val == null || val === '') return 0;
- const n = Number(val);
- if (Number.isFinite(n) && n > 1e11) return n;
- const d = new Date(val);
- return Number.isNaN(d.getTime()) ? 0 : d.getTime();
- };
- const matchesGroupPeriod = (item, group, period) => {
- const g = normalizeGroupName(item.c_ssqpjt);
- if (g !== group) return false;
- const fillMonth = extractFillMonth(item);
- if (fillMonth) return fillMonth === period;
- return extractPeriod(item, period) === period;
- };
- const groupHasAnySubmission = (lists, group, period) =>
- lists.some((list) => list.some((item) => matchesGroupPeriod(item, group, period)));
- const groupHasApproved = (approvedList, group, period) =>
- approvedList.some((item) => matchesGroupPeriod(item, group, period));
- const groupHasRejected = (rejectedList, group, period) =>
- rejectedList.some((item) => matchesGroupPeriod(item, group, period));
- const buildRoute = (tab, query = {}) => buildManageRoute({ tab, ...query });
- const notifyYwid = (prefix, period, group, suffix = '') =>
- suffix ? `${prefix}:${period}:${group}:${suffix}` : `${prefix}:${period}:${group}`;
- /** 月报聚合记录唯一 ywid(账期 + 青浦集团 + 所属集团,避免同集团多条记录互相覆盖) */
- const notifyAggregateYwid = (prefix, record) => {
- const qingpuGroup = (record.qingpuGroup ?? '').trim() || 'unknown';
- const groupName = (record.group ?? '').trim() || qingpuGroup;
- return `${prefix}:${record.period}:${qingpuGroup}:${groupName}`;
- };
- /** 通知列表过滤、展示用青浦集团 */
- const notifyDisplayGroup = (record) => record.qingpuGroup || record.group;
- /** 截止前剩余天数 → c_tbtx(1=7天,2=3天,3=1天) */
- const getReminderTbtx = (daysLeft) => {
- if (daysLeft <= 0) return 0;
- if (daysLeft <= 1) return 3;
- if (daysLeft <= 3) return 2;
- if (daysLeft <= 7) return 1;
- return 0;
- };
- const reminderTitle = (period, group, daysLeft) => {
- const label = daysLeft <= 1 ? '1 天' : `${daysLeft} 天`;
- return `${period} 月报截止倒计时 ${label}(${group})`;
- };
- /**
- * 生成当前时刻所有适用的通知(幂等,同 c_ywid 不会重复入库)
- */
- export const buildNotifications = ({
- pendingList = [],
- approvedList = [],
- rejectedList = [],
- } = {}, {
- currentGroup = '',
- } = {}) => {
- const notifications = [];
- const now = Date.now();
- const currentPeriod = getCurrentPeriod();
- const deadline = getPeriodDeadlineDate(currentPeriod);
- const daysLeft = Math.ceil((deadline.getTime() - now) / (86400000));
- const reminderTbtx = getReminderTbtx(daysLeft);
- // ── 待审核 ────────────────────────────────────────────────
- aggregateReportRecords(pendingList, { fallbackPeriod: currentPeriod }).forEach((record) => {
- const group = notifyDisplayGroup(record);
- if (currentGroup && group !== currentGroup) return;
- const latest = record.items?.reduce((best, item) => {
- const t = toTimestamp(getFillTime(item));
- return t >= toTimestamp(getFillTime(best)) ? item : best;
- }, record.items?.[0]);
- const title = `${group} ${record.period} 资产月报(${record.count} 条)待审核`;
- notifications.push({
- ywid: notifyAggregateYwid('pending', record),
- time: record.submittedAt,
- type: '待审核',
- title,
- content: title,
- timestamp: toTimestamp(getFillTime(latest)),
- group,
- tzlx: NOTIFY_TZLX.PENDING,
- tbtx: 0,
- shjg: 0,
- route: buildRoute('audit', {
- sub: 'pending',
- group: record.qingpuGroup || group,
- period: record.period,
- }),
- });
- });
- // ── 已通过 ────────────────────────────────────────────────
- aggregateReportRecords(approvedList, { fallbackPeriod: currentPeriod }).forEach((record) => {
- const group = notifyDisplayGroup(record);
- if (currentGroup && group !== currentGroup) return;
- const latest = record.items?.reduce((best, item) => {
- const t = toTimestamp(getAuditTime(item));
- return t >= toTimestamp(getAuditTime(best)) ? item : best;
- }, record.items?.[0]);
- const title = `${record.period} 月报已通过(${record.group},${record.count} 条)`;
- notifications.push({
- ywid: notifyAggregateYwid('approved', record),
- time: formatDateTime(getAuditTime(latest)),
- type: '审核结果',
- title,
- content: title,
- timestamp: toTimestamp(getAuditTime(latest)),
- group,
- tzlx: NOTIFY_TZLX.AUDIT_RESULT,
- tbtx: 0,
- shjg: 1,
- route: buildRoute('archive', {
- sub: 'history',
- group: record.qingpuGroup || group,
- period: record.period,
- status: '已通过',
- }),
- });
- });
- // ── 已驳回 ────────────────────────────────────────────────
- aggregateReportRecords(rejectedList, { fallbackPeriod: currentPeriod }).forEach((record) => {
- const group = notifyDisplayGroup(record);
- if (currentGroup && group !== currentGroup) return;
- const latest = record.items?.reduce((best, item) => {
- const t = toTimestamp(getAuditTime(item));
- return t >= toTimestamp(getAuditTime(best)) ? item : best;
- }, record.items?.[0]);
- const reason = getRejectReason(latest);
- const title = `${record.period} 月报已被驳回(${record.group})${reason ? `:${reason}` : ''}`;
- notifications.push({
- ywid: notifyAggregateYwid('rejected', record),
- time: formatDateTime(getAuditTime(latest)),
- type: '审核结果',
- title,
- content: title,
- timestamp: toTimestamp(getAuditTime(latest)),
- group,
- tzlx: NOTIFY_TZLX.AUDIT_RESULT,
- tbtx: 0,
- shjg: 0,
- route: buildRoute('archive', {
- sub: 'history',
- group: record.qingpuGroup || group,
- period: record.period,
- status: '已驳回',
- }),
- });
- });
- // ── 填报提醒(截止前 7/3/1 天,尚未提交的集团)────────────
- if (reminderTbtx > 0) {
- const allLists = [pendingList, approvedList, rejectedList];
- GROUP_ORDER.forEach((group) => {
- if (currentGroup && group !== currentGroup) return;
- if (groupHasAnySubmission(allLists, group, currentPeriod)) return;
- const title = reminderTitle(currentPeriod, group, daysLeft);
- notifications.push({
- ywid: notifyYwid('reminder', currentPeriod, group, String(reminderTbtx)),
- time: formatDateTime(now),
- type: '填报提醒',
- title,
- content: title,
- timestamp: now,
- group,
- tzlx: NOTIFY_TZLX.REMINDER,
- tbtx: reminderTbtx,
- shjg: 0,
- route: buildRoute('archive'),
- });
- });
- }
- // ── 逾期催办 ──────────────────────────────────────────────
- const prevPeriod = getPreviousPeriod(currentPeriod);
- const prevDeadline = getPeriodDeadlineDate(prevPeriod);
- if (now > prevDeadline.getTime()) {
- GROUP_ORDER.forEach((group) => {
- if (currentGroup && group !== currentGroup) return;
- if (groupHasApproved(approvedList, group, prevPeriod)) return;
- const rejected = groupHasRejected(rejectedList, group, prevPeriod);
- const hasAny = groupHasAnySubmission(
- [pendingList, approvedList, rejectedList],
- group,
- prevPeriod
- );
- if (!hasAny) {
- const title = `${prevPeriod} 月报已逾期,请补报(${group})`;
- notifications.push({
- ywid: notifyYwid('overdue', prevPeriod, group),
- time: formatDateTime(prevDeadline),
- type: '逾期催办',
- title,
- content: title,
- timestamp: prevDeadline.getTime(),
- group,
- tzlx: NOTIFY_TZLX.OVERDUE,
- tbtx: 0,
- shjg: 0,
- route: buildRoute('archive'),
- });
- return;
- }
- if (rejected) {
- const title = `${prevPeriod} 月报被驳回且未重报,请尽快处理(${group})`;
- notifications.push({
- ywid: notifyYwid('overdue-reject', prevPeriod, group),
- time: formatDateTime(now),
- type: '逾期催办',
- title,
- content: title,
- timestamp: now,
- group,
- tzlx: NOTIFY_TZLX.OVERDUE,
- tbtx: 0,
- shjg: 0,
- route: buildRoute('archive', { sub: 'history', group, period: prevPeriod, status: '已驳回' }),
- });
- }
- });
- }
- // ── 租约到期/预警 ─────────────────────────────────────────
- const expiryItems = approvedList
- .filter((item) => item.status === 'red' || item.status === 'orange')
- .slice(0, 8);
- expiryItems.forEach((item) => {
- const label = item.c_bh || item.c_qymc || '资产';
- const withinOneMonth = item.status === 'red';
- const group = normalizeGroupName(item.c_ssqpjt) || '未知';
- if (currentGroup && group !== currentGroup) return;
- const title = `${label} 租约${withinOneMonth ? '将于一个月内到期' : '将于三个月内到期'},请关注`;
- notifications.push({
- ywid: `expiry:${item.id}`,
- time: formatDateTime(item.update_time || now),
- type: withinOneMonth ? '租约到期' : '租约预警',
- title,
- content: title,
- timestamp: toTimestamp(item.update_time || now),
- group,
- tzlx: withinOneMonth ? NOTIFY_TZLX.LEASE_EXPIRY : NOTIFY_TZLX.LEASE_WARNING,
- tbtx: 0,
- shjg: 0,
- route: buildRoute('assets'),
- });
- });
- return notifications.sort((a, b) => b.timestamp - a.timestamp);
- };
|