update license check

This commit is contained in:
2025-10-21 16:43:31 +08:00
parent 6649680296
commit 7a3eb40aa9
7 changed files with 110 additions and 47 deletions

View File

@@ -1,6 +1,6 @@
import i18n from '@/i18n'; import i18n from '@/i18n';
import type { Setting } from '@/types'; import type { Setting } from '@/types';
import { LicenseService } from '@/services/licenseService'; import { LicenseService } from '@/services/LicenseService';
import client from '@/plugins/kintoneClient.ts' import client from '@/plugins/kintoneClient.ts'
import { Button } from 'kintone-ui-component/lib/button'; import { Button } from 'kintone-ui-component/lib/button';
@@ -59,8 +59,7 @@ import { Button } from 'kintone-ui-component/lib/button';
{ {
expiryDialogTitle: '自定义插件到期提示', expiryDialogTitle: '自定义插件到期提示',
expiryDialogMessage: '您的自定义插件许可证已过期,请联系管理员购买新的许可证。', expiryDialogMessage: '您的自定义插件许可证已过期,请联系管理员购买新的许可证。',
warningDialogTitle: '自定义插件即将到期', // warningDialogMessage: '您的自定义插件许可证还有3天就到期了请及时续费。',
warningDialogMessage: '您的自定义插件许可证还有3天就到期了请及时续费。',
}, },
); );
}); });

View File

@@ -1,6 +1,6 @@
import i18n from '@/i18n'; import i18n from '@/i18n';
import type { Setting } from '@/types'; import type { Setting } from '@/types';
import { LicenseService } from '@/services/licenseService'; import { LicenseService } from '@/services/LicenseService';
import client from '@/plugins/kintoneClient.ts' import client from '@/plugins/kintoneClient.ts'
import { MobileButton } from 'kintone-ui-component/lib/mobile/button'; import { MobileButton } from 'kintone-ui-component/lib/mobile/button';
@@ -59,8 +59,7 @@ import { MobileButton } from 'kintone-ui-component/lib/mobile/button';
{ {
expiryDialogTitle: '自定义插件到期提示', expiryDialogTitle: '自定义插件到期提示',
expiryDialogMessage: '您的自定义插件许可证已过期,请联系管理员购买新的许可证。', expiryDialogMessage: '您的自定义插件许可证已过期,请联系管理员购买新的许可证。',
warningDialogTitle: '自定义插件即将到期', // warningDialogMessage: '您的自定义插件许可证还有3天就到期了请及时续费。',
warningDialogMessage: '您的自定义插件许可证还有3天就到期了请及时续费。',
}, },
); );
}); });

View File

@@ -1,13 +1,13 @@
import type { LicenseInfo, LicenseCheckResult } from '@/types/license'; import type { LicenseInfo, LicenseCheckResult } from '@/types/license';
import { LicenseStorage } from '@/utils/licenseStorage'; import { LicenseStorage } from '@/utils/LicenseStorage';
import { PermissionService } from '@/utils/permissions'; import { PermissionService } from '@/utils/permissions';
import { Notification } from 'kintone-ui-component/lib/notification'; import { Notification } from 'kintone-ui-component/lib/notification';
import { createApp } from 'vue'; import { MobileNotification } from 'kintone-ui-component/lib/mobile/notification';
import i18n from '@/i18n';
export class LicenseService { export class LicenseService {
// 常量定义 // 常量定义
private static readonly WARNING_DAYS_BEFORE_EXPIRY = 7; private static readonly WARNING_DAYS_BEFORE_EXPIRY = 7;
private static readonly TRIAL_DATE = 1;
private static PLUGIN_ID: string = ''; private static PLUGIN_ID: string = '';
// ============ 基础工具函数 ============ // ============ 基础工具函数 ============
@@ -45,24 +45,27 @@ export class LicenseService {
return true; return true;
} }
const today = new Date();
// 检查存储是否过期(是否同一天) // 检查存储是否过期(是否同一天)
const fetchDate = new Date(license.fetchTime).toDateString(); if (!this.isToday(license.fetchTime)) {
const todayStr = today.toDateString();
if (fetchDate !== todayStr) {
// 不是同一天,已过期 // 不是同一天,已过期
return false; return false;
} }
// 检查试用是否到期 // 检查试用是否到期
const expiredTime = new Date(license.expiredTime); const expiredTime = new Date(license.expiredTime);
if (expiredTime < today) { if (expiredTime < new Date()) {
return false; return false;
} }
return true return true
} }
static isToday(timestamp: number): boolean {
const fetchDate = new Date(timestamp).toDateString();
const todayStr = new Date().toDateString();
return fetchDate == todayStr;
}
/** /**
* 许可证验证 * 许可证验证
*/ */
@@ -160,36 +163,36 @@ export class LicenseService {
/** /**
* 显示到期警告通知 * 显示到期警告通知
*/ */
static showExpiryWarning(license: any, options?: { static async showExpiryWarning(license: any, options?: {
warningDialogTitle?: string;
warningDialogMessage?: string; warningDialogMessage?: string;
}) { }) {
const remainingDays = this.getDaysRemaining(license.expiredTime); const remainingDays = this.getDaysRemaining(license.expiredTime);
const defaultMessage = `您的插件许可证将在 ${remainingDays} 天后到期,请及时续期以避免服务中断。`; const msg = remainingDays > 0 ? ` ${remainingDays} 天后` : '今天'
const defaultMessage = `您的插件许可证将在${msg}到期,请及时续期以避免服务中断。`;
// 使用自定义消息或默认消息 // 使用自定义消息或默认消息
const message = options?.warningDialogMessage || defaultMessage; const message = options?.warningDialogMessage || defaultMessage;
// 检查是否已设置不再提醒 // 检查是否已设置不再提醒
const dontShowKey = `license_notification_dont_show_again_${this.getPluginId()}`; const settings = LicenseStorage.getSettings(this.getPluginId());
const dontShowAgain = localStorage.getItem(dontShowKey) === 'true'; if (settings.suppressMsgTime && this.isToday(settings.suppressMsgTime)) {
if (dontShowAgain) return; return
}
delete settings.suppressMsgTime
LicenseStorage.saveSetting(settings, this.getPluginId());
// 使用KUC Notification if (await kintone.isMobilePage()) {
const notification = new Notification({ const notification = new MobileNotification({
text: message, text: message,
type: 'info', });
duration: -1 // 不自动关闭 notification.open();
}); } else {
const notification = new Notification({
// 添加到页面 text: message,
const container = document.body; type: 'info',
container.appendChild(notification); });
notification.open();
// 监听关闭事件 }
notification.addEventListener('close', () => {
// 这里可以添加不再提醒的逻辑
});
} }
// ============ 主要入口函数 ============ // ============ 主要入口函数 ============
@@ -203,7 +206,6 @@ export class LicenseService {
options?: { options?: {
expiryDialogTitle?: string; expiryDialogTitle?: string;
expiryDialogMessage?: string; expiryDialogMessage?: string;
warningDialogTitle?: string;
warningDialogMessage?: string; warningDialogMessage?: string;
} }
) { ) {
@@ -233,13 +235,14 @@ export class LicenseService {
} }
// 许可证有效,如果快要到期,管理员可以看到警告 // 许可证有效,如果快要到期,管理员可以看到警告
if (isManager && licenseCheck.license && !licenseCheck.license.isPaid && this.isExpiringSoon(licenseCheck.license.expiredTime)) { if (isManager &&
licenseCheck.license &&
!licenseCheck.license.isPaid &&
this.isExpiringSoon(licenseCheck.license.expiredTime)) {
// 管理员可以看到过期弹框 // 管理员可以看到过期弹框
alert('即将过期') this.showExpiryWarning(licenseCheck.license, {
// this.showExpiryWarning(licenseCheck.license, { warningDialogMessage: options?.warningDialogMessage
// warningDialogTitle: options?.warningDialogTitle, });
// warningDialogMessage: options?.warningDialogMessage
// });
} }
// 许可证有效,可以加载插件功能 // 许可证有效,可以加载插件功能
@@ -275,7 +278,7 @@ export class LicenseService {
*/ */
private static mockCreateTrialLicense(): LicenseInfo { private static mockCreateTrialLicense(): LicenseInfo {
const expiryDate = new Date(); const expiryDate = new Date();
expiryDate.setDate(expiryDate.getDate() + 30); expiryDate.setDate(expiryDate.getDate() + this.TRIAL_DATE);
return { return {
expiredTime: expiryDate.getTime(), expiredTime: expiryDate.getTime(),
@@ -283,6 +286,7 @@ export class LicenseService {
domain: this.getDomain(), domain: this.getDomain(),
pluginId: this.getPluginId(), pluginId: this.getPluginId(),
fetchTime: new Date().getTime(), fetchTime: new Date().getTime(),
version: 1,
}; };
} }

View File

@@ -11,6 +11,12 @@ export interface LicenseInfo {
pluginId: string; pluginId: string;
// 获取许可证的时间戳 // 获取许可证的时间戳
fetchTime: number; fetchTime: number;
// 版本号
version: number;
}
export interface LicenseSetting {
suppressMsgTime?: number;
} }
export interface LicenseCheckResult { export interface LicenseCheckResult {

View File

@@ -1,6 +1,16 @@
// 导入官方 REST API 客户端的类型定义 // 导入官方 REST API 客户端的类型定义
import { KintoneFormFieldProperty } from '@kintone/rest-api-client'; import { KintoneFormFieldProperty } from '@kintone/rest-api-client';
declare global {
namespace kintone {
/**
* 判断当前页面是否为移动端页面
* @returns Promise<boolean> 返回是否为移动端页面
*/
function isMobilePage(): Promise<boolean>;
}
}
// KUC 组件事件类型定义 // KUC 组件事件类型定义
// 定义 kintone UI 组件触发事件的结构 // 定义 kintone UI 组件触发事件的结构
export interface KucEvent<T> { export interface KucEvent<T> {

View File

@@ -1,9 +1,10 @@
// 本地存储加密工具类 // 本地存储加密工具类
import { LicenseService } from '@/services/licenseService'; import { LicenseService } from '@/services/LicenseService';
import type { LicenseInfo } from '@/types/license'; import type { LicenseInfo, LicenseSetting } from '@/types/license';
export class LicenseStorage { export class LicenseStorage {
private static readonly STORAGE_KEY_PREFIX = 'alicorns_plugin_'; private static readonly STORAGE_KEY_PREFIX = 'alicorns_plugin_';
private static readonly STORAGE_SETTING_KEY_PREFIX = this.STORAGE_KEY_PREFIX + 'setting_';
/** /**
* 生成存储key * 生成存储key
@@ -38,7 +39,7 @@ export class LicenseStorage {
const parsedData: LicenseInfo = JSON.parse(storedData); const parsedData: LicenseInfo = JSON.parse(storedData);
const isValid = LicenseService.checkLicenseAvailable(parsedData) const isValid = LicenseService.checkLicenseAvailable(parsedData);
if (!isValid) { if (!isValid) {
// 获取许可证信息失败,清理存储 // 获取许可证信息失败,清理存储
this.clearLicense(pluginId); this.clearLicense(pluginId);
@@ -62,4 +63,48 @@ export class LicenseStorage {
localStorage.removeItem(key); localStorage.removeItem(key);
} }
/**
* 生成存储key
*/
private static generateSettingStorageKey(pluginId: string): string {
return `${this.STORAGE_SETTING_KEY_PREFIX}${pluginId}`;
}
/**
* 保存设置信息到本地存储
*/
static saveSetting(setting: LicenseSetting, pluginId: string): void {
try {
const key = this.generateSettingStorageKey(pluginId);
localStorage.setItem(key, JSON.stringify(setting));
} catch (error) {
console.error('Failed to save setting:', error);
throw error;
}
}
/**
* 从本地存储获取设置信息
*/
static getSettings(pluginId: string): LicenseSetting {
try {
const key = this.generateSettingStorageKey(pluginId);
const storedData = localStorage.getItem(key);
if (!storedData) return {};
return JSON.parse(storedData);
} catch (error) {
console.error('Failed to get setting:', error);
return {};
}
}
/**
* 清除设置信息
*/
static clearSetting(pluginId: string): void {
const key = this.generateSettingStorageKey(pluginId);
localStorage.removeItem(key);
}
} }

View File

@@ -10,7 +10,7 @@ export class PermissionService {
static async checkPermissions(): Promise<PermissionConfig> { static async checkPermissions(): Promise<PermissionConfig> {
try { try {
// 获取应用信息 // 获取应用信息
const appId = kintone.app.getId(); const appId = kintone.app.getId() || kintone.mobile.app.getId();
if (!appId) { if (!appId) {
return this.getDefaultPermissions(); return this.getDefaultPermissions();
} }