upload project source code

This commit is contained in:
2026-04-30 18:49:43 +08:00
commit 9b394ba682
2277 changed files with 660945 additions and 0 deletions

View File

@@ -0,0 +1,153 @@
# -*- coding: utf-8 -*-
import uuid
import json
import httpx
from datetime import datetime, timedelta
from redis.asyncio.client import Redis
from app.core.exceptions import CustomException
from app.core.logger import log
from app.core.security import create_access_token
from app.core.redis_crud import RedisCURD
from app.common.enums import RedisInitKeyConfig
from app.config.setting import settings
from app.api.v1.module_system.auth.schema import AuthSchema, JWTPayloadSchema
from .crud import MiniappUserCRUD
from .schema import (
MiniappLoginSchema,
MiniappUserCreateSchema,
MiniappUserOutSchema,
MiniappLoginOutSchema,
)
class MiniappService:
"""小程序服务层"""
# 微信登录接口
WX_LOGIN_URL = "https://api.weixin.qq.com/sns/jscode2session"
@classmethod
async def login_service(cls, auth: AuthSchema, redis: Redis, data: MiniappLoginSchema) -> dict:
"""
小程序登录
流程:
1. 用微信code换取openid和session_key
2. 查找或创建用户
3. 生成JWT token
"""
# 1. 调用微信接口获取openid
wx_result = await cls._get_wx_session(code=data.code)
openid = wx_result.get("openid")
session_key = wx_result.get("session_key")
unionid = wx_result.get("unionid")
if not openid:
raise CustomException(msg="微信登录失败无法获取openid")
# 2. 查找或创建用户
user = await MiniappUserCRUD(auth).get_by_openid(openid=openid)
if user:
# 更新session_key和登录时间
await MiniappUserCRUD(auth).update_session_key(id=user.id, session_key=session_key)
await MiniappUserCRUD(auth).update_last_login(id=user.id)
log.info(f"小程序用户登录: {openid}")
else:
# 创建新用户
user_data = MiniappUserCreateSchema(
openid=openid,
unionid=unionid,
session_key=session_key,
)
user = await MiniappUserCRUD(auth).create(data=user_data)
log.info(f"小程序新用户注册: {openid}")
# 3. 生成token
token_data = await cls._create_miniapp_token(redis=redis, user_id=user.id, openid=openid)
return MiniappLoginOutSchema(
access_token=token_data["access_token"],
token_type="Bearer",
expires_in=token_data["expires_in"],
user=MiniappUserOutSchema.model_validate(user)
).model_dump()
@classmethod
async def get_user_info_service(cls, auth: AuthSchema, user_id: int) -> dict:
"""获取用户信息"""
user = await MiniappUserCRUD(auth).get_by_id_crud(id=user_id)
if not user:
raise CustomException(msg="用户不存在")
return MiniappUserOutSchema.model_validate(user).model_dump()
@classmethod
async def _get_wx_session(cls, code: str) -> dict:
"""
调用微信接口获取session信息
注意: 需要在配置中设置 MINIAPP_APPID 和 MINIAPP_SECRET
"""
appid = getattr(settings, "MINIAPP_APPID", None)
secret = getattr(settings, "MINIAPP_SECRET", None)
if not appid or not secret:
# 开发环境模拟返回
log.warning("未配置小程序appid和secret使用模拟数据")
return {
"openid": f"mock_openid_{code[:8]}",
"session_key": "mock_session_key",
"unionid": None
}
params = {
"appid": appid,
"secret": secret,
"js_code": code,
"grant_type": "authorization_code"
}
async with httpx.AsyncClient() as client:
response = await client.get(cls.WX_LOGIN_URL, params=params)
result = response.json()
if "errcode" in result and result["errcode"] != 0:
log.error(f"微信登录失败: {result}")
raise CustomException(msg=f"微信登录失败: {result.get('errmsg', '未知错误')}")
return result
@classmethod
async def _create_miniapp_token(cls, redis: Redis, user_id: int, openid: str) -> dict:
"""创建小程序用户token"""
session_id = str(uuid.uuid4())
access_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
now = datetime.now()
session_info = json.dumps({
"session_id": session_id,
"user_id": user_id,
"openid": openid,
"login_type": "miniapp"
})
access_token = create_access_token(payload=JWTPayloadSchema(
sub=session_info,
is_refresh=False,
exp=now + access_expires,
))
# 存储到Redis
await RedisCURD(redis).set(
key=f'{RedisInitKeyConfig.ACCESS_TOKEN.key}:miniapp:{session_id}',
value=access_token,
expire=int(access_expires.total_seconds())
)
return {
"access_token": access_token,
"expires_in": int(access_expires.total_seconds())
}