BreweryControlSystem/code/src/database.rs

612 lines
20 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::collections::HashSet;
use std::env;
// use crate::schema::equipment::dsl::*;
// use crate::schema::worker::dsl::*;
use crate::models::*;
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_position_by_id(&self, id: i32) -> Result<Position, sqlx::Error> {
sqlx::query_as::<_, Position>("SELECT * FROM `position` WHERE id = ?")
.bind(id)
.fetch_one(&self.pool)
.await
}
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,
fire_date: worker.fire_date,
});
}
Ok(rets)
}
pub async fn get_worker_by_id(&self, id: i32) -> Result<Worker, sqlx::Error> {
match sqlx::query_as::<_, WorkerRow>("SELECT * FROM `worker` WHERE id = ?")
.bind(id)
.fetch_one(&self.pool)
.await
{
Ok(n) => Ok(Worker {
id: n.id,
full_name: n.full_name,
position: self
.get_position_by_id(n.position_id)
.await
.unwrap_or(Position::default()),
is_fired: n.is_fired,
hire_date: n.hire_date,
fire_date: n.fire_date,
}),
Err(e) => Err(e),
}
}
pub async fn get_equipment(
&self,
start: i32,
count: i32,
) -> Result<Vec<Equipment>, sqlx::Error> {
let pre_rets =
sqlx::query_as::<_, EquipmentRow>("SELECT * FROM `equipment` LIMIT ? OFFSET ?")
.bind(count)
.bind(start)
.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_equipment_by_id(&self, id: i32) -> Result<Equipment, sqlx::Error> {
match sqlx::query_as::<_, EquipmentRow>("SELECT * FROM `equipment` WHERE id = ?")
.bind(id)
.fetch_one(&self.pool)
.await
{
Ok(n) => Ok(Equipment {
id,
name: n.name,
inv_number: n.inv_number,
maintenance_date: n.maintenance_date,
worker: self
.get_worker_by_id(n.worker_id)
.await
.unwrap_or(Worker::default()),
is_written_off: n.is_written_off,
}),
Err(e) => Err(e),
}
}
pub async fn add_equipment(&self, equipment: Equipment) -> Result<bool, sqlx::Error> {
match sqlx::query("INSERT INTO `equipment` (id, inv_number, maintenance_date, worker_id, name, is_written_off) VALUES(0, ?, ?, ?, ?, 0);")
.bind(equipment.inv_number)
.bind(equipment.maintenance_date)
.bind(equipment.worker.id)
.bind(equipment.name)
.execute(&self.pool)
.await{
Ok(_) => Ok(true),
Err(_) => Ok(false)
}
}
pub async fn update_equipment(&self, equipment: Equipment) -> Result<bool, sqlx::Error> {
match sqlx::query("UPDATE `equipment` SET inv_number=?, maintenance_date=?, worker_id=?, name=?, is_written_off=? WHERE id=?;")
.bind(equipment.inv_number)
.bind(equipment.maintenance_date)
.bind(equipment.worker.id)
.bind(equipment.name)
.bind(equipment.is_written_off)
.bind(equipment.id)
.execute(&self.pool)
.await{
Ok(_) => Ok(true),
Err(_) => Ok(false)
}
}
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 promote_worker(
&self,
worker: Worker,
position_to: Position,
date: DateTime<chrono::Local>,
) -> Result<(), sqlx::Error> {
let mut tx = self.pool.begin().await?;
sqlx::query("INSERT INTO promotion (worker_id, from_position_id, to_position_id, `date`) VALUES(?, ?, ?, ?);").bind(worker.id).bind(worker.position.id).bind(position_to.id).bind(date).execute(&mut *tx).await?;
sqlx::query("UPDATE worker SET position_id=? WHERE id=?;")
.bind(position_to.id)
.bind(worker.id)
.execute(&mut *tx)
.await?;
tx.commit().await
}
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 `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 `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 `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 `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 `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 `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 `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 `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 `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 update_position(&self, pos: Position) -> Result<bool, sqlx::Error> {
match sqlx::query("UPDATE `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 `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
`profile`
JOIN
`worker`
JOIN
`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 `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 `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 `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 mut ret: Vec<Recipe> = Vec::new();
let elements = self.get_recipe_elements().await.unwrap_or(Vec::new());
for pr in pre_ret.iter() {
let mut els: Vec<RecipeElement> = Vec::new();
for elem in elements.clone() {
if elem.recipe_id == pr.id {
els.push(elem.clone());
}
}
ret.push(Recipe {
id: pr.id,
name: pr.name.clone(),
elements: els.into_iter().collect(),
});
}
Ok(ret)
}
pub async fn add_recipe(&self, rec: Recipe) -> Result<u64, sqlx::Error> {
let mut tx = self.pool.begin().await?;
sqlx::query("INSERT INTO `recipe` (id,name) VALUES (?,?)")
.bind(rec.id)
.bind(rec.name)
.execute(&mut *tx)
.await?;
let ret = sqlx::query("SELECT LAST_INSERT_ID();")
.fetch_one(&mut *tx)
.await?;
match tx.commit().await {
Ok(_) => Ok(ret.get(0)),
Err(_) => Ok(0),
}
}
pub async fn remove_recipe(&self, rec_id: i32) -> Result<bool, sqlx::Error> {
match sqlx::query("DELETE FROM `recipe` WHERE id=?")
.bind(rec_id)
.execute(&self.pool)
.await
{
Ok(_) => Ok(true),
Err(_) => Ok(false),
}
}
pub async fn update_recipe(&self, recipe: Recipe) -> Result<bool, sqlx::Error> {
match sqlx::query("UPDATE `recipe` SET name=? WHERE id=?;")
.bind(recipe.name)
.bind(recipe.id)
.execute(&self.pool)
.await
{
Ok(_) => Ok(true),
Err(_) => Ok(false),
}
}
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 `recipe_element`;",
)
.fetch_all(&self.pool)
.await;
let mats = self.get_materials().await.unwrap();
let mut 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 = self.get_equipment_by_id(re.equipment_id).await.ok();
match (mat, eq) {
(Some(m), Some(e)) => {
ret.push(RecipeElement {
id: re.id,
material: m.clone(),
equipment: e.clone(),
quantity: re.quantity,
recipe_id: re.recipe_id,
});
}
(_, _) => {}
}
}
Ok(ret)
}
pub async fn add_recipe_elems(
&self,
elems: HashSet<RecipeElement>,
rec_id: i32,
) -> Result<bool, sqlx::Error> {
let mut tx = self.pool.begin().await?;
for el in elems.iter() {
sqlx::query("INSERT INTO `recipe_element` (id, recipe_id, material_id, equipment_id, quantity) VALUES(0, ?, ?, ?, ?);")
.bind(rec_id)
.bind(el.material.id)
.bind(el.equipment.id)
.bind(el.quantity)
.execute(&mut *tx)
.await?;
}
match tx.commit().await {
Ok(_) => Ok(true),
Err(e) => Err(e),
}
}
pub async fn remove_recipe_elements(&self, rec_id: i32) -> Result<bool, sqlx::Error> {
match sqlx::query("DELETE FROM `recipe_element` WHERE recipe_id=?")
.bind(rec_id)
.execute(&self.pool)
.await
{
Ok(_) => Ok(true),
Err(_) => Ok(false),
}
}
}
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),
}
}
};
}