fix(umind): el aviso de tope se repetía en cada despliegue y el cliente no se enteraba
Qué pasa cuando un cliente supera el tope de consumo de su plan: el servicio sigue funcionando y el excedente se factura en el próximo ciclo. Es la decisión de diseño y no cambia — cortarle el asistente a un negocio en medio de una conversación con un cliente suyo es peor que la factura. Lo que sí estaba mal es cómo se avisaba. El control de "a este ya le avisé" vivía en memoria, con un ponytail: que asumía reinicios raros. Con despliegue automático en cada push, ese flag se borra varias veces por día: el mismo cliente pasado de tope recibía el aviso de nuevo en el siguiente mensaje, y otra vez, y otra. Ahora se marca en la base, con un UPDATE condicionado que además lo hace atómico — dos mensajes que crucen el tope a la vez, o dos réplicas del proceso, avisan una sola vez. Y el aviso iba sólo al Telegram del staff. El dueño del negocio, que es el que va a recibir la factura con el excedente, no se enteraba por ningún lado: tenía que entrar al panel a mirar. Ahora le llega un correo que dice cuánto lleva consumido, cuánto incluye su plan y —lo más importante— que su asistente sigue funcionando con normalidad. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
9dba93e7f4
commit
97e66b82e3
@@ -31,6 +31,23 @@ type UmindTenant struct {
|
||||
// el staff los asigne, y el ALTER TABLE no falla sobre datos existentes.
|
||||
ClienteID *uint `json:"cliente_id" gorm:"column:cliente_id;index"`
|
||||
PlanID *uint `json:"plan_id" gorm:"column:plan_id;index"`
|
||||
// UltimoAvisoTope guarda el período ("2026-08") en que se avisó que se
|
||||
// pasó el tope de consumo, para no repetirlo por cada mensaje. En la base
|
||||
// y no en memoria: con despliegues varias veces al día, un flag en RAM se
|
||||
// borra seguido y el cliente recibe el mismo aviso una y otra vez.
|
||||
UltimoAvisoTope string `json:"ultimo_aviso_tope" gorm:"column:ultimo_aviso_tope;size:7"`
|
||||
}
|
||||
|
||||
// MarcarAvisoTope deja registrado que ya se avisó en ese período. Devuelve
|
||||
// false si el aviso ya estaba puesto, que es la señal de no volver a mandarlo.
|
||||
//
|
||||
// El UPDATE condicionado es lo que hace la operación atómica: si dos mensajes
|
||||
// cruzan el tope a la vez, sólo uno afecta una fila y sólo ese avisa.
|
||||
func MarcarAvisoTope(tenantID uint, periodo string) bool {
|
||||
res := app.Http.Database.DB.Model(&UmindTenant{}).
|
||||
Where("id = ? AND (ultimo_aviso_tope IS NULL OR ultimo_aviso_tope <> ?)", tenantID, periodo).
|
||||
Update("ultimo_aviso_tope", periodo)
|
||||
return res.Error == nil && res.RowsAffected > 0
|
||||
}
|
||||
|
||||
func (UmindTenant) TableName() string { return "umind_tenants" }
|
||||
|
||||
Reference in New Issue
Block a user