thread-master/backend/internal/worker/job/refill_publish_inventory.go

135 lines
4.6 KiB
Go

package job
import (
"context"
"fmt"
"strings"
"haixun-backend/internal/library/publishschedule"
copydraftdomain "haixun-backend/internal/model/copy_draft/domain/usecase"
missiondomain "haixun-backend/internal/model/copy_mission/domain/usecase"
jobdom "haixun-backend/internal/model/job/domain/usecase"
inventorydomain "haixun-backend/internal/model/publish_inventory/domain/usecase"
pqdomain "haixun-backend/internal/model/publish_queue/domain/usecase"
)
type RefillPublishInventoryDeps struct {
Jobs jobdom.UseCase
PublishInventory inventorydomain.UseCase
PublishQueue pqdomain.UseCase
CopyMission missiondomain.UseCase
CopyDraft copydraftdomain.UseCase
}
func RegisterRefillPublishInventoryHandler(runner *Runner, deps RefillPublishInventoryDeps) {
if runner == nil {
return
}
runner.RegisterStepHandler("refill_publish_inventory", func(ctx context.Context, step StepContext) error {
return runRefillPublishInventory(ctx, step, deps)
})
}
func runRefillPublishInventory(ctx context.Context, step StepContext, deps RefillPublishInventoryDeps) error {
payload := step.Run.Payload
tenantID, ownerUID := runActorFromPayload(payload, step.Run)
accountID := stringField(payload, "account_id")
if tenantID == "" || ownerUID == "" || accountID == "" {
return fmt.Errorf("refill publish inventory payload missing tenant_id, owner_uid, or account_id")
}
if deps.PublishInventory == nil || deps.PublishQueue == nil {
return fmt.Errorf("publish inventory dependencies are not configured")
}
policy, err := deps.PublishInventory.Get(ctx, tenantID, ownerUID, accountID)
if err != nil {
return err
}
if !policy.Enabled {
return nil
}
health, err := deps.PublishQueue.ListPublishHealth(ctx, tenantID, ownerUID, accountID, 1, 1)
if err != nil {
return err
}
need := policy.LowStockThreshold - int(health.Summary.PendingScheduled)
if requested := intField(payload, "count"); requested > 0 {
need = requested
}
if need <= 0 {
return nil
}
if need > 12 {
need = 12
}
slots := make([]publishschedule.Slot, 0, len(policy.Slots))
for _, slot := range policy.Slots {
slots = append(slots, publishschedule.Slot{Weekday: slot.Weekday, Time: slot.Time})
}
times := publishschedule.BuildSchedule(0, policy.Timezone, slots, need)
for i := 0; i < need; i++ {
if err := step.Heartbeat(ctx); err != nil {
return err
}
ref := pickSourceRef(policy.SourceRefs, i)
text, personaID, missionID := refillText(ctx, deps, tenantID, ownerUID, ref, i)
createReq := pqdomain.CreateRequest{
TenantID: tenantID, OwnerUID: ownerUID, AccountID: accountID,
PersonaID: personaID, CopyMissionID: missionID, Text: text,
}
if i < len(times) {
createReq.ScheduledAt = times[i]
}
if deps.CopyDraft != nil && personaID != "" {
draft, err := deps.CopyDraft.Create(ctx, copydraftdomain.CreateRequest{
TenantID: tenantID, OwnerUID: ownerUID, PersonaID: personaID,
CopyMissionID: missionID, DraftType: "inventory-refill", SortOrder: i + 1,
Text: text, Rationale: "自動補庫存產生",
})
if err == nil && draft != nil {
createReq.CopyDraftID = draft.ID
}
}
item, err := deps.PublishQueue.Create(ctx, createReq)
if err != nil {
return err
}
if deps.CopyDraft != nil && createReq.CopyDraftID != "" {
_, _ = deps.CopyDraft.MarkScheduled(ctx, copydraftdomain.MarkScheduledRequest{
TenantID: tenantID, OwnerUID: ownerUID, PersonaID: personaID, DraftID: createReq.CopyDraftID, QueueID: item.ID,
})
}
}
return nil
}
func pickSourceRef(items []inventorydomain.SourceRef, idx int) inventorydomain.SourceRef {
if len(items) == 0 {
return inventorydomain.SourceRef{Type: "manual_seed", ManualSeed: "分享一個今天巡樓觀察到的重點"}
}
return items[idx%len(items)]
}
func refillText(ctx context.Context, deps RefillPublishInventoryDeps, tenantID, ownerUID string, ref inventorydomain.SourceRef, idx int) (string, string, string) {
personaID := strings.TrimSpace(ref.PersonaID)
missionID := strings.TrimSpace(ref.CopyMissionID)
seed := strings.TrimSpace(ref.ManualSeed)
if missionID != "" && personaID != "" && deps.CopyMission != nil {
if mission, err := deps.CopyMission.Get(ctx, tenantID, ownerUID, personaID, missionID); err == nil {
seed = strings.TrimSpace(firstNonEmpty(mission.Brief, mission.Label, mission.SeedQuery))
}
}
if seed == "" {
seed = "分享一個今天巡樓觀察到的重點"
}
return fmt.Sprintf("%s\n\n巡樓筆記 #%d", seed, idx+1), personaID, missionID
}
func firstNonEmpty(values ...string) string {
for _, value := range values {
if strings.TrimSpace(value) != "" {
return strings.TrimSpace(value)
}
}
return ""
}