2025-11-21 04:32:08 +08:00
|
|
|
|
---
|
|
|
|
|
|
alwaysApply: true
|
|
|
|
|
|
path: backend/**
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
# 后端开发规范
|
|
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
## 核心原则
|
|
|
|
|
|
- **禁止**在代码中添加 TODO/FIXME/XXX 注释
|
|
|
|
|
|
- **禁止**返回 mock 数据或硬编码的假数据
|
|
|
|
|
|
- **禁止**在 API 调用失败时返回默认值作为 fallback
|
|
|
|
|
|
- 所有功能必须完整实现,确保代码可以正常运行
|
2025-11-21 04:32:08 +08:00
|
|
|
|
|
|
|
|
|
|
## 项目范围
|
2026-01-09 08:35:05 +08:00
|
|
|
|
- **平台**: 仅支持 Polymarket
|
2025-11-21 04:32:08 +08:00
|
|
|
|
- **包名**: `com.wrbug.polymarketbot`
|
|
|
|
|
|
|
|
|
|
|
|
## 实体类规范
|
|
|
|
|
|
```kotlin
|
|
|
|
|
|
@Entity
|
|
|
|
|
|
@Table(name = "example_table")
|
|
|
|
|
|
data class ExampleEntity(
|
|
|
|
|
|
@Id
|
|
|
|
|
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
2026-01-09 08:35:05 +08:00
|
|
|
|
val id: Long? = null, // ✅ 正确:使用可空类型
|
|
|
|
|
|
|
|
|
|
|
|
@Column(name = "created_at", nullable = false)
|
|
|
|
|
|
val createdAt: Long = System.currentTimeMillis(), // ✅ 使用 Long 时间戳
|
|
|
|
|
|
|
|
|
|
|
|
@Column(name = "amount", nullable = false, precision = 20, scale = 8)
|
|
|
|
|
|
val amount: BigDecimal = BigDecimal.ZERO // ✅ 使用 BigDecimal
|
2025-11-21 04:32:08 +08:00
|
|
|
|
)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
**规则**:
|
|
|
|
|
|
- ID 字段必须使用 `Long? = null`
|
|
|
|
|
|
- 时间字段使用 `Long` 时间戳(毫秒)
|
|
|
|
|
|
- 数值字段使用 `BigDecimal`
|
|
|
|
|
|
- **禁止**使用 `LocalDateTime` 或 `Double`
|
2025-11-21 04:32:08 +08:00
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
## 配置文件规范
|
|
|
|
|
|
- **必须**使用 `application.properties` 格式(禁止 `application.yml`)
|
|
|
|
|
|
- 使用 `${ENV_VAR:default}` 引用环境变量
|
2025-11-21 04:32:08 +08:00
|
|
|
|
|
|
|
|
|
|
## 代码规范
|
|
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
### Controller
|
|
|
|
|
|
- **禁止**使用 `suspend`
|
|
|
|
|
|
- 使用 `runBlocking` 调用 suspend 方法
|
|
|
|
|
|
- 统一使用 `@PostMapping`
|
2025-11-21 04:32:08 +08:00
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
### Service
|
|
|
|
|
|
- 可以使用 `suspend` 方法
|
2025-11-21 04:32:08 +08:00
|
|
|
|
- 使用 `@Transactional` 管理事务
|
|
|
|
|
|
- 使用构造函数注入依赖
|
|
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
### Repository
|
|
|
|
|
|
- 继承 `JpaRepository<Entity, Long>`
|
2025-11-21 04:32:08 +08:00
|
|
|
|
- 使用 Spring Data JPA 方法命名规范
|
|
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
## API 接口规范
|
2025-11-21 04:32:08 +08:00
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
### 统一响应格式
|
2025-11-21 04:32:08 +08:00
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": 0,
|
|
|
|
|
|
"data": {},
|
|
|
|
|
|
"msg": ""
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
### 错误码范围
|
|
|
|
|
|
- `0`: 成功
|
|
|
|
|
|
- `1001-1999`: 参数错误
|
|
|
|
|
|
- `2001-2999`: 认证/权限错误
|
|
|
|
|
|
- `3001-3999`: 资源不存在
|
|
|
|
|
|
- `4001-4999`: 业务逻辑错误
|
|
|
|
|
|
- `5001-5999`: 服务器内部错误
|
2025-11-21 04:32:08 +08:00
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
### Controller 示例
|
2025-11-21 04:32:08 +08:00
|
|
|
|
```kotlin
|
|
|
|
|
|
@RestController
|
|
|
|
|
|
@RequestMapping("/api/example")
|
|
|
|
|
|
class ExampleController(
|
2026-01-09 08:35:05 +08:00
|
|
|
|
private val exampleService: ExampleService,
|
|
|
|
|
|
private val messageSource: MessageSource
|
2025-11-21 04:32:08 +08:00
|
|
|
|
) {
|
|
|
|
|
|
@PostMapping("/list")
|
|
|
|
|
|
fun getList(@RequestBody request: ExampleListRequest): ResponseEntity<ApiResponse<ExampleListResponse>> {
|
|
|
|
|
|
return try {
|
|
|
|
|
|
val data = runBlocking { exampleService.getList(request) }
|
2026-01-09 08:35:05 +08:00
|
|
|
|
ResponseEntity.ok(ApiResponse.success(data))
|
2025-11-21 04:32:08 +08:00
|
|
|
|
} catch (e: Exception) {
|
|
|
|
|
|
logger.error("Failed to get list", e)
|
2026-01-09 08:35:05 +08:00
|
|
|
|
ResponseEntity.ok(ApiResponse.error(ErrorCode.SERVER_ERROR, messageSource))
|
2025-11-21 04:32:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
## 数值计算
|
|
|
|
|
|
```kotlin
|
|
|
|
|
|
val amount = "0.5".toSafeBigDecimal()
|
|
|
|
|
|
val total = amount.add("0.4".toSafeBigDecimal())
|
2025-11-21 04:32:08 +08:00
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
if (total.lt(BigDecimal.ONE)) {
|
|
|
|
|
|
// 业务逻辑
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
2025-11-21 04:32:08 +08:00
|
|
|
|
|
2026-01-09 10:41:15 +08:00
|
|
|
|
## JSON 解析规范
|
|
|
|
|
|
|
|
|
|
|
|
### 使用扩展函数(推荐)
|
|
|
|
|
|
- **优先**使用扩展函数 `fromJson<T>()` 和 `toJson()`,禁止直接使用 `Gson`
|
|
|
|
|
|
- 扩展函数提供了类型安全的解析,使用方便
|
|
|
|
|
|
|
|
|
|
|
|
```kotlin
|
|
|
|
|
|
// ✅ 正确:使用扩展函数(推荐)
|
|
|
|
|
|
val json = "{\"name\":\"test\"}"
|
|
|
|
|
|
val obj = json.fromJson<MyDataClass>()
|
|
|
|
|
|
val jsonStr = obj.toJson()
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ 也可以:使用 JsonUtils 类(兼容旧代码)
|
|
|
|
|
|
val obj = jsonUtils.fromJson<MyDataClass>(json)
|
|
|
|
|
|
|
|
|
|
|
|
// ❌ 错误:直接使用 Gson
|
|
|
|
|
|
val obj = gson.fromJson(json, MyDataClass::class.java)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 扩展函数列表
|
|
|
|
|
|
- `String?.fromJson<T>()` - 解析 JSON 字符串为对象(支持泛型)
|
|
|
|
|
|
- `JsonElement?.fromJson<T>()` - 解析 JsonElement 为对象(支持泛型)
|
|
|
|
|
|
- `Any?.toJson()` - 将对象转换为 JSON 字符串
|
|
|
|
|
|
- `String?.parseStringArray()` - 解析 JSON 字符串数组
|
|
|
|
|
|
|
|
|
|
|
|
### JsonUtils 类
|
|
|
|
|
|
- `JsonUtils` 主要用于初始化全局 Gson 实例,供扩展函数使用
|
|
|
|
|
|
- 保留 `parseStringArray()` 方法用于兼容旧代码
|
|
|
|
|
|
- 不推荐直接使用 `JsonUtils` 的方法,优先使用扩展函数
|
|
|
|
|
|
|
|
|
|
|
|
### Data Class 规范
|
|
|
|
|
|
- **所有 data class 字段必须提供默认值**
|
|
|
|
|
|
- 可空字段使用 `? = null`
|
|
|
|
|
|
- 非空字段提供合适的默认值(空字符串、空集合、默认数值等)
|
|
|
|
|
|
|
|
|
|
|
|
```kotlin
|
|
|
|
|
|
// ✅ 正确:所有字段都有默认值
|
|
|
|
|
|
data class MyDto(
|
|
|
|
|
|
val name: String = "",
|
|
|
|
|
|
val age: Int = 0,
|
|
|
|
|
|
val tags: List<String> = emptyList(),
|
|
|
|
|
|
val optional: String? = null
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// ❌ 错误:缺少默认值
|
|
|
|
|
|
data class MyDto(
|
|
|
|
|
|
val name: String, // 缺少默认值
|
|
|
|
|
|
val age: Int = 0
|
|
|
|
|
|
)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
## Side 判断规范
|
|
|
|
|
|
**禁止**使用 "YES"/"NO" 字符串判断 side
|
2025-11-21 04:32:08 +08:00
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
```kotlin
|
|
|
|
|
|
// ❌ 错误
|
|
|
|
|
|
if (side != null && side.uppercase() == "NO") { }
|
2025-11-21 04:32:08 +08:00
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
// ✅ 正确
|
|
|
|
|
|
if (outcomeIndex != null && outcomeIndex == 1) { }
|
|
|
|
|
|
```
|
2025-12-05 00:23:44 +08:00
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
## 多语言规范
|
|
|
|
|
|
- **禁止**硬编码中文或英文错误消息
|
|
|
|
|
|
- **必须**使用 `ErrorCode` 枚举和 `MessageSource`
|
2025-12-05 00:23:44 +08:00
|
|
|
|
|
|
|
|
|
|
```kotlin
|
2026-01-09 08:35:05 +08:00
|
|
|
|
// ❌ 错误
|
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.paramError("配置ID不能为空"))
|
2025-12-05 00:23:44 +08:00
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
// ✅ 正确
|
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.error(ErrorCode.PARAM_EMPTY, messageSource))
|
|
|
|
|
|
```
|
2025-12-05 00:23:44 +08:00
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
### 多语言支持范围
|
|
|
|
|
|
1. **API 响应消息**: 使用 `ErrorCode` + `MessageSource`
|
|
|
|
|
|
2. **日志消息**: 可使用中文或英文
|
|
|
|
|
|
3. **代码注释**: 建议使用中文
|
|
|
|
|
|
4. **数据库字段**: 使用英文(snake_case)
|
2025-12-05 00:23:44 +08:00
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
## HTTP 客户端
|
|
|
|
|
|
- 使用 Retrofit + OkHttp
|
|
|
|
|
|
- 使用拦截器处理认证
|
2025-12-05 02:20:46 +08:00
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
## 跟单系统需求
|
|
|
|
|
|
参考文档: `docs/copy-trading-requirements.md`
|
2025-12-05 02:20:46 +08:00
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
核心功能:
|
|
|
|
|
|
- 账户管理(私钥导入,多账户)
|
|
|
|
|
|
- Leader 管理
|
|
|
|
|
|
- 订单同步与执行
|
|
|
|
|
|
- 跟单配置管理
|
|
|
|
|
|
- 风险控制
|
|
|
|
|
|
- 跟单记录与统计
|
2025-12-05 02:20:46 +08:00
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
## 禁止事项
|
2025-12-05 02:20:46 +08:00
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
### 代码质量
|
|
|
|
|
|
- ❌ 禁止使用 `!!` 除非有明确原因
|
|
|
|
|
|
- ❌ 禁止忽略异常
|
|
|
|
|
|
- ❌ 禁止硬编码配置值
|
|
|
|
|
|
- ❌ 禁止提交敏感信息到 Git
|
2025-12-05 02:20:46 +08:00
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
### 类型
|
|
|
|
|
|
- ❌ 禁止使用 `Double` 进行数值计算
|
|
|
|
|
|
- ❌ 禁止使用 `LocalDateTime` 存储时间
|
|
|
|
|
|
- ❌ 禁止实体类 ID 使用非空默认值
|
2026-01-09 10:41:15 +08:00
|
|
|
|
- ❌ 禁止直接使用 `Gson`(必须使用 `JsonUtils`)
|
|
|
|
|
|
- ❌ 禁止 data class 字段缺少默认值
|
2025-12-05 02:20:46 +08:00
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
### API 接口
|
|
|
|
|
|
- ❌ 禁止使用 GET/PUT/DELETE(统一使用 POST)
|
|
|
|
|
|
- ❌ 禁止返回不符合统一格式的响应
|
|
|
|
|
|
- ❌ 禁止在响应中直接返回 Map 类型
|
2025-12-05 02:20:46 +08:00
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
### Side 判断
|
|
|
|
|
|
- ❌ 禁止使用 "YES"/"NO" 字符串判断 side
|
|
|
|
|
|
- ✅ 必须使用 `outcomeIndex` 判断
|
2025-12-05 02:20:46 +08:00
|
|
|
|
|
2026-01-09 08:35:05 +08:00
|
|
|
|
### 数据源
|
|
|
|
|
|
- ❌ 禁止直接返回 mock 数据
|
|
|
|
|
|
- ❌ 禁止在 API 调用失败时返回 mock 数据
|
|
|
|
|
|
- ✅ 所有数据必须来自真实 API 或数据库查询
|