feat: add utoipa path annotations to all API handlers and wire OpenAPI spec

- Add #[utoipa::path] annotations to all 70+ handler functions across
  auth, config, workflow, and message modules
- Add IntoParams/ToSchema derives to Pagination, PaginatedResponse, ApiResponse
  in erp-core, and MessageQuery/TemplateQuery in erp-message
- Collect all module paths into OpenAPI spec via AuthApiDoc, ConfigApiDoc,
  WorkflowApiDoc, MessageApiDoc structs in erp-server main.rs
- Update openapi_spec handler to merge all module specs
- The /docs/openapi.json endpoint now returns complete API documentation
  with all endpoints, request/response schemas, and security requirements
This commit is contained in:
iven
2026-04-15 01:23:27 +08:00
parent ee65b6e3c9
commit e44d6063be
21 changed files with 1165 additions and 22 deletions

View File

@@ -9,9 +9,20 @@ use erp_core::types::{ApiResponse, TenantContext};
use uuid::Uuid;
use crate::config_state::ConfigState;
use crate::dto::{BatchSaveMenusReq, CreateMenuReq, MenuResp};
use crate::dto::{BatchSaveMenusReq, CreateMenuReq, MenuResp, UpdateMenuReq};
use crate::service::menu_service::MenuService;
#[utoipa::path(
get,
path = "/api/v1/config/menus",
responses(
(status = 200, description = "成功", body = ApiResponse<Vec<MenuResp>>),
(status = 401, description = "未授权"),
(status = 403, description = "权限不足"),
),
security(("bearer_auth" = [])),
tag = "菜单管理"
)]
/// GET /api/v1/config/menus
///
/// 获取当前租户下当前用户角色可见的菜单树。
@@ -36,6 +47,18 @@ where
Ok(JsonResponse(ApiResponse::ok(menus)))
}
#[utoipa::path(
post,
path = "/api/v1/config/menus",
request_body = CreateMenuReq,
responses(
(status = 200, description = "成功", body = ApiResponse<MenuResp>),
(status = 401, description = "未授权"),
(status = 403, description = "权限不足"),
),
security(("bearer_auth" = [])),
tag = "菜单管理"
)]
/// POST /api/v1/config/menus
///
/// 创建单个菜单项。
@@ -65,6 +88,19 @@ where
Ok(JsonResponse(ApiResponse::ok(resp)))
}
#[utoipa::path(
put,
path = "/api/v1/config/menus/{id}",
params(("id" = Uuid, Path, description = "菜单ID")),
request_body = UpdateMenuReq,
responses(
(status = 200, description = "成功", body = ApiResponse<MenuResp>),
(status = 401, description = "未授权"),
(status = 403, description = "权限不足"),
),
security(("bearer_auth" = [])),
tag = "菜单管理"
)]
/// PUT /api/v1/config/menus/{id}
///
/// 更新单个菜单项。
@@ -72,7 +108,7 @@ pub async fn update_menu<S>(
State(state): State<ConfigState>,
Extension(ctx): Extension<TenantContext>,
Path(id): Path<Uuid>,
Json(req): Json<crate::dto::UpdateMenuReq>,
Json(req): Json<UpdateMenuReq>,
) -> Result<JsonResponse<ApiResponse<MenuResp>>, AppError>
where
ConfigState: FromRef<S>,
@@ -84,6 +120,19 @@ where
Ok(JsonResponse(ApiResponse::ok(resp)))
}
#[utoipa::path(
delete,
path = "/api/v1/config/menus/{id}",
params(("id" = Uuid, Path, description = "菜单ID")),
request_body = DeleteMenuVersionReq,
responses(
(status = 200, description = "成功"),
(status = 401, description = "未授权"),
(status = 403, description = "权限不足"),
),
security(("bearer_auth" = [])),
tag = "菜单管理"
)]
/// DELETE /api/v1/config/menus/{id}
///
/// 软删除单个菜单项。需要请求体包含 version 字段用于乐观锁校验。
@@ -111,6 +160,18 @@ where
Ok(JsonResponse(ApiResponse::ok(())))
}
#[utoipa::path(
put,
path = "/api/v1/config/menus/batch",
request_body = BatchSaveMenusReq,
responses(
(status = 200, description = "成功"),
(status = 401, description = "未授权"),
(status = 403, description = "权限不足"),
),
security(("bearer_auth" = [])),
tag = "菜单管理"
)]
/// PUT /api/v1/config/menus/batch
///
/// 批量保存菜单列表。
@@ -132,7 +193,7 @@ where
match item.id {
Some(id) => {
let version = item.version.unwrap_or(0);
let update_req = crate::dto::UpdateMenuReq {
let update_req = UpdateMenuReq {
title: Some(item.title.clone()),
path: item.path.clone(),
icon: item.icon.clone(),
@@ -176,7 +237,7 @@ where
}
/// 删除菜单的乐观锁版本号请求体。
#[derive(Debug, serde::Deserialize)]
#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
pub struct DeleteMenuVersionReq {
pub version: i32,
}