162 lines
5.3 KiB
JavaScript
162 lines
5.3 KiB
JavaScript
'use strict'
|
|
|
|
const { query, queryOne } = require('../database/postgres')
|
|
|
|
class GroupChat {
|
|
/**
|
|
* Listar grupos em que o colaborador participa (via team_members) com unread_count
|
|
*/
|
|
static async listForAccount(tenantId, accountId) {
|
|
return query(
|
|
`SELECT gc.id, gc.sector_id, gc.name,
|
|
s.icon, s.color,
|
|
(SELECT gm.content
|
|
FROM group_messages gm
|
|
WHERE gm.group_chat_id = gc.id
|
|
ORDER BY gm.created_at DESC LIMIT 1) AS last_message,
|
|
(SELECT gm.created_at
|
|
FROM group_messages gm
|
|
WHERE gm.group_chat_id = gc.id
|
|
ORDER BY gm.created_at DESC LIMIT 1) AS last_message_at,
|
|
(SELECT COUNT(*)::int
|
|
FROM group_messages gm2
|
|
LEFT JOIN group_last_read glr
|
|
ON glr.group_chat_id = gm2.group_chat_id AND glr.account_id = $2
|
|
WHERE gm2.group_chat_id = gc.id
|
|
AND gm2.from_account != $2
|
|
AND (glr.last_read_at IS NULL OR gm2.created_at > glr.last_read_at)
|
|
) AS unread_count
|
|
FROM group_chats gc
|
|
JOIN sectors s ON s.id = gc.sector_id
|
|
JOIN team_members tm ON tm.sector_id = gc.sector_id AND tm.account_id = $2
|
|
WHERE gc.tenant_id = $1
|
|
ORDER BY s.sort_order, gc.name`,
|
|
[tenantId, accountId]
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Listar todos os grupos do tenant (para owners/managers) com unread_count
|
|
*/
|
|
static async listAll(tenantId, accountId) {
|
|
return query(
|
|
`SELECT gc.id, gc.sector_id, gc.name,
|
|
s.icon, s.color,
|
|
(SELECT gm.content
|
|
FROM group_messages gm
|
|
WHERE gm.group_chat_id = gc.id
|
|
ORDER BY gm.created_at DESC LIMIT 1) AS last_message,
|
|
(SELECT gm.created_at
|
|
FROM group_messages gm
|
|
WHERE gm.group_chat_id = gc.id
|
|
ORDER BY gm.created_at DESC LIMIT 1) AS last_message_at,
|
|
(SELECT COUNT(*)::int
|
|
FROM group_messages gm2
|
|
LEFT JOIN group_last_read glr
|
|
ON glr.group_chat_id = gm2.group_chat_id AND glr.account_id = $2
|
|
WHERE gm2.group_chat_id = gc.id
|
|
AND gm2.from_account != $2
|
|
AND (glr.last_read_at IS NULL OR gm2.created_at > glr.last_read_at)
|
|
) AS unread_count
|
|
FROM group_chats gc
|
|
JOIN sectors s ON s.id = gc.sector_id
|
|
WHERE gc.tenant_id = $1
|
|
ORDER BY s.sort_order, gc.name`,
|
|
[tenantId, accountId]
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Marcar grupo como lido (upsert last_read_at)
|
|
*/
|
|
static async markRead(accountId, groupChatId) {
|
|
await queryOne(
|
|
`INSERT INTO group_last_read (account_id, group_chat_id, last_read_at)
|
|
VALUES ($1, $2, NOW())
|
|
ON CONFLICT (account_id, group_chat_id)
|
|
DO UPDATE SET last_read_at = NOW()`,
|
|
[accountId, groupChatId]
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Buscar grupo por id, verificando pertença ao tenant
|
|
*/
|
|
static async findById(tenantId, groupChatId) {
|
|
return queryOne(
|
|
`SELECT gc.*, s.name AS sector_name, s.icon, s.color,
|
|
(SELECT COUNT(*)::int FROM team_members tm WHERE tm.sector_id = s.id) AS member_count
|
|
FROM group_chats gc
|
|
JOIN sectors s ON s.id = gc.sector_id
|
|
WHERE gc.id = $1 AND gc.tenant_id = $2`,
|
|
[groupChatId, tenantId]
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Buscar mensagens de um grupo (paginação por cursor)
|
|
*/
|
|
static async getMessages(tenantId, groupChatId, limit = 50, before = null) {
|
|
const params = [tenantId, groupChatId, limit]
|
|
const beforeClause = before ? `AND gm.created_at < $4` : ''
|
|
if (before) params.push(before)
|
|
|
|
const msgs = await query(
|
|
`SELECT gm.id, gm.from_account, gm.content, gm.created_at,
|
|
a.name AS sender_name, a.avatar_url AS sender_avatar
|
|
FROM group_messages gm
|
|
LEFT JOIN accounts a ON a.id = gm.from_account
|
|
WHERE gm.tenant_id = $1
|
|
AND gm.group_chat_id = $2
|
|
${beforeClause}
|
|
ORDER BY gm.created_at DESC
|
|
LIMIT $3`,
|
|
params
|
|
)
|
|
return msgs.reverse()
|
|
}
|
|
|
|
/**
|
|
* Enviar mensagem em um grupo
|
|
*/
|
|
static async sendMessage(tenantId, groupChatId, fromAccount, content) {
|
|
return queryOne(
|
|
`INSERT INTO group_messages (tenant_id, group_chat_id, from_account, content)
|
|
VALUES ($1, $2, $3, $4)
|
|
RETURNING id, group_chat_id, from_account, content, created_at`,
|
|
[tenantId, groupChatId, fromAccount, content]
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Listar membros de um grupo (via team_members do setor)
|
|
*/
|
|
static async getMembers(tenantId, groupChatId) {
|
|
return query(
|
|
`SELECT a.id, a.name, a.avatar_url, a.availability
|
|
FROM group_chats gc
|
|
JOIN team_members tm ON tm.sector_id = gc.sector_id
|
|
JOIN accounts a ON a.id = tm.account_id
|
|
WHERE gc.id = $1 AND gc.tenant_id = $2 AND a.active = TRUE
|
|
ORDER BY a.name`,
|
|
[groupChatId, tenantId]
|
|
)
|
|
}
|
|
|
|
/**
|
|
* IDs dos membros de um grupo (para SSE direcionado)
|
|
*/
|
|
static async getMemberIds(groupChatId) {
|
|
const rows = await query(
|
|
`SELECT tm.account_id
|
|
FROM group_chats gc
|
|
JOIN team_members tm ON tm.sector_id = gc.sector_id
|
|
WHERE gc.id = $1`,
|
|
[groupChatId]
|
|
)
|
|
return rows.map(r => r.account_id)
|
|
}
|
|
}
|
|
|
|
module.exports = GroupChat
|