R81 补充企微回包字段取证

这个提交包含在:
2026-05-04 18:10:55 +08:00
父节点 9dbac63744
当前提交 849fd35638
共修改 3 个文件,包含 90 行新增和 7 行删除
+20
查看文件
@@ -214,10 +214,26 @@ async function exchangeWecomCode(config, code) {
details: { wecom: "userinfo_exchange_failed", payload },
});
}
const payloadKeys = payload && typeof payload === "object" ? Object.keys(payload).sort() : [];
const hasStringValue = (value) => typeof value === "string" && value.trim().length > 0;
return {
externalUserId: typeof payload.UserId === "string" && payload.UserId.trim() ? payload.UserId.trim() : null,
externalOpenUserId: typeof payload.OpenId === "string" && payload.OpenId.trim() ? payload.OpenId.trim() : null,
deviceId: typeof payload.DeviceId === "string" ? payload.DeviceId : null,
diagnostics: {
errcode: Number(payload.errcode ?? 0),
errmsg: typeof payload.errmsg === "string" ? payload.errmsg : null,
payloadKeys,
fieldPresence: {
UserId: hasStringValue(payload.UserId),
userid: hasStringValue(payload.userid),
OpenId: hasStringValue(payload.OpenId),
openid: hasStringValue(payload.openid),
external_userid: hasStringValue(payload.external_userid),
user_ticket: hasStringValue(payload.user_ticket),
corpid: hasStringValue(payload.corpid),
},
},
};
}
@@ -370,6 +386,10 @@ async function wecomAuthRoutes(fastify) {
hasUserId: false,
hasOpenId: Boolean(profile.externalOpenUserId),
hasDeviceId: Boolean(profile.deviceId),
wecomErrCode: profile.diagnostics?.errcode ?? null,
wecomErrMsg: profile.diagnostics?.errmsg ?? null,
payloadKeys: profile.diagnostics?.payloadKeys ?? [],
fieldPresence: profile.diagnostics?.fieldPresence ?? {},
},
"wecom callback missing internal user id",
);
+68 -5
查看文件
@@ -31,8 +31,8 @@ function buildPrisma(overrides = {}) {
};
}
async function buildApp({ config = {}, prisma = buildPrisma() } = {}) {
const app = Fastify();
async function buildApp({ config = {}, prisma = buildPrisma(), fastifyOptions = {} } = {}) {
const app = Fastify(fastifyOptions);
await app.register(fastifyCookie);
app.decorate("config", {
appOrigin: "https://oa.hyinda.com",
@@ -87,6 +87,18 @@ function mockWecomFetch({ userId = "wecom-user-1", openId = "wecom-open-1" } = {
});
}
function mockWecomFetchWithPayload(payload) {
global.fetch = vi.fn()
.mockResolvedValueOnce({
ok: true,
json: async () => ({ errcode: 0, access_token: "access-token" }),
})
.mockResolvedValueOnce({
ok: true,
json: async () => payload,
});
}
afterEach(() => {
global.fetch = originalFetch;
vi.restoreAllMocks();
@@ -178,8 +190,26 @@ describe("wecom auth routes R27", () => {
});
it("returns structured diagnostics when wecom callback has only open id", async () => {
mockWecomFetch({ userId: "", openId: "wecom-open-only" });
const app = await buildApp();
const logs = [];
mockWecomFetchWithPayload({
errcode: 0,
errmsg: "ok",
openid: "lowercase-openid-value",
user_ticket: "ticket-value",
corpid: "corp-id-value",
});
const app = await buildApp({
fastifyOptions: {
logger: {
level: "warn",
stream: {
write(message) {
logs.push(JSON.parse(message));
},
},
},
},
});
const state = signPayload({
exp: Date.now() + 60_000,
nonce: "nonce-openid-only",
@@ -198,7 +228,40 @@ describe("wecom auth routes R27", () => {
message: "企业微信认证失败",
details: expect.objectContaining({
wecom: "missing_user_id",
hasOpenId: true,
hasOpenId: false,
mode: "login",
}),
}));
expect(logs.some((entry) => (
entry.msg === "wecom callback missing internal user id"
&& entry.mode === "login"
&& entry.hasUserId === false
&& entry.hasOpenId === false
&& entry.hasDeviceId === false
&& entry.wecomErrCode === 0
&& entry.wecomErrMsg === "ok"
&& Array.isArray(entry.payloadKeys)
&& entry.payloadKeys.join(",") === "corpid,errcode,errmsg,openid,user_ticket"
&& entry.fieldPresence?.UserId === false
&& entry.fieldPresence?.userid === false
&& entry.fieldPresence?.OpenId === false
&& entry.fieldPresence?.openid === true
&& entry.fieldPresence?.external_userid === false
&& entry.fieldPresence?.user_ticket === true
&& entry.fieldPresence?.corpid === true
))).toBe(true);
expect(logs.some((entry) => (
entry.msg === "wecom callback missing internal user id"
&& typeof entry.requestId === "string"
))).toBe(true);
expect(response.json().error).toEqual(expect.objectContaining({
code: "AUTH_003",
message: "企业微信认证失败",
details: expect.objectContaining({
wecom: "missing_user_id",
hasOpenId: false,
mode: "login",
}),
}));