thread-master/apps/backend/internal/logic/radarmap/map.go

229 lines
6.4 KiB
Go
Raw Permalink Normal View History

2026-08-03 05:52:02 +00:00
package radarmap
import (
"apps/backend/internal/module/radar/domain"
"apps/backend/internal/types"
)
/*
ServiceProfile domain 檔案轉成 API 形狀
p nil 代表使用者還沒建檔 exists=false 的空殼而不是 404 表單本來就要能開空的
exists 這個欄位必須誠實訂閱閘門SP-01與前端引導都看它
*/
func ServiceProfile(p *domain.ServiceProfile) *types.ServiceProfilePublic {
if p == nil {
return &types.ServiceProfilePublic{
Exists: false,
Services: []types.ServiceItem{},
Cases: []types.ServiceCasePublic{},
Forbidden: []string{},
Faq: []types.FaqItem{},
ServiceAreas: []string{},
}
}
services := make([]types.ServiceItem, 0, len(p.Services))
for _, s := range p.Services {
services = append(services, types.ServiceItem{
Name: s.Name,
PriceMin: s.PriceMin,
PriceMax: s.PriceMax,
Currency: s.Currency,
})
}
cases := make([]types.ServiceCasePublic, 0, len(p.Cases))
for _, c := range p.Cases {
cases = append(cases, types.ServiceCasePublic{Title: c.Title, Summary: c.Summary, Link: c.Link})
}
faq := make([]types.FaqItem, 0, len(p.Faq))
for _, f := range p.Faq {
faq = append(faq, types.FaqItem{Question: f.Question, Answer: f.Answer})
}
forbidden := p.Forbidden
if forbidden == nil {
forbidden = []string{}
}
areas := p.ServiceAreas
if areas == nil {
areas = []string{}
}
return &types.ServiceProfilePublic{
Exists: true,
Services: services,
Cases: cases,
Forbidden: forbidden,
Faq: faq,
ServiceAreas: areas,
RemoteOk: p.RemoteOk,
Availability: p.Availability,
ToneNote: p.ToneNote,
UpdatedAt: p.UpdatedAt,
}
}
func Watch(w *domain.RadarWatch) *types.RadarWatchPublic {
if w == nil {
return nil
}
terms, excludes, regions := w.Terms, w.ExcludeTerms, w.Regions
if terms == nil {
terms = []string{}
}
if excludes == nil {
excludes = []string{}
}
if regions == nil {
regions = []string{}
}
return &types.RadarWatchPublic{
Id: w.ID,
Terms: terms,
ExcludeTerms: excludes,
Regions: regions,
Status: w.Status,
LastSweptAt: w.LastSweptAt,
CreatedAt: w.CreatedAt,
UpdatedAt: w.UpdatedAt,
}
}
func WatchList(list []*domain.RadarWatch) []types.RadarWatchPublic {
out := make([]types.RadarWatchPublic, 0, len(list))
for _, w := range list {
if p := Watch(w); p != nil {
out = append(out, *p)
}
}
return out
}
func Pagination(page, pageSize int, total int64) types.Pagination {
if page < 1 {
page = 1
}
if pageSize < 1 {
pageSize = 20
}
tp := int(total) / pageSize
if int(total)%pageSize != 0 {
tp++
}
return types.Pagination{Page: page, PageSize: pageSize, Total: total, TotalPages: tp}
}
// ServiceProfileInput 把 PUT 的 request 轉成 domain。owner_uid 一律由 logic 從 JWT 帶入。
func ServiceProfileInput(req *types.UpsertServiceProfileReq) *domain.ServiceProfile {
if req == nil {
return nil
}
services := make([]domain.ServiceItem, 0, len(req.Services))
for _, s := range req.Services {
services = append(services, domain.ServiceItem{
Name: s.Name,
PriceMin: s.PriceMin,
PriceMax: s.PriceMax,
Currency: s.Currency,
})
}
cases := make([]domain.ServiceCase, 0, len(req.Cases))
for _, c := range req.Cases {
cases = append(cases, domain.ServiceCase{Title: c.Title, Summary: c.Summary, Link: c.Link})
}
faq := make([]domain.FaqItem, 0, len(req.Faq))
for _, f := range req.Faq {
faq = append(faq, domain.FaqItem{Question: f.Question, Answer: f.Answer})
}
return &domain.ServiceProfile{
Services: services,
Cases: cases,
Forbidden: req.Forbidden,
Faq: faq,
ServiceAreas: req.ServiceAreas,
RemoteOk: req.RemoteOk,
Availability: req.Availability,
ToneNote: req.ToneNote,
}
}
func Opportunity(o *domain.Opportunity) *types.OpportunityPublic {
if o == nil {
return nil
}
reasons := make([]types.OpportunityReason, 0, len(o.Reasons))
for _, r := range o.Reasons {
reasons = append(reasons, types.OpportunityReason{Dimension: r.Dimension, Score: r.Score, Reason: r.Reason})
}
terms := o.MatchedTerms
if terms == nil {
terms = []string{}
}
out := &types.OpportunityPublic{
Id: o.ID, WatchId: o.WatchID, Source: o.Source, SourceScoutPostId: o.SourceScoutPostID,
ExternalId: o.ExternalID, Permalink: o.Permalink, AuthorHandle: o.AuthorHandle, Text: o.Text,
PostedAt: o.PostedAt, Status: o.Status, IntentScore: o.IntentScore, IntentBand: o.IntentBand,
Reasons: reasons, RegionDetected: o.RegionDetected, RegionMatch: o.RegionMatch,
FreshnessHours: o.FreshnessHours, MatchedService: o.MatchedService, MatchedTerms: terms,
RejectReason: o.RejectReason, ContactId: o.ContactID, CreatedAt: o.CreatedAt,
}
if o.Override != nil {
out.Override = &types.OpportunityOverride{
FromBand: o.Override.FromBand, ToBand: o.Override.ToBand,
FromStatus: o.Override.FromStatus, ToStatus: o.Override.ToStatus,
ActorUid: o.Override.ActorUID, At: o.Override.At,
}
}
return out
}
func OpportunityList(list []*domain.Opportunity) []types.OpportunityPublic {
out := make([]types.OpportunityPublic, 0, len(list))
for _, o := range list {
if p := Opportunity(o); p != nil {
out = append(out, *p)
}
}
return out
}
func Sweep(s *domain.RadarSweep) *types.RadarSweepPublic {
if s == nil {
return nil
}
return &types.RadarSweepPublic{
Id: s.ID, WatchId: s.WatchID, JobId: s.JobID, Path: s.Path,
HitCount: s.HitCount, JudgedCount: s.JudgedCount, CreatedCount: s.CreatedCount,
TruncatedCount: s.TruncatedCount, FailedReason: s.FailedReason, CreditsUsed: s.CreditsUsed,
StartedAt: s.StartedAt, EndedAt: s.EndedAt,
}
}
func SweepList(list []*domain.RadarSweep) []types.RadarSweepPublic {
out := make([]types.RadarSweepPublic, 0, len(list))
for _, s := range list {
if p := Sweep(s); p != nil {
out = append(out, *p)
}
}
return out
}
func Reply(r *domain.ReplyVariant) *types.ReplyVariantPublic {
if r == nil {
return nil
}
return &types.ReplyVariantPublic{
Id: r.ID, OpportunityId: r.OpportunityID, Variant: r.Variant, Text: r.Text,
UsedAt: r.UsedAt, SentChannel: r.SentChannel, CreatedAt: r.CreatedAt,
}
}
func ReplyList(list []*domain.ReplyVariant) []types.ReplyVariantPublic {
out := make([]types.ReplyVariantPublic, 0, len(list))
for _, r := range list {
if p := Reply(r); p != nil {
out = append(out, *p)
}
}
return out
}