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

351 lines
11 KiB
Plaintext
Raw Normal View History

2026-08-03 05:52:02 +00:00
syntax = "v1"
// demand-radar: service profile / radar watches / sweeps / opportunities / replies
// spec: docs/product/demand-radar/spec.md §5.2
// 未實作能力一律回 501radarDomain.ErrNotReady禁止 102000 空成功。
type (
// ---------- ServiceProfile ----------
ServiceItem {
Name string `json:"name"`
PriceMin float64 `json:"price_min,optional"`
PriceMax float64 `json:"price_max,optional"`
Currency string `json:"currency,optional"`
}
ServiceCasePublic {
Title string `json:"title"`
Summary string `json:"summary,optional"`
Link string `json:"link,optional"`
}
FaqItem {
Question string `json:"question"`
Answer string `json:"answer"`
}
ServiceProfilePublic {
Exists bool `json:"exists"`
Services []ServiceItem `json:"services"`
Cases []ServiceCasePublic `json:"cases"`
Forbidden []string `json:"forbidden"`
Faq []FaqItem `json:"faq"`
ServiceAreas []string `json:"service_areas"`
RemoteOk bool `json:"remote_ok"`
Availability string `json:"availability,optional"`
ToneNote string `json:"tone_note,optional"`
UpdatedAt int64 `json:"updated_at,optional"`
}
UpsertServiceProfileReq {
Services []ServiceItem `json:"services"`
Cases []ServiceCasePublic `json:"cases,optional"`
Forbidden []string `json:"forbidden,optional"`
Faq []FaqItem `json:"faq,optional"`
ServiceAreas []string `json:"service_areas,optional"`
RemoteOk bool `json:"remote_ok,optional"`
Availability string `json:"availability,optional"`
ToneNote string `json:"tone_note,optional"`
}
// ---------- RadarWatch ----------
RadarWatchPublic {
Id string `json:"id"`
Terms []string `json:"terms"`
ExcludeTerms []string `json:"exclude_terms"`
Regions []string `json:"regions"`
Status string `json:"status"` // active | paused | archived
LastSweptAt int64 `json:"last_swept_at,optional"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
ListWatchesReq {
Page int `form:"page,default=1"`
PageSize int `form:"pageSize,default=20"`
Status string `form:"status,optional"`
}
WatchListData {
List []RadarWatchPublic `json:"list"`
Pagination Pagination `json:"pagination"`
ActiveCount int `json:"active_count"`
MaxActive int `json:"max_active"`
ProfileExists bool `json:"profile_exists"`
}
CreateWatchReq {
Terms []string `json:"terms"`
ExcludeTerms []string `json:"exclude_terms,optional"`
Regions []string `json:"regions,optional"`
Enabled bool `json:"enabled,optional"`
}
UpdateWatchReq {
Id string `path:"id"`
Terms []string `json:"terms,optional"`
ExcludeTerms []string `json:"exclude_terms,optional"`
Regions []string `json:"regions,optional"`
}
WatchIdReq {
Id string `path:"id"`
}
SuggestWatchTermsReq {
Limit int `json:"limit,optional"`
}
WatchTermSuggestion {
Term string `json:"term"`
Reason string `json:"reason"`
Usage string `json:"usage"` // include | exclude
}
WatchSuggestData {
List []WatchTermSuggestion `json:"list"`
}
TriggerSweepData {
JobId string `json:"job_id"`
SweepId string `json:"sweep_id,optional"`
}
// ---------- Opportunity ----------
OpportunityReason {
Dimension string `json:"dimension"` // authenticity | intent | region | freshness | fit
Score int `json:"score"`
Reason string `json:"reason"`
}
OpportunityOverride {
FromBand string `json:"from_band,optional"`
ToBand string `json:"to_band,optional"`
FromStatus string `json:"from_status,optional"`
ToStatus string `json:"to_status,optional"`
ActorUid int64 `json:"actor_uid"`
At int64 `json:"at"`
}
OpportunityPublic {
Id string `json:"id"`
WatchId string `json:"watch_id,optional"`
Source string `json:"source"` // threads | manual | scout_promote
SourceScoutPostId string `json:"source_scout_post_id,optional"`
ExternalId string `json:"external_id"`
Permalink string `json:"permalink"`
AuthorHandle string `json:"author_handle"`
Text string `json:"text"`
PostedAt int64 `json:"posted_at"`
Status string `json:"status"` // judging | qualified | rejected | accepted | dismissed
IntentScore int `json:"intent_score"`
IntentBand string `json:"intent_band"` // high | mid | low
Reasons []OpportunityReason `json:"reasons"`
RegionDetected string `json:"region_detected,optional"`
RegionMatch string `json:"region_match"` // match | mismatch | unknown
FreshnessHours int `json:"freshness_hours"`
MatchedService string `json:"matched_service,optional"`
MatchedTerms []string `json:"matched_terms"`
RejectReason string `json:"reject_reason,optional"`
Override *OpportunityOverride `json:"override,optional"`
ContactId string `json:"contact_id,optional"`
DefaultReply *ReplyVariantPublic `json:"default_reply,optional"`
CreatedAt int64 `json:"created_at"`
}
RadarTodayStats {
Total int `json:"total"`
High int `json:"high"`
Mid int `json:"mid"`
Low int `json:"low"`
}
RadarTodayData {
Stats RadarTodayStats `json:"stats"`
High []OpportunityPublic `json:"high"`
Mid []OpportunityPublic `json:"mid"`
Low []OpportunityPublic `json:"low"`
TruncatedCount int `json:"truncated_count"`
LastSweptAt int64 `json:"last_swept_at,optional"`
// EmptyReason: not_swept_yet | all_watches_paused | no_watch | no_profile | sweep_failed | no_hit
EmptyReason string `json:"empty_reason,optional"`
EmptyHint string `json:"empty_hint,optional"`
}
ListOpportunitiesReq {
Page int `form:"page,default=1"`
PageSize int `form:"pageSize,default=20"`
Band string `form:"band,optional"`
Status string `form:"status,optional"`
WatchId string `form:"watch_id,optional"`
From int64 `form:"from,optional"`
To int64 `form:"to,optional"`
}
OpportunityListData {
List []OpportunityPublic `json:"list"`
Pagination Pagination `json:"pagination"`
}
OpportunityIdReq {
Id string `path:"id"`
}
AcceptOpportunityReq {
Id string `path:"id"`
}
AcceptOpportunityData {
OpportunityId string `json:"opportunity_id"`
ContactId string `json:"contact_id,optional"`
Status string `json:"status"`
}
DismissOpportunityReq {
Id string `path:"id"`
Reason string `json:"reason,optional"`
}
OverrideOpportunityReq {
Id string `path:"id"`
Band string `json:"band,optional"`
Status string `json:"status,optional"`
Note string `json:"note,optional"`
}
// ---------- ReplyVariant ----------
ReplyVariantPublic {
Id string `json:"id"`
OpportunityId string `json:"opportunity_id"`
Variant string `json:"variant"` // public_comment | dm | no_sales | professional | humorous
Text string `json:"text"`
UsedAt int64 `json:"used_at,optional"`
SentChannel string `json:"sent_channel,optional"` // outbox | manual_copy
CreatedAt int64 `json:"created_at"`
}
ListRepliesReq {
Id string `path:"id"`
}
ReplyListData {
List []ReplyVariantPublic `json:"list"`
}
CreateReplyReq {
Id string `path:"id"`
Variant string `json:"variant"`
}
// MarkReplyUsedReq: channel=outbox|manual_copydm 僅 manual_copy
MarkReplyUsedReq {
Id string `path:"id"`
ReplyId string `path:"replyId"`
Channel string `json:"channel"` // outbox | manual_copy
}
MarkReplyUsedData {
Reply ReplyVariantPublic `json:"reply"`
HealthAdvice string `json:"health_advice,optional"`
}
// ---------- RadarSweep ----------
RadarSweepPublic {
Id string `json:"id"`
WatchId string `json:"watch_id"`
JobId string `json:"job_id,optional"`
Path string `json:"path"` // api | crawler
HitCount int `json:"hit_count"`
JudgedCount int `json:"judged_count"`
CreatedCount int `json:"created_count"`
TruncatedCount int `json:"truncated_count"`
FailedReason string `json:"failed_reason,optional"`
CreditsUsed int `json:"credits_used"`
StartedAt int64 `json:"started_at"`
EndedAt int64 `json:"ended_at,optional"`
}
ListSweepsReq {
Page int `form:"page,default=1"`
PageSize int `form:"pageSize,default=20"`
WatchId string `form:"watch_id,optional"`
From int64 `form:"from,optional"`
To int64 `form:"to,optional"`
}
SweepListData {
List []RadarSweepPublic `json:"list"`
Pagination Pagination `json:"pagination"`
}
)
@server (
group: radar
prefix: /api/v1/radar
middleware: AuthJWT
)
service gateway {
@handler GetServiceProfile
get /service-profile returns (ServiceProfilePublic)
@handler UpsertServiceProfile
put /service-profile (UpsertServiceProfileReq) returns (ServiceProfilePublic)
@handler ListWatches
get /watches (ListWatchesReq) returns (WatchListData)
@handler CreateWatch
post /watches (CreateWatchReq) returns (RadarWatchPublic)
@handler SuggestWatchTerms
post /watches/suggest (SuggestWatchTermsReq) returns (WatchSuggestData)
@handler GetWatch
get /watches/:id (WatchIdReq) returns (RadarWatchPublic)
@handler UpdateWatch
put /watches/:id (UpdateWatchReq) returns (RadarWatchPublic)
@handler ArchiveWatch
delete /watches/:id (WatchIdReq) returns (OkData)
@handler PauseWatch
post /watches/:id/pause (WatchIdReq) returns (RadarWatchPublic)
@handler ResumeWatch
post /watches/:id/resume (WatchIdReq) returns (RadarWatchPublic)
@handler TriggerWatchSweep
post /watches/:id/sweep (WatchIdReq) returns (TriggerSweepData)
@handler GetRadarToday
get /today returns (RadarTodayData)
@handler ListOpportunities
get /opportunities (ListOpportunitiesReq) returns (OpportunityListData)
@handler GetOpportunity
get /opportunities/:id (OpportunityIdReq) returns (OpportunityPublic)
@handler AcceptOpportunity
post /opportunities/:id/accept (AcceptOpportunityReq) returns (AcceptOpportunityData)
@handler DismissOpportunity
post /opportunities/:id/dismiss (DismissOpportunityReq) returns (OpportunityPublic)
@handler OverrideOpportunity
post /opportunities/:id/override (OverrideOpportunityReq) returns (OpportunityPublic)
@handler ListOpportunityReplies
get /opportunities/:id/replies (ListRepliesReq) returns (ReplyListData)
@handler CreateOpportunityReply
post /opportunities/:id/replies (CreateReplyReq) returns (ReplyVariantPublic)
@handler MarkOpportunityReplyUsed
post /opportunities/:id/replies/:replyId/mark-used (MarkReplyUsedReq) returns (MarkReplyUsedData)
@handler ListSweeps
get /sweeps (ListSweepsReq) returns (SweepListData)
}