feat(edit): 商品编辑页开发
This commit is contained in:
@@ -1,6 +1,21 @@
|
||||
import { Body, Controller, Get, Headers, Inject, Param, Post, Put, Req, Res } from '@nestjs/common';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Headers,
|
||||
Inject,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
Req,
|
||||
Res,
|
||||
} from '@nestjs/common';
|
||||
import { z } from 'zod';
|
||||
import { saveProductEditorDraftRequestSchema, type Workspace } from '@atlas/contracts';
|
||||
import {
|
||||
saveProductEditorDraftRequestSchema,
|
||||
type Workspace,
|
||||
} from '@atlas/contracts';
|
||||
import { ApiError } from '../common/api-error.js';
|
||||
import type { AuthenticatedRequest } from '../auth/auth.guard.js';
|
||||
import { CurrentWorkspace } from '../workspace/current-workspace.decorator.js';
|
||||
@@ -50,6 +65,137 @@ export class ProductEditorController {
|
||||
};
|
||||
}
|
||||
|
||||
@Get('product-platform-profiles/:profileId/category-options')
|
||||
async categoryOptions(
|
||||
@Param('profileId') profileId: string,
|
||||
@Query('q') query: string | undefined,
|
||||
@Headers('x-atlas-platform-id') platformId: string | undefined,
|
||||
@CurrentWorkspace() workspace: Workspace,
|
||||
) {
|
||||
return {
|
||||
data: await this.editor.categoryOptions(
|
||||
workspace.id,
|
||||
requiredUuid('profile_id', profileId),
|
||||
requiredUuid('X-Atlas-Platform-Id', platformId),
|
||||
query,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@Post('product-platform-profiles/:profileId/category-syncs')
|
||||
async categorySync(
|
||||
@Param('profileId') profileId: string,
|
||||
@Headers('x-atlas-platform-id') platformId: string | undefined,
|
||||
@Headers('idempotency-key') key: string | undefined,
|
||||
@Req() request: AuthenticatedRequest,
|
||||
@CurrentWorkspace() workspace: Workspace,
|
||||
@Res({ passthrough: true }) response: Response,
|
||||
) {
|
||||
const result = await this.editor.requestCategoryTreeSync(
|
||||
workspace.id,
|
||||
request.auth.userId,
|
||||
requiredUuid('profile_id', profileId),
|
||||
requiredUuid('X-Atlas-Platform-Id', platformId),
|
||||
requiredUuid('Idempotency-Key', key),
|
||||
);
|
||||
response.status(result.status);
|
||||
return result.body;
|
||||
}
|
||||
|
||||
@Get('product-platform-profiles/:profileId/attribute-schema')
|
||||
async attributeSchema(
|
||||
@Param('profileId') profileId: string,
|
||||
@Query('category_id') categoryId: string | undefined,
|
||||
@Headers('x-atlas-platform-id') platformId: string | undefined,
|
||||
@CurrentWorkspace() workspace: Workspace,
|
||||
) {
|
||||
return {
|
||||
data: await this.editor.attributeSchema(
|
||||
workspace.id,
|
||||
requiredUuid('profile_id', profileId),
|
||||
requiredUuid('X-Atlas-Platform-Id', platformId),
|
||||
optionalUuid('category_id', categoryId),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@Post('product-platform-profiles/:profileId/attribute-schema-syncs')
|
||||
async attributeSchemaSync(
|
||||
@Param('profileId') profileId: string,
|
||||
@Query('category_id') categoryId: string | undefined,
|
||||
@Headers('x-atlas-platform-id') platformId: string | undefined,
|
||||
@Headers('idempotency-key') key: string | undefined,
|
||||
@Req() request: AuthenticatedRequest,
|
||||
@CurrentWorkspace() workspace: Workspace,
|
||||
@Res({ passthrough: true }) response: Response,
|
||||
) {
|
||||
const result = await this.editor.requestAttributeSchemaSync(
|
||||
workspace.id,
|
||||
request.auth.userId,
|
||||
requiredUuid('profile_id', profileId),
|
||||
requiredUuid('X-Atlas-Platform-Id', platformId),
|
||||
requiredUuid('Idempotency-Key', key),
|
||||
requiredUuid('category_id', categoryId),
|
||||
);
|
||||
response.status(result.status);
|
||||
return result.body;
|
||||
}
|
||||
|
||||
@Get(
|
||||
'product-platform-profiles/:profileId/attribute-dictionaries/:dictionaryId',
|
||||
)
|
||||
async attributeDictionary(
|
||||
@Param('profileId') profileId: string,
|
||||
@Param('dictionaryId') dictionaryId: string,
|
||||
@Query('q') query: string | undefined,
|
||||
@Query('category_id') categoryId: string | undefined,
|
||||
@Headers('x-atlas-platform-id') platformId: string | undefined,
|
||||
@CurrentWorkspace() workspace: Workspace,
|
||||
) {
|
||||
if (!dictionaryId.trim() || dictionaryId.length > 200)
|
||||
throw new ApiError(400, 'VALIDATION_FAILED', '字典标识不合法');
|
||||
return {
|
||||
data: await this.editor.attributeDictionary(
|
||||
workspace.id,
|
||||
requiredUuid('profile_id', profileId),
|
||||
requiredUuid('X-Atlas-Platform-Id', platformId),
|
||||
dictionaryId,
|
||||
query,
|
||||
optionalUuid('category_id', categoryId),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@Post(
|
||||
'product-platform-profiles/:profileId/attribute-dictionaries/:dictionaryId/syncs',
|
||||
)
|
||||
async attributeDictionarySync(
|
||||
@Param('profileId') profileId: string,
|
||||
@Param('dictionaryId') dictionaryId: string,
|
||||
@Query('category_id') categoryId: string | undefined,
|
||||
@Query('q') query: string | undefined,
|
||||
@Headers('x-atlas-platform-id') platformId: string | undefined,
|
||||
@Headers('idempotency-key') key: string | undefined,
|
||||
@Req() request: AuthenticatedRequest,
|
||||
@CurrentWorkspace() workspace: Workspace,
|
||||
@Res({ passthrough: true }) response: Response,
|
||||
) {
|
||||
if (!dictionaryId.trim() || dictionaryId.length > 200)
|
||||
throw new ApiError(400, 'VALIDATION_FAILED', '字典标识不合法');
|
||||
const result = await this.editor.requestAttributeDictionarySync(
|
||||
workspace.id,
|
||||
request.auth.userId,
|
||||
requiredUuid('profile_id', profileId),
|
||||
requiredUuid('X-Atlas-Platform-Id', platformId),
|
||||
requiredUuid('Idempotency-Key', key),
|
||||
requiredUuid('category_id', categoryId),
|
||||
dictionaryId,
|
||||
query ?? '',
|
||||
);
|
||||
response.status(result.status);
|
||||
return result.body;
|
||||
}
|
||||
|
||||
@Put('products/:productId/editor-draft')
|
||||
async save(
|
||||
@Param('productId') productId: string,
|
||||
@@ -62,19 +208,28 @@ export class ProductEditorController {
|
||||
) {
|
||||
const parsed = saveProductEditorDraftRequestSchema.safeParse(body);
|
||||
if (!parsed.success) {
|
||||
throw new ApiError(422, 'VALIDATION_FAILED', '请求参数不符合要求', parsed.error.issues.map((issue) => ({
|
||||
field: issue.path.join('.'), code: 'INVALID_FIELD', message: issue.message,
|
||||
})));
|
||||
throw new ApiError(
|
||||
422,
|
||||
'VALIDATION_FAILED',
|
||||
'请求参数不符合要求',
|
||||
parsed.error.issues.map((issue) => ({
|
||||
field: issue.path.join('.'),
|
||||
code: 'INVALID_FIELD',
|
||||
message: issue.message,
|
||||
})),
|
||||
);
|
||||
}
|
||||
return (await this.editor.save(
|
||||
workspace.id,
|
||||
request.auth.userId,
|
||||
requiredUuid('product_id', productId),
|
||||
requiredUuid('X-Atlas-Platform-Id', platformId),
|
||||
requiredUuid('Idempotency-Key', key),
|
||||
parseEditorRevision(ifMatch),
|
||||
parsed.data,
|
||||
)).body;
|
||||
return (
|
||||
await this.editor.save(
|
||||
workspace.id,
|
||||
request.auth.userId,
|
||||
requiredUuid('product_id', productId),
|
||||
requiredUuid('X-Atlas-Platform-Id', platformId),
|
||||
requiredUuid('Idempotency-Key', key),
|
||||
parseEditorRevision(ifMatch),
|
||||
parsed.data,
|
||||
)
|
||||
).body;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,8 +240,18 @@ function requiredUuid(field: string, value: string | undefined) {
|
||||
]);
|
||||
}
|
||||
|
||||
function optionalUuid(field: string, value: string | undefined) {
|
||||
if (value === undefined || value === '') return undefined;
|
||||
return requiredUuid(field, value);
|
||||
}
|
||||
|
||||
function parseEditorRevision(value: string | undefined) {
|
||||
const match = value?.match(/^"([1-9]\d*):([1-9]\d*)"$/);
|
||||
if (!match) throw new ApiError(400, 'IF_MATCH_REQUIRED', '请求缺少或格式错误的 If-Match');
|
||||
if (!match)
|
||||
throw new ApiError(
|
||||
400,
|
||||
'IF_MATCH_REQUIRED',
|
||||
'请求缺少或格式错误的 If-Match',
|
||||
);
|
||||
return { product: Number(match[1]), profile: Number(match[2]) };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user