FEAT: Aprimora Templates Whatsapp
All checks were successful
Deploy Dev / deploy (push) Successful in 3s

This commit is contained in:
Rafael Alves Lopes 2026-05-22 10:51:44 -03:00
parent 3ae6b7e978
commit 22e4742384
2 changed files with 141 additions and 11 deletions

View File

@ -65,12 +65,27 @@ export class WhatsappController {
}
@Post('templates')
async saveTemplate(@Body() body: { name: string; content: string }) {
return this.whatsappService.saveTemplate(body.name, body.content);
async saveTemplate(@Body() body: { name: string; content: string; areaId?: number | null; requestedByRole?: string }) {
return this.whatsappService.saveTemplate(body.name, body.content, body.areaId, body.requestedByRole);
}
@Post('templates/update/:id')
async updateTemplate(@Param('id') id: string, @Body() body: { name: string; content: string }) {
return this.whatsappService.updateTemplate(Number(id), body.name, body.content);
async updateTemplate(@Param('id') id: string, @Body() body: { name: string; content: string; areaId?: number | null }) {
return this.whatsappService.updateTemplate(Number(id), body.name, body.content, body.areaId);
}
@Post('templates/approve-admin/:id')
async approveTemplateByAdmin(@Param('id') id: string) {
return this.whatsappService.approveTemplateByAdmin(Number(id));
}
@Post('templates/reject-admin/:id')
async rejectTemplateByAdmin(@Param('id') id: string) {
return this.whatsappService.rejectTemplateByAdmin(Number(id));
}
@Delete('templates/:id')
async deleteTemplate(@Param('id') id: string) {
return this.whatsappService.deleteTemplate(Number(id));
}
}

View File

@ -30,10 +30,25 @@ export class WhatsappService implements OnModuleInit {
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL UNIQUE,
content TEXT NOT NULL,
area_id INTEGER REFERENCES areas (id) ON DELETE SET NULL,
status VARCHAR(40) NOT NULL DEFAULT 'approved',
requested_by_role VARCHAR(40),
admin_approved_at TIMESTAMP WITH TIME ZONE,
meta_submitted_at TIMESTAMP WITH TIME ZONE,
meta_approved_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
`);
await this.db.query(`
ALTER TABLE whatsapp_templates
ADD COLUMN IF NOT EXISTS area_id INTEGER REFERENCES areas (id) ON DELETE SET NULL,
ADD COLUMN IF NOT EXISTS status VARCHAR(40) NOT NULL DEFAULT 'approved',
ADD COLUMN IF NOT EXISTS requested_by_role VARCHAR(40),
ADD COLUMN IF NOT EXISTS admin_approved_at TIMESTAMP WITH TIME ZONE,
ADD COLUMN IF NOT EXISTS meta_submitted_at TIMESTAMP WITH TIME ZONE,
ADD COLUMN IF NOT EXISTS meta_approved_at TIMESTAMP WITH TIME ZONE;
`);
await this.db.query(`
INSERT INTO whatsapp_templates (name, content) VALUES
('aviso_fatura', 'Olá, {nome}. Estamos entrando em contato para lembrá-lo que a sua fatura está programada para {data}.'),
@ -576,28 +591,128 @@ export class WhatsappService implements OnModuleInit {
}
async getTemplates() {
const res = await this.db.query('SELECT * FROM whatsapp_templates ORDER BY id ASC');
await this.refreshFakeMetaApprovals();
const res = await this.db.query(`
SELECT
wt.*,
a.nome AS area_nome
FROM whatsapp_templates wt
LEFT JOIN areas a ON a.id = wt.area_id
ORDER BY wt.id ASC
`);
return res.rows;
}
private async getTemplateById(id: number) {
await this.refreshFakeMetaApprovals();
const res = await this.db.query('SELECT * FROM whatsapp_templates WHERE id = $1 LIMIT 1', [id]);
return res.rows[0] || null;
}
async saveTemplate(name: string, content: string) {
async saveTemplate(name: string, content: string, areaId?: number | null, requestedByRole = 'admin') {
const isSupervisor = requestedByRole === 'supervisor';
const status = isSupervisor ? 'admin_review' : 'meta_review';
const adminApprovedAt = isSupervisor ? null : 'CURRENT_TIMESTAMP';
const metaSubmittedAt = isSupervisor ? null : 'CURRENT_TIMESTAMP';
const res = await this.db.query(
'INSERT INTO whatsapp_templates (name, content) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET content = EXCLUDED.content, updated_at = CURRENT_TIMESTAMP RETURNING *',
[name, content]
`
INSERT INTO whatsapp_templates (
name,
content,
area_id,
status,
requested_by_role,
admin_approved_at,
meta_submitted_at,
meta_approved_at,
updated_at
)
VALUES ($1, $2, $3, $4, $5, ${adminApprovedAt}, ${metaSubmittedAt}, NULL, CURRENT_TIMESTAMP)
ON CONFLICT (name) DO UPDATE SET
content = EXCLUDED.content,
area_id = EXCLUDED.area_id,
status = EXCLUDED.status,
requested_by_role = EXCLUDED.requested_by_role,
admin_approved_at = EXCLUDED.admin_approved_at,
meta_submitted_at = EXCLUDED.meta_submitted_at,
meta_approved_at = NULL,
updated_at = CURRENT_TIMESTAMP
RETURNING *
`,
[name, content, areaId || null, status, requestedByRole]
);
return res.rows[0];
}
async updateTemplate(id: number, name: string, content: string) {
async updateTemplate(id: number, name: string, content: string, areaId?: number | null) {
const res = await this.db.query(
'UPDATE whatsapp_templates SET name = $1, content = $2, updated_at = CURRENT_TIMESTAMP WHERE id = $3 RETURNING *',
[name, content, id]
`
UPDATE whatsapp_templates
SET
name = $1,
content = $2,
area_id = $3,
status = 'meta_review',
admin_approved_at = CURRENT_TIMESTAMP,
meta_submitted_at = CURRENT_TIMESTAMP,
meta_approved_at = NULL,
updated_at = CURRENT_TIMESTAMP
WHERE id = $4
RETURNING *
`,
[name, content, areaId || null, id]
);
return res.rows[0];
}
async approveTemplateByAdmin(id: number) {
const res = await this.db.query(
`
UPDATE whatsapp_templates
SET
status = 'meta_review',
admin_approved_at = CURRENT_TIMESTAMP,
meta_submitted_at = CURRENT_TIMESTAMP,
meta_approved_at = NULL,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
RETURNING *
`,
[id],
);
return res.rows[0];
}
async rejectTemplateByAdmin(id: number) {
const res = await this.db.query(
`
UPDATE whatsapp_templates
SET
status = 'rejected',
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
RETURNING *
`,
[id],
);
return res.rows[0];
}
async deleteTemplate(id: number) {
await this.db.query('DELETE FROM whatsapp_templates WHERE id = $1', [id]);
return { success: true };
}
private async refreshFakeMetaApprovals() {
await this.db.query(`
UPDATE whatsapp_templates
SET
status = 'approved',
meta_approved_at = COALESCE(meta_approved_at, meta_submitted_at + INTERVAL '15 minutes'),
updated_at = CURRENT_TIMESTAMP
WHERE status = 'meta_review'
AND meta_submitted_at IS NOT NULL
AND meta_submitted_at <= CURRENT_TIMESTAMP - INTERVAL '15 minutes'
`);
}
}