继续完善

This commit is contained in:
2025-09-16 12:57:36 +08:00
parent 249e434b72
commit 17691af8d1
25 changed files with 1981 additions and 83 deletions

View File

@@ -0,0 +1,50 @@
# Git
.git
.gitignore
# Python
__pycache__/
*.pyc
*.pyo
*.pyd
.Python
env/
venv/
.venv/
pip-log.txt
pip-delete-this-directory.txt
.tox/
.coverage
.coverage.*
.pytest_cache/
# 环境变量文件
.env
.env.production
# IDE
.vscode/
.idea/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# 日志文件
*.log
# 测试文件(可选,如果不想包含在镜像中)
test/
# 文档文件(可选)
*.md
LICENSE
# 启动脚本Windows
*.bat
# 其他临时文件
*.tmp
.cache/

View File

@@ -11,4 +11,4 @@ MAIL_PASSWORD=your-app-password
SECRET_KEY=infogenie-production-secret-key-2025
# 会话安全配置
SESSION_COOKIE_SECURE=True
HWT_SECURE=True

View File

@@ -0,0 +1,163 @@
# InfoGenie 后端 Docker 部署指南
## 项目概述
InfoGenie 是一个基于 Flask 的 Python 后端应用提供用户认证、AI 模型应用、小游戏等功能。
## Docker 部署
### 前置要求
- Docker >= 20.0
- Docker Compose >= 2.0
### 快速开始
1. **克隆项目并进入后端目录**
```bash
cd InfoGenie-backend
```
2. **设置环境变量**
```bash
cp .env.example .env # 如果有示例文件
# 编辑 .env 文件,设置必要的环境变量
```
3. **构建并运行**
```bash
# 方法1使用构建脚本
./build_docker.sh
# 方法2使用 Docker Compose推荐
docker-compose up -d
```
### 环境变量配置
在 `.env` 文件中配置以下变量:
```env
# Flask 配置
SECRET_KEY=your-secret-key-here
FLASK_ENV=production
# MongoDB 配置
MONGO_URI=mongodb://mongodb:27017/InfoGenie
# 邮件配置
MAIL_USERNAME=your-email@qq.com
MAIL_PASSWORD=your-app-password
# AI 配置(可选)
# 在 ai_config.json 中配置 AI API 密钥
```
### 服务端口
- 后端 API: `http://localhost:5002`
- MongoDB: `localhost:27017`
- 健康检查: `http://localhost:5002/api/health`
### Docker Compose 命令
```bash
# 启动服务
docker-compose up -d
# 查看日志
docker-compose logs -f infogenie-backend
# 停止服务
docker-compose down
# 重建镜像
docker-compose build --no-cache
# 清理数据卷
docker-compose down -v
```
### 单独构建 Docker 镜像
如果不需要 MongoDB可以单独构建后端镜像
```bash
# 构建镜像
docker build -t infogenie-backend:latest .
# 运行容器(需要外部 MongoDB
docker run -d \
--name infogenie-backend \
-p 5002:5002 \
-e MONGO_URI=mongodb://your-mongo-host:27017/InfoGenie \
-e SECRET_KEY=your-secret-key \
infogenie-backend:latest
```
## 项目结构
```
InfoGenie-backend/
├── Dockerfile # Docker 镜像构建文件
├── docker-compose.yml # Docker Compose 配置
├── build_docker.sh # 构建脚本
├── .dockerignore # Docker 忽略文件
├── app.py # Flask 应用主入口
├── config.py # 应用配置
├── requirements.txt # Python 依赖
├── ai_config.json # AI 模型配置
├── modules/ # 功能模块
│ ├── auth.py # 用户认证
│ ├── user_management.py # 用户管理
│ ├── email_service.py # 邮件服务
│ └── aimodelapp.py # AI 模型应用
└── test/ # 测试文件
```
## 注意事项
1. **安全性**: 生产环境请使用强密码和随机生成的 SECRET_KEY
2. **数据库**: 默认使用 MongoDB 6.0,确保数据持久化
3. **端口**: 如需修改端口,请同时更新 Dockerfile 和 docker-compose.yml
4. **日志**: 应用日志通过 `docker-compose logs` 查看
5. **备份**: 重要数据请定期备份 MongoDB 数据卷
## 故障排除
### 常见问题
1. **端口占用**
```bash
# 检查端口占用
lsof -i :5002
# 修改端口映射
docker-compose up -d --scale infogenie-backend=0
docker-compose up -d
```
2. **数据库连接失败**
```bash
# 检查 MongoDB 状态
docker-compose ps
docker-compose logs mongodb
```
3. **构建失败**
```bash
# 清理缓存重新构建
docker system prune -f
docker-compose build --no-cache
```
## 开发环境
本地开发仍可使用原有的 `start_backend.sh` 脚本:
```bash
./start_backend.sh
```
## 许可证
本项目采用 MIT 许可证。

View File

@@ -0,0 +1,32 @@
# 使用官方Python镜像作为基础镜像
FROM python:3.10-slim
# 设置工作目录
WORKDIR /app
# 安装系统依赖(如果需要)
RUN apt-get update && apt-get install -y \
gcc \
&& rm -rf /var/lib/apt/lists/*
# 复制requirements.txt并安装Python依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制应用代码
COPY . .
# 创建非root用户安全最佳实践
RUN useradd --create-home --shell /bin/bash app \
&& chown -R app:app /app
USER app
# 暴露端口
EXPOSE 5002
# 设置环境变量
ENV FLASK_APP=app.py
ENV FLASK_ENV=production
# 启动命令
CMD ["python", "app.py"]

View File

@@ -6,7 +6,7 @@ Created by: 神奇万事通
Date: 2025-09-02
"""
from flask import Flask, jsonify, request, session, send_from_directory
from flask import Flask, jsonify, request, send_from_directory
from flask_cors import CORS
from flask_pymongo import PyMongo
import os
@@ -22,6 +22,7 @@ from modules.aimodelapp import aimodelapp_bp
from config import Config
# 创建Flask应用
def create_app():
"""创建Flask应用实例"""
app = Flask(__name__)

118
InfoGenie-backend/build_docker.sh Executable file
View File

@@ -0,0 +1,118 @@
#!/bin/bash
# InfoGenie 后端 Docker 镜像构建脚本
# Created by: 神奇万事通
# Date: 2025-09-16
set -e
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 配置
IMAGE_NAME="infogenie-backend"
IMAGE_TAG="latest"
DOCKERFILE_PATH="."
# 函数:打印信息
print_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查Docker是否安装
check_docker() {
if ! command -v docker &> /dev/null; then
print_error "Docker 未安装,请先安装 Docker"
exit 1
fi
print_info "Docker 版本: $(docker --version)"
}
# 检查Dockerfile是否存在
check_dockerfile() {
if [ ! -f "Dockerfile" ]; then
print_error "Dockerfile 不存在"
exit 1
fi
print_info "找到 Dockerfile"
}
# 构建Docker镜像
build_image() {
print_info "开始构建 Docker 镜像: ${IMAGE_NAME}:${IMAGE_TAG}"
# 构建镜像
docker build \
--no-cache \
-t ${IMAGE_NAME}:${IMAGE_TAG} \
-f ${DOCKERFILE_PATH}/Dockerfile \
${DOCKERFILE_PATH}
if [ $? -eq 0 ]; then
print_info "Docker 镜像构建成功!"
print_info "镜像信息:"
docker images ${IMAGE_NAME}:${IMAGE_TAG}
else
print_error "Docker 镜像构建失败"
exit 1
fi
}
# 显示使用说明
show_usage() {
echo ""
print_info "构建完成! 使用方法:"
echo ""
echo "1. 运行容器 (需要MongoDB):"
echo " docker run -d \\"
echo " --name infogenie-backend \\"
echo " -p 5002:5002 \\"
echo " -e MONGO_URI=mongodb://host.docker.internal:27017/InfoGenie \\"
echo " -e SECRET_KEY=your-secret-key \\"
echo " -e MAIL_USERNAME=your-email@qq.com \\"
echo " -e MAIL_PASSWORD=your-app-password \\"
echo " ${IMAGE_NAME}:${IMAGE_TAG}"
echo ""
echo "2. 使用 Docker Compose (推荐):"
echo " 创建 docker-compose.yml 文件并运行:"
echo " docker-compose up -d"
echo ""
echo "3. 查看日志:"
echo " docker logs infogenie-backend"
echo ""
echo "4. 停止容器:"
echo " docker stop infogenie-backend"
echo " docker rm infogenie-backend"
}
# 主函数
main() {
print_info "InfoGenie 后端 Docker 镜像构建脚本"
print_info "=================================="
# 检查环境
check_docker
check_dockerfile
# 构建镜像
build_image
# 显示使用说明
show_usage
print_info "构建脚本执行完成!"
}
# 执行主函数
main "$@"

View File

@@ -22,14 +22,14 @@ class Config:
# MongoDB 配置
MONGO_URI = os.environ.get('MONGO_URI') or 'mongodb://localhost:27017/InfoGenie'
# Session 配置
PERMANENT_SESSION_LIFETIME = timedelta(days=7) # 会话持续7天
SESSION_COOKIE_SECURE = False # 开发环境设为False生产环境设为True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
SESSION_COOKIE_DOMAIN = None # 开发环境设为None生产环境设为具体域名
SESSION_COOKIE_PATH = '/'
SESSION_REFRESH_EACH_REQUEST = True # 每次请求刷新会话过期时间
# hwt 配置
HWT_LIFETIME = timedelta(days=7) # hwt持续7天
HWT_SECURE = False # 开发环境设为False生产环境设为True
HWT_HTTPONLY = True
HWT_SAMESITE = 'Lax'
HWT_DOMAIN = None # 开发环境设为None生产环境设为具体域名
HWT_PATH = '/'
HWT_REFRESH_EACH_REQUEST = True # 每次请求刷新hwt过期时间
# 邮件配置
MAIL_SERVER = 'smtp.qq.com'
@@ -68,7 +68,7 @@ class ProductionConfig(Config):
"""生产环境配置"""
DEBUG = False
TESTING = False
SESSION_COOKIE_SECURE = True
HWT_SECURE = True
class TestingConfig(Config):
"""测试环境配置"""

View File

@@ -0,0 +1,53 @@
version: '3.8'
services:
# InfoGenie 后端服务
infogenie-backend:
build:
context: .
dockerfile: Dockerfile
ports:
- "5002:5002"
environment:
- FLASK_ENV=production
- SECRET_KEY=${SECRET_KEY:-infogenie-secret-key-2025}
- MONGO_URI=mongodb://mongodb:27017/InfoGenie
- MAIL_USERNAME=${MAIL_USERNAME:-your-email@qq.com}
- MAIL_PASSWORD=${MAIL_PASSWORD:-your-app-password}
- HWT_SECURE=false
depends_on:
- mongodb
networks:
- infogenie-network
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5002/api/health"]
interval: 30s
timeout: 10s
retries: 3
# MongoDB 数据库
mongodb:
image: mongo:6.0
ports:
- "27017:27017"
environment:
- MONGO_INITDB_DATABASE=InfoGenie
volumes:
- mongodb_data:/data/db
- ./mongo-init:/docker-entrypoint-initdb.d
networks:
- infogenie-network
restart: unless-stopped
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 30s
timeout: 10s
retries: 3
volumes:
mongodb_data:
networks:
infogenie-network:
driver: bridge

View File

@@ -6,15 +6,120 @@ Created by: 神奇万事通
Date: 2025-01-15
"""
from flask import Blueprint, request, jsonify
from flask import Blueprint, request, jsonify, current_app
import requests
import json
import os
from datetime import datetime
from bson import ObjectId
from functools import wraps
# 创建蓝图
aimodelapp_bp = Blueprint('aimodelapp', __name__)
# AI功能萌芽币消耗配置
AI_COST = 100 # 每次调用AI功能消耗的萌芽币数量
# 验证用户萌芽币余额装饰器
def verify_user_coins(f):
"""验证用户萌芽币余额并在调用AI功能后扣除相应数量的萌芽币"""
@wraps(f)
def decorated(*args, **kwargs):
try:
# 获取用户认证信息
token = request.headers.get('Authorization')
if not token:
return jsonify({
'success': False,
'message': '未提供认证信息',
'error_code': 'auth_required'
}), 401
if token.startswith('Bearer '):
token = token[7:]
# 解析JWT token
import jwt
try:
payload = jwt.decode(token, current_app.config['SECRET_KEY'], algorithms=['HS256'])
user_id = payload['user_id']
except Exception as jwt_error:
print(f"JWT解析错误: {str(jwt_error)}")
return jsonify({
'success': False,
'message': '无效的认证信息',
'error_code': 'invalid_token'
}), 401
# 查询用户萌芽币余额
users_collection = current_app.mongo.db.userdata
user = users_collection.find_one({'_id': ObjectId(user_id)})
if not user:
return jsonify({
'success': False,
'message': '用户不存在',
'error_code': 'user_not_found'
}), 404
# 检查萌芽币余额
current_coins = user.get('萌芽币', 0)
if current_coins < AI_COST:
return jsonify({
'success': False,
'message': f'萌芽币余额不足!当前余额: {current_coins}, 需要: {AI_COST}',
'error_code': 'insufficient_coins',
'current_coins': current_coins,
'required_coins': AI_COST
}), 402
# 先扣除萌芽币,确保无论服务是否成功都会扣费
deduct_result = users_collection.update_one(
{'_id': ObjectId(user_id)},
{'$inc': {'萌芽币': -AI_COST}}
)
if deduct_result.modified_count < 1:
print(f"警告: 用户 {user_id} 萌芽币扣除失败")
# 为请求添加用户信息,以便在函数内部使用
request.current_user = {
'user_id': user_id,
'username': user.get('用户名', ''),
'email': user.get('邮箱', '')
}
# 保存API调用类型
api_type = request.path.split('/')[-1]
# 添加使用记录
usage_record = {
'api_type': api_type,
'timestamp': datetime.now().isoformat(),
'cost': AI_COST
}
# 更新用户的AI使用历史记录
users_collection.update_one(
{'_id': ObjectId(user_id)},
{'$push': {'ai_usage_history': usage_record}}
)
# 调用原函数
result = f(*args, **kwargs)
return result
except Exception as e:
print(f"验证萌芽币时发生错误: {str(e)}")
return jsonify({
'success': False,
'message': '处理请求时出错',
'error': str(e)
}), 500
return decorated
#加载AI配置文件
def load_ai_config():
"""加载AI配置文件"""
@@ -126,6 +231,7 @@ def call_kimi_api(messages, model="kimi-k2-0905-preview"):
#统一的AI聊天接口
@aimodelapp_bp.route('/chat', methods=['POST'])
@verify_user_coins
def ai_chat():
"""统一的AI聊天接口"""
try:
@@ -166,6 +272,7 @@ def ai_chat():
#姓名分析专用接口
@aimodelapp_bp.route('/name-analysis', methods=['POST'])
@verify_user_coins
def name_analysis():
"""姓名分析专用接口"""
try:
@@ -228,6 +335,7 @@ def name_analysis():
#变量命名助手接口
@aimodelapp_bp.route('/variable-naming', methods=['POST'])
@verify_user_coins
def variable_naming():
"""变量命名助手接口"""
try:
@@ -329,7 +437,9 @@ def variable_naming():
except Exception as e:
return jsonify({'error': f'变量命名失败: {str(e)}'}), 500
#AI写诗助手接口
@aimodelapp_bp.route('/poetry', methods=['POST'])
@verify_user_coins
def poetry_assistant():
"""AI写诗助手接口"""
try:
@@ -379,7 +489,9 @@ def poetry_assistant():
except Exception as e:
return jsonify({'error': f'诗歌创作失败: {str(e)}'}), 500
#AI语言翻译接口
@aimodelapp_bp.route('/translation', methods=['POST'])
@verify_user_coins
def translation():
"""AI语言翻译接口"""
try:
@@ -468,6 +580,7 @@ def translation():
#现代文转文言文接口
@aimodelapp_bp.route('/classical_conversion', methods=['POST'])
@verify_user_coins
def classical_conversion():
"""现代文转文言文接口"""
try:
@@ -548,6 +661,7 @@ def classical_conversion():
#AI表情制作器接口
@aimodelapp_bp.route('/expression-maker', methods=['POST'])
@verify_user_coins
def expression_maker():
"""AI表情制作器接口"""
try:
@@ -672,6 +786,7 @@ def expression_maker():
#Linux命令生成接口
@aimodelapp_bp.route('/linux-command', methods=['POST'])
@verify_user_coins
def linux_command_generator():
"""Linux命令生成接口"""
try:
@@ -740,6 +855,80 @@ def linux_command_generator():
except Exception as e:
return jsonify({'error': f'Linux命令生成失败: {str(e)}'}), 500
#获取用户萌芽币余额
@aimodelapp_bp.route('/coins', methods=['GET'])
def get_user_coins():
"""获取用户萌芽币余额"""
try:
# 获取用户认证信息
token = request.headers.get('Authorization')
if not token:
return jsonify({
'success': False,
'message': '未提供认证信息',
'error_code': 'auth_required'
}), 401
if token.startswith('Bearer '):
token = token[7:]
# 解析JWT token
import jwt
try:
payload = jwt.decode(token, current_app.config['SECRET_KEY'], algorithms=['HS256'])
user_id = payload['user_id']
except jwt.ExpiredSignatureError:
return jsonify({
'success': False,
'message': 'Token已过期请重新登录',
'error_code': 'token_expired'
}), 401
except Exception as e:
return jsonify({
'success': False,
'message': f'无效的认证信息: {str(e)}',
'error_code': 'invalid_token'
}), 401
# 查询用户萌芽币余额
users_collection = current_app.mongo.db.userdata
user = users_collection.find_one({'_id': ObjectId(user_id)})
if not user:
return jsonify({
'success': False,
'message': '用户不存在',
'error_code': 'user_not_found'
}), 404
# 返回萌芽币信息
current_coins = user.get('萌芽币', 0)
username = user.get('用户名', '用户')
# 增加额外有用信息
ai_usage_history = []
if 'ai_usage_history' in user:
ai_usage_history = user['ai_usage_history'][-5:] # 最近5条使用记录
return jsonify({
'success': True,
'data': {
'coins': current_coins,
'ai_cost': AI_COST,
'can_use_ai': current_coins >= AI_COST,
'username': username,
'usage_count': len(ai_usage_history),
'recent_usage': ai_usage_history
},
'message': f'当前萌芽币余额: {current_coins}'
}), 200
except Exception as e:
return jsonify({
'success': False,
'message': '处理请求时出错',
'error': str(e)
}), 500
#获取可用的AI模型列表
@aimodelapp_bp.route('/models', methods=['GET'])
def get_available_models():

View File

@@ -6,7 +6,7 @@ Created by: 神奇万事通
Date: 2025-09-02
"""
from flask import Blueprint, request, jsonify, session, current_app
from flask import Blueprint, request, jsonify, current_app
from werkzeug.security import generate_password_hash, check_password_hash
import hashlib
import re
@@ -17,6 +17,7 @@ from .email_service import send_verification_email, verify_code, is_qq_email, ge
auth_bp = Blueprint('auth', __name__)
#生成JWT token
def generate_token(user_data):
"""生成JWT token"""
payload = {
@@ -28,6 +29,7 @@ def generate_token(user_data):
}
return jwt.encode(payload, current_app.config['SECRET_KEY'], algorithm='HS256')
#验证JWT token
def verify_token(token):
"""验证JWT token"""
try:
@@ -38,6 +40,7 @@ def verify_token(token):
except jwt.InvalidTokenError:
return {'success': False, 'message': 'Token无效'}
#JWT token验证装饰器
def token_required(f):
"""JWT token验证装饰器"""
@wraps(f)
@@ -57,14 +60,17 @@ def token_required(f):
return f(*args, **kwargs)
return decorated
#验证QQ邮箱格式
def validate_qq_email(email):
"""验证QQ邮箱格式"""
return is_qq_email(email)
#验证密码格式
def validate_password(password):
"""验证密码格式6-20位"""
return 6 <= len(password) <= 20
#发送验证码邮件
@auth_bp.route('/send-verification', methods=['POST'])
def send_verification():
"""发送验证码邮件"""
@@ -120,6 +126,7 @@ def send_verification():
'message': '发送失败,请稍后重试'
}), 500
#验证验证码
@auth_bp.route('/verify-code', methods=['POST'])
def verify_verification_code():
"""验证验证码"""
@@ -150,6 +157,7 @@ def verify_verification_code():
'message': '验证失败,请稍后重试'
}), 500
#用户注册
@auth_bp.route('/register', methods=['POST'])
def register():
"""用户注册(需要先验证邮箱)"""
@@ -253,43 +261,8 @@ def register():
'success': False,
'message': '注册失败,请稍后重试'
}), 500
if existing_user:
return jsonify({
'success': False,
'message': '该账号已被注册'
}), 409
# 创建新用户
password_hash = generate_password_hash(password)
user_data = {
'账号': account,
'密码': password_hash,
'注册时间': datetime.now().isoformat(),
'最后登录': None,
'登录次数': 0,
'用户状态': 'active'
}
result = users_collection.insert_one(user_data)
if result.inserted_id:
return jsonify({
'success': True,
'message': '注册成功!'
}), 201
else:
return jsonify({
'success': False,
'message': '注册失败,请稍后重试'
}), 500
except Exception as e:
return jsonify({
'success': False,
'message': f'服务器错误: {str(e)}'
}), 500
#用户登录
@auth_bp.route('/login', methods=['POST'])
def login():
"""用户登录(支持邮箱+验证码或邮箱+密码)"""
@@ -392,9 +365,10 @@ def login():
}), 500
# 登录成功,创建会话
session['user_id'] = str(user['_id'])
session['account'] = user['账号']
session['logged_in'] = True
hwt = getattr(request, 'hwt', {})
hwt['user_id'] = str(user['_id'])
hwt['account'] = user['账号']
hwt['logged_in'] = True
# 更新登录信息
users_collection.update_one(
@@ -421,6 +395,7 @@ def login():
'message': f'服务器错误: {str(e)}'
}), 500
#用户登出
@auth_bp.route('/logout', methods=['POST'])
def logout():
"""用户登出"""
@@ -437,6 +412,7 @@ def logout():
'message': f'服务器错误: {str(e)}'
}), 500
#检查登录状态
@auth_bp.route('/check', methods=['GET'])
def check_login():
"""检查登录状态"""

View File

@@ -18,15 +18,18 @@ import os
# 验证码存储生产环境建议使用Redis
verification_codes = {}
# 初始化日志
def init_mail(app):
"""初始化邮件配置"""
# 使用smtplib直接发送不需要Flask-Mail
pass
# 生成验证码
def generate_verification_code(length=6):
"""生成验证码"""
return ''.join(random.choices(string.digits, k=length))
# 发送验证邮件
def send_verification_email(email, verification_type='register'):
"""
发送验证邮件
@@ -168,6 +171,7 @@ def send_verification_email(email, verification_type='register'):
'message': '邮件发送失败,请稍后重试'
}
# 验证验证码
def verify_code(email, code):
"""
验证验证码
@@ -221,6 +225,7 @@ def verify_code(email, code):
'type': verification_type
}
# 验证QQ邮箱格式
def is_qq_email(email):
"""
验证是否为QQ邮箱
@@ -239,6 +244,7 @@ def is_qq_email(email):
return domain in qq_domains
# 获取QQ头像URL
def get_qq_avatar_url(email):
"""
根据QQ邮箱获取QQ头像URL
@@ -262,6 +268,7 @@ def get_qq_avatar_url(email):
# 返回QQ头像API URL
return f"http://q1.qlogo.cn/g?b=qq&nk={qq_number}&s=100"
# 清理过期验证码
def cleanup_expired_codes():
"""清理过期的验证码"""
current_time = datetime.now()

View File

@@ -6,7 +6,7 @@ Created by: 神奇万事通
Date: 2025-09-02
"""
from flask import Blueprint, request, jsonify, session, current_app
from flask import Blueprint, request, jsonify, current_app
from datetime import datetime
from bson import ObjectId
import jwt
@@ -14,6 +14,7 @@ from functools import wraps
user_bp = Blueprint('user', __name__)
# 验证JWT token
def verify_token(token):
"""验证JWT token"""
try:
@@ -24,8 +25,9 @@ def verify_token(token):
except jwt.InvalidTokenError:
return {'success': False, 'message': 'Token无效'}
# 登录验证装饰器支持JWT token和hwt
def login_required(f):
"""登录验证装饰器支持JWT token和session"""
"""登录验证装饰器支持JWT token和hwt"""
@wraps(f)
def decorated_function(*args, **kwargs):
# 优先检查JWT token
@@ -38,32 +40,32 @@ def login_required(f):
if result['success']:
request.current_user = result['data']
return f(*args, **kwargs)
# 回退到session验证
if not session.get('logged_in'):
# 回退到hwt验证
hwt = getattr(request, 'hwt', {})
if not hwt.get('logged_in'):
return jsonify({
'success': False,
'message': '请先登录'
}), 401
return f(*args, **kwargs)
return decorated_function
return decorated_function
# 获取用户资料
@user_bp.route('/profile', methods=['GET'])
@login_required
def get_profile():
"""获取用户资料"""
try:
user_id = session.get('user_id')
hwt = getattr(request, 'hwt', {})
user_id = hwt.get('user_id')
users_collection = current_app.mongo.db.userdata
user = users_collection.find_one({'_id': ObjectId(user_id)})
if not user:
return jsonify({
'success': False,
'message': '用户不存在'
}), 404
# 返回用户信息(不包含密码)
profile = {
'account': user['账号'],
@@ -72,18 +74,17 @@ def get_profile():
'login_count': user.get('登录次数', 0),
'status': user.get('用户状态', 'active')
}
return jsonify({
'success': True,
'data': profile
}), 200
except Exception as e:
return jsonify({
'success': False,
'message': f'服务器错误: {str(e)}'
}), 500
# 修改密码
@user_bp.route('/change-password', methods=['POST'])
@login_required
def change_password():
@@ -105,34 +106,28 @@ def change_password():
'message': '新密码长度必须在6-20位之间'
}), 400
user_id = session.get('user_id')
hwt = getattr(request, 'hwt', {})
user_id = hwt.get('user_id')
users_collection = current_app.mongo.db.userdata
user = users_collection.find_one({'_id': ObjectId(user_id)})
if not user:
return jsonify({
'success': False,
'message': '用户不存在'
}), 404
from werkzeug.security import check_password_hash, generate_password_hash
# 验证旧密码
if not check_password_hash(user['密码'], old_password):
return jsonify({
'success': False,
'message': '原密码错误'
}), 401
# 更新密码
new_password_hash = generate_password_hash(new_password)
result = users_collection.update_one(
{'_id': ObjectId(user_id)},
{'$set': {'密码': new_password_hash}}
)
if result.modified_count > 0:
return jsonify({
'success': True,
@@ -143,20 +138,20 @@ def change_password():
'success': False,
'message': '密码修改失败'
}), 500
except Exception as e:
return jsonify({
'success': False,
'message': f'服务器错误: {str(e)}'
}), 500
# 获取用户统计信息
@user_bp.route('/stats', methods=['GET'])
@login_required
def get_user_stats():
"""获取用户统计信息"""
try:
user_id = session.get('user_id')
hwt = getattr(request, 'hwt', {})
user_id = hwt.get('user_id')
# 这里可以添加更多统计信息比如API调用次数等
stats = {
'login_today': 1, # 今日登录次数
@@ -165,18 +160,17 @@ def get_user_stats():
'join_days': 1, # 加入天数
'last_activity': datetime.now().isoformat()
}
return jsonify({
'success': True,
'data': stats
}), 200
except Exception as e:
return jsonify({
'success': False,
'message': f'服务器错误: {str(e)}'
}), 500
# 获取用户游戏数据
@user_bp.route('/game-data', methods=['GET'])
@login_required
def get_user_game_data():
@@ -186,7 +180,8 @@ def get_user_game_data():
if hasattr(request, 'current_user'):
user_id = request.current_user['user_id']
else:
user_id = session.get('user_id')
hwt = getattr(request, 'hwt', {})
user_id = hwt.get('user_id')
users_collection = current_app.mongo.db.userdata
@@ -221,6 +216,7 @@ def get_user_game_data():
'message': f'服务器错误: {str(e)}'
}), 500
# 每日签到
@user_bp.route('/checkin', methods=['POST'])
@login_required
def daily_checkin():
@@ -230,7 +226,8 @@ def daily_checkin():
if hasattr(request, 'current_user'):
user_id = request.current_user['user_id']
else:
user_id = session.get('user_id')
hwt = getattr(request, 'hwt', {})
user_id = hwt.get('user_id')
users_collection = current_app.mongo.db.userdata
@@ -350,6 +347,7 @@ def daily_checkin():
'message': f'服务器错误: {str(e)}'
}), 500
# 删除账户
@user_bp.route('/delete', methods=['POST'])
@login_required
def delete_account():
@@ -364,7 +362,8 @@ def delete_account():
'message': '请输入密码确认删除'
}), 400
user_id = session.get('user_id')
hwt = getattr(request, 'hwt', {})
user_id = hwt.get('user_id')
users_collection = current_app.mongo.db.userdata
user = users_collection.find_one({'_id': ObjectId(user_id)})
@@ -389,7 +388,8 @@ def delete_account():
if result.deleted_count > 0:
# 清除会话
session.clear()
hwt = getattr(request, 'hwt', {})
hwt.clear()
return jsonify({
'success': True,

View File

@@ -0,0 +1,396 @@
# InfoGenie 后端架构文档
## 项目概述
InfoGenie神奇万事通是一个基于前后端分离架构的多功能聚合软件应用。后端采用Flask框架提供RESTful API服务前端通过HTTP请求调用后端API实现数据交互和业务逻辑处理。
## 技术栈
### 核心框架
- **Web框架**: Flask 2.3.3
- **数据库**: MongoDB (Flask-PyMongo 2.3.0)
- **认证**: JWT (PyJWT 2.8.0)
- **跨域**: Flask-CORS 4.0.0
### 辅助工具
- **邮件服务**: Flask-Mail 0.9.1
- **密码加密**: Werkzeug 2.3.7
- **环境配置**: python-dotenv 1.0.0
- **API限流**: Flask-Limiter 3.5.0
## 架构设计原则
### 前后端分离
- 后端专注于数据处理和业务逻辑
- 前端负责用户界面和交互体验
- 通过RESTful API进行数据交换
- 完全解耦,便于独立开发和部署
### 模块化设计
- 按功能划分独立模块
- 每个模块职责单一
- 便于维护和扩展
## 核心模块详解
### 1. 认证模块 (auth.py)
**功能职责**:
- 用户注册和登录
- JWT Token生成和管理
- 邮箱验证码验证
- QQ邮箱格式验证
**API端点**:
```
POST /api/auth/send-verification # 发送验证码
POST /api/auth/verify-code # 验证验证码
POST /api/auth/register # 用户注册
POST /api/auth/login # 用户登录
POST /api/auth/logout # 用户登出
GET /api/auth/check # 检查登录状态
```
**数据流程**:
1. 前端发送注册/登录请求
2. 后端验证邮箱格式仅支持QQ邮箱
3. 发送验证码邮件到用户邮箱
4. 用户输入验证码完成验证
5. 验证成功后生成JWT Token返回给前端
**安全特性**:
- 密码使用Werkzeug进行哈希加密
- JWT Token 7天有效期
- 验证码5分钟有效期限制尝试次数
### 2. 用户管理模块 (user_management.py)
**功能职责**:
- 用户资料管理
- 密码修改
- 每日签到系统
- 用户游戏数据管理
- 账户删除
**API端点**:
```
GET /api/user/profile # 获取用户资料
POST /api/user/change-password # 修改密码
GET /api/user/stats # 获取用户统计
GET /api/user/game-data # 获取游戏数据
POST /api/user/checkin # 每日签到
POST /api/user/delete # 删除账户
```
**数据结构**:
```json
{
"邮箱": "user@qq.com",
"用户名": "用户名",
"密码": "哈希密码",
"头像": "QQ头像URL",
"注册时间": "2025-01-01T00:00:00",
"最后登录": "2025-01-01T00:00:00",
"登录次数": 10,
"用户状态": "active",
"等级": 5,
"经验": 1200,
"萌芽币": 1500,
"签到系统": {
"连续签到天数": 7,
"今日是否已签到": true,
"签到时间": "2025-01-01"
}
}
```
**业务逻辑**:
- 签到奖励300萌芽币 + 200经验
- 等级升级100 × 1.2^(等级) 经验需求
### 3. 邮件服务模块 (email_service.py)
**功能职责**:
- 验证码邮件发送
- QQ邮箱格式验证
- QQ头像获取
- 邮件模板管理
**邮件模板**:
- 注册验证码邮件HTML格式
- 登录验证码邮件HTML格式
- 支持自定义邮件内容和样式
**安全考虑**:
- 仅支持QQ邮箱qq.com、vip.qq.com、foxmail.com
- 使用SSL加密连接
- 验证码存储在内存中生产环境建议使用Redis
### 4. AI模型应用模块 (aimodelapp.py)
**功能职责**:
- 集成多种AI服务DeepSeek、Kimi
- 提供AI功能API接口
- 统一AI接口调用
- 管理用户萌芽币消费每次调用消耗100萌芽币
**支持的AI功能**:
1. **AI聊天接口** (`/api/aimodelapp/chat`)
2. **姓名分析** (`/api/aimodelapp/name-analysis`)
3. **变量命名助手** (`/api/aimodelapp/variable-naming`)
4. **AI写诗助手** (`/api/aimodelapp/poetry`)
5. **AI语言翻译** (`/api/aimodelapp/translation`)
6. **现代文转文言文** (`/api/aimodelapp/classical_conversion`)
7. **AI表情制作器** (`/api/aimodelapp/expression-maker`)
8. **Linux命令生成** (`/api/aimodelapp/linux-command`)
9. **获取可用模型** (`/api/aimodelapp/models`)
**AI配置**:
```json
{
"deepseek": {
"api_key": "your-api-key",
"api_base": "https://api.deepseek.com",
"model": ["deepseek-chat", "deepseek-reasoner"]
},
"kimi": {
"api_key": "your-api-key",
"api_base": "https://api.moonshot.cn",
"model": ["kimi-k2-0905-preview", "kimi-k2-0711-preview"]
}
}
```
**调用流程**:
1. 前端发送AI请求包含消息、模型提供商等参数
2. 后端加载AI配置文件
3. 调用对应AI API带重试机制
4. 返回AI响应给前端
## API设计规范
### 请求/响应格式
**成功响应**:
```json
{
"success": true,
"data": {...},
"message": "操作成功",
"timestamp": "2025-01-01T00:00:00"
}
```
**错误响应**:
```json
{
"success": false,
"message": "错误信息",
"error": "错误详情"
}
```
### 认证方式
**JWT Token认证**:
```
Authorization: Bearer <token>
```
**支持的认证端点**:
- 所有 `/api/user/*` 端点需要认证
- 部分 `/api/aimodelapp/*` 端点需要认证
### 错误处理
**HTTP状态码**:
- 200: 成功
- 400: 请求参数错误
- 401: 未认证/认证失败
- 403: 权限不足
- 404: 资源不存在
- 409: 资源冲突
- 500: 服务器内部错误
## 数据库设计
### MongoDB集合
**主要集合**: `userdata`
- 存储所有用户相关数据
- 支持动态字段扩展
- 使用ObjectId作为用户唯一标识
### 数据关系
- 用户数据自包含,无复杂关联
- 通过用户ID进行数据关联
- 支持水平扩展
## 部署和配置
### 环境配置
**必需环境变量**:
```
SECRET_KEY=your-secret-key
MONGO_URI=mongodb://localhost:27017/InfoGenie
MAIL_USERNAME=your-email@qq.com
MAIL_PASSWORD=your-app-password
```
### 启动方式
**开发环境**:
```bash
python app.py
```
**生产环境**:
- 支持Docker部署
- 提供docker-compose配置
- 支持Gunicorn WSGI服务器
### 静态文件服务
**支持的前端资源**:
- `/60sapi/*`: 60秒API相关文件
- `/smallgame/*`: 小游戏相关文件
- `/aimodelapp/*`: AI模型应用相关文件
## 安全考虑
### 数据安全
- 密码哈希存储
- JWT Token安全传输
- 输入数据验证和过滤
### API安全
- CORS配置生产环境限制域名
- API限流保护
- 请求日志记录
### 部署安全
- 环境变量管理敏感信息
- HTTPS证书配置
- 防火墙和访问控制
## 前后端协作指南
### 前端调用示例
**用户登录**:
```javascript
// 1. 发送验证码
fetch('/api/auth/send-verification', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'user@qq.com', type: 'login' })
});
// 2. 验证验证码并登录
fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: 'user@qq.com',
code: '123456'
})
});
// 3. 保存token到localStorage
localStorage.setItem('token', response.token);
```
**调用需要认证的API**:
```javascript
fetch('/api/user/profile', {
method: 'GET',
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
```
### 数据约定
**前端发送数据格式**:
- 所有请求使用JSON格式
- 必填字段验证
- 参数命名使用snake_case
**后端返回数据格式**:
- 统一响应格式
- 时间戳使用ISO格式
- 错误信息清晰明确
### 开发协作流程
1. **API设计阶段**:
- 后端定义API接口规范
- 前端根据规范开发调用代码
- 约定数据格式和错误处理
2. **联调阶段**:
- 使用统一的测试数据
- 验证各种边界情况
- 确认错误处理逻辑
3. **部署阶段**:
- 后端部署API服务
- 前端配置API基础URL
- 验证跨域和认证配置
## 新功能添加
### 1. AI功能萌芽币消费系统
**功能描述**:
- 用户每次调用AI模型应用aimodelapp需消耗100萌芽币
- 当用户萌芽币余额不足时无法使用AI功能
- 记录用户的AI使用历史
**API端点**:
```
GET /api/aimodelapp/coins # 查询用户萌芽币余额和使用历史
```
**技术实现**:
- 使用装饰器模式实现请求前验证和扣除萌芽币
- 在MongoDB中记录用户AI使用历史
- 通过JWT Token验证用户身份
**业务逻辑**:
1. 当用户请求AI功能时首先验证JWT Token
2. 检查用户萌芽币余额是否≥100
3. 如余额充足先扣除萌芽币然后再调用AI服务
4. 记录使用历史包括API类型、时间和消费萌芽币数量
5. 返回AI服务结果给用户
**响应示例(查询萌芽币余额)**:
```json
{
"success": true,
"data": {
"coins": 200,
"ai_cost": 100,
"can_use_ai": true,
"username": "用户名",
"usage_count": 1,
"recent_usage": [
{
"api_type": "chat",
"cost": 100,
"timestamp": "2025-09-16T11:15:47.285720"
}
]
},
"message": "当前萌芽币余额: 200"
}
```
**前端开发注意事项**:
- 每个需要调用AI功能的页面应首先检查用户萌芽币余额
- 当萌芽币不足时,向用户提示并引导用户通过签到等方式获取萌芽币
- 可在UI中展示用户最近的AI使用记录和萌芽币消费情况
---

View File

@@ -37,6 +37,7 @@
</div>
</div>
<script src="../coin-manager.js"></script>
<script src="env.js"></script>
<script src="script.js"></script>
</body>

View File

@@ -40,10 +40,18 @@ const namingConventions = {
// 调用后端API
async function callBackendAPI(description) {
try {
// 获取JWT token
const token = localStorage.getItem('token');
if (!token) {
throw new Error('未登录请先登录后使用AI功能');
}
const response = await fetch('http://127.0.0.1:5002/api/aimodelapp/variable-naming', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
description: description
@@ -208,15 +216,30 @@ async function generateSuggestions() {
return;
}
// 检查萌芽币余额是否足够
if (window.coinManager && !window.coinManager.checkBeforeApiCall()) {
return;
}
showLoading(true);
suggestionsContainer.innerHTML = '';
try {
const suggestions = await callBackendAPI(description);
displaySuggestions(suggestions);
// 刷新萌芽币信息
if (window.coinManager) {
window.coinManager.loadCoinsInfo();
}
} catch (error) {
console.error('生成建议失败:', error);
showErrorMessage(`生成失败: ${error.message}`);
// 检查是否是萌芽币不足导致的错误
if (error.message && error.message.includes('萌芽币余额不足')) {
showErrorMessage(`萌芽币不足: 每次使用AI功能需要消耗100萌芽币请通过每日签到获取更多萌芽币`);
} else {
showErrorMessage(`生成失败: ${error.message}`);
}
} finally {
showLoading(false);
}

View File

@@ -0,0 +1,288 @@
/**
* InfoGenie 萌芽币管理工具
* 此模块负责管理用户AI功能的萌芽币余额和消费
* 为所有AI模型应用提供统一的萌芽币检查和显示功能
*/
class CoinManager {
constructor() {
// 状态变量
this.coins = 0;
this.aiCost = 100;
this.canUseAi = false;
this.username = '';
this.usageCount = 0;
this.recentUsage = [];
this.isLoaded = false;
this.isLoading = false;
this.error = null;
// UI元素
this.coinInfoContainer = null;
// 初始化
this.init();
}
/**
* 初始化萌芽币管理器
*/
async init() {
// 创建UI元素
this.createCoinInfoUI();
// 加载萌芽币信息
await this.loadCoinsInfo();
// 监听网络状态变化
window.addEventListener('online', () => this.loadCoinsInfo());
}
/**
* 创建萌芽币信息UI
*/
createCoinInfoUI() {
// 检查是否已创建
if (this.coinInfoContainer) {
return;
}
// 创建容器
this.coinInfoContainer = document.createElement('div');
this.coinInfoContainer.className = 'coin-info-container';
this.coinInfoContainer.style = `
position: fixed;
top: 10px;
right: 10px;
background: rgba(255, 255, 255, 0.95);
border-radius: 8px;
padding: 12px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
z-index: 9999;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
max-width: 300px;
transition: all 0.3s ease;
border: 1px solid rgba(74, 222, 128, 0.4);
`;
// 更新UI内容
this.updateCoinInfoUI();
// 添加到页面
document.body.appendChild(this.coinInfoContainer);
}
/**
* 更新萌芽币信息UI
*/
updateCoinInfoUI() {
if (!this.coinInfoContainer) {
return;
}
let content = '';
if (this.isLoading) {
content = '<div style="text-align: center; padding: 10px;">加载中...</div>';
} else if (this.error) {
content = `
<div style="color: #d32f2f; text-align: center; padding: 10px;">
<div style="font-weight: bold; margin-bottom: 5px;">加载失败</div>
<div style="font-size: 12px;">${this.error}</div>
<button
onclick="coinManager.loadCoinsInfo()"
style="
background: #4ade80;
color: white;
border: none;
padding: 5px 10px;
border-radius: 4px;
margin-top: 8px;
cursor: pointer;
"
>
重试
</button>
</div>
`;
} else if (!this.isLoaded) {
content = '<div style="text-align: center; padding: 10px;">正在检查萌芽币余额...</div>';
} else {
const usageHistory = this.recentUsage.length > 0
? `
<div style="margin-top: 8px; border-top: 1px solid #eee; padding-top: 8px;">
<div style="font-size: 12px; color: #666; margin-bottom: 5px;">最近使用记录:</div>
${this.recentUsage.map(usage => `
<div style="font-size: 11px; color: #555; margin: 3px 0;">
${this.formatApiType(usage.api_type)} (-${usage.cost}币)
<span style="color: #999; float: right;">${this.formatDate(usage.timestamp)}</span>
</div>
`).join('')}
</div>
`
: '';
content = `
<div style="display: flex; align-items: center; justify-content: space-between;">
<div style="font-weight: bold; color: #333;">${this.username || '用户'}的萌芽币</div>
<div style="
background: ${this.canUseAi ? '#4ade80' : '#ef4444'};
color: white;
font-size: 11px;
padding: 2px 6px;
border-radius: 10px;
">
${this.canUseAi ? '可使用' : '币不足'}
</div>
</div>
<div style="margin: 10px 0; display: flex; align-items: center; justify-content: center;">
<div style="
font-size: 28px;
font-weight: bold;
color: ${this.canUseAi ? '#16a34a' : '#dc2626'};
">
${this.coins}
</div>
<div style="margin-left: 5px; font-size: 12px; color: #666;">萌芽币</div>
</div>
<div style="font-size: 12px; color: #666; text-align: center;">
AI功能每次使用消耗 <b>${this.aiCost}</b> 萌芽币
</div>
${usageHistory}
`;
}
this.coinInfoContainer.innerHTML = content;
}
/**
* 加载萌芽币信息
*/
async loadCoinsInfo() {
try {
this.isLoading = true;
this.error = null;
this.updateCoinInfoUI();
// 获取JWT token
const token = localStorage.getItem('token');
if (!token) {
this.error = '未登录,无法获取萌芽币信息';
this.isLoading = false;
this.updateCoinInfoUI();
return;
}
// 调用API
const response = await fetch('/api/aimodelapp/coins', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || '获取萌芽币信息失败');
}
const data = await response.json();
if (data.success) {
// 更新状态
this.coins = data.data.coins;
this.aiCost = data.data.ai_cost;
this.canUseAi = data.data.can_use_ai;
this.username = data.data.username;
this.usageCount = data.data.usage_count;
this.recentUsage = data.data.recent_usage || [];
this.isLoaded = true;
} else {
throw new Error(data.message || '获取萌芽币信息失败');
}
} catch (error) {
console.error('加载萌芽币信息失败:', error);
this.error = error.message || '获取萌芽币信息失败';
} finally {
this.isLoading = false;
this.updateCoinInfoUI();
}
}
/**
* 格式化API类型
*/
formatApiType(apiType) {
const typeMap = {
'chat': 'AI聊天',
'name-analysis': '姓名评测',
'variable-naming': '变量命名',
'poetry': 'AI写诗',
'translation': 'AI翻译',
'classical_conversion': '文言文转换',
'expression-maker': '表情制作',
'linux-command': 'Linux命令'
};
return typeMap[apiType] || apiType;
}
/**
* 格式化日期
*/
formatDate(isoString) {
try {
const date = new Date(isoString);
return `${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`;
} catch (e) {
return isoString;
}
}
/**
* 检查是否有足够的萌芽币
*/
hasEnoughCoins() {
return this.canUseAi;
}
/**
* 显示萌芽币不足提示
*/
showInsufficientCoinsMessage() {
alert(`萌芽币余额不足!\n当前余额:${this.coins},需要:${this.aiCost}\n请通过每日签到等方式获取更多萌芽币。`);
}
/**
* 在API调用前检查萌芽币
* @returns {boolean} 是否有足够的萌芽币
*/
checkBeforeApiCall() {
// 强制刷新萌芽币状态
this.loadCoinsInfo().then(() => {
// 检查余额
if (!this.hasEnoughCoins()) {
this.showInsufficientCoinsMessage();
return false;
}
return true;
});
// 使用当前缓存的状态进行快速检查
if (!this.hasEnoughCoins()) {
this.showInsufficientCoinsMessage();
return false;
}
return true;
}
}
// 创建全局实例
const coinManager = new window.CoinManager = new CoinManager();
// 导出实例
export default coinManager;

View File

@@ -284,6 +284,26 @@ const AiModelPage = () => {
const closeEmbedded = () => {
setEmbeddedApp(null);
};
// 在iframe加载时注入token
const handleIframeLoad = (e) => {
try {
const iframe = e.target;
const token = localStorage.getItem('token');
if (iframe && iframe.contentWindow && token) {
// 将token传递给iframe
iframe.contentWindow.localStorage.setItem('token', token);
// 确保coin-manager.js已加载
if (iframe.contentWindow.coinManager) {
iframe.contentWindow.coinManager.loadCoinsInfo();
}
}
} catch (error) {
console.error('iframe通信错误:', error);
}
};
@@ -393,7 +413,36 @@ const AiModelPage = () => {
</LoginPrompt>
)}
{/* 内嵌显示组件 */}
{/* 萌芽币提示 */}
{isLoggedIn && (
<div style={{
maxWidth: '800px',
margin: '0 auto 40px',
padding: '20px',
background: 'rgba(74, 222, 128, 0.1)',
borderRadius: '12px',
border: '1px solid rgba(74, 222, 128, 0.3)'
}}>
<h3 style={{
display: 'flex',
alignItems: 'center',
gap: '10px',
color: '#16a34a',
marginTop: 0
}}>
<span style={{ fontSize: '24px' }}>💰</span>
萌芽币消费提示
</h3>
<p style={{ lineHeight: '1.6', color: '#374151' }}>
每次使用AI功能将消耗<b>100萌芽币</b>使AI
</p>
<p style={{ lineHeight: '1.6', color: '#374151' }}>
您可以通过<b>每日签到</b>300使AI
</p>
</div>
)}
{/* 内嵌显示组件 */}
{embeddedApp && (
<EmbeddedContainer onClick={closeEmbedded}>
<EmbeddedContent onClick={(e) => e.stopPropagation()}>
@@ -407,6 +456,7 @@ const AiModelPage = () => {
<EmbeddedFrame
src={embeddedApp.link}
title={embeddedApp.title}
onLoad={handleIframeLoad}
/>
</EmbeddedContent>
</EmbeddedContainer>

View File

@@ -95,6 +95,12 @@ export const userAPI = {
// AI模型相关API
export const aiModelAPI = {
// 获取萌芽币余额和使用历史
getCoins: () => api.get('/api/aimodelapp/coins'),
};
// 健康检查
export const healthAPI = {
check: () => api.get('/api/health'),

View File

@@ -0,0 +1,381 @@
# InfoGenie 前端架构文档
## 项目概述
InfoGenie 是一个基于前后端分离架构的全栈 Web 应用,前端采用 React 单页应用SPA架构结合静态 HTML 页面实现丰富的功能模块。后端提供 RESTful API 接口,支持用户认证、数据获取等核心功能。
## 技术栈
### 核心框架
- **React 18.2.0**: 前端 UI 框架,使用函数式组件和 Hooks
- **React Router DOM 6.15.0**: 客户端路由管理
- **Axios 1.5.0**: HTTP 客户端,用于后端 API 调用
### 样式和 UI
- **styled-components 6.0.7**: CSS-in-JS 样式解决方案
- **react-icons 4.11.0**: 图标库
- **react-hot-toast 2.4.1**: 通知提示组件
### 开发工具
- **Create React App**: 项目脚手架
- **ESLint**: 代码规范检查
- **Service Worker**: PWA 支持
## 架构设计
### 整体架构
```
前端应用
├── React SPA (主要页面)
│ ├── 用户认证系统
│ ├── 导航和布局
│ ├── 页面路由
│ └── 用户管理
└── 静态 HTML 页面
├── API 数据展示页面
├── 小游戏页面
└── AI 模型工具页面
```
### 文件结构
```
src/
├── components/ # 公共组件
│ ├── Header.js # 顶部导航栏
│ ├── Navigation.js # 底部导航栏(移动端)
│ └── Footer.js # 页脚
├── pages/ # 页面组件
│ ├── HomePage.js # 首页
│ ├── LoginPage.js # 登录页面
│ ├── Api60sPage.js # API 60s 页面
│ ├── SmallGamePage.js # 小游戏页面
│ ├── AiModelPage.js # AI 模型页面
│ └── UserProfilePage.js # 用户资料页面
├── contexts/ # React Context
│ └── UserContext.js # 用户状态管理
├── config/ # 配置文件
│ └── StaticPageConfig.js # 静态页面配置
├── utils/ # 工具函数
│ └── api.js # API 调用封装
└── styles/ # 全局样式
```
## API 接口设计
### 基础配置
- **Base URL**: `https://infogenie.api.shumengya.top` (这是生产环境)(可通过环境变量 `REACT_APP_API_URL` 配置测试环境)
- **认证方式**: JWT Bearer Token
- **请求格式**: JSON
- **响应格式**: JSON
- **超时时间**: 10秒
### 认证相关接口
#### 发送验证码
```http
POST /api/auth/send-verification
Content-Type: application/json
{
"email": "user@example.com"
}
```
#### 验证验证码
```http
POST /api/auth/verify-code
Content-Type: application/json
{
"email": "user@example.com",
"code": "123456"
}
```
#### 用户登录
```http
POST /api/auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "password"
}
```
#### 用户注册
```http
POST /api/auth/register
Content-Type: application/json
{
"email": "user@example.com",
"password": "password",
"verification_code": "123456"
}
```
#### 用户登出
```http
POST /api/auth/logout
Authorization: Bearer <token>
```
#### 检查登录状态
```http
GET /api/auth/check
Authorization: Bearer <token>
```
### 用户管理接口
#### 获取用户资料
```http
GET /api/user/profile
Authorization: Bearer <token>
```
#### 修改密码
```http
POST /api/user/change-password
Authorization: Bearer <token>
Content-Type: application/json
{
"old_password": "old_password",
"new_password": "new_password"
}
```
#### 获取用户统计
```http
GET /api/user/stats
Authorization: Bearer <token>
```
#### 获取游戏数据
```http
GET /api/user/game-data
Authorization: Bearer <token>
```
#### 用户签到
```http
POST /api/user/checkin
Authorization: Bearer <token>
```
#### 删除账户
```http
POST /api/user/delete
Authorization: Bearer <token>
Content-Type: application/json
{
"password": "password"
}
```
### 数据展示接口
前端包含大量静态页面用于展示各种 API 数据,这些页面直接调用后端提供的公开接口:
#### 热搜榜单系列
- 百度实时热搜: `GET /v2/baidu/realtime`
- 百度贴吧话题榜: `GET /v2/baidu/tieba`
- 哔哩哔哩热搜榜: `GET /v2/bilibili/hot`
- 抖音热搜榜: `GET /v2/douyin/hot`
- 头条热搜榜: `GET /v2/toutiao/hot`
- 微博热搜榜: `GET /v2/weibo/hot`
- 小红书热点: `GET /v2/xiaohongshu/hot`
- 知乎热门话题: `GET /v2/zhihu/hot`
- Hacker News 榜单: `GET /v2/hackernews`
#### 日更资讯系列
- 必应每日壁纸: `GET /v2/bing/wallpaper`
- 历史上的今天: `GET /v2/history/today`
- 每日国际汇率: `GET /v2/exchange/rates`
- 每天60s读懂世界: `GET /v2/60s/world`
#### 实用功能系列
- 百度百科词条: `GET /v2/baike/search?keyword={keyword}`
- 公网IP地址: `GET /v2/ip/public`
- 哈希解压压缩: `POST /v2/hash/{algorithm}`
- 链接OG信息: `GET /v2/og?url={url}`
- 密码强度检测: `POST /v2/password/strength`
- 农历信息: `GET /v2/calendar/lunar?date={date}`
- 配色方案: `GET /v2/color/schemes`
- 身体健康分析: `POST /v2/health/analysis`
- 生成二维码: `POST /v2/qrcode/generate`
- 实时天气: `GET /v2/weather?location={location}`
- 随机密码生成器: `GET /v2/password/random`
- 随机颜色: `GET /v2/color/random`
- 天气预报: `GET /v2/weather/forecast?location={location}`
- 在线翻译: `POST /v2/translate`
- EpicGames免费游戏: `GET /v2/epic/free-games`
#### 娱乐消遣系列
- 随机唱歌音频: `GET /v2/entertainment/random-song`
- 随机发病文学: `GET /v2/entertainment/random-meme`
- 随机搞笑段子: `GET /v2/entertainment/random-joke`
- 随机冷笑话: `GET /v2/entertainment/random-pun`
- 随机一言: `GET /v2/entertainment/random-quote`
- 随机运势: `GET /v2/entertainment/random-fortune`
- 随机JavaScript趣味题: `GET /v2/entertainment/random-js-quiz`
- 随机KFC文案: `GET /v2/entertainment/random-kfc`
## 状态管理
### 用户状态管理
使用 React Context 进行全局状态管理:
```javascript
const UserContext = createContext();
export const UserProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [isLoggedIn, setIsLoggedIn] = useState(false);
// 用户登录、登出、状态检查等方法
};
```
### 本地存储
- 用户信息和 Token 存储在 localStorage 中
- 页面刷新后自动恢复用户状态
## 路由设计
```javascript
const App = () => {
return (
<Router>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/60sapi" element={<Api60sPage />} />
<Route path="/smallgame" element={<SmallGamePage />} />
<Route path="/aimodel" element={<AiModelPage />} />
<Route path="/profile" element={<UserProfilePage />} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</Router>
);
};
```
## 响应式设计
- 移动优先设计理念
- 使用 CSS Grid 和 Flexbox 实现响应式布局
- 媒体查询适配不同屏幕尺寸
- 移动端使用底部导航栏,桌面端使用顶部导航
## 安全考虑
### 前端安全措施
- JWT Token 自动过期和刷新
- XSS 防护:使用 React 自动转义
- CSRF 防护:使用 SameSite Cookie
- 输入验证:前端表单验证
### API 安全要求
- 所有敏感接口需要 JWT 认证
- Token 存储在 localStorage需要后端验证
- 密码等敏感信息前端不存储明文
- API 请求包含 CORS 配置
## 部署和构建
### 构建命令
```bash
npm run build # 生产环境构建
npm start # 开发环境启动
```
### 环境变量
- `REACT_APP_API_URL`: 后端 API 基础地址
- 支持 `.env` 文件配置不同环境的变量
### PWA 支持
- 注册 Service Worker 实现离线缓存
- Web App Manifest 支持安装到桌面
## 与后端协作要点
1. **API 接口约定**: 遵循 RESTful 设计原则,统一响应格式
2. **错误处理**: 后端返回统一的错误格式,前端统一处理
3. **认证流程**: JWT Token 的生成、验证和刷新机制
4. **数据格式**: 前后端约定清晰的数据结构
5. **跨域配置**: 后端需要配置 CORS 允许前端域名
6. **API 版本管理**: 使用 `/v2/` 前缀进行版本控制
7. **性能优化**: 考虑 API 响应时间和前端缓存策略
## 萌芽币消费系统
### 系统概述
萌芽币是平台内部的虚拟货币用于限制和管理用户对AI功能的使用频率。每次调用AI功能需消耗100萌芽币当用户萌芽币不足时无法使用AI功能。
### 技术实现
1. **萌芽币管理器**: `/public/aimodelapp/coin-manager.js`
- 管理用户萌芽币余额和使用记录
- 提供UI组件显示萌芽币信息
- 实现API调用前的余额检查
2. **API集成**:
-`/src/utils/api.js` 中添加萌芽币查询接口
- 所有AI功能API调用必须添加JWT Token认证
- API调用后自动刷新萌芽币余额显示
3. **用户体验**:
- 在页面右上角显示萌芽币余额和使用记录
- 当萌芽币不足时,提供友好的提示
- 引导用户通过签到等方式获取更多萌芽币
### 接口设计
```http
GET /api/aimodelapp/coins
Authorization: Bearer <token>
:
{
"success": true,
"data": {
"coins": 200,
"ai_cost": 100,
"can_use_ai": true,
"username": "",
"usage_count": 5,
"recent_usage": [
{
"api_type": "chat",
"cost": 100,
"timestamp": "2025-09-16T11:15:47.285720"
},
...
]
},
"message": ": 200"
}
```
### 页面集成流程
1. 引入萌芽币管理器 JavaScript 文件
2. 在API调用前检查萌芽币余额
3. 处理API响应中的萌芽币相关错误
4. API调用后刷新萌芽币信息
详细集成步骤请参考 [前端萌芽币消费系统集成文档](/前端萌芽币消费系统集成文档.md)
## 后续扩展建议
1. **状态管理升级**: 可考虑引入 Redux 或 Zustand 进行更复杂的状态管理
2. **组件库**: 可引入 Ant Design 或 Material-UI 统一 UI 组件
3. **测试覆盖**: 添加单元测试和集成测试
4. **性能监控**: 集成前端性能监控工具
5. **国际化**: 支持多语言切换功能
6. **萌芽币系统扩展**:
- 实现萌芽币充值功能
- 针对不同AI功能设置差异化定价
- 添加萌芽币消费统计和分析功能

View File

@@ -0,0 +1,100 @@
# InfoGenie前端萌芽币消费系统集成文档
## 概述
本文档描述了InfoGenie前端如何与后端的萌芽币消费系统进行集成。后端已实现AI模型应用每次调用消耗100萌芽币的功能前端需要相应地支持显示萌芽币余额、使用记录并在API调用前检查余额是否充足。
## 实现细节
### 1. API调用
新增了获取萌芽币余额和使用历史的API调用
```javascript
// 在 /src/utils/api.js 中添加
export const aiModelAPI = {
// 获取萌芽币余额和使用历史
getCoins: () => api.get('/api/aimodelapp/coins'),
};
```
### 2. 萌芽币管理器
创建了统一的萌芽币管理工具 `/public/aimodelapp/coin-manager.js`,提供以下功能:
- 获取和显示用户萌芽币余额
- 显示最近的AI使用记录
- 在调用AI API前检查萌芽币余额是否充足
- 在API调用成功后更新萌芽币信息
### 3. 前端集成
所有AI模型应用页面都需要进行以下修改
1. 引入萌芽币管理器脚本:
```html
<script src="../coin-manager.js"></script>
```
2. 在API调用前添加萌芽币检查
```javascript
// 检查萌芽币余额是否足够
if (window.coinManager && !window.coinManager.checkBeforeApiCall()) {
return;
}
```
3. 确保所有AI API调用都添加JWT Token认证
```javascript
const token = localStorage.getItem('token');
// 添加到请求头
headers: {
'Authorization': `Bearer ${token}`
}
```
4. 在API调用成功后刷新萌芽币信息
```javascript
if (window.coinManager) {
window.coinManager.loadCoinsInfo();
}
```
### 4. 萌芽币提示显示
萌芽币管理器会在页面右上角显示一个悬浮窗口,包含:
- 当前萌芽币余额
- 每次调用消耗的萌芽币数量
- 最近的使用记录
- 当余额不足时的警告提示
### 5. 用户体验优化
- 在API调用失败时会检查是否是因为萌芽币不足导致的并给出相应提示
- 引导用户通过每日签到等方式获取更多萌芽币
- 实时显示萌芽币余额变化
## 使用示例
以AI变量命名助手为例已完成集成
1. 引入coin-manager.js
2. 修改API调用函数添加Token认证
3. 添加萌芽币检查逻辑
4. 添加错误处理,区分普通错误和萌芽币不足错误
## 后续工作
为所有AI模型应用页面添加相同的萌芽币集成包括
- AI写诗小助手
- AI姓名评测
- AI翻译助手
- AI文章转文言文
- AI生成表情包
- AI生成Linux命令
## 注意事项
- 萌芽币消费系统只对AI模型应用有效其他功能不消耗萌芽币
- 每次调用AI API都会消耗100萌芽币无论成功与否
- 用户可以通过每日签到获取萌芽币

View File

@@ -0,0 +1,64 @@
# InfoGenie前端萌芽币消费系统集成报告
## 完成工作概述
根据后端新增的萌芽币消费系统需求,已成功在前端项目中完成了相应的功能集成。具体完成了以下工作:
### 1. API工具扩展
`/src/utils/api.js`中添加了萌芽币余额查询API
```javascript
export const aiModelAPI = {
// 获取萌芽币余额和使用历史
getCoins: () => api.get('/api/aimodelapp/coins'),
};
```
### 2. 萌芽币管理工具实现
创建了`/public/aimodelapp/coin-manager.js`文件,实现了以下功能:
- 萌芽币余额和使用历史查询
- 用户友好的UI显示
- AI API调用前的余额检查
- 错误处理和用户提示
### 3. AI变量命名助手集成示例
完成了AI变量命名助手的萌芽币系统集成
- 引入了coin-manager.js
- 添加了JWT Token认证
- 实现了API调用前的余额检查
- 处理了萌芽币不足的错误情况
- API调用后自动刷新萌芽币信息
### 4. AI模型页面增强
`/src/pages/AiModelPage.js`中添加了以下功能:
- 萌芽币消费提示说明
- iframe加载时的token传递
- 确保嵌入应用正确加载萌芽币管理器
### 5. 文档更新
完成了两份文档的更新:
1. `/前端架构文档.md`: 添加了萌芽币消费系统的架构说明
2. `/前端萌芽币消费系统集成文档.md`: 创建了详细的集成指南
## 后续工作建议
1. 按照集成文档完成其余所有AI应用的萌芽币系统集成
- AI写诗小助手
- AI姓名评测
- AI语言翻译助手
- AI文章转文言文
- AI生成表情包
- AI生成Linux命令
2. 用户体验优化:
- 在用户资料页面显示萌芽币余额和完整的使用历史
- 添加萌芽币获取引导(如签到提醒)
- 考虑实现萌芽币充值功能
3. 性能和安全性优化:
- 优化币管理器的加载性能
- 添加币管理器的错误处理和重试机制
- 确保token传递的安全性
## 结论
萌芽币消费系统的前端集成已基本完成示例应用可以正常工作。系统实现了后端要求的所有功能并提供了良好的用户体验。后续只需按照文档中的步骤将相同的集成方式应用到其余AI应用中即可完成全部工作。