Files
gochat/internal/repository/sla_policy_repo.go
T

306 lines
10 KiB
Go

package repository
import (
"context"
"time"
"gorm.io/datatypes"
"gorm.io/gorm"
"github.com/gochat/gochat/internal/model"
)
// SlaPolicyRepo implements GORM repository for SlaPolicy.
type SlaPolicyRepo struct {
db *gorm.DB
}
// NewSlaPolicyRepo creates a new SlaPolicy repository.
func NewSlaPolicyRepo(db *gorm.DB) *SlaPolicyRepo {
return &SlaPolicyRepo{db: db}
}
// Create creates a new SLA policy.
func (r *SlaPolicyRepo) Create(ctx context.Context, policy *model.SlaPolicy) error {
return r.db.WithContext(ctx).Create(policy).Error
}
// FindByID retrieves a SLA policy by primary key.
func (r *SlaPolicyRepo) FindByID(ctx context.Context, id uint) (*model.SlaPolicy, error) {
var policy model.SlaPolicy
if err := r.db.WithContext(ctx).First(&policy, id).Error; err != nil {
return nil, err
}
return &policy, nil
}
// FindByAccount retrieves all SLA policies for an account.
func (r *SlaPolicyRepo) FindByAccount(ctx context.Context, accountID uint) ([]model.SlaPolicy, error) {
var policies []model.SlaPolicy
if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).Find(&policies).Error; err != nil {
return nil, err
}
return policies, nil
}
// Update updates a SLA policy.
func (r *SlaPolicyRepo) Update(ctx context.Context, policy *model.SlaPolicy) error {
return r.db.WithContext(ctx).Save(policy).Error
}
// Delete soft-deletes a SLA policy.
func (r *SlaPolicyRepo) Delete(ctx context.Context, id uint) error {
return r.db.WithContext(ctx).Delete(&model.SlaPolicy{}, id).Error
}
// AppliedSlaRepo implements GORM repository for AppliedSLA.
type AppliedSlaRepo struct {
db *gorm.DB
}
// DB returns the underlying gorm.DB for serializer/report queries.
func (r *AppliedSlaRepo) DB() *gorm.DB {
return r.db
}
// NewAppliedSlaRepo creates a new AppliedSLA repository.
func NewAppliedSlaRepo(db *gorm.DB) *AppliedSlaRepo {
return &AppliedSlaRepo{db: db}
}
// FindByConversation retrieves the applied SLA for a conversation.
func (r *AppliedSlaRepo) FindByConversation(ctx context.Context, conversationID uint) (*model.AppliedSLA, error) {
var applied model.AppliedSLA
if err := r.db.WithContext(ctx).
Where("conversation_id = ?", conversationID).
First(&applied).Error; err != nil {
return nil, err
}
return &applied, nil
}
// FindByAccount retrieves all applied SLAs for an account.
func (r *AppliedSlaRepo) FindByAccount(ctx context.Context, accountID uint) ([]model.AppliedSLA, error) {
var applied []model.AppliedSLA
if err := r.db.WithContext(ctx).
Preload("SlaPolicy").
Preload("SlaEvents").
Where("account_id = ?", accountID).
Find(&applied).Error; err != nil {
return nil, err
}
return applied, nil
}
// AppliedSlaReportFilter contains Chatwoot applied_slas report filters.
type AppliedSlaReportFilter struct {
Since *time.Time
Until *time.Time
InboxID *uint
TeamID *uint
SlaPolicyID *uint
LabelList string
AssignedAgentID *uint
SLAStatus string
}
// FindReport returns filtered applied SLAs with report preloads.
func (r *AppliedSlaRepo) FindReport(ctx context.Context, accountID uint, filter AppliedSlaReportFilter, missedOnly bool, offset, limit int) ([]model.AppliedSLA, int64, error) {
base := r.reportQuery(ctx, accountID, filter, missedOnly)
var total int64
if err := base.Session(&gorm.Session{}).Count(&total).Error; err != nil {
return nil, 0, err
}
query := base.Preload("SlaPolicy").Preload("SlaEvents").Order("applied_slas.created_at DESC")
if limit > 0 {
query = query.Offset(offset).Limit(limit)
}
var applied []model.AppliedSLA
if err := query.Find(&applied).Error; err != nil {
return nil, 0, err
}
return applied, total, nil
}
// CountReport returns total and missed counts for Chatwoot SLA report metrics.
func (r *AppliedSlaRepo) CountReport(ctx context.Context, accountID uint, filter AppliedSlaReportFilter) (int64, int64, error) {
var total int64
if err := r.reportQuery(ctx, accountID, filter, false).Count(&total).Error; err != nil {
return 0, 0, err
}
var missed int64
if err := r.reportQuery(ctx, accountID, filter, true).Count(&missed).Error; err != nil {
return 0, 0, err
}
return total, missed, nil
}
func (r *AppliedSlaRepo) reportQuery(ctx context.Context, accountID uint, filter AppliedSlaReportFilter, missedOnly bool) *gorm.DB {
query := r.db.WithContext(ctx).Model(&model.AppliedSLA{}).
Where("applied_slas.account_id = ?", accountID)
if filter.Since != nil {
query = query.Where("applied_slas.created_at >= ?", *filter.Since)
}
if filter.Until != nil {
query = query.Where("applied_slas.created_at <= ?", *filter.Until)
}
if filter.SlaPolicyID != nil && *filter.SlaPolicyID != 0 {
query = query.Where("applied_slas.sla_policy_id = ?", *filter.SlaPolicyID)
}
if filter.SLAStatus != "" {
query = query.Where("applied_slas.sla_status = ?", filter.SLAStatus)
}
if missedOnly {
query = query.Where("applied_slas.sla_status IN ?", []model.SLAStatus{model.SLAStatusMissed, model.SLAStatusActiveWithMisses})
}
needsConversation := filter.InboxID != nil || filter.TeamID != nil || filter.AssignedAgentID != nil || filter.LabelList != ""
if needsConversation {
query = query.Joins("JOIN conversations ON conversations.id = applied_slas.conversation_id")
if filter.InboxID != nil && *filter.InboxID != 0 {
query = query.Where("conversations.inbox_id = ?", *filter.InboxID)
}
if filter.TeamID != nil && *filter.TeamID != 0 {
query = query.Where("conversations.team_id = ?", *filter.TeamID)
}
if filter.AssignedAgentID != nil && *filter.AssignedAgentID != 0 {
query = query.Where("conversations.assignee_id = ?", *filter.AssignedAgentID)
}
if filter.LabelList != "" {
query = query.Where("conversations.labels LIKE ?", "%"+filter.LabelList+"%")
}
}
return query
}
// Create creates a new applied SLA record.
func (r *AppliedSlaRepo) Create(ctx context.Context, applied *model.AppliedSLA) error {
return r.db.WithContext(ctx).Create(applied).Error
}
// FindByID retrieves an applied SLA by primary key.
func (r *AppliedSlaRepo) FindByID(ctx context.Context, id uint) (*model.AppliedSLA, error) {
var applied model.AppliedSLA
if err := r.db.WithContext(ctx).First(&applied, id).Error; err != nil {
return nil, err
}
return &applied, nil
}
// Update persists changes to an existing AppliedSLA record.
func (r *AppliedSlaRepo) Update(ctx context.Context, applied *model.AppliedSLA) error {
return r.db.WithContext(ctx).Save(applied).Error
}
// Delete soft-deletes an applied SLA.
func (r *AppliedSlaRepo) Delete(ctx context.Context, id uint) error {
return r.db.WithContext(ctx).Delete(&model.AppliedSLA{}, id).Error
}
// SlaEventRepo implements GORM repository for SlaEvent.
type SlaEventRepo struct {
db *gorm.DB
}
// NewSlaEventRepo creates a new SlaEvent repository.
func NewSlaEventRepo(db *gorm.DB) *SlaEventRepo {
return &SlaEventRepo{db: db}
}
// FindByAppliedSla retrieves all SLA events for an applied SLA.
func (r *SlaEventRepo) FindByAppliedSla(ctx context.Context, appliedSlaID uint) ([]model.SlaEvent, error) {
var events []model.SlaEvent
if err := r.db.WithContext(ctx).
Where("applied_sla_id = ?", appliedSlaID).
Find(&events).Error; err != nil {
return nil, err
}
return events, nil
}
// ExistsByAppliedSlaEventAndMeta checks whether a missed SLA threshold was already recorded.
func (r *SlaEventRepo) ExistsByAppliedSlaEventAndMeta(ctx context.Context, appliedSlaID uint, eventType model.SLAEventType, meta datatypes.JSON) (bool, error) {
var count int64
query := r.db.WithContext(ctx).Model(&model.SlaEvent{}).
Where("applied_sla_id = ? AND event_type = ?", appliedSlaID, eventType)
if len(meta) > 0 {
query = query.Where("meta = ?", meta)
}
if err := query.Count(&count).Error; err != nil {
return false, err
}
return count > 0, nil
}
// Create creates a new SLA event.
func (r *SlaEventRepo) Create(ctx context.Context, event *model.SlaEvent) error {
return r.db.WithContext(ctx).Create(event).Error
}
// Delete soft-deletes a SLA event.
func (r *SlaEventRepo) Delete(ctx context.Context, id uint) error {
return r.db.WithContext(ctx).Delete(&model.SlaEvent{}, id).Error
}
// SlaPolicyInboxRepo implements GORM repository for SlaPolicyInbox (join table).
type SlaPolicyInboxRepo struct {
db *gorm.DB
}
// NewSlaPolicyInboxRepo creates a new SlaPolicyInbox repository.
func NewSlaPolicyInboxRepo(db *gorm.DB) *SlaPolicyInboxRepo {
return &SlaPolicyInboxRepo{db: db}
}
// Create creates a new SLA policy inbox association.
func (r *SlaPolicyInboxRepo) Create(ctx context.Context, spi *model.SlaPolicyInbox) error {
return r.db.WithContext(ctx).Create(spi).Error
}
// FindBySlaPolicy retrieves all inbox associations for a SLA policy.
func (r *SlaPolicyInboxRepo) FindBySlaPolicy(ctx context.Context, slaPolicyID uint) ([]model.SlaPolicyInbox, error) {
var spInboxes []model.SlaPolicyInbox
if err := r.db.WithContext(ctx).
Where("sla_policy_id = ?", slaPolicyID).
Find(&spInboxes).Error; err != nil {
return nil, err
}
return spInboxes, nil
}
// FindByInbox retrieves the SLA policy inbox association for a specific inbox.
func (r *SlaPolicyInboxRepo) FindByInbox(ctx context.Context, inboxID uint) (*model.SlaPolicyInbox, error) {
var spi model.SlaPolicyInbox
if err := r.db.WithContext(ctx).
Where("inbox_id = ?", inboxID).
First(&spi).Error; err != nil {
return nil, err
}
return &spi, nil
}
// Delete soft-deletes a SLA policy inbox association.
func (r *SlaPolicyInboxRepo) Delete(ctx context.Context, id uint) error {
return r.db.WithContext(ctx).Delete(&model.SlaPolicyInbox{}, id).Error
}
// DeleteBySlaPolicy removes all inbox associations for a SLA policy.
func (r *SlaPolicyInboxRepo) DeleteBySlaPolicy(ctx context.Context, slaPolicyID uint) error {
return r.db.WithContext(ctx).
Where("sla_policy_id = ?", slaPolicyID).
Delete(&model.SlaPolicyInbox{}).Error
}
// DeleteByInbox removes the SLA policy association for a specific inbox.
func (r *SlaPolicyInboxRepo) DeleteByInbox(ctx context.Context, inboxID uint) error {
return r.db.WithContext(ctx).
Where("inbox_id = ?", inboxID).
Delete(&model.SlaPolicyInbox{}).Error
}