125 lines
4.3 KiB
Python
125 lines
4.3 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
邀请码申请 API - 简洁版本
|
||
|
||
使用方法:
|
||
1. 直接运行: ./scripts/run_invite_api.sh
|
||
2. 或者: uv run scripts/invite_code_api.py
|
||
3. 访问: POST http://localhost:8000/api/invite-code/apply
|
||
|
||
默认使用 AWS RDS 数据库(与 Go 项目共享)
|
||
如需使用本地数据库,请设置环境变量或创建 scripts/.env 文件
|
||
"""
|
||
|
||
import os
|
||
from datetime import datetime, timezone
|
||
from typing import Optional
|
||
|
||
from fastapi import FastAPI, HTTPException
|
||
from pydantic import BaseModel, EmailStr
|
||
from sqlalchemy import create_engine, Column, Integer, String, DateTime, Text
|
||
from sqlalchemy.orm import declarative_base, Session
|
||
from sqlalchemy.exc import IntegrityError
|
||
|
||
# 数据库配置 - 从环境变量读取(默认使用 AWS RDS 配置)
|
||
DB_USER = os.getenv("DB_USER", "goalfymax_prod")
|
||
DB_PASSWORD = os.getenv("DB_PASSWORD", "X6cQDaOLOifFBOMq")
|
||
DB_HOST = os.getenv("DB_HOST", "goalfyagent-aurora-mysql-staging.cb2sq6y2mg93.us-west-2.rds.amazonaws.com")
|
||
DB_PORT = os.getenv("DB_PORT", "3306")
|
||
DB_NAME = os.getenv("DB_NAME", "goalfymax_prod")
|
||
|
||
DATABASE_URL = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?charset=utf8mb4"
|
||
|
||
Base = declarative_base()
|
||
engine = create_engine(DATABASE_URL, echo=False, pool_pre_ping=True)
|
||
|
||
# 数据模型 - 与 Go 项目的 admin_invite_code_applications 表结构保持一致
|
||
class InviteCodeApplication(Base):
|
||
__tablename__ = "admin_invite_code_applications"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
email = Column(String(255), nullable=False, index=True)
|
||
reason = Column(Text, nullable=True)
|
||
language = Column(String(10), nullable=False, default="zh")
|
||
status = Column(String(20), nullable=False, default="pending", index=True)
|
||
created_at = Column(DateTime, nullable=False, default=lambda: datetime.now(timezone.utc))
|
||
updated_at = Column(DateTime, nullable=False, default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc))
|
||
|
||
# 不自动创建表(使用 Go 项目已有的表结构)
|
||
# Base.metadata.create_all(engine)
|
||
|
||
# FastAPI 应用
|
||
app = FastAPI(title="邀请码申请 API")
|
||
|
||
# 请求/响应模型
|
||
class ApplicationRequest(BaseModel):
|
||
email: EmailStr
|
||
reason: Optional[str] = None
|
||
language: Optional[str] = "zh" # zh 或 en,默认 zh
|
||
|
||
class ApplicationResponse(BaseModel):
|
||
id: int
|
||
email: str
|
||
reason: Optional[str]
|
||
language: str
|
||
status: str
|
||
created_at: datetime
|
||
updated_at: datetime
|
||
|
||
class Config:
|
||
from_attributes = True
|
||
|
||
class SuccessResponse(BaseModel):
|
||
code: int = 0
|
||
message: str
|
||
data: ApplicationResponse
|
||
|
||
# API 端点
|
||
@app.post("/api/invite-code/apply", response_model=SuccessResponse)
|
||
async def submit_application(req: ApplicationRequest):
|
||
"""提交邀请码申请"""
|
||
|
||
with Session(engine) as session:
|
||
# 检查是否已有待处理或已通过的申请
|
||
existing = session.query(InviteCodeApplication).filter(
|
||
InviteCodeApplication.email == req.email,
|
||
InviteCodeApplication.status.in_(["pending", "approved"])
|
||
).first()
|
||
|
||
if existing:
|
||
if existing.status == "pending":
|
||
raise HTTPException(status_code=400, detail="您已经提交过申请,请等待审核")
|
||
if existing.status == "approved":
|
||
raise HTTPException(status_code=400, detail="您的申请已通过,请检查邮箱")
|
||
|
||
# 创建新申请
|
||
language = req.language if req.language in ["zh", "en"] else "zh"
|
||
application = InviteCodeApplication(
|
||
email=req.email,
|
||
reason=req.reason,
|
||
language=language,
|
||
status="pending"
|
||
)
|
||
|
||
try:
|
||
session.add(application)
|
||
session.commit()
|
||
session.refresh(application)
|
||
|
||
return SuccessResponse(
|
||
message="申请已提交,我们将在1-2个工作日内处理您的申请",
|
||
data=ApplicationResponse.model_validate(application)
|
||
)
|
||
except IntegrityError:
|
||
session.rollback()
|
||
raise HTTPException(status_code=500, detail="创建申请失败")
|
||
|
||
# 健康检查
|
||
@app.get("/health")
|
||
async def health_check():
|
||
return {"status": "ok"}
|
||
|
||
if __name__ == "__main__":
|
||
import uvicorn
|
||
uvicorn.run(app, host="0.0.0.0", port=8000)
|