218 lines
6.1 KiB
TypeScript
218 lines
6.1 KiB
TypeScript
/** Shared contracts for the official XFT OpenAPI adapter. */
|
|
export interface AuthenticatedDingTalkUser {
|
|
userId: string;
|
|
name?: string;
|
|
}
|
|
|
|
export interface XftTodoItem {
|
|
source: "xft";
|
|
billId: string;
|
|
taskId?: string;
|
|
eventSeqId?: string;
|
|
title?: string;
|
|
applicantName?: string;
|
|
applicantDepartment?: string;
|
|
applyTime?: string;
|
|
amount?: number;
|
|
currency?: string;
|
|
reason?: string;
|
|
allowPass?: boolean;
|
|
allowReject?: boolean;
|
|
eventType?: string;
|
|
}
|
|
|
|
export type ApprovalHistoryScope = "handled" | "submitted";
|
|
|
|
export interface ApprovalHistoryRange {
|
|
startTime: number;
|
|
endTime: number;
|
|
}
|
|
|
|
export interface XftHistoryItem extends Omit<XftTodoItem, "allowPass" | "allowReject"> {
|
|
historyScope: ApprovalHistoryScope;
|
|
status?: string;
|
|
statusName?: string;
|
|
completedAt?: string;
|
|
}
|
|
|
|
export type XftApprovalAction = "agree" | "reject" | "return";
|
|
|
|
export interface XftApprovalBatchItem {
|
|
billId: string;
|
|
targetNodeId?: string;
|
|
}
|
|
|
|
export type XftApprovalBatchResultItem =
|
|
| { billId: string; status: "succeeded"; success: true }
|
|
| { billId: string; status: "failed"; success: false; failureConfirmed: true; error: string }
|
|
| {
|
|
billId: string;
|
|
status: "outcome_uncertain";
|
|
success: false;
|
|
outcomeUncertain: true;
|
|
error: string;
|
|
};
|
|
|
|
export type XftSubmissionKind =
|
|
| "travel_apply"
|
|
| "expense_apply"
|
|
| "entertainment_apply"
|
|
| "common_apply"
|
|
| "use_car_apply"
|
|
| "common_reimburse"
|
|
| "travel_reimburse";
|
|
|
|
export interface XftSubmissionTemplate {
|
|
eventSeqId: string;
|
|
eventNumber: string;
|
|
eventName: string;
|
|
eventType: string;
|
|
kind: XftSubmissionKind;
|
|
}
|
|
|
|
export interface XftSubmissionField {
|
|
fieldNumber: string;
|
|
name: string;
|
|
type: string;
|
|
required: boolean;
|
|
description?: string;
|
|
multiple?: boolean;
|
|
options?: XftSubmissionOption[];
|
|
enumLabels?: Record<string, string>;
|
|
moduleType?: string;
|
|
valueFormat?: string;
|
|
itemFields?: Array<Record<string, unknown>>;
|
|
optionSource?: {
|
|
tool: "approval_get_submission_options";
|
|
kind: XftSubmissionOptionKind;
|
|
};
|
|
selectionCapability?: XftSubmissionSelectionCapability;
|
|
}
|
|
|
|
export type XftSubmissionOptionKind = "department" | "city" | "expense_type";
|
|
|
|
export interface XftSubmissionOption {
|
|
code: string;
|
|
label: string;
|
|
}
|
|
|
|
export type XftSubmissionSelectionCapability =
|
|
| {
|
|
status: "supported";
|
|
userInput: "label";
|
|
submitValue: "code";
|
|
exposeInternalValue: false;
|
|
}
|
|
| {
|
|
status: "client_only";
|
|
reason: string;
|
|
};
|
|
|
|
export interface XftApprovalProvider {
|
|
authStatus(user: AuthenticatedDingTalkUser): Promise<{
|
|
authenticated: true;
|
|
identity: Record<string, unknown>;
|
|
binding: Record<string, unknown>;
|
|
persistedBusinessData: false;
|
|
}>;
|
|
listMyTodos(user: AuthenticatedDingTalkUser, limit: number, search?: string): Promise<{
|
|
items: XftTodoItem[];
|
|
totalCount: number;
|
|
identity: Record<string, unknown>;
|
|
diagnostics: Record<string, unknown>;
|
|
}>;
|
|
listMyHistory(
|
|
user: AuthenticatedDingTalkUser,
|
|
scope: ApprovalHistoryScope,
|
|
limit: number,
|
|
search: string,
|
|
range: ApprovalHistoryRange,
|
|
): Promise<{
|
|
items: XftHistoryItem[];
|
|
totalCount: number;
|
|
diagnostics: Record<string, unknown>;
|
|
}>;
|
|
getTodoDetail(user: AuthenticatedDingTalkUser, billId: string): Promise<Record<string, unknown>>;
|
|
getHistoryDetail(
|
|
user: AuthenticatedDingTalkUser,
|
|
scope: ApprovalHistoryScope,
|
|
billId: string,
|
|
): Promise<Record<string, unknown>>;
|
|
listReturnNodes(user: AuthenticatedDingTalkUser, billId: string): Promise<{
|
|
billId: string;
|
|
nodes: Array<{ nodeId: string; nodeName?: string; approverNames?: string[] }>;
|
|
requirements: {
|
|
commentRequired: boolean;
|
|
signatureRequired: boolean;
|
|
canSkipPreviouslyApprovedNodes: boolean;
|
|
};
|
|
diagnostics: Record<string, unknown>;
|
|
}>;
|
|
processApprovalBatch(
|
|
user: AuthenticatedDingTalkUser,
|
|
action: XftApprovalAction,
|
|
remark: string,
|
|
items: XftApprovalBatchItem[],
|
|
): Promise<{
|
|
action: XftApprovalAction;
|
|
total: number;
|
|
succeeded: number;
|
|
failed: number;
|
|
results: XftApprovalBatchResultItem[];
|
|
diagnostics: Record<string, unknown>;
|
|
}>;
|
|
listSubmissionTemplates(user: AuthenticatedDingTalkUser, limit: number, search?: string): Promise<XftSubmissionTemplate[]>;
|
|
getSubmissionSchema(user: AuthenticatedDingTalkUser, template: XftSubmissionTemplate): Promise<{
|
|
template: XftSubmissionTemplate;
|
|
fields: XftSubmissionField[];
|
|
inputContract: Record<string, unknown>;
|
|
diagnostics: Record<string, unknown>;
|
|
}>;
|
|
getSubmissionOptions(
|
|
user: AuthenticatedDingTalkUser,
|
|
template: XftSubmissionTemplate,
|
|
kind: XftSubmissionOptionKind,
|
|
query?: string,
|
|
limit?: number,
|
|
): Promise<{
|
|
kind: XftSubmissionOptionKind;
|
|
items: XftSubmissionOption[];
|
|
total?: number;
|
|
hasMore?: boolean;
|
|
diagnostics: Record<string, unknown>;
|
|
}>;
|
|
validateSubmissionIdentity(user: AuthenticatedDingTalkUser): Promise<{
|
|
validated: true;
|
|
diagnostics: Record<string, unknown>;
|
|
}>;
|
|
submitApproval(
|
|
user: AuthenticatedDingTalkUser,
|
|
template: XftSubmissionTemplate,
|
|
values: Record<string, unknown>,
|
|
): Promise<{ billId: string; status: "submitted"; diagnostics: Record<string, unknown> }>;
|
|
recallSubmission(
|
|
user: AuthenticatedDingTalkUser,
|
|
billId: string,
|
|
reason?: string,
|
|
): Promise<{ status: "revoked" | "failed" | "outcome_uncertain"; billId: string; message?: string; diagnostics: Record<string, unknown> }>;
|
|
getMySubmissionStatus(
|
|
user: AuthenticatedDingTalkUser,
|
|
billId: string,
|
|
): Promise<{
|
|
billId: string;
|
|
status: string;
|
|
statusName: string;
|
|
approvalStatus?: string;
|
|
revoked: boolean;
|
|
revokeAllowed: boolean;
|
|
flowHistory?: Array<{ nodeName?: string; actions: string[] }>;
|
|
diagnostics: Record<string, unknown>;
|
|
}>;
|
|
}
|
|
|
|
/** Success codes observed on the documented XFT OpenAPI endpoints. */
|
|
export function isXftSuccessCode(code: string | undefined): boolean {
|
|
if (code === undefined || code === "") return true;
|
|
return ["0", "200", "SUCCESS", "SUC0000", "000000", "0000000"].includes(code.toUpperCase());
|
|
}
|