cool_core/controller/
mod.rs

1//! 控制器模块
2//!
3//! 对应 TypeScript 版本的 `controller/`
4
5use crate::entity::{DeleteParam, ListQuery, PageQuery, QueryOption};
6use crate::error::{CoolError, CoolResponse, CoolResult};
7use crate::service::BaseService;
8use salvo::prelude::*;
9use serde_json::Value;
10
11/// CRUD API 类型
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum CrudApi {
14    Add,
15    Delete,
16    Update,
17    Page,
18    Info,
19    List,
20}
21
22impl CrudApi {
23    pub fn all() -> Vec<Self> {
24        vec![
25            Self::Add,
26            Self::Delete,
27            Self::Update,
28            Self::Page,
29            Self::Info,
30            Self::List,
31        ]
32    }
33
34    pub fn as_str(&self) -> &'static str {
35        match self {
36            Self::Add => "add",
37            Self::Delete => "delete",
38            Self::Update => "update",
39            Self::Page => "page",
40            Self::Info => "info",
41            Self::List => "list",
42        }
43    }
44}
45
46/// 控制器配置
47#[derive(Debug, Clone, Default)]
48pub struct ControllerOption {
49    /// 路由前缀
50    pub prefix: Option<String>,
51    /// 启用的 API
52    pub api: Vec<CrudApi>,
53    /// 分页查询配置
54    pub page_query_op: QueryOption,
55    /// 列表查询配置
56    pub list_query_op: QueryOption,
57    /// info 忽略返回属性
58    pub info_ignore_property: Vec<String>,
59}
60
61/// 控制器基类 trait
62///
63/// 提供通用的 CRUD 接口处理
64#[async_trait]
65pub trait BaseController: Send + Sync + 'static {
66    /// 获取控制器配置
67    fn option(&self) -> &ControllerOption;
68
69    /// 获取表名
70    fn table_name(&self) -> &str;
71
72    /// 获取数据库连接
73    fn db(&self) -> &sea_orm::DatabaseConnection;
74}
75
76/// CRUD 处理器
77///
78/// 用于处理通用的 CRUD 请求
79#[allow(dead_code)]
80pub struct CrudHandler {
81    table: String,
82    db: std::sync::Arc<sea_orm::DatabaseConnection>,
83}
84
85impl CrudHandler {
86    pub fn new(table: impl Into<String>, db: std::sync::Arc<sea_orm::DatabaseConnection>) -> Self {
87        Self {
88            table: table.into(),
89            db,
90        }
91    }
92}
93
94/// 从 Depot 中获取通用 CRUD Service
95fn get_crud_service(depot: &Depot) -> CoolResult<&dyn BaseService> {
96    if let Ok(svc) = depot.get::<&'static dyn BaseService>("crud_service") {
97        Ok(*svc)
98    } else {
99        Err(CoolError::comm(
100            "未找到 CRUD 服务实例,请先在 Depot 中注册 `crud_service`",
101        ))
102    }
103}
104
105/// 新增接口
106#[handler]
107pub async fn handle_add(req: &mut Request, res: &mut Response, depot: &mut Depot) {
108    let body: Value = match req.parse_json().await {
109        Ok(v) => v,
110        Err(e) => {
111            res.render(Json(CoolResponse::<()>::fail(e.to_string())));
112            return;
113        }
114    };
115
116    let service = match get_crud_service(depot) {
117        Ok(s) => s,
118        Err(e) => {
119            res.render(Json(CoolResponse::<()>::from_error(&e)));
120            return;
121        }
122    };
123
124    match service.add(body).await {
125        Ok(result) => res.render(Json(CoolResponse::ok(result))),
126        Err(e) => res.render(Json(CoolResponse::<()>::from_error(&e))),
127    }
128}
129
130/// 删除接口
131#[handler]
132pub async fn handle_delete(req: &mut Request, res: &mut Response, depot: &mut Depot) {
133    let param: DeleteParam = match req.parse_json().await {
134        Ok(v) => v,
135        Err(e) => {
136            res.render(Json(CoolResponse::<()>::fail(e.to_string())));
137            return;
138        }
139    };
140
141    let service = match get_crud_service(depot) {
142        Ok(s) => s,
143        Err(e) => {
144            res.render(Json(CoolResponse::<()>::from_error(&e)));
145            return;
146        }
147    };
148
149    match service.delete(param).await {
150        Ok(_) => res.render(Json(CoolResponse::<()>::ok_empty())),
151        Err(e) => res.render(Json(CoolResponse::<()>::from_error(&e))),
152    }
153}
154
155/// 修改接口
156#[handler]
157pub async fn handle_update(req: &mut Request, res: &mut Response, depot: &mut Depot) {
158    let body: Value = match req.parse_json().await {
159        Ok(v) => v,
160        Err(e) => {
161            res.render(Json(CoolResponse::<()>::fail(e.to_string())));
162            return;
163        }
164    };
165
166    let service = match get_crud_service(depot) {
167        Ok(s) => s,
168        Err(e) => {
169            res.render(Json(CoolResponse::<()>::from_error(&e)));
170            return;
171        }
172    };
173
174    match service.update(body).await {
175        Ok(_) => res.render(Json(CoolResponse::<()>::ok_empty())),
176        Err(e) => res.render(Json(CoolResponse::<()>::from_error(&e))),
177    }
178}
179
180/// 分页查询接口
181#[handler]
182pub async fn handle_page(req: &mut Request, res: &mut Response, depot: &mut Depot) {
183    let query: PageQuery = req.parse_queries().unwrap_or_default();
184
185    let service = match get_crud_service(depot) {
186        Ok(s) => s,
187        Err(e) => {
188            res.render(Json(CoolResponse::<()>::from_error(&e)));
189            return;
190        }
191    };
192
193    // 这里暂时使用默认的 QueryOption,后续可以按需从 Depot 或配置中注入
194    let option = QueryOption::default();
195
196    match service.page(query, option).await {
197        Ok(result) => res.render(Json(CoolResponse::ok(result))),
198        Err(e) => res.render(Json(CoolResponse::<()>::from_error(&e))),
199    }
200}
201
202/// 详情查询接口
203#[handler]
204pub async fn handle_info(req: &mut Request, res: &mut Response, depot: &mut Depot) {
205    let id: Option<i64> = req.query("id").and_then(|s: &str| s.parse::<i64>().ok());
206
207    let id = match id {
208        Some(id) => id,
209        None => {
210            res.render(Json(CoolResponse::<()>::fail("缺少 id 参数")));
211            return;
212        }
213    };
214
215    let service = match get_crud_service(depot) {
216        Ok(s) => s,
217        Err(e) => {
218            res.render(Json(CoolResponse::<()>::from_error(&e)));
219            return;
220        }
221    };
222
223    // 暂不处理 ignore_property,可在上层自行过滤字段
224    match service.info(id, None).await {
225        Ok(data) => res.render(Json(CoolResponse::ok(data))),
226        Err(e) => res.render(Json(CoolResponse::<()>::from_error(&e))),
227    }
228}
229
230/// 列表查询接口
231#[handler]
232pub async fn handle_list(req: &mut Request, res: &mut Response, depot: &mut Depot) {
233    let query: ListQuery = req.parse_queries().unwrap_or_default();
234
235    let service = match get_crud_service(depot) {
236        Ok(s) => s,
237        Err(e) => {
238            res.render(Json(CoolResponse::<()>::from_error(&e)));
239            return;
240        }
241    };
242
243    // 这里暂时使用默认的 QueryOption,后续可以按需从 Depot 或配置中注入
244    let option = QueryOption::default();
245
246    match service.list(query, option).await {
247        Ok(result) => res.render(Json(CoolResponse::ok(result))),
248        Err(e) => res.render(Json(CoolResponse::<()>::from_error(&e))),
249    }
250}
251
252/// 构建 CRUD 路由
253///
254/// 根据控制器配置自动生成路由
255pub fn build_crud_router(prefix: &str, apis: &[CrudApi]) -> Router {
256    let mut router = Router::with_path(prefix);
257
258    for api in apis {
259        match api {
260            CrudApi::Add => {
261                router = router.push(Router::with_path("add").post(handle_add));
262            }
263            CrudApi::Delete => {
264                router = router.push(Router::with_path("delete").post(handle_delete));
265            }
266            CrudApi::Update => {
267                router = router.push(Router::with_path("update").post(handle_update));
268            }
269            CrudApi::Page => {
270                router = router.push(Router::with_path("page").post(handle_page));
271            }
272            CrudApi::Info => {
273                router = router.push(Router::with_path("info").get(handle_info));
274            }
275            CrudApi::List => {
276                router = router.push(Router::with_path("list").post(handle_list));
277            }
278        }
279    }
280
281    router
282}