feat: Implement solution for searching by message content and fix bug when closing ticket

feat-scaling-ticket-remote-creation
adriano 2024-04-29 17:27:15 -03:00
parent beb4fd64c4
commit e71150ff75
6 changed files with 203 additions and 105 deletions

View File

@ -0,0 +1,23 @@
const monthsAgo = (months: number) => {
const { subMonths, startOfMonth, format } = require("date-fns");
// Get the current date
const currentDate = new Date();
// Subtract 4 months from the current date
const monthsAgo = subMonths(currentDate, months);
// Get the start of the month for the current date and four months ago
// const startOfCurrentMonth = startOfMonth(currentDate);
const startMonthsAgo = startOfMonth(monthsAgo);
// Format the dates in YYYY-MM-DD format
// const formattedCurrentMonth = format(startOfCurrentMonth, "yyyy-MM-dd");
const formattedMonthsAgo = format(startMonthsAgo, "yyyy-MM-dd");
// console.log("Current Month:", formattedCurrentMonth);
// console.log("Months Ago:", formattedMonthsAgo);
return formattedMonthsAgo;
};
export default monthsAgo;

View File

@ -29,7 +29,7 @@ export async function getSimple(key: string) {
export async function get({ key, value, parse }: getData) { export async function get({ key, value, parse }: getData) {
if (key.includes("*")) { if (key.includes("*")) {
const keys = await redis.keys(key); const keys = await redis.keys(key);
if (keys.length > 0) { if (keys.length > 0) {
if (value) { if (value) {
for (const key of keys) { for (const key of keys) {
@ -70,7 +70,7 @@ export async function clearAllKeys(...keys: string[]) {
// If there are keys, delete them // If there are keys, delete them
if (del_keys.length > 0) { if (del_keys.length > 0) {
console.log("del_keys: ", del_keys); // console.log("del_keys: ", del_keys);
await redis.del(...del_keys); await redis.del(...del_keys);
} }
} }
@ -220,3 +220,5 @@ export async function getHashesWithPattern(
return hashes; return hashes;
} }

View File

@ -6,14 +6,18 @@ import { StartAllWhatsAppsSessions } from "./services/WbotServices/StartAllWhats
import User from "./models/User"; import User from "./models/User";
import Whatsapp from "./models/Whatsapp"; import Whatsapp from "./models/Whatsapp";
import endPointQuery from "./helpers/EndPointQuery"; import endPointQuery from "./helpers/EndPointQuery";
import { cacheSize, flushCache, loadTicketsCache } from "./helpers/TicketCache"; import {
cacheSize,
flushCache,
loadTicketsCache,
} from "./helpers/TicketCache";
import "./helpers/CloseBotTickets"; import "./helpers/CloseBotTickets";
import { loadContactsCache } from "./helpers/ContactsCache"; import { loadContactsCache } from "./helpers/ContactsCache";
import { loadSchedulesCache } from "./helpers/SchedulingNotifyCache"; import { loadSchedulesCache } from "./helpers/SchedulingNotifyCache";
import { delRestoreControllFile } from "./helpers/RestoreControll"; import { delRestoreControllFile } from "./helpers/RestoreControll";
import "./helpers/AutoCloseTickets"; import "./helpers/AutoCloseTickets";
import "./helpers/AutoRemoteTickets" import "./helpers/AutoRemoteTickets";
import "./helpers/SchedulingNotifySendMessage"; import "./helpers/SchedulingNotifySendMessage";
import axios from "axios"; import axios from "axios";
@ -30,6 +34,7 @@ import { json } from "sequelize";
import { setBotInfo } from "./helpers/SetBotInfo"; import { setBotInfo } from "./helpers/SetBotInfo";
import Queue from "./models/Queue"; import Queue from "./models/Queue";
import StatusChatEnd from "./models/StatusChatEnd"; import StatusChatEnd from "./models/StatusChatEnd";
import Message from "./models/Message";
const server = app.listen(process.env.PORT, () => { const server = app.listen(process.env.PORT, () => {
logger.info(`Server started on port: ${process.env.PORT}`); logger.info(`Server started on port: ${process.env.PORT}`);
@ -50,7 +55,21 @@ gracefulShutdown(server);
(async () => { (async () => {
console.log("os.tmpdir(): ", os.tmpdir()); console.log("os.tmpdir(): ", os.tmpdir());
await clearAllKeys("user:*", "whatsapp:*", "queue:*", "statusChatEnd:*"); await clearAllKeys(
"user:*",
"whatsapp:*",
"queue:*",
"statusChatEnd:*",
);
// let messages: any = await Message.findAll({
// attributes: ["id", "ticketId", "body"]
// });
// for (const message of messages) {
// const { id, body, ticketId } = message;
// await set(`message:ticketId:${ticketId}:id:${id}`, body);
// }
const statusChatEnds = await StatusChatEnd.findAll(); const statusChatEnds = await StatusChatEnd.findAll();
@ -90,17 +109,17 @@ gracefulShutdown(server);
try { try {
const { phoneNumberId, id, greetingMessage } = whatsapps[i]; const { phoneNumberId, id, greetingMessage } = whatsapps[i];
if (phoneNumberId) { // if (phoneNumberId) {
// await set( // await set(
// `whatsapp:${whatsapps[i].dataValues.id}`, // `whatsapp:${whatsapps[i].dataValues.id}`,
// JSON.stringify({ // JSON.stringify({
// number: whatsapps[i].dataValues.number, // number: whatsapps[i].dataValues.number,
// id, // id,
// greetingMessage, // greetingMessage,
// phoneNumberId // phoneNumberId
// }) // })
// ); // );
} // }
await set( await set(
`whatsapp:${whatsapps[i].dataValues.id}`, `whatsapp:${whatsapps[i].dataValues.id}`,

View File

@ -15,12 +15,10 @@ const dateToday = splitDateTime(
new Date(format(new Date(), "yyyy-MM-dd HH:mm:ss", { locale: ptBR })) new Date(format(new Date(), "yyyy-MM-dd HH:mm:ss", { locale: ptBR }))
); );
import ListTicketServiceCache from "./ListTicketServiceCache";
import { searchTicketCache, loadTicketsCache } from "../../helpers/TicketCache";
import { getWbot } from "../../libs/wbot"; import { getWbot } from "../../libs/wbot";
import User from "../../models/User"; import User from "../../models/User";
import { get } from "../../helpers/RedisClient"; import { get } from "../../helpers/RedisClient";
import monthsAgo from "../../helpers/MonthsAgo";
interface Request { interface Request {
searchParam?: string; searchParam?: string;
@ -54,7 +52,12 @@ const ListTicketsService = async ({
unlimited = "false", unlimited = "false",
searchParamContent = "" searchParamContent = ""
}: Request): Promise<Response> => { }: Request): Promise<Response> => {
console.log("----------> searchParamContent: ", searchParamContent); console.log(
"----------> searchParamContent: ",
searchParamContent,
" | searchParam: ",
searchParam
);
let whereCondition: Filterable["where"] = { let whereCondition: Filterable["where"] = {
[Op.or]: [{ userId }, { status: "pending" }], [Op.or]: [{ userId }, { status: "pending" }],
@ -67,46 +70,46 @@ const ListTicketsService = async ({
pageNumber = "1"; pageNumber = "1";
} }
if (searchParam && searchParam.trim().length > 0 && process.env.CACHE) { // if (searchParam && searchParam.trim().length > 0 && process.env.CACHE) {
try { // try {
const offset = 40 * (+pageNumber - 1); // const offset = 40 * (+pageNumber - 1);
searchParam = searchParam.replace(/\s+/g, " ").trim().toLowerCase(); // searchParam = searchParam.replace(/\s+/g, " ").trim().toLowerCase();
console.log("QUERY TICKET SEARCH PARAM FROM CACHE: ", searchParam); // console.log("QUERY TICKET SEARCH PARAM FROM CACHE: ", searchParam);
let tickets: any = await searchTicketCache(searchParam, offset, 40); // let tickets: any = await searchTicketCache(searchParam, offset, 40);
if (tickets) { // if (tickets) {
console.log( // console.log(
"QUERY TICKET SEARCH PARAM FROM CACHE LENGTH...: ", // "QUERY TICKET SEARCH PARAM FROM CACHE LENGTH...: ",
tickets.length // tickets.length
); // );
tickets.map((t: any) => { // tickets.map((t: any) => {
t["contact.number"] = t["contact_number"]; // t["contact.number"] = t["contact_number"];
delete t["contact_number"]; // delete t["contact_number"];
return { ...["contact_number"] }; // return { ...["contact_number"] };
}); // });
tickets = tickets.map((e: any) => unflatten(e)); // tickets = tickets.map((e: any) => unflatten(e));
return { // return {
tickets, // tickets,
count: tickets.length, // count: tickets.length,
hasMore: tickets.length > 0 ? true : false // hasMore: tickets.length > 0 ? true : false
}; // };
} // }
} catch (error) { // } catch (error) {
console.log( // console.log(
"There was an error on search ListTicketservice.ts search cache: ", // "There was an error on search ListTicketservice.ts search cache: ",
error // error
); // );
} // }
console.log("QUERY TICKETS FROM DATABASE..."); // console.log("QUERY TICKETS FROM DATABASE...");
} // }
let includeCondition: Includeable[]; let includeCondition: Includeable[];
@ -143,38 +146,32 @@ const ListTicketsService = async ({
if (searchParam) { if (searchParam) {
const sanitizedSearchParam = searchParam.toLocaleLowerCase().trim(); const sanitizedSearchParam = searchParam.toLocaleLowerCase().trim();
const sanitizedSearchParamContent = searchParamContent
.toLocaleLowerCase()
.trim();
if (searchParamContent.length > 0) { includeCondition = [
includeCondition = [ ...includeCondition,
...includeCondition, {
{ model: Message,
model: Message, as: "messages",
as: "messages", attributes: ["id", "body"],
attributes: ["id", "body"], where: {
where: { body: where(
body: where( fn("LOWER", col("body")),
fn("LOWER", col("body")), "LIKE",
"LIKE", `%${sanitizedSearchParam}%`
`%${sanitizedSearchParamContent}%` )
) },
}, required: false,
required: false, duplicating: false
duplicating: false }
} ];
];
whereCondition = { whereCondition = {
...whereCondition, ...whereCondition,
"$message.body$": where( createdAt: {
fn("LOWER", col("body")), [Op.gte]: monthsAgo(4) + " 00:00:00.000000",
"LIKE", [Op.lte]: dateToday.fullDate + " 23:59:59.999999"
`%${sanitizedSearchParamContent}%` }
) };
};
}
whereCondition = { whereCondition = {
...whereCondition, ...whereCondition,
@ -186,11 +183,65 @@ const ListTicketsService = async ({
`%${sanitizedSearchParam}%` `%${sanitizedSearchParam}%`
) )
}, },
{ "$contact.number$": { [Op.like]: `%${sanitizedSearchParam}%` } },
{ "$contact.number$": { [Op.like]: `%${sanitizedSearchParam}%` } } {
"$message.body$": where(
fn("LOWER", col("body")),
"LIKE",
`%${sanitizedSearchParam}%`
)
}
] ]
}; };
// const sanitizedSearchParamContent = searchParamContent
// .toLocaleLowerCase()
// .trim();
// if (searchParamContent.length > 0) {
// includeCondition = [
// ...includeCondition,
// {
// model: Message,
// as: "messages",
// attributes: ["id", "body"],
// where: {
// body: where(
// fn("LOWER", col("body")),
// "LIKE",
// `%${sanitizedSearchParamContent}%`
// )
// },
// required: false,
// duplicating: false
// }
// ];
// whereCondition = {
// ...whereCondition,
// "$message.body$": where(
// fn("LOWER", col("body")),
// "LIKE",
// `%${sanitizedSearchParamContent}%`
// )
// };
// }
// whereCondition = {
// ...whereCondition,
// [Op.or]: [
// {
// "$contact.name$": where(
// fn("LOWER", col("contact.name")),
// "LIKE",
// `%${sanitizedSearchParam}%`
// )
// },
// { "$contact.number$": { [Op.like]: `%${sanitizedSearchParam}%` } }
// ]
// };
const userProfile: any = await User.findByPk(userId); const userProfile: any = await User.findByPk(userId);
if ( if (
@ -243,8 +294,8 @@ const ListTicketsService = async ({
const hasMore = count > offset + tickets.length; const hasMore = count > offset + tickets.length;
const ticketIds = await get({ key: `remote:controll`, parse: true }); const ticketIds = await get({ key: `remote:controll`, parse: true });
return { return {
tickets, tickets,
count, count,

View File

@ -75,16 +75,19 @@ const UpdateTicketService = async ({
del(`remote:ticketId:${ticket.id}`); del(`remote:ticketId:${ticket.id}`);
let ticketsIds = await get({ key: `remote:controll`, parse: true }); let ticketsIds = await get({ key: `remote:controll`, parse: true });
const index = ticketsIds.findIndex((t: any) => t == ticket.id);
console.log("ticketsIds 1: ", ticketsIds); if (ticketsIds) {
const index = ticketsIds.findIndex((t: any) => t == ticket.id);
if (index != -1) { console.log("ticketsIds 1: ", ticketsIds);
ticketsIds.splice(index, 1);
console.log("ticketsIds 2: ", ticketsIds); if (index != -1) {
ticketsIds.splice(index, 1);
set(`remote:controll`, JSON.stringify(ticketsIds)); console.log("ticketsIds 2: ", ticketsIds);
set(`remote:controll`, JSON.stringify(ticketsIds));
}
} }
} }

View File

@ -346,7 +346,7 @@ const TicketsManager = () => {
{/* <IconButton onClick={() => setShowContentSearch(prev => !prev)}> {/* <IconButton onClick={() => setShowContentSearch(prev => !prev)}>
<MenuIcon className={classes.menuSearch} /> <MenuIcon className={classes.menuSearch} />
</IconButton> */} </IconButton> */}
<Tooltip {/* <Tooltip
open={openTooltipSearch} open={openTooltipSearch}
onOpen={() => handleOpenTooltipSearch()} onOpen={() => handleOpenTooltipSearch()}
onClose={() => handleCloseTooltipSearch()} onClose={() => handleCloseTooltipSearch()}
@ -360,22 +360,22 @@ const TicketsManager = () => {
<MenuIcon className={classes.menuSearch} /> <MenuIcon className={classes.menuSearch} />
</IconButton> </IconButton>
</span> </span>
</Tooltip> </Tooltip> */}
</div> </div>
{ {
// showContentSearch ? // showContentSearch ?
(showContentSearch && searchParam.searchParam.length >= 4) ? // (showContentSearch /*&& searchParam.searchParam.length >= 4*/) ?
(<div className={classes.searchContentInput}> // (<div className={classes.searchContentInput}>
<FindInPageIcon className={classes.searchIcon} /> // <FindInPageIcon className={classes.searchIcon} />
<InputBase // <InputBase
className={classes.searchInput} // className={classes.searchInput}
inputRef={searchContentInputRef} // inputRef={searchContentInputRef}
placeholder={i18n.t("Busca por conteúdo")} // placeholder={i18n.t("Busca por conteúdo")}
type="search" // type="search"
value={inputContentSearch} // value={inputContentSearch}
onChange={(e) => handleContentSearch(e)} // onChange={(e) => handleContentSearch(e)}
/> // />
</div>) : null // </div>) : null
} }
</div> </div>
) : ( ) : (