export const PLUGIN_GUID = 'asc.{C764F7D6-055D-4C7D-97FD-83D2C4440585}';
|
export const PLUGIN_VERSION = '0.1.0';
|
export const PLUGIN_BIZ_MODULE = 'dms';
|
export const PLUGIN_BIZ_SCENE = 'dms.qianpiye';
|
export const PLUGIN_SCOPE = 'onlyoffice:dms-qianpiye';
|
|
interface QianpiyePluginContextBase {
|
apiBaseUrl: string;
|
bizDataId?: string;
|
bizModule: typeof PLUGIN_BIZ_MODULE;
|
bizScene: typeof PLUGIN_BIZ_SCENE;
|
documentId: string;
|
pluginVersion: typeof PLUGIN_VERSION;
|
}
|
|
export type QianpiyePluginContext = QianpiyePluginContextBase & (
|
| { canAnnotate: false }
|
| { canAnnotate: true; scope: typeof PLUGIN_SCOPE; ticket: string }
|
);
|
|
export type WritableQianpiyePluginContext = Extract<QianpiyePluginContext, { canAnnotate: true }>;
|
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
}
|
|
function isNonEmptyString(value: unknown): value is string {
|
return typeof value === 'string' && value.trim().length > 0;
|
}
|
|
function isHttpUrl(value: unknown): value is string {
|
if (!isNonEmptyString(value)) return false;
|
try {
|
const url = new URL(value);
|
return ['http:', 'https:'].includes(url.protocol) && !url.username && !url.password && !url.search && !url.hash;
|
} catch {
|
return false;
|
}
|
}
|
|
function parseOptions(options: unknown): unknown {
|
if (typeof options !== 'string') return options;
|
try {
|
return JSON.parse(options) as unknown;
|
} catch {
|
return undefined;
|
}
|
}
|
|
function resolvePluginOptions(options: unknown): unknown {
|
const value = parseOptions(options);
|
if (!isRecord(value) || 'apiBaseUrl' in value) return value;
|
return parseOptions(value[PLUGIN_GUID]);
|
}
|
|
function validationFailure(value: unknown): string | undefined {
|
if (!isRecord(value)) return '未收到插件上下文';
|
const invalidFields = [
|
!isHttpUrl(value.apiBaseUrl) ? 'apiBaseUrl' : '',
|
!isNonEmptyString(value.documentId) ? 'documentId' : '',
|
value.bizModule !== PLUGIN_BIZ_MODULE ? 'bizModule' : '',
|
value.bizScene !== PLUGIN_BIZ_SCENE ? 'bizScene' : '',
|
value.pluginVersion !== PLUGIN_VERSION ? 'pluginVersion' : '',
|
typeof value.canAnnotate !== 'boolean' ? 'canAnnotate' : '',
|
value.bizDataId !== undefined && value.bizDataId !== null && !isNonEmptyString(value.bizDataId)
|
? 'bizDataId'
|
: '',
|
].filter(Boolean);
|
if (value.canAnnotate === true) {
|
if (value.scope !== PLUGIN_SCOPE) invalidFields.push('scope');
|
if (!isNonEmptyString(value.ticket)) invalidFields.push('ticket');
|
}
|
return invalidFields.length > 0 ? `缺少或无效字段:${invalidFields.join(', ')}` : undefined;
|
}
|
|
export function parsePluginContext(options: unknown): QianpiyePluginContext {
|
const value = resolvePluginOptions(options);
|
if (!isRecord(value)) {
|
throw new Error('签批页标注插件运行上下文无效(未收到插件上下文)');
|
}
|
const failure = validationFailure(value);
|
if (failure) {
|
throw new Error(`签批页标注插件运行上下文无效(${failure})`);
|
}
|
const apiBaseUrl = value.apiBaseUrl;
|
const documentId = value.documentId;
|
const bizDataId = value.bizDataId;
|
const canAnnotate = value.canAnnotate;
|
if (!isHttpUrl(apiBaseUrl) || !isNonEmptyString(documentId) || typeof canAnnotate !== 'boolean') {
|
throw new Error('签批页标注插件运行上下文无效(字段校验失败)');
|
}
|
const base: QianpiyePluginContextBase = {
|
apiBaseUrl: apiBaseUrl.replace(/\/+$/u, ''),
|
documentId,
|
...(isNonEmptyString(bizDataId) ? { bizDataId } : {}),
|
bizModule: PLUGIN_BIZ_MODULE,
|
bizScene: PLUGIN_BIZ_SCENE,
|
pluginVersion: PLUGIN_VERSION,
|
};
|
if (!canAnnotate) {
|
return { ...base, canAnnotate: false };
|
}
|
const ticket = value.ticket;
|
if (value.scope !== PLUGIN_SCOPE || !isNonEmptyString(ticket)) {
|
throw new Error('签批页标注插件运行上下文无效(缺少或无效字段:scope, ticket)');
|
}
|
return {
|
...base,
|
canAnnotate: true,
|
scope: PLUGIN_SCOPE,
|
ticket,
|
};
|
}
|