package com.rongyichuang.service;
|
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.databind.JsonMappingException;
|
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.rongyichuang.config.WechatConfig;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
import org.springframework.web.client.RestTemplate;
|
|
/**
|
* 微信API服务
|
*/
|
@Service
|
public class WechatApiService {
|
|
private static final Logger logger = LoggerFactory.getLogger(WechatApiService.class);
|
|
@Autowired
|
private WechatConfig wechatConfig;
|
|
@Autowired
|
private RestTemplate restTemplate;
|
|
@Autowired
|
private ObjectMapper objectMapper;
|
|
/**
|
* 通过code获取微信用户的openid和unionid
|
*/
|
public WechatUserInfo getWechatUserInfo(String code) throws JsonProcessingException, JsonMappingException {
|
logger.info("=== 开始调用微信API获取用户信息 ===");
|
logger.info("请求时间: {}", java.time.LocalDateTime.now());
|
logger.info("输入code: {}", code);
|
logger.info("code长度: {}", code != null ? code.length() : 0);
|
|
// 验证微信配置
|
if (wechatConfig.getAppSecret() == null || wechatConfig.getAppSecret().trim().isEmpty()) {
|
logger.error("❌ 微信小程序AppSecret未正确配置");
|
logger.error("当前AppSecret值: {}", wechatConfig.getAppSecret());
|
logger.error("请在application.yml中配置正确的微信小程序AppSecret");
|
throw new RuntimeException("微信小程序AppSecret未正确配置,请检查application.yml配置");
|
}
|
|
try {
|
// 构建请求URL
|
String url = String.format("%s?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
|
wechatConfig.getApi().getCode2session(),
|
wechatConfig.getAppId(),
|
wechatConfig.getAppSecret(),
|
code);
|
|
logger.info("微信API配置信息:");
|
logger.info("- API地址: {}", wechatConfig.getApi().getCode2session());
|
logger.info("- AppId: {}", wechatConfig.getAppId());
|
logger.info("- AppSecret: {}***", wechatConfig.getAppSecret() != null ?
|
wechatConfig.getAppSecret().substring(0, Math.min(8, wechatConfig.getAppSecret().length())) : "null");
|
logger.info("完整请求URL: {}", url.replaceAll("secret=[^&]*", "secret=***"));
|
|
logger.info("开始发送HTTP请求到微信API...");
|
long startTime = System.currentTimeMillis();
|
|
String response = restTemplate.getForObject(url, String.class);
|
|
long endTime = System.currentTimeMillis();
|
logger.info("微信API响应时间: {}ms", endTime - startTime);
|
logger.info("微信API原始响应: {}", response);
|
|
if (response == null || response.trim().isEmpty()) {
|
logger.error("❌ 微信API返回空响应");
|
throw new RuntimeException("微信API返回空响应");
|
}
|
|
JsonNode jsonNode = objectMapper.readTree(response);
|
logger.info("解析后的JSON节点: {}", jsonNode.toString());
|
|
// 检查是否有错误
|
if (jsonNode.has("errcode")) {
|
int errcode = jsonNode.get("errcode").asInt();
|
logger.info("微信API返回errcode: {}", errcode);
|
|
if (errcode != 0) {
|
String errmsg = jsonNode.get("errmsg").asText();
|
logger.error("❌ 微信API调用失败");
|
logger.error("错误代码: {}", errcode);
|
logger.error("错误信息: {}", errmsg);
|
logger.error("完整响应: {}", response);
|
throw new RuntimeException("微信API调用失败: " + errmsg + " (errcode: " + errcode + ")");
|
}
|
}
|
|
// 提取用户信息
|
String openid = jsonNode.has("openid") ? jsonNode.get("openid").asText() : null;
|
String sessionKey = jsonNode.has("session_key") ? jsonNode.get("session_key").asText() : null;
|
String unionid = jsonNode.has("unionid") ? jsonNode.get("unionid").asText() : null;
|
|
logger.info("=== 微信API响应解析结果 ===");
|
logger.info("openid存在: {}", openid != null);
|
logger.info("openid值: {}", openid);
|
logger.info("sessionKey存在: {}", sessionKey != null);
|
logger.info("sessionKey长度: {}", sessionKey != null ? sessionKey.length() : 0);
|
logger.info("unionid存在: {}", unionid != null);
|
logger.info("unionid值: {}", unionid);
|
|
if (openid == null || openid.trim().isEmpty()) {
|
logger.error("❌ 微信API响应中缺少openid字段");
|
logger.error("完整响应: {}", response);
|
throw new RuntimeException("微信API响应中缺少openid字段");
|
}
|
|
if (sessionKey == null || sessionKey.trim().isEmpty()) {
|
logger.error("❌ 微信API响应中缺少session_key字段");
|
logger.error("完整响应: {}", response);
|
throw new RuntimeException("微信API响应中缺少session_key字段");
|
}
|
|
logger.info("✅ 成功获取微信用户信息");
|
logger.info("openid: {}", openid);
|
logger.info("unionid: {}", unionid != null ? unionid : "未提供");
|
logger.info("sessionKey长度: {}", sessionKey.length());
|
|
return new WechatUserInfo(openid, unionid, sessionKey);
|
|
} catch (Exception e) {
|
logger.error("=== 调用微信API发生异常 ===");
|
logger.error("异常时间: {}", java.time.LocalDateTime.now());
|
logger.error("异常类型: {}", e.getClass().getSimpleName());
|
logger.error("异常信息: {}", e.getMessage());
|
logger.error("异常堆栈:", e);
|
|
if (e instanceof RuntimeException) {
|
throw e;
|
} else {
|
throw new RuntimeException("调用微信API失败: " + e.getMessage(), e);
|
}
|
}
|
}
|
|
/**
|
* 获取微信小程序access_token
|
*/
|
public String getAccessToken() throws JsonProcessingException {
|
logger.info("=== 开始获取微信小程序access_token ===");
|
|
try {
|
// 构建请求URL
|
String url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
|
wechatConfig.getAppId(),
|
wechatConfig.getAppSecret());
|
|
logger.info("请求access_token URL: {}", url.replaceAll("secret=[^&]*", "secret=***"));
|
|
String response = restTemplate.getForObject(url, String.class);
|
logger.info("access_token响应: {}", response);
|
|
if (response == null || response.trim().isEmpty()) {
|
throw new RuntimeException("获取access_token返回空响应");
|
}
|
|
JsonNode jsonNode = objectMapper.readTree(response);
|
|
// 检查是否有错误
|
if (jsonNode.has("errcode")) {
|
int errcode = jsonNode.get("errcode").asInt();
|
if (errcode != 0) {
|
String errmsg = jsonNode.get("errmsg").asText();
|
logger.error("获取access_token失败: {} (errcode: {})", errmsg, errcode);
|
throw new RuntimeException("获取access_token失败: " + errmsg + " (errcode: " + errcode + ")");
|
}
|
}
|
|
String accessToken = jsonNode.get("access_token").asText();
|
logger.info("✅ 成功获取access_token");
|
|
return accessToken;
|
|
} catch (Exception e) {
|
logger.error("获取access_token发生异常: {}", e.getMessage(), e);
|
throw new RuntimeException("获取access_token失败: " + e.getMessage(), e);
|
}
|
}
|
|
/**
|
* 使用新版API通过code直接获取手机号
|
*/
|
public WechatPhoneInfo getPhoneNumberByCode(String code) throws JsonProcessingException {
|
logger.info("=== 开始使用新版API获取手机号 ===");
|
logger.info("输入code: {}", code);
|
|
try {
|
// 1. 先获取access_token
|
String accessToken = getAccessToken();
|
|
// 2. 构建请求URL
|
String url = wechatConfig.getApi().getGetPhoneNumber() + "?access_token=" + accessToken;
|
logger.info("请求手机号URL: {}", url);
|
|
// 3. 构建请求体
|
String requestBody = String.format("{\"code\":\"%s\"}", code);
|
logger.info("请求体: {}", requestBody);
|
|
// 4. 发送POST请求
|
org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
|
headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON);
|
|
org.springframework.http.HttpEntity<String> entity = new org.springframework.http.HttpEntity<>(requestBody, headers);
|
|
String response = restTemplate.postForObject(url, entity, String.class);
|
logger.info("手机号API响应: {}", response);
|
|
if (response == null || response.trim().isEmpty()) {
|
throw new RuntimeException("获取手机号返回空响应");
|
}
|
|
JsonNode jsonNode = objectMapper.readTree(response);
|
|
// 检查是否有错误
|
if (jsonNode.has("errcode")) {
|
int errcode = jsonNode.get("errcode").asInt();
|
if (errcode != 0) {
|
String errmsg = jsonNode.get("errmsg").asText();
|
logger.error("获取手机号失败: {} (errcode: {})", errmsg, errcode);
|
|
// 根据错误码提供更友好的错误信息
|
String friendlyMessage = getFriendlyErrorMessage(errcode, errmsg);
|
throw new RuntimeException(friendlyMessage);
|
}
|
}
|
|
// 解析手机号信息
|
JsonNode phoneInfo = jsonNode.get("phone_info");
|
if (phoneInfo == null) {
|
throw new RuntimeException("响应中缺少phone_info字段");
|
}
|
|
String phoneNumber = phoneInfo.get("phoneNumber").asText();
|
String purePhoneNumber = phoneInfo.get("purePhoneNumber").asText();
|
String countryCode = phoneInfo.get("countryCode").asText();
|
|
logger.info("✅ 成功获取手机号: {}", phoneNumber);
|
|
return new WechatPhoneInfo(phoneNumber, purePhoneNumber, countryCode);
|
|
} catch (Exception e) {
|
logger.error("获取手机号发生异常: {}", e.getMessage(), e);
|
throw new RuntimeException("获取手机号失败: " + e.getMessage(), e);
|
}
|
}
|
|
/**
|
* 微信用户信息
|
*/
|
public static class WechatUserInfo {
|
private String openid;
|
private String unionid;
|
private String sessionKey;
|
|
public WechatUserInfo(String openid, String unionid, String sessionKey) {
|
this.openid = openid;
|
this.unionid = unionid;
|
this.sessionKey = sessionKey;
|
}
|
|
public String getOpenid() {
|
return openid;
|
}
|
|
public String getUnionid() {
|
return unionid;
|
}
|
|
public String getSessionKey() {
|
return sessionKey;
|
}
|
}
|
|
/**
|
* 微信手机号信息
|
*/
|
public static class WechatPhoneInfo {
|
private String phoneNumber;
|
private String purePhoneNumber;
|
private String countryCode;
|
|
public WechatPhoneInfo(String phoneNumber, String purePhoneNumber, String countryCode) {
|
this.phoneNumber = phoneNumber;
|
this.purePhoneNumber = purePhoneNumber;
|
this.countryCode = countryCode;
|
}
|
|
public String getPhoneNumber() {
|
return phoneNumber;
|
}
|
|
public String getPurePhoneNumber() {
|
return purePhoneNumber;
|
}
|
|
public String getCountryCode() {
|
return countryCode;
|
}
|
}
|
|
/**
|
* 根据微信API错误码提供友好的错误信息
|
*/
|
private String getFriendlyErrorMessage(int errcode, String errmsg) {
|
switch (errcode) {
|
case 40001:
|
return "微信授权失败:access_token无效,请重新授权";
|
case 40003:
|
return "微信授权失败:openid错误,请重新登录";
|
case 40013:
|
return "微信配置错误:AppID无效,请联系管理员";
|
case 40029:
|
return "授权码已过期或无效,请重新获取手机号授权";
|
case 47001:
|
return "请求参数错误,请重试";
|
case 61023:
|
return "授权码无效或已过期,请重新获取手机号授权";
|
case 61024:
|
return "授权码已被使用,请重新获取手机号授权";
|
case 45009:
|
return "接口调用超过限制,请稍后重试";
|
case 89503:
|
return "服务器IP未在微信白名单中,请联系管理员";
|
default:
|
return "获取手机号失败:" + errmsg + " (错误码: " + errcode + ")";
|
}
|
}
|
}
|