This commit is contained in:
2026-08-17 01:30:28 +02:00
parent c2429873b3
commit 0dceb3a404
18 changed files with 1021 additions and 219 deletions
+99 -58
View File
@@ -8,7 +8,9 @@ import (
"regexp"
"strings"
"github.com/descrybe/descrybe-v2/apps/api/internal/billing"
"github.com/descrybe/descrybe-v2/apps/api/internal/catalog"
"github.com/descrybe/descrybe-v2/apps/api/internal/company"
"github.com/descrybe/descrybe-v2/apps/api/internal/eprel"
"github.com/google/uuid"
)
@@ -209,6 +211,11 @@ func (p *Pipeline) LoadV1ProcessJobItems(ctx context.Context, companyID, jobID u
}
allowedAttrs := loadCompanyAttributeKeySet(ctx, p, companyID)
categoryNames := loadCompanyCategoryNameMap(ctx, p, companyID)
omitSEOMeta := companyOmitsSEOMeta(ctx, p, companyID)
language := ""
if p != nil {
language = company.LoadLanguage(ctx, p.Pool, companyID)
}
rows, err := p.Pool.Query(ctx, `
SELECT
COALESCE(r.gtin, p.product_id, '') AS ean,
@@ -356,49 +363,53 @@ func (p *Pipeline) LoadV1ProcessJobItems(ctx context.Context, companyID, jobID u
}
}
metaTitleOut := nullIfEmptyPtr(metaTitle)
metaDescOut := nullIfEmptyPtr(metaDesc)
if s, ok := metaTitleOut.(string); ok && isPoisonedMetaTitle(s) {
metaTitleOut = nil
// Poisoned title refresh: also drop empty/weak/leakage stub meta_description.
if ds, ok := metaDescOut.(string); ok && (ds == "" || isWeakPriorEnhanceDescription(ds, titleStr) || isPromptLeakageTitle(ds)) {
titleStr = ensureReadableTitleSpacing(titleStr)
var metaTitleOut, metaDescOut any
if !omitSEOMeta {
metaTitleOut = nullIfEmptyPtr(metaTitle)
metaDescOut = nullIfEmptyPtr(metaDesc)
if s, ok := metaTitleOut.(string); ok && isPoisonedMetaTitle(s) {
metaTitleOut = nil
if ds, ok := metaDescOut.(string); ok && (ds == "" || isWeakPriorEnhanceDescription(ds, titleStr) || isPromptLeakageTitle(ds)) {
metaDescOut = nil
}
}
if ds, ok := metaDescOut.(string); ok && isPromptLeakageTitle(ds) {
metaDescOut = nil
}
}
if ds, ok := metaDescOut.(string); ok && isPromptLeakageTitle(ds) {
metaDescOut = nil
}
catLabel := catNameStr
if catLabel == "" {
catLabel = catStr
}
if (metaTitleOut == nil || metaDescOut == nil) && (titleStr != "" || descOut != "" || catLabel != "") {
synthTitle, synthDesc := fillMetaFromResult(StepResult{
Name: titleStr,
ProcessedName: titleStr,
Category: catStr,
CategoryName: catNameStr,
Description: descOut,
ProcessedDescription: descOut,
Attributes: attrs,
ProcessedAttributes: attrs,
})
if metaTitleOut == nil {
if synthTitle != "" {
metaTitleOut = synthTitle
} else {
metaTitleOut = nullIfEmptyPtr(title)
}
catLabel := catNameStr
if catLabel == "" {
catLabel = catStr
}
if metaDescOut == nil {
if synthDesc != "" {
metaDescOut = synthDesc
} else if descOut != "" {
metaDescOut = truncateMetaDescription(v1PlainDescription(descOut), v1MetaDescriptionMaxChars)
if (metaTitleOut == nil || metaDescOut == nil) && (titleStr != "" || descOut != "" || catLabel != "") {
synthTitle, synthDesc := fillMetaFromResult(StepResult{
Name: titleStr,
ProcessedName: titleStr,
Category: catStr,
CategoryName: catNameStr,
Description: descOut,
ProcessedDescription: descOut,
Attributes: attrs,
ProcessedAttributes: attrs,
})
if metaTitleOut == nil {
if synthTitle != "" {
metaTitleOut = synthTitle
} else {
metaTitleOut = nullIfEmptyPtr(title)
}
}
if metaDescOut == nil {
if synthDesc != "" {
metaDescOut = synthDesc
} else if descOut != "" {
metaDescOut = truncateMetaDescription(v1PlainDescription(descOut), v1MetaDescriptionMaxChars)
}
}
} else if metaTitleOut == nil {
metaTitleOut = nullIfEmptyPtr(title)
}
} else if metaTitleOut == nil {
metaTitleOut = nullIfEmptyPtr(title)
}
var description any
@@ -407,32 +418,37 @@ func (p *Pipeline) LoadV1ProcessJobItems(ctx context.Context, companyID, jobID u
} else {
description = nil
}
var catOut, catNameOut any
if catStr != "" {
catOut = catStr
}
// ASSUMPTION: V1 `category` is the display name; `category_id` is unique_id
// (additive). `category_name` mirrors the display name for dual-mode clients.
var catNameOut, catIDOut any
if catNameStr != "" {
catNameOut = catNameStr
}
if catStr != "" {
catIDOut = catStr
}
var titleOut any
if titleStr != "" {
titleOut = titleStr
}
item := V1ProcessJobItem{
"ean": ean,
"status": MapV1JobItemStatus(itemStatus, true),
"category": catOut,
"category_name": catNameOut,
"title": titleOut,
"name": titleOut,
"meta_title": metaTitleOut,
"meta_description": metaDescOut,
"description": description,
"attributes": nil,
"main_image": nil,
"more_images": nil,
"eprel": eprelVal,
"ean": ean,
"status": MapV1JobItemStatus(itemStatus, true),
"category": catNameOut,
"category_id": catIDOut,
"category_name": catNameOut,
"title": titleOut,
"name": titleOut,
"description": description,
"attributes": nil,
"main_image": nil,
"more_images": nil,
"eprel": eprelVal,
}
if !omitSEOMeta {
item["meta_title"] = metaTitleOut
item["meta_description"] = metaDescOut
}
applyV1ProcessItemIDs(item, processedID, rawProductID)
if itemError != nil && *itemError != "" {
@@ -447,7 +463,11 @@ func (p *Pipeline) LoadV1ProcessJobItems(ctx context.Context, companyID, jobID u
if len(more) > 0 {
item["more_images"] = more
}
item = EnforceV1ProcessCompletedItem(item, "", allowedAttrs)
item = EnforceV1ProcessCompletedItemOpts(item, EnforceV1Opts{
Language: language,
Allowed: allowedAttrs,
OmitSEOMeta: omitSEOMeta,
})
full = append(full, item)
}
if err := rows.Err(); err != nil {
@@ -463,6 +483,22 @@ func nullIfEmptyPtr(s *string) any {
return *s
}
// companyOmitsSEOMeta is true for the A1 cohort (no meta_title / meta_description).
func companyOmitsSEOMeta(ctx context.Context, p *Pipeline, companyID uuid.UUID) bool {
if p == nil || p.Pool == nil {
return false
}
var legacy string
err := p.Pool.QueryRow(ctx, `
SELECT COALESCE(legacy_company_id, '')
FROM companies
WHERE id = $1`, companyID).Scan(&legacy)
if err != nil {
return false
}
return billing.IsA1CohortCompany(legacy, "")
}
func derefStringPtr(s *string) string {
if s == nil {
return ""
@@ -685,6 +721,7 @@ func ProjectV1ProcessJobItems(storedType string, items []V1ProcessJobItem) []V1P
switch step {
case "category":
projected["category"] = item["category"]
projected["category_id"] = item["category_id"]
projected["category_name"] = item["category_name"]
case "title":
projected["title"] = item["title"]
@@ -692,10 +729,14 @@ func ProjectV1ProcessJobItems(storedType string, items []V1ProcessJobItem) []V1P
if projected["name"] == nil {
projected["name"] = item["title"]
}
projected["meta_title"] = item["meta_title"]
if _, ok := item["meta_title"]; ok {
projected["meta_title"] = item["meta_title"]
}
case "description":
projected["description"] = item["description"]
projected["meta_description"] = item["meta_description"]
if _, ok := item["meta_description"]; ok {
projected["meta_description"] = item["meta_description"]
}
case "attributes":
projected["attributes"] = item["attributes"]
}