403 lines
15 KiB
Go
403 lines
15 KiB
Go
|
|
package svc
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"os"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"haixun-backend/internal/config"
|
||
|
|
libmongo "haixun-backend/internal/library/mongo"
|
||
|
|
libredis "haixun-backend/internal/library/redis"
|
||
|
|
"haixun-backend/internal/library/validate"
|
||
|
|
"haixun-backend/internal/middleware"
|
||
|
|
aisettings "haixun-backend/internal/model/ai/usecase"
|
||
|
|
authrepodomain "haixun-backend/internal/model/auth/domain/repository"
|
||
|
|
authdomain "haixun-backend/internal/model/auth/domain/usecase"
|
||
|
|
authrepo "haixun-backend/internal/model/auth/repository"
|
||
|
|
authuc "haixun-backend/internal/model/auth/usecase"
|
||
|
|
branddomain "haixun-backend/internal/model/brand/domain/usecase"
|
||
|
|
brandrepo "haixun-backend/internal/model/brand/repository"
|
||
|
|
brandusecase "haixun-backend/internal/model/brand/usecase"
|
||
|
|
cmatrixdomain "haixun-backend/internal/model/content_matrix/domain/usecase"
|
||
|
|
cmatrixrepo "haixun-backend/internal/model/content_matrix/repository"
|
||
|
|
cmatrixusecase "haixun-backend/internal/model/content_matrix/usecase"
|
||
|
|
copydraftdomain "haixun-backend/internal/model/copy_draft/domain/usecase"
|
||
|
|
copydraftrepo "haixun-backend/internal/model/copy_draft/repository"
|
||
|
|
copydraftusecase "haixun-backend/internal/model/copy_draft/usecase"
|
||
|
|
copymissiondomain "haixun-backend/internal/model/copy_mission/domain/usecase"
|
||
|
|
copymissionrepo "haixun-backend/internal/model/copy_mission/repository"
|
||
|
|
copymissionusecase "haixun-backend/internal/model/copy_mission/usecase"
|
||
|
|
jobrepo "haixun-backend/internal/model/job/repository"
|
||
|
|
jobusecase "haixun-backend/internal/model/job/usecase"
|
||
|
|
kgdomain "haixun-backend/internal/model/knowledge_graph/domain/usecase"
|
||
|
|
kgrepo "haixun-backend/internal/model/knowledge_graph/repository"
|
||
|
|
kgusecase "haixun-backend/internal/model/knowledge_graph/usecase"
|
||
|
|
memberdomain "haixun-backend/internal/model/member/domain/usecase"
|
||
|
|
memberrepo "haixun-backend/internal/model/member/repository"
|
||
|
|
memberuc "haixun-backend/internal/model/member/usecase"
|
||
|
|
outreachdraftdomain "haixun-backend/internal/model/outreach_draft/domain/usecase"
|
||
|
|
outreachdraftrepo "haixun-backend/internal/model/outreach_draft/repository"
|
||
|
|
outreachdraftusecase "haixun-backend/internal/model/outreach_draft/usecase"
|
||
|
|
permissiondomain "haixun-backend/internal/model/permission/domain/usecase"
|
||
|
|
permissionrepo "haixun-backend/internal/model/permission/repository"
|
||
|
|
permissionuc "haixun-backend/internal/model/permission/usecase"
|
||
|
|
personadomain "haixun-backend/internal/model/persona/domain/usecase"
|
||
|
|
personarepo "haixun-backend/internal/model/persona/repository"
|
||
|
|
personausecase "haixun-backend/internal/model/persona/usecase"
|
||
|
|
placementusecase "haixun-backend/internal/model/placement/usecase"
|
||
|
|
placementtopicdomain "haixun-backend/internal/model/placement_topic/domain/usecase"
|
||
|
|
placementtopicrepo "haixun-backend/internal/model/placement_topic/repository"
|
||
|
|
placementtopicusecase "haixun-backend/internal/model/placement_topic/usecase"
|
||
|
|
scanpostdomain "haixun-backend/internal/model/scan_post/domain/usecase"
|
||
|
|
scanpostrepo "haixun-backend/internal/model/scan_post/repository"
|
||
|
|
scanpostusecase "haixun-backend/internal/model/scan_post/usecase"
|
||
|
|
settingrepo "haixun-backend/internal/model/setting/repository"
|
||
|
|
settingusecase "haixun-backend/internal/model/setting/usecase"
|
||
|
|
threadsaccountdomain "haixun-backend/internal/model/threads_account/domain/usecase"
|
||
|
|
threadsaccountrepo "haixun-backend/internal/model/threads_account/repository"
|
||
|
|
threadsaccountusecase "haixun-backend/internal/model/threads_account/usecase"
|
||
|
|
jobworker "haixun-backend/internal/worker/job"
|
||
|
|
|
||
|
|
goredis "github.com/redis/go-redis/v9"
|
||
|
|
"github.com/zeromicro/go-zero/rest"
|
||
|
|
)
|
||
|
|
|
||
|
|
type ServiceContext struct {
|
||
|
|
Config config.Config
|
||
|
|
Validator *validate.Validate
|
||
|
|
Mongo *libmongo.Client
|
||
|
|
Redis *goredis.Client
|
||
|
|
|
||
|
|
Setting settingusecase.UseCase
|
||
|
|
AI aisettings.UseCase
|
||
|
|
Job jobusecase.UseCase
|
||
|
|
AuthToken authdomain.TokenUseCase
|
||
|
|
Member memberdomain.UseCase
|
||
|
|
Permission permissiondomain.UseCase
|
||
|
|
Persona personadomain.UseCase
|
||
|
|
CopyMission copymissiondomain.UseCase
|
||
|
|
Brand branddomain.UseCase
|
||
|
|
PlacementTopic placementtopicdomain.UseCase
|
||
|
|
KnowledgeGraph kgdomain.UseCase
|
||
|
|
Placement placementusecase.UseCase
|
||
|
|
ScanPost scanpostdomain.UseCase
|
||
|
|
OutreachDraft outreachdraftdomain.UseCase
|
||
|
|
ContentMatrix cmatrixdomain.UseCase
|
||
|
|
CopyDraft copydraftdomain.UseCase
|
||
|
|
ThreadsAccount threadsaccountdomain.UseCase
|
||
|
|
|
||
|
|
// Middlewares mounted per route group via generate/api `middleware:` directive.
|
||
|
|
AuthJWT rest.Middleware
|
||
|
|
MemberAuth rest.Middleware
|
||
|
|
WorkerSecret rest.Middleware
|
||
|
|
|
||
|
|
stopWorker context.CancelFunc
|
||
|
|
stopScheduler context.CancelFunc
|
||
|
|
stopReaper context.CancelFunc
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewServiceContext(c config.Config) *ServiceContext {
|
||
|
|
ctx := context.Background()
|
||
|
|
mongoClient, err := libmongo.NewClient(ctx, c.Mongo)
|
||
|
|
if err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
redisClient := libredis.NewClient(c.Redis)
|
||
|
|
|
||
|
|
settingRepository := settingrepo.NewMongoRepository(mongoClient.Database())
|
||
|
|
if err := settingRepository.EnsureIndexes(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
settingUseCase := settingusecase.NewUseCase(settingRepository)
|
||
|
|
|
||
|
|
var tokenRevokeStore authrepodomain.TokenRevokeStore
|
||
|
|
if redisClient != nil {
|
||
|
|
tokenRevokeStore = authrepo.NewRedisTokenRevokeStore(redisClient)
|
||
|
|
}
|
||
|
|
authTokenUseCase := authuc.NewTokenUseCase(c.Auth, tokenRevokeStore)
|
||
|
|
|
||
|
|
memberRepository := memberrepo.NewMongoRepository(mongoClient.Database())
|
||
|
|
if err := memberRepository.EnsureIndexes(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
memberUseCase := memberuc.NewUseCase(memberRepository, authTokenUseCase)
|
||
|
|
|
||
|
|
permissionRepository := permissionrepo.NewMongoPermissionRepository(mongoClient.Database())
|
||
|
|
rolePermissionRepository := permissionrepo.NewMongoRolePermissionRepository(mongoClient.Database())
|
||
|
|
if err := permissionRepository.EnsureIndexes(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
if err := rolePermissionRepository.EnsureIndexes(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
permissionUseCase := permissionuc.NewUseCase(permissionRepository, rolePermissionRepository)
|
||
|
|
if err := permissionUseCase.EnsureDefaultPermissions(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
if err := permissionUseCase.EnsureDefaultRolePermissions(ctx, "default"); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
|
||
|
|
jobTemplateRepository := jobrepo.NewMongoTemplateRepository(mongoClient.Database())
|
||
|
|
jobRunRepository := jobrepo.NewMongoRunRepository(mongoClient.Database())
|
||
|
|
jobScheduleRepository := jobrepo.NewMongoScheduleRepository(mongoClient.Database())
|
||
|
|
jobEventRepository := jobrepo.NewMongoEventRepository(mongoClient.Database())
|
||
|
|
jobQueueRepository := jobrepo.NewRedisQueueRepository(redisClient)
|
||
|
|
if err := jobTemplateRepository.EnsureIndexes(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
if err := jobRunRepository.EnsureIndexes(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
if err := jobScheduleRepository.EnsureIndexes(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
if err := jobEventRepository.EnsureIndexes(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
|
||
|
|
jobUseCase := jobusecase.NewUseCase(jobTemplateRepository, jobRunRepository, jobScheduleRepository, jobEventRepository, jobQueueRepository)
|
||
|
|
if err := jobUseCase.EnsureDemoTemplate(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
if err := jobUseCase.EnsureStyle8DTemplate(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
if err := jobUseCase.EnsureExpandGraphTemplate(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
if err := jobUseCase.EnsurePlacementScanTemplate(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
if err := jobUseCase.EnsureScanViralTemplate(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
if err := jobUseCase.EnsureAnalyzeCopyMissionTemplate(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
if err := jobUseCase.EnsureGenerateCopyMatrixTemplate(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
if err := jobUseCase.EnsureGenerateCopyDraftTemplate(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
|
||
|
|
copyMissionRepository := copymissionrepo.NewMongoRepository(mongoClient.Database())
|
||
|
|
if err := copyMissionRepository.EnsureIndexes(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
copyMissionUseCase := copymissionusecase.NewUseCase(copyMissionRepository)
|
||
|
|
|
||
|
|
copyDraftRepository := copydraftrepo.NewMongoRepository(mongoClient.Database())
|
||
|
|
if err := copyDraftRepository.EnsureIndexes(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
copyDraftUseCase := copydraftusecase.NewUseCase(copyDraftRepository)
|
||
|
|
|
||
|
|
scanPostRepository := scanpostrepo.NewMongoRepository(mongoClient.Database())
|
||
|
|
if err := scanPostRepository.EnsureIndexes(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
scanPostUseCase := scanpostusecase.NewUseCase(scanPostRepository)
|
||
|
|
|
||
|
|
outreachDraftRepository := outreachdraftrepo.NewMongoRepository(mongoClient.Database())
|
||
|
|
if err := outreachDraftRepository.EnsureIndexes(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
outreachDraftUseCase := outreachdraftusecase.NewUseCase(outreachDraftRepository)
|
||
|
|
|
||
|
|
contentMatrixRepository := cmatrixrepo.NewMongoRepository(mongoClient.Database())
|
||
|
|
if err := contentMatrixRepository.EnsureIndexes(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
contentMatrixUseCase := cmatrixusecase.NewUseCase(contentMatrixRepository)
|
||
|
|
|
||
|
|
knowledgeGraphRepository := kgrepo.NewMongoRepository(mongoClient.Database())
|
||
|
|
if err := knowledgeGraphRepository.EnsureIndexes(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
knowledgeGraphUseCase := kgusecase.NewUseCase(knowledgeGraphRepository)
|
||
|
|
|
||
|
|
personaRepository := personarepo.NewMongoRepository(mongoClient.Database())
|
||
|
|
if err := personaRepository.EnsureIndexes(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
personaUseCase := personausecase.NewUseCase(personaRepository)
|
||
|
|
|
||
|
|
brandRepository := brandrepo.NewMongoRepository(mongoClient.Database())
|
||
|
|
if err := brandRepository.EnsureIndexes(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
brandUseCase := brandusecase.NewUseCase(brandRepository)
|
||
|
|
placementTopicRepository := placementtopicrepo.NewMongoRepository(mongoClient.Database())
|
||
|
|
if err := placementTopicRepository.EnsureIndexes(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
placementTopicUseCase := placementtopicusecase.NewUseCase(
|
||
|
|
placementTopicRepository,
|
||
|
|
brandRepository,
|
||
|
|
knowledgeGraphUseCase,
|
||
|
|
scanPostRepository,
|
||
|
|
)
|
||
|
|
threadsAccountRepository := threadsaccountrepo.NewMongoRepository(mongoClient.Database())
|
||
|
|
threadsAccountSecretsRepository := threadsaccountrepo.NewSecretsMongoRepository(mongoClient.Database())
|
||
|
|
if err := threadsAccountRepository.EnsureIndexes(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
if err := threadsAccountSecretsRepository.EnsureIndexes(ctx); err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
threadsAccountUseCase := threadsaccountusecase.NewUseCase(
|
||
|
|
threadsAccountRepository,
|
||
|
|
threadsAccountSecretsRepository,
|
||
|
|
memberUseCase,
|
||
|
|
settingUseCase,
|
||
|
|
personaUseCase,
|
||
|
|
)
|
||
|
|
|
||
|
|
placementUseCase := placementusecase.NewUseCase(settingUseCase)
|
||
|
|
|
||
|
|
sc := &ServiceContext{
|
||
|
|
Config: c,
|
||
|
|
Validator: validate.New(),
|
||
|
|
Mongo: mongoClient,
|
||
|
|
Redis: redisClient,
|
||
|
|
Setting: settingUseCase,
|
||
|
|
AI: aisettings.NewUseCase(),
|
||
|
|
Job: jobUseCase,
|
||
|
|
AuthToken: authTokenUseCase,
|
||
|
|
Member: memberUseCase,
|
||
|
|
Permission: permissionUseCase,
|
||
|
|
Persona: personaUseCase,
|
||
|
|
CopyMission: copyMissionUseCase,
|
||
|
|
Brand: brandUseCase,
|
||
|
|
PlacementTopic: placementTopicUseCase,
|
||
|
|
KnowledgeGraph: knowledgeGraphUseCase,
|
||
|
|
Placement: placementUseCase,
|
||
|
|
ScanPost: scanPostUseCase,
|
||
|
|
OutreachDraft: outreachDraftUseCase,
|
||
|
|
ContentMatrix: contentMatrixUseCase,
|
||
|
|
CopyDraft: copyDraftUseCase,
|
||
|
|
ThreadsAccount: threadsAccountUseCase,
|
||
|
|
}
|
||
|
|
|
||
|
|
hostname, _ := os.Hostname()
|
||
|
|
if c.JobWorker.Enabled && redisClient != nil {
|
||
|
|
workerType := c.JobWorker.WorkerType
|
||
|
|
if workerType == "" {
|
||
|
|
workerType = "go"
|
||
|
|
}
|
||
|
|
workerID := c.JobWorker.WorkerID
|
||
|
|
if workerID == "" {
|
||
|
|
workerID = fmt.Sprintf("%s-%s-worker", hostname, workerType)
|
||
|
|
}
|
||
|
|
workerCtx, cancel := context.WithCancel(context.Background())
|
||
|
|
sc.stopWorker = cancel
|
||
|
|
runner := jobworker.NewRunner(workerID, workerType, jobUseCase)
|
||
|
|
jobworker.RegisterExpandGraphHandler(runner, jobworker.ExpandGraphDeps{
|
||
|
|
Jobs: jobUseCase,
|
||
|
|
Brand: brandUseCase,
|
||
|
|
PlacementTopic: placementTopicUseCase,
|
||
|
|
KnowledgeGraph: knowledgeGraphUseCase,
|
||
|
|
ThreadsAccount: threadsAccountUseCase,
|
||
|
|
Placement: placementUseCase,
|
||
|
|
AI: sc.AI,
|
||
|
|
})
|
||
|
|
jobworker.RegisterScanPlacementHandler(runner, jobworker.ScanPlacementDeps{
|
||
|
|
Jobs: jobUseCase,
|
||
|
|
Brand: brandUseCase,
|
||
|
|
PlacementTopic: placementTopicUseCase,
|
||
|
|
KnowledgeGraph: knowledgeGraphUseCase,
|
||
|
|
ScanPost: scanPostUseCase,
|
||
|
|
ThreadsAccount: threadsAccountUseCase,
|
||
|
|
Placement: placementUseCase,
|
||
|
|
})
|
||
|
|
jobworker.RegisterScanViralHandler(runner, jobworker.ScanViralDeps{
|
||
|
|
Jobs: jobUseCase,
|
||
|
|
CopyMission: copyMissionUseCase,
|
||
|
|
Persona: personaUseCase,
|
||
|
|
ScanPost: scanPostUseCase,
|
||
|
|
CopyDraft: copyDraftUseCase,
|
||
|
|
ThreadsAccount: threadsAccountUseCase,
|
||
|
|
Placement: placementUseCase,
|
||
|
|
AI: sc.AI,
|
||
|
|
})
|
||
|
|
jobworker.RegisterAnalyzeCopyMissionHandler(runner, jobworker.AnalyzeCopyMissionDeps{
|
||
|
|
Jobs: jobUseCase,
|
||
|
|
CopyMission: copyMissionUseCase,
|
||
|
|
Persona: personaUseCase,
|
||
|
|
ThreadsAccount: threadsAccountUseCase,
|
||
|
|
Placement: placementUseCase,
|
||
|
|
AI: sc.AI,
|
||
|
|
})
|
||
|
|
jobworker.RegisterGenerateCopyMatrixHandler(runner, jobworker.GenerateCopyMatrixDeps{
|
||
|
|
Jobs: jobUseCase,
|
||
|
|
CopyMission: copyMissionUseCase,
|
||
|
|
Persona: personaUseCase,
|
||
|
|
ScanPost: scanPostUseCase,
|
||
|
|
CopyDraft: copyDraftUseCase,
|
||
|
|
ThreadsAccount: threadsAccountUseCase,
|
||
|
|
AI: sc.AI,
|
||
|
|
})
|
||
|
|
jobworker.RegisterGenerateCopyDraftHandler(runner, jobworker.GenerateCopyDraftDeps{
|
||
|
|
Jobs: jobUseCase,
|
||
|
|
CopyMission: copyMissionUseCase,
|
||
|
|
Persona: personaUseCase,
|
||
|
|
ScanPost: scanPostUseCase,
|
||
|
|
CopyDraft: copyDraftUseCase,
|
||
|
|
ThreadsAccount: threadsAccountUseCase,
|
||
|
|
AI: sc.AI,
|
||
|
|
})
|
||
|
|
go runner.Start(workerCtx)
|
||
|
|
}
|
||
|
|
|
||
|
|
if c.JobScheduler.Enabled && redisClient != nil {
|
||
|
|
schedulerHolder := fmt.Sprintf("%s-scheduler", hostname)
|
||
|
|
interval := time.Duration(c.JobScheduler.IntervalSeconds) * time.Second
|
||
|
|
if interval <= 0 {
|
||
|
|
interval = time.Minute
|
||
|
|
}
|
||
|
|
schedulerCtx, cancel := context.WithCancel(context.Background())
|
||
|
|
sc.stopScheduler = cancel
|
||
|
|
scheduler := jobworker.NewScheduler(schedulerHolder, jobUseCase)
|
||
|
|
go scheduler.Start(schedulerCtx, interval)
|
||
|
|
}
|
||
|
|
|
||
|
|
if c.JobReaper.Enabled && redisClient != nil {
|
||
|
|
interval := time.Duration(c.JobReaper.IntervalSeconds) * time.Second
|
||
|
|
if interval <= 0 {
|
||
|
|
interval = 30 * time.Second
|
||
|
|
}
|
||
|
|
reaperCtx, cancel := context.WithCancel(context.Background())
|
||
|
|
sc.stopReaper = cancel
|
||
|
|
reaper := jobworker.NewReaper(jobUseCase)
|
||
|
|
go reaper.Start(reaperCtx, interval)
|
||
|
|
}
|
||
|
|
|
||
|
|
sc.AuthJWT = middleware.NewAuthJWTMiddleware(sc.AuthToken, sc.Config.Auth).Handle
|
||
|
|
sc.MemberAuth = middleware.NewMemberAuthMiddleware(sc.AuthToken, sc.Config.Auth).Handle
|
||
|
|
sc.WorkerSecret = middleware.NewWorkerSecretMiddleware(sc.Config.InternalWorker).Handle
|
||
|
|
|
||
|
|
return sc
|
||
|
|
}
|
||
|
|
|
||
|
|
func (sc *ServiceContext) Close(ctx context.Context) {
|
||
|
|
if sc == nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if sc.stopWorker != nil {
|
||
|
|
sc.stopWorker()
|
||
|
|
}
|
||
|
|
if sc.stopScheduler != nil {
|
||
|
|
sc.stopScheduler()
|
||
|
|
}
|
||
|
|
if sc.stopReaper != nil {
|
||
|
|
sc.stopReaper()
|
||
|
|
}
|
||
|
|
_ = sc.Mongo.Close(ctx)
|
||
|
|
if sc.Redis != nil {
|
||
|
|
_ = sc.Redis.Close()
|
||
|
|
}
|
||
|
|
}
|