Files
story/internal/extraction/import.go
T

90 lines
2.8 KiB
Go
Raw Normal View History

package extraction
import (
"context"
"data-terminal-cloud/internal/outbox"
"errors"
"github.com/jackc/pgx/v5"
)
const ImportStoryEvent = "memoir.import_requested"
func loadImport(ctx context.Context, q queryer, id string) (extractionSnapshot, string, error) {
var s extractionSnapshot
var status, date string
e := q.QueryRow(ctx, `SELECT ci.tenant_id::text,ci.subject_id::text,ci.status,to_char(ci.created_at AT TIME ZONE 'Asia/Shanghai','YYYY-MM-DD'),
sp.status='active' AND NOT EXISTS(SELECT 1 FROM privacy_request pr WHERE pr.subject_id=sp.id AND pr.request_type='DELETE')
AND NOT EXISTS(SELECT 1 FROM (VALUES('transcript_storage'),('story_extraction')) k(kind) WHERE NOT EXISTS(
SELECT 1 FROM consent_record cr WHERE cr.subject_id=sp.id AND cr.consent_type=k.kind AND cr.withdrawn_at IS NULL
AND cr.granted_at<=ci.created_at AND (cr.expires_at IS NULL OR cr.expires_at>now())))
FROM memoir_chat_import ci JOIN subject_profile sp ON sp.id=ci.subject_id WHERE ci.id=$1::uuid`, id).Scan(&s.tenantID, &s.subjectID, &status, &date, &s.storyExtractionAllowed)
if errors.Is(e, pgx.ErrNoRows) {
return s, "", nil
}
if e != nil {
return s, "", e
}
s.exists = true
s.status = status
rows, e := q.Query(ctx, `SELECT id::text,speaker,content FROM memoir_import_turn WHERE import_id=$1::uuid ORDER BY sequence_no`, id)
if e != nil {
return s, "", e
}
defer rows.Close()
for rows.Next() {
var t turn
if e = rows.Scan(&t.id, &t.speaker, &t.text); e != nil {
return s, "", e
}
t.publicID = t.id
s.turns = append(s.turns, t)
}
s.digest = snapshotDigest(s)
return s, date, rows.Err()
}
func (p *Processor) HandleImport(ctx context.Context, event outbox.Event) error {
s, date, e := loadImport(ctx, p.pool, event.AggregateID)
if e != nil {
return e
}
if !s.exists || s.status == "READY" {
return nil
}
if !s.storyExtractionAllowed {
return errors.New("import consent unavailable")
}
result, previous, e := p.generateEventsSafely(ctx, s, date)
if e != nil {
return e
}
tx, e := p.pool.Begin(ctx)
if e != nil {
return e
}
defer tx.Rollback(context.Background())
if e = lockStoryContext(ctx, tx, s, previous); e != nil {
return e
}
if _, e = tx.Exec(ctx, `SELECT id FROM memoir_chat_import WHERE id=$1::uuid FOR UPDATE`, event.AggregateID); e != nil {
return e
}
current, _, e := loadImport(ctx, tx, event.AggregateID)
if e != nil {
return e
}
if current.status == "READY" {
return tx.Commit(ctx)
}
if !current.storyExtractionAllowed || current.digest != s.digest {
return outbox.Retryable(ErrConversationContentChanged)
}
if e = storeEventStories(ctx, tx, "", "", event.AggregateID, 0, current, result, 1, previous); e != nil {
return e
}
_, e = tx.Exec(ctx, `UPDATE memoir_chat_import SET status='READY' WHERE id=$1::uuid`, event.AggregateID)
if e != nil {
return e
}
return tx.Commit(ctx)
}