feat: 可扩展档案模式框架 + 内置 3C(电子) 模式
CI / Go (api) (pull_request) Successful in 13s
CI / Python (ingestion) (pull_request) Successful in 9s
CI / Migrations (postgres) (pull_request) Successful in 14s

- 新增 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:
sulaimaannaasif6866
2026-06-21 06:13:42 +00:00
parent 98eb01f4ea
commit 916bdd9c7c
15 changed files with 695 additions and 55 deletions
+9 -8
View File
@@ -96,11 +96,12 @@ func (s *Store) CreateCategory(ctx context.Context, actor string, in CategoryInp
parentPath := ""
parentLevel := -1
kind := DefaultKind
var parentID *string
if pid := trimPtr(in.ParentID); pid != nil {
var path string
var level int
err := s.pool.QueryRow(ctx, "SELECT path::text, level FROM category WHERE id = $1", *pid).Scan(&path, &level)
err := s.pool.QueryRow(ctx, "SELECT path::text, level, archive_kind FROM category WHERE id = $1", *pid).Scan(&path, &level, &kind)
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrInvalidParent
}
@@ -119,11 +120,11 @@ func (s *Store) CreateCategory(ctx context.Context, actor string, in CategoryInp
var c Category
err := s.pool.QueryRow(ctx, `
INSERT INTO category (name_zh, name_en, parent_id, path, gpc_brick_code, level)
VALUES ($1, $2, $3, $4::ltree, $5, $6)
RETURNING id, name_zh, name_en, path::text, level, parent_id::text, gpc_brick_code, 0`,
name, trimPtr(in.NameEN), parentID, path, trimPtr(in.GPCBrickCode), level).
Scan(&c.ID, &c.NameZH, &c.NameEN, &c.Path, &c.Level, &c.ParentID, &c.GPCBrickCode, &c.ProductCount)
INSERT INTO category (name_zh, name_en, parent_id, path, gpc_brick_code, level, archive_kind)
VALUES ($1, $2, $3, $4::ltree, $5, $6, $7)
RETURNING id, name_zh, name_en, path::text, level, parent_id::text, gpc_brick_code, archive_kind, 0`,
name, trimPtr(in.NameEN), parentID, path, trimPtr(in.GPCBrickCode), level, kind).
Scan(&c.ID, &c.NameZH, &c.NameEN, &c.Path, &c.Level, &c.ParentID, &c.GPCBrickCode, &c.ArchiveKind, &c.ProductCount)
if isUniqueViolation(err) {
return nil, ErrDuplicatePath
}
@@ -273,10 +274,10 @@ func (s *Store) getCategory(ctx context.Context, id string) (*Category, error) {
var c Category
err := s.pool.QueryRow(ctx, `
SELECT c.id, c.name_zh, c.name_en, c.path::text, c.level, c.parent_id::text,
c.gpc_brick_code,
c.gpc_brick_code, c.archive_kind,
(SELECT count(*) FROM product p WHERE p.category_id = c.id)
FROM category c WHERE c.id = $1`, id).
Scan(&c.ID, &c.NameZH, &c.NameEN, &c.Path, &c.Level, &c.ParentID, &c.GPCBrickCode, &c.ProductCount)
Scan(&c.ID, &c.NameZH, &c.NameEN, &c.Path, &c.Level, &c.ParentID, &c.GPCBrickCode, &c.ArchiveKind, &c.ProductCount)
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrNotFound
}