|
- package draftsvc
-
- import (
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "strconv"
- "strings"
- "time"
-
- "qctextbuilder/internal/domain"
- "qctextbuilder/internal/store"
- )
-
- type UpsertDraftRequest struct {
- DraftID string `json:"draftId,omitempty"`
- TemplateID int64 `json:"templateId"`
- ManifestID string `json:"manifestId,omitempty"`
- Source string `json:"source,omitempty"`
- RequestName string `json:"requestName,omitempty"`
- GlobalData map[string]any `json:"globalData"`
- FieldValues map[string]string `json:"fieldValues"`
- Status string `json:"status,omitempty"`
- Notes string `json:"notes,omitempty"`
- }
-
- type Service struct {
- drafts store.DraftStore
- templates store.TemplateStore
- manifests store.ManifestStore
- }
-
- func New(draftStore store.DraftStore, templateStore store.TemplateStore, manifestStore store.ManifestStore) *Service {
- return &Service{
- drafts: draftStore,
- templates: templateStore,
- manifests: manifestStore,
- }
- }
-
- func (s *Service) SaveDraft(ctx context.Context, req UpsertDraftRequest) (*domain.BuildDraft, error) {
- templateID := req.TemplateID
- if strings.TrimSpace(req.DraftID) != "" {
- existing, err := s.drafts.GetDraftByID(ctx, strings.TrimSpace(req.DraftID))
- if err != nil {
- return nil, fmt.Errorf("get draft: %w", err)
- }
- if templateID == 0 {
- templateID = existing.TemplateID
- }
- }
- if templateID <= 0 {
- return nil, errors.New("templateId is required")
- }
-
- template, err := s.templates.GetTemplateByID(ctx, templateID)
- if err != nil {
- return nil, fmt.Errorf("get template: %w", err)
- }
- if !template.IsAITemplate {
- return nil, errors.New("only ai templates are allowed")
- }
-
- manifestID := strings.TrimSpace(req.ManifestID)
- if manifestID == "" {
- manifest, err := s.manifests.GetActiveManifestByTemplateID(ctx, templateID)
- if err != nil {
- return nil, fmt.Errorf("get active manifest: %w", err)
- }
- manifestID = manifest.ID
- }
-
- globalDataJSON, err := json.Marshal(req.GlobalData)
- if err != nil {
- return nil, errors.New("globalData is invalid JSON")
- }
- fieldValuesJSON, err := json.Marshal(req.FieldValues)
- if err != nil {
- return nil, errors.New("fieldValues is invalid JSON")
- }
-
- now := time.Now().UTC()
- source := defaultString(req.Source, "ui")
- status := normalizeDraftStatus(req.Status)
- if status == "" {
- status = "draft"
- }
-
- draft := domain.BuildDraft{
- ID: strings.TrimSpace(req.DraftID),
- TemplateID: templateID,
- ManifestID: manifestID,
- Source: source,
- RequestName: strings.TrimSpace(req.RequestName),
- GlobalDataJSON: globalDataJSON,
- FieldValuesJSON: fieldValuesJSON,
- Status: status,
- Notes: strings.TrimSpace(req.Notes),
- UpdatedAt: now,
- }
- if draft.ID == "" {
- draft.ID = strconv.FormatInt(time.Now().UnixNano(), 10)
- draft.CreatedAt = now
- if err := s.drafts.CreateDraft(ctx, draft); err != nil {
- return nil, fmt.Errorf("create draft: %w", err)
- }
- } else {
- existing, err := s.drafts.GetDraftByID(ctx, draft.ID)
- if err != nil {
- return nil, fmt.Errorf("get draft: %w", err)
- }
- draft.CreatedAt = existing.CreatedAt
- if err := s.drafts.UpdateDraft(ctx, draft); err != nil {
- return nil, fmt.Errorf("update draft: %w", err)
- }
- }
-
- return s.drafts.GetDraftByID(ctx, draft.ID)
- }
-
- func (s *Service) GetDraft(ctx context.Context, draftID string) (*domain.BuildDraft, error) {
- return s.drafts.GetDraftByID(ctx, strings.TrimSpace(draftID))
- }
-
- func (s *Service) ListDrafts(ctx context.Context, limit int) ([]domain.BuildDraft, error) {
- if limit <= 0 {
- limit = 50
- }
- return s.drafts.ListDrafts(ctx, limit)
- }
-
- func normalizeDraftStatus(status string) string {
- switch strings.ToLower(strings.TrimSpace(status)) {
- case "":
- return ""
- case "draft", "reviewed", "submitted":
- return strings.ToLower(strings.TrimSpace(status))
- default:
- return "draft"
- }
- }
-
- func defaultString(value, fallback string) string {
- if strings.TrimSpace(value) == "" {
- return fallback
- }
- return strings.TrimSpace(value)
- }
|