Files
inkreach-official-website/docs/superpowers/specs/2026-06-15-product-center-design.md
T

362 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 产品中心 (Product Center) 设计规格
## 概述
为 InkReach 官网新增"产品中心"二级页面,包含三个子系统:
1. **NestJS 后端服务** — Prisma + PostgreSQLRESTful APISDS 定时同步
2. **Vue 3 Admin 后台** — Element Plus,商品/品类/国家/标签/位置管理
3. **Nuxt 4 官网页面** — 产品中心二级页,带侧边栏分类、国家筛选、搜索、分页
## 架构
- NestJS 监听 `:3001`,提供 RESTful API
- Admin 直连 NestJS API(开发环境 Vite proxy 解决跨域)
- Website 通过 Nitro server route 代理 NestJS API
## 实施顺序
后端服务 → Admin 后台 → 官网产品中心页
---
## 1. NestJS 后端服务
### 技术栈
| 技术 | 版本 | 用途 |
|------|------|------|
| NestJS | 10+ | 后端框架 |
| Prisma | 6+ | ORM |
| PostgreSQL | 16+ | 数据库 |
| @nestjs/schedule | - | 定时同步 SDS |
| passport + jwt | - | JWT 认证 |
| class-validator | - | DTO 验证 |
### 项目结构
```
inkreach-official-nestjs/
├── prisma/
│ ├── schema.prisma
│ └── migrations/
├── src/
│ ├── main.ts
│ ├── app.module.ts
│ ├── auth/
│ │ ├── auth.module.ts
│ │ ├── auth.controller.ts
│ │ ├── auth.service.ts
│ │ ├── jwt.strategy.ts
│ │ └── dto/
│ ├── goods/
│ │ ├── goods.module.ts
│ │ ├── goods.controller.ts
│ │ ├── goods.service.ts
│ │ └── dto/
│ ├── categories/
│ ├── countries/
│ ├── tags/
│ ├── positions/
│ ├── origin-goods/
│ └── sync/
│ ├── sync.module.ts
│ ├── sync.service.ts
│ └── sync.controller.ts
├── test/
├── .env
├── nest-cli.json
├── tsconfig.json
└── package.json
```
### Prisma Schema
`docs/dev/database-table-design.md` 的 DDL 转换为 Prisma schema6 个核心模型:`OriginGoods`, `Country`, `Category`, `Tag`, `Position`, `Good`
关系映射:
- `Good``OriginGoods` (多对一, RESTRICT)
- `Good``Country` (多对一, RESTRICT)
- `Good``Category` (多对一, RESTRICT)
- `Good``Tag` (多对一, SET NULL)
- `Good``Position` (多对一, SET NULL)
- `Category` 自关联 (parent_category_id)
- `Position``Country` (多对一, CASCADE)
- `Position``Category` (多对一, CASCADE)
### API 设计
#### 认证
| 方法 | 路径 | 描述 |
|------|------|------|
| POST | `/auth/login` | 管理员登录,返回 JWT |
| POST | `/auth/register` | 注册(仅初始化用) |
#### 商品管理 (Admin, 需认证)
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/goods` | 分页查询商品(支持 country/category/tag/position/keyword 筛选) |
| GET | `/goods/:id` | 商品详情 |
| POST | `/goods` | 创建商品 |
| PATCH | `/goods/:id` | 更新商品 |
| DELETE | `/goods/:id` | 删除商品 |
| PATCH | `/goods/batch-priority` | 批量更新优先级 |
| POST | `/goods/batch` | 批量创建商品(从 origin_goods 选取) |
#### 品类管理 (Admin)
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/categories` | 获取品类树 |
| GET | `/categories/:id` | 品类详情 |
| POST | `/categories` | 创建品类 |
| PATCH | `/categories/:id` | 更新品类 |
| DELETE | `/categories/:id` | 删除品类 |
#### 国家管理 (Admin)
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/countries` | 国家列表 |
| POST | `/countries` | 创建国家 |
| PATCH | `/countries/:id` | 更新国家 |
| DELETE | `/countries/:id` | 删除国家 |
#### 标签管理 (Admin)
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/tags` | 标签列表 |
| POST | `/tags` | 创建标签 |
| PATCH | `/tags/:id` | 更新标签 |
| DELETE | `/tags/:id` | 删除标签 |
#### 位置管理 (Admin)
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/positions` | 位置列表 |
| POST | `/positions` | 创建位置 |
| PATCH | `/positions/:id` | 更新位置 |
| DELETE | `/positions/:id` | 删除位置 |
#### 原商品管理 (Admin)
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/origin-goods` | 原商品列表(来自 SDS 同步) |
#### 同步 (Admin)
| 方法 | 路径 | 描述 |
|------|------|------|
| POST | `/sync/categories` | 手动触发品类同步 |
| POST | `/sync/products` | 手动触发产品同步 |
| GET | `/sync/status` | 获取最近同步状态 |
#### 公开 API (Website, 无需认证)
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/public/categories` | 品类树(仅含已配置商品的品类) |
| GET | `/public/countries` | 国家列表(仅含已配置商品的国家) |
| GET | `/public/goods` | 分页查询商品(支持 country/category/tag/keyword 筛选) |
| GET | `/public/goods/:id` | 商品详情 |
### SDS 同步逻辑
1. **品类同步**: 调用 `https://mapi.sdspod.com/category/tree/3`,将返回的树形结构扁平化写入 `categories`
2. **产品同步**: 按 SDS 分类遍历产品,写入 `origin_goods`
3. **定时策略**: 每小时自动同步一次 (`@Cron('0 * * * *')`)
4. **增量策略**: 对比现有记录,存在则跳过,不存在则插入
### 商品查询逻辑 (公开 API)
排序: `good_priority DESC → position.index_val ASC → created_at DESC`
筛选参数:
- `countryId` — 按国家筛选
- `categoryId` — 按品类筛选(含子品类)
- `tagId` — 按标签筛选
- `keyword` — 按商品名模糊搜索
- `page` / `pageSize` — 分页
---
## 2. Admin 后台
### 技术栈
| 技术 | 版本 | 用途 |
|------|------|------|
| Vue | 3.5+ | UI 框架 |
| Vite | 6+ | 构建工具 |
| Element Plus | 2.9+ | UI 组件库 |
| Vue Router | 4+ | 路由 |
| Pinia | 3+ | 状态管理 |
| Axios | 1.7+ | HTTP 客户端 |
| TypeScript | 5+ | 类型安全 |
### 项目结构
```
inkreach-official-admin/
├── src/
│ ├── main.ts
│ ├── App.vue
│ ├── router/index.ts
│ ├── stores/
│ │ ├── auth.ts
│ │ └── app.ts
│ ├── api/
│ │ ├── request.ts
│ │ ├── auth.ts
│ │ ├── goods.ts
│ │ ├── categories.ts
│ │ ├── countries.ts
│ │ ├── tags.ts
│ │ ├── positions.ts
│ │ ├── origin-goods.ts
│ │ └── sync.ts
│ ├── layouts/
│ │ └── DefaultLayout.vue
│ ├── views/
│ │ ├── login/LoginView.vue
│ │ ├── goods/GoodsView.vue
│ │ ├── categories/CategoriesView.vue
│ │ ├── countries/CountriesView.vue
│ │ ├── tags/TagsView.vue
│ │ ├── positions/PositionsView.vue
│ │ └── sync/SyncView.vue
│ ├── components/
│ └── types/index.ts
├── .env
├── vite.config.ts
├── tsconfig.json
└── package.json
```
### 核心页面功能
**商品管理 (GoodsView)**:
- 表格展示:商品名、国家、品类、标签、位置、优先级
- 筛选:按国家/品类/标签筛选
- 操作:新增、编辑、删除、调整优先级
- 批量添加:从 origin_goods 选择商品,批量配置国家/品类/标签/优先级
**品类管理 (CategoriesView)**:
- 树形表格展示层级关系
- 新增/编辑/删除品类
**同步管理 (SyncView)**:
- 显示最近同步时间、同步状态
- 手动触发品类同步/产品同步按钮
---
## 3. 官网产品中心页
### 页面结构
左右两栏布局:左侧固定宽度侧边栏(品类导航),右侧主内容区(国家筛选 + 筛选栏 + 商品网格 + 分页)。
### 组件拆分
```
app/pages/product-center.vue # 页面入口
app/components/product/
├── ProductSidebar.vue # 侧边栏品类导航
├── ProductCountryFilter.vue # 国家标签栏
├── ProductFilterBar.vue # 筛选下拉 + 已选标签 + 搜索
├── ProductGrid.vue # 商品卡片网格
├── ProductCard.vue # 单个商品卡片
├── ProductCardSkeleton.vue # 骨架屏卡片
└── ProductPagination.vue # 分页控件
app/composables/
└── useProductCenter.ts # 产品中心数据逻辑
```
### 商品卡片设计
- 商品图片 (1:1 方形)
- 商品名 (单行截断)
- 标签行 (pill 样式,颜色体系:绿色=包邮,橙色=工艺,灰色=其他)
- 价格 (粗体,"¥XX.XX起")
### 侧边栏品类导航
- 顶级品类带图标 + 折叠箭头
- 展开子品类列表,左侧橙色竖条标识当前选中
- 选中子品类背景 `bg-inkreach-homepage-3` + 橙色文字
### 国家标签栏
- Pill 形状按钮,每个带国旗图标
- "全部" 为默认选中,橙色边框 + 橙色文字
- 未选中为灰色边框
### 搜索
- 分体式搜索框:左侧输入框 + 右侧橙色"搜索"按钮
- 支持按商品名搜索
### 分页
- 左侧:"总计 N 个产品"
- 中间:页码按钮(方形)
- 右侧:每页条数下拉 + 跳转输入框
### 骨架屏
数据加载时显示骨架屏:
- 卡片区域显示 ProductCardSkeleton (4x3 网格)
- 分页隐藏
- 侧边栏和筛选区正常显示
### Nitro API 代理
```
server/api/backend/categories.get.ts -> GET :3001/public/categories
server/api/backend/countries.get.ts -> GET :3001/public/countries
server/api/backend/goods.get.ts -> GET :3001/public/goods
server/api/backend/goods/[id].get.ts -> GET :3001/public/goods/:id
```
### 数据流
1. 页面加载 → `/api/backend/categories` → 渲染侧边栏
2. 页面加载 → `/api/backend/countries` → 渲染国家标签栏
3. 筛选/分页变化 → `/api/backend/goods?countryId=&categoryId=&keyword=&page=&pageSize=` → 渲染商品网格
### 关联改动
- 首页 `PodProducts.vue` 的"更多产品"按钮改为 `<NuxtLink to="/product-center">`
- `AppHeader.vue` 的"产品中心"链接改为 `<NuxtLink to="/product-center">`
---
## 跨项目配置
### 环境变量
**inkreach-official-nestjs/.env**:
- DATABASE_URL=postgresql://postgres:yoyoki219765.@localhost:5432/inkreach-official
- JWT_SECRET=(generate-secure-secret)
- SDS_API_BASE=https://mapi.sdspod.com
- PORT=3001
**inkreach-official-admin/.env**:
- VITE_API_BASE=http://localhost:3001
**inkreach-official-website**:
- NUXT_PUBLIC_BACKEND_URL=http://localhost:3001
### 端口分配
| 项目 | 端口 |
|------|------|
| NestJS Backend | 3001 |
| Admin (Vite dev) | 5173 |
| Website (Nuxt dev) | 3000 |