# 产品中心 (Product Center) 设计规格 ## 概述 为 InkReach 官网新增"产品中心"二级页面,包含三个子系统: 1. **NestJS 后端服务** — Prisma + PostgreSQL,RESTful API,SDS 定时同步 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 schema,6 个核心模型:`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` 的"更多产品"按钮改为 `` - `AppHeader.vue` 的"产品中心"链接改为 `` --- ## 跨项目配置 ### 环境变量 **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 |