Răsfoiți Sursa

✨ feat(goods): 支持商品轮播图多文件关联

- 在商品实体中添加slideImages多对多关联,关联File实体
- 更新CRUD路由配置,添加slideImageIds关联字段配置
- 修改商品Schema,将slideImages从URL数组改为File对象数组
- 调整创建和更新DTO,使用slideImageIds接收轮播图文件ID数组
- 更新关系配置,在查询商品时包含slideImages关联数据
yourname 4 luni în urmă
părinte
comite
922f23e0df

+ 9 - 1
src/server/api/goods/index.ts

@@ -2,6 +2,7 @@ import { createCrudRoutes } from '@/server/utils/generic-crud.routes';
 import { Goods } from '@/server/modules/goods/goods.entity';
 import { GoodsSchema, CreateGoodsDto, UpdateGoodsDto } from '@/server/modules/goods/goods.schema';
 import { authMiddleware } from '@/server/middleware/auth.middleware';
+import { File } from '@/server/modules/files/file.entity';
 
 const goodsRoutes = createCrudRoutes({
   entity: Goods,
@@ -10,11 +11,18 @@ const goodsRoutes = createCrudRoutes({
   getSchema: GoodsSchema,
   listSchema: GoodsSchema,
   searchFields: ['name', 'instructions'],
-  relations: ['category1', 'category2', 'category3', 'supplier', 'imageFile'],
+  relations: ['category1', 'category2', 'category3', 'supplier', 'imageFile', 'slideImages'],
   middleware: [authMiddleware],
   userTracking: {
     createdByField: 'created_by',
     updatedByField: 'updated_by'
+  },
+  relationFields: {
+    slideImageIds: {
+      relationName: 'slideImages',
+      targetEntity: File,
+      joinTableName: 'goods_slide_images'
+    }
   }
 });
 

+ 8 - 3
src/server/modules/goods/goods.entity.ts

@@ -1,4 +1,4 @@
-import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm';
+import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn, ManyToMany, JoinTable } from 'typeorm';
 import { GoodsCategory } from './goods-category.entity';
 import { Supplier } from '@/server/modules/supplier/supplier.entity';
 import { File } from '@/server/modules/files/file.entity';
@@ -41,8 +41,13 @@ export class Goods {
   @Column({ name: 'image_file_id', type: 'int', unsigned: true, nullable: true, comment: '商品主图文件ID' })
   imageFileId!: number | null;
 
-  @Column({ name: 'slide_images', type: 'text', nullable: true, comment: '商品轮播图URL数组(JSON)' })
-  slideImages!: string | null;
+  @ManyToMany(() => File)
+  @JoinTable({
+    name: 'goods_slide_images',
+    joinColumn: { name: 'goods_id', referencedColumnName: 'id' },
+    inverseJoinColumn: { name: 'file_id', referencedColumnName: 'id' }
+  })
+  slideImages!: File[];
 
   @Column({ name: 'detail', type: 'text', nullable: true, comment: '商品详情' })
   detail!: string | null;

+ 18 - 8
src/server/modules/goods/goods.schema.ts

@@ -3,11 +3,6 @@ import { GoodsCategorySchema } from './goods-category.schema';
 import { SupplierSchema } from '@/server/modules/supplier/supplier.schema';
 import { FileSchema } from '@/server/modules/files/file.schema';
 
-const SlideImagesSchema = z.array(z.string()).nullable().optional().openapi({
-  description: '商品轮播图URL数组',
-  example: ['https://example.com/image1.jpg', 'https://example.com/image2.jpg']
-});
-
 export const GoodsSchema = z.object({
   id: z.number().int().positive().openapi({ description: '商品ID' }),
   name: z.string().min(1, '商品名称不能为空').max(255, '商品名称最多255个字符').openapi({
@@ -54,7 +49,16 @@ export const GoodsSchema = z.object({
     description: '商品主图文件ID',
     example: 1
   }),
-  slideImages: SlideImagesSchema,
+  slideImages: z.array(FileSchema).nullable().optional().openapi({
+    description: '商品轮播图文件列表',
+    example: [{
+      id: 1,
+      name: 'image1.jpg',
+      fullUrl: 'https://example.com/image1.jpg',
+      type: 'image/jpeg',
+      size: 102400
+    }]
+  }),
   detail: z.string().nullable().optional().openapi({
     description: '商品详情',
     example: '这是商品详情内容'
@@ -157,7 +161,10 @@ export const CreateGoodsDto = z.object({
     description: '商品主图文件ID',
     example: 1
   }),
-  slideImages: SlideImagesSchema,
+  slideImageIds: z.array(z.number().int().positive()).nullable().optional().openapi({
+    description: '商品轮播图文件ID数组',
+    example: [1, 2, 3]
+  }),
   detail: z.string().nullable().optional().openapi({
     description: '商品详情',
     example: '这是商品详情内容'
@@ -229,7 +236,10 @@ export const UpdateGoodsDto = z.object({
     description: '商品主图文件ID',
     example: 1
   }),
-  slideImages: SlideImagesSchema,
+  slideImageIds: z.array(z.number().int().positive()).nullable().optional().openapi({
+    description: '商品轮播图文件ID数组',
+    example: [1, 2, 3]
+  }),
   detail: z.string().nullable().optional().openapi({
     description: '商品详情',
     example: '这是商品详情内容'