feat(capability): 补齐全部后端 API 能力拦截

## 背景
审计发现 13 项用户能力中,部分后端 API 端点缺少 @RequireCapability
拦截,用户可绕过前端 UI 限制直接调用 API。本次逐服务补齐。

## Phase 1: 高优先级 — 操作端点

### auth-service
- POST /auth/password/change → @RequireCapability('PROFILE_EDIT')
  修改登录密码需要 PROFILE_EDIT 能力
- POST /auth/trade-password/set → @RequireCapability('PROFILE_EDIT')
  设置交易密码需要 PROFILE_EDIT 能力
- POST /auth/trade-password/change → @RequireCapability('PROFILE_EDIT')
  修改交易密码需要 PROFILE_EDIT 能力
- POST /auth/trade-password/verify → @RequireCapability('TRADING')
  验证交易密码是交易前置步骤,需要 TRADING 能力

### trading-service
- POST /c2c/orders/:orderNo/cancel → @RequireCapability('C2C')
  C2C 取消订单是唯一缺失 C2C 能力检查的操作端点

## Phase 2: 低优先级 — 查看端点

### trading-service
- GET /trading/orders → VIEW_RECORDS (用户订单列表)
- GET /trading/trades → VIEW_RECORDS (成交记录)
- GET /transfers/history → VIEW_RECORDS (划转历史)
- GET /p2p/transfers/:accountSequence → VIEW_RECORDS (P2P转账历史)
- GET /c2c/orders/my → VIEW_RECORDS (我的C2C订单)

### contribution-service
- GET /contribution/accounts/:accountSequence/active → VIEW_ASSET
- GET /contribution/accounts/:accountSequence/planting-ledger → VIEW_RECORDS

## 能力覆盖总览 (补齐后)
| 能力 | 端点数 | 状态 |
|------|--------|------|
| LOGIN | 全局 |  JwtAuthGuard 拦截 |
| TRADING | 3 |  createOrder, cancelOrder, verifyTradePassword |
| C2C | 6 |  create, take, cancel, confirmPayment, confirmReceived, uploadProof |
| TRANSFER_IN | 1 |  transferIn |
| TRANSFER_OUT | 1 |  transferOut |
| P2P_SEND | 1 |  transfer |
| KYC | 1 |  submitKyc |
| PROFILE_EDIT | 3 |  changePassword, setTradePassword, changeTradePassword |
| VIEW_ASSET | 2 |  getMyAsset, getActiveContribution |
| VIEW_TEAM | 2 |  getMyTeamInfo, getDirectReferrals |
| VIEW_RECORDS | 6 |  各服务历史记录端点 |
| P2P_RECEIVE | 0 | 仅前端展示控制(无后端操作端点) |
| MINING_CLAIM | 0 | mining-service 需后续重构(@Public 类级别) |

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-02-28 05:22:37 -08:00
parent 97f8b7339f
commit f4c9535e12
7 changed files with 20 additions and 4 deletions

View File

@ -9,7 +9,9 @@ import {
import { ThrottlerGuard } from '@nestjs/throttler';
import { PasswordService } from '@/application/services';
import { JwtAuthGuard } from '@/shared/guards/jwt-auth.guard';
import { CapabilityGuard } from '@/shared/guards/capability.guard';
import { CurrentUser } from '@/shared/decorators/current-user.decorator';
import { RequireCapability } from '@/shared/decorators/require-capability.decorator';
class ResetPasswordDto {
phone: string;
@ -46,7 +48,8 @@ export class PasswordController {
*/
@Post('change')
@HttpCode(HttpStatus.OK)
@UseGuards(JwtAuthGuard)
@UseGuards(JwtAuthGuard, CapabilityGuard)
@RequireCapability('PROFILE_EDIT')
async changePassword(
@CurrentUser() user: { accountSequence: string },
@Body() dto: ChangePasswordDto,

View File

@ -11,7 +11,9 @@ import { IsString, IsNotEmpty } from 'class-validator';
import { ThrottlerGuard } from '@nestjs/throttler';
import { TradePasswordService } from '@/application/services/trade-password.service';
import { JwtAuthGuard } from '@/shared/guards/jwt-auth.guard';
import { CapabilityGuard } from '@/shared/guards/capability.guard';
import { CurrentUser } from '@/shared/decorators/current-user.decorator';
import { RequireCapability } from '@/shared/decorators/require-capability.decorator';
class SetTradePasswordDto {
@IsString()
@ -62,7 +64,8 @@ export class TradePasswordController {
*/
@Post('set')
@HttpCode(HttpStatus.OK)
@UseGuards(JwtAuthGuard)
@UseGuards(JwtAuthGuard, CapabilityGuard)
@RequireCapability('PROFILE_EDIT')
async setTradePassword(
@CurrentUser() user: { accountSequence: string },
@Body() dto: SetTradePasswordDto,
@ -82,7 +85,8 @@ export class TradePasswordController {
*/
@Post('change')
@HttpCode(HttpStatus.OK)
@UseGuards(JwtAuthGuard)
@UseGuards(JwtAuthGuard, CapabilityGuard)
@RequireCapability('PROFILE_EDIT')
async changeTradePassword(
@CurrentUser() user: { accountSequence: string },
@Body() dto: ChangeTradePasswordDto,
@ -102,7 +106,8 @@ export class TradePasswordController {
*/
@Post('verify')
@HttpCode(HttpStatus.OK)
@UseGuards(JwtAuthGuard)
@UseGuards(JwtAuthGuard, CapabilityGuard)
@RequireCapability('TRADING')
async verifyTradePassword(
@CurrentUser() user: { accountSequence: string },
@Body() dto: VerifyTradePasswordDto,

View File

@ -79,6 +79,7 @@ export class ContributionController {
}
@Get('accounts/:accountSequence/active')
@RequireCapability('VIEW_ASSET')
@ApiOperation({ summary: '获取账户活跃算力统计' })
@ApiParam({ name: 'accountSequence', description: '账户序号' })
@ApiResponse({ status: 200, type: ActiveContributionResponse })
@ -105,6 +106,7 @@ export class ContributionController {
}
@Get('accounts/:accountSequence/planting-ledger')
@RequireCapability('VIEW_RECORDS')
@ApiOperation({ summary: '获取账户认种分类账' })
@ApiParam({ name: 'accountSequence', description: '账户序号' })
@ApiQuery({ name: 'page', required: false, type: Number, description: '页码' })

View File

@ -111,6 +111,7 @@ export class C2cController {
}
@Get('orders/my')
@RequireCapability('VIEW_RECORDS')
@ApiOperation({ summary: '获取我的C2C订单' })
@ApiResponse({ status: 200, description: '我的订单列表' })
async getMyOrders(
@ -213,6 +214,7 @@ export class C2cController {
}
@Post('orders/:orderNo/cancel')
@RequireCapability('C2C')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: '取消C2C订单' })
@ApiParam({ name: 'orderNo', description: '订单号' })

View File

@ -59,6 +59,7 @@ export class P2pTransferController {
}
@Get('transfers/:accountSequence')
@RequireCapability('VIEW_RECORDS')
@ApiOperation({ summary: '获取P2P转账历史' })
@ApiParam({ name: 'accountSequence', required: true, description: '账户序列号' })
@ApiQuery({ name: 'page', required: false, type: Number })

View File

@ -88,6 +88,7 @@ export class TradingController {
}
@Get('orders')
@RequireCapability('VIEW_RECORDS')
@ApiOperation({ summary: '获取用户订单列表' })
@ApiQuery({ name: 'page', required: false, type: Number })
@ApiQuery({ name: 'pageSize', required: false, type: Number })
@ -127,6 +128,7 @@ export class TradingController {
}
@Get('trades')
@RequireCapability('VIEW_RECORDS')
@ApiOperation({ summary: '获取用户成交记录(含手续费明细)' })
@ApiQuery({ name: 'page', required: false, type: Number })
@ApiQuery({ name: 'pageSize', required: false, type: Number })

View File

@ -40,6 +40,7 @@ export class TransferController {
}
@Get('history')
@RequireCapability('VIEW_RECORDS')
@ApiOperation({ summary: '获取划转历史' })
@ApiQuery({ name: 'page', required: false, type: Number })
@ApiQuery({ name: 'pageSize', required: false, type: Number })