nxtgauge-backend-rust/crates/db/src/models/verification.rs

153 lines
4.5 KiB
Rust

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, PgPool};
use uuid::Uuid;
#[derive(Debug, Serialize, Deserialize, FromRow)]
pub struct Verification {
pub id: Uuid,
pub user_id: Uuid,
pub role_key: String,
pub status: String,
pub priority: String,
pub case_type: String,
pub payload: serde_json::Value,
pub documents: serde_json::Value,
pub notes: Option<String>,
pub rejection_reason: Option<String>,
pub assigned_to: Option<Uuid>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Serialize, Deserialize, FromRow)]
pub struct VerificationLog {
pub id: Uuid,
pub verification_id: Uuid,
pub action: String,
pub actor_id: Option<Uuid>,
pub old_status: Option<String>,
pub new_status: Option<String>,
pub message: Option<String>,
pub created_at: DateTime<Utc>,
}
pub struct VerificationRepository;
impl VerificationRepository {
pub async fn create(
pool: &PgPool,
user_id: Uuid,
role_key: &str,
case_type: &str,
priority: &str,
payload: serde_json::Value,
documents: serde_json::Value,
) -> Result<Verification, sqlx::Error> {
sqlx::query_as::<_, Verification>(
r#"
INSERT INTO verifications (user_id, role_key, case_type, priority, payload, documents)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING *
"#
)
.bind(user_id)
.bind(role_key)
.bind(case_type)
.bind(priority)
.bind(payload)
.bind(documents)
.fetch_one(pool)
.await
}
pub async fn get_by_id(pool: &PgPool, id: Uuid) -> Result<Option<Verification>, sqlx::Error> {
sqlx::query_as::<_, Verification>("SELECT * FROM verifications WHERE id = $1")
.bind(id)
.fetch_optional(pool)
.await
}
pub async fn list(
pool: &PgPool,
status: Option<&str>,
case_type: Option<&str>,
page: i64,
limit: i64,
) -> Result<Vec<Verification>, sqlx::Error> {
let offset = (page - 1) * limit;
let mut query = "SELECT * FROM verifications WHERE 1=1".to_string();
if status.is_some() { query.push_str(" AND status = $1"); }
if case_type.is_some() { query.push_str(if status.is_some() { " AND case_type = $2" } else { " AND case_type = $1" }); }
query.push_str(" ORDER BY created_at DESC LIMIT $3 OFFSET $4"); // This simplified query string concatenation is for readability, handle properly in prod.
// Actually implementing with sqlx properly:
sqlx::query_as::<_, Verification>(
r#"
SELECT * FROM verifications
WHERE ($1::TEXT IS NULL OR status = $1)
AND ($2::TEXT IS NULL OR case_type = $2)
ORDER BY
CASE priority WHEN 'HIGH' THEN 1 WHEN 'MEDIUM' THEN 2 ELSE 3 END,
created_at DESC
LIMIT $3 OFFSET $4
"#
)
.bind(status)
.bind(case_type)
.bind(limit)
.bind(offset)
.fetch_all(pool)
.await
}
pub async fn update_status(
pool: &PgPool,
id: Uuid,
new_status: &str,
actor_id: Option<Uuid>,
notes: Option<&str>,
rejection_reason: Option<&str>,
) -> Result<Verification, sqlx::Error> {
let mut tx = pool.begin().await?;
let old = sqlx::query_as::<_, Verification>("SELECT * FROM verifications WHERE id = $1 FOR UPDATE")
.bind(id)
.fetch_one(&mut *tx)
.await?;
let updated = sqlx::query_as::<_, Verification>(
r#"
UPDATE verifications
SET status = $2, notes = COALESCE($3, notes), rejection_reason = COALESCE($4, rejection_reason), updated_at = NOW()
WHERE id = $1
RETURNING *
"#
)
.bind(id)
.bind(new_status)
.bind(notes)
.bind(rejection_reason)
.fetch_one(&mut *tx)
.await?;
sqlx::query(
r#"
INSERT INTO verification_logs (verification_id, action, actor_id, old_status, new_status, message)
VALUES ($1, 'STATUS_CHANGE', $2, $3, $4, $5)
"#
)
.bind(id)
.bind(actor_id)
.bind(&old.status)
.bind(new_status)
.bind(notes)
.execute(&mut *tx)
.await?;
tx.commit().await?;
Ok(updated)
}
}