区分环境

This commit is contained in:
aaron 2025-02-27 15:24:56 +08:00
parent 78e744143f
commit 381a95654a
8 changed files with 188 additions and 97 deletions

0
.env Normal file
View File

0
.env.dev Normal file
View File

0
.env.prd Normal file
View File

1
.gitignore vendored
View File

@ -38,6 +38,5 @@ ENV/
*.log
# Local development
.env
.env.local
*.db

View File

@ -44,7 +44,7 @@ async def wechat_phone_login(
):
"""通过微信手机号登录/注册"""
try:
# 初始化微信客户端
# 初始化微信客户端
wechat = WeChatClient()
# 获取用户 openid
@ -66,17 +66,17 @@ async def wechat_phone_login(
if not phone:
return error_response(code=400, message="手机号为空")
# # 获取企业微信的 userid
wecom_client = WecomClient()
wecom_info = await wecom_client.miniprogram_to_userid(openid=openid)
print(f"获取到的企业微信用户信息: {wecom_info}")
# # # 获取企业微信的 userid
# wecom_client = WecomClient()
# wecom_info = await wecom_client.miniprogram_to_userid(openid=openid)
# print(f"获取到的企业微信用户信息: {wecom_info}")
wecom_userid = None
wecom_pending_id = None
# wecom_userid = None
# wecom_pending_id = None
if wecom_info:
wecom_userid = wecom_info.get("userid")
wecom_pending_id = wecom_info.get("pending_id")
# if wecom_info:
# wecom_userid = wecom_info.get("userid")
# wecom_pending_id = wecom_info.get("pending_id")
# 查找或创建用户
@ -93,8 +93,8 @@ async def wechat_phone_login(
password=get_password_hash("123456"),
openid=openid, # 保存 openid
unionid=unionid, # 保存 unionid
wecom_userid=wecom_userid,
wecom_pending_id=wecom_pending_id
# wecom_userid=wecom_userid,
# wecom_pending_id=wecom_pending_id
)
db.add(user)
db.flush()
@ -106,8 +106,8 @@ async def wechat_phone_login(
# 更新现有用户的 openid 和 unionid
user.openid = openid
user.unionid = unionid
user.wecom_userid = wecom_userid
user.wecom_pending_id = wecom_pending_id
# user.wecom_userid = wecom_userid
# user.wecom_pending_id = wecom_pending_id
db.commit()
# 创建访问令牌

View File

@ -1,5 +1,7 @@
from typing import Optional
from pydantic_settings import BaseSettings
import os
from functools import lru_cache
class Settings(BaseSettings):
DEBUG: bool = True # 开发模式标志
@ -108,4 +110,49 @@ class Settings(BaseSettings):
env_file = ".env"
extra = "allow" # 允许额外的环境变量
settings = Settings()
class DevSettings(Settings):
DEBUG: bool = True # 开发模式标志
API_V1_STR: str = "/api/v1"
PROJECT_NAME: str = "FastAPI 项目 (开发环境)"
MYSQL_HOST: str = "gz-cynosdbmysql-grp-2j1cnopr.sql.tencentcdb.com"
MYSQL_PORT: int = 27469
MYSQL_USER: str = "root"
MYSQL_PASSWORD: str = "Aa#223388"
MYSQL_DB: str = "beefast"
class Config:
env_file = ".env.dev"
class ProdSettings(Settings):
DEBUG: bool = False # 生产模式标志
API_V1_STR: str = "/api/v1"
PROJECT_NAME: str = "FastAPI 项目 (生产环境)"
MYSQL_HOST: str = "cd-cynosdbmysql-grp-7kdd8qe4.sql.tencentcdb.com"
MYSQL_PORT: int = 26558
MYSQL_USER: str = "root"
MYSQL_PASSWORD: str = "Ss@123!@#"
MYSQL_DB: str = "beefast"
class Config:
env_file = ".env.prod"
@lru_cache()
def get_settings() -> BaseSettings:
"""
获取配置实例
使用环境变量 APP_ENV 来决定使用哪个配置
"""
env = os.getenv("APP_ENV", "dev").lower()
config_map = {
"dev": DevSettings,
"prod": ProdSettings
}
config_class = config_map.get(env, DevSettings)
return config_class()
# 导出配置实例
settings = get_settings()

View File

@ -1,93 +1,130 @@
import aiohttp
from app.core.config import settings
import requests
import json
import time
import logging
from typing import Optional, Dict
from app.core.config import settings
from typing import Dict, Any, Optional, List
class WecomClient:
"""企业微信客户端"""
def __init__(self):
self.corpid = settings.WECHAT_CORP_ID
self.secret = settings.WECHAT_CORP_SECRET # 注意:这里使用普通的 secret不是 provider_secret
self.corp_id = settings.WECHAT_CORP_ID
self.corp_secret = settings.WECHAT_CORP_SECRET
self.access_token = None
self.token_expires_at = 0
async def get_access_token(self) -> str:
"""获取企业微信普通access_token"""
"""获取访问令牌"""
# 检查令牌是否过期
if self.access_token and time.time() < self.token_expires_at:
return self.access_token
# 获取新令牌
url = f"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={self.corp_id}&corpsecret={self.corp_secret}"
try:
url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken"
params = {
"corpid": self.corpid,
"corpsecret": self.secret
}
response = requests.get(url)
result = response.json()
async with aiohttp.ClientSession() as session:
async with session.get(url, params=params) as response:
result = await response.json()
if result.get("errcode") == 0:
self.access_token = result["access_token"]
print(f"企业微信access_token: {self.access_token}")
return self.access_token
else:
logging.error(f"获取access_token返回: {result}")
raise Exception(f"获取access_token失败: {result}")
if result.get("errcode") == 0:
self.access_token = result.get("access_token")
self.token_expires_at = time.time() + result.get("expires_in") - 200 # 提前200秒过期
return self.access_token
else:
logging.error(f"获取企业微信访问令牌失败: {result}")
return None
except Exception as e:
logging.error(f"获取access_token失败: {str(e)}")
raise
async def miniprogram_to_userid(self, openid: str) -> Optional[Dict]:
"""
转换小程序身份到企业微信
可以使用code或openid
"""
try:
# 构建请求参数
url = "https://qyapi.weixin.qq.com/cgi-bin/miniprogram/jscode2session"
params = {
"access_token": await self.get_access_token(),
"appid": settings.WECHAT_APPID,
}
# 根据传入参数类型选择
params["openid"] = openid
async with aiohttp.ClientSession() as session:
async with session.get(url, params=params) as response:
result = await response.json()
logging.info(f"转换结果: {result}")
if result.get("errcode") == 0:
return {
"userid": result.get("userid"), # 如果是企业成员返回userid
"pending_id": result.get("pending_id"), # 如果是外部联系人返回pending_id
"openid": result.get("openid"),
"session_key": result.get("session_key")
}
else:
logging.error(f"身份转换失败: {result}")
return None
except Exception as e:
logging.error(f"身份转换失败: {str(e)}")
logging.exception(f"获取企业微信访问令牌异常: {str(e)}")
return None
async def get_user_info(self, userid: str) -> Optional[Dict]:
"""获取企业成员信息"""
try:
url = "https://qyapi.weixin.qq.com/cgi-bin/user/get"
params = {
"access_token": await self.get_access_token(),
"userid": userid
}
async def miniprogram_to_userid(self, openid: str) -> Optional[Dict[str, Any]]:
"""
小程序openid转换为企业微信userid
Args:
openid: 小程序用户的openid
async with aiohttp.ClientSession() as session:
async with session.get(url, params=params) as response:
result = await response.json()
if result.get("errcode") == 0:
return result
else:
logging.error(f"获取用户信息失败: {result}")
return None
Returns:
Dict: 包含userid或pending_id的字典
"""
token = await self.get_access_token()
if not token:
return None
url = f"https://qyapi.weixin.qq.com/cgi-bin/miniprogram/jscode2session?access_token={token}"
data = {
"code": openid,
"grant_type": "authorization_code"
}
try:
response = requests.post(url, json=data)
result = response.json()
if result.get("errcode") == 0:
return {
"userid": result.get("userid"),
"pending_id": result.get("pending_id")
}
else:
logging.error(f"小程序openid转换失败: {result}")
return None
except Exception as e:
logging.error(f"获取用户信息失败: {str(e)}")
return None
logging.exception(f"小程序openid转换异常: {str(e)}")
return None
async def get_user_in_group_chat(self, userid: str) -> List[str]:
"""
获取用户所在的群聊列表
Args:
userid: 企业微信用户ID
Returns:
List[str]: 群聊ID列表
"""
token = await self.get_access_token()
if not token:
return []
url = f"https://qyapi.weixin.qq.com/cgi-bin/externalcontact/list_group_chat?access_token={token}"
# 先获取所有群聊
try:
response = requests.post(url, json={})
result = response.json()
if result.get("errcode") != 0:
logging.error(f"获取群聊列表失败: {result}")
return []
chat_ids = []
for chat in result.get("group_chat_list", []):
chat_id = chat.get("chat_id")
# 获取群聊详情
detail_url = f"https://qyapi.weixin.qq.com/cgi-bin/externalcontact/groupchat/get?access_token={token}"
detail_response = requests.post(detail_url, json={"chat_id": chat_id})
detail_result = detail_response.json()
if detail_result.get("errcode") == 0:
group_chat = detail_result.get("group_chat", {})
members = group_chat.get("member_list", [])
# 检查用户是否在群内
for member in members:
if member.get("userid") == userid:
chat_ids.append(chat_id)
break
return chat_ids
except Exception as e:
logging.exception(f"获取用户群聊异常: {str(e)}")
return []
wecom_client = WecomClient()

View File

@ -35,6 +35,14 @@ app.add_middleware(
# 添加请求日志中间件
app.add_middleware(RequestLoggerMiddleware)
@app.get("/api/info")
async def get_info():
"""获取当前环境信息"""
return {
"project": settings.PROJECT_NAME,
"debug": settings.DEBUG
}
# 添加用户路由
app.include_router(dashboard.router, prefix="/api/dashboard", tags=["仪表盘"])
app.include_router(wechat.router,prefix="/api/wechat",tags=["微信"])