Files
goods/migrations/0010_archive_kinds.up.sql
sulaimaannaasif6866 916bdd9c7c
CI / Go (api) (pull_request) Successful in 13s
CI / Python (ingestion) (pull_request) Successful in 9s
CI / Migrations (postgres) (pull_request) Successful in 14s
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>
2026-06-21 06:13:42 +00:00

65 lines
3.7 KiB
SQL

-- Generic, extensible "archive kind" (档案模式) framework.
-- Instead of a dedicated detail table per domain (food/electronics/drug/...),
-- each category belongs to an archive_kind, and a metadata table (kind_field)
-- describes the editable spec fields for that kind. Non-food spec values live
-- in the existing product.attributes JSONB. Adding a new domain later (drug,
-- machinery, ...) is just: seed kind_field rows + a category subtree.
ALTER TABLE category ADD COLUMN archive_kind VARCHAR(24) NOT NULL DEFAULT 'generic';
-- The existing seeded tree is the food domain.
UPDATE category SET archive_kind = 'food' WHERE path <@ 'food';
-- Field template per archive kind: drives the dynamic admin form and the
-- kind-aware completeness/qualified computation.
CREATE TABLE kind_field (
kind VARCHAR(24) NOT NULL,
field_key VARCHAR(64) NOT NULL,
group_label TEXT NOT NULL DEFAULT '',
label_zh TEXT NOT NULL,
field_type VARCHAR(16) NOT NULL DEFAULT 'text',
unit TEXT,
options TEXT[] NOT NULL DEFAULT '{}',
placeholder TEXT,
sort_order INT NOT NULL DEFAULT 0,
qualified BOOLEAN NOT NULL DEFAULT false,
PRIMARY KEY (kind, field_key),
CONSTRAINT kind_field_type_chk CHECK (field_type IN ('text','number','textarea','select','list'))
);
-- Seed the electronics (3C) field template.
INSERT INTO kind_field (kind, field_key, group_label, label_zh, field_type, unit, sort_order, qualified) VALUES
('electronics','model_number', '基础规格','型号', 'text', NULL, 10, true),
('electronics','ccc_cert', '基础规格','3C认证号(CCC)','text', NULL, 20, true),
('electronics','color', '基础规格','颜色', 'text', NULL, 30, false),
('electronics','release_year', '基础规格','发布年份', 'number', NULL, 40, false),
('electronics','warranty_months','基础规格','保修期', 'number', '', 50, false),
('electronics','dimensions', '外形','尺寸(长x宽x高)', 'text', 'mm', 60, false),
('electronics','weight', '外形','重量', 'number', 'g', 70, false),
('electronics','power', '外形','电源/功率', 'text', NULL, 80, false),
('electronics','os', '关键参数','操作系统', 'text', NULL, 90, false),
('electronics','cpu', '关键参数','处理器', 'text', NULL, 100, false),
('electronics','ram', '关键参数','内存', 'text', NULL, 110, false),
('electronics','storage', '关键参数','存储', 'text', NULL, 120, false),
('electronics','screen_size', '关键参数','屏幕尺寸', 'text', NULL, 130, true),
('electronics','battery', '关键参数','电池容量', 'text', NULL, 140, false),
('electronics','ports', '关键参数','接口', 'text', NULL, 150, false);
-- Seed the 3C category tree.
INSERT INTO category (name_zh, name_en, parent_id, path, level, archive_kind)
VALUES ('电子数码', 'Electronics', NULL, 'electronics', 0, 'electronics');
INSERT INTO category (name_zh, name_en, parent_id, path, level, archive_kind)
SELECT v.name_zh, v.name_en, c.id, v.path::ltree, 1, 'electronics'
FROM (VALUES
('手机', 'Smartphone', 'electronics.phone'),
('笔记本电脑', 'Laptop', 'electronics.laptop'),
('平板电脑', 'Tablet', 'electronics.tablet'),
('智能手表', 'Smartwatch', 'electronics.watch'),
('耳机', 'Headphone', 'electronics.headphone'),
('相机', 'Camera', 'electronics.camera'),
('电视', 'TV', 'electronics.tv'),
('家用电器', 'Home appliance', 'electronics.appliance')
) AS v(name_zh, name_en, path)
JOIN category c ON c.path = 'electronics';