Add PRODUCTS, PRODUCT, SEARCH PRODUCTS keywords for ERP integration
This commit is contained in:
parent
1c4cc2f986
commit
ee9341163f
3 changed files with 283 additions and 0 deletions
|
|
@ -26,6 +26,7 @@ pub mod face_api;
|
|||
pub mod file_operations;
|
||||
pub mod find;
|
||||
pub mod first;
|
||||
pub mod products;
|
||||
pub mod search;
|
||||
pub mod for_next;
|
||||
pub mod format;
|
||||
|
|
@ -125,6 +126,9 @@ pub fn get_all_keywords() -> Vec<String> {
|
|||
"FIND".to_string(),
|
||||
"FIRST".to_string(),
|
||||
"SEARCH".to_string(),
|
||||
"SEARCH PRODUCTS".to_string(),
|
||||
"PRODUCTS".to_string(),
|
||||
"PRODUCT".to_string(),
|
||||
"AUTOCOMPLETE".to_string(),
|
||||
"GROUP BY".to_string(),
|
||||
"INSERT".to_string(),
|
||||
|
|
|
|||
277
src/basic/keywords/products.rs
Normal file
277
src/basic/keywords/products.rs
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
use crate::bot::get_default_bot;
|
||||
use crate::core::shared::schema::products;
|
||||
use crate::shared::models::UserSession;
|
||||
use crate::shared::state::AppState;
|
||||
use crate::shared::utils;
|
||||
use diesel::prelude::*;
|
||||
use diesel::sql_types::{Integer, Text};
|
||||
use log::{error, trace};
|
||||
use rhai::{Dynamic, Engine};
|
||||
use serde_json::{json, Value};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(QueryableByName)]
|
||||
struct JsonRow {
|
||||
#[diesel(sql_type = Text)]
|
||||
row_data: String,
|
||||
}
|
||||
|
||||
pub fn products_keyword(state: &AppState, _user: UserSession, engine: &mut Engine) {
|
||||
let connection = state.conn.clone();
|
||||
|
||||
engine.register_fn("PRODUCTS", {
|
||||
let conn = connection.clone();
|
||||
move || -> Dynamic {
|
||||
let mut binding = match conn.get() {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
error!("PRODUCTS db error: {e}");
|
||||
return Dynamic::from(rhai::Array::new());
|
||||
}
|
||||
};
|
||||
|
||||
match get_all_products(&mut binding) {
|
||||
Ok(products) => utils::json_value_to_dynamic(&products),
|
||||
Err(e) => {
|
||||
error!("PRODUCTS error: {e}");
|
||||
Dynamic::from(rhai::Array::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
engine.register_fn("products", {
|
||||
let conn = connection.clone();
|
||||
move || -> Dynamic {
|
||||
let mut binding = match conn.get() {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
error!("products db error: {e}");
|
||||
return Dynamic::from(rhai::Array::new());
|
||||
}
|
||||
};
|
||||
|
||||
match get_all_products(&mut binding) {
|
||||
Ok(products) => utils::json_value_to_dynamic(&products),
|
||||
Err(e) => {
|
||||
error!("products error: {e}");
|
||||
Dynamic::from(rhai::Array::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
engine.register_fn("PRODUCT", {
|
||||
let conn = connection.clone();
|
||||
move |id: i64| -> Dynamic {
|
||||
let mut binding = match conn.get() {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
error!("PRODUCT db error: {e}");
|
||||
return Dynamic::UNIT;
|
||||
}
|
||||
};
|
||||
|
||||
match get_product_by_id(&mut binding, id) {
|
||||
Ok(Some(product)) => utils::json_value_to_dynamic(&product),
|
||||
Ok(None) => Dynamic::UNIT,
|
||||
Err(e) => {
|
||||
error!("PRODUCT error: {e}");
|
||||
Dynamic::UNIT
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
engine.register_fn("product", {
|
||||
let conn = connection.clone();
|
||||
move |id: i64| -> Dynamic {
|
||||
let mut binding = match conn.get() {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
error!("product db error: {e}");
|
||||
return Dynamic::UNIT;
|
||||
}
|
||||
};
|
||||
|
||||
match get_product_by_id(&mut binding, id) {
|
||||
Ok(Some(product)) => utils::json_value_to_dynamic(&product),
|
||||
Ok(None) => Dynamic::UNIT,
|
||||
Err(e) => {
|
||||
error!("product error: {e}");
|
||||
Dynamic::UNIT
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
engine
|
||||
.register_custom_syntax(
|
||||
["SEARCH", "PRODUCTS", "$expr$", ",", "$expr$"],
|
||||
false,
|
||||
{
|
||||
let conn = connection.clone();
|
||||
move |context, inputs| {
|
||||
let query = context.eval_expression_tree(&inputs[0])?;
|
||||
let limit = context.eval_expression_tree(&inputs[1])?;
|
||||
|
||||
let mut binding = conn.get().map_err(|e| format!("DB error: {e}"))?;
|
||||
let query_str = query.to_string();
|
||||
let limit_val = limit.as_int().unwrap_or(10) as i32;
|
||||
|
||||
let result = search_products(&mut binding, &query_str, limit_val)
|
||||
.map_err(|e| format!("Search error: {e}"))?;
|
||||
|
||||
Ok(utils::json_value_to_dynamic(&result))
|
||||
}
|
||||
},
|
||||
)
|
||||
.expect("valid syntax");
|
||||
|
||||
engine
|
||||
.register_custom_syntax(["SEARCH", "PRODUCTS", "$expr$"], false, {
|
||||
let conn = connection.clone();
|
||||
move |context, inputs| {
|
||||
let query = context.eval_expression_tree(&inputs[0])?;
|
||||
|
||||
let mut binding = conn.get().map_err(|e| format!("DB error: {e}"))?;
|
||||
let query_str = query.to_string();
|
||||
|
||||
let result = search_products(&mut binding, &query_str, 10)
|
||||
.map_err(|e| format!("Search error: {e}"))?;
|
||||
|
||||
Ok(utils::json_value_to_dynamic(&result))
|
||||
}
|
||||
})
|
||||
.expect("valid syntax");
|
||||
|
||||
engine.register_fn("SEARCH_PRODUCTS", {
|
||||
let conn = connection.clone();
|
||||
move |query: String, limit: i64| -> Dynamic {
|
||||
let mut binding = match conn.get() {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
error!("SEARCH_PRODUCTS db error: {e}");
|
||||
return Dynamic::from(rhai::Array::new());
|
||||
}
|
||||
};
|
||||
|
||||
match search_products(&mut binding, &query, limit as i32) {
|
||||
Ok(products) => utils::json_value_to_dynamic(&products),
|
||||
Err(e) => {
|
||||
error!("SEARCH_PRODUCTS error: {e}");
|
||||
Dynamic::from(rhai::Array::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
engine.register_fn("search_products", {
|
||||
let conn = connection.clone();
|
||||
move |query: String, limit: i64| -> Dynamic {
|
||||
let mut binding = match conn.get() {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
error!("search_products db error: {e}");
|
||||
return Dynamic::from(rhai::Array::new());
|
||||
}
|
||||
};
|
||||
|
||||
match search_products(&mut binding, &query, limit as i32) {
|
||||
Ok(products) => utils::json_value_to_dynamic(&products),
|
||||
Err(e) => {
|
||||
error!("search_products error: {e}");
|
||||
Dynamic::from(rhai::Array::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn get_all_products(conn: &mut diesel::PgConnection) -> Result<Value, String> {
|
||||
trace!("get_all_products");
|
||||
|
||||
let (bot_id, _) = get_default_bot(conn);
|
||||
|
||||
let query = r#"
|
||||
SELECT row_to_json(p)::text as row_data
|
||||
FROM products p
|
||||
WHERE p.bot_id = $1 AND p.is_active = true
|
||||
ORDER BY p.name
|
||||
LIMIT 100
|
||||
"#;
|
||||
|
||||
let results: Vec<JsonRow> = diesel::sql_query(query)
|
||||
.bind::<diesel::sql_types::Uuid, _>(bot_id)
|
||||
.load(conn)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let products: Vec<Value> = results
|
||||
.into_iter()
|
||||
.filter_map(|row| serde_json::from_str(&row.row_data).ok())
|
||||
.collect();
|
||||
|
||||
Ok(json!(products))
|
||||
}
|
||||
|
||||
fn get_product_by_id(conn: &mut diesel::PgConnection, id: i64) -> Result<Option<Value>, String> {
|
||||
trace!("get_product_by_id: {id}");
|
||||
|
||||
let query = r#"
|
||||
SELECT row_to_json(p)::text as row_data
|
||||
FROM products p
|
||||
WHERE p.id = $1
|
||||
LIMIT 1
|
||||
"#;
|
||||
|
||||
let uuid = uuid::Uuid::from_u64_pair(0, id as u64);
|
||||
|
||||
let results: Vec<JsonRow> = diesel::sql_query(query)
|
||||
.bind::<diesel::sql_types::Uuid, _>(uuid)
|
||||
.load(conn)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(results
|
||||
.into_iter()
|
||||
.next()
|
||||
.and_then(|row| serde_json::from_str(&row.row_data).ok()))
|
||||
}
|
||||
|
||||
fn search_products(conn: &mut diesel::PgConnection, query: &str, limit: i32) -> Result<Value, String> {
|
||||
trace!("search_products: query={query}, limit={limit}");
|
||||
|
||||
let (bot_id, _) = get_default_bot(conn);
|
||||
let safe_query = query.replace('\'', "''");
|
||||
|
||||
let sql = r#"
|
||||
SELECT row_to_json(p)::text as row_data
|
||||
FROM products p
|
||||
WHERE p.bot_id = $1
|
||||
AND p.is_active = true
|
||||
AND (
|
||||
p.name ILIKE '%' || $2 || '%'
|
||||
OR p.description ILIKE '%' || $2 || '%'
|
||||
OR p.sku ILIKE '%' || $2 || '%'
|
||||
OR p.brand ILIKE '%' || $2 || '%'
|
||||
OR p.category ILIKE '%' || $2 || '%'
|
||||
)
|
||||
ORDER BY
|
||||
CASE WHEN p.name ILIKE $2 || '%' THEN 0 ELSE 1 END,
|
||||
p.name
|
||||
LIMIT $3
|
||||
"#;
|
||||
|
||||
let results: Vec<JsonRow> = diesel::sql_query(sql)
|
||||
.bind::<diesel::sql_types::Uuid, _>(bot_id)
|
||||
.bind::<Text, _>(&safe_query)
|
||||
.bind::<Integer, _>(limit)
|
||||
.load(conn)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let products: Vec<Value> = results
|
||||
.into_iter()
|
||||
.filter_map(|row| serde_json::from_str(&row.row_data).ok())
|
||||
.collect();
|
||||
|
||||
Ok(json!(products))
|
||||
}
|
||||
|
|
@ -35,6 +35,7 @@ use self::keywords::data_operations::register_data_operations;
|
|||
use self::keywords::file_operations::register_file_operations;
|
||||
use self::keywords::find::find_keyword;
|
||||
use self::keywords::search::search_keyword;
|
||||
use self::keywords::products::products_keyword;
|
||||
use self::keywords::first::first_keyword;
|
||||
use self::keywords::for_next::for_keyword;
|
||||
use self::keywords::format::format_keyword;
|
||||
|
|
@ -88,6 +89,7 @@ impl ScriptService {
|
|||
create_site_keyword(&state, user.clone(), &mut engine);
|
||||
find_keyword(&state, user.clone(), &mut engine);
|
||||
search_keyword(&state, user.clone(), &mut engine);
|
||||
products_keyword(&state, user.clone(), &mut engine);
|
||||
for_keyword(&state, user.clone(), &mut engine);
|
||||
let _ = register_use_kb_keyword(&mut engine, state.clone(), Arc::new(user.clone()));
|
||||
let _ = register_clear_kb_keyword(&mut engine, state.clone(), Arc::new(user.clone()));
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue