区分环境
This commit is contained in:
parent
78e744143f
commit
381a95654a
1
.gitignore
vendored
1
.gitignore
vendored
@ -38,6 +38,5 @@ ENV/
|
|||||||
*.log
|
*.log
|
||||||
|
|
||||||
# Local development
|
# Local development
|
||||||
.env
|
|
||||||
.env.local
|
.env.local
|
||||||
*.db
|
*.db
|
||||||
@ -66,17 +66,17 @@ async def wechat_phone_login(
|
|||||||
if not phone:
|
if not phone:
|
||||||
return error_response(code=400, message="手机号为空")
|
return error_response(code=400, message="手机号为空")
|
||||||
|
|
||||||
# # 获取企业微信的 userid
|
# # # 获取企业微信的 userid
|
||||||
wecom_client = WecomClient()
|
# wecom_client = WecomClient()
|
||||||
wecom_info = await wecom_client.miniprogram_to_userid(openid=openid)
|
# wecom_info = await wecom_client.miniprogram_to_userid(openid=openid)
|
||||||
print(f"获取到的企业微信用户信息: {wecom_info}")
|
# print(f"获取到的企业微信用户信息: {wecom_info}")
|
||||||
|
|
||||||
wecom_userid = None
|
# wecom_userid = None
|
||||||
wecom_pending_id = None
|
# wecom_pending_id = None
|
||||||
|
|
||||||
if wecom_info:
|
# if wecom_info:
|
||||||
wecom_userid = wecom_info.get("userid")
|
# wecom_userid = wecom_info.get("userid")
|
||||||
wecom_pending_id = wecom_info.get("pending_id")
|
# wecom_pending_id = wecom_info.get("pending_id")
|
||||||
|
|
||||||
|
|
||||||
# 查找或创建用户
|
# 查找或创建用户
|
||||||
@ -93,8 +93,8 @@ async def wechat_phone_login(
|
|||||||
password=get_password_hash("123456"),
|
password=get_password_hash("123456"),
|
||||||
openid=openid, # 保存 openid
|
openid=openid, # 保存 openid
|
||||||
unionid=unionid, # 保存 unionid
|
unionid=unionid, # 保存 unionid
|
||||||
wecom_userid=wecom_userid,
|
# wecom_userid=wecom_userid,
|
||||||
wecom_pending_id=wecom_pending_id
|
# wecom_pending_id=wecom_pending_id
|
||||||
)
|
)
|
||||||
db.add(user)
|
db.add(user)
|
||||||
db.flush()
|
db.flush()
|
||||||
@ -106,8 +106,8 @@ async def wechat_phone_login(
|
|||||||
# 更新现有用户的 openid 和 unionid
|
# 更新现有用户的 openid 和 unionid
|
||||||
user.openid = openid
|
user.openid = openid
|
||||||
user.unionid = unionid
|
user.unionid = unionid
|
||||||
user.wecom_userid = wecom_userid
|
# user.wecom_userid = wecom_userid
|
||||||
user.wecom_pending_id = wecom_pending_id
|
# user.wecom_pending_id = wecom_pending_id
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
# 创建访问令牌
|
# 创建访问令牌
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
from pydantic_settings import BaseSettings
|
from pydantic_settings import BaseSettings
|
||||||
|
import os
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
DEBUG: bool = True # 开发模式标志
|
DEBUG: bool = True # 开发模式标志
|
||||||
@ -108,4 +110,49 @@ class Settings(BaseSettings):
|
|||||||
env_file = ".env"
|
env_file = ".env"
|
||||||
extra = "allow" # 允许额外的环境变量
|
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()
|
||||||
@ -1,93 +1,130 @@
|
|||||||
import aiohttp
|
import requests
|
||||||
from app.core.config import settings
|
import json
|
||||||
|
import time
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional, Dict
|
from app.core.config import settings
|
||||||
|
from typing import Dict, Any, Optional, List
|
||||||
|
|
||||||
class WecomClient:
|
class WecomClient:
|
||||||
|
"""企业微信客户端"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.corpid = settings.WECHAT_CORP_ID
|
self.corp_id = settings.WECHAT_CORP_ID
|
||||||
self.secret = settings.WECHAT_CORP_SECRET # 注意:这里使用普通的 secret,不是 provider_secret
|
self.corp_secret = settings.WECHAT_CORP_SECRET
|
||||||
self.access_token = None
|
self.access_token = None
|
||||||
|
self.token_expires_at = 0
|
||||||
|
|
||||||
async def get_access_token(self) -> str:
|
async def get_access_token(self) -> str:
|
||||||
"""获取企业微信普通access_token"""
|
"""获取访问令牌"""
|
||||||
try:
|
# 检查令牌是否过期
|
||||||
url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken"
|
if self.access_token and time.time() < self.token_expires_at:
|
||||||
params = {
|
return self.access_token
|
||||||
"corpid": self.corpid,
|
|
||||||
"corpsecret": self.secret
|
|
||||||
}
|
|
||||||
|
|
||||||
async with aiohttp.ClientSession() as session:
|
# 获取新令牌
|
||||||
async with session.get(url, params=params) as response:
|
url = f"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={self.corp_id}&corpsecret={self.corp_secret}"
|
||||||
result = await response.json()
|
|
||||||
|
try:
|
||||||
|
response = requests.get(url)
|
||||||
|
result = response.json()
|
||||||
|
|
||||||
if result.get("errcode") == 0:
|
if result.get("errcode") == 0:
|
||||||
self.access_token = result["access_token"]
|
self.access_token = result.get("access_token")
|
||||||
print(f"企业微信access_token: {self.access_token}")
|
self.token_expires_at = time.time() + result.get("expires_in") - 200 # 提前200秒过期
|
||||||
return self.access_token
|
return self.access_token
|
||||||
else:
|
else:
|
||||||
logging.error(f"获取access_token返回: {result}")
|
logging.error(f"获取企业微信访问令牌失败: {result}")
|
||||||
raise Exception(f"获取access_token失败: {result}")
|
return None
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"获取access_token失败: {str(e)}")
|
logging.exception(f"获取企业微信访问令牌异常: {str(e)}")
|
||||||
raise
|
return None
|
||||||
|
|
||||||
async def miniprogram_to_userid(self, openid: str) -> Optional[Dict]:
|
async def miniprogram_to_userid(self, openid: str) -> Optional[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
转换小程序身份到企业微信
|
小程序openid转换为企业微信userid
|
||||||
可以使用code或openid
|
|
||||||
|
Args:
|
||||||
|
openid: 小程序用户的openid
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 包含userid或pending_id的字典
|
||||||
"""
|
"""
|
||||||
try:
|
token = await self.get_access_token()
|
||||||
# 构建请求参数
|
if not token:
|
||||||
url = "https://qyapi.weixin.qq.com/cgi-bin/miniprogram/jscode2session"
|
return None
|
||||||
params = {
|
|
||||||
"access_token": await self.get_access_token(),
|
url = f"https://qyapi.weixin.qq.com/cgi-bin/miniprogram/jscode2session?access_token={token}"
|
||||||
"appid": settings.WECHAT_APPID,
|
|
||||||
|
data = {
|
||||||
|
"code": openid,
|
||||||
|
"grant_type": "authorization_code"
|
||||||
}
|
}
|
||||||
|
|
||||||
# 根据传入参数类型选择
|
try:
|
||||||
params["openid"] = openid
|
response = requests.post(url, json=data)
|
||||||
|
result = response.json()
|
||||||
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:
|
if result.get("errcode") == 0:
|
||||||
return {
|
return {
|
||||||
"userid": result.get("userid"), # 如果是企业成员,返回userid
|
"userid": result.get("userid"),
|
||||||
"pending_id": result.get("pending_id"), # 如果是外部联系人,返回pending_id
|
"pending_id": result.get("pending_id")
|
||||||
"openid": result.get("openid"),
|
|
||||||
"session_key": result.get("session_key")
|
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
logging.error(f"身份转换失败: {result}")
|
logging.error(f"小程序openid转换失败: {result}")
|
||||||
return None
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"身份转换失败: {str(e)}")
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def get_user_info(self, userid: str) -> Optional[Dict]:
|
except Exception as e:
|
||||||
"""获取企业成员信息"""
|
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:
|
try:
|
||||||
url = "https://qyapi.weixin.qq.com/cgi-bin/user/get"
|
response = requests.post(url, json={})
|
||||||
params = {
|
result = response.json()
|
||||||
"access_token": await self.get_access_token(),
|
|
||||||
"userid": userid
|
|
||||||
}
|
|
||||||
|
|
||||||
async with aiohttp.ClientSession() as session:
|
if result.get("errcode") != 0:
|
||||||
async with session.get(url, params=params) as response:
|
logging.error(f"获取群聊列表失败: {result}")
|
||||||
result = await response.json()
|
return []
|
||||||
|
|
||||||
if result.get("errcode") == 0:
|
chat_ids = []
|
||||||
return result
|
for chat in result.get("group_chat_list", []):
|
||||||
else:
|
chat_id = chat.get("chat_id")
|
||||||
logging.error(f"获取用户信息失败: {result}")
|
|
||||||
return None
|
# 获取群聊详情
|
||||||
|
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:
|
except Exception as e:
|
||||||
logging.error(f"获取用户信息失败: {str(e)}")
|
logging.exception(f"获取用户群聊异常: {str(e)}")
|
||||||
return None
|
return []
|
||||||
|
|
||||||
|
wecom_client = WecomClient()
|
||||||
@ -35,6 +35,14 @@ app.add_middleware(
|
|||||||
# 添加请求日志中间件
|
# 添加请求日志中间件
|
||||||
app.add_middleware(RequestLoggerMiddleware)
|
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(dashboard.router, prefix="/api/dashboard", tags=["仪表盘"])
|
||||||
app.include_router(wechat.router,prefix="/api/wechat",tags=["微信"])
|
app.include_router(wechat.router,prefix="/api/wechat",tags=["微信"])
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user