refactor: 重构产品相关Mapper

- 空值处理与类型控制:为相关关系型字段的数据处理添加空值处理与类型控制
- 调整目录结构:将文件目录按照实际查询划分为Product和ProductList两个文件
This commit is contained in:
2025-12-04 17:38:43 +08:00
parent f081a3b33a
commit 1245df497b
14 changed files with 801 additions and 414 deletions

View File

@ -1,69 +1,111 @@
import { describe, test, expect } from 'vitest';
import {
toProductListView,
toProductImageView,
toProductSpecView,
toProductSpecGroupView,
toProductQuestionView,
toProductDocumentView,
toProductView,
} from './productMapper';
/**
* 单元测试: toProductListView
* 单元测试: toProductImageView
*/
describe('toProductListView', () => {
test('convert raw data to ProductListView correctly', () => {
const rawData: Product = {
describe('toProductImageView', () => {
const baseData: Array<ProductsProductImage> = [
{
id: 1,
translations: [
{ id: 10, name: 'Product Name', summary: 'Product Summary' },
],
cover: {
id: 'cover-file-uuid-1234',
},
product_type: {
product_images_id: {
id: 1,
translations: [{ id: 20, name: 'Type Name' }],
sort: 1,
image: {
id: 'rand-om__-uuid-1234',
},
translations: [
{
id: 1,
caption: 'Image Caption',
},
],
},
};
},
{
id: 2,
product_images_id: {
id: 2,
image: {
id: 'rand-om__-uuid-5678',
},
translations: [
{
id: 2,
caption: 'Image Caption 2',
},
],
},
},
];
expect(toProductListView(rawData)).toEqual({
id: '1',
name: 'Product Name',
summary: 'Product Summary',
cover: 'cover-file-uuid-1234',
product_type: {
test('convert raw data to ProductImageView correctly', () => {
const rawData: Array<ProductsProductImage> = [...baseData];
expect(toProductImageView(rawData)).toEqual([
{
id: '1',
name: 'Type Name',
sort: 1,
image: 'rand-om__-uuid-1234',
caption: 'Image Caption',
},
});
{
id: '2',
image: 'rand-om__-uuid-5678',
caption: 'Image Caption 2',
},
]);
});
test('convert raw data with missing translations', () => {
const rawData: Product = {
id: 1,
translations: [],
cover: {
id: 'cover-file-uuid-1234',
const rawData: Array<ProductsProductImage> = [
{
id: 1,
product_images_id: {
id: 1,
image: {
id: 'rand-om__-uuid-1234',
},
translations: [],
},
},
product_type: {
id: 20,
translations: [],
sort: 1,
},
};
];
expect(toProductListView(rawData)).toEqual({
id: '1',
name: '',
summary: '',
cover: 'cover-file-uuid-1234',
product_type: {
id: '20',
name: '',
sort: 1,
expect(toProductImageView(rawData)).toEqual([
{
id: '1',
image: 'rand-om__-uuid-1234',
caption: '',
},
});
]);
});
test('convert empty array to empty ProductImageView array', () => {
const rawData: Array<ProductsProductImage> = [];
expect(toProductImageView(rawData)).toEqual([]);
});
test('convert input with wrong type to default value', () => {
const rawData: string[] = ['1', '2'];
expect(toProductImageView(rawData)).toEqual([
{
id: '1',
image: '',
caption: '',
},
{
id: '2',
image: '',
caption: '',
},
]);
});
});
@ -71,30 +113,78 @@ describe('toProductListView', () => {
* 单元测试: toProductSpecView
*/
describe('toProductSpecView', () => {
test('convert raw data to ProductSpecView correctly', () => {
const rawData: ProductSpec = {
const baseData: ProductSpec[] = [
{
id: 1,
translations: [{ id: 1, key: 'Key', value: 'Value' }],
};
translations: [
{
id: 1,
key: 'key 1',
value: 'value 1',
},
],
},
{
id: 2,
translations: [
{
id: 2,
key: 'key 2',
value: 'value 2',
},
],
},
];
test('convert raw data to ProductSpecView correctly', () => {
const rawData: ProductSpec[] = [...baseData];
expect(toProductSpecView(rawData)).toEqual({
id: '1',
key: 'Key',
value: 'Value',
});
expect(toProductSpecView(rawData)).toEqual([
{
id: '1',
key: 'key 1',
value: 'value 1',
},
{
id: '2',
key: 'key 2',
value: 'value 2',
},
]);
});
test('convert raw data with missing translations', () => {
const rawData: ProductSpec = {
id: 1,
translations: [],
};
const rawData: ProductSpec[] = [{ id: 1, translations: [] }];
expect(toProductSpecView(rawData)).toEqual({
id: '1',
key: '',
value: '',
});
expect(toProductSpecView(rawData)).toEqual([
{
id: '1',
key: '',
value: '',
},
]);
});
test('convert empty input to empty array', () => {
const rawData: ProductSpec[] = [];
expect(toProductSpecView(rawData)).toEqual([]);
});
test('convert input with wrong type to default value', () => {
const rawData: string[] = ['1', '2'];
expect(toProductSpecView(rawData)).toEqual([
{
id: '1',
key: '',
value: '',
},
{
id: '2',
key: '',
value: '',
},
]);
});
});
@ -103,37 +193,237 @@ describe('toProductSpecView', () => {
*/
describe('toProductSpecGroupView', () => {
test('convert raw data to ProductSpecGroupView correctly', () => {
const rawData: ProductSpecGroup = {
id: 1,
translations: [{ id: 1, name: 'Group Name' }],
specs: [
{ id: 1, translations: [{ id: 1, key: 'Key1', value: 'Value1' }] },
{ id: 2, translations: [{ id: 2, key: 'Key2', value: 'Value2' }] },
],
};
const rawData: ProductSpecGroup[] = [
{
id: 1,
translations: [{ id: 1, name: 'Group Name' }],
specs: [
{ id: 1, translations: [{ id: 1, key: 'Key1', value: 'Value1' }] },
{ id: 2, translations: [{ id: 2, key: 'Key2', value: 'Value2' }] },
],
},
];
expect(toProductSpecGroupView(rawData)).toEqual({
id: '1',
name: 'Group Name',
specs: [
{ id: '1', key: 'Key1', value: 'Value1' },
{ id: '2', key: 'Key2', value: 'Value2' },
],
});
expect(toProductSpecGroupView(rawData)).toEqual([
{
id: '1',
name: 'Group Name',
specs: [
{ id: '1', key: 'Key1', value: 'Value1' },
{ id: '2', key: 'Key2', value: 'Value2' },
],
},
]);
});
test('convert raw data with missing translations', () => {
const rawData: ProductSpecGroup = {
id: 1,
translations: [],
specs: [],
};
const rawData: ProductSpecGroup[] = [
{
id: 1,
translations: [],
specs: [],
},
];
expect(toProductSpecGroupView(rawData)).toEqual({
id: '1',
name: '',
specs: [],
});
expect(toProductSpecGroupView(rawData)).toEqual([
{
id: '1',
name: '',
specs: [],
},
]);
});
test('convert input with wrong type to default value', () => {
const rawData: string[] = ['1', '2'];
expect(toProductSpecGroupView(rawData)).toEqual([
{
id: '1',
name: '',
specs: [],
},
{
id: '2',
name: '',
specs: [],
},
]);
});
test('convert empty ar ray to empty ProductSpecGroupView array', () => {
const rawData: ProductSpec[] = [];
expect(toProductSpecGroupView(rawData)).toEqual([]);
});
});
/**
* 单元测试: toProductQuestionView
*/
describe('toProductQuestionView', () => {
test('convert raw data to ProductQuestionView correctly', () => {
const rawData: ProductsQuestion[] = [
{
id: 1,
questions_id: {
id: 1,
translations: [
{
id: 1,
title: 'Question Title',
content: 'Question Content',
},
],
},
},
];
expect(toProductQuestionView(rawData)).toEqual([
{
id: '1',
title: 'Question Title',
content: 'Question Content',
},
]);
});
test('convert raw data with missing translations', () => {
const rawData: ProductsQuestion[] = [
{
id: 1,
questions_id: {
id: 1,
translations: [],
},
},
];
expect(toProductQuestionView(rawData)).toEqual([
{
id: '1',
title: '',
content: '',
},
]);
});
test('convert empty array to empty ProductQuestionView array', () => {
const rawData: ProductsQuestion[] = [];
expect(toProductQuestionView(rawData)).toEqual([]);
});
test('convert input with wrong type to default value', () => {
const rawData: string[] = ['1', '2'];
expect(toProductQuestionView(rawData)).toEqual([
{
id: '1',
title: '',
content: '',
},
{
id: '2',
title: '',
content: '',
},
]);
});
});
/**
* 单元测试: toProductDocumentView
*/
describe('toProductDocumentView', () => {
test('convert raw data to ProductDocumentView correctly', () => {
const rawData: ProductsProductDocument[] = [
{
id: 1,
product_documents_id: {
id: 1,
file: {
id: 'rand-om__-uuid-1234',
filesize: 1000,
filename_download: 'doc1.pdf',
},
translations: [
{
id: 1,
title: 'Document Title 1',
},
],
},
},
];
expect(toProductDocumentView(rawData)).toEqual([
{
id: '1',
fileId: 'rand-om__-uuid-1234',
filename: 'doc1.pdf',
title: 'Document Title 1',
size: 1000,
url: '/api/assets/rand-om__-uuid-1234',
},
]);
});
test('convert raw data with missing translations', () => {
const rawData: ProductsProductDocument[] = [
{
id: 1,
product_documents_id: {
id: 1,
file: {
id: 'rand-om__-uuid-1234',
filesize: 1000,
filename_download: 'doc1.pdf',
},
translations: [],
},
},
];
expect(toProductDocumentView(rawData)).toEqual([
{
id: '1',
fileId: 'rand-om__-uuid-1234',
filename: 'doc1.pdf',
title: '',
size: 1000,
url: '/api/assets/rand-om__-uuid-1234',
},
]);
});
test('convert empty array to empty ProductDocumentView array', () => {
const rawData: ProductsProductDocument[] = [];
expect(toProductDocumentView(rawData)).toEqual([]);
});
test('convert input with wrong type to default value', () => {
const rawData: string[] = ['1', '2'];
expect(toProductDocumentView(rawData)).toEqual([
{
id: '1',
fileId: '',
filename: '',
title: '',
size: 0,
url: '',
},
{
id: '2',
fileId: '',
filename: '',
title: '',
size: 0,
url: '',
},
]);
});
});
@ -159,7 +449,6 @@ describe('toProductView', () => {
name: 'Product Name',
summary: 'Product Summary',
description: 'Product Description',
product_type: '',
images: [],
documents: [],
faqs: [],
@ -178,7 +467,6 @@ describe('toProductView', () => {
name: '',
summary: '',
description: '',
product_type: '',
images: [],
documents: [],
faqs: [],