BreweryControlSystem/code/src/database.rs

468 lines
16 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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<Vec<Position>, sqlx::Error> {
let rets: Vec<Position> = sqlx::query_as::<_, Position>("SELECT * FROM `position`")
.fetch_all(&self.pool)
.await?;
Ok(rets)
}
pub async fn get_workers(&self) -> Result<Vec<Worker>, sqlx::Error> {
let pre_rets: Vec<WorkerRow> = sqlx::query_as::<_, WorkerRow>("SELECT * FROM `worker`")
.fetch_all(&self.pool)
.await?;
let mut rets: Vec<Worker> = 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<Vec<Equipment>, sqlx::Error> {
let pre_rets = sqlx::query_as::<_, EquipmentRow>("SELECT * FROM `equipment`")
.fetch_all(&self.pool)
.await?;
let mut rets: Vec<Equipment> = 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<Vec<MaterialCategory>, 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<Vec<Material>, 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<bool, sqlx::Error> {
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<bool, sqlx::Error> {
// 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<bool, sqlx::Error> {
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<bool, sqlx::Error> {
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<bool, sqlx::Error> {
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<bool, sqlx::Error> {
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<bool, sqlx::Error> {
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<bool, sqlx::Error> {
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<bool, sqlx::Error> {
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<bool, sqlx::Error> {
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<bool, sqlx::Error> {
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<Worker, sqlx::Error> {
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<bool, sqlx::Error> {
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<bool, sqlx::Error> {
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<bool, sqlx::Error> {
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<bool, sqlx::Error> {
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<Vec<Salary>, sqlx::Error> {
let sals = sqlx::query_as::<_, SalaryRow>("SELECT * FROM `salary`")
.fetch_all(&self.pool)
.await?;
let mut rets: Vec<Salary> = 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<chrono::Local>,
salary_size: BigDecimal,
comment: Option<String>,
) -> Result<bool, sqlx::Error> {
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<bool, sqlx::Error> {
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<bool, sqlx::Error> {
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<Vec<Recipe>, sqlx::Error> {
let pre_ret = sqlx::query_as::<_, RecipeRow>("SELECT * FROM recipe")
.fetch_all(&self.pool)
.await
.unwrap_or(Vec::new());
let ret: Vec<Recipe> = 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<Vec<RecipeElement>, 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<RecipeElement> = 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<bool, sqlx::Error> {
match sqlx::query("UPDATE `$table` SET WHERE id = ?")
.bind($id)
.execute(&self.pool)
.await
{
Ok(_) => Ok(true),
Err(e) => Err(e),
}
}
};
}