# fy **Repository Path**: sjq04/fy ## Basic Information - **Project Name**: fy - **Description**: 非遗 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-04-02 - **Last Updated**: 2026-04-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # fy 基于 FastAPI + SQLAlchemy Async + MySQL 的后端服务,提供以下能力: - 非遗项目浏览与详情查询 - 收藏、纠错、故事投稿等用户交互 - AI 文案生成、生成记录保存与历史查询 - AI 对话接口 接口默认返回统一结构: ```json { "code": 200, "message": "success", "data": {} } ``` ## 运行环境 - Python 3.10+ - MySQL 8.x ## 安装与启动 1. 创建虚拟环境 ```bash python -m venv .venv ``` 2. 激活虚拟环境 Windows PowerShell: ```powershell .\.venv\Scripts\Activate.ps1 ``` 3. 安装依赖 ```bash pip install -r requirements.txt ``` 4. 复制环境变量文件 ```bash copy .env.example .env ``` 5. 启动服务 ```bash uvicorn main:app --reload --host 0.0.0.0 --port 8000 ``` 启动后可访问: - `GET /hello` - `GET /docs` - `GET /redoc` ## 环境变量 `.env.example` 中当前包含以下配置: ### 数据库 - `DB_HOST` - `DB_PORT` - `DB_NAME` - `DB_USER` - `DB_PASSWORD` - `DB_CHARSET` - `DB_ECHO` ### OpenAI 兼容模型 - `LLM_API_KEY` - `LLM_BASE_URL` - `LLM_MODEL` - `LLM_TIMEOUT` - `LLM_MAX_HISTORY` - `LLM_TEMPERATURE` ### 联网搜索 - `WEB_SEARCH_PROVIDER` - `WEB_SEARCH_API_KEY` - `WEB_SEARCH_BASE_URL` - `WEB_SEARCH_TIMEOUT` ### 本地模型桥接 - `FY_LOCAL_MODEL_PATH` - `FY_LOCAL_MODEL_MODULE` - `FY_LOCAL_MODEL_FUNCTION` 说明: - 如果配置了本地模型,优先走本地生成。 - 如果未配置本地模型,但配置了 LLM 参数,则走 OpenAI-compatible 接口。 - 如果两者都未配置,当前会回退到 demo 内容生成。 ## 数据库初始化 初始化 SQL 位于 [sql/create_table.sql](/e:/comp/project/fy/sql/create_table.sql)。 可先手动创建数据库: ```sql CREATE DATABASE fy DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ``` 当前主要表: - `users` - `projects` - `project_views` - `generated_contents` - `favorites` - `corrections` - `stories` 其中 `generated_contents` 用于保存 AI 生成后的内容,当前字段包括: - `id` - `user_id` - `project_id` - `content` - `generation_type` - `created_at` - `updated_at` ORM 模型位于 [models/](/e:/comp/project/fy/models)。 ## 项目结构 ```text fy/ ├─ config/ # 配置与数据库连接 ├─ crud/ # 数据访问层 ├─ models/ # SQLAlchemy 模型 ├─ routers/ # 路由层 ├─ schemas/ # 请求/响应模型 ├─ services/ # 业务能力与 AI 服务 ├─ sql/ # 初始化 SQL 与数据脚本 ├─ tests/ # 测试 ├─ main.py # 应用入口 └─ requirements.txt ``` ## 路由概览 ### Auth - `POST /auth/register` 用户注册(当前为明文密码,仅用于开发联调) - `POST /auth/login` 用户/管理员登录(返回 JWT) - `GET /auth/me` 获取当前登录用户信息(需 Bearer Token) ### Explore - `GET /explore/projects` 项目列表 - `GET /explore/{project_id}` 项目详情 - `GET /explore/{project_id}/associations` 关联项目 - `GET /explore/{project_id}/stories` 已通过故事列表 - `POST /explore/{project_id}/view` 记录浏览 ### User - `GET /user/favorites` 我的收藏 - `POST /user/favorites/{project_id}` 收藏或取消收藏 - `POST /user/contributions/correction` 提交纠错 - `GET /user/corrections` 我的纠错记录 - `GET /user/stories` 我的故事记录 - `POST /user/{project_id}/stories` 提交故事 ### AI - `POST /ai/generate` 生成文案 - `POST /ai/generated-contents` 保存生成结果 - `GET /ai/generated-contents` 查询生成历史 - `POST /ai/chat` AI 对话 ### Admin - `POST /admin/projects` 上传非遗资料 - `PUT /admin/projects/{project_id}` 更新非遗项目 - `GET /admin/corrections` 获取用户纠错审核列表 - `POST /admin/corrections/{correction_id}/review` 审核用户纠错 - `GET /admin/stories` 获取用户故事审核列表 - `POST /admin/stories/{story_id}/review` 审核用户故事 ## AI 生成功能说明 ### 1. 生成文案 接口:`POST /ai/generate` 请求体字段: - `user_id`: 用户 ID - `project_id`: 项目 ID - `generation_style`: 生成风格 - `prompt`: 补充提示词,可为空字符串 - `source_content`: 基于上一版内容继续改写时传入,可选 - `max_chars`: 期望最大字数,可选 示例: ```json { "user_id": 1, "project_id": 1, "generation_style": "自然生动", "prompt": "适合前端详情页展示", "source_content": "这是一段上一版文案", "max_chars": 300 } ``` 返回字段: - `generated_content` - `provider` - `generation_style` 补充说明: - 当前接口会先校验 `project_id` 是否存在。 - 代码内带有限流:同一 `user_id` 在 60 秒内最多 20 次请求。 ### 2. 保存生成内容 接口:`POST /ai/generated-contents` 请求体字段: - `user_id` - `project_id` - `generation_type` - `content` ### 3. 查询生成历史 接口:`GET /ai/generated-contents` 支持查询参数: - `user_id` - `page` - `pageSize` - `projectName` - `generationType` 返回结构中的 `data` 包含: - `list` - `total` - `hasMore` ### 4. AI 对话 接口:`POST /ai/chat` 请求体示例: ```json { "messages": [ { "role": "user", "content": "请帮我润色一段非遗介绍" }, { "role": "assistant", "content": "可以,把原文发给我。" }, { "role": "user", "content": "内容要更适合网页展示。" } ] } ``` ## 接口19:上传非遗资料 接口:`POST /admin/projects` 请求体说明(不传 `project_id`): - `project_seq` - `project_code` - `name` - `title` - `category` - `publish_time` - `type` - `region` - `declare_area` - `protect_unit` - `image` - `content` - `related_news_count` - `related_news`(数组,元素包含 `title`、`url`) 请求体示例: ```json { "project_seq": 1, "project_code": "Ⅰ-1", "name": "苗族古歌", "title": "苗族古歌", "category": "苗族古歌", "publish_time": "2006(第一批)", "type": "新增项目", "region": "贵州省", "declare_area": "贵州省台江县", "protect_unit": "台江县非物质文化遗产保护中心", "image": "https://example.com/images/suxiu_cover.jpg", "content": "苗族分布在我国西南数省区......", "related_news_count": 5, "related_news": [ { "title": "2023.06.25传承老手艺 激活新动力——我国非遗传承助力乡村振兴现状调研", "url": "https://www.ihchina.cn/project_details/27619.html" }, { "title": "2023.04.19非遗活力持续上升——3月非遗传播活力数据解读", "url": "https://www.ihchina.cn/project_details/27112.html" } ] } ``` 返回示例: ```json { "code": 200, "message": "创建成功", "data": { "project_id": 1001 } } ``` 补充说明: - 接口会自动以 `related_news` 的实际数量覆盖 `related_news_count`。 ## 接口20:更新非遗项目 接口:`PUT /admin/projects/{project_id}` 请求体说明: - 字段与“接口19 上传非遗资料”一致,但均为可选字段(部分更新) - `project_id` 通过路径参数传入,不在请求体中传 更新规则: - 显式传 `null` 的字段会被忽略,不会更新 - 仅当传入 `related_news` 字段时,才会同步更新 `related_news_json` 并重算 `related_news_count` - 若未传 `related_news`,即使传了 `related_news_count` 也会保持原值不变 请求体示例: ```json { "title": "苗族古歌(修订版)", "region": "贵州省", "related_news": [ { "title": "2023.06.25传承老手艺 激活新动力——我国非遗传承助力乡村振兴现状调研", "url": "https://www.ihchina.cn/project_details/27619.html" } ] } ``` 返回示例: ```json { "code": 200, "message": "更新成功" } ``` ## 接口21:获取用户纠错审核列表 接口:`GET /admin/corrections` Query 参数: - `status`:可选,审核状态(`0` 待审核 / `1` 通过 / `2` 拒绝) - `page`:页码,默认 `1` - `size`:每页条数,默认 `10` - `name`:可选,按项目名称模糊搜索 返回示例: ```json { "code": 200, "message": "success", "data": { "total": 12, "page": 1, "size": 10, "items": [ { "id": 501, "project_id": 1, "project_name": "苗族古歌", "content": "这里的公布时间写错了,应该是2006年第一批。", "user_name": "张三", "status": 0, "reject_reason": null, "created_at": "2026-03-31T10:00:00Z" } ] } } ``` ## 接口22:审核用户纠错 接口:`POST /admin/corrections/{correction_id}/review` 请求体字段: - `status`:必填,审核结果(`1` 通过,`2` 拒绝) - `reject_reason`:可选;当 `status=2` 时必填 请求体示例: ```json { "status": 1, "reject_reason": "" } ``` 返回示例: ```json { "code": 200, "message": "审核成功" } ``` ## 接口23:获取用户故事审核列表 接口:`GET /admin/stories` Query 参数: - `status`:可选,审核状态(`0` 待审核 / `1` 通过 / `2` 拒绝) - `page`:页码,默认 `1` - `size`:每页条数,默认 `10` - `keyword`:可选,按项目名称模糊搜索 返回示例: ```json { "code": 200, "message": "success", "data": { "total": 8, "page": 1, "size": 10, "items": [ { "id": 601, "project_id": 3, "project_name": "苏绣", "content": "小时候,外婆总会一边绣花一边给我讲苏绣的故事……", "user_name": "李四", "is_public": 0, "status": 0, "reject_reason": null, "created_at": "2026-03-31T11:00:00Z" } ] } } ``` ## 接口24:审核用户故事 接口:`POST /admin/stories/{story_id}/review` 请求体字段: - `action`:必填,审核结果(`1` 通过,`2` 拒绝) - `reject_reason`:可选;当 `action=2` 时必填 请求体示例: ```json { "action": 2, "reject_reason": "内容不符合社区规范" } ``` 返回示例: ```json { "code": 200, "message": "审核成功" } ``` ## 接口25:用户注册 接口:`POST /auth/register` 说明:管理员账号不通过本接口注册。当前实现将密码以**明文**写入数据库,仅适合本地与演示;上线前请改为密码哈希等安全方案。 请求体字段: - `phone`:手机号,必填,唯一 - `password`:密码,必填(明文存储) - `username`:用户名,必填 - `gender`:性别,必填(`0` 男 / `1` 女 / `2` 其他或不便透露) 请求体示例: ```json { "phone": "13800138000", "password": "123456", "username": "张三", "gender": 0 } ``` 返回示例: ```json { "code": 200, "message": "注册成功", "data": { "user_id": 10001, "role": 0 } } ``` 错误说明: - `409`:手机号已注册 - 请求体验证不通过时,FastAPI 默认返回 `422` 环境变量(可选):与 JWT 共用配置见接口 26 说明中的 `JWT_SECRET` 等(注册本身不签发 token)。 ## 接口26:用户/管理员登录 接口:`POST /auth/login` 请求体字段: - `phone`:手机号 - `password`:密码(与库中明文比对) 请求体示例: ```json { "phone": "13800138000", "password": "123456" } ``` 返回示例: ```json { "code": 200, "message": "登录成功", "data": { "token": "eyJhbGciOiJIUzI1NiJ9.xxx.xxx", "user_info": { "user_id": 10001, "phone": "13800138000", "username": "张三", "gender": 0, "role": 0 } } } ``` 字段说明: - `role`:`0` 普通用户,`1` 管理员 错误说明: - `401`:手机号或密码错误 - `403`:账号被禁用(`users.status = 1`) 环境变量(`.env` 可选): - `JWT_SECRET`:签名密钥(生产环境务必修改为强随机字符串) - `JWT_ALGORITHM`:默认 `HS256` - `ACCESS_TOKEN_EXPIRE_MINUTES`:令牌有效期(分钟),默认 10080(7 天) ## 接口27:获取当前登录用户信息 接口:`GET /auth/me` 请求头:`Authorization: Bearer `(Swagger 中可先调用登录接口,再点击 Authorize 填入 token) 返回示例: ```json { "code": 200, "message": "success", "data": { "user_id": 10001, "phone": "13800138000", "username": "张三", "gender": 0, "role": 0 } } ``` 错误说明: - `401`:未携带 token、token 无效或已过期、用户已不存在 ## 开发说明 - 应用入口位于 [main.py](/e:/comp/project/fy/main.py)。 - 路由注册位于 `main.py`,当前挂载了 `auth`、`explore`、`ai`、`user`、`admin` 路由。 - AI 生成逻辑位于 [services/ai_generate.py](/e:/comp/project/fy/services/ai_generate.py)。 - AI 相关请求与响应模型位于 [schemas/ai.py](/e:/comp/project/fy/schemas/ai.py)。 - AI 路由位于 [routers/ai.py](/e:/comp/project/fy/routers/ai.py)。 ## 常见问题 ### 1. `ModuleNotFoundError` 通常是依赖未安装,重新执行: ```bash pip install -r requirements.txt ``` ### 2. 数据库连接失败 请确认: - `.env` 中数据库配置正确 - MySQL 服务已经启动 - 数据库名已创建 - 字符集建议使用 `utf8mb4` ### 3. AI 生成功能没有调用外部模型 请检查: - 本地模型参数是否配置正确 - 或 OpenAI-compatible 参数是否完整 - 如果两者都未配置,当前会返回 demo 生成内容 ## AI 对话与联网搜索 当前 `POST /ai/chat` 已支持两类资料来源: - 站内项目资料 RAG - 联网搜索结果 ### 当前已支持的请求字段 - `messages`: 对话消息数组,必填 - `project_id`: 可选,指定项目后优先基于该项目资料回答 - `use_rag`: 是否启用站内资料检索,默认 `true` - `top_k`: 站内资料返回条数,范围 `1-5` - `use_web_search`: 是否启用联网搜索,默认 `false` - `web_search_top_k`: 联网搜索返回条数,范围 `1-5` ### 当前返回结构 `data` 中会返回: - `reply`: 模型回复 - `provider`: 当前回复来源,例如 `openai-compatible` 或 `demo` - `sources`: 本轮参考资料列表 `sources` 中: - `source_type = "project"` 表示站内项目资料 - `source_type = "web"` 表示联网搜索结果 - 当 `source_type = "web"` 时,会返回 `title`、`url`、`snippet` - `url` 可直接用于前端渲染成可点击验证链接 ### 请求示例 ```json { "project_id": 1, "use_rag": true, "top_k": 2, "use_web_search": true, "web_search_top_k": 3, "messages": [ { "role": "user", "content": "介绍一下这个项目,并给我几个可以点击验证的网页链接" } ] } ``` ### 响应示例 ```json { "code": 200, "message": "success", "data": { "reply": "苗族古歌不仅是苗族社会历史记忆的重要载体,也保留了丰富的口头传统内容。", "provider": "openai-compatible", "sources": [ { "source_type": "project", "title": "苗族古歌", "url": null, "project_id": 1, "project_name": "苗族古歌", "category": "民间文学", "region": "贵州省", "publish_time": "2006(第一批)", "snippet": "苗族古歌是苗族口头传统的重要组成部分。" }, { "source_type": "web", "title": "中国非物质文化遗产网相关页面", "url": "https://www.ihchina.cn/project_details/12178.html", "project_id": null, "project_name": null, "category": null, "region": null, "publish_time": null, "snippet": "网页摘要示例。" } ] } } ``` ### 环境变量 大模型配置: ```env LLM_API_KEY=your_key_here LLM_BASE_URL=https://api.siliconflow.cn/v1 LLM_MODEL=Qwen/Qwen2.5-7B-Instruct LLM_TIMEOUT=30 LLM_MAX_HISTORY=12 LLM_TEMPERATURE=0.2 ``` 联网搜索配置: ```env WEB_SEARCH_PROVIDER=tavily WEB_SEARCH_API_KEY=your_tavily_key_here WEB_SEARCH_BASE_URL=https://api.tavily.com/search WEB_SEARCH_TIMEOUT=15 ``` 说明: - 只配置 `LLM_*` 时,可以正常进行大模型对话,但不会返回外部网页链接 - 只有在同时开启 `use_web_search=true` 且配置了 `WEB_SEARCH_*` 后,`sources` 中才会出现 `source_type = "web"` 的可点击链接 - 若联网搜索服务暂时失败,接口仍会返回正常对话结果,只是本轮 `web` 来源可能为空