279 lines
8.3 KiB
Plaintext
279 lines
8.3 KiB
Plaintext
|
|
syntax = "v1"
|
|||
|
|
|
|||
|
|
// demand-radar: contacts / touches / follow-ups / conversion / stats
|
|||
|
|
// spec: docs/product/demand-radar/spec.md §5.2
|
|||
|
|
// 未實作能力一律回 501(crmDomain.ErrNotReady),禁止 102000 空成功。
|
|||
|
|
// 命名紅線:不得以 lead 指稱銷售線索。
|
|||
|
|
|
|||
|
|
type (
|
|||
|
|
// ---------- Contact ----------
|
|||
|
|
ContactPublic {
|
|||
|
|
Id string `json:"id"`
|
|||
|
|
SourcePlatform string `json:"source_platform"`
|
|||
|
|
AuthorHandle string `json:"author_handle"`
|
|||
|
|
DisplayName string `json:"display_name,optional"`
|
|||
|
|
Stage string `json:"stage"` // new_found | engaged | dm_sent | replied | quoted | won | lost
|
|||
|
|
NeedsFollowUp bool `json:"needs_follow_up"`
|
|||
|
|
FollowUpDays int `json:"follow_up_days"`
|
|||
|
|
LastTouchAt int64 `json:"last_touch_at,optional"`
|
|||
|
|
OpportunityIds []string `json:"opportunity_ids"`
|
|||
|
|
OpportunityCount int `json:"opportunity_count"`
|
|||
|
|
MergedFrom []string `json:"merged_from,optional"`
|
|||
|
|
TopIntentBand string `json:"top_intent_band,optional"`
|
|||
|
|
TopIntentScore int `json:"top_intent_score,optional"`
|
|||
|
|
CreatedAt int64 `json:"created_at"`
|
|||
|
|
UpdatedAt int64 `json:"updated_at"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ContactBrief {
|
|||
|
|
Id string `json:"id"`
|
|||
|
|
SourcePlatform string `json:"source_platform"`
|
|||
|
|
AuthorHandle string `json:"author_handle"`
|
|||
|
|
DisplayName string `json:"display_name,optional"`
|
|||
|
|
Stage string `json:"stage"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// StageCount — 八格視圖用;stage 值另含 needs_follow_up(跨階段檢視,非階段值)
|
|||
|
|
StageCount {
|
|||
|
|
Stage string `json:"stage"`
|
|||
|
|
Count int `json:"count"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ListContactsReq {
|
|||
|
|
Page int `form:"page,default=1"`
|
|||
|
|
PageSize int `form:"pageSize,default=20"`
|
|||
|
|
Stage string `form:"stage,optional"`
|
|||
|
|
// FollowUp: true | false(留空=不篩選)
|
|||
|
|
FollowUp string `form:"follow_up,optional"`
|
|||
|
|
Band string `form:"band,optional"`
|
|||
|
|
// Sort: last_touch_at | intent_score(預設 last_touch_at 倒序)
|
|||
|
|
Sort string `form:"sort,optional"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ContactListData {
|
|||
|
|
List []ContactPublic `json:"list"`
|
|||
|
|
Pagination Pagination `json:"pagination"`
|
|||
|
|
StageCounts []StageCount `json:"stage_counts"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ContactTouchPublic {
|
|||
|
|
Id string `json:"id"`
|
|||
|
|
ContactId string `json:"contact_id"`
|
|||
|
|
Type string `json:"type"` // stage | reply | note | conversion
|
|||
|
|
FromStage string `json:"from_stage,optional"`
|
|||
|
|
ToStage string `json:"to_stage,optional"`
|
|||
|
|
Body string `json:"body,optional"`
|
|||
|
|
ActorUid int64 `json:"actor_uid"`
|
|||
|
|
CreatedAt int64 `json:"created_at"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ContactOpportunityBrief {
|
|||
|
|
Id string `json:"id"`
|
|||
|
|
Permalink string `json:"permalink"`
|
|||
|
|
Text string `json:"text"`
|
|||
|
|
IntentScore int `json:"intent_score"`
|
|||
|
|
IntentBand string `json:"intent_band"`
|
|||
|
|
CreatedAt int64 `json:"created_at"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
GetContactReq {
|
|||
|
|
Id string `path:"id"`
|
|||
|
|
Page int `form:"page,default=1"`
|
|||
|
|
PageSize int `form:"pageSize,default=20"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ContactDetailData {
|
|||
|
|
Contact ContactPublic `json:"contact"`
|
|||
|
|
Touches []ContactTouchPublic `json:"touches"`
|
|||
|
|
Pagination Pagination `json:"pagination"`
|
|||
|
|
Opportunities []ContactOpportunityBrief `json:"opportunities"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ContactIdReq {
|
|||
|
|
Id string `path:"id"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
UpdateContactStageReq {
|
|||
|
|
Id string `path:"id"`
|
|||
|
|
Stage string `json:"stage"`
|
|||
|
|
Note string `json:"note,optional"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
SetContactFollowUpReq {
|
|||
|
|
Id string `path:"id"`
|
|||
|
|
NeedsFollowUp bool `json:"needs_follow_up"`
|
|||
|
|
Days int `json:"days,optional"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
CreateContactNoteReq {
|
|||
|
|
Id string `path:"id"`
|
|||
|
|
Body string `json:"body"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
MergeContactReq {
|
|||
|
|
Id string `path:"id"`
|
|||
|
|
SourceContactId string `json:"source_contact_id"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
UnmergeContactReq {
|
|||
|
|
Id string `path:"id"`
|
|||
|
|
MergedContactId string `json:"merged_contact_id"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ---------- Conversion(寫既有 growth_outcomes,不另建成交帳) ----------
|
|||
|
|
CreateCrmConversionReq {
|
|||
|
|
Id string `path:"id"`
|
|||
|
|
Amount float64 `json:"amount,optional"`
|
|||
|
|
Currency string `json:"currency,optional"`
|
|||
|
|
Note string `json:"note,optional"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
UpdateCrmConversionReq {
|
|||
|
|
Id string `path:"id"`
|
|||
|
|
Amount float64 `json:"amount,optional"`
|
|||
|
|
Currency string `json:"currency,optional"`
|
|||
|
|
Note string `json:"note,optional"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
DeleteCrmConversionReq {
|
|||
|
|
Id string `path:"id"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
CrmConversionData {
|
|||
|
|
ContactId string `json:"contact_id"`
|
|||
|
|
OutcomeId string `json:"outcome_id,optional"`
|
|||
|
|
Stage string `json:"stage"`
|
|||
|
|
Amount float64 `json:"amount,optional"`
|
|||
|
|
Currency string `json:"currency,optional"`
|
|||
|
|
Note string `json:"note,optional"`
|
|||
|
|
UpdatedAt int64 `json:"updated_at"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ---------- FollowUp ----------
|
|||
|
|
FollowUpPublic {
|
|||
|
|
Id string `json:"id"`
|
|||
|
|
ContactId string `json:"contact_id"`
|
|||
|
|
DueAt int64 `json:"due_at"`
|
|||
|
|
Status string `json:"status"` // scheduled | notified | done | snoozed | escalated
|
|||
|
|
NotifiedCount int `json:"notified_count"`
|
|||
|
|
Contact ContactBrief `json:"contact"`
|
|||
|
|
CreatedAt int64 `json:"created_at"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ListFollowUpsReq {
|
|||
|
|
Page int `form:"page,default=1"`
|
|||
|
|
PageSize int `form:"pageSize,default=20"`
|
|||
|
|
Status string `form:"status,optional"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FollowUpListData {
|
|||
|
|
List []FollowUpPublic `json:"list"`
|
|||
|
|
Pagination Pagination `json:"pagination"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FollowUpIdReq {
|
|||
|
|
Id string `path:"id"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
SnoozeFollowUpReq {
|
|||
|
|
Id string `path:"id"`
|
|||
|
|
Days int `json:"days"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
GenerateFollowUpMessageReq {
|
|||
|
|
Id string `path:"id"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
GenerateFollowUpMessageData {
|
|||
|
|
Text string `json:"text"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ---------- Stats(樣本 < 5 只給絕對數) ----------
|
|||
|
|
TermConversionStat {
|
|||
|
|
Term string `json:"term"`
|
|||
|
|
Accepted int `json:"accepted"`
|
|||
|
|
Replied int `json:"replied"`
|
|||
|
|
Won int `json:"won"`
|
|||
|
|
ConversionRate float64 `json:"conversion_rate,optional"`
|
|||
|
|
InsufficientSample bool `json:"insufficient_sample"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
VariantConversionStat {
|
|||
|
|
Variant string `json:"variant"`
|
|||
|
|
Used int `json:"used"`
|
|||
|
|
Replied int `json:"replied"`
|
|||
|
|
Won int `json:"won"`
|
|||
|
|
SuccessRate float64 `json:"success_rate,optional"`
|
|||
|
|
InsufficientSample bool `json:"insufficient_sample"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
SourceConversionStat {
|
|||
|
|
Source string `json:"source"` // radar | scout | manual_import
|
|||
|
|
Won int `json:"won"`
|
|||
|
|
InsufficientSample bool `json:"insufficient_sample"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
CrmStatsReq {
|
|||
|
|
From int64 `form:"from,optional"`
|
|||
|
|
To int64 `form:"to,optional"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
CrmStatsData {
|
|||
|
|
Terms []TermConversionStat `json:"terms"`
|
|||
|
|
Variants []VariantConversionStat `json:"variants"`
|
|||
|
|
Sources []SourceConversionStat `json:"sources"`
|
|||
|
|
}
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
@server (
|
|||
|
|
group: crm
|
|||
|
|
prefix: /api/v1/crm
|
|||
|
|
middleware: AuthJWT
|
|||
|
|
)
|
|||
|
|
service gateway {
|
|||
|
|
@handler ListContacts
|
|||
|
|
get /contacts (ListContactsReq) returns (ContactListData)
|
|||
|
|
|
|||
|
|
@handler GetContact
|
|||
|
|
get /contacts/:id (GetContactReq) returns (ContactDetailData)
|
|||
|
|
|
|||
|
|
@handler UpdateContactStage
|
|||
|
|
post /contacts/:id/stage (UpdateContactStageReq) returns (ContactPublic)
|
|||
|
|
|
|||
|
|
@handler SetContactFollowUp
|
|||
|
|
post /contacts/:id/follow-up (SetContactFollowUpReq) returns (ContactPublic)
|
|||
|
|
|
|||
|
|
@handler CreateContactNote
|
|||
|
|
post /contacts/:id/notes (CreateContactNoteReq) returns (ContactTouchPublic)
|
|||
|
|
|
|||
|
|
@handler MergeContact
|
|||
|
|
post /contacts/:id/merge (MergeContactReq) returns (ContactPublic)
|
|||
|
|
|
|||
|
|
@handler UnmergeContact
|
|||
|
|
post /contacts/:id/unmerge (UnmergeContactReq) returns (ContactPublic)
|
|||
|
|
|
|||
|
|
@handler CreateContactConversion
|
|||
|
|
post /contacts/:id/conversion (CreateCrmConversionReq) returns (CrmConversionData)
|
|||
|
|
|
|||
|
|
@handler UpdateContactConversion
|
|||
|
|
put /contacts/:id/conversion (UpdateCrmConversionReq) returns (CrmConversionData)
|
|||
|
|
|
|||
|
|
@handler DeleteContactConversion
|
|||
|
|
delete /contacts/:id/conversion (DeleteCrmConversionReq) returns (OkData)
|
|||
|
|
|
|||
|
|
@handler ListFollowUps
|
|||
|
|
get /followups (ListFollowUpsReq) returns (FollowUpListData)
|
|||
|
|
|
|||
|
|
@handler DoneFollowUp
|
|||
|
|
post /followups/:id/done (FollowUpIdReq) returns (FollowUpPublic)
|
|||
|
|
|
|||
|
|
@handler SnoozeFollowUp
|
|||
|
|
post /followups/:id/snooze (SnoozeFollowUpReq) returns (FollowUpPublic)
|
|||
|
|
|
|||
|
|
@handler GenerateFollowUpMessage
|
|||
|
|
post /followups/:id/message (GenerateFollowUpMessageReq) returns (GenerateFollowUpMessageData)
|
|||
|
|
|
|||
|
|
@handler GetCrmStats
|
|||
|
|
get /stats (CrmStatsReq) returns (CrmStatsData)
|
|||
|
|
}
|