675 行
21 KiB
JavaScript
675 行
21 KiB
JavaScript
const { getWecomAppAccessToken, sanitizeWecomResponse } = require("./wecomAccessToken");
|
|
|
|
const WECOM_API_BASE = "https://qyapi.weixin.qq.com";
|
|
const APPROVAL_GET_INFO_ENDPOINT = "/cgi-bin/oa/getapprovalinfo";
|
|
const APPROVAL_CREATE_TEMPLATE_ENDPOINT = "/cgi-bin/oa/approval/create_template";
|
|
const APPROVAL_APPLY_EVENT_ENDPOINT = "/cgi-bin/oa/applyevent";
|
|
|
|
const APPROVAL_POC_TARGETS = Object.freeze([
|
|
{ key: "leave", label: "请假" },
|
|
{ key: "missed_punch", label: "补卡" },
|
|
]);
|
|
const APPROVAL_HR_ATTENDANCE_TARGETS = Object.freeze([
|
|
...APPROVAL_POC_TARGETS,
|
|
{ key: "outing", label: "外出" },
|
|
{ key: "overtime", label: "加班" },
|
|
]);
|
|
|
|
class WecomApprovalPocError extends Error {
|
|
constructor(code, message, details = {}) {
|
|
super(message);
|
|
this.name = "WecomApprovalPocError";
|
|
this.code = code;
|
|
this.details = details;
|
|
}
|
|
}
|
|
|
|
function toSafeString(value, maxLength = 500) {
|
|
if (value == null) return "";
|
|
return String(value).trim().slice(0, maxLength);
|
|
}
|
|
|
|
function sanitizeErrorMessage(value) {
|
|
return toSafeString(value, 500)
|
|
.replace(/access_token=[^&\s]+/gi, "access_token=***")
|
|
.replace(/[A-Za-z0-9_-]{24,}/g, "***")
|
|
.slice(0, 240);
|
|
}
|
|
|
|
function maskApprovalIdentifier(value) {
|
|
const raw = toSafeString(value, 160);
|
|
if (!raw) return null;
|
|
if (raw.length <= 6) return `${raw.slice(0, 2)}***`;
|
|
return `${raw.slice(0, Math.min(8, raw.length))}***`;
|
|
}
|
|
|
|
function countBy(items, selector) {
|
|
const counts = {};
|
|
for (const item of items) {
|
|
const key = toSafeString(selector(item), 80) || "unknown";
|
|
counts[key] = (counts[key] || 0) + 1;
|
|
}
|
|
return counts;
|
|
}
|
|
|
|
function normalizeControls(value) {
|
|
if (!value) return [];
|
|
if (Array.isArray(value)) return value.filter((item) => item && typeof item === "object");
|
|
if (typeof value === "object") {
|
|
for (const key of ["controls", "contents", "items", "children"]) {
|
|
if (Array.isArray(value[key])) return normalizeControls(value[key]);
|
|
}
|
|
}
|
|
return [];
|
|
}
|
|
|
|
function controlType(control) {
|
|
if (!control || typeof control !== "object") return "unknown";
|
|
return (
|
|
control.control
|
|
|| control.control_type
|
|
|| control.controlType
|
|
|| control.type
|
|
|| control.property?.control
|
|
|| control.property?.control_type
|
|
|| control.property?.type
|
|
|| "unknown"
|
|
);
|
|
}
|
|
|
|
function summarizeTemplatePayload(payload) {
|
|
const templateId = payload?.template_id ?? payload?.templateId;
|
|
const controls = normalizeControls(payload?.template_content?.controls)
|
|
.concat(normalizeControls(payload?.template_content?.contents))
|
|
.concat(normalizeControls(payload?.controls));
|
|
const dedupedControls = controls.length ? controls : normalizeControls(payload?.template_content);
|
|
|
|
return {
|
|
templateIdPrefix: maskApprovalIdentifier(templateId),
|
|
fieldCount: dedupedControls.length,
|
|
fieldTypeCounts: countBy(dedupedControls, controlType),
|
|
hasTemplateNames: Array.isArray(payload?.template_names) && payload.template_names.length > 0,
|
|
};
|
|
}
|
|
|
|
function summarizeApprovalPayload(payload) {
|
|
const info = payload?.info && typeof payload.info === "object" ? payload.info : {};
|
|
const applyData = info.apply_data && typeof info.apply_data === "object" ? info.apply_data : {};
|
|
const contents = Array.isArray(applyData.contents) ? applyData.contents : [];
|
|
const records = Array.isArray(info.sp_record) ? info.sp_record : [];
|
|
const notifyers = Array.isArray(info.notifyer) ? info.notifyer : [];
|
|
const comments = Array.isArray(info.comments) ? info.comments : [];
|
|
|
|
return {
|
|
spNoPrefix: maskApprovalIdentifier(info.sp_no ?? payload?.sp_no),
|
|
templateIdPrefix: maskApprovalIdentifier(info.template_id),
|
|
spName: toSafeString(info.sp_name ?? info.spName, 120) || null,
|
|
spStatus: info.sp_status == null ? null : toSafeString(info.sp_status, 40),
|
|
applyFieldCount: contents.length,
|
|
applyFieldTypeCounts: countBy(contents, controlType),
|
|
approvalNodeCount: records.length,
|
|
notifyerCount: notifyers.length,
|
|
commentCount: comments.length,
|
|
};
|
|
}
|
|
|
|
function extractApprovalInfoSpNos(payload) {
|
|
if (!payload || typeof payload !== "object") return [];
|
|
const candidates = [
|
|
payload.sp_no_list,
|
|
payload.spNoList,
|
|
payload.info?.sp_no_list,
|
|
];
|
|
const list = candidates.find((item) => Array.isArray(item)) || [];
|
|
return list.map((item) => toSafeString(item, 160)).filter(Boolean);
|
|
}
|
|
|
|
function extractApprovalInfoNextCursor(payload) {
|
|
if (!payload || typeof payload !== "object") return "";
|
|
return toSafeString(payload.next_cursor ?? payload.nextCursor ?? payload.info?.next_cursor, 160);
|
|
}
|
|
|
|
function summarizeApprovalInfoPayload(payload) {
|
|
const spNos = extractApprovalInfoSpNos(payload);
|
|
const nextCursor = extractApprovalInfoNextCursor(payload);
|
|
return {
|
|
spNoCount: spNos.length,
|
|
spNoPrefixes: spNos.map(maskApprovalIdentifier),
|
|
nextCursorPresent: Boolean(nextCursor),
|
|
};
|
|
}
|
|
|
|
function sanitizeApprovalApiResponse(payload) {
|
|
if (!payload || typeof payload !== "object") return null;
|
|
const result = sanitizeWecomResponse(payload) ?? {};
|
|
if (payload.template_id || payload.templateId) {
|
|
result.templateIdPrefix = maskApprovalIdentifier(payload.template_id ?? payload.templateId);
|
|
}
|
|
if (payload.sp_no || payload.spNo) {
|
|
result.spNoPrefix = maskApprovalIdentifier(payload.sp_no ?? payload.spNo);
|
|
}
|
|
if (payload.template_content) {
|
|
const summary = summarizeTemplatePayload(payload);
|
|
result.fieldCount = summary.fieldCount;
|
|
result.fieldTypeCounts = summary.fieldTypeCounts;
|
|
}
|
|
if (payload.info) {
|
|
const summary = summarizeApprovalPayload(payload);
|
|
result.spStatus = summary.spStatus;
|
|
result.applyFieldCount = summary.applyFieldCount;
|
|
result.approvalNodeCount = summary.approvalNodeCount;
|
|
result.notifyerCount = summary.notifyerCount;
|
|
result.commentCount = summary.commentCount;
|
|
}
|
|
if (Array.isArray(payload.sp_no_list) || Array.isArray(payload.spNoList)) {
|
|
const summary = summarizeApprovalInfoPayload(payload);
|
|
result.spNoCount = summary.spNoCount;
|
|
result.spNoPrefixes = summary.spNoPrefixes;
|
|
result.nextCursorPresent = summary.nextCursorPresent;
|
|
}
|
|
return Object.keys(result).length ? result : null;
|
|
}
|
|
|
|
function classifyWecomApprovalResult(result, fallbackCode = "wecom_approval_api_error") {
|
|
if (result?.networkError) {
|
|
return {
|
|
code: result.errorMessage === "timeout" ? "wecom_approval_timeout" : "wecom_approval_network_error",
|
|
message: result.errorMessage || "WeCom approval API network error",
|
|
response: null,
|
|
};
|
|
}
|
|
|
|
const errcode = Number(result?.payload?.errcode);
|
|
if (Number.isFinite(errcode) && errcode !== 0) {
|
|
return {
|
|
code: "wecom_approval_api_error",
|
|
message: `WeCom approval API errcode ${errcode}`,
|
|
response: sanitizeApprovalApiResponse(result.payload),
|
|
};
|
|
}
|
|
|
|
return {
|
|
code: result?.httpStatus ? `wecom_approval_http_${result.httpStatus}` : fallbackCode,
|
|
message: "WeCom approval API failed",
|
|
response: sanitizeApprovalApiResponse(result?.payload),
|
|
};
|
|
}
|
|
|
|
async function fetchWecomApprovalJson(pathName, accessToken, body, options = {}) {
|
|
const url = new URL(`${WECOM_API_BASE}${pathName}`);
|
|
url.searchParams.set("access_token", accessToken);
|
|
|
|
const timeoutMs = Math.max(500, Number(options.timeoutMs ?? 10000));
|
|
const controller = new AbortController();
|
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
try {
|
|
const response = await (options.fetchImpl ?? fetch)(url, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(body ?? {}),
|
|
signal: controller.signal,
|
|
});
|
|
const payload = await response.json().catch(() => null);
|
|
const errcode = payload && typeof payload === "object" ? Number(payload.errcode) : NaN;
|
|
return {
|
|
ok: response.ok && Number.isFinite(errcode) && errcode === 0,
|
|
httpStatus: response.status,
|
|
payload,
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
ok: false,
|
|
httpStatus: null,
|
|
payload: null,
|
|
networkError: true,
|
|
errorMessage: error?.name === "AbortError" ? "timeout" : sanitizeErrorMessage(error?.message ?? error),
|
|
};
|
|
} finally {
|
|
clearTimeout(timer);
|
|
}
|
|
}
|
|
|
|
async function getApprovalAccessToken({ config, fetchImpl, timeoutMs }) {
|
|
const tokenResult = await getWecomAppAccessToken({
|
|
config: {
|
|
...config,
|
|
wecomRequireAgentId: false,
|
|
},
|
|
timeoutMs,
|
|
fetchImpl,
|
|
});
|
|
|
|
if (!tokenResult.ok) {
|
|
throw new WecomApprovalPocError(tokenResult.code, tokenResult.message || "WeCom app token unavailable", {
|
|
response: tokenResult.response,
|
|
});
|
|
}
|
|
|
|
return {
|
|
accessToken: tokenResult.accessToken,
|
|
tokenSource: "WECOM_AGENT_SECRET",
|
|
response: tokenResult.response,
|
|
};
|
|
}
|
|
|
|
async function fetchWecomApprovalTemplateDetail({
|
|
config,
|
|
templateId,
|
|
fetchImpl = fetch,
|
|
timeoutMs = config?.wecomRequestTimeoutMs ?? 10000,
|
|
}) {
|
|
const resolvedTemplateId = toSafeString(templateId, 160);
|
|
if (!resolvedTemplateId) {
|
|
return {
|
|
ok: false,
|
|
skipped: true,
|
|
retryable: false,
|
|
code: "approval_template_id_missing",
|
|
message: "Missing approval template id",
|
|
tokenSource: "WECOM_AGENT_SECRET",
|
|
response: null,
|
|
summary: null,
|
|
};
|
|
}
|
|
|
|
try {
|
|
const token = await getApprovalAccessToken({ config, fetchImpl, timeoutMs });
|
|
const result = await fetchWecomApprovalJson("/cgi-bin/oa/gettemplatedetail", token.accessToken, {
|
|
template_id: resolvedTemplateId,
|
|
}, { fetchImpl, timeoutMs });
|
|
|
|
if (!result.ok) {
|
|
const error = classifyWecomApprovalResult(result, "wecom_approval_template_detail_failed");
|
|
return {
|
|
ok: false,
|
|
skipped: false,
|
|
retryable: false,
|
|
code: error.code,
|
|
message: error.message,
|
|
tokenSource: token.tokenSource,
|
|
response: error.response,
|
|
summary: null,
|
|
};
|
|
}
|
|
|
|
return {
|
|
ok: true,
|
|
skipped: false,
|
|
retryable: false,
|
|
code: "wecom_approval_template_detail_fetched",
|
|
message: null,
|
|
tokenSource: token.tokenSource,
|
|
response: sanitizeApprovalApiResponse(result.payload),
|
|
summary: summarizeTemplatePayload(result.payload),
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
ok: false,
|
|
skipped: false,
|
|
retryable: Boolean(error?.details?.retryable),
|
|
code: error?.code || "wecom_approval_template_detail_failed",
|
|
message: sanitizeErrorMessage(error?.message || error),
|
|
tokenSource: "WECOM_AGENT_SECRET",
|
|
response: error?.details?.response || null,
|
|
summary: null,
|
|
};
|
|
}
|
|
}
|
|
|
|
async function fetchWecomApprovalDetail({
|
|
config,
|
|
spNo,
|
|
fetchImpl = fetch,
|
|
timeoutMs = config?.wecomRequestTimeoutMs ?? 10000,
|
|
includeRawPayload = false,
|
|
}) {
|
|
const resolvedSpNo = toSafeString(spNo, 160);
|
|
if (!resolvedSpNo) {
|
|
return {
|
|
ok: false,
|
|
skipped: true,
|
|
retryable: false,
|
|
code: "approval_sp_no_missing",
|
|
message: "Missing approval sp_no",
|
|
tokenSource: "WECOM_AGENT_SECRET",
|
|
response: null,
|
|
summary: null,
|
|
};
|
|
}
|
|
|
|
try {
|
|
const token = await getApprovalAccessToken({ config, fetchImpl, timeoutMs });
|
|
const result = await fetchWecomApprovalJson("/cgi-bin/oa/getapprovaldetail", token.accessToken, {
|
|
sp_no: resolvedSpNo,
|
|
}, { fetchImpl, timeoutMs });
|
|
|
|
if (!result.ok) {
|
|
const error = classifyWecomApprovalResult(result, "wecom_approval_detail_failed");
|
|
return {
|
|
ok: false,
|
|
skipped: false,
|
|
retryable: false,
|
|
code: error.code,
|
|
message: error.message,
|
|
tokenSource: token.tokenSource,
|
|
response: error.response,
|
|
summary: null,
|
|
};
|
|
}
|
|
|
|
return {
|
|
ok: true,
|
|
skipped: false,
|
|
retryable: false,
|
|
code: "wecom_approval_detail_fetched",
|
|
message: null,
|
|
tokenSource: token.tokenSource,
|
|
response: sanitizeApprovalApiResponse(result.payload),
|
|
rawPayload: includeRawPayload ? result.payload : undefined,
|
|
templateId: toSafeString(result.payload?.info?.template_id ?? result.payload?.info?.templateId, 160),
|
|
spName: toSafeString(result.payload?.info?.sp_name ?? result.payload?.info?.spName, 120),
|
|
summary: summarizeApprovalPayload(result.payload),
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
ok: false,
|
|
skipped: false,
|
|
retryable: Boolean(error?.details?.retryable),
|
|
code: error?.code || "wecom_approval_detail_failed",
|
|
message: sanitizeErrorMessage(error?.message || error),
|
|
tokenSource: "WECOM_AGENT_SECRET",
|
|
response: error?.details?.response || null,
|
|
summary: null,
|
|
};
|
|
}
|
|
}
|
|
|
|
function normalizeApprovalInfoFilter(filter) {
|
|
if (!filter || typeof filter !== "object") return null;
|
|
const key = toSafeString(filter.key, 80);
|
|
const value = toSafeString(filter.value, 200);
|
|
if (!key || !value) return null;
|
|
return { key, value };
|
|
}
|
|
|
|
function normalizeApprovalInfoRequest({
|
|
startTime,
|
|
endTime,
|
|
size,
|
|
nextCursor,
|
|
filters,
|
|
}) {
|
|
const normalizedStart = Math.floor(Number(startTime));
|
|
const normalizedEnd = Math.floor(Number(endTime));
|
|
const normalizedSize = Math.max(1, Math.min(100, Math.floor(Number(size || 100))));
|
|
|
|
const body = {
|
|
starttime: normalizedStart,
|
|
endtime: normalizedEnd,
|
|
size: normalizedSize,
|
|
};
|
|
const cursor = toSafeString(nextCursor, 160);
|
|
if (cursor) body.next_cursor = cursor;
|
|
|
|
const normalizedFilters = Array.isArray(filters)
|
|
? filters.map(normalizeApprovalInfoFilter).filter(Boolean)
|
|
: [];
|
|
if (normalizedFilters.length) {
|
|
body.filters = normalizedFilters;
|
|
}
|
|
return body;
|
|
}
|
|
|
|
async function fetchWecomApprovalInfoList({
|
|
config,
|
|
startTime,
|
|
endTime,
|
|
size = 100,
|
|
nextCursor = "",
|
|
filters = [],
|
|
fetchImpl = fetch,
|
|
timeoutMs = config?.wecomRequestTimeoutMs ?? 10000,
|
|
}) {
|
|
const normalizedStart = Math.floor(Number(startTime));
|
|
const normalizedEnd = Math.floor(Number(endTime));
|
|
if (!Number.isFinite(normalizedStart) || !Number.isFinite(normalizedEnd) || normalizedStart <= 0 || normalizedEnd <= 0) {
|
|
return {
|
|
ok: false,
|
|
skipped: true,
|
|
retryable: false,
|
|
code: "approval_compensation_window_invalid",
|
|
message: "Missing or invalid approval compensation time window",
|
|
tokenSource: "WECOM_AGENT_SECRET",
|
|
endpoint: `POST ${APPROVAL_GET_INFO_ENDPOINT}`,
|
|
response: null,
|
|
spNos: [],
|
|
nextCursor: "",
|
|
summary: null,
|
|
};
|
|
}
|
|
if (normalizedStart >= normalizedEnd) {
|
|
return {
|
|
ok: false,
|
|
skipped: true,
|
|
retryable: false,
|
|
code: "approval_compensation_window_invalid",
|
|
message: "Approval compensation startTime must be before endTime",
|
|
tokenSource: "WECOM_AGENT_SECRET",
|
|
endpoint: `POST ${APPROVAL_GET_INFO_ENDPOINT}`,
|
|
response: null,
|
|
spNos: [],
|
|
nextCursor: "",
|
|
summary: null,
|
|
};
|
|
}
|
|
|
|
try {
|
|
const token = await getApprovalAccessToken({ config, fetchImpl, timeoutMs });
|
|
const requestBody = normalizeApprovalInfoRequest({
|
|
startTime: normalizedStart,
|
|
endTime: normalizedEnd,
|
|
size,
|
|
nextCursor,
|
|
filters,
|
|
});
|
|
const result = await fetchWecomApprovalJson(APPROVAL_GET_INFO_ENDPOINT, token.accessToken, requestBody, {
|
|
fetchImpl,
|
|
timeoutMs,
|
|
});
|
|
|
|
if (!result.ok) {
|
|
const error = classifyWecomApprovalResult(result, "wecom_approval_info_failed");
|
|
return {
|
|
ok: false,
|
|
skipped: false,
|
|
retryable: false,
|
|
code: error.code,
|
|
message: error.message,
|
|
tokenSource: token.tokenSource,
|
|
endpoint: `POST ${APPROVAL_GET_INFO_ENDPOINT}`,
|
|
response: error.response,
|
|
spNos: [],
|
|
nextCursor: "",
|
|
summary: null,
|
|
};
|
|
}
|
|
|
|
const spNos = extractApprovalInfoSpNos(result.payload);
|
|
const nextCursorValue = extractApprovalInfoNextCursor(result.payload);
|
|
return {
|
|
ok: true,
|
|
skipped: false,
|
|
retryable: false,
|
|
code: "wecom_approval_info_fetched",
|
|
message: null,
|
|
tokenSource: token.tokenSource,
|
|
endpoint: `POST ${APPROVAL_GET_INFO_ENDPOINT}`,
|
|
response: sanitizeApprovalApiResponse(result.payload),
|
|
spNos,
|
|
nextCursor: nextCursorValue,
|
|
summary: summarizeApprovalInfoPayload(result.payload),
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
ok: false,
|
|
skipped: false,
|
|
retryable: Boolean(error?.details?.retryable),
|
|
code: error?.code || "wecom_approval_info_failed",
|
|
message: sanitizeErrorMessage(error?.message || error),
|
|
tokenSource: "WECOM_AGENT_SECRET",
|
|
endpoint: `POST ${APPROVAL_GET_INFO_ENDPOINT}`,
|
|
response: error?.details?.response || null,
|
|
spNos: [],
|
|
nextCursor: "",
|
|
summary: null,
|
|
};
|
|
}
|
|
}
|
|
|
|
function extractTemplateId(payload) {
|
|
return toSafeString(payload?.template_id ?? payload?.templateId, 160);
|
|
}
|
|
|
|
function extractSpNo(payload) {
|
|
return toSafeString(payload?.sp_no ?? payload?.spNo, 160);
|
|
}
|
|
|
|
async function fetchWecomApprovalTemplateCreate({
|
|
config,
|
|
payload,
|
|
fetchImpl = fetch,
|
|
timeoutMs = config?.wecomRequestTimeoutMs ?? 10000,
|
|
}) {
|
|
try {
|
|
const token = await getApprovalAccessToken({ config, fetchImpl, timeoutMs });
|
|
const result = await fetchWecomApprovalJson(APPROVAL_CREATE_TEMPLATE_ENDPOINT, token.accessToken, payload, {
|
|
fetchImpl,
|
|
timeoutMs,
|
|
});
|
|
|
|
if (!result.ok) {
|
|
const error = classifyWecomApprovalResult(result, "wecom_approval_create_template_failed");
|
|
return {
|
|
ok: false,
|
|
skipped: false,
|
|
retryable: false,
|
|
code: error.code,
|
|
message: error.message,
|
|
tokenSource: token.tokenSource,
|
|
endpoint: `POST ${APPROVAL_CREATE_TEMPLATE_ENDPOINT}`,
|
|
response: error.response,
|
|
templateId: null,
|
|
summary: null,
|
|
};
|
|
}
|
|
|
|
const templateId = extractTemplateId(result.payload);
|
|
return {
|
|
ok: true,
|
|
skipped: false,
|
|
retryable: false,
|
|
code: "wecom_approval_template_created",
|
|
message: null,
|
|
tokenSource: token.tokenSource,
|
|
endpoint: `POST ${APPROVAL_CREATE_TEMPLATE_ENDPOINT}`,
|
|
response: sanitizeApprovalApiResponse(result.payload),
|
|
templateId,
|
|
summary: {
|
|
templateIdPrefix: maskApprovalIdentifier(templateId),
|
|
},
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
ok: false,
|
|
skipped: false,
|
|
retryable: Boolean(error?.details?.retryable),
|
|
code: error?.code || "wecom_approval_create_template_failed",
|
|
message: sanitizeErrorMessage(error?.message || error),
|
|
tokenSource: "WECOM_AGENT_SECRET",
|
|
endpoint: `POST ${APPROVAL_CREATE_TEMPLATE_ENDPOINT}`,
|
|
response: error?.details?.response || null,
|
|
templateId: null,
|
|
summary: null,
|
|
};
|
|
}
|
|
}
|
|
|
|
async function fetchWecomApprovalApplyEvent({
|
|
config,
|
|
payload,
|
|
fetchImpl = fetch,
|
|
timeoutMs = config?.wecomRequestTimeoutMs ?? 10000,
|
|
}) {
|
|
try {
|
|
const token = await getApprovalAccessToken({ config, fetchImpl, timeoutMs });
|
|
const result = await fetchWecomApprovalJson(APPROVAL_APPLY_EVENT_ENDPOINT, token.accessToken, payload, {
|
|
fetchImpl,
|
|
timeoutMs,
|
|
});
|
|
|
|
if (!result.ok) {
|
|
const error = classifyWecomApprovalResult(result, "wecom_approval_apply_event_failed");
|
|
return {
|
|
ok: false,
|
|
skipped: false,
|
|
retryable: false,
|
|
code: error.code,
|
|
message: error.message,
|
|
tokenSource: token.tokenSource,
|
|
endpoint: `POST ${APPROVAL_APPLY_EVENT_ENDPOINT}`,
|
|
response: error.response,
|
|
spNo: null,
|
|
summary: null,
|
|
};
|
|
}
|
|
|
|
const spNo = extractSpNo(result.payload);
|
|
return {
|
|
ok: true,
|
|
skipped: false,
|
|
retryable: false,
|
|
code: "wecom_approval_apply_event_submitted",
|
|
message: null,
|
|
tokenSource: token.tokenSource,
|
|
endpoint: `POST ${APPROVAL_APPLY_EVENT_ENDPOINT}`,
|
|
response: sanitizeApprovalApiResponse(result.payload),
|
|
spNo,
|
|
summary: {
|
|
spNoPrefix: maskApprovalIdentifier(spNo),
|
|
},
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
ok: false,
|
|
skipped: false,
|
|
retryable: Boolean(error?.details?.retryable),
|
|
code: error?.code || "wecom_approval_apply_event_failed",
|
|
message: sanitizeErrorMessage(error?.message || error),
|
|
tokenSource: "WECOM_AGENT_SECRET",
|
|
endpoint: `POST ${APPROVAL_APPLY_EVENT_ENDPOINT}`,
|
|
response: error?.details?.response || null,
|
|
spNo: null,
|
|
summary: null,
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
APPROVAL_HR_ATTENDANCE_TARGETS,
|
|
APPROVAL_POC_TARGETS,
|
|
APPROVAL_APPLY_EVENT_ENDPOINT,
|
|
APPROVAL_CREATE_TEMPLATE_ENDPOINT,
|
|
APPROVAL_GET_INFO_ENDPOINT,
|
|
WecomApprovalPocError,
|
|
fetchWecomApprovalApplyEvent,
|
|
fetchWecomApprovalDetail,
|
|
fetchWecomApprovalInfoList,
|
|
fetchWecomApprovalTemplateCreate,
|
|
fetchWecomApprovalTemplateDetail,
|
|
maskApprovalIdentifier,
|
|
sanitizeApprovalApiResponse,
|
|
summarizeApprovalInfoPayload,
|
|
summarizeApprovalPayload,
|
|
summarizeTemplatePayload,
|
|
};
|