fix: unblock first growth report generation

This commit is contained in:
2026-08-21 07:49:04 +08:00
parent e6afaa7a10
commit 927319807b
7 changed files with 67 additions and 14 deletions

View File

@@ -95,7 +95,7 @@ func (r *Repository) CreateOrReuse(ctx context.Context, in CreateRecord, beforeI
return Record{}, false, fmt.Errorf("locking growth report refresh: %w", err)
}
var hasAISummary bool
if err := tx.QueryRow(ctx, `SELECT EXISTS(SELECT 1 FROM growth_reports WHERE merchant_id=$1 AND terminal_source_key IS NOT DISTINCT FROM $2::text AND status='READY' AND ai_response IS NOT NULL)`, in.MerchantID, nilIfEmpty(in.TerminalID)).Scan(&hasAISummary); err != nil {
if err := tx.QueryRow(ctx, `SELECT EXISTS(SELECT 1 FROM growth_reports WHERE merchant_id=$1 AND terminal_source_key IS NOT DISTINCT FROM $2::text AND analytics_version=$3 AND status='READY' AND ai_response IS NOT NULL)`, in.MerchantID, nilIfEmpty(in.TerminalID), in.AnalyticsVersion).Scan(&hasAISummary); err != nil {
return Record{}, false, fmt.Errorf("checking prior AI summary: %w", err)
}
var refreshUsed bool
@@ -145,6 +145,14 @@ func (r *Repository) Latest(ctx context.Context, merchantID int64, terminalID, a
return scanRecord(r.pool.QueryRow(ctx, reportSelect+` WHERE merchant_id=$1 AND terminal_source_key IS NOT DISTINCT FROM $2::text AND analytics_version=$3 AND status IN ('READY','PARTIAL') ORDER BY created_at DESC LIMIT 1`, merchantID, nilIfEmpty(terminalID), analyticsVersion))
}
func (r *Repository) HasAISummary(ctx context.Context, merchantID int64, terminalID, analyticsVersion string) (bool, error) {
var exists bool
if err := r.pool.QueryRow(ctx, `SELECT EXISTS(SELECT 1 FROM growth_reports WHERE merchant_id=$1 AND terminal_source_key IS NOT DISTINCT FROM $2::text AND analytics_version=$3 AND status='READY' AND ai_response IS NOT NULL)`, merchantID, nilIfEmpty(terminalID), analyticsVersion).Scan(&exists); err != nil {
return false, fmt.Errorf("checking current AI summary: %w", err)
}
return exists, nil
}
func (r *Repository) Claim(ctx context.Context) (Record, error) {
// PostgreSQL is the durable queue and coordination authority. SKIP LOCKED plus
// the state transition prevents two worker processes from generating one row.
@@ -263,13 +271,13 @@ func nilIfEmpty(value string) any {
return value
}
func (r *Repository) NextRefreshAt(ctx context.Context, merchantID int64, terminalID string, now time.Time) (time.Time, error) {
func (r *Repository) NextRefreshAt(ctx context.Context, merchantID int64, terminalID, analyticsVersion string, now time.Time) (time.Time, error) {
var hasAISummary bool
var last *time.Time
if err := r.pool.QueryRow(ctx, `SELECT
EXISTS(SELECT 1 FROM growth_reports WHERE merchant_id=$1 AND terminal_source_key IS NOT DISTINCT FROM $2::text AND status='READY' AND ai_response IS NOT NULL),
EXISTS(SELECT 1 FROM growth_reports WHERE merchant_id=$1 AND terminal_source_key IS NOT DISTINCT FROM $2::text AND analytics_version=$3 AND status='READY' AND ai_response IS NOT NULL),
max(created_at) FILTER (WHERE manual_refresh=true)
FROM growth_reports WHERE merchant_id=$1 AND terminal_source_key IS NOT DISTINCT FROM $2::text`, merchantID, nilIfEmpty(terminalID)).Scan(&hasAISummary, &last); err != nil {
FROM growth_reports WHERE merchant_id=$1 AND terminal_source_key IS NOT DISTINCT FROM $2::text AND analytics_version=$3`, merchantID, nilIfEmpty(terminalID), analyticsVersion).Scan(&hasAISummary, &last); err != nil {
return time.Time{}, fmt.Errorf("loading refresh quota: %w", err)
}
if !hasAISummary || last == nil {