feat(deploy): production deployment setup and fixes
- Debian-based api image (bookworm-slim), docker/debian mirrors, prisma binaryTargets for openssl 3.0 - nginx: admin SPA under /admin, TLS via acme.sh (ZeroSSL) + auto-renewal cron, http->https redirect - prisma: add origin_goods.delisted migration, sync missing schema (good_image/tag_font_color/good_tags), fix users.createdAt Timestamptz - api: CORS wildcard reflection, helmet CORP cross-origin, price backfill in persistProductDetail, categoryIcon ancestor fallback, mediaByColor per-color gallery in public goods detail - admin: /admin base path (vite + router) - import-data.mjs: udt_name casting, serial sequence advance fix
This commit is contained in:
@@ -1,92 +1,92 @@
|
||||
# Goods Management Redesign
|
||||
|
||||
## Date: 2026-06-21
|
||||
|
||||
## Problem
|
||||
|
||||
1. **Origin Good name not displayed**: GoodsView doesn't show the origin product name/image/price
|
||||
2. **Single tag per good**: Current schema only allows one tag per good via `goods.tag_id` FK
|
||||
3. **Easy to miss unconfigured origin products**: Flat paginated table with no visibility into which origin products have been configured vs not
|
||||
|
||||
## Solution
|
||||
|
||||
### 1. Multi-Tag Support (GoodTag Junction Table)
|
||||
|
||||
**Schema change:**
|
||||
- New `good_tags` junction table: `(good_id, tag_id)` composite PK
|
||||
- Migrate existing `goods.tag_id` data into `good_tags`
|
||||
- Set `goods.tag_id` to nullable (will be dropped in a future migration)
|
||||
|
||||
**Backend:**
|
||||
- `CreateGoodDto`: `tagIds: number[]` (replaces `tagId`)
|
||||
- `UpdateGoodDto`: `tagIds?: number[]` (replaces `tagId`)
|
||||
- `BatchCreateGoodDto`: `tagIds?: number[]` (replaces `tagId`)
|
||||
- `QueryGoodDto`: `tagId` filter queries `tags.some` instead of direct FK
|
||||
- `GoodsService.create/update`: manage `good_tags` records in transaction
|
||||
- `GOOD_INCLUDE`: include `tags: { include: { tag: true } }`
|
||||
|
||||
### 2. Origin Goods Tree API
|
||||
|
||||
New endpoint: `GET /origin-goods/tree`
|
||||
|
||||
Returns origin goods grouped by SDS category, with configuration status:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"sdsCategoryId": "4185",
|
||||
"categoryName": "(包邮)180g纯棉T恤-单面印花",
|
||||
"originGoods": [
|
||||
{
|
||||
"id": "123",
|
||||
"goodName": "产品A",
|
||||
"goodImage": "https://...",
|
||||
"goodPrice": "12.50",
|
||||
"sdsGoodId": "p-123",
|
||||
"configuredCount": 2,
|
||||
"configuredCountries": ["美国", "英国"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Origin goods without `sdsCategoryId` are grouped under "未分类".
|
||||
|
||||
### 3. Admin UI: Dual Tree Layout
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ [搜索原产品] [210/439 已配置] │
|
||||
├───────────────────┬─────────────────────────────────────┤
|
||||
│ 首页分类树 │ 原产品分类树 │
|
||||
│ │ │
|
||||
│ ▼ 美国 │ ▼ (包邮)180g纯棉T恤 │
|
||||
│ ▼ T恤 │ ✓ 产品A [美国,英国] │
|
||||
│ ▼ 卫衣 │ ✗ 产品B [未配置] │
|
||||
│ ▼ 英国 │ ▼ 未分类 │
|
||||
│ ▼ 韩国 │ ✗ 产品C [未配置] │
|
||||
├───────────────────┴─────────────────────────────────────┤
|
||||
│ 详情/操作区域 │
|
||||
│ - 选左侧分类: 该分类下所有 goods 表格 │
|
||||
│ - 选右侧原产品: 该原产品的配置详情 + 快捷配置按钮 │
|
||||
│ - 多标签选择 (el-select multiple) │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Key interactions:**
|
||||
- Left tree click: filter goods by selected category
|
||||
- Right tree click: show origin good detail + all its goods configs
|
||||
- Right tree badges: ✓ configured / ✗ unconfigured
|
||||
- Quick configure: select country+category+tags, click origin goods to batch create
|
||||
- Statistics bar: configured/total count
|
||||
|
||||
### 4. Data Migration
|
||||
|
||||
```sql
|
||||
-- Step 1: Create good_tags table
|
||||
-- Step 2: Copy existing tag_id data
|
||||
INSERT INTO good_tags (good_id, tag_id, created_at)
|
||||
SELECT good_id, tag_id, NOW() FROM goods WHERE tag_id IS NOT NULL;
|
||||
-- Step 3: Good.tag_id becomes nullable (already nullable)
|
||||
```
|
||||
# Goods Management Redesign
|
||||
|
||||
## Date: 2026-06-21
|
||||
|
||||
## Problem
|
||||
|
||||
1. **Origin Good name not displayed**: GoodsView doesn't show the origin product name/image/price
|
||||
2. **Single tag per good**: Current schema only allows one tag per good via `goods.tag_id` FK
|
||||
3. **Easy to miss unconfigured origin products**: Flat paginated table with no visibility into which origin products have been configured vs not
|
||||
|
||||
## Solution
|
||||
|
||||
### 1. Multi-Tag Support (GoodTag Junction Table)
|
||||
|
||||
**Schema change:**
|
||||
- New `good_tags` junction table: `(good_id, tag_id)` composite PK
|
||||
- Migrate existing `goods.tag_id` data into `good_tags`
|
||||
- Set `goods.tag_id` to nullable (will be dropped in a future migration)
|
||||
|
||||
**Backend:**
|
||||
- `CreateGoodDto`: `tagIds: number[]` (replaces `tagId`)
|
||||
- `UpdateGoodDto`: `tagIds?: number[]` (replaces `tagId`)
|
||||
- `BatchCreateGoodDto`: `tagIds?: number[]` (replaces `tagId`)
|
||||
- `QueryGoodDto`: `tagId` filter queries `tags.some` instead of direct FK
|
||||
- `GoodsService.create/update`: manage `good_tags` records in transaction
|
||||
- `GOOD_INCLUDE`: include `tags: { include: { tag: true } }`
|
||||
|
||||
### 2. Origin Goods Tree API
|
||||
|
||||
New endpoint: `GET /origin-goods/tree`
|
||||
|
||||
Returns origin goods grouped by SDS category, with configuration status:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"sdsCategoryId": "4185",
|
||||
"categoryName": "(包邮)180g纯棉T恤-单面印花",
|
||||
"originGoods": [
|
||||
{
|
||||
"id": "123",
|
||||
"goodName": "产品A",
|
||||
"goodImage": "https://...",
|
||||
"goodPrice": "12.50",
|
||||
"sdsGoodId": "p-123",
|
||||
"configuredCount": 2,
|
||||
"configuredCountries": ["美国", "英国"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Origin goods without `sdsCategoryId` are grouped under "未分类".
|
||||
|
||||
### 3. Admin UI: Dual Tree Layout
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ [搜索原产品] [210/439 已配置] │
|
||||
├───────────────────┬─────────────────────────────────────┤
|
||||
│ 首页分类树 │ 原产品分类树 │
|
||||
│ │ │
|
||||
│ ▼ 美国 │ ▼ (包邮)180g纯棉T恤 │
|
||||
│ ▼ T恤 │ ✓ 产品A [美国,英国] │
|
||||
│ ▼ 卫衣 │ ✗ 产品B [未配置] │
|
||||
│ ▼ 英国 │ ▼ 未分类 │
|
||||
│ ▼ 韩国 │ ✗ 产品C [未配置] │
|
||||
├───────────────────┴─────────────────────────────────────┤
|
||||
│ 详情/操作区域 │
|
||||
│ - 选左侧分类: 该分类下所有 goods 表格 │
|
||||
│ - 选右侧原产品: 该原产品的配置详情 + 快捷配置按钮 │
|
||||
│ - 多标签选择 (el-select multiple) │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Key interactions:**
|
||||
- Left tree click: filter goods by selected category
|
||||
- Right tree click: show origin good detail + all its goods configs
|
||||
- Right tree badges: ✓ configured / ✗ unconfigured
|
||||
- Quick configure: select country+category+tags, click origin goods to batch create
|
||||
- Statistics bar: configured/total count
|
||||
|
||||
### 4. Data Migration
|
||||
|
||||
```sql
|
||||
-- Step 1: Create good_tags table
|
||||
-- Step 2: Copy existing tag_id data
|
||||
INSERT INTO good_tags (good_id, tag_id, created_at)
|
||||
SELECT good_id, tag_id, NOW() FROM goods WHERE tag_id IS NOT NULL;
|
||||
-- Step 3: Good.tag_id becomes nullable (already nullable)
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user