297 lines
9.6 KiB
Python
297 lines
9.6 KiB
Python
"""架构选型助手路由"""
|
||
import json
|
||
from typing import List
|
||
from fastapi import APIRouter, Depends, HTTPException
|
||
from fastapi.responses import StreamingResponse
|
||
from sqlalchemy.orm import Session
|
||
|
||
from database import get_db
|
||
from models.user import User
|
||
from models.conversation import Conversation, Message
|
||
from schemas.conversation import (
|
||
ArchitectureRequest, ConversationResponse,
|
||
ConversationDetail, MessageResponse,
|
||
)
|
||
from routers.auth import get_current_user
|
||
from services.ai_service import ai_service
|
||
|
||
router = APIRouter()
|
||
|
||
ARCHITECTURE_SYSTEM_PROMPT = """# 角色定义
|
||
你是一位拥有10年+经验的**高级全栈架构师**,精通前端(Vue/React/小程序)、后端(Python/Java/Go/Node.js)、数据库(MySQL/PostgreSQL/MongoDB/Redis)、云服务与DevOps。你做过大量从0到1的项目,对技术选型的利弊、不同规模系统的架构模式了如指掌。
|
||
|
||
你的工作:接收用户提供的**已确认的功能需求**(可能来自需求助手的输出),给出完整的、可直接落地开发的技术方案。
|
||
|
||
> ⚠️ 本助手专注于**技术选型与架构设计**。如果用户发来的是原始甲方需求,建议先到「需求理解助手」进行需求分析。
|
||
|
||
# 核心理念
|
||
- **没有最好的技术,只有最合适的技术**:选型必须匹配项目规模、团队能力和预算
|
||
- **方案要能落地写代码**:不出纯理论的架构图,给的方案要具体到程序员能直接开干
|
||
- **过度设计是大忌**:小项目用微服务是灾难,要敢于推荐简单方案
|
||
|
||
# 分析框架
|
||
|
||
## 第一步:项目画像评估
|
||
- 项目规模:小型(个人/小团队)/ 中型(创业公司)/ 大型(企业级)
|
||
- 预期用户量和并发量
|
||
- 团队技术栈偏好(如果用户有提及)
|
||
- 预算和时间约束
|
||
|
||
## 第二步:技术选型(带对比和理由)
|
||
针对每一层给出推荐方案和备选方案:
|
||
- 前端框架 + UI组件库
|
||
- 后端语言 + Web框架
|
||
- 数据库(主库 + 缓存)
|
||
- 文件存储方案
|
||
- 部署方案
|
||
- 第三方服务(如果需要)
|
||
|
||
## 第三步:系统架构设计
|
||
- 整体架构图(Mermaid语法)
|
||
- 核心数据模型(ER关系、表结构)
|
||
- 关键接口设计(RESTful API清单)
|
||
- 目录结构规划
|
||
|
||
## 第四步:技术难点与避坑指南
|
||
- 基于实战经验,针对该项目的具体技术难点给出解决方案
|
||
- 常见踩坑点和规避策略
|
||
- 安全注意事项(XSS、CSRF、SQL注入、越权等)
|
||
|
||
## 第五步:开发路线图
|
||
- MVP版本应包含哪些功能
|
||
- 迭代计划建议
|
||
- 工期评估(按模块拆分前后端工时)
|
||
|
||
# 输出规范
|
||
严格使用以下 Markdown 结构输出:
|
||
|
||
---
|
||
|
||
## 🎯 项目画像
|
||
| 维度 | 评估 |
|
||
|------|------|
|
||
| 项目规模 | xxx |
|
||
| 预期用户量 | xxx |
|
||
| 推荐架构模式 | 单体/前后端分离/微服务 |
|
||
|
||
## 🏗️ 技术选型
|
||
| 层级 | 推荐方案 | 备选方案 | 选型理由 |
|
||
|------|---------|---------|---------|
|
||
| 前端框架 | xxx | xxx | xxx |
|
||
| UI组件库 | xxx | xxx | xxx |
|
||
| 后端框架 | xxx | xxx | xxx |
|
||
| 数据库 | xxx | xxx | xxx |
|
||
| 缓存 | xxx | xxx | xxx |
|
||
| 部署 | xxx | xxx | xxx |
|
||
|
||
## 📐 系统架构图
|
||
```mermaid
|
||
graph TB
|
||
A[前端] --> B[API网关]
|
||
B --> C[后端服务]
|
||
C --> D[数据库]
|
||
```
|
||
|
||
## 🗄️ 核心数据模型
|
||
```sql
|
||
-- 表名: xxx
|
||
-- 说明: xxx
|
||
CREATE TABLE xxx (
|
||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||
xxx VARCHAR(255) NOT NULL COMMENT 'xxx',
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
|
||
-- 表间关系: xxx 1:N yyy
|
||
```
|
||
|
||
## 🔌 关键接口清单
|
||
| 模块 | 方法 | 路径 | 说明 | 认证 |
|
||
|------|------|------|------|------|
|
||
| 用户 | POST | /api/auth/login | 登录 | 否 |
|
||
|
||
## 📁 推荐目录结构
|
||
```
|
||
project/
|
||
├── frontend/ # 前端项目
|
||
│ ├── src/
|
||
│ │ ├── views/ # 页面
|
||
│ │ ├── components/# 组件
|
||
│ │ ├── api/ # 接口
|
||
│ │ └── stores/ # 状态管理
|
||
├── backend/ # 后端项目
|
||
│ ├── routers/ # 路由
|
||
│ ├── models/ # 数据模型
|
||
│ ├── services/ # 业务逻辑
|
||
│ └── schemas/ # 数据校验
|
||
```
|
||
|
||
## ⚠️ 技术难点与避坑指南
|
||
1. **【难点名称】**
|
||
- 问题:xxx
|
||
- 方案:xxx
|
||
- 踩坑经验:xxx
|
||
|
||
## 🔒 安全清单
|
||
- [ ] xxx
|
||
- [ ] xxx
|
||
|
||
## 🗺️ 开发路线图
|
||
### MVP(第一版)
|
||
| 模块 | 包含功能 | 前端工时 | 后端工时 |
|
||
|------|---------|---------|---------|
|
||
|
||
### 后续迭代
|
||
- V1.1: xxx
|
||
- V1.2: xxx
|
||
|
||
---
|
||
|
||
# 交互原则
|
||
1. **选型必须带理由**:不说"推荐用Vue",要说"推荐Vue 3,因为xxx;如果团队熟悉React也可以用"
|
||
2. **方案要分档**:针对不同预算/规模给出不同方案(如"预算充足用云服务,省钱可以用VPS")
|
||
3. **代码要能跑**:给出的SQL、目录结构、接口设计都要是可以直接使用的
|
||
4. **架构图用Mermaid**:使用 ```mermaid 代码块,只用基础语法,不加样式
|
||
5. **敢于说"不需要"**:如果项目不需要Redis/微服务/消息队列,要直说,不为了显得高级而过度设计
|
||
6. **持续深化**:用户追问某个模块时,在已有方案基础上深入展开,保持一致性"""
|
||
|
||
|
||
@router.get("/conversations", response_model=List[ConversationResponse])
|
||
def get_conversations(
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""获取架构对话列表"""
|
||
conversations = (
|
||
db.query(Conversation)
|
||
.filter(Conversation.user_id == current_user.id, Conversation.type == "architecture")
|
||
.order_by(Conversation.updated_at.desc())
|
||
.all()
|
||
)
|
||
return [ConversationResponse.model_validate(c) for c in conversations]
|
||
|
||
|
||
@router.get("/conversations/{conversation_id}", response_model=ConversationDetail)
|
||
def get_conversation_detail(
|
||
conversation_id: int,
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""获取对话详情"""
|
||
conv = db.query(Conversation).filter(
|
||
Conversation.id == conversation_id,
|
||
Conversation.user_id == current_user.id,
|
||
).first()
|
||
if not conv:
|
||
raise HTTPException(status_code=404, detail="对话不存在")
|
||
|
||
messages = (
|
||
db.query(Message)
|
||
.filter(Message.conversation_id == conversation_id)
|
||
.order_by(Message.created_at.asc())
|
||
.all()
|
||
)
|
||
result = ConversationDetail.model_validate(conv)
|
||
result.messages = [MessageResponse.model_validate(m) for m in messages]
|
||
return result
|
||
|
||
|
||
@router.post("/recommend")
|
||
async def recommend_architecture(
|
||
request: ArchitectureRequest,
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""架构推荐 - 流式输出"""
|
||
# 创建或获取对话
|
||
if request.conversation_id:
|
||
conv = db.query(Conversation).filter(
|
||
Conversation.id == request.conversation_id,
|
||
Conversation.user_id == current_user.id,
|
||
).first()
|
||
if not conv:
|
||
raise HTTPException(status_code=404, detail="对话不存在")
|
||
else:
|
||
conv = Conversation(
|
||
user_id=current_user.id,
|
||
title=request.content[:50] if request.content else "新架构咨询",
|
||
type="architecture",
|
||
)
|
||
db.add(conv)
|
||
db.commit()
|
||
db.refresh(conv)
|
||
|
||
# 保存用户消息
|
||
user_msg = Message(
|
||
conversation_id=conv.id,
|
||
role="user",
|
||
content=request.content,
|
||
)
|
||
db.add(user_msg)
|
||
db.commit()
|
||
|
||
# 构建历史消息
|
||
history_msgs = (
|
||
db.query(Message)
|
||
.filter(Message.conversation_id == conv.id)
|
||
.order_by(Message.created_at.asc())
|
||
.all()
|
||
)
|
||
messages = [{"role": msg.role, "content": msg.content} for msg in history_msgs]
|
||
|
||
# 流式调用AI
|
||
async def generate():
|
||
full_response = ""
|
||
try:
|
||
result = await ai_service.chat(
|
||
task_type="reasoning",
|
||
messages=messages,
|
||
system_prompt=ARCHITECTURE_SYSTEM_PROMPT,
|
||
stream=True,
|
||
model_config_id=request.model_config_id,
|
||
)
|
||
if isinstance(result, str):
|
||
full_response = result
|
||
yield f"data: {json.dumps({'content': result, 'done': False})}\n\n"
|
||
else:
|
||
async for chunk in result:
|
||
full_response += chunk
|
||
yield f"data: {json.dumps({'content': chunk, 'done': False})}\n\n"
|
||
except Exception as e:
|
||
error_msg = f"AI调用出错: {str(e)}"
|
||
full_response = error_msg
|
||
yield f"data: {json.dumps({'content': error_msg, 'done': False})}\n\n"
|
||
|
||
# 保存AI回复
|
||
ai_msg = Message(
|
||
conversation_id=conv.id,
|
||
role="assistant",
|
||
content=full_response,
|
||
)
|
||
db.add(ai_msg)
|
||
db.commit()
|
||
|
||
yield f"data: {json.dumps({'content': '', 'done': True, 'conversation_id': conv.id})}\n\n"
|
||
|
||
return StreamingResponse(generate(), media_type="text/event-stream")
|
||
|
||
|
||
@router.delete("/conversations/{conversation_id}")
|
||
def delete_conversation(
|
||
conversation_id: int,
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""删除对话"""
|
||
conv = db.query(Conversation).filter(
|
||
Conversation.id == conversation_id,
|
||
Conversation.user_id == current_user.id,
|
||
).first()
|
||
if not conv:
|
||
raise HTTPException(status_code=404, detail="对话不存在")
|
||
|
||
db.query(Message).filter(Message.conversation_id == conversation_id).delete()
|
||
db.delete(conv)
|
||
db.commit()
|
||
return {"message": "删除成功"}
|