82 lines
2.3 KiB
TypeScript
82 lines
2.3 KiB
TypeScript
import {
|
||
Injectable,
|
||
NestInterceptor,
|
||
ExecutionContext,
|
||
CallHandler,
|
||
ForbiddenException,
|
||
Logger,
|
||
} from '@nestjs/common';
|
||
import { Observable } from 'rxjs';
|
||
import { PrePlantingClient } from './pre-planting.client';
|
||
|
||
/**
|
||
* 预种授权申请拦截器
|
||
*
|
||
* 仅拦截用户端授权申请路由(POST /authorizations/...)
|
||
* 规则:
|
||
* - 无预种记录(纯认种用户)→ 直接放行
|
||
* - 有预种记录且已合并成树 → 放行
|
||
* - 有预种记录但未合并 → 拦截
|
||
* - planting-service 不可达 → 放行(fail-open,不影响现有功能)
|
||
*/
|
||
@Injectable()
|
||
export class PrePlantingAuthorizationInterceptor implements NestInterceptor {
|
||
private readonly logger = new Logger(PrePlantingAuthorizationInterceptor.name);
|
||
|
||
private readonly protectedPaths = [
|
||
'/authorizations/community',
|
||
'/authorizations/province',
|
||
'/authorizations/city',
|
||
'/authorizations/self-apply',
|
||
];
|
||
|
||
constructor(private readonly client: PrePlantingClient) {}
|
||
|
||
async intercept(
|
||
context: ExecutionContext,
|
||
next: CallHandler,
|
||
): Promise<Observable<unknown>> {
|
||
const req = context.switchToHttp().getRequest();
|
||
|
||
// 仅拦截 POST 请求
|
||
if (req.method !== 'POST') {
|
||
return next.handle();
|
||
}
|
||
|
||
// 仅拦截特定路由
|
||
const reqPath: string = req.path || req.url || '';
|
||
if (!this.protectedPaths.some((p) => reqPath.endsWith(p))) {
|
||
return next.handle();
|
||
}
|
||
|
||
const accountSequence = req.user?.accountSequence;
|
||
if (!accountSequence) {
|
||
return next.handle();
|
||
}
|
||
|
||
try {
|
||
const eligibility = await this.client.getEligibility(accountSequence);
|
||
|
||
// 无预种记录 → 纯认种用户,直接放行
|
||
if (!eligibility.hasPrePlanting) {
|
||
return next.handle();
|
||
}
|
||
|
||
// 有预种但未满足条件 → 拦截
|
||
if (!eligibility.canApplyAuthorization) {
|
||
throw new ForbiddenException(
|
||
'须累积购买5份预种计划合并成树后方可申请授权',
|
||
);
|
||
}
|
||
} catch (error) {
|
||
if (error instanceof ForbiddenException) throw error;
|
||
// planting-service 不可达,默认放行
|
||
this.logger.warn(
|
||
`[PRE-PLANTING] Failed to check eligibility for ${accountSequence}, allowing through`,
|
||
);
|
||
}
|
||
|
||
return next.handle();
|
||
}
|
||
}
|