shinnosuke
2026-08-12 fa232086731fbabcb6df070246d1b09f68930096
talent-card-server/src/main/java/com/talentcard/server/business/auth/WeChatMiniProgramClient.java
@@ -45,8 +45,14 @@
    }
    public String exchangePhoneNumber(String code) {
        String token = clientAccessToken();
        String token = clientAccessToken(false);
        Map<String, Object> response = post("/wxa/business/getuserphonenumber?access_token=" + encode(token), Map.of("code", code.trim()));
        // 微信返回 40001 时,缓存的 access_token 可能已被其它实例刷新。使用稳定
        // 令牌接口强制刷新一次并重试,避免把令牌问题误报成手机号授权失效。
        if (number(response.get("errcode"), 0) == 40001) {
            token = clientAccessToken(true);
            response = post("/wxa/business/getuserphonenumber?access_token=" + encode(token), Map.of("code", code.trim()));
        }
        ensureSuccess(response, "手机号授权已失效,请重新授权");
        Object phoneInfo = response.get("phone_info");
        if (!(phoneInfo instanceof Map<?, ?> raw)) throw new BusinessException(ErrorCode.CONFLICT, "未获取到微信手机号,请重新授权");
@@ -56,16 +62,24 @@
        return phone;
    }
    private String clientAccessToken() {
    private String clientAccessToken(boolean forceRefresh) {
        long now = System.currentTimeMillis();
        if (!accessToken.isBlank() && now < accessTokenExpiresAt) return accessToken;
        if (!forceRefresh && !accessToken.isBlank() && now < accessTokenExpiresAt) return accessToken;
        synchronized (this) {
            now = System.currentTimeMillis();
            if (!accessToken.isBlank() && now < accessTokenExpiresAt) return accessToken;
            Map<String, Object> response = get("/cgi-bin/token", Map.of(
                    "grant_type", "client_credential",
            if (!forceRefresh && !accessToken.isBlank() && now < accessTokenExpiresAt) return accessToken;
            Map<String, Object> response = post("/cgi-bin/stable_token", Map.of(
                    "appid", required(properties.getAppId()),
                    "secret", required(properties.getAppSecret())));
                    "secret", required(properties.getAppSecret()),
                    "grant_type", "client_credential",
                    "force_refresh", forceRefresh));
            // 兼容旧微信接口/网关:稳定令牌接口不可用时再回退到旧接口。
            if (number(response.get("errcode"), 0) != 0 && !forceRefresh) {
                response = get("/cgi-bin/token", Map.of(
                "grant_type", "client_credential",
                "appid", required(properties.getAppId()),
                "secret", required(properties.getAppSecret())));
            }
            ensureSuccess(response, "微信服务暂不可用,请稍后重试");
            String token = text(response.get("access_token"));
            long expiresIn = number(response.get("expires_in"), 7200);