feat: add template detail and config APIs
This commit is contained in:
@@ -0,0 +1,27 @@
|
|||||||
|
from fastapi import APIRouter, HTTPException
|
||||||
|
|
||||||
|
from ..schemas.template import DataSourcePayload
|
||||||
|
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/api/data-sources", tags=["data-sources"])
|
||||||
|
|
||||||
|
DATA_SOURCES: list[dict] = [
|
||||||
|
{"code": "policy", "name": "政策文件", "description": "政策法规、制度文件等资料"},
|
||||||
|
{"code": "business_data", "name": "业务数据", "description": "业务系统导出的结构化数据"},
|
||||||
|
{"code": "research", "name": "调研材料", "description": "访谈、问卷、调研纪要等非结构化资料"},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("")
|
||||||
|
def list_data_sources():
|
||||||
|
return {"data": DATA_SOURCES, "message": "ok"}
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("")
|
||||||
|
def create_data_source(payload: DataSourcePayload):
|
||||||
|
if any(item["code"] == payload.code for item in DATA_SOURCES):
|
||||||
|
raise HTTPException(status_code=400, detail="数据源编码已存在")
|
||||||
|
|
||||||
|
item = payload.model_dump()
|
||||||
|
DATA_SOURCES.append(item)
|
||||||
|
return {"data": item, "message": "ok"}
|
||||||
@@ -1,8 +1,16 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile
|
from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from ..database import get_db
|
from ..database import get_db
|
||||||
|
from ..models.block_config import BlockConfig
|
||||||
|
from ..models.template import Template
|
||||||
|
from ..models.template_block import TemplateBlock
|
||||||
|
from ..schemas.template import BlockConfigPayload
|
||||||
|
from ..services.doc_parser import build_tree
|
||||||
from ..services.doc_parser import parse_document
|
from ..services.doc_parser import parse_document
|
||||||
|
from ..services.html_generator import generate_preview_html
|
||||||
from ..services.template_service import create_template_record, save_template_blocks, store_template_file
|
from ..services.template_service import create_template_record, save_template_blocks, store_template_file
|
||||||
|
|
||||||
|
|
||||||
@@ -37,3 +45,120 @@ def upload_template(
|
|||||||
},
|
},
|
||||||
"message": "ok",
|
"message": "ok",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{template_id}")
|
||||||
|
def get_template_detail(template_id: int, db: Session = Depends(get_db)):
|
||||||
|
template = db.get(Template, template_id)
|
||||||
|
if not template:
|
||||||
|
raise HTTPException(status_code=404, detail="模板不存在")
|
||||||
|
|
||||||
|
block_records = (
|
||||||
|
db.query(TemplateBlock)
|
||||||
|
.filter(TemplateBlock.template_id == template_id)
|
||||||
|
.order_by(TemplateBlock.sort_order.asc(), TemplateBlock.id.asc())
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
blocks = [serialize_block(block) for block in block_records]
|
||||||
|
tree = build_tree([block.copy() for block in blocks])
|
||||||
|
configs = {
|
||||||
|
config.block_id: serialize_config(config)
|
||||||
|
for config in db.query(BlockConfig).filter(BlockConfig.template_id == template_id).all()
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"data": {
|
||||||
|
"template": serialize_template(template),
|
||||||
|
"preview_html": generate_preview_html(blocks),
|
||||||
|
"blocks": blocks,
|
||||||
|
"tree": tree,
|
||||||
|
"configs": configs,
|
||||||
|
},
|
||||||
|
"message": "ok",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{template_id}/blocks/{block_id}/config")
|
||||||
|
def save_block_config(
|
||||||
|
template_id: int,
|
||||||
|
block_id: str,
|
||||||
|
payload: BlockConfigPayload,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
):
|
||||||
|
block = (
|
||||||
|
db.query(TemplateBlock)
|
||||||
|
.filter(TemplateBlock.template_id == template_id, TemplateBlock.block_id == block_id)
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
if not block:
|
||||||
|
raise HTTPException(status_code=404, detail="模板区域不存在")
|
||||||
|
|
||||||
|
config = (
|
||||||
|
db.query(BlockConfig)
|
||||||
|
.filter(BlockConfig.template_id == template_id, BlockConfig.block_id == block_id)
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
if not config:
|
||||||
|
config = BlockConfig(template_id=template_id, block_id=block_id)
|
||||||
|
db.add(config)
|
||||||
|
|
||||||
|
config.region_name = payload.region_name
|
||||||
|
config.region_type = payload.region_type
|
||||||
|
config.data_sources = json.dumps(payload.data_sources, ensure_ascii=False)
|
||||||
|
config.prompt = payload.prompt
|
||||||
|
config.output_format = payload.output_format
|
||||||
|
config.need_review = payload.need_review
|
||||||
|
config.remark = payload.remark
|
||||||
|
config.enabled = payload.enabled
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
db.refresh(config)
|
||||||
|
return {"data": serialize_config(config), "message": "ok"}
|
||||||
|
|
||||||
|
|
||||||
|
def serialize_template(template: Template) -> dict:
|
||||||
|
return {
|
||||||
|
"id": template.id,
|
||||||
|
"name": template.name,
|
||||||
|
"type": template.type,
|
||||||
|
"version": template.version,
|
||||||
|
"original_file_path": template.original_file_path,
|
||||||
|
"status": template.status,
|
||||||
|
"created_by": template.created_by,
|
||||||
|
"created_at": template.created_at.isoformat() if template.created_at else None,
|
||||||
|
"updated_at": template.updated_at.isoformat() if template.updated_at else None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def serialize_block(block: TemplateBlock) -> dict:
|
||||||
|
return {
|
||||||
|
"id": block.id,
|
||||||
|
"template_id": block.template_id,
|
||||||
|
"block_id": block.block_id,
|
||||||
|
"parent_block_id": block.parent_block_id,
|
||||||
|
"type": block.block_type,
|
||||||
|
"block_type": block.block_type,
|
||||||
|
"block_name": block.block_name,
|
||||||
|
"text": block.text_preview or "",
|
||||||
|
"text_preview": block.text_preview,
|
||||||
|
"level": block.level,
|
||||||
|
"sort_order": block.sort_order,
|
||||||
|
"table_rows": block.table_rows,
|
||||||
|
"table_cols": block.table_cols,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def serialize_config(config: BlockConfig) -> dict:
|
||||||
|
return {
|
||||||
|
"id": config.id,
|
||||||
|
"template_id": config.template_id,
|
||||||
|
"block_id": config.block_id,
|
||||||
|
"region_name": config.region_name,
|
||||||
|
"region_type": config.region_type,
|
||||||
|
"data_sources": json.loads(config.data_sources or "[]"),
|
||||||
|
"prompt": config.prompt,
|
||||||
|
"output_format": config.output_format,
|
||||||
|
"need_review": config.need_review,
|
||||||
|
"remark": config.remark,
|
||||||
|
"enabled": config.enabled,
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
|
||||||
|
from .api.data_sources import router as data_sources_router
|
||||||
from .api.templates import router as templates_router
|
from .api.templates import router as templates_router
|
||||||
|
|
||||||
app = FastAPI(title="AI 文档模板生成系统", version="0.1.0")
|
app = FastAPI(title="AI 文档模板生成系统", version="0.1.0")
|
||||||
@@ -14,6 +15,7 @@ app.add_middleware(
|
|||||||
)
|
)
|
||||||
|
|
||||||
app.include_router(templates_router)
|
app.include_router(templates_router)
|
||||||
|
app.include_router(data_sources_router)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/health")
|
@app.get("/health")
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
|
||||||
|
class BlockConfigPayload(BaseModel):
|
||||||
|
region_name: str | None = None
|
||||||
|
region_type: str = "ai_generate"
|
||||||
|
data_sources: list[str] = Field(default_factory=list)
|
||||||
|
prompt: str | None = None
|
||||||
|
output_format: str = "formal_paragraph"
|
||||||
|
need_review: int = 1
|
||||||
|
remark: str | None = None
|
||||||
|
enabled: int = 1
|
||||||
|
|
||||||
|
|
||||||
|
class DataSourcePayload(BaseModel):
|
||||||
|
name: str
|
||||||
|
code: str
|
||||||
|
description: str | None = None
|
||||||
@@ -45,10 +45,10 @@
|
|||||||
| 028 | 表格 → `<table data-block-id>` 转换 + 边框样式 | 后端 | 0.25d | ✅ 已完成 |
|
| 028 | 表格 → `<table data-block-id>` 转换 + 边框样式 | 后端 | 0.25d | ✅ 已完成 |
|
||||||
| 029 | 拼接完整 HTML 字符串(带内联 CSS) | 后端 | 0.25d | ✅ 已完成 |
|
| 029 | 拼接完整 HTML 字符串(带内联 CSS) | 后端 | 0.25d | ✅ 已完成 |
|
||||||
| | **查询 + 配置接口** | | | |
|
| | **查询 + 配置接口** | | | |
|
||||||
| 030 | 实现 `GET /api/templates/{id}` 组装全部数据 | 后端 | 0.5d | ⏳ 未完成 |
|
| 030 | 实现 `GET /api/templates/{id}` 组装全部数据 | 后端 | 0.5d | ✅ 已完成 |
|
||||||
| 031 | 实现 `POST /api/templates/{id}/blocks/{blockId}/config` upsert | 后端 | 0.25d | ⏳ 未完成 |
|
| 031 | 实现 `POST /api/templates/{id}/blocks/{blockId}/config` upsert | 后端 | 0.25d | ✅ 已完成 |
|
||||||
| 032 | 实现 `GET /api/data-sources` 返回预设列表 | 后端 | 0.25d | ⏳ 未完成 |
|
| 032 | 实现 `GET /api/data-sources` 返回预设列表 | 后端 | 0.25d | ✅ 已完成 |
|
||||||
| 033 | 实现 `POST /api/data-sources` 新增数据源 | 后端 | 0.25d | ⏳ 未完成 |
|
| 033 | 实现 `POST /api/data-sources` 新增数据源 | 后端 | 0.25d | ✅ 已完成 |
|
||||||
| | **四栏布局** | | | |
|
| | **四栏布局** | | | |
|
||||||
| 034 | 实现四栏 CSS Grid 布局 | 前端 | 0.5d | ⏳ 未完成 |
|
| 034 | 实现四栏 CSS Grid 布局 | 前端 | 0.5d | ⏳ 未完成 |
|
||||||
| 035 | 实现左侧菜单 shell 组件 | 前端 | 0.25d | ⏳ 未完成 |
|
| 035 | 实现左侧菜单 shell 组件 | 前端 | 0.25d | ⏳ 未完成 |
|
||||||
|
|||||||
@@ -47,3 +47,15 @@
|
|||||||
5. 增加 HTML 转义,避免 Word 文本中的特殊字符破坏预览结构。
|
5. 增加 HTML 转义,避免 Word 文本中的特殊字符破坏预览结构。
|
||||||
6. 使用示例 blocks 验证标题、段落、表格 HTML 均生成正确。
|
6. 使用示例 blocks 验证标题、段落、表格 HTML 均生成正确。
|
||||||
- **执行结果**: 完成任务 026-029,HTML 预览生成服务已满足基础预览要求。
|
- **执行结果**: 完成任务 026-029,HTML 预览生成服务已满足基础预览要求。
|
||||||
|
|
||||||
|
## 会话 ID: 20260701-query-config-api
|
||||||
|
- [2026-07-01 20:31:33]
|
||||||
|
- **执行原因**: 按任务清单继续完成查询与配置接口 030-033。
|
||||||
|
- **执行过程**:
|
||||||
|
1. 新增 `BlockConfigPayload` 和 `DataSourcePayload` 请求模型。
|
||||||
|
2. 实现 `GET /api/templates/{template_id}`,聚合模板、预览 HTML、blocks、tree 和 configs。
|
||||||
|
3. 实现 `POST /api/templates/{template_id}/blocks/{block_id}/config`,按 `(template_id, block_id)` 进行 upsert。
|
||||||
|
4. 新增 `GET /api/data-sources` 返回 MVP 预设数据源列表。
|
||||||
|
5. 新增 `POST /api/data-sources` 支持新增内存数据源并校验 code 唯一。
|
||||||
|
6. 执行语法检查和 FastAPI 路由注册检查。
|
||||||
|
- **执行结果**: 完成任务 030-033,模板详情、区域配置保存和数据源接口已可调用。
|
||||||
|
|||||||
Reference in New Issue
Block a user