130 lines
4.0 KiB
Go
130 lines
4.0 KiB
Go
package domain
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
func NowNano() int64 { return time.Now().UTC().UnixNano() }
|
|
func NewID() string { return uuid.NewString() }
|
|
|
|
const (
|
|
StageNewFound = "new_found"
|
|
StageEngaged = "engaged"
|
|
StageDMSent = "dm_sent"
|
|
StageReplied = "replied"
|
|
StageQuoted = "quoted"
|
|
StageWon = "won"
|
|
StageLost = "lost"
|
|
|
|
PlatformThreads = "threads"
|
|
|
|
TouchStage = "stage"
|
|
TouchReply = "reply"
|
|
TouchNote = "note"
|
|
TouchConversion = "conversion"
|
|
|
|
FollowUpScheduled = "scheduled"
|
|
FollowUpNotified = "notified"
|
|
FollowUpDone = "done"
|
|
FollowUpSnoozed = "snoozed"
|
|
FollowUpEscalated = "escalated"
|
|
|
|
DefaultFollowUpDays = 3
|
|
)
|
|
|
|
type Contact struct {
|
|
ID string `bson:"_id" json:"id"`
|
|
OwnerUID int64 `bson:"owner_uid" json:"owner_uid"`
|
|
SourcePlatform string `bson:"source_platform" json:"source_platform"`
|
|
AuthorHandle string `bson:"author_handle" json:"author_handle"`
|
|
DisplayName string `bson:"display_name,omitempty" json:"display_name,omitempty"`
|
|
Stage string `bson:"stage" json:"stage"`
|
|
NeedsFollowUp bool `bson:"needs_follow_up" json:"needs_follow_up"`
|
|
FollowUpDays int `bson:"follow_up_days" json:"follow_up_days"`
|
|
LastTouchAt int64 `bson:"last_touch_at,omitempty" json:"last_touch_at,omitempty"`
|
|
OpportunityIDs []string `bson:"opportunity_ids" json:"opportunity_ids"`
|
|
MergedFrom []string `bson:"merged_from,omitempty" json:"merged_from,omitempty"`
|
|
TopIntentBand string `bson:"top_intent_band,omitempty" json:"top_intent_band,omitempty"`
|
|
TopIntentScore int `bson:"top_intent_score,omitempty" json:"top_intent_score,omitempty"`
|
|
// OutcomeID links the latest conversion in growth_outcomes.
|
|
OutcomeID string `bson:"outcome_id,omitempty" json:"outcome_id,omitempty"`
|
|
CreatedAt int64 `bson:"created_at" json:"created_at"`
|
|
UpdatedAt int64 `bson:"updated_at" json:"updated_at"`
|
|
}
|
|
|
|
type ContactTouch struct {
|
|
ID string `bson:"_id" json:"id"`
|
|
OwnerUID int64 `bson:"owner_uid" json:"owner_uid"`
|
|
ContactID string `bson:"contact_id" json:"contact_id"`
|
|
Type string `bson:"type" json:"type"`
|
|
FromStage string `bson:"from_stage,omitempty" json:"from_stage,omitempty"`
|
|
ToStage string `bson:"to_stage,omitempty" json:"to_stage,omitempty"`
|
|
Body string `bson:"body,omitempty" json:"body,omitempty"`
|
|
ActorUID int64 `bson:"actor_uid" json:"actor_uid"`
|
|
CreatedAt int64 `bson:"created_at" json:"created_at"`
|
|
}
|
|
|
|
type FollowUp struct {
|
|
ID string `bson:"_id" json:"id"`
|
|
OwnerUID int64 `bson:"owner_uid" json:"owner_uid"`
|
|
ContactID string `bson:"contact_id" json:"contact_id"`
|
|
DueAt int64 `bson:"due_at" json:"due_at"`
|
|
Status string `bson:"status" json:"status"`
|
|
NotifiedCount int `bson:"notified_count" json:"notified_count"`
|
|
CreatedAt int64 `bson:"created_at" json:"created_at"`
|
|
UpdatedAt int64 `bson:"updated_at" json:"updated_at"`
|
|
}
|
|
|
|
type ContactListFilter struct {
|
|
Stage string
|
|
FollowUp *bool
|
|
Band string
|
|
Sort string // last_touch_at | intent_score
|
|
Page int
|
|
PageSize int
|
|
}
|
|
|
|
type FollowUpListFilter struct {
|
|
Status string
|
|
Page int
|
|
PageSize int
|
|
}
|
|
|
|
func IsStage(s string) bool {
|
|
switch s {
|
|
case StageNewFound, StageEngaged, StageDMSent, StageReplied, StageQuoted, StageWon, StageLost:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (c *Contact) Normalize() error {
|
|
if c.OwnerUID <= 0 {
|
|
return fmt.Errorf("%w: owner_uid required", ErrValidation)
|
|
}
|
|
c.AuthorHandle = strings.TrimPrefix(strings.TrimSpace(c.AuthorHandle), "@")
|
|
if c.AuthorHandle == "" {
|
|
return fmt.Errorf("%w: author_handle required", ErrValidation)
|
|
}
|
|
if c.SourcePlatform == "" {
|
|
c.SourcePlatform = PlatformThreads
|
|
}
|
|
if c.Stage == "" {
|
|
c.Stage = StageNewFound
|
|
}
|
|
if !IsStage(c.Stage) {
|
|
return fmt.Errorf("%w: unknown stage %q", ErrValidation, c.Stage)
|
|
}
|
|
if c.FollowUpDays <= 0 {
|
|
c.FollowUpDays = DefaultFollowUpDays
|
|
}
|
|
if c.OpportunityIDs == nil {
|
|
c.OpportunityIDs = []string{}
|
|
}
|
|
return nil
|
|
}
|