"""
微信API服务
"""
import asyncio
import logging
from dataclasses import dataclass
from typing import Any, Dict
import requests
from django.conf import settings
logger = logging.getLogger(__name__)
@dataclass
class WechatUser:
"""微信用户数据"""
openid: str
nickname: str = ""
avatar: str = ""
sex: int = 0
province: str = ""
city: str = ""
country: str = ""
language: str = "zh_CN"
@classmethod
def from_api_response(cls, data: Dict[str, Any]) -> "WechatUser":
"""从API响应创建用户对象"""
return cls(
openid=data["openid"],
nickname=data.get("nickname", ""),
avatar=data.get("headimgurl", ""),
sex=data.get("sex", 0),
province=data.get("province", ""),
city=data.get("city", ""),
country=data.get("country", ""),
language=data.get("language", "zh_CN"),
)
class WechatAPIService:
"""微信API服务"""
def __init__(self):
self.base_url = "https://api.weixin.qq.com"
self.app_id = settings.WECHAT_APP_ID
self.app_secret = settings.WECHAT_APP_SECRET
async def _request(self, url: str, params: Dict[str, Any]) -> Dict[str, Any]:
"""通用请求方法"""
def _sync_request():
response = requests.get(url, params=params)
data = response.json()
if "errcode" in data:
raise ValueError(f"微信API错误: {data.get('errmsg', '未知错误')}")
return data
return await asyncio.get_event_loop().run_in_executor(None, _sync_request)
async def get_access_token(self, code: str) -> Dict[str, Any]:
"""获取访问令牌"""
url = f"{self.base_url}/sns/oauth2/access_token"
params = {
"appid": self.app_id,
"secret": self.app_secret,
"code": code,
"grant_type": "authorization_code",
}
return await self._request(url, params)
async def get_user_info(self, access_token: str, openid: str) -> WechatUser:
"""获取用户信息"""
url = f"{self.base_url}/sns/userinfo"
params = {"access_token": access_token, "openid": openid, "lang": "zh_CN"}
data = await self._request(url, params)
return WechatUser.from_api_response(data)
async def login_with_code(self, code: str) -> WechatUser:
"""使用授权码登录,返回令牌数据和用户信息"""
token_data = await self.get_access_token(code)
wechat_user = await self.get_user_info(
token_data["access_token"], token_data["openid"]
)
return wechat_user
# 全局实例
wechat_api = WechatAPIService()
在这段代码中,直接拿了微信的返回值解析,会导致乱码:
data = response.json()
可以看到此处直接解析,拿到的是乱码。
经排查,该内容返回的 encoding 竟然是: ISO-8859-1。

解决方案:
data = response.text.encode("iso-8859-1").decode("utf8")
data = json.loads(data)手动编码 ISO-8859-1 后,再解码为 UTF-8 即可。