Files
supply-intelligence/internal/app/app.go
2026-05-07 10:16:46 +08:00

161 lines
5.3 KiB
Go

package app
import (
"context"
"time"
"supply-intelligence/internal/admission"
"supply-intelligence/internal/discovery"
"supply-intelligence/internal/domain"
"supply-intelligence/internal/gatewayconsumer"
"supply-intelligence/internal/httpapi"
"supply-intelligence/internal/poller"
"supply-intelligence/internal/probe"
"supply-intelligence/internal/publish"
"supply-intelligence/internal/repository"
)
type Application struct {
Repo *repository.MemoryRepository
ProbeService *probe.Service
PublishService *publish.Service
DiscoveryService *discovery.Service
GatewayConsumerService *gatewayconsumer.Service
GatewayPoller *poller.GatewayPackagePoller
GatewayRuntime *poller.Runtime
AdmissionService *admission.Service
Server *httpapi.Server
}
func New() *Application {
repo := repository.NewMemoryRepository()
probeService := probe.NewService(repo)
publishService := publish.NewService(repo)
discoveryService := discovery.NewService(repo)
gatewayConsumerService := gatewayconsumer.NewService(repo)
gatewayPoller := poller.NewGatewayPackagePoller(gatewayConsumerService)
gatewayRuntime := poller.NewRuntime(gatewayPoller, time.Second)
// Wire MemoryRepository as admission's CandidateRepository
candidateRepo := &admissionMemoryRepoAdapter{repo: repo}
packageRepo := &admissionSupplyPackageAdapter{repo: repo}
runner := admission.NewHTTPTestRunner()
// Build test suites for known platforms (in real use, loaded from config)
suites := []admission.TestSuite{
admission.BuildTestSuiteForPlatform("openai", "https://api.openai.com", ""),
admission.BuildTestSuiteForPlatform("anthropic", "https://api.anthropic.com", ""),
}
admissionService := admission.NewService(candidateRepo, packageRepo, suites, runner)
return &Application{
Repo: repo,
ProbeService: probeService,
PublishService: publishService,
DiscoveryService: discoveryService,
GatewayConsumerService: gatewayConsumerService,
GatewayPoller: gatewayPoller,
GatewayRuntime: gatewayRuntime,
AdmissionService: admissionService,
Server: httpapi.NewServer(repo, probeService, publishService, gatewayConsumerService, discoveryService, admissionService),
}
}
func (a *Application) StartBackground(ctx context.Context) {
if a == nil || a.GatewayRuntime == nil {
return
}
a.GatewayRuntime.Start(ctx)
}
func (a *Application) StopBackground() {
if a == nil || a.GatewayRuntime == nil {
return
}
a.GatewayRuntime.Stop()
}
func (a *Application) IsInMemoryGatewayState() bool {
return a != nil && a.Repo != nil
}
// --- Adapters that bridge MemoryRepository to admission.Repository interfaces ---
// admissionMemoryRepoAdapter adapts MemoryRepository to admission.CandidateRepository
type admissionMemoryRepoAdapter struct {
repo *repository.MemoryRepository
}
func (a *admissionMemoryRepoAdapter) GetCandidateByIDContext(ctx context.Context, candidateID string) (admission.Candidate, bool) {
c, ok := a.repo.GetDiscoveryCandidateByIDContext(ctx, candidateID)
if !ok {
return admission.Candidate{}, false
}
return toAdmissionCandidate(c), true
}
func (a *admissionMemoryRepoAdapter) UpdateCandidateStatus(ctx context.Context, candidateID string, status admission.CandidateStatus, failureCode, failureSummary string) error {
return a.repo.UpdateCandidateStatus(ctx, candidateID, domain.DiscoveryCandidateStatus(status), failureCode, failureSummary)
}
func (a *admissionMemoryRepoAdapter) ListCandidatesByStatus(ctx context.Context, status admission.CandidateStatus) []admission.Candidate {
candidates := a.repo.ListDiscoveryCandidatesContext(ctx, domain.DiscoveryCandidateStatus(status))
result := make([]admission.Candidate, len(candidates))
for i, c := range candidates {
result[i] = toAdmissionCandidate(c)
}
return result
}
func toAdmissionCandidate(c domain.DiscoveryCandidate) admission.Candidate {
return admission.Candidate{
CandidateID: c.CandidateID,
AccountID: c.AccountID,
Platform: c.Platform,
Model: c.Model,
Status: admission.CandidateStatus(c.Status),
Source: c.Source,
ReasonCode: c.ReasonCode,
DiscoveredAt: c.DiscoveredAt,
UpdatedAt: c.UpdatedAt,
Version: c.Version,
}
}
// admissionSupplyPackageAdapter adapts MemoryRepository to admission.SupplyPackageRepository
type admissionSupplyPackageAdapter struct {
repo *repository.MemoryRepository
}
func (a *admissionSupplyPackageAdapter) UpsertDraftPackage(ctx context.Context, platform, model, source string) (int64, error) {
if existing, ok := a.repo.GetSupplyPackage(platform, model); ok {
return existing.PackageID, nil
}
pkg := domain.SupplyPackage{
Platform: platform,
Model: model,
Status: "draft",
Source: source,
}
a.repo.UpsertSupplyPackage(pkg)
if newPkg, ok := a.repo.GetSupplyPackage(platform, model); ok {
return newPkg.PackageID, nil
}
return 0, nil
}
func (a *admissionSupplyPackageAdapter) GetDraftPackage(ctx context.Context, platform, model string) (admission.DraftPackage, bool) {
pkg, ok := a.repo.GetSupplyPackage(platform, model)
if !ok {
return admission.DraftPackage{}, false
}
return admission.DraftPackage{
PackageID: pkg.PackageID,
Platform: pkg.Platform,
Model: pkg.Model,
Status: pkg.Status,
Source: pkg.Source,
}, true
}