fix:优化项目内容
This commit is contained in:
@@ -3,7 +3,11 @@ from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Union
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
import json
|
||||
from typing import AsyncGenerator
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy.orm import Session, joinedload
|
||||
|
||||
from backend.app import models
|
||||
@@ -105,6 +109,7 @@ def _build_markdown_from_analysis(data: Union[Dict[str, Any], List[Any]]) -> str
|
||||
@router.get("/", response_model=list[ProjectRead])
|
||||
async def list_projects(
|
||||
customer_tag: str | None = None,
|
||||
limit: int = Query(30, ge=1, le=200, description="默认只返回最近 N 条"),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""列表项目;customer_tag 不为空时只返回该客户标签下的项目(按客户 tags 筛选)。"""
|
||||
@@ -127,7 +132,7 @@ async def list_projects(
|
||||
t.ilike(f"%,{tag}"),
|
||||
)
|
||||
)
|
||||
return query.all()
|
||||
return query.limit(limit).all()
|
||||
|
||||
|
||||
@router.get("/{project_id}", response_model=ProjectRead)
|
||||
@@ -200,6 +205,88 @@ async def analyze_project_requirement(
|
||||
)
|
||||
|
||||
|
||||
@router.post("/analyze_stream")
|
||||
async def analyze_project_requirement_stream(
|
||||
payload: RequirementAnalyzeRequest,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
SSE 流式输出 Markdown 到前端(用于编辑器实时显示)。
|
||||
data: {"type":"delta","content":"..."} / {"type":"done","project_id":1}
|
||||
"""
|
||||
customer = db.query(models.Customer).get(payload.customer_id)
|
||||
if not customer:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Customer not found")
|
||||
|
||||
# Create a project first so we can return project_id at end
|
||||
project = models.Project(
|
||||
customer_id=payload.customer_id,
|
||||
raw_requirement=payload.raw_text,
|
||||
ai_solution_md="",
|
||||
status="draft",
|
||||
)
|
||||
db.add(project)
|
||||
db.commit()
|
||||
db.refresh(project)
|
||||
|
||||
async def gen() -> AsyncGenerator[str, None]:
|
||||
from backend.app.services.ai_service import get_active_ai_config
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
config = get_active_ai_config()
|
||||
api_key = (config.get("api_key") or "").strip()
|
||||
if not api_key:
|
||||
yield f"data: {json.dumps({'type':'error','message':'AI API Key 未配置'})}\n\n"
|
||||
return
|
||||
base_url = (config.get("base_url") or "").strip() or None
|
||||
client = AsyncOpenAI(api_key=api_key, base_url=base_url)
|
||||
model = config.get("model_name") or "gpt-4o-mini"
|
||||
temperature = float(config.get("temperature", 0.2))
|
||||
|
||||
system_prompt = (
|
||||
(config.get("system_prompt_override") or "").strip()
|
||||
or "你是一名资深系统架构师,请输出可直接编辑的 Markdown 方案,不要输出 JSON。"
|
||||
)
|
||||
user_prompt = (
|
||||
"请基于以下客户原始需求,输出一份可交付的项目方案草稿(Markdown)。\n"
|
||||
"要求包含:概要、功能模块拆分、技术实现思路、工时与报价估算、备注。\n\n"
|
||||
f"【客户原始需求】\n{payload.raw_text}"
|
||||
)
|
||||
|
||||
full = ""
|
||||
try:
|
||||
stream = await client.chat.completions.create(
|
||||
model=model,
|
||||
messages=[
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": user_prompt},
|
||||
],
|
||||
temperature=temperature,
|
||||
stream=True,
|
||||
)
|
||||
async for event in stream:
|
||||
delta = (event.choices[0].delta.content or "") if event.choices else ""
|
||||
if not delta:
|
||||
continue
|
||||
full += delta
|
||||
yield f"data: {json.dumps({'type':'delta','content':delta}, ensure_ascii=False)}\n\n"
|
||||
except Exception as exc:
|
||||
yield f"data: {json.dumps({'type':'error','message':str(exc)}, ensure_ascii=False)}\n\n"
|
||||
return
|
||||
|
||||
# Save final markdown
|
||||
try:
|
||||
project.ai_solution_md = full
|
||||
db.add(project)
|
||||
db.commit()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
yield f"data: {json.dumps({'type':'done','project_id':project.id}, ensure_ascii=False)}\n\n"
|
||||
|
||||
return StreamingResponse(gen(), media_type="text/event-stream")
|
||||
|
||||
|
||||
@router.post("/{project_id}/generate_quote", response_model=QuoteGenerateResponse)
|
||||
async def generate_project_quote(
|
||||
project_id: int,
|
||||
|
||||
Reference in New Issue
Block a user