use axum::Extension; use axum::extract::{FromRef, Path, Query, State}; use axum::response::Json; use validator::Validate; use erp_core::error::AppError; use erp_core::rbac::require_permission; use erp_core::types::{ApiResponse, PaginatedResponse, Pagination, TenantContext}; use uuid::Uuid; use crate::config_state::ConfigState; use crate::dto::{ CreateNumberingRuleReq, GenerateNumberResp, NumberingRuleResp, UpdateNumberingRuleReq, }; use crate::service::numbering_service::NumberingService; #[utoipa::path( get, path = "/api/v1/numbering-rules", params(Pagination), responses( (status = 200, description = "成功", body = ApiResponse>), (status = 401, description = "未授权"), (status = 403, description = "权限不足"), ), security(("bearer_auth" = [])), tag = "编号规则" )] /// GET /api/v1/numbering-rules /// /// 分页查询当前租户下的编号规则列表。 /// 需要 `numbering.list` 权限。 pub async fn list_numbering_rules( State(state): State, Extension(ctx): Extension, Query(pagination): Query, ) -> Result>>, AppError> where ConfigState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "numbering.list")?; let (rules, total) = NumberingService::list(ctx.tenant_id, &pagination, &state.db).await?; let page = pagination.page.unwrap_or(1); let page_size = pagination.limit(); let total_pages = total.div_ceil(page_size); Ok(Json(ApiResponse::ok(PaginatedResponse { data: rules, total, page, page_size, total_pages, }))) } #[utoipa::path( post, path = "/api/v1/numbering-rules", request_body = CreateNumberingRuleReq, responses( (status = 200, description = "成功", body = ApiResponse), (status = 401, description = "未授权"), (status = 403, description = "权限不足"), ), security(("bearer_auth" = [])), tag = "编号规则" )] /// POST /api/v1/numbering-rules /// /// 创建新的编号规则。 /// 规则编码在租户内必须唯一。 /// 需要 `numbering.create` 权限。 pub async fn create_numbering_rule( State(state): State, Extension(ctx): Extension, Json(req): Json, ) -> Result>, AppError> where ConfigState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "numbering.create")?; req.validate() .map_err(|e| AppError::Validation(e.to_string()))?; let rule = NumberingService::create( ctx.tenant_id, ctx.user_id, &req, &state.db, &state.event_bus, ) .await?; 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), (status = 401, description = "未授权"), (status = 403, description = "权限不足"), ), security(("bearer_auth" = [])), tag = "编号规则" )] /// PUT /api/v1/numbering-rules/:id /// /// 更新编号规则的可编辑字段。 /// 需要 `numbering.update` 权限。 pub async fn update_numbering_rule( State(state): State, Extension(ctx): Extension, Path(id): Path, Json(req): Json, ) -> Result>, AppError> where ConfigState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "numbering.update")?; let rule = NumberingService::update(id, ctx.tenant_id, ctx.user_id, &req, &state.db).await?; 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), (status = 401, description = "未授权"), (status = 403, description = "权限不足"), ), security(("bearer_auth" = [])), tag = "编号规则" )] /// POST /api/v1/numbering-rules/:id/generate /// /// 根据编号规则生成新的编号。 /// 使用 PostgreSQL advisory lock 保证并发安全。 /// 需要 `numbering.generate` 权限。 pub async fn generate_number( State(state): State, Extension(ctx): Extension, Path(id): Path, ) -> Result>, AppError> where ConfigState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "numbering.generate")?; let result = NumberingService::generate_number(id, ctx.tenant_id, &state.db).await?; 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 时间戳。 /// 需要请求体包含 version 字段用于乐观锁校验。 /// 需要 `numbering.delete` 权限。 pub async fn delete_numbering_rule( State(state): State, Extension(ctx): Extension, Path(id): Path, Json(req): Json, ) -> Result>, AppError> where ConfigState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "numbering.delete")?; NumberingService::delete( id, ctx.tenant_id, ctx.user_id, req.version, &state.db, &state.event_bus, ) .await?; Ok(Json(ApiResponse { success: true, data: None, message: Some("编号规则已删除".to_string()), })) } /// 删除编号规则的乐观锁版本号请求体。 #[derive(Debug, serde::Deserialize, utoipa::ToSchema)] pub struct DeleteNumberingVersionReq { pub version: i32, }