Philani50/fraud-detection-engine-
GitHub: Philani50/fraud-detection-engine-
一个基于 Spring Boot 的生产级金融欺诈检测微服务,通过可配置的规则引擎对交易进行实时风险评估并生成告警。
Stars: 0 | Forks: 0
# 欺诈检测引擎
一个生产级的 Spring Boot 微服务,可针对可配置的欺诈检测规则集实时评估金融交易。警报会被持久化,可通过 Prometheus 指标进行监控,并受 OAuth2 JWT 身份验证保护。
## 目录
- [概述](#overview)
- [架构](#architecture)
- [欺诈规则](#fraud-rules)
- [代码指南 — 每个类的功能](#code-guide--what-each-class-does)
- [技术栈](#tech-stack)
- [项目结构](#project-structure)
- [入门指南](#getting-started)
- [API 参考](#api-reference)
- [配置](#configuration)
- [安全性](#security)
- [数据库](#database)
- [可观测性](#observability)
- [测试](#testing)
## 概述
欺诈检测引擎通过 REST 接收交易 payload,保存该交易,根据所有已启用的欺诈规则对其进行评估,持久化生成的警报,并将评估结果返回给调用者。
规则可以通过配置独立启用或禁用,可以独立隔离测试,并遵循开闭原则 — 添加新规则只需新建一个类,无需对现有代码进行任何改动。
## 架构
```
POST /v1/fraud/evaluations
│
▼
FraudController
│ maps request → TransactionEntity (MapStruct)
│ saves transaction
▼
FraudEvaluationService
│ builds RuleContext (memoised client profile, DB-backed queries)
│ iterates List in @Order sequence
│ for each enabled + applicable rule → evaluate()
│ persists alerts via FraudPersistenceService
│ increments Micrometer counters + records Timer
▼
FraudAlertEntity (Flyway-managed PostgreSQL / H2 for local)
```
### 关键设计决策
| 决策 | 理由 |
|---|---|
| `RuleContext` 普通对象(非 Spring bean) | 按请求创建;持有一个备忘录模式的 `clientProfileSupplier`,因此无论有多少规则调用 `getClientProfile()`,每次评估最多只会访问一次数据库 |
| 不向规则传递原始的 `List` | 所有时间窗口查询都委托给建立了索引的数据库方法 — 防止 O(n) 内存陷阱 |
| `FraudAlertEntity` 上的 `@Builder.Default status = PENDING` | 确保警报状态永不为 null,而无需依赖无参构造函数的副作用 |
| 使用 AES-256-GCM 加密 PII 字段 | 认证加密可防止密文被篡改;每次加密调用都会生成随机的 12 字节 IV |
| 选择 Flyway 而非 `ddl-auto` | Schema 迁移在所有环境中均具有版本控制、可审计且可重现 |
## 欺诈规则
系统内置了六条规则。默认全部启用,并且可以独立控制开关。
| 规则 | 代码 | 严重性 | 触发条件 |
|---|---|---|---|
| 长期结构化拆分 | `RULE-001` | HIGH | 24 小时内发生 3 笔或以上低于 10,000 兰特的现金交易,且总额 ≥ 10,000 兰特 |
| 资料变更后大额交易 | `RULE-002` | CRITICAL | 在 PII 资料更新后的 48 小时内,发生金额 ≥ 50,000 兰特的交易 |
| 可疑消费行为 | `RULE-003` | MEDIUM | 交易金额超过客户月平均收入的 300% |
| 交易频率激增 | `RULE-004` | HIGH | 同一客户在过去 1 小时内发生 10 笔或以上交易 |
| 整数金额模式 | `RULE-005` | MEDIUM | 24 小时内发生 3 笔或以上金额可被 1,000 兰特整除的交易 |
| 高风险快速转移 | `RULE-006` | CRITICAL | 在 30 分钟内,汇出转账/取款金额 ≥ 20,000 兰特入金存款的 80% |
## 代码指南 — 每个类的功能
本节用通俗的语言解释每一个类,以便您在无需具备框架先备知识的情况下也能浏览代码库。
### 入口点
#### `Application.java`
整个服务的起点。当您运行应用程序时,Java 会调用此类中的 `main` 方法。它会指示 Spring Boot 启动并自动加载所有其他类。
### 控制器 — “正门”
控制器是接收来自外部世界(例如 Postman 或其他服务)HTTP 请求的类。
#### `FraudController.java`
处理主端点 `POST /v1/fraud/evaluations`。当一笔交易传入时:
1. 验证请求字段
2. 将其转换为数据库对象
3. 保存该对象
4. 将其发送给欺诈评估引擎
5. 返回结果(触发了哪些规则,创建了哪些警报)
#### `ServiceController.java`
处理 `GET /api/v1/health`。返回一条简单的“服务已上线”消息。供监控工具检查应用程序是否正在运行。无需身份验证。
### 服务层 — “大脑”
服务包含业务逻辑。它们位于控制器和数据库之间。
#### `FraudEvaluationService.java`
一个接口(契约),声明:“实现我的类必须拥有一个 `evaluate` 方法,该方法接收一笔交易并返回一个警报列表。”这使得代码更容易替换或测试。
#### `FraudEvaluationServiceImpl.java`
`FraudEvaluationService` 的实际实现。这是编排器 — 它负责:
1. 为交易构建一个 `RuleContext`
2. 循环遍历所有 6 条欺诈规则
3. 跳过已禁用或不适用的规则
4. 对每条适用的规则调用 `evaluate()`
5. 保存所有生成的警报
6. 记录计时指标并记录所有日志
### 欺诈规则 — “侦探”
每条欺诈规则都是一个独立的类。它们都实现了相同的 `FraudRule` 接口,以便引擎可以统一对待它们。
#### `FraudRule.java`
每条规则都必须遵循的接口(蓝图)。它定义了每条规则必须回答的三个问题:
- `isEnabled()` — 这条规则是否应该运行?
- `isApplicable(transaction)` — 这条规则与这种类型的交易相关吗?
- `evaluate(transaction, context)` — 这笔交易看起来可疑吗?
#### `StructuringRule.java` — RULE-001
检测结构化拆分:一种某人进行多笔小额现金存款以避免触发大额交易警报的模式。当 24 小时内发生 3 笔或以上低于 10,000 兰特且总额达到 10,000 兰特以上的存款时触发。
#### `ProfileChangeRule.java` — RULE-002
标记在客户个人资料更新后不久发生的大额交易。资料变更后紧接着进行大额转账是一种常见的欺诈模式。
#### `SuspiciousSpendRule.java` — RULE-003
将交易金额与客户的月平均收入进行比较。如果某人在一笔交易中的支出超过其月收入的 3 倍,则会被标记。
#### `VelocityRule.java` — RULE-004
检测异常快速的交易活动。如果客户在一小时内进行 10 笔或更多交易,则会触发 — 这可能表明存在自动化攻击或账户被盗用。
#### `RoundAmountRule.java` — RULE-005
寻找具有可疑整数金额(例如 5,000 兰特、10,000 兰特、2,000 兰特)的交易模式。合法购买极少会以精确的千位整数结尾。
#### `RapidMovementRule.java` — RULE-006
检测“快进快出”的资金流动:大额存款后迅速提取大部分相同资金。这是一种典型的洗钱模式。
### 规则工具 — “助手”
#### `RuleContext.java`
为每次交易评估全新创建的辅助对象。它为规则提供了安全、高效的数据库数据(近期交易、客户资料)访问权限,而无需每个规则直接查询数据库。重要的是,客户资料只会获取一次 — 即使多个规则请求它。
#### `RuleContextFactory.java`
负责构建 `RuleContext`。它连接数据库连接和备忘录模式的资料查找功能,以便将准备就绪的 `RuleContext` 交给每个规则使用。
#### `FraudAlertFactory.java`
一个以一致方式构建 `FraudAlertEntity`(警报的数据库记录)的工厂。每个规则都使用它而不是手动构建警报,从而确保它们具有相同的外观。
#### `TimeWindowCalculator.java`
一个用于计算时间边界的小助手。例如,“整整 24 小时前的时间是多少?”规则使用它来定义其回溯窗口,而无需每个规则重复编写相同的日期算术。
### 持久层 — “数据库层”
这些类处理与保存和加载数据相关的所有事情。
#### `FraudPersistenceService.java`
应用程序和数据库之间的唯一联系点。所有交易、客户资料和欺诈警报的读写都通过此类进行。它处理错误日志记录,并将数据库异常包装为有意义的领域错误。
#### `TransactionEntity.java`
表示 `transaction` 数据库表中的一行。每个字段映射到一个列。这就是新交易传入时保存的内容。
#### `ClientProfileEntity.java`
表示 `client_profile` 表中的一行。包含有关客户的个人详细信息 — 姓名、身份证号、平均收入、风险评级等。`fullName` 和 `idNumber` 字段在保存前会自动加密,在加载时自动解密。
#### `FraudAlertEntity.java`
表示 `fraud_alert` 表中的一行。存储规则触发时的结果 — 触发了哪条规则、风险评分是多少、详细信息是什么以及当前状态(PENDING、UNDER_REVIEW 等)。
#### `PpiChangeRecord.java`
嵌入在 `ClientProfileEntity` 中的子记录。每当客户的个人详细信息发生更改时,都会在此处添加一条记录,跟踪更改的内容、时间以及旧值是什么。旧值和新值也会被加密。
#### `TransactionRepository.java`
提供交易的数据库查询方法。例如:“查找过去一小时内该客户的所有交易”或“计算该客户今天的交易数量”。
#### `ClientProfileRepository.java`
提供客户资料的数据库查询方法。例如:“查找客户 ID 为 X 的资料。”
#### `FraudAlertRepository.java`
提供欺诈警报的数据库访问 — 在保存新生成的警报时使用。
#### `EncryptedStringConverter.java`
一个位于应用程序和数据库之间的隐形助手。每当保存敏感文本字段(如姓名或身份证号)时,此类会自动将其加密。当重新加载时,此类会自动将其解密。使用它的规则或服务完全无需关心加密过程。
### 安全 — “锁”
#### `FieldEncryptionService.java`
定义了两个操作的接口:`encrypt` 和 `decrypt`。将接口与实现分离使得在需要时可以轻松更换加密算法。
#### `AesGcmFieldEncryptionService.java`
实际的加密实现。使用 AES-256-GCM,这是一种现代认证加密标准。每次加密调用都会生成一个新的随机 IV(初始化向量),因此相同的数据永远不会产生两次相同的密文。
#### `SecurityConfig.java`
配置哪些端点需要身份验证,哪些是公开的。设置 OAuth2 JWT 资源服务器,以便传入请求必须包含由配置的身份提供者签名的有效 JWT token。同时启用控制器方法上的 `@PreAuthorize` 注解。
### 适配器 — “外部调用”
适配器包处理与此应用程序外部的外部服务的通信。
#### `ClientProfileAdapter.java`
定义了一个操作的接口:`fetchProfile(clientId)`。任何从外部服务获取客户资料的类都必须实现此接口。
#### `ClientProfileAdapterImpl.java`
真正的实现。它调用外部的 CRM 或核心银行服务以获取客户资料。它装饰有:
- **CircuitBreaker** — 如果外部服务宕机,在重复失败后停止调用它
- **TimeLimiter** — 如果调用耗时超过 3 秒,则取消该调用
- **Bulkhead** — 限制同时进行的飞行调用数量
- **Cache** — 如果最近获取过同一客户的资料,则返回缓存副本,而不是再次调用外部服务
如果外部服务失败,它会优雅地回退并返回空结果 — 引擎将继续使用不需要该资料的规则进行评估。
#### `ClientProfileProxyClient.java`
声明式 HTTP 客户端。该接口声明了端点(`GET /v1/clients/{clientId}/profile`),Spring 会自动生成实现,而无需编写原始 HTTP 代码。
#### `ClientProfileProxyConfig.java`
配置 HTTP 客户端 — 设置基本 URL、超时、`X-Channel-Source` 标头以及针对 401 Unauthorized 响应的错误处理器。
#### `ClientProfileProperties.java`
保存外部服务 URL 和超时设置的配置记录。值来自 `application.yml`,可以通过环境变量覆盖。
#### `ClientProfileResponse.java`
一个表示来自外部客户资料服务的 JSON 响应的记录(不可变数据对象)。字段包括客户 ID、全名、身份证号、收入和风险评级。
### 映射器 — “翻译器”
映射器在不同对象类型之间进行转换,以保持其余代码的整洁。
#### `FraudMapper.java`
在 API 层和数据库层之间进行转换:
- 将传入的 `FraudEvaluationRequest`(来自 API)转换为 `TransactionEntity`(用于数据库)
- 将 `FraudAlertEntity`(来自数据库)转换为 `FraudAlertResponse`(用于 API 响应)
- 构建返回给调用者的完整 `FraudEvaluationResponse`
#### `ClientProfileMapper.java`
将 `ClientProfileResponse`(来自外部服务)转换为 `ClientProfileEntity`(规则使用的内部表示)。
### 模型 — “数据形态”
这些是简单的数据容器 — 它们定义了进出 API 的数据形态。
#### `FraudEvaluationRequest.java`
定义调用者在提交交易进行评估时必须发送的内容。字段包括账户 ID、客户 ID、金额、币种、交易类型渠道和时间戳。所有必填字段在处理请求前都会进行验证。
#### `FraudEvaluationResponse.java`
定义调用者在评估后收到的内容。包括交易 ID、客户 ID、生成的警报计数以及完整的警报详细信息列表。
#### `FraudAlertResponse.java`
响应中单个警报的数据形态。包括规则代码、规则名称、风险评分、严重性、状态、详细信息及其创建时间。
#### `ErrorResponse.java`
错误响应的标准形态。每当出现问题时(验证失败、服务器错误等),响应体都会遵循此结构,包含状态码、错误类型、消息、时间戳和关联 ID。
### 异常处理 — “错误管理者”
#### `BusinessException.java`
所有预期业务错误的抽象基类。应用程序主动抛出的任何错误都应扩展此类 — 它标志着“这是一个已知的、已处理的失败”,而不是意外崩溃。
#### `FraudEvaluationException.java`
当欺诈规则在评估期间意外崩溃时抛出。这是编程或基础设施错误,而不是业务验证失败。
#### `FraudDatabaseException.java`
当数据库操作失败时抛出。将底层错误包装为干净的领域异常。
#### `ClientProfileServiceException.java`
当外部客户资料服务返回错误(例如 HTTP 4xx/5xx)时抛出。标志着下游服务调用失败。
#### `FieldEncryptionException.java`
当加密或解密失败时抛出。通常表示密文损坏或密钥不正确。
#### `GlobalExceptionHandler.java`
捕获应用程序任何地方抛出的每个异常,并将其转换为干净、一致的 `ErrorResponse` JSON 的单一类。如果没有它,未处理的异常将向调用者暴露内部堆栈跟踪。
### 配置 — “设置”
#### `CacheConfig.java`
设置应用程序使用的两个内存缓存:
- `clientProfiles` — 缓存客户资料查找 5 分钟,这样就不会在每次交易时都调用外部服务
- `recentTransactionCounts` — 缓存频率计数 2 分钟
#### `EncryptionProperties.java`
保存加密密钥的配置记录。密钥从 `APP_ENCRYPTION_KEY` 环境变量加载,因此永远不会硬编码。
#### `RuleEngineConfig.java`
每条规则具有一个布尔值(`rule001Enabled` 到 `rule006Enabled`)的配置记录。值来自 `application.yml`。将值设置为 `false` 可禁用该规则,而无需重新部署。
#### `OpenApiConfig.java`
配置自动生成 API 文档的 Swagger UI。定义服务标题、版本、描述和联系信息。文档可在 `/swagger-ui/index.html` 访问。
#### `SecurityConfig.java`
配置 Spring Security。定义哪些端点是公开的,哪些需要有效的 JWT。设置 OAuth2 JWT 资源服务器并启用方法级的 `@PreAuthorize` 检查。
## 技术栈
| 层 | 技术 |
|---|---|
| 语言 | Java 25 |
| 框架 | Spring Boot 3.5.14 |
| 构建 | Maven(BOM 管理依赖) |
| 数据库(本地) | H2 内存数据库(PostgreSQL 兼容模式) |
| 数据库(生产) | PostgreSQL |
| Schema 迁移 | Flyway |
| 字段加密 | AES-256-GCM (`javax.crypto`) |
| 对象映射 | MapStruct 1.6.3 |
| 缓存 | Caffeine |
| 弹性韧性 | Resilience4j (CircuitBreaker, TimeLimiter, Bulkhead) |
| 可观测性 | Micrometer + Prometheus + OpenTelemetry (OTLP) |
| 安全性 | Spring Security OAuth2 Resource Server (JWT) |
| API 文档 | SpringDoc OpenAPI 2.8.13 |
| 代码质量 | Spotless (Palantir Java Format) + Checkstyle |
## 项目结构
```
src/main/java/za/co/capitecbank/
├── adapter/ # ClientProfile external service integration
│ ├── ClientProfileAdapter.java
│ ├── ClientProfileProxyClient.java
│ ├── config/ # @ConfigurationProperties + RestClient factory
│ ├── impl/ # Resilience4j-decorated implementation
│ └── model/ # ClientProfileResponse record
├── config/ # CacheConfig, EncryptionProperties, OpenApiConfig,
│ # RuleEngineConfig, SecurityConfig
├── controller/ # FraudController, ServiceController
├── enums/ # AlertSeverity, AlertStatus, Channel,
│ # RiskRating, TransactionType
├── exception/ # BusinessException hierarchy + GlobalExceptionHandler
├── fraudrule/
│ ├── FraudRule.java # Strategy interface
│ ├── context/ # RuleContext + RuleContextFactory
│ ├── impl/ # Six rule implementations
│ └── util/ # FraudAlertFactory, TimeWindowCalculator
├── mapper/ # FraudMapper (MapStruct), ClientProfileMapper
├── model/
│ ├── request/ # FraudEvaluationRequest record
│ └── response/ # FraudEvaluationResponse, FraudAlertResponse,
│ # ErrorResponse records
├── persistence/
│ ├── FraudPersistenceService.java
│ ├── converter/ # EncryptedStringConverter (AES-256-GCM)
│ ├── entity/ # TransactionEntity, ClientProfileEntity,
│ │ # FraudAlertEntity, PpiChangeRecord
│ └── repository/ # Spring Data JPA repositories
├── security/
│ ├── FieldEncryptionService.java
│ └── impl/AesGcmFieldEncryptionService.java
└── service/
├── FraudEvaluationService.java
└── impl/FraudEvaluationServiceImpl.java
src/main/resources/
├── application.yml
└── db/migration/
├── V1__create_transaction_table.sql
├── V2__create_client_profile_table.sql
├── V3__create_fraud_alert_table.sql
└── V4__seed_test_client_profiles.sql
```
## 入门指南
### 前置条件
- Java 25(推荐使用 Amazon Corretto 25)
- Maven 3.9+ 或 `mvnd`
- Docker Desktop
- PowerShell (Windows) 或 Terminal (Mac/Linux)
### 步骤 1 — 克隆仓库
```
git clone https://github.com/CP379076/fraud-detection-engine.git
cd fraud-detection-engine
```
### 步骤 2 — 安装 Java 25
从以下地址下载并安装 **Amazon Corretto 25**:
```
https://aws.amazon.com/corretto
```
安装后,在每次构建会话之前在 PowerShell 中设置 `JAVA_HOME`:
```
$env:JAVA_HOME = "C:\Users\\.jdks\corretto-25.0.3"
```
验证:
```
java -version
```
### 步骤 3 — 导入 Zscaler SSL 证书(仅限企业网络)
**导出证书:**
```
$tcpClient = New-Object System.Net.Sockets.TcpClient("repo.maven.apache.org", 443)
$sslStream = New-Object System.Net.Security.SslStream($tcpClient.GetStream(), $false, {$true})
$sslStream.AuthenticateAsClient("repo.maven.apache.org")
$chain = New-Object System.Security.Cryptography.X509Certificates.X509Chain
$chain.Build($sslStream.RemoteCertificate)
$root = $chain.ChainElements[$chain.ChainElements.Count - 1].Certificate
[System.IO.File]::WriteAllBytes("C:\Users\$env:USERNAME\corporate-root.cer", $root.Export('Cert'))
Write-Host "Exported: $($root.Subject)"
```
**将其导入 JVM 信任库(以管理员身份运行 PowerShell):**
```
$keytool = "C:\Users\$env:USERNAME\.jdks\corretto-25.0.3\bin\keytool.exe"
$keystore = "C:\Users\$env:USERNAME\.jdks\corretto-25.0.3\lib\security\cacerts"
$certfile = "C:\Users\$env:USERNAME\corporate-root.cer"
& $keytool -import -trustcacerts -alias zscaler-root -keystore $keystore -storepass changeit -file $certfile -noprompt
```
预期输出:
```
Certificate was added to keystore
```
### 步骤 4 — 构建 JAR
```
mvnd -s settings-personal.xml clean package -DskipTests
```
或使用标准 Maven:
```
mvn clean package -DskipTests
```
预期输出:
```
[INFO] BUILD SUCCESS
```
### 步骤 5 — 启动完整技术栈
```
docker-compose up --build
```
Docker 按顺序启动三个容器:
| 容器 | 端口 | 用途 |
|---|---|---|
| `fraud-detection-postgres` | `5432` | PostgreSQL 数据库 |
| `fraud-detection-keycloak` | `9090` | OAuth2 身份提供者 |
| `fraud-detection-engine` | `8080` | 应用程序 |
等待直到您看到:
```
fraud-detection-engine | Started Application in X seconds
```
### 步骤 6 — 验证技术栈是否已启动
```
curl.exe http://localhost:8080/actuator/health
```
预期:`{"status":"UP"}`
```
curl.exe http://localhost:8080/api/v1/health
```
预期:`fraud-detection-engine is live`
### 步骤 7 — 获取 JWT token
```
$body = "grant_type=client_credentials&client_id=fraud-detection-engine&client_secret=fraud-engine-secret"
curl.exe -X POST "http://localhost:9090/realms/fraud-detection-engine/protocol/openid-connect/token" -H "Content-Type: application/x-www-form-urlencoded" -d $body
```
从响应中复制 `access_token` 并存储:
```
$token = ""
```
token 将在 1 小时后过期。重新运行上述命令以获取新 token。
### 步骤 8 — 测试欺诈评估端点
将请求体写入文件(避免 PowerShell 引用问题):
```
'{"account_id":"ACC-001","client_id":"CLIENT-001","amount":500.00,"currency":"ZAR","transaction_type":"DEPOSIT","channel":"ONLINE","timestamp":"2026-06-03T12:00:00"}' | Out-File -FilePath body.json -Encoding utf8NoBOM
curl.exe -X POST "http://localhost:8080/v1/fraud/evaluations" -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d "@body.json"
```
### 停止技术栈
```
docker-compose down
```
若要同时清除数据库卷:
```
docker-compose down -v
```
## API 参考
Base URL: `http://localhost:8080`
### 端点
| 方法 | 路径 | 认证 | 描述 |
|---|---|---|---|
| `GET` | `/actuator/health` | 无 | Spring 健康检查 |
| `GET` | `/api/v1/health` | 无 | 服务存活检查 |
| `POST` | `/v1/fraud/evaluations` | Bearer JWT | 评估交易 |
### POST /v1/fraud/evaluations
**请求字段:**
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `account_id` | String | 是 | |
| `client_id` | String | 是 | |
| `amount` | Decimal | 是 | 最小值 0.01 |
| `currency` | String | 是 | 3 个字符,例如 `ZAR` |
| `transaction_type` | Enum | 是 | `DEPOSIT` `WITHDRAWAL` `TRANSFER` `PAYMENT` |
| `channel` | Enum | 是 | `ONLINE` `ATM` `BRANCH` `MOBILE` |
| `timestamp` | DateTime | 是 | ISO 格式,例如 `2026-06-03T12:00:00` |
| `merchant_category` | String | 否 | |
| `destination_account_id` | String | 否 | |
| `location` | String | 否 | |
### 测试 Payloads
所有 payload 均使用基于文件的方式,以兼容 PowerShell。
#### 无警报 — 正常交易
```
'{"account_id":"ACC-001","client_id":"CLIENT-001","amount":500.00,"currency":"ZAR","transaction_type":"DEPOSIT","channel":"ONLINE","timestamp":"2026-06-03T12:00:00","location":"Cape Town"}' | Out-File -FilePath body.json -Encoding utf8NoBOM
curl.exe -X POST "http://localhost:8080/v1/fraud/evaluations" -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d "@body.json"
```
预期:`alert_count: 0`
#### RULE-002 — 资料变更后大额交易
客户 `CLIENT-LARGE` 的资料今天已更新。任何金额 ≥ 50,000 兰特的转账都会触发此规则。
```
'{"account_id":"ACC-001","client_id":"CLIENT-LARGE","amount":75000.00,"currency":"ZAR","transaction_type":"TRANSFER","channel":"ONLINE","destination_account_id":"ACC-TARGET-001","timestamp":"2026-06-03T12:00:00","location":"Johannesburg"}' | Out-File -FilePath body.json -Encoding utf8NoBOM
curl.exe -X POST "http://localhost:8080/v1/fraud/evaluations" -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d "@body.json"
```
预期:`alert_count: 1`,`rule_code: RULE-002`,`severity: CRITICAL`
#### RULE-003 — 可疑消费行为
客户 `CLIENT-SPEND` 的月平均收入为 10,000 兰特。任何金额 > 30,000 兰特都会触发此规则(收入的 300%)。
```
'{"account_id":"ACC-001","client_id":"CLIENT-SPEND","amount":35000.00,"currency":"ZAR","transaction_type":"PAYMENT","channel":"MOBILE","timestamp":"2026-06-03T12:00:00","location":"Durban"}' | Out-File -FilePath body.json -Encoding utf8NoBOM
curl.exe -X POST "http://localhost:8080/v1/fraud/evaluations" -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d "@body.json"
```
预期:`alert_count: 1`,`rule_code: RULE-003`,`severity: MEDIUM`
#### RULE-005 — 整数金额模式
快速连续发送 3 次相同的整数金额交易以触发该模式。
```
'{"account_id":"ACC-001","client_id":"CLIENT-001","amount":5000.00,"currency":"ZAR","transaction_type":"WITHDRAWAL","channel":"ATM","timestamp":"2026-06-03T12:00:00"}' | Out-File -FilePath body.json -Encoding utf8NoBOM
curl.exe -X POST "http://localhost:8080/v1/fraud/evaluations" -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d "@body.json"
curl.exe -X POST "http://localhost:8080/v1/fraud/evaluations" -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d "@body.json"
curl.exe -X POST "http://localhost:8080/v1/fraud/evaluations" -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d "@body.json"
```
预期:第三次请求返回 `alert_count: 1`,`rule_code: RULE-005`,`severity: MEDIUM`
#### RULE-004 — 交易频率激增
在 1 小时内发送 10 笔或以上来自同一客户的交易。
```
'{"account_id":"ACC-001","client_id":"CLIENT-001","amount":100.00,"currency":"ZAR","transaction_type":"PAYMENT","channel":"ONLINE","timestamp":"2026-06-03T12:00:00"}' | Out-File -FilePath body.json -Encoding utf8NoBOM
for ($i = 1; $i -le 11; $i++) {
curl.exe -X POST "http://localhost:8080/v1/fraud/evaluations" -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d "@body.json"
}
```
预期:在第 10 次请求之后,`alert_count: 1`,`rule_code: RULE-004`,`severity: HIGH`
#### 400 Bad Request — 验证失败
```
'{"account_id":"","client_id":"CLIENT-001","amount":-1,"currency":"ZA","transaction_type":"PAYMENT","channel":"ONLINE","timestamp":"2026-06-03T12:00:00"}' | Out-File -FilePath body.json -Encoding utf8NoBOM
curl.exe -X POST "http://localhost:8080/v1/fraud/evaluations" -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d "@body.json"
```
预期:`400 Bad Request`,附带字段验证错误。
#### 401 Unauthorized — 缺少 token
```
'{"account_id":"ACC-001","client_id":"CLIENT-001","amount":500.00,"currency":"ZAR","transaction_type":"DEPOSIT","channel":"ONLINE","timestamp":"2026-06-03T12:00:00"}' | Out-File -FilePath body.json -Encoding utf8NoBOM
curl.exe -X POST "http://localhost:8080/v1/fraud/evaluations" -H "Content-Type: application/json" -d "@body.json"
```
预期:`401 Unauthorized`
**错误响应参考:**
| 状态 | 原因 |
|---|---|
| `400 Bad Request` | 验证失败 — 字段错误列在 `message` 中 |
| `401 Unauthorized` | JWT 缺失或无效 |
| `403 Forbidden` | JWT 有效但缺少 `fraud:evaluate` scope |
| `500 Internal Server Error` | 规则评估失败 |
### 健康检查(无需认证)
```
GET /api/v1/health
```
### Swagger UI(无需认证)
```
GET /swagger-ui/index.html
```
## 配置
所有配置均位于 `src/main/resources/application.yml` 中。敏感值通过环境变量进行外部化。
| 环境变量 | 默认值(本地) | 描述 |
|---|---|---|
| `APP_ENCRYPTION_KEY` | `test-aes-256-key-32byteslong!!!!!` | 用于 PII 字段加密的 32 字节 AES 密钥。**在生产环境中必须被覆盖。** |
| `JWT_ISSUER_URI` | `http://localhost:9090/realms/fraud-detection-engine` | 用于 JWT 签名验证的 JWKS 端点 |
| `JWT_AUDIENCE` | `fraud-detection-engine` | 传入 JWT 中预期的 `aud` claim |
| `H2_CONSOLE_ENABLED` | `false` | 在 `/h2-console` 启用 H2 Web 控制台(仅限本地开发) |
| `POSTGRES_PASSWORD` | `frauddetectionengine_local` | PostgreSQL 密码(仅限 Docker) |
| `KEYCLOAK_ADMIN_PASSWORD` | `admin` | Keycloak 管理控制台密码(仅限 Docker) |
### 切换欺诈规则
每条规则都可以独立禁用而无需重新部署:
```
app:
fraud-rules:
rule001-enabled: true # Structuring Over Time
rule002-enabled: true # Large Transaction After Profile Change
rule003-enabled: true # Suspicious Spend Behaviour
rule004-enabled: false # Velocity Spike — disabled
rule005-enabled: true # Round Amount Pattern
rule006-enabled: true # High-Risk Rapid Movement
```
### ClientProfile 外部服务
```
app:
http-configs:
rest:
client-profile-service:
url: ${CLIENT_PROFILE_SERVICE_URL:http://localhost:9091}
connect-timeout: PT2S
read-timeout: PT3S
```
## 安全性
### 身份验证
除 `/actuator/health`、`/actuator/info`、`/swagger-ui/**` 和 `/v3/api-docs/**` 之外的所有端点都需要有效的 JWT。
JWT 必须满足:
- 由配置的签发者签名(`JWT_ISSUER_URI`)
- 包含 `fraud-detection-engine` audience claim
- 对于 `POST /v1/fraud/evaluations`,包含 `fraud:evaluate` scope
通过 Docker 运行时,Keycloak 将在 `http://localhost:9090` 自动启动。名为 `fraud-detection-engine` 的 realm 将在启动时从 `keycloak/` 目录导入。
### PII 字段加密
`ClientProfileEntity.fullName` 和 `ClientProfileEntity.idNumber` 使用 **AES-256-GCM** 进行静态加密:
- 使用 `SecureRandom` 为每次加密调用生成随机的 12 字节 IV
- IV 作为前缀添加到密文中,以 Base64 格式存储
- GCM 身份验证标签(128 位)可防止密文被篡改
- 密钥来源于 `APP_ENCRYPTION_KEY` 环境变量
### 密钥
没有任何密钥被提交到版本控制中。有关所需变量,请参见 `.env.example`。
## 数据库
### Schema
由 Flyway 管理。迁移将在应用程序启动时自动运行。
| 迁移 | 描述 |
|---|---|
| `V1` | `transaction` 表 — 存储所有已评估的交易 |
| `V2` | `client_profile` 表 + `client_profile_ppi_change` — PII 加密的客户数据 |
| `V3` | `fraud_alert` 表 — 包含规则元数据的持久化警报 |
| `V4` | 种子数据 — 用于本地开发的测试客户资料 |
### 本地 H2 控制台
当 `H2_CONSOLE_ENABLED=true` 时:
```
URL: http://localhost:8080/h2-console
JDBC URL: jdbc:h2:mem:frauddetectionengine
Username: sa
Password: (leave blank)
```
### 测试客户资料(由 V4 植入)
| `client_id` | 设置 | 触发条件 |
|---|---|---|
| `CLIENT-LARGE` | 资料已更新,收入 80,000 兰特 | RULE-002,金额 ≥ 50,000 兰特 |
| `CLIENT-SPEND` | 月平均收入 10,000 兰特 | RULE-003,金额 > 30,000 兰特 |
| `CLIENT-RAPID` | 无特殊资料 | 用于 RULE-006 快速转移测试 |
## 可观测性
### 指标
在 `GET /actuator/prometheus` 暴露,供 Prometheus 抓取。
| 指标 | 类型 | 标签 | 描述 |
|---|---|---|---|
| `fraud.alerts.generated` | Counter | `ruleCode` | 每持久化一个警报即递增 |
| `fraud.evaluation.duration` | Timer | `clientId` | 完整评估循环持续时间 |
| `http.server.requests` | Timer | `uri`, `status` | 标准 Spring HTTP 指标 |
### 链路追踪
通过 Micrometer + OpenTelemetry 实现 W3C `traceparent` 传播。通过以下方式配置 OTLP 端点:
```
management:
otlp:
tracing:
endpoint: ${OTLP_ENDPOINT:http://localhost:4318/v1/traces}
```
### 健康
```
GET /actuator/health → {"status": "UP"}
GET /actuator/info → build info
GET /actuator/metrics → available metric names
```
## 测试
```
# 运行所有单元测试(72 个测试)
mvn test
# 运行集成测试(需要 app context + H2)
mvn verify
```
### 测试覆盖率
| 层 | 测试类 | 测试数 |
|---|---|---|
| 加密 | `AesGcmFieldEncryptionServiceUnitTest` | 4 |
| 字段转换器 | `EncryptedStringConverterUnitTest` | 4 |
| 实体 | `TransactionEntityUnitTest`, `ClientProfileEntityUnitTest`, `FraudAlertEntityUnitTest` | 8 |
| 持久层 | `FraudPersistenceServiceUnitTest` | 6 |
| 规则上下文 | `RuleContextUnitTest`, `RuleContextFactoryUnitTest` | 5 |
| 欺诈规则 | `StructuringRule`, `ProfileChangeRule`, `SuspiciousSpendRule`, `VelocityRule`, `RoundAmountRule`, `RapidMovementRule` | 31 |
| 引擎 | `FraudEvaluationServiceImplUnitTest` | 5 |
| 控制器 | `FraudControllerUnitTest` | 1 |
| 异常处理器 | `GlobalExceptionHandlerUnitTest` | 4 |
| 适配器 | `ClientProfileAdapterImplUnitTest` | 2 |
| 缓存 | `CacheConfigUnitTest` | 2 |
| 集成 | `FraudPersistenceIntegrationTest` | 3 |
标签:API集成, CISA项目, Flyway, H2数据库, JS文件枚举, JWT认证, MapStruct, Micrometer, OAuth2, PostgreSQL, REST API, Spring Boot, Web安全, 云计算, 反欺诈, 可观测性, 域名枚举, 实时交易监控, 欺诈检测引擎, 测试用例, 生产就绪, 自定义请求头, 蓝队分析, 规则引擎, 金融交易, 风控系统