WACP3 实现企微审批补偿拉取 POC

这个提交包含在:
2026-05-19 19:37:51 +08:00
父节点 e149a73192
当前提交 37ac4f592f
共修改 14 个文件,包含 1369 行新增和 17 行删除
@@ -1,6 +1,7 @@
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";
@@ -106,6 +107,32 @@ function summarizeApprovalPayload(payload) {
};
}
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) ?? {};
@@ -128,6 +155,12 @@ function sanitizeApprovalApiResponse(payload) {
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;
}
@@ -341,6 +374,148 @@ async function fetchWecomApprovalDetail({
}
}
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);
}
@@ -473,13 +648,16 @@ module.exports = {
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,
};