58 lines
2.0 KiB
TypeScript
58 lines
2.0 KiB
TypeScript
|
|
// services/api/src/albums/support.ts —— 相册域服务共用小工具(Task 15 起自
|
||
|
|
// albums.service.ts 抽出,Task 17 起 members/categories 服务同用)。
|
||
|
|
import { HttpStatus } from '@nestjs/common';
|
||
|
|
import { ErrorCodes } from '@photo/contracts';
|
||
|
|
import { ApiException } from '../api-exception';
|
||
|
|
|
||
|
|
/** 统一 404:不可见(含不存在)同码防 id 枚举(契约 albums/categories 404 语义)。 */
|
||
|
|
export function albumNotFound(): ApiException {
|
||
|
|
return new ApiException(
|
||
|
|
ErrorCodes.ALBUM_NOT_FOUND,
|
||
|
|
'相册不存在或不可见',
|
||
|
|
HttpStatus.NOT_FOUND,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 统一 403:可见但无管理权限(协作者视角)。 */
|
||
|
|
export function albumForbidden(message = '仅创建者或管理员可操作'): ApiException {
|
||
|
|
return new ApiException(ErrorCodes.AUTH_FORBIDDEN, message, HttpStatus.FORBIDDEN);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 统一 400(契约 VALIDATION_FAILED)。 */
|
||
|
|
export function validationFailed(message: string): ApiException {
|
||
|
|
return new ApiException(
|
||
|
|
ErrorCodes.VALIDATION_FAILED,
|
||
|
|
message,
|
||
|
|
HttpStatus.BAD_REQUEST,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* P2002 是否命中指定唯一索引。uniqueName 用 Prisma 唯一选择器名(单列
|
||
|
|
* 'publicToken' / 复合 'albumId_userId')。meta.target 两种形态都兼容:
|
||
|
|
* 字段数组(部分引擎/版本)或 DB 约束名字符串(MySQL 实测形如
|
||
|
|
* 'albums_publicToken_key' / 'album_members_albumId_userId_key'——
|
||
|
|
* 表名前缀 + 选择器名 + _key 后缀,两种后缀都匹配)。
|
||
|
|
*/
|
||
|
|
export function isUniqueViolationOn(err: unknown, uniqueName: string): boolean {
|
||
|
|
if (typeof err !== 'object' || err === null) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
const { code, meta } = err as { code?: unknown; meta?: { target?: unknown } };
|
||
|
|
if (code !== 'P2002') {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
const target = meta?.target;
|
||
|
|
if (Array.isArray(target)) {
|
||
|
|
return target.map(String).includes(uniqueName);
|
||
|
|
}
|
||
|
|
if (typeof target === 'string') {
|
||
|
|
return (
|
||
|
|
target === uniqueName ||
|
||
|
|
target.endsWith(`${uniqueName}_key`) ||
|
||
|
|
target.endsWith(`_${uniqueName}_key`)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|