use bigdecimal::BigDecimal; use chrono::DateTime; use sqlx::Row; use sqlx::mysql::MySqlPool; use sqlx::mysql::MySqlPoolOptions; use dotenvy::dotenv_override; use std::collections::HashMap; use std::env; // use crate::schema::equipment::dsl::*; // use crate::schema::worker::dsl::*; use crate::models::*; use anyhow::anyhow; pub struct DBOperator { pool: MySqlPool, } impl DBOperator { pub async fn new() -> Self { dotenv_override().ok(); let db_url = env::var("DATABASE_URL").expect("DATABASE_URL установи!"); Self { pool: MySqlPool::connect(&db_url).await.unwrap(), } } pub async fn get_position(&self) -> Result, sqlx::Error> { let rets: Vec = sqlx::query_as::<_, Position>("SELECT * FROM `position`") .fetch_all(&self.pool) .await?; Ok(rets) } pub async fn get_workers(&self) -> Result, sqlx::Error> { let pre_rets: Vec = sqlx::query_as::<_, WorkerRow>("SELECT * FROM `worker`") .fetch_all(&self.pool) .await?; let mut rets: Vec = Vec::new(); let pos = self.get_position().await.unwrap(); for worker in pre_rets { let mut ppos = Position::default(); for position in pos.clone() { if position.id == worker.position_id { ppos = position.clone(); break; } } rets.push(Worker { id: worker.id, full_name: worker.full_name, hire_date: worker.hire_date, position: ppos, is_fired: worker.is_fired, }); } Ok(rets) } pub async fn get_equipment(&self) -> Result, sqlx::Error> { let pre_rets = sqlx::query_as::<_, EquipmentRow>("SELECT * FROM `equipment`") .fetch_all(&self.pool) .await?; let mut rets: Vec = Vec::new(); let workers = self.get_workers().await?; for eq in pre_rets { let mut pworker = Worker::default(); for worker in workers.clone() { if worker.id == eq.worker_id { pworker = worker.clone(); break; } } rets.push(Equipment { id: eq.id, name: eq.name, inv_number: eq.inv_number, maintenance_date: eq.maintenance_date, worker: pworker, is_written_off: eq.is_written_off, }); } Ok(rets) } pub async fn get_mcat(&self) -> Result, sqlx::Error> { let rets = sqlx::query_as::<_, MaterialCategory>("SELECT * FROM `material_category`") .fetch_all(&self.pool) .await?; Ok(rets) } pub async fn get_materials(&self) -> Result, sqlx::Error> { let mats = sqlx::query_as::<_, MaterialRow>("SELECT * FROM `material`") .fetch_all(&self.pool) .await?; let cats = self.get_mcat().await?; let cat_map: HashMap<_, MaterialCategory> = cats.into_iter().map(|cat| (cat.id, cat)).collect(); let mut rets = Vec::with_capacity(mats.len()); for mat in mats { let cat = cat_map .get(&mat.category_id) .expect("Никто не знает как, но БД не смогла связать материал с его категорией. Заставьте разраба это починить.") .clone(); rets.push(Material { id: mat.id, name: mat.name, quantity: mat.quantity, category: cat, }); } Ok(rets) } pub async fn check_worker(&self, worker: Worker) -> Result { let ret = sqlx::query(&format!( "SELECT * FROM `worker` WHERE full_name = {}, position_id = {}, hire_date = {}", worker.full_name, worker.position.id, worker.hire_date.to_string() )) .fetch_all(&self.pool) .await?; if ret.len() > 0 { return Ok(true); } Ok(false) } pub async fn add_worker(&self, worker: WorkerRow) -> Result { // sqlx::query!("INSERT INTO Brewery.worker (id, position_id, hire_date, is_fired, full_name) VALUES(0, ?, ?, ?, ?);",worker.position.id, worker.hire_date ,worker.is_fired,worker.full_name) match sqlx::query("INSERT INTO Brewery.worker (id, position_id, hire_date, is_fired, full_name) VALUES(0, ?, ?, ?, ?);") .bind(worker.position_id) .bind(worker.hire_date) .bind(worker.is_fired) .bind(worker.full_name) .execute(&self.pool) .await { Ok(_) =>{ return Ok(true) }, Err(_) =>{ Ok(false) } } } pub async fn add_position(&self, position: Position) -> Result { match sqlx::query("INSERT INTO Brewery.`position` (id, name, wage) VALUES(0, ?, ?);") .bind(position.name) .bind(position.wage) .execute(&self.pool) .await { Ok(_) => Ok(true), Err(_) => Ok(false), } } pub async fn add_material_category( &self, mat_cat: MaterialCategory, ) -> Result { match sqlx::query("INSERT INTO Brewery.`material_category` (id, name) VALUES (0, ?)") .bind(mat_cat.name) .execute(&self.pool) .await { Ok(_) => Ok(true), Err(_) => Ok(false), } } pub async fn add_material(&self, mat: MaterialRow) -> Result { match sqlx::query( "INSERT INTO Brewery.material (id, name, quantity, category_id) VALUES(0, ?, ?, ?);", ) .bind(mat.name) .bind(mat.quantity) .bind(mat.category_id) .execute(&self.pool) .await { Ok(_) => Ok(true), Err(_) => Ok(false), } } pub async fn delete_position(&self, position: Position) -> Result { match sqlx::query("DELETE FROM Brewery.`position` WHERE id = ?;") .bind(position.id) .execute(&self.pool) .await { Ok(_) => Ok(true), Err(_) => Ok(false), } } pub async fn delete_material_category( &self, mat_cat: MaterialCategory, ) -> Result { match sqlx::query("DELETE FROM Brewery.`material_category` WHERE id = ?;") .bind(mat_cat.id) .execute(&self.pool) .await { Ok(_) => Ok(true), Err(_) => Ok(false), } } pub async fn delete_material(&self, mat: MaterialRow) -> Result { match sqlx::query("DELETE FROM Brewery.`material` WHERE id = ?;") .bind(mat.id) .execute(&self.pool) .await { Ok(_) => Ok(true), Err(_) => Ok(false), } } pub async fn update_worker(&self, worker: WorkerRow) -> Result { match sqlx::query("UPDATE Brewery.worker SET position_id=?, hire_date=?, is_fired=?, full_name=? WHERE id=?;") .bind(worker.position_id) .bind(worker.hire_date) .bind(worker.is_fired) .bind(worker.full_name) .bind(worker.id) .execute(&self.pool) .await{ Ok(_) =>{ Ok(true) }, Err(_)=>{ Ok(false) } } } pub async fn update_material_category( &self, mat_cat: MaterialCategory, ) -> Result { match sqlx::query("UPDATE Brewery.`material_category` SET name=? WHERE id=?;") .bind(mat_cat.name) .bind(mat_cat.id) .execute(&self.pool) .await { Ok(_) => Ok(true), Err(_) => Ok(false), } } pub async fn update_material(&self, mat: MaterialRow) -> Result { match sqlx::query( "UPDATE Brewery.material SET name=?, quantity=?, category_id=? WHERE id=?;", ) .bind(mat.name) .bind(mat.quantity) .bind(mat.category_id) .bind(mat.id) .execute(&self.pool) .await { Ok(_) => Ok(true), Err(_) => Ok(false), } } pub async fn get_worker_by_id(&self, worker_id: i32) -> Result { let preret = sqlx::query_as::<_, WorkerRow>("SELECT * FROM `worker` WHERE id = ?;") .bind(worker_id) .fetch_all(&self.pool) .await?; let poses = self.get_position().await?; let ret = Ok(Worker { id: preret[0].id, full_name: preret[0].full_name.clone(), hire_date: preret[0].hire_date, is_fired: preret[0].is_fired, position: poses .iter() .find(|&pos| &pos.id == &preret[0].position_id) .unwrap() .clone(), }); ret } pub async fn update_position(&self, pos: Position) -> Result { match sqlx::query("UPDATE Brewery.position SET name=?, wage=? WHERE id=?") .bind(pos.name) .bind(pos.wage) .bind(pos.id) .execute(&self.pool) .await { Ok(_) => Ok(true), Err(_) => Ok(false), } } pub async fn check_password( &self, username: String, password_hash: String, ) -> Result { let ans = sqlx::query("SELECT * FROM Brewery.profile WHERE login=? AND password=?") .bind(username) .bind(password_hash) .fetch_all(&self.pool) .await; match ans { Ok(n) => { if n.len() > 0 { return Ok(true); } else { return Ok(false); } } Err(_) => Ok(false), } } pub async fn check_admin(&self, username: String) -> Result { let ans: bool = sqlx::query_scalar( r#"SELECT position.is_admin, profile.worker_id, worker.id, profile.login, position.id FROM Brewery.profile JOIN Brewery.worker JOIN Brewery.position ON profile.worker_id = worker.id AND profile.login=? AND worker.position_id =position.id"#, ) .bind(username) .fetch_one(&self.pool) .await .unwrap(); Ok(ans) } pub async fn write_off_equipment(&self, equipment_id: i32) -> Result { match sqlx::query("UPDATE `equipment` SET is_written_off=1 WHERE id=?") .bind(equipment_id) .execute(&self.pool) .await { Ok(_) => Ok(true), Err(_) => Ok(false), } } pub async fn get_salaries(&self) -> Result, sqlx::Error> { let sals = sqlx::query_as::<_, SalaryRow>("SELECT * FROM `salary`") .fetch_all(&self.pool) .await?; let mut rets: Vec = Vec::new(); let workers = self.get_workers().await.unwrap(); for sal in sals { match workers.iter().find(|w| w.id == sal.worker_id) { Some(w) => rets.push(Salary { id: sal.id, worker: w.clone(), salary_date: sal.salary_date, salary_size: sal.salary_size, comment: sal.comment, }), None => {} } } Ok(rets) } pub async fn add_salary( &self, worker_id: i32, salary_date: DateTime, salary_size: BigDecimal, comment: Option, ) -> Result { match sqlx::query("INSERT INTO Brewery.salary (id, worker_id, salary_size, salary_date, comment) VALUES(0, ?, ?, ?, ?);") .bind(worker_id) .bind(salary_size) .bind(salary_date) .bind(comment.unwrap_or(String::new())) .execute(&self.pool) .await{ Ok(_) => Ok(true), Err(_) => Ok(false) } } pub async fn update_salary(&self, salary: Salary) -> Result { match sqlx::query("UPDATE Brewery.salary SET worker_id=?, salary_size=?, salary_date=?, comment=? WHERE id=?;") .bind(salary.worker.id) .bind(salary.salary_size) .bind(salary.salary_date) .bind(salary.comment) .bind(salary.id) .execute(&self.pool) .await{ Ok(_) => Ok(true), Err(_) => Ok(false) } } pub async fn remove_salary(&self, id: i32) -> Result { match sqlx::query("DELETE FROM Brewery.salary WHERE id=?;") .bind(id) .execute(&self.pool) .await { Ok(_) => Ok(true), Err(_) => Ok(false), } } pub async fn get_recipes(&self) -> Result, sqlx::Error> { let pre_ret = sqlx::query_as::<_, RecipeRow>("SELECT * FROM recipe") .fetch_all(&self.pool) .await .unwrap_or(Vec::new()); let ret: Vec = Vec::new(); let elements = self.get_recipe_elements().await.unwrap_or(Vec::new()); for pr in pre_ret.iter() {} Ok(ret) } pub async fn get_recipe_elements(&self) -> Result, sqlx::Error> { let pre_ret = sqlx::query_as::<_, RecipeElementRow>("SELECT id, recipe_id, material_id, equipment_id, quantity FROM Brewery.recipe_element;").fetch_all(&self.pool).await; let mats = self.get_materials().await.unwrap(); let eqs = self.get_equipment().await.unwrap(); let ret: Vec = Vec::new(); for re in pre_ret.unwrap().iter() { let mat = mats.iter().find(|m| m.id == re.material_id); let eq = eqs.iter().find(|e| e.id == re.equipment_id); match (mat, eq) { (Some(m), Some(e)) => { ret.push(RecipeElement { id: re.id, material: m.clone(), equipment: e.clone(), quantity: re.quantity, }); } (_, _) => {} } } Ok(ret) } } pub async fn estabilish_connection() -> Result<(), sqlx::Error> { dotenv_override().ok(); let db_url = env::var("DATABASE_URL").expect("DATABASE_URL установи!"); let _pool = MySqlPoolOptions::new() .max_connections(5) .connect(&db_url) .await?; Ok(()) } macro_rules! add_get { ($fn_name:ident, $table:ident) => { pub async fn $fn_name(&self, $id: i32) -> Result { match sqlx::query("UPDATE `$table` SET WHERE id = ?") .bind($id) .execute(&self.pool) .await { Ok(_) => Ok(true), Err(e) => Err(e), } } }; }