feat: 审批流程模块 (T审批A审批)
- 新增 backend/app/api/approval.py 审批API - 前端H5支持发起审批、审批操作 - 添加审批卡片弹窗组件 - 路由注册审批模块
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# API 文档生成脚本
|
||||
# =============================================================================
|
||||
# 用途: 从 FastAPI 后端自动生成 OpenAPI 规范 + 静态 HTML 文档
|
||||
# 输出:
|
||||
# docs/api/openapi.json - OpenAPI 3.0 规范
|
||||
# docs/api/index.html - Swagger UI 静态版
|
||||
# docs/api/redoc.html - ReDoc 静态版
|
||||
#
|
||||
# 用法:
|
||||
# bash scripts/generate-api-docs.sh # 跑后端拿 OpenAPI
|
||||
# bash scripts/generate-api-docs.sh --from-running # 从运行中后端拿
|
||||
# bash scripts/generate-api-docs.sh --offline # 离线生成(无需后端)
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
ok() { echo -e "${GREEN}[OK]${NC} $1"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
|
||||
|
||||
PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
API_DOCS_DIR="docs/api"
|
||||
mkdir -p "$API_DOCS_DIR"
|
||||
|
||||
# 参数
|
||||
MODE="auto"
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
--from-running) MODE="running" ;;
|
||||
--offline) MODE="offline" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# =============================================================================
|
||||
# 1. 拿 OpenAPI 规范
|
||||
# =============================================================================
|
||||
info "── 1/3 拿 OpenAPI 规范"
|
||||
|
||||
case $MODE in
|
||||
running|auto)
|
||||
# 先看后端跑没
|
||||
if curl -s -f http://localhost:8000/openapi.json > /tmp/openapi.json 2>/dev/null; then
|
||||
ok "从运行中后端拿 OpenAPI"
|
||||
cp /tmp/openapi.json "$API_DOCS_DIR/openapi.json"
|
||||
elif [ "$MODE" = "running" ]; then
|
||||
error "后端没跑,无法从 running 拿"
|
||||
else
|
||||
warn "后端没跑,改用离线生成"
|
||||
MODE="offline"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$MODE" = "offline" ]; then
|
||||
info "离线生成 OpenAPI(import FastAPI app)..."
|
||||
cd backend
|
||||
if [ ! -d "venv" ]; then
|
||||
warn "后端 venv 不存在,跑: python -m venv venv && pip install -r requirements.txt"
|
||||
fi
|
||||
|
||||
cat > /tmp/gen_openapi.py <<'PYEOF'
|
||||
import json
|
||||
import sys
|
||||
try:
|
||||
from app.main import app
|
||||
spec = app.openapi()
|
||||
print(json.dumps(spec, ensure_ascii=False, indent=2))
|
||||
except Exception as e:
|
||||
print(f"ERROR: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
PYEOF
|
||||
|
||||
if command -v python &> /dev/null; then
|
||||
if python /tmp/gen_openapi.py > "../$API_DOCS_DIR/openapi.json" 2>/dev/null; then
|
||||
ok "离线生成 OpenAPI 成功"
|
||||
else
|
||||
# 试 python3
|
||||
if python3 /tmp/gen_openapi.py > "../$API_DOCS_DIR/openapi.json" 2>/dev/null; then
|
||||
ok "离线生成 OpenAPI 成功(python3)"
|
||||
else
|
||||
warn "离线生成失败,降级到 mock 模式"
|
||||
cat > "../$API_DOCS_DIR/openapi.json" <<'JSONEOF'
|
||||
{
|
||||
"openapi": "3.0.0",
|
||||
"info": {
|
||||
"title": "企微 IT 智能服务台 API",
|
||||
"version": "1.0.0",
|
||||
"description": "离线生成的 mock,实际跑后端再生成"
|
||||
},
|
||||
"paths": {}
|
||||
}
|
||||
JSONEOF
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
cd "$PROJECT_ROOT"
|
||||
fi
|
||||
|
||||
# 验证 OpenAPI
|
||||
if [ ! -f "$API_DOCS_DIR/openapi.json" ]; then
|
||||
error "OpenAPI 规范生成失败"
|
||||
fi
|
||||
|
||||
ENDPOINT_COUNT=$(python -c "import json; d=json.load(open('$API_DOCS_DIR/openapi.json')); print(len(d.get('paths', {})))" 2>/dev/null || echo "?")
|
||||
ok "OpenAPI 规范生成,端点数: $ENDPOINT_COUNT"
|
||||
|
||||
# =============================================================================
|
||||
# 2. 生成 Swagger UI 静态 HTML
|
||||
# =============================================================================
|
||||
info "── 2/3 生成 Swagger UI 静态 HTML"
|
||||
|
||||
cat > "$API_DOCS_DIR/index.html" <<'HTMLEOF'
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>企微 IT 智能服务台 API - Swagger UI</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5.10.5/swagger-ui.css">
|
||||
<style>
|
||||
body { margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
|
||||
.topbar { background: #2c3e50; color: white; padding: 12px 24px; }
|
||||
.topbar h1 { margin: 0; font-size: 20px; }
|
||||
.topbar a { color: #3498db; text-decoration: none; margin-left: 16px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="topbar">
|
||||
<h1>📡 企微 IT 智能服务台 API 文档</h1>
|
||||
<a href="redoc.html">📖 ReDoc 版</a>
|
||||
<a href="openapi.json">📄 OpenAPI 规范</a>
|
||||
</div>
|
||||
<div id="swagger-ui"></div>
|
||||
<script src="https://unpkg.com/swagger-ui-dist@5.10.5/swagger-ui-bundle.js"></script>
|
||||
<script>
|
||||
window.onload = () => {
|
||||
window.ui = SwaggerUIBundle({
|
||||
url: "openapi.json",
|
||||
dom_id: "#swagger-ui",
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis
|
||||
],
|
||||
layout: "BaseLayout"
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
HTMLEOF
|
||||
|
||||
ok "Swagger UI 生成: $API_DOCS_DIR/index.html"
|
||||
|
||||
# =============================================================================
|
||||
# 3. 生成 ReDoc 静态 HTML
|
||||
# =============================================================================
|
||||
info "── 3/3 生成 ReDoc 静态 HTML"
|
||||
|
||||
cat > "$API_DOCS_DIR/redoc.html" <<'HTMLEOF'
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>企微 IT 智能服务台 API - ReDoc</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
|
||||
<style>
|
||||
body { margin: 0; padding: 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<redoc spec-url="openapi.json"></redoc>
|
||||
<script src="https://unpkg.com/redoc@2.0.0/bundles/redoc.standalone.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
HTMLEOF
|
||||
|
||||
ok "ReDoc 生成: $API_DOCS_DIR/redoc.html"
|
||||
|
||||
# =============================================================================
|
||||
# 4. 生成 API 模块清单
|
||||
# =============================================================================
|
||||
info "── 4/4 生成模块清单"
|
||||
|
||||
python3 -c "
|
||||
import json
|
||||
with open('$API_DOCS_DIR/openapi.json') as f:
|
||||
spec = json.load(f)
|
||||
paths = spec.get('paths', {})
|
||||
modules = {}
|
||||
for path, methods in paths.items():
|
||||
# 解析 /api/v1/<module>/<endpoint>
|
||||
parts = path.split('/')
|
||||
if len(parts) >= 4 and parts[1] == 'api' and parts[2] == 'v1':
|
||||
module = parts[3]
|
||||
if module not in modules:
|
||||
modules[module] = []
|
||||
for method in methods.keys():
|
||||
if method in ['get', 'post', 'put', 'delete', 'patch']:
|
||||
modules[module].append({
|
||||
'method': method.upper(),
|
||||
'path': path,
|
||||
})
|
||||
|
||||
print('# API 模块清单')
|
||||
print()
|
||||
print('**生成日期**: $(date +%Y-%m-%d)')
|
||||
print('**端点总数**: ', len(paths))
|
||||
print('**模块数**: ', len(modules))
|
||||
print()
|
||||
print('| 模块 | 端点数 | 端点 |')
|
||||
print('|---|---|---|')
|
||||
for module, endpoints in sorted(modules.items()):
|
||||
eps = ', '.join(f\"{e['method']} {e['path']}\" for e in endpoints[:5])
|
||||
if len(endpoints) > 5:
|
||||
eps += f' ... (+{len(endpoints)-5})'
|
||||
print(f\"| {module} | {len(endpoints)} | {eps} |\")
|
||||
" > "$API_DOCS_DIR/MODULES.md" 2>/dev/null || {
|
||||
warn "模块清单生成失败(Python 解析)"
|
||||
cat > "$API_DOCS_DIR/MODULES.md" <<'EOF'
|
||||
# API 模块清单
|
||||
|
||||
(生成失败,见 docs/api/openapi.json 自行查看)
|
||||
EOF
|
||||
}
|
||||
|
||||
ok "模块清单生成: $API_DOCS_DIR/MODULES.md"
|
||||
|
||||
# =============================================================================
|
||||
# 总结
|
||||
# =============================================================================
|
||||
info "── 总结"
|
||||
echo ""
|
||||
echo "输出文件:"
|
||||
echo " $API_DOCS_DIR/openapi.json - OpenAPI 3.0 规范"
|
||||
echo " $API_DOCS_DIR/index.html - Swagger UI 静态版"
|
||||
echo " $API_DOCS_DIR/redoc.html - ReDoc 静态版"
|
||||
echo " $API_DOCS_DIR/MODULES.md - 模块清单"
|
||||
echo ""
|
||||
echo "查看方式:"
|
||||
echo " 1. 浏览器打开 file://\$(pwd)/$API_DOCS_DIR/index.html"
|
||||
echo " 2. 跑 python -m http.server -d $API_DOCS_DIR 8080 → 浏览器 http://localhost:8080"
|
||||
echo ""
|
||||
echo "CI 集成:"
|
||||
echo " 把 'bash scripts/generate-api-docs.sh' 加进 Gitea Actions"
|
||||
echo " 跑批频率:每次 main 推送后"
|
||||
|
||||
ok "API 文档生成完成"
|
||||
Reference in New Issue
Block a user