feat(controller):增加控制器功能以及产品检索控制器。

This commit is contained in:
徐涛 2024-04-03 09:55:29 +08:00
parent 55dd3c0cc0
commit e068143f60
8 changed files with 74 additions and 3 deletions

View File

@ -1,8 +1,11 @@
mod license;
mod products;
use axum::Router;
/// 生成可迭代的转化为路由定义的控制器列表。
pub fn controllers() -> Box<dyn Iterator<Item = Box<Router>>> {
let controllers: Vec<Box<Router>> = vec![];
let controllers: Vec<Box<Router>> = vec![Box::new(products::ProductsController::init().into())];
Box::from(controllers.into_iter())
}

View File

@ -0,0 +1,43 @@
use axum::{extract::Query, http::StatusCode, response::IntoResponse, routing, Json, Router};
use tracing::info;
use crate::vo::KeywordQuery;
pub struct ProductsController {
routes: Router,
}
impl Into<Router> for ProductsController {
fn into(self) -> Router {
self.routes
}
}
impl ProductsController {
pub fn init() -> Self {
let routes = Router::new().route("/products", routing::get(search_products));
Self { routes }
}
}
async fn search_products(Query(keyword_query): Query<KeywordQuery>) -> impl IntoResponse {
info!(keyword = keyword_query.keyword(), "search products");
let response_products = {
if let Some(keyword) = keyword_query.keyword() {
crate::products::get()
.iter()
.filter(|&p| p.name.to_lowercase().contains(&(keyword.to_lowercase())))
.collect::<Vec<_>>()
} else {
crate::products::get().iter().collect::<Vec<_>>()
}
};
let products_length = response_products.len();
info!(
fetched_length = ?products_length,
"retrieved {} products", products_length
);
(StatusCode::OK, Json(response_products))
}

View File

@ -7,6 +7,8 @@ mod controllers;
mod logging;
mod products;
mod server_routes;
mod utils;
mod vo;
#[tokio::main]
async fn main() {

View File

@ -1,9 +1,9 @@
use std::sync::OnceLock;
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use tokio::{fs::File, io::AsyncReadExt};
#[derive(Debug, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Product {
pub id: String,
pub name: String,

View File

@ -0,0 +1,8 @@
pub fn empty_to_none<S: Into<String>>(s: S) -> Option<String> {
let s = s.into();
if s.is_empty() {
None
} else {
Some(s)
}
}

View File

@ -0,0 +1,3 @@
mod shared;
pub use shared::*;

View File

@ -0,0 +1,12 @@
use serde::Deserialize;
#[derive(Debug, Clone, Deserialize)]
pub struct KeywordQuery {
pub keyword: Option<String>,
}
impl KeywordQuery {
pub fn keyword(&self) -> Option<String> {
self.keyword.clone().and_then(crate::utils::empty_to_none)
}
}