Files
bianchengshequ/backend/routers/architecture.py

297 lines
9.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""架构选型助手路由"""
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": "删除成功"}