use anyhow::Context as _;
use sqlx::mysql::MySqlRow;
use sqlx::{MySql, Pool, QueryBuilder};
use crate::commons::string_to_number;
use crate::{commons::timestamp_millis, core::error2::Result};
use super::models::OtpCodeRecord;
#[derive(serde::Deserialize)]
pub struct OptCodeRenewParam {
pub id: String,
pub name: String,
}
impl OtpCodeRecord {
pub async fn query_by_user_id(
pool: &Pool<MySql>,
user_id: &str,
) -> Result<Vec<indexmap::IndexMap<String, String>>> {
#[rustfmt::skip]
let statment: &str = *super::models::SELECT_STATMENT;
let mut query_builder: sqlx::QueryBuilder<sqlx::MySql> = sqlx::QueryBuilder::new(statment);
query_builder.push(" where f_user_id = ");
query_builder.push_bind(user_id);
let rows = query_builder
.build()
.fetch_all(pool)
.await
.context("query_by_user_id: Failed to perform a query to retrieve a otpcode.")?;
let mut list: Vec<indexmap::IndexMap<String, String>> = Vec::new();
for row in rows {
let record = OtpCodeRecord::from_row(row)
.context("query_by_user_id: Failed to perform from_row.")?;
let mut result = indexmap::IndexMap::<String, String>::new();
result.insert("id".to_string(), record.id.to_owned().to_string());
result.insert("name".to_string(), record.name.to_owned());
result.insert("code".to_string(), record.code.to_string());
result.insert("expired_at".to_string(), record.expired_at.to_string());
result.insert("seconds".to_string(), record.period.to_string());
list.push(result);
}
Ok(list)
}
pub async fn query_by_name<'a>(
pool: &Pool<MySql>,
name: &'a str,
key: &'a str,
) -> Result<Option<MySqlRow>> {
#[rustfmt::skip]
let statment: &str = *super::models::SELECT_STATMENT;
let mut query_builder: sqlx::QueryBuilder<'a, sqlx::MySql> =
sqlx::QueryBuilder::new(statment);
query_builder.push(" where f_name = ");
query_builder.push_bind(name);
query_builder.push(" and f_key = ");
query_builder.push_bind(key);
let row = query_builder
.build()
.fetch_optional(pool)
.await
.context("query_by_name: Failed to perform a query to retrieve a otpcode.")?;
Ok(row)
}
pub async fn query_by_id<'a>(
pool: &Pool<MySql>,
id: &'a str,
name: &'a str,
user_id: &'a str,
) -> Result<Option<MySqlRow>> {
#[rustfmt::skip]
let statment: &str = *super::models::SELECT_STATMENT;
let mut query_builder: sqlx::QueryBuilder<'a, sqlx::MySql> =
sqlx::QueryBuilder::new(statment);
query_builder.push(" where f_id = ");
query_builder.push_bind(string_to_number::<u64>(id)?);
query_builder.push(" and f_name = ");
query_builder.push_bind(name);
query_builder.push(" and f_user_id = ");
query_builder.push_bind(user_id);
let row = query_builder
.build()
.fetch_optional(pool)
.await
.context("query_by_id: Failed to perform a query to retrieve a otpcode.")?;
Ok(row)
}
pub async fn re_new_code(pool: &Pool<MySql>, record: &mut OtpCodeRecord) -> Result<bool> {
record.code = crate::commons::random_number(6);
record.expired_at = timestamp_millis() as u64 + ((record.period * 1000) as u64);
let mut query_builder: QueryBuilder<MySql> = QueryBuilder::new("update t_otp_code");
query_builder.push(" set f_expired_at = ");
query_builder.push_bind(record.expired_at);
query_builder.push(", f_code = ");
query_builder.push_bind(record.code);
query_builder.push(", f_updated_at = ");
query_builder.push_bind(timestamp_millis());
query_builder.push(" where f_id = ");
query_builder.push_bind(record.id);
let update_result_int = query_builder
.build()
.execute(pool)
.await
.context("re_new_code: Failed to perform a query to update a otpcode.")?
.rows_affected();
Ok(update_result_int > 0)
}
}