From 17691af8d14a3f862aacf6bc8c610eb3ea4ad262 Mon Sep 17 00:00:00 2001 From: shumengya <3205788256@qq.com> Date: Tue, 16 Sep 2025 12:57:36 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- InfoGenie-backend/.dockerignore | 50 +++ InfoGenie-backend/.env.production | 2 +- InfoGenie-backend/DOCKER_README.md | 163 +++++++ InfoGenie-backend/Dockerfile | 32 ++ InfoGenie-backend/app.py | 3 +- InfoGenie-backend/build_docker.sh | 118 ++++++ InfoGenie-backend/config.py | 18 +- InfoGenie-backend/docker-compose.yml | 53 +++ .../__pycache__/aimodelapp.cpython-310.pyc | Bin 19237 -> 22610 bytes .../modules/__pycache__/auth.cpython-310.pyc | Bin 8565 -> 8406 bytes .../__pycache__/email_service.cpython-310.pyc | Bin 8052 -> 8052 bytes .../user_management.cpython-310.pyc | Bin 7342 -> 7520 bytes InfoGenie-backend/modules/aimodelapp.py | 191 ++++++++- InfoGenie-backend/modules/auth.py | 56 +-- InfoGenie-backend/modules/email_service.py | 7 + InfoGenie-backend/modules/user_management.py | 56 +-- InfoGenie-backend/后端架构文档.md | 396 ++++++++++++++++++ .../aimodelapp/AI变量命名助手/index.html | 1 + .../aimodelapp/AI变量命名助手/script.js | 27 +- .../public/aimodelapp/coin-manager.js | 288 +++++++++++++ InfoGenie-frontend/src/pages/AiModelPage.js | 52 ++- InfoGenie-frontend/src/utils/api.js | 6 + InfoGenie-frontend/前端架构文档.md | 381 +++++++++++++++++ .../前端萌芽币消费系统集成文档.md | 100 +++++ InfoGenie-frontend/萌芽币消费系统集成报告.md | 64 +++ 25 files changed, 1981 insertions(+), 83 deletions(-) create mode 100644 InfoGenie-backend/.dockerignore create mode 100644 InfoGenie-backend/DOCKER_README.md create mode 100644 InfoGenie-backend/Dockerfile create mode 100755 InfoGenie-backend/build_docker.sh create mode 100644 InfoGenie-backend/docker-compose.yml create mode 100644 InfoGenie-frontend/前端萌芽币消费系统集成文档.md create mode 100644 InfoGenie-frontend/萌芽币消费系统集成报告.md diff --git a/InfoGenie-backend/.dockerignore b/InfoGenie-backend/.dockerignore new file mode 100644 index 00000000..74b16a7c --- /dev/null +++ b/InfoGenie-backend/.dockerignore @@ -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/ diff --git a/InfoGenie-backend/.env.production b/InfoGenie-backend/.env.production index 15d521db..bef38d25 100755 --- a/InfoGenie-backend/.env.production +++ b/InfoGenie-backend/.env.production @@ -11,4 +11,4 @@ MAIL_PASSWORD=your-app-password SECRET_KEY=infogenie-production-secret-key-2025 # 会话安全配置 -SESSION_COOKIE_SECURE=True \ No newline at end of file +HWT_SECURE=True \ No newline at end of file diff --git a/InfoGenie-backend/DOCKER_README.md b/InfoGenie-backend/DOCKER_README.md new file mode 100644 index 00000000..bbf552dc --- /dev/null +++ b/InfoGenie-backend/DOCKER_README.md @@ -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 许可证。 diff --git a/InfoGenie-backend/Dockerfile b/InfoGenie-backend/Dockerfile new file mode 100644 index 00000000..c0aae50c --- /dev/null +++ b/InfoGenie-backend/Dockerfile @@ -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"] diff --git a/InfoGenie-backend/app.py b/InfoGenie-backend/app.py index bf3bfc54..41e49ea8 100755 --- a/InfoGenie-backend/app.py +++ b/InfoGenie-backend/app.py @@ -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__) diff --git a/InfoGenie-backend/build_docker.sh b/InfoGenie-backend/build_docker.sh new file mode 100755 index 00000000..1f246bfc --- /dev/null +++ b/InfoGenie-backend/build_docker.sh @@ -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 "$@" diff --git a/InfoGenie-backend/config.py b/InfoGenie-backend/config.py index da162388..824532c5 100755 --- a/InfoGenie-backend/config.py +++ b/InfoGenie-backend/config.py @@ -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): """测试环境配置""" diff --git a/InfoGenie-backend/docker-compose.yml b/InfoGenie-backend/docker-compose.yml new file mode 100644 index 00000000..96795f45 --- /dev/null +++ b/InfoGenie-backend/docker-compose.yml @@ -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 diff --git a/InfoGenie-backend/modules/__pycache__/aimodelapp.cpython-310.pyc b/InfoGenie-backend/modules/__pycache__/aimodelapp.cpython-310.pyc index 8b01fef65ddde306c04c456eff18dc30cfb5eea8..abcfffba50e40b39ec2ab3c8658e5ad18ffab01b 100644 GIT binary patch delta 6535 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 delta 2760 zcmZ`*U5p#m74{w5^w6Gxol6G1#nfTt_b#`oT z?u-*Q{1`~!g;o&V3Kh{x2$hNe(gKS_rAjOsAgOvVE6xBtx#7apFvq>qq;mv)YJ8mgg^03|eNM;e-; zHKImT&O>(0hyfq6<3?P*6Gj5wVLRDK8p(!k=u%I$`x+@D1>*?S>~y2w=x=0nD)~QyhqsJ=~0@cxp_hd zex=Ysn!gyhK#VcT7rOiq9hUsK@i0QS#$mzDT!7ev9O{BEL;;P@-ii*eV4T zx@}|Q?Q}x&C9yz`k+Ap^8Pg&TbDVmsx%v;F;u(l2&m!c+zTgeTC=5Nd?be#Da~$LC z1y7%IJ(V?UEy}nmo(`5tNxUAk3b@MA0o0&MRFZ;6{d^$8E213wjXDGZJ}f>6O_A;5 zi_myx42`Lj`F2oR6uZMeJg^mqr7o0FEpe9d`rNYVwia1)92h4BpeT8gAo|8n`HJse zeI-0VN>z|uxkLhi|3?loi!)y213>ZZ2s;oa#6Q&?WVcWwTenXF;i;TCZQH#}JTm8R zF87V%@wnwOx>Fp9+%_Et-KxTGK^q4U z@vq2DQ+W6)@xy$Lr|GknU8k_4dFG7C8dlxLeH;ZqQ~0#FPkVCmdzUO(j&rWX>22RE z8BzGm>Q|Z|Br8O0FR6+TV=qAb&A2(Z7nHoP%6w_T^V*I!`kf`j~k5a;}%~#rCO=|WoxKY$>Q+J~c`Az*a5f$(#LUjS6YynAwf3RyfQ zKZ(#2I&noGC693J&ksqnws9Ki^+@vi-6^|iHUe!2Gh zx7Xi&b*k9L73(XnuD|<-wddYjd+(j~SKjb7s_In?l`r7)FywWF(+G>AJv4s*@b%*D z1ufg|sgD`dm*JO@e-Nj~5l$fd6d{ceL5PZbv%8MTJbgPw+yt=~v1+weyXjVwJqhax zBXmnXn^B-iKf#%!z;J_wC$F z^(>MU0ps0H0IaP%^WT@Byz=W8*H>2fjpB|%d9R-V)Jg(WmGhyp!(706v*rcOI^_)c zAn$F>zbzDI7H|!8IVgBf0kv+)@CJ}x+-}X+o2DP(P*3y2hR0h7N5yA_sgAsDzV$Q6 zK8&lQuzH?Rv)+WAB@?qP$2Fx<{y6G25ja3~@OqWgQ#9XODp*Jzt^sauxrOjkwB4gV!G z`ki8#71JY^@09sJ3TlU>-`ww0LElq(Hrap3cX|yh@l*(1tL?rfelS`kPpuvw9U`3@ zK=8mJ_@c<7>A;MmdA_09z(5e)`8fxBAv$E_%(|Mt!jRjZbEi1 zLP+-AeK_2Wu<1dF%P{RnRz~e4j(VCWOucw*fz=lMiuj}0KYl{J2z0e9UKlTTWMlTy z=NMl+&)V~o4y(0!-Cgz@gUonj!EzSt`YBI=Pc;5F;O9Mtwxe_HX3cH2Y=`5u@sjdW zXgZ9at=AaWQ5%bg~uzf$@y~M_?fxbxL6tLIruruID~m506L`_U;qFB delta 92 zcmexj_r;DcpO=@50SGMDoXA+Ox{;5WiBWX25Yu5ssm<4z?g}%?ZQdfL&dj)a^Bu`X wM#cx5~u!X$@y~M_?fs^xmXzLIrunCIfQt`0U~f2{r~^~ diff --git a/InfoGenie-backend/modules/__pycache__/user_management.cpython-310.pyc b/InfoGenie-backend/modules/__pycache__/user_management.cpython-310.pyc index 334dabc57f13519294d952c3c7426977ab64a07e..74317c1bfffbcfe5caede01295a5631115117dc3 100644 GIT binary patch literal 7520 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 F{{o&V^tb>3 literal 7342 zcmbVR?RON{d7rnPot=Hr>J?i)_C>xk zi;&JNg##Fw@YWQJY2;X&f&)DKBSk7A)^!-ij>SDQze~VjFzH9QBgLERw*_V6Xi(J9sE}po;@1sdW7z=6}rb+U#MHdBD-F&4S#B){7 zowAq7j*s`NfyFSKmoGU~-nF;jhP3;>MNdB(3#q+FbzAbpVVtAGkldrX0$hz-HM^0l`;8*s?e92EXu4I%~bX*=^~Q@ zEnRF`H=*>~)izAC_zx!*<_>-9&A~peGUk*6d-wKj-`PHx`LpeNg2*>_Z+Y%{Jc6Aq zj#PNw8!fqmS2R3zHJ8JK2L>K9yQ7-IID^!?aqXWLZXIjRd~ikMX;cQ$5}IX4oN9cj zN%Q<{^W1d58R&%xFEE*tt1yR~)Y!~b%ES4QpnLoN@jQ3f?)*qO>s7h4o%0G0+Fvj4 z%NFxY%p|J%wICu_9Uae37AskHMd9l()ZIadfze9I8E{9dC8sPb|Lwqk%Uj zz0pc};PvuwV_hxf=2sTivuxin92k0ebO4%|V0AqNY#wUZsK8NGQXT-Ga zn8l^%)U>r~LQREV?Z5e>sC?k{lH=;sWi5oKOv8bWDZ?tc?-A>;T2p-$=Bro#80z^8 zHyfXwlP_oC?~g8C`nYl7@|{non`h^mCzfAVit!ci-|IpWFb*9x--sx*hXUb8nSE*^9;cHlm?(59xRnSUjkzVx8M5YyA$t%8k zQ9XradM3s)>#F;l7bTlxu^IGZ_OP}a54N&pe|%?CV0 zvtTI(VGrK5IV=ze))VbjU`;Bs>Z-WL!YiUj9kvDyVn31dY zFZ}b7=DQbysPN%D3-nB$?YK*bC-t|lpKN^eUSsC6G_12QtSas3!Y3a#PG1w|*Zk?_ z#@i>F=YO_%{u5a-bFgvdl(a`tA&bqQ)SKr%=7i?5Rz{xVKWiR56qs3<%{~W#B%00T zDpiD$BzH#gt_N(P2oc!Dtm|dO3l8G)J5S0oit4sR5A)`+#>qJ=`O%;-2tN-fOgAHM|yb< z*2UU=D^H%Q)na}O?5zze{ys+NW;m>YgzcGL1hV<1lc(aS-tBYL_)eY2hsrBNyTa6nZHf|kV ztlxxlKfiIy=G&j0y#4dm=;d6-1XilGL9RGm7fF=*<8W;a@;gd`J zYtZo*A$In6$w+6BTM^~tNxRLLWLj&@{=FVBjO1V;#;ZJD-ggJLLdGbiPuRGNbpxl zkmIcwuYKjK69JAnmJQ1x{v8ehp5h2`7sXi9*1IJBy%a3kZsnLkcHL)U*R2S4>=m zlO?M}Bv%H#`I{dsT&>@}Rc{`e6R&5H%v;)cWGT3K6_%Y0+Op&M4ERXV1(y{SUd786 zk-vo1L99>#eSyjik|IQjNCwN10ZG{K4q>RmErs3g1UVB_OGF`}cY$86Oa!sKTN$qK zQq~h@yTYn;8EatE*yx8^NQ$gqgmKa40tR(AK~P*lP!Xi-e-TCYqRW&$Fp@1f8z~a5 ziX5w0(XXiGE>H2miVkZ$;i(0U5j$4O5l^2rh&6*NMG9sC7%#X|jOpMjE4k8_z4(KX z1YF3V*DP3(f$Vyz6mOLhEQWqba3vj#scptUB#E;$7*o42h>jLQCnhT}9?6(!u1+u6 zVocJ|x?0B59Wo|y6~@!^fblf1UvEyI29s%?yvhkwm+^9k1We*s0yPB!&VTgI{EZ(A zE{-a+srazwl=}nmD=eA6-pWV3m*(>^8!P;*9 zaQ@~;vQgvW6)fOxH+tQ^dAxc4QuDpJ#+eV1S4#bg@gy$;osSfqeNHj3o&Dp;&75-1 z2_iX|t{bETsKbceNzkp*3cHnNOGvx3KZQ&p>t;QV3$&GwzHG!_h^zNovt&-opcur+ z-rz(?P9BDS9WC2#wfh8%a=!%u`_>Ywt=ZcB+{um#-Xts^Pt(kKs;?*A~KOT6%zNK^aCZBFgQ|Mj4EWpPt%)+1kLJEYw6rSr=u? zxBTc@rI038#5yo`%{N)cpC~o6ENcvYgmvIf^Ua$1rc%aPnK)TN-5#Z^k&boNY~&zu z@eJK!)`Tech;mYt*NAe;Px|pSO0CUL)zUG=YiBs+^3$D47qFt%?zj2vV-fx@bp0x| zN{h9m%W7GD>#Cpd?KKK?X{>QA5CHqc)&UVZkezibBMw{dcL=7}>30@7nGL4*8}vzy z8MQ9#+*x?@22f&$b5u~Wi zm%MJjdleHDtwoS}vetc|8$n831rPo+q&fBL56xq6l8x zp*lu_Tw-vMiX&eUpa1xJzj2>lF6Rxj5v)%>jO25er9X-@Knj@L!MOqxaI{oT<6w(u z7bSu+pjra6GS%@QMqEiEm?%o*ic&2AtzVbNBb~HJqF>d=AeOL_=%Re&`O7DfnJykX zxiEKbC4ubWCWF3#1lh8Zz(WcRD0 zcPu>Z!AT;WZ&P$ACQ8X#1lcffo+ZO1wdjE;)+??8`PK;J zYfR5BUbu);CHxr?SWEdY|0XteN0rhb0M^1U{sJ@9UX;BguNLqna;?p1 zet+t!%DihARB}F%ZLi2g2dl)Uf@(}WsZsaGRF?M8?sN~QPOvRPtJG>ll89+qL~b<&`i8JncI&qEm6dLR=}hj0ncZP}@{gi@(53At6sULl_r K@~<^y=l=tHTGXZh diff --git a/InfoGenie-backend/modules/aimodelapp.py b/InfoGenie-backend/modules/aimodelapp.py index 72dedcbc..ce340932 100755 --- a/InfoGenie-backend/modules/aimodelapp.py +++ b/InfoGenie-backend/modules/aimodelapp.py @@ -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(): diff --git a/InfoGenie-backend/modules/auth.py b/InfoGenie-backend/modules/auth.py index b7adb752..0c7090de 100755 --- a/InfoGenie-backend/modules/auth.py +++ b/InfoGenie-backend/modules/auth.py @@ -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(): """检查登录状态""" diff --git a/InfoGenie-backend/modules/email_service.py b/InfoGenie-backend/modules/email_service.py index 13e33591..dfbe45a5 100755 --- a/InfoGenie-backend/modules/email_service.py +++ b/InfoGenie-backend/modules/email_service.py @@ -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() diff --git a/InfoGenie-backend/modules/user_management.py b/InfoGenie-backend/modules/user_management.py index 97b70df6..e3cb659d 100755 --- a/InfoGenie-backend/modules/user_management.py +++ b/InfoGenie-backend/modules/user_management.py @@ -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, diff --git a/InfoGenie-backend/后端架构文档.md b/InfoGenie-backend/后端架构文档.md index e69de29b..325ddf92 100755 --- a/InfoGenie-backend/后端架构文档.md +++ b/InfoGenie-backend/后端架构文档.md @@ -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 +``` + +**支持的认证端点**: +- 所有 `/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使用记录和萌芽币消费情况 + +--- diff --git a/InfoGenie-frontend/public/aimodelapp/AI变量命名助手/index.html b/InfoGenie-frontend/public/aimodelapp/AI变量命名助手/index.html index 6e76ba22..d344633a 100755 --- a/InfoGenie-frontend/public/aimodelapp/AI变量命名助手/index.html +++ b/InfoGenie-frontend/public/aimodelapp/AI变量命名助手/index.html @@ -37,6 +37,7 @@ + diff --git a/InfoGenie-frontend/public/aimodelapp/AI变量命名助手/script.js b/InfoGenie-frontend/public/aimodelapp/AI变量命名助手/script.js index 2dff09f9..f012924f 100755 --- a/InfoGenie-frontend/public/aimodelapp/AI变量命名助手/script.js +++ b/InfoGenie-frontend/public/aimodelapp/AI变量命名助手/script.js @@ -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); } diff --git a/InfoGenie-frontend/public/aimodelapp/coin-manager.js b/InfoGenie-frontend/public/aimodelapp/coin-manager.js index e69de29b..2685272a 100644 --- a/InfoGenie-frontend/public/aimodelapp/coin-manager.js +++ b/InfoGenie-frontend/public/aimodelapp/coin-manager.js @@ -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 = '
加载中...
'; + } else if (this.error) { + content = ` +
+
加载失败
+
${this.error}
+ +
+ `; + } else if (!this.isLoaded) { + content = '
正在检查萌芽币余额...
'; + } else { + const usageHistory = this.recentUsage.length > 0 + ? ` +
+
最近使用记录:
+ ${this.recentUsage.map(usage => ` +
+ ${this.formatApiType(usage.api_type)} (-${usage.cost}币) + ${this.formatDate(usage.timestamp)} +
+ `).join('')} +
+ ` + : ''; + + content = ` +
+
${this.username || '用户'}的萌芽币
+
+ ${this.canUseAi ? '可使用' : '币不足'} +
+
+ +
+
+ ${this.coins} +
+
萌芽币
+
+ +
+ AI功能每次使用消耗 ${this.aiCost} 萌芽币 +
+ + ${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; diff --git a/InfoGenie-frontend/src/pages/AiModelPage.js b/InfoGenie-frontend/src/pages/AiModelPage.js index e89157bb..7adf4d89 100755 --- a/InfoGenie-frontend/src/pages/AiModelPage.js +++ b/InfoGenie-frontend/src/pages/AiModelPage.js @@ -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 = () => { )} - {/* 内嵌显示组件 */} + {/* 萌芽币提示 */} + {isLoggedIn && ( +
+

+ 💰 + 萌芽币消费提示 +

+

+ 每次使用AI功能将消耗100萌芽币,无论成功与否。当萌芽币余额不足时,无法使用AI功能。 +

+

+ 您可以通过每日签到获得300萌芽币。详细的萌芽币余额和使用记录将显示在各AI应用的右上角。 +

+
+ )} + + {/* 内嵌显示组件 */} {embeddedApp && ( e.stopPropagation()}> @@ -407,6 +456,7 @@ const AiModelPage = () => { diff --git a/InfoGenie-frontend/src/utils/api.js b/InfoGenie-frontend/src/utils/api.js index 92564528..9cee216a 100755 --- a/InfoGenie-frontend/src/utils/api.js +++ b/InfoGenie-frontend/src/utils/api.js @@ -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'), diff --git a/InfoGenie-frontend/前端架构文档.md b/InfoGenie-frontend/前端架构文档.md index e69de29b..97ce4b22 100755 --- a/InfoGenie-frontend/前端架构文档.md +++ b/InfoGenie-frontend/前端架构文档.md @@ -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 +``` + +#### 检查登录状态 +```http +GET /api/auth/check +Authorization: Bearer +``` + +### 用户管理接口 + +#### 获取用户资料 +```http +GET /api/user/profile +Authorization: Bearer +``` + +#### 修改密码 +```http +POST /api/user/change-password +Authorization: Bearer +Content-Type: application/json + +{ + "old_password": "old_password", + "new_password": "new_password" +} +``` + +#### 获取用户统计 +```http +GET /api/user/stats +Authorization: Bearer +``` + +#### 获取游戏数据 +```http +GET /api/user/game-data +Authorization: Bearer +``` + +#### 用户签到 +```http +POST /api/user/checkin +Authorization: Bearer +``` + +#### 删除账户 +```http +POST /api/user/delete +Authorization: Bearer +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 ( + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + ); +}; +``` + +## 响应式设计 + +- 移动优先设计理念 +- 使用 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 + +响应: +{ + "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功能设置差异化定价 + - 添加萌芽币消费统计和分析功能 \ No newline at end of file diff --git a/InfoGenie-frontend/前端萌芽币消费系统集成文档.md b/InfoGenie-frontend/前端萌芽币消费系统集成文档.md new file mode 100644 index 00000000..64896fb9 --- /dev/null +++ b/InfoGenie-frontend/前端萌芽币消费系统集成文档.md @@ -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 + +``` + +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萌芽币,无论成功与否 +- 用户可以通过每日签到获取萌芽币 diff --git a/InfoGenie-frontend/萌芽币消费系统集成报告.md b/InfoGenie-frontend/萌芽币消费系统集成报告.md new file mode 100644 index 00000000..47332179 --- /dev/null +++ b/InfoGenie-frontend/萌芽币消费系统集成报告.md @@ -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应用中即可完成全部工作。