QiLuo Framework · Module test_api Integration Guide
QiLuo Framework · Module test_api Integration Guide
This document describes the complete flow of adding a new business module (using test_api as an example), including database, entities, parameters, model operations, services, routes, and validation.
1. Database (Two Methods)
Method A: Manually create the data table (if the table already exists)
CREATE TABLE `test_api` (
`id` bigint NOT NULL COMMENT 'ID',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'Name',
`age` int DEFAULT NULL COMMENT 'Age',
`email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'Email',
`created_at` datetime DEFAULT NULL COMMENT 'Creation time',
`updated_at` datetime DEFAULT NULL COMMENT 'Update time',
`deleted_at` datetime DEFAULT NULL COMMENT 'Deletion time',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC;Method B: SeaORM Migration (recommended if familiar)
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.create_table(
Table::create()
.table(TestApi::Table)
.if_not_exists()
.col(ColumnDef::new(TestApi::Id).big_integer().not_null().primary_key())
.col(ColumnDef::new(TestApi::Name).string().string_len(255).null())
.col(ColumnDef::new(TestApi::Age).integer().null())
.col(ColumnDef::new(TestApi::Email).string().string_len(255).null())
.col(ColumnDef::new(TestApi::CreatedAt).date_time().null())
.col(ColumnDef::new(TestApi::UpdatedAt).date_time().null())
.col(ColumnDef::new(TestApi::DeletedAt).date_time().null())
.to_owned(),
).await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.drop_table(Table::drop().table(TestApi::Table).to_owned()).await
}
}2. Generate Entity
sea-orm-cli generate entity -o src/db/entities -t test_apiThis will generate:
- src/db/entities/test_api.rs
- src/db/entities/prelude.rs (adds TestApi)
// src/db/entities/test_api.rs (generated, manually add some macros)
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "test_api")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i64,
pub name: Option<String>,
pub age: Option<i32>,
pub email: Option<String>,
pub created_at: Option<DateTime>,
pub updated_at: Option<DateTime>,
pub deleted_at: Option<DateTime>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}3. Directory & Module Declaration
Recommended structure:
src/
api/
mod.rs
test/
mod.rs
model/
prelude.rs
test/
mod.rs
args/
mod.rs
atest_api.rs
model/
mod.rs
mtest_api.rs
entity/
mod.rs
prelude.rs
test_api.rs
service/
test/
mod.rs
stest_api.rsModule declaration files are in the Chinese source. Follow the same pattern.
4. Parameter Definitions
Define request/response parameters in src/model/test/args/atest_api.rs:
// Search parameters
#[derive(Debug, Clone, Serialize, Deserialize, FromQueryResult, Validate)]
pub struct TestApiSearch {
pub id: Option<String>,
pub name: Option<String>,
pub age: Option<i32>,
pub email: Option<String>,
}
// Add request
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct TestApiAdd {
#[validate(length(min = 1, max = 255))]
pub name: Option<String>,
#[validate(range(min = 0, max = 150))]
pub age: Option<i32>,
#[validate(email)]
pub email: Option<String>,
}
// Edit request
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct TestApiEdit {
#[serde(with = "i64_to_string")]
pub id: i64,
#[validate(length(min = 1, max = 255))]
pub name: Option<String>,
#[validate(range(min = 0, max = 150))]
pub age: Option<i32>,
#[validate(email)]
pub email: Option<String>,
}
// Delete request
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct TestApiDel {
#[serde(with = "i64_to_string")]
pub id: i64,
}5. Database Operations (Model Layer)
In src/model/test/model/mtest_api.rs, implement CRUD operations using SeaORM's ActiveModel and Entity traits with soft-delete support.
6. Service Layer
In src/service/test/stest_api.rs, wrap model operations and business logic:
use crate::model::test::model::mtest_api::{
TestApiAdd, TestApiDel, TestApiEdit, TestApiModel, TestApiSearch,
};
use crate::service::prelude::*;
pub async fn list(VQuery(arg): VQuery<PageParams>, VQuery(search): VQuery<TestApiSearch>) -> impl IntoResponse {
let rlist = TestApiModel::list(arg, search).await;
ApiResponse::from_result(rlist)
}
pub async fn edit(VJson(arg): VJson<TestApiEdit>) -> impl IntoResponse {
let r = TestApiModel::edit(arg).await;
ApiResponse::from_result(r)
}
pub async fn add(VJson(arg): VJson<TestApiAdd>) -> impl IntoResponse {
let r = TestApiModel::add(arg).await;
ApiResponse::from_result(r)
}
pub async fn delete(VQuery(arg): VQuery<TestApiDel>) -> impl IntoResponse {
let r = TestApiModel::del(arg).await;
ApiResponse::from_result(r)
}7. Route Registration (API)
In src/api/test/mod.rs, register routes using WebPath:
use axum::routing::{delete, get, post, put};
pub fn router_test() -> WebPath {
WebPath::new().nest("test", test_api_routes())
}
fn test_api_routes() -> WebPath {
WebPath::new()
.route("/list", WebPathType::Get, Some("Test API List"), get(stest_api::list))
.route("/add", WebPathType::Post, Some("Test API Add"), post(stest_api::add))
.route("/edit", WebPathType::Put, Some("Test API Edit"), put(stest_api::edit))
.route("/del", WebPathType::Delete, Some("Test API Delete"), delete(stest_api::delete))
}Then merge in the parent api module.
8. Debug & Verify
# Start backend
cargo run
# Access: http://localhost:5001/api/test/list?page_num=1&page_size=109. Validate Reference
#[validate(length(min = 1, max = 255))]- String length validation#[validate(range(min = 0, max = 150))]- Numeric range validation#[validate(email)]- Email format validation#[validate(url)]- URL format validation#[validate(regex = "...")]- Regex pattern validation#[validate(custom = "function")]- Custom validation function
10. Important Note (ID Precision)
Snowflake IDs (i64) can exceed JavaScript's safe integer range (2^53). Use #[serde(with = "i64_to_string")] to serialize IDs as strings for the frontend and deserialize strings back to i64. This must be applied to all DTOs that carry ID fields.