文件
OmniCard/mpuniapp/common/calendar.uts
wikiboss 2d3ff2f5bd feat(mpuniapp): 落地微官网商务日程预约与日历排期功能 (TDD)
- 新增 common/calendar.uts 日历工具,支持 Google Calendar、Outlook Live 与后端 RFC 5545 ICS 一键下载
- 扩展 common/types.uts,支持 appointment 卡片类型与日历预约参数契约
- 补齐 common/locale.uts 全球 7 种语言(含阿语 RTL)商务预约词条
- 在 pages/portal/index.uvue 中集成 Bento 预约卡片与底部预约抽屉 (Bottom Sheet)
- 在 pages/portal/usePortal.uts 中增加预约雷达轨迹上报 (touchType: appointment) 闭环联动 CRM
- 新增 mpuniapp/tests/appointment.test.ts 自动化单元测试套件并通过全部验证
2026-09-21 15:16:39 +08:00

121 行
3.9 KiB
Plaintext

/**
/**
* OmniCard AI (CRMmp) - 商务日程预约与日历服务 (UTS 强类型版本)
* 严格对齐 backend/src/modules/card/service/calendar.ts 规范
*/
import { MeetingInviteOptions, CalendarInviteBundle } from './types.uts';
import { getBaseUrl } from './request.uts';
/**
* 格式化 UTC 时间为 RFC 5545 日期时间戳 (YYYYMMDDTHHmmssZ)
*/
export function formatIcsDate(date: Date | string | number): string {
const d = date instanceof Date ? date : new Date(date);
if (isNaN(d.getTime())) {
return '';
}
const pad = (n: number): string => (n < 10 ? '0' + n : String(n));
const year = d.getUTCFullYear();
const month = pad(d.getUTCMonth() + 1);
const day = pad(d.getUTCDate());
const hours = pad(d.getUTCHours());
const minutes = pad(d.getUTCMinutes());
const seconds = pad(d.getUTCSeconds());
return `${year}${month}${day}T${hours}${minutes}${seconds}Z`;
}
/**
* 格式化为 ISO 8601 字符串 (用于 Outlook)
*/
export function toIsoDate(date: Date | string | number): string {
const d = date instanceof Date ? date : new Date(date);
if (isNaN(d.getTime())) {
return '';
}
return d.toISOString();
}
/**
* 生成 Google Calendar Web 一键预约 URL
*/
export function generateGoogleCalendarUrl(meeting: MeetingInviteOptions): string {
const summary = meeting.summary || meeting.title || '商务洽谈';
const dtStart = formatIcsDate(meeting.startTime);
const dtEnd = formatIcsDate(meeting.endTime);
let details = meeting.description || '';
if (meeting.organizerEmail) {
details += `\nOrganizer: ${meeting.organizerEmail}`;
}
const queryParts: string[] = [
'action=TEMPLATE',
`text=${encodeURIComponent(summary)}`,
`dates=${encodeURIComponent(`${dtStart}/${dtEnd}`)}`,
`details=${encodeURIComponent(details.trim())}`,
`location=${encodeURIComponent(meeting.location || '')}`,
];
return `https://calendar.google.com/calendar/render?${queryParts.join('&')}`;
}
/**
* 生成 Outlook Live 一键预约 URL
*/
export function generateOutlookCalendarUrl(meeting: MeetingInviteOptions): string {
const summary = meeting.summary || meeting.title || '商务洽谈';
const startDt = toIsoDate(meeting.startTime);
const endDt = toIsoDate(meeting.endTime);
const queryParts: string[] = [
'path=%2Fcalendar%2Faction%2Fcompose',
'rru=addevent',
`subject=${encodeURIComponent(summary)}`,
`startdt=${encodeURIComponent(startDt)}`,
`enddt=${encodeURIComponent(endDt)}`,
`body=${encodeURIComponent(meeting.description || '')}`,
`location=${encodeURIComponent(meeting.location || '')}`,
];
return `https://outlook.live.com/calendar/0/deeplink/compose?${queryParts.join('&')}`;
}
/**
* 获取后端 RFC 5545 ICS 订阅文件下载 URL
*/
export function getMeetingIcsDownloadUrl(meeting: MeetingInviteOptions): string {
const baseUrl = getBaseUrl();
const summary = meeting.summary || meeting.title || '商务洽谈';
const dtStart = formatIcsDate(meeting.startTime);
const dtEnd = formatIcsDate(meeting.endTime);
const queryParts: string[] = [
`summary=${encodeURIComponent(summary)}`,
`startTime=${encodeURIComponent(dtStart)}`,
`endTime=${encodeURIComponent(dtEnd)}`,
`description=${encodeURIComponent(meeting.description || '')}`,
`location=${encodeURIComponent(meeting.location || '')}`,
];
if (meeting.organizerName) {
queryParts.push(`organizerName=${encodeURIComponent(meeting.organizerName)}`);
}
if (meeting.organizerEmail) {
queryParts.push(`organizerEmail=${encodeURIComponent(meeting.organizerEmail)}`);
}
return `${baseUrl}/open/card/portal/meeting-ics?${queryParts.join('&')}`;
}
/**
* 综合生成全平台日历预约包
*/
export function buildCalendarInviteBundle(meeting: MeetingInviteOptions): CalendarInviteBundle {
return {
googleUrl: generateGoogleCalendarUrl(meeting),
outlookUrl: generateOutlookCalendarUrl(meeting),
icsUrl: getMeetingIcsDownloadUrl(meeting),
};
}