#!/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)