Files
cms-service/src/infrastructure/repositories/column.rs
2026-02-02 14:27:56 +08:00

175 lines
4.2 KiB
Rust

use crate::domain::models::Column;
use common_telemetry::AppError;
use sqlx::PgPool;
use uuid::Uuid;
pub async fn create_column(
pool: &PgPool,
tenant_id: Uuid,
name: String,
slug: String,
description: Option<String>,
parent_id: Option<Uuid>,
sort_order: i32,
) -> Result<Column, AppError> {
let column = sqlx::query_as::<_, Column>(
r#"
INSERT INTO cms_columns (tenant_id, name, slug, description, parent_id, sort_order)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING tenant_id, id, name, slug, description, parent_id, sort_order, created_at, updated_at
"#,
)
.bind(tenant_id)
.bind(name)
.bind(slug)
.bind(description)
.bind(parent_id)
.bind(sort_order)
.fetch_one(pool)
.await?;
Ok(column)
}
pub async fn get_column(pool: &PgPool, tenant_id: Uuid, id: Uuid) -> Result<Column, AppError> {
let column = sqlx::query_as::<_, Column>(
r#"
SELECT tenant_id, id, name, slug, description, parent_id, sort_order, created_at, updated_at
FROM cms_columns
WHERE tenant_id = $1 AND id = $2
"#,
)
.bind(tenant_id)
.bind(id)
.fetch_one(pool)
.await?;
Ok(column)
}
pub async fn update_column(
pool: &PgPool,
tenant_id: Uuid,
id: Uuid,
name: Option<String>,
slug: Option<String>,
description: Option<Option<String>>,
parent_id: Option<Option<Uuid>>,
sort_order: Option<i32>,
) -> Result<Column, AppError> {
let column = sqlx::query_as::<_, Column>(
r#"
UPDATE cms_columns
SET
name = COALESCE($3, name),
slug = COALESCE($4, slug),
description = COALESCE($5, description),
parent_id = COALESCE($6, parent_id),
sort_order = COALESCE($7, sort_order),
updated_at = now()
WHERE tenant_id = $1 AND id = $2
RETURNING tenant_id, id, name, slug, description, parent_id, sort_order, created_at, updated_at
"#,
)
.bind(tenant_id)
.bind(id)
.bind(name)
.bind(slug)
.bind(description)
.bind(parent_id)
.bind(sort_order)
.fetch_one(pool)
.await?;
Ok(column)
}
pub async fn delete_column(pool: &PgPool, tenant_id: Uuid, id: Uuid) -> Result<(), AppError> {
let res = sqlx::query(
r#"
DELETE FROM cms_columns
WHERE tenant_id = $1 AND id = $2
"#,
)
.bind(tenant_id)
.bind(id)
.execute(pool)
.await?;
if res.rows_affected() == 0 {
return Err(AppError::NotFound("column:not_found".into()));
}
Ok(())
}
#[derive(Debug, Clone)]
pub struct ListColumnsQuery {
pub page: u32,
pub page_size: u32,
pub search: Option<String>,
pub parent_id: Option<Uuid>,
}
#[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)]
pub struct Paged<T> {
pub items: Vec<T>,
pub page: u32,
pub page_size: u32,
pub total: i64,
}
pub async fn list_columns(
pool: &PgPool,
tenant_id: Uuid,
q: ListColumnsQuery,
) -> Result<Paged<Column>, AppError> {
let page = q.page.max(1);
let page_size = q.page_size.clamp(1, 200);
let offset = ((page - 1) * page_size) as i64;
let limit = page_size as i64;
let like = q.search.map(|s| format!("%{}%", s));
let total: i64 = sqlx::query_scalar(
r#"
SELECT COUNT(*)
FROM cms_columns
WHERE tenant_id = $1
AND ($2::uuid IS NULL OR parent_id = $2)
AND ($3::text IS NULL OR name ILIKE $3 OR slug ILIKE $3)
"#,
)
.bind(tenant_id)
.bind(q.parent_id)
.bind(like.as_deref())
.fetch_one(pool)
.await?;
let items = sqlx::query_as::<_, Column>(
r#"
SELECT tenant_id, id, name, slug, description, parent_id, sort_order, created_at, updated_at
FROM cms_columns
WHERE tenant_id = $1
AND ($2::uuid IS NULL OR parent_id = $2)
AND ($3::text IS NULL OR name ILIKE $3 OR slug ILIKE $3)
ORDER BY sort_order ASC, updated_at DESC
OFFSET $4
LIMIT $5
"#,
)
.bind(tenant_id)
.bind(q.parent_id)
.bind(like.as_deref())
.bind(offset)
.bind(limit)
.fetch_all(pool)
.await?;
Ok(Paged {
items,
page,
page_size,
total,
})
}