Files
WanJieDaoyou/apps/api/src/alchemy/craft.controller.ts
T
2026-10-03 15:02:19 +08:00

109 lines
3.3 KiB
TypeScript

import {
All,
Controller,
Get,
HttpCode,
HttpException,
Inject,
Post,
UseFilters,
} from '@nestjs/common';
import type { ActiveCultivatorRef } from '@server/lib/auth/types';
import { redisLockErrorResponse } from '@server/lib/http/errors';
import { AlchemyServiceError } from '@server/lib/services/AlchemyServiceError';
import { PlayerCommandIdempotencyError } from '@server/lib/services/CommandExecutors';
import { CraftCommandError } from '@server/lib/services/CraftApplicationService';
import { QiServiceError } from '@server/lib/services/QiService';
import { z } from 'zod';
import { Access, CurrentCultivator } from '../auth/access';
import { apiErrorFilter } from '../http/error-filter';
import { FirstQuery } from '../http/first-query';
import { JsonBody } from '../http/json-body';
import { CraftCommandSchema, CraftSchema } from './alchemy-input';
import { CraftService } from './craft.service';
const retired = new Set(['refine', 'create_skill', 'create_gongfa']);
const retiredMessage = '旧功法、神通及装备生产已停用,历史物品保留在洞府宝库';
const CraftErrors = apiErrorFilter((error) => {
const lock = redisLockErrorResponse(error);
if (lock) return lock;
if (error instanceof z.ZodError || error instanceof SyntaxError)
return Response.json(
{ success: false, error: '请求参数无效' },
{ status: 400 },
);
if (
error instanceof AlchemyServiceError ||
error instanceof CraftCommandError ||
error instanceof PlayerCommandIdempotencyError ||
error instanceof QiServiceError
)
return Response.json(
{ success: false, error: error.message },
{ status: error.status },
);
console.error('[alchemy] request failed', error);
return Response.json(
{ success: false, error: '炼丹请求失败,请重新核对材料' },
{ status: 500 },
);
});
@Controller('api/craft')
@Access('active')
@UseFilters(CraftErrors)
export class CraftController {
constructor(@Inject(CraftService) private readonly craft: CraftService) {}
@Get()
preview(
@CurrentCultivator() actor: ActiveCultivatorRef,
@FirstQuery() query: Record<string, string | undefined>,
) {
if (query.craftType && retired.has(query.craftType))
throw new HttpException({ error: retiredMessage }, 410);
const input = CraftSchema.parse({
craftType: query.craftType,
alchemyMode: query.alchemyMode,
materialIds: (query.materialIds ?? '').split(','),
materialQuantities: query.materialQuantities
? JSON.parse(query.materialQuantities)
: undefined,
formulaId: query.formulaId,
materialVersions: query.materialVersions
? JSON.parse(query.materialVersions)
: {},
});
return this.craft.preview(actor, input);
}
@Post()
@HttpCode(200)
execute(
@CurrentCultivator() actor: ActiveCultivatorRef,
@JsonBody() body: unknown,
) {
if (
body &&
typeof body === 'object' &&
'craftType' in body &&
typeof body.craftType === 'string' &&
retired.has(body.craftType)
)
throw new HttpException({ error: retiredMessage }, 410);
return this.craft.execute(actor, CraftCommandSchema.parse(body));
}
@All('pending')
@HttpCode(410)
pending() {
return { error: retiredMessage };
}
@All('confirm')
@HttpCode(410)
confirm() {
return { error: retiredMessage };
}
}