feat():learning后台管理项目初始化
This commit is contained in:
103
scripts/README_invite_api.md
Normal file
103
scripts/README_invite_api.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# 邀请码申请 API - Python 实现
|
||||
|
||||
这是 `/api/invite-code/apply` 接口的 Python 单文件实现,与 Go 项目共用同一个 MySQL 数据库。
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 直接启动(默认使用 AWS RDS)
|
||||
|
||||
```bash
|
||||
# 使用启动脚本(推荐)
|
||||
./scripts/run_invite_api.sh
|
||||
|
||||
# 或者直接运行
|
||||
uv run scripts/invite_code_api.py
|
||||
```
|
||||
|
||||
> 💡 **提示**: 脚本已预配置 AWS RDS 数据库连接,无需额外配置即可运行。
|
||||
|
||||
### 2. 使用本地数据库(可选)
|
||||
|
||||
如需连接本地 MySQL,创建 `.env` 文件:
|
||||
|
||||
```bash
|
||||
cp scripts/.env.example scripts/.env
|
||||
vim scripts/.env # 修改为本地数据库配置
|
||||
```
|
||||
|
||||
服务启动后:
|
||||
- API 地址: `http://localhost:8000`
|
||||
- 交互式文档: `http://localhost:8000/docs`
|
||||
- 健康检查: `http://localhost:8000/health`
|
||||
|
||||
## API 使用示例
|
||||
|
||||
### 提交申请
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/api/invite-code/apply \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"email": "user@example.com",
|
||||
"reason": "想要体验产品功能"
|
||||
}'
|
||||
```
|
||||
|
||||
**成功响应:**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "申请已提交,我们将在1-2个工作日内处理您的申请",
|
||||
"data": {
|
||||
"id": 1,
|
||||
"email": "user@example.com",
|
||||
"reason": "想要体验产品功能",
|
||||
"status": "pending",
|
||||
"created_at": "2025-11-03T10:00:00Z",
|
||||
"updated_at": "2025-11-03T10:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**错误响应(重复申请):**
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "您已经提交过申请,请等待审核"
|
||||
}
|
||||
```
|
||||
|
||||
## 业务逻辑
|
||||
|
||||
1. **参数验证**: Email 必填且格式正确
|
||||
2. **重复检查**:
|
||||
- 如果已有 `pending` 状态申请 → 提示等待审核
|
||||
- 如果已有 `approved` 状态申请 → 提示检查邮箱
|
||||
- 只有 `rejected` 或无申请时才能提交
|
||||
3. **数据存储**: 保存到 `admin_invite_code_applications` 表
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **FastAPI**: 现代化 Python Web 框架
|
||||
- **SQLAlchemy**: ORM 框架
|
||||
- **PyMySQL**: MySQL 数据库驱动
|
||||
- **Pydantic**: 数据验证
|
||||
|
||||
## 与 Go 项目的兼容性
|
||||
|
||||
- ✅ 使用相同的数据库表 `admin_invite_code_applications`
|
||||
- ✅ 完全相同的业务逻辑
|
||||
- ✅ 相同的请求/响应格式
|
||||
- ✅ 相同的错误处理
|
||||
|
||||
## 开发说明
|
||||
|
||||
文件位置: `scripts/invite_code_api.py`(单文件,约 120 行代码)
|
||||
|
||||
支持的环境变量:
|
||||
- `DB_USER`: 数据库用户名(默认: root)
|
||||
- `DB_PASSWORD`: 数据库密码(默认: password)
|
||||
- `DB_HOST`: 数据库地址(默认: localhost)
|
||||
- `DB_PORT`: 数据库端口(默认: 3306)
|
||||
- `DB_NAME`: 数据库名称(默认: goalfymax_prod)
|
||||
124
scripts/invite_code_api.py
Normal file
124
scripts/invite_code_api.py
Normal file
@@ -0,0 +1,124 @@
|
||||
#!/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)
|
||||
50
scripts/migrate.sh
Executable file
50
scripts/migrate.sh
Executable file
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 设置错误时退出
|
||||
set -e
|
||||
|
||||
# 设置颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${GREEN}开始执行数据库迁移...${NC}"
|
||||
|
||||
# 加载环境变量
|
||||
if [ -f ".env" ]; then
|
||||
export $(cat .env | grep -v '^#' | xargs)
|
||||
fi
|
||||
|
||||
# 检查数据库连接
|
||||
echo -e "${YELLOW}检查数据库连接...${NC}"
|
||||
mysql -h${DB_HOST} -P${DB_PORT} -u${DB_USER} -p${DB_PASSWORD} -e "SELECT 1" >/dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN}数据库连接成功${NC}"
|
||||
else
|
||||
echo -e "${RED}数据库连接失败,请检查配置${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 执行迁移文件
|
||||
MIGRATION_DIR="migrations"
|
||||
if [ -d "$MIGRATION_DIR" ]; then
|
||||
echo -e "${YELLOW}执行迁移文件...${NC}"
|
||||
|
||||
for file in $(ls $MIGRATION_DIR/*.sql 2>/dev/null | sort); do
|
||||
filename=$(basename "$file")
|
||||
echo -e "${GREEN}执行: $filename${NC}"
|
||||
mysql -h${DB_HOST} -P${DB_PORT} -u${DB_USER} -p${DB_PASSWORD} ${DB_NAME} < "$file"
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN} ✓ 成功${NC}"
|
||||
else
|
||||
echo -e "${RED} ✗ 失败${NC}"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "${GREEN}数据库迁移完成!${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}没有找到迁移目录${NC}"
|
||||
exit 1
|
||||
fi
|
||||
28
scripts/run_invite_api.sh
Executable file
28
scripts/run_invite_api.sh
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
# 邀请码申请 API 启动脚本
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
echo "=== 邀请码申请 API 启动 ==="
|
||||
|
||||
# 加载环境变量(如果存在 .env 文件)
|
||||
if [ -f scripts/.env ]; then
|
||||
echo "✓ 加载自定义配置: scripts/.env"
|
||||
export $(grep -v '^#' scripts/.env | xargs)
|
||||
else
|
||||
echo "✓ 使用默认配置: AWS RDS (goalfyagent-aurora-mysql-staging)"
|
||||
echo " 提示: 如需使用本地数据库,请创建 scripts/.env 文件"
|
||||
fi
|
||||
|
||||
# 检查依赖
|
||||
echo "检查 Python 依赖..."
|
||||
uv pip install -q fastapi uvicorn sqlalchemy pymysql pydantic[email]
|
||||
|
||||
# 启动服务
|
||||
echo "启动服务..."
|
||||
echo "访问地址: http://localhost:8000"
|
||||
echo "API 文档: http://localhost:8000/docs"
|
||||
echo ""
|
||||
uv run scripts/invite_code_api.py
|
||||
54
scripts/start.sh
Executable file
54
scripts/start.sh
Executable file
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 设置错误时退出
|
||||
set -e
|
||||
|
||||
# 设置颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${GREEN}启动 Goalfymax Admin 服务...${NC}"
|
||||
|
||||
# 切换到项目根目录
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
# 检查是否已经有服务在运行
|
||||
if [ -f "admin-server.pid" ]; then
|
||||
PID=$(cat admin-server.pid)
|
||||
if ps -p $PID > /dev/null 2>&1; then
|
||||
echo -e "${YELLOW}服务已经在运行 (PID: $PID)${NC}"
|
||||
echo -e "${YELLOW}如需重启,请先执行 ./scripts/stop.sh${NC}"
|
||||
exit 1
|
||||
else
|
||||
# 删除过期的 PID 文件
|
||||
rm -f admin-server.pid
|
||||
fi
|
||||
fi
|
||||
|
||||
# 创建日志目录
|
||||
mkdir -p logs
|
||||
|
||||
# 启动服务
|
||||
echo -e "${YELLOW}正在启动服务...${NC}"
|
||||
nohup ./admin-server --config etc/config.yaml > logs/admin-server.log 2>&1 &
|
||||
PID=$!
|
||||
|
||||
# 保存 PID
|
||||
echo $PID > admin-server.pid
|
||||
|
||||
# 等待服务启动
|
||||
sleep 2
|
||||
|
||||
# 检查服务是否启动成功
|
||||
if ps -p $PID > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ 服务启动成功 (PID: $PID)${NC}"
|
||||
echo -e "${GREEN}✓ 日志文件: logs/admin-server.log${NC}"
|
||||
echo -e "${GREEN}✓ 服务端口: 8087${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ 服务启动失败${NC}"
|
||||
echo -e "${RED}请检查日志文件: logs/admin-server.log${NC}"
|
||||
rm -f admin-server.pid
|
||||
exit 1
|
||||
fi
|
||||
65
scripts/status.sh
Executable file
65
scripts/status.sh
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 设置颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${CYAN}=== Goalfymax Admin 服务状态 ===${NC}"
|
||||
echo
|
||||
|
||||
# 检查进程状态
|
||||
if [ -f "admin-server.pid" ]; then
|
||||
PID=$(cat admin-server.pid)
|
||||
if ps -p $PID > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ 服务运行中${NC}"
|
||||
echo -e " 进程ID: $PID"
|
||||
echo -e " 端口: 8087"
|
||||
else
|
||||
echo -e "${RED}✗ 服务未运行(PID文件存在但进程不存在)${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}✗ 服务未运行${NC}"
|
||||
fi
|
||||
echo
|
||||
|
||||
# 测试健康检查
|
||||
echo -e "${YELLOW}测试健康检查接口...${NC}"
|
||||
HEALTH=$(curl -s http://localhost:8087/health 2>/dev/null)
|
||||
if [ "$HEALTH" = '{"status":"ok"}' ]; then
|
||||
echo -e "${GREEN}✓ 健康检查通过${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ 健康检查失败${NC}"
|
||||
fi
|
||||
echo
|
||||
|
||||
# 检查数据库连接
|
||||
echo -e "${YELLOW}检查最近的日志...${NC}"
|
||||
if [ -f "logs/admin-server.log" ]; then
|
||||
LAST_ERROR=$(tail -n 100 logs/admin-server.log | grep -i "error\|fatal" | tail -n 1)
|
||||
if [ -n "$LAST_ERROR" ]; then
|
||||
echo -e "${RED}✗ 发现错误:${NC}"
|
||||
echo " $LAST_ERROR"
|
||||
else
|
||||
echo -e "${GREEN}✓ 无错误日志${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}! 日志文件不存在${NC}"
|
||||
fi
|
||||
echo
|
||||
|
||||
# 显示API端点
|
||||
echo -e "${CYAN}可用的API端点:${NC}"
|
||||
echo -e " 公开接口:"
|
||||
echo -e " POST http://localhost:8087/api/public/invite-code/apply"
|
||||
echo -e ""
|
||||
echo -e " 管理接口(需要认证):"
|
||||
echo -e " GET http://localhost:8087/api/admin/invite-applications"
|
||||
echo -e " GET http://localhost:8087/api/admin/invite-applications/statistics"
|
||||
echo -e " POST http://localhost:8087/api/admin/invite-applications/approve"
|
||||
echo -e " POST http://localhost:8087/api/admin/invite-applications/reject"
|
||||
echo
|
||||
|
||||
echo -e "${CYAN}=== 状态检查完成 ===${NC}"
|
||||
56
scripts/stop.sh
Executable file
56
scripts/stop.sh
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 设置颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${YELLOW}停止 Goalfymax Admin 服务...${NC}"
|
||||
|
||||
# 切换到项目根目录
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
# 检查 PID 文件是否存在
|
||||
if [ ! -f "admin-server.pid" ]; then
|
||||
echo -e "${RED}✗ 服务未运行 (找不到 PID 文件)${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 读取 PID
|
||||
PID=$(cat admin-server.pid)
|
||||
|
||||
# 检查进程是否存在
|
||||
if ! ps -p $PID > /dev/null 2>&1; then
|
||||
echo -e "${RED}✗ 服务未运行 (进程不存在)${NC}"
|
||||
rm -f admin-server.pid
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 停止服务
|
||||
echo -e "${YELLOW}正在停止服务 (PID: $PID)...${NC}"
|
||||
kill $PID
|
||||
|
||||
# 等待进程结束
|
||||
for i in {1..10}; do
|
||||
if ! ps -p $PID > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ 服务已停止${NC}"
|
||||
rm -f admin-server.pid
|
||||
exit 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# 如果进程还没结束,强制终止
|
||||
if ps -p $PID > /dev/null 2>&1; then
|
||||
echo -e "${YELLOW}强制终止服务...${NC}"
|
||||
kill -9 $PID
|
||||
sleep 1
|
||||
if ! ps -p $PID > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ 服务已强制停止${NC}"
|
||||
rm -f admin-server.pid
|
||||
else
|
||||
echo -e "${RED}✗ 无法停止服务${NC}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
49
scripts/test_api.sh
Executable file
49
scripts/test_api.sh
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 测试邀请码申请API
|
||||
|
||||
# 设置颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# API基础URL
|
||||
API_BASE_URL="${API_BASE_URL:-http://localhost:8080}"
|
||||
|
||||
echo -e "${CYAN}=== 测试邀请码申请API ===${NC}"
|
||||
echo
|
||||
|
||||
# 1. 测试提交申请(公开接口)
|
||||
echo -e "${YELLOW}1. 测试提交邀请码申请(公开接口)${NC}"
|
||||
curl -X POST "${API_BASE_URL}/api/public/invite-code/apply" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"email": "test@example.com",
|
||||
"reason": "我想体验一下Goalfy的AI编程助手功能"
|
||||
}'
|
||||
echo -e "\n${GREEN}✓ 申请提交完成${NC}\n"
|
||||
|
||||
# 2. 测试重复提交(应该返回错误)
|
||||
echo -e "${YELLOW}2. 测试重复提交(应该返回错误)${NC}"
|
||||
curl -X POST "${API_BASE_URL}/api/public/invite-code/apply" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"email": "test@example.com",
|
||||
"reason": "第二次申请"
|
||||
}'
|
||||
echo -e "\n${GREEN}✓ 重复提交测试完成${NC}\n"
|
||||
|
||||
# 3. 测试不同邮箱提交
|
||||
echo -e "${YELLOW}3. 测试不同邮箱提交${NC}"
|
||||
curl -X POST "${API_BASE_URL}/api/public/invite-code/apply" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"email": "another@example.com",
|
||||
"reason": "希望获得邀请码"
|
||||
}'
|
||||
echo -e "\n${GREEN}✓ 不同邮箱申请完成${NC}\n"
|
||||
|
||||
echo -e "${CYAN}=== API测试完成 ===${NC}"
|
||||
echo -e "${GREEN}请登录后台管理页面查看待处理申请${NC}"
|
||||
174
scripts/test_email.py
Executable file
174
scripts/test_email.py
Executable file
@@ -0,0 +1,174 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
邮件发送功能测试脚本
|
||||
|
||||
测试步骤:
|
||||
1. 添加 language 字段到数据库
|
||||
2. 创建中文和英文测试申请
|
||||
3. 通过 Go 服务审批并发送邮件
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import pymysql
|
||||
from datetime import datetime
|
||||
|
||||
# 数据库配置
|
||||
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 = int(os.getenv("DB_PORT", "3306"))
|
||||
DB_NAME = os.getenv("DB_NAME", "goalfymax_prod")
|
||||
|
||||
def get_db_connection():
|
||||
"""获取数据库连接"""
|
||||
return pymysql.connect(
|
||||
host=DB_HOST,
|
||||
port=DB_PORT,
|
||||
user=DB_USER,
|
||||
password=DB_PASSWORD,
|
||||
database=DB_NAME,
|
||||
charset='utf8mb4',
|
||||
cursorclass=pymysql.cursors.DictCursor
|
||||
)
|
||||
|
||||
def add_language_column():
|
||||
"""添加 language 字段"""
|
||||
print("\n=== 步骤 1: 添加 language 字段 ===")
|
||||
|
||||
conn = get_db_connection()
|
||||
try:
|
||||
with conn.cursor() as cursor:
|
||||
# 检查字段是否已存在
|
||||
cursor.execute("""
|
||||
SELECT COLUMN_NAME
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = %s
|
||||
AND TABLE_NAME = 'admin_invite_code_applications'
|
||||
AND COLUMN_NAME = 'language'
|
||||
""", (DB_NAME,))
|
||||
|
||||
if cursor.fetchone():
|
||||
print("✓ language 字段已存在")
|
||||
else:
|
||||
# 添加字段
|
||||
cursor.execute("""
|
||||
ALTER TABLE admin_invite_code_applications
|
||||
ADD COLUMN language VARCHAR(10) DEFAULT 'zh' COMMENT '语言:zh-中文,en-英文' AFTER reason
|
||||
""")
|
||||
conn.commit()
|
||||
print("✓ language 字段添加成功")
|
||||
except Exception as e:
|
||||
print(f"✗ 添加字段失败: {e}")
|
||||
return False
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
return True
|
||||
|
||||
def create_test_applications():
|
||||
"""创建测试申请"""
|
||||
print("\n=== 步骤 2: 创建测试申请 ===")
|
||||
|
||||
conn = get_db_connection()
|
||||
test_emails = [
|
||||
("test_zh@example.com", "测试中文邮件", "zh"),
|
||||
("test_en@example.com", "Testing English email", "en")
|
||||
]
|
||||
|
||||
created_ids = []
|
||||
|
||||
try:
|
||||
with conn.cursor() as cursor:
|
||||
for email, reason, language in test_emails:
|
||||
# 删除旧的测试数据
|
||||
cursor.execute("""
|
||||
DELETE FROM admin_invite_code_applications
|
||||
WHERE email = %s
|
||||
""", (email,))
|
||||
|
||||
# 创建新申请
|
||||
cursor.execute("""
|
||||
INSERT INTO admin_invite_code_applications
|
||||
(email, reason, language, status, created_at, updated_at)
|
||||
VALUES (%s, %s, %s, 'pending', NOW(), NOW())
|
||||
""", (email, reason, language))
|
||||
|
||||
created_ids.append(cursor.lastrowid)
|
||||
print(f"✓ 创建申请成功: {email} (ID: {cursor.lastrowid}, Language: {language})")
|
||||
|
||||
conn.commit()
|
||||
except Exception as e:
|
||||
print(f"✗ 创建申请失败: {e}")
|
||||
return []
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
return created_ids
|
||||
|
||||
def check_applications():
|
||||
"""检查申请状态"""
|
||||
print("\n=== 查看测试申请 ===")
|
||||
|
||||
conn = get_db_connection()
|
||||
try:
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute("""
|
||||
SELECT id, email, reason, language, status, created_at
|
||||
FROM admin_invite_code_applications
|
||||
WHERE email LIKE 'test_%@example.com'
|
||||
ORDER BY id DESC
|
||||
LIMIT 10
|
||||
""")
|
||||
|
||||
applications = cursor.fetchall()
|
||||
if applications:
|
||||
print("\n当前测试申请:")
|
||||
for app in applications:
|
||||
print(f" ID: {app['id']}, Email: {app['email']}, Language: {app['language']}, Status: {app['status']}")
|
||||
else:
|
||||
print("没有找到测试申请")
|
||||
|
||||
return applications
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def main():
|
||||
print("=" * 60)
|
||||
print("邮件发送功能测试")
|
||||
print("=" * 60)
|
||||
|
||||
# 步骤 1: 添加 language 字段
|
||||
if not add_language_column():
|
||||
print("\n❌ 测试失败:无法添加 language 字段")
|
||||
sys.exit(1)
|
||||
|
||||
# 步骤 2: 创建测试申请
|
||||
application_ids = create_test_applications()
|
||||
if not application_ids:
|
||||
print("\n❌ 测试失败:无法创建测试申请")
|
||||
sys.exit(1)
|
||||
|
||||
# 步骤 3: 显示测试申请
|
||||
check_applications()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("✓ 测试准备完成!")
|
||||
print("=" * 60)
|
||||
print("\n下一步操作:")
|
||||
print("1. 启动 Go 服务: ./scripts/start.sh")
|
||||
print("2. 登录后台管理系统")
|
||||
print("3. 在邀请码申请管理中审批这些测试申请")
|
||||
print("4. 检查邮件发送情况(中文和英文)")
|
||||
print("\n测试邮箱:")
|
||||
print(" - test_zh@example.com (中文)")
|
||||
print(" - test_en@example.com (英文)")
|
||||
print("\n或者使用 API 测试:")
|
||||
for app_id in application_ids:
|
||||
print(f" curl -X POST http://localhost:8087/api/admin/invite-code/applications/approve \\")
|
||||
print(f" -H 'Content-Type: application/json' \\")
|
||||
print(f" -d '{{\"application_id\": {app_id}, \"valid_days\": 7}}'")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user