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)
|
||
}
|