feat: 可扩展档案模式框架 + 内置 3C(电子) 模式
- 新增 category.archive_kind 与 kind_field 字段模板表(迁移 0010) - 种子 electronics 字段模板 + 3C 品类树(手机/笔记本/平板等) - 后端按档案模式动态计算完整度/合格:食品沿用 food_detail, 其它模式走 product.attributes + kind_field - 新增 GET /api/kind-fields?kind= 接口 - 后台编辑页按品类模式动态渲染规格参数表单 - 公开详情页/接口输出带标签的规格表 Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
@@ -2,6 +2,7 @@ package adminstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
@@ -58,35 +59,56 @@ func (s *Store) computeQuality(ctx context.Context, q queryer, productID string)
|
||||
var netCanonical *float64
|
||||
var ingredients *string
|
||||
var hasNutri, hasImage bool
|
||||
var kind string
|
||||
var attributes []byte
|
||||
err := q.QueryRow(ctx, `
|
||||
SELECT p.name, p.gtin, p.brand_id, p.category_id, p.net_content_canonical,
|
||||
p.country_of_origin, f.ingredients_text,
|
||||
p.country_of_origin, COALESCE(c.archive_kind, 'generic'), p.attributes,
|
||||
f.ingredients_text,
|
||||
(f.nutriments IS NOT NULL AND f.nutriments::text <> '{}'),
|
||||
EXISTS (SELECT 1 FROM product_image pi WHERE pi.product_id = p.id)
|
||||
FROM product p LEFT JOIN food_detail f ON f.product_id = p.id
|
||||
FROM product p
|
||||
LEFT JOIN category c ON c.id = p.category_id
|
||||
LEFT JOIN food_detail f ON f.product_id = p.id
|
||||
WHERE p.id = $1`, productID).Scan(
|
||||
&name, >in, &brandID, &categoryID, &netCanonical, &country,
|
||||
&ingredients, &hasNutri, &hasImage)
|
||||
&kind, &attributes, &ingredients, &hasNutri, &hasImage)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
attrs := map[string]any{}
|
||||
if len(attributes) > 0 {
|
||||
_ = json.Unmarshal(attributes, &attrs)
|
||||
}
|
||||
qualified, err := s.kindQualifiedKeys(ctx, s.pool)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
qkeys := qualified[kind]
|
||||
|
||||
known := map[string]bool{
|
||||
"name": name != nil && *name != "",
|
||||
"gtin": gtin != nil && *gtin != "",
|
||||
"brand": brandID != nil,
|
||||
"category": categoryID != nil,
|
||||
"net_content": netCanonical != nil,
|
||||
"country_of_origin": country != nil && *country != "",
|
||||
"nutriments": hasNutri,
|
||||
"ingredients": ingredients != nil && *ingredients != "",
|
||||
"image": hasImage,
|
||||
}
|
||||
for _, k := range qkeys {
|
||||
known[k] = attrPresent(attrs, k)
|
||||
}
|
||||
keys := completenessKeys(kind, qkeys)
|
||||
present := 0
|
||||
bump := func(ok bool) {
|
||||
if ok {
|
||||
for _, k := range keys {
|
||||
if known[k] {
|
||||
present++
|
||||
}
|
||||
}
|
||||
bump(name != nil && *name != "")
|
||||
bump(gtin != nil && *gtin != "")
|
||||
bump(brandID != nil)
|
||||
bump(categoryID != nil)
|
||||
bump(netCanonical != nil)
|
||||
bump(country != nil && *country != "")
|
||||
bump(hasNutri)
|
||||
bump(ingredients != nil && *ingredients != "")
|
||||
bump(hasImage)
|
||||
completeness := float64(present) / float64(len(CompletenessFields))
|
||||
completeness := float64(present) / float64(len(keys))
|
||||
|
||||
var sourceCount int
|
||||
var sourceTrust *float64
|
||||
|
||||
Reference in New Issue
Block a user