thread-master/apps/backend/generate/api/crm.api

279 lines
8.3 KiB
Plaintext
Raw Normal View History

2026-08-03 05:52:02 +00:00
syntax = "v1"
// demand-radar: contacts / touches / follow-ups / conversion / stats
// spec: docs/product/demand-radar/spec.md §5.2
// 未實作能力一律回 501crmDomain.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)
}