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:
@@ -15,6 +15,18 @@ use crate::dto::{
|
||||
};
|
||||
use crate::service::dictionary_service::DictionaryService;
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/dictionaries",
|
||||
params(Pagination),
|
||||
responses(
|
||||
(status = 200, description = "成功", body = ApiResponse<PaginatedResponse<DictionaryResp>>),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "字典管理"
|
||||
)]
|
||||
/// GET /api/v1/dictionaries
|
||||
///
|
||||
/// 分页查询当前租户下的字典列表。
|
||||
@@ -47,6 +59,18 @@ where
|
||||
})))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/v1/dictionaries",
|
||||
request_body = CreateDictionaryReq,
|
||||
responses(
|
||||
(status = 200, description = "成功", body = ApiResponse<DictionaryResp>),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "字典管理"
|
||||
)]
|
||||
/// POST /api/v1/dictionaries
|
||||
///
|
||||
/// 在当前租户下创建新字典。
|
||||
@@ -80,6 +104,19 @@ where
|
||||
Ok(Json(ApiResponse::ok(dictionary)))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/api/v1/dictionaries/{id}",
|
||||
params(("id" = Uuid, Path, description = "字典ID")),
|
||||
request_body = UpdateDictionaryReq,
|
||||
responses(
|
||||
(status = 200, description = "成功", body = ApiResponse<DictionaryResp>),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "字典管理"
|
||||
)]
|
||||
/// PUT /api/v1/dictionaries/:id
|
||||
///
|
||||
/// 更新字典的可编辑字段(名称、描述)。
|
||||
@@ -103,6 +140,19 @@ where
|
||||
Ok(Json(ApiResponse::ok(dictionary)))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
delete,
|
||||
path = "/api/v1/dictionaries/{id}",
|
||||
params(("id" = Uuid, Path, description = "字典ID")),
|
||||
request_body = DeleteVersionReq,
|
||||
responses(
|
||||
(status = 200, description = "成功"),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "字典管理"
|
||||
)]
|
||||
/// DELETE /api/v1/dictionaries/:id
|
||||
///
|
||||
/// 软删除字典,设置 deleted_at 时间戳。
|
||||
@@ -137,6 +187,18 @@ where
|
||||
}))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/dictionaries/items-by-code",
|
||||
params(("code" = String, Query, description = "字典编码")),
|
||||
responses(
|
||||
(status = 200, description = "成功", body = ApiResponse<Vec<DictionaryItemResp>>),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "字典管理"
|
||||
)]
|
||||
/// GET /api/v1/dictionaries/items-by-code?code=xxx
|
||||
///
|
||||
/// 根据字典编码查询所有字典项。
|
||||
@@ -159,6 +221,19 @@ where
|
||||
Ok(Json(ApiResponse::ok(items)))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/v1/dictionaries/{dict_id}/items",
|
||||
params(("dict_id" = Uuid, Path, description = "字典ID")),
|
||||
request_body = CreateDictionaryItemReq,
|
||||
responses(
|
||||
(status = 200, description = "成功", body = ApiResponse<DictionaryItemResp>),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "字典管理"
|
||||
)]
|
||||
/// POST /api/v1/dictionaries/:dict_id/items
|
||||
///
|
||||
/// 向指定字典添加新的字典项。
|
||||
@@ -185,6 +260,22 @@ where
|
||||
Ok(Json(ApiResponse::ok(item)))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/api/v1/dictionaries/{dict_id}/items/{item_id}",
|
||||
params(
|
||||
("dict_id" = Uuid, Path, description = "字典ID"),
|
||||
("item_id" = Uuid, Path, description = "字典项ID"),
|
||||
),
|
||||
request_body = UpdateDictionaryItemReq,
|
||||
responses(
|
||||
(status = 200, description = "成功", body = ApiResponse<DictionaryItemResp>),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "字典管理"
|
||||
)]
|
||||
/// PUT /api/v1/dictionaries/:dict_id/items/:item_id
|
||||
///
|
||||
/// 更新字典项的可编辑字段(label、value、sort_order、color)。
|
||||
@@ -213,6 +304,22 @@ where
|
||||
Ok(Json(ApiResponse::ok(item)))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
delete,
|
||||
path = "/api/v1/dictionaries/{dict_id}/items/{item_id}",
|
||||
params(
|
||||
("dict_id" = Uuid, Path, description = "字典ID"),
|
||||
("item_id" = Uuid, Path, description = "字典项ID"),
|
||||
),
|
||||
request_body = DeleteVersionReq,
|
||||
responses(
|
||||
(status = 200, description = "成功"),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "字典管理"
|
||||
)]
|
||||
/// DELETE /api/v1/dictionaries/:dict_id/items/:item_id
|
||||
///
|
||||
/// 软删除字典项,设置 deleted_at 时间戳。
|
||||
@@ -247,7 +354,7 @@ pub struct ItemsByCodeQuery {
|
||||
}
|
||||
|
||||
/// 删除操作的乐观锁版本号。
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
|
||||
pub struct DeleteVersionReq {
|
||||
pub version: i32,
|
||||
}
|
||||
|
||||
@@ -10,6 +10,17 @@ use crate::config_state::ConfigState;
|
||||
use crate::dto::{LanguageResp, SetSettingParams, UpdateLanguageReq};
|
||||
use crate::service::setting_service::SettingService;
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/languages",
|
||||
responses(
|
||||
(status = 200, description = "成功", body = ApiResponse<Vec<LanguageResp>>),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "语言管理"
|
||||
)]
|
||||
/// GET /api/v1/languages
|
||||
///
|
||||
/// 获取当前租户的语言配置列表。
|
||||
@@ -56,6 +67,19 @@ where
|
||||
Ok(JsonResponse(ApiResponse::ok(languages)))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/api/v1/languages/{code}",
|
||||
params(("code" = String, Path, description = "语言编码")),
|
||||
request_body = UpdateLanguageReq,
|
||||
responses(
|
||||
(status = 200, description = "成功", body = ApiResponse<LanguageResp>),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "语言管理"
|
||||
)]
|
||||
/// PUT /api/v1/languages/:code
|
||||
///
|
||||
/// 更新指定语言配置的激活状态。
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -14,6 +14,18 @@ use crate::dto::{
|
||||
};
|
||||
use crate::service::numbering_service::NumberingService;
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/numbering-rules",
|
||||
params(Pagination),
|
||||
responses(
|
||||
(status = 200, description = "成功", body = ApiResponse<PaginatedResponse<NumberingRuleResp>>),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "编号规则"
|
||||
)]
|
||||
/// GET /api/v1/numbering-rules
|
||||
///
|
||||
/// 分页查询当前租户下的编号规则列表。
|
||||
@@ -44,6 +56,18 @@ where
|
||||
})))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/v1/numbering-rules",
|
||||
request_body = CreateNumberingRuleReq,
|
||||
responses(
|
||||
(status = 200, description = "成功", body = ApiResponse<NumberingRuleResp>),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "编号规则"
|
||||
)]
|
||||
/// POST /api/v1/numbering-rules
|
||||
///
|
||||
/// 创建新的编号规则。
|
||||
@@ -75,6 +99,19 @@ where
|
||||
Ok(Json(ApiResponse::ok(rule)))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/api/v1/numbering-rules/{id}",
|
||||
params(("id" = Uuid, Path, description = "编号规则ID")),
|
||||
request_body = UpdateNumberingRuleReq,
|
||||
responses(
|
||||
(status = 200, description = "成功", body = ApiResponse<NumberingRuleResp>),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "编号规则"
|
||||
)]
|
||||
/// PUT /api/v1/numbering-rules/:id
|
||||
///
|
||||
/// 更新编号规则的可编辑字段。
|
||||
@@ -96,6 +133,18 @@ where
|
||||
Ok(Json(ApiResponse::ok(rule)))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/v1/numbering-rules/{id}/generate",
|
||||
params(("id" = Uuid, Path, description = "编号规则ID")),
|
||||
responses(
|
||||
(status = 200, description = "成功", body = ApiResponse<GenerateNumberResp>),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "编号规则"
|
||||
)]
|
||||
/// POST /api/v1/numbering-rules/:id/generate
|
||||
///
|
||||
/// 根据编号规则生成新的编号。
|
||||
@@ -117,6 +166,19 @@ where
|
||||
Ok(Json(ApiResponse::ok(result)))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
delete,
|
||||
path = "/api/v1/numbering-rules/{id}",
|
||||
params(("id" = Uuid, Path, description = "编号规则ID")),
|
||||
request_body = DeleteNumberingVersionReq,
|
||||
responses(
|
||||
(status = 200, description = "成功"),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "编号规则"
|
||||
)]
|
||||
/// DELETE /api/v1/numbering-rules/:id
|
||||
///
|
||||
/// 软删除编号规则,设置 deleted_at 时间戳。
|
||||
@@ -152,7 +214,7 @@ where
|
||||
}
|
||||
|
||||
/// 删除编号规则的乐观锁版本号请求体。
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
|
||||
pub struct DeleteNumberingVersionReq {
|
||||
pub version: i32,
|
||||
}
|
||||
|
||||
@@ -11,6 +11,22 @@ use crate::config_state::ConfigState;
|
||||
use crate::dto::{SetSettingParams, SettingResp, UpdateSettingReq};
|
||||
use crate::service::setting_service::SettingService;
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/settings/{key}",
|
||||
params(
|
||||
("key" = String, Path, description = "设置键名"),
|
||||
("scope" = Option<String>, Query, description = "作用域"),
|
||||
("scope_id" = Option<Uuid>, Query, description = "作用域ID"),
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "成功", body = ApiResponse<SettingResp>),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "系统设置"
|
||||
)]
|
||||
/// GET /api/v1/settings/:key?scope=tenant&scope_id=xxx
|
||||
///
|
||||
/// 获取设置值,支持分层回退查找。
|
||||
@@ -36,6 +52,19 @@ where
|
||||
Ok(Json(ApiResponse::ok(setting)))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/api/v1/settings/{key}",
|
||||
params(("key" = String, Path, description = "设置键名")),
|
||||
request_body = UpdateSettingReq,
|
||||
responses(
|
||||
(status = 200, description = "成功", body = ApiResponse<SettingResp>),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "系统设置"
|
||||
)]
|
||||
/// PUT /api/v1/settings/:key
|
||||
///
|
||||
/// 创建或更新设置值。
|
||||
@@ -78,6 +107,23 @@ pub struct SettingQuery {
|
||||
pub scope_id: Option<Uuid>,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
delete,
|
||||
path = "/api/v1/settings/{key}",
|
||||
params(
|
||||
("key" = String, Path, description = "设置键名"),
|
||||
("scope" = Option<String>, Query, description = "作用域"),
|
||||
("scope_id" = Option<Uuid>, Query, description = "作用域ID"),
|
||||
),
|
||||
request_body = DeleteSettingVersionReq,
|
||||
responses(
|
||||
(status = 200, description = "成功"),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "系统设置"
|
||||
)]
|
||||
/// DELETE /api/v1/settings/:key
|
||||
///
|
||||
/// 软删除设置值,设置 deleted_at 时间戳。
|
||||
@@ -117,7 +163,7 @@ where
|
||||
}
|
||||
|
||||
/// 删除设置的乐观锁版本号请求体。
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
|
||||
pub struct DeleteSettingVersionReq {
|
||||
pub version: i32,
|
||||
}
|
||||
|
||||
@@ -10,6 +10,17 @@ use crate::config_state::ConfigState;
|
||||
use crate::dto::{SetSettingParams, ThemeResp};
|
||||
use crate::service::setting_service::SettingService;
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/themes",
|
||||
responses(
|
||||
(status = 200, description = "成功", body = ApiResponse<ThemeResp>),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "主题设置"
|
||||
)]
|
||||
/// GET /api/v1/theme
|
||||
///
|
||||
/// 获取当前租户的主题配置。
|
||||
@@ -33,6 +44,18 @@ where
|
||||
Ok(JsonResponse(ApiResponse::ok(theme)))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/api/v1/themes",
|
||||
request_body = ThemeResp,
|
||||
responses(
|
||||
(status = 200, description = "成功", body = ApiResponse<ThemeResp>),
|
||||
(status = 401, description = "未授权"),
|
||||
(status = 403, description = "权限不足"),
|
||||
),
|
||||
security(("bearer_auth" = [])),
|
||||
tag = "主题设置"
|
||||
)]
|
||||
/// PUT /api/v1/theme
|
||||
///
|
||||
/// 更新当前租户的主题配置。
|
||||
|
||||
Reference in New Issue
Block a user