mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-30 22:26:38 +08:00
质量优化
This commit is contained in:
+1
-1
@@ -306,7 +306,7 @@ s3:
|
||||
- **Scheduler 独占**:**【注意】** 为避免重复触发定时 Cron 任务,`wavelet scheduler` 定时调度器进程**同一时间应仅运行单个活跃实例**(主备高可用可以通过容器平台的单实例保障或 K8s Job 机制来限制实例数为 1)。
|
||||
|
||||
#### 5. ClickHouse 高并发同步
|
||||
在大数据量、高频支付结算场景下,开启 ClickHouse 以接收系统的历史数据同步(如订单流水和任务大宽表),通过定时器把 PostgreSQL 的压力转移到 ClickHouse 列式存储中。
|
||||
在大数据量、高频支付结算场景下,开启 ClickHouse 以接收系统的历史数据同步,通过定时器把 PostgreSQL 的压力转移到 ClickHouse 列式存储中。
|
||||
```yaml
|
||||
clickhouse:
|
||||
enabled: true
|
||||
|
||||
@@ -1,524 +0,0 @@
|
||||
# 服务层架构文档
|
||||
|
||||
> **前端服务层** - 统一的 API 交互层,基于 TypeScript 和面向对象设计
|
||||
|
||||
## 架构设计
|
||||
|
||||
### 设计理念
|
||||
|
||||
服务层采用**分层架构**和**继承模式**,遵循以下核心原则:
|
||||
|
||||
1. **单一职责** - 每个服务模块负责一个业务领域
|
||||
2. **类型安全** - 全量 TypeScript 类型定义,杜绝 `any`
|
||||
3. **统一规范** - 所有服务遵循相同的设计模式
|
||||
4. **错误分类** - 细粒度错误类型,便于精确处理
|
||||
5. **可扩展性** - 基于继承的设计,易于添加新服务
|
||||
|
||||
---
|
||||
|
||||
## 架构分层
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph UI["业务组件层"]
|
||||
Components["React Components<br/>Hooks & Contexts"]
|
||||
end
|
||||
|
||||
subgraph Services["服务层 (Services Layer)"]
|
||||
direction LR
|
||||
Auth["AuthService<br/><small>认证服务</small>"]
|
||||
Admin["AdminService<br/><small>管理员服务</small>"]
|
||||
Merchant["MerchantService<br/><small>商户服务</small>"]
|
||||
Transaction["TransactionService<br/><small>交易服务</small>"]
|
||||
Dispute["DisputeService<br/><small>争议服务</small>"]
|
||||
User["UserService<br/><small>用户服务</small>"]
|
||||
Config["ConfigService<br/><small>配置服务</small>"]
|
||||
end
|
||||
|
||||
subgraph BaseLayer["服务基类"]
|
||||
BaseService["BaseService<br/><small>• get() post() put() delete()<br/>• rawGet() rawPost()</small>"]
|
||||
end
|
||||
|
||||
subgraph Core["核心层 (Core Layer)"]
|
||||
direction LR
|
||||
ApiClient["api-client.ts<br/><small>• 请求/响应拦截<br/>• 错误处理<br/>• 请求去重<br/>• 401重定向</small>"]
|
||||
Errors["errors.ts<br/><small>• ApiErrorBase<br/>• NetworkError<br/>• ValidationError<br/>• 等7种错误</small>"]
|
||||
Types["types.ts<br/><small>• ApiResponse<br/>• PaginationParams<br/>• RequestConfig</small>"]
|
||||
CoreConfig["config.ts<br/><small>• baseURL<br/>• timeout<br/>• credentials</small>"]
|
||||
end
|
||||
|
||||
Backend["Backend API<br/><small>(Go + Gin)</small>"]
|
||||
|
||||
Components -->|调用| Auth
|
||||
Components -->|调用| Admin
|
||||
Components -->|调用| Merchant
|
||||
Components -->|调用| Transaction
|
||||
Components -->|调用| Dispute
|
||||
Components -->|调用| User
|
||||
Components -->|调用| Config
|
||||
|
||||
Auth -.->|继承| BaseService
|
||||
Admin -.->|继承| BaseService
|
||||
Merchant -.->|继承| BaseService
|
||||
Transaction -.->|继承| BaseService
|
||||
Dispute -.->|继承| BaseService
|
||||
User -.->|继承| BaseService
|
||||
Config -.->|继承| BaseService
|
||||
|
||||
BaseService -->|使用| ApiClient
|
||||
BaseService -->|使用| Errors
|
||||
BaseService -->|使用| Types
|
||||
ApiClient -->|读取| CoreConfig
|
||||
|
||||
ApiClient ==>|HTTP 请求| Backend
|
||||
|
||||
style UI fill:#e1f5ff
|
||||
style Services fill:#fff9e6
|
||||
style BaseLayer fill:#f0f0f0
|
||||
style Core fill:#e8f5e9
|
||||
style Backend fill:#ffebee
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
lib/services/
|
||||
├── core/ # 核心基础设施层
|
||||
│ ├── api-client.ts # HTTP 客户端(请求拦截、错误处理)
|
||||
│ ├── base.service.ts # 服务基类(封装 CRUD 方法)
|
||||
│ ├── config.ts # API 配置(环境变量、超时等)
|
||||
│ ├── errors.ts # 错误类型定义(7种错误类型)
|
||||
│ ├── types.ts # 核心类型(ApiResponse、分页等)
|
||||
│ └── index.ts # 核心模块导出
|
||||
│
|
||||
├── auth/ # 认证服务模块
|
||||
│ ├── auth.service.ts # OAuth 认证、登录、登出
|
||||
│ ├── types.ts # User、OAuthLoginUrlResponse 等
|
||||
│ └── index.ts # 模块导出 + 文档
|
||||
│
|
||||
├── admin/ # 管理员服务模块
|
||||
│ ├── admin.service.ts # 系统配置、用户积分配置管理
|
||||
│ ├── types.ts # SystemConfig、UserPayConfig 等
|
||||
│ └── index.ts # 模块导出 + 文档
|
||||
│
|
||||
├── merchant/ # 商户服务模块
|
||||
│ ├── merchant.service.ts # API Key、支付链接、订单管理
|
||||
│ ├── types.ts # MerchantAPIKey、PaymentLink 等
|
||||
│ └── index.ts # 模块导出 + 文档
|
||||
│
|
||||
├── transaction/ # 交易服务模块
|
||||
│ ├── transaction.service.ts # 交易记录
|
||||
│ ├── types.ts # Order、TransactionQueryParams 等
|
||||
│ └── index.ts # 模块导出 + 文档
|
||||
│
|
||||
├── dispute/ # 争议服务模块
|
||||
│ ├── dispute.service.ts # 创建争议、退款审核
|
||||
│ ├── types.ts # Dispute、DisputeWithOrder 等
|
||||
│ └── index.ts # 模块导出 + 文档
|
||||
│
|
||||
├── user/ # 用户服务模块
|
||||
│ ├── user.service.ts # 用户设置(支付密钥等)
|
||||
│ ├── types.ts # UpdatePayKeyRequest 等
|
||||
│ └── index.ts # 模块导出 + 文档
|
||||
│
|
||||
├── config/ # 配置服务模块
|
||||
│ ├── config.service.ts # 获取公共配置
|
||||
│ ├── types.ts # PublicConfigResponse
|
||||
│ └── index.ts # 模块导出 + 文档
|
||||
│
|
||||
├── index.ts # 统一导出入口
|
||||
└── README.md # 本文档
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 核心模块详解
|
||||
|
||||
### 1. BaseService - 服务基类
|
||||
|
||||
**职责**:为所有业务服务提供统一的 HTTP 方法封装
|
||||
|
||||
**关键设计**:
|
||||
```typescript
|
||||
export class BaseService {
|
||||
protected static readonly basePath: string = '';
|
||||
|
||||
// 标准 RESTful 方法
|
||||
protected static async get<T>(path: string, params?: Record<string, unknown>): Promise<T>
|
||||
protected static async post<T>(path: string, data?: unknown): Promise<T>
|
||||
protected static async put<T>(path: string, data?: unknown): Promise<T>
|
||||
protected static async patch<T>(path: string, data?: unknown): Promise<T>
|
||||
protected static async delete<T>(path: string, params?: Record<string, unknown>): Promise<T>
|
||||
|
||||
// 特殊端点支持(不遵循标准响应格式)
|
||||
protected static async rawGet<T>(url: string, params?: unknown): Promise<T>
|
||||
protected static async rawPost<T>(url: string, data?: unknown): Promise<T>
|
||||
}
|
||||
```
|
||||
|
||||
**设计优势**:
|
||||
- ✅ 子类只需设置 `basePath`,无需重复实现 HTTP 逻辑
|
||||
- ✅ 统一的响应解包(`response.data.data`)
|
||||
- ✅ 类型安全的泛型设计
|
||||
- ✅ 支持特殊端点(如 `/api.php`)
|
||||
|
||||
---
|
||||
|
||||
### 2. API Client - HTTP 客户端
|
||||
|
||||
**职责**:提供全局唯一的 Axios 实例,处理所有 HTTP 请求
|
||||
|
||||
**核心功能**:
|
||||
|
||||
#### 请求拦截器
|
||||
- 自动添加 Cancel Token(支持请求取消)
|
||||
- 请求去重(避免重复请求)
|
||||
|
||||
#### 响应拦截器
|
||||
- **401 自动重定向** - 未授权时自动跳转登录页
|
||||
- **错误分类** - 将 HTTP 状态码映射为具体错误类型
|
||||
- **统一响应格式** - 解析 `ApiResponse<T>` 结构
|
||||
|
||||
#### 错误处理映射
|
||||
| HTTP 状态码 | 错误类型 | 说明 |
|
||||
|------------|---------|------|
|
||||
| 400 | `ValidationError` | 参数验证失败 |
|
||||
| 401 | 自动重定向 | 跳转到登录页 |
|
||||
| 403 | `ForbiddenError` | 权限不足 |
|
||||
| 404 | `NotFoundError` | 资源不存在 |
|
||||
| 5xx | `ServerError` | 服务器错误 |
|
||||
| 超时 | `TimeoutError` | 请求超时 |
|
||||
| 网络 | `NetworkError` | 网络连接失败 |
|
||||
|
||||
---
|
||||
|
||||
### 3. 错误类型层级
|
||||
|
||||
```
|
||||
ApiErrorBase (基类)
|
||||
├── NetworkError (网络连接错误)
|
||||
├── TimeoutError (请求超时)
|
||||
├── UnauthorizedError (401 - 未授权)
|
||||
├── ForbiddenError (403 - 权限不足)
|
||||
├── NotFoundError (404 - 资源不存在)
|
||||
├── ValidationError (400 - 参数验证失败)
|
||||
└── ServerError (5xx - 服务器错误)
|
||||
```
|
||||
|
||||
**设计优势**:
|
||||
- 支持 `instanceof` 类型判断
|
||||
- 携带详细错误信息(`error_code`、`details`)
|
||||
- 便于前端精确处理不同错误场景
|
||||
|
||||
---
|
||||
|
||||
## 开发规范
|
||||
|
||||
### 1. 服务类规范
|
||||
|
||||
#### 必须遵循
|
||||
```typescript
|
||||
export class SomeService extends BaseService {
|
||||
// 1. 必须继承 BaseService
|
||||
// 2. basePath 必须是 protected static readonly
|
||||
protected static readonly basePath = '/api/v1/resource';
|
||||
|
||||
// 3. 方法必须是 static async
|
||||
// 4. 返回类型必须明确(禁止 any)
|
||||
static async getAll(): Promise<Resource[]> {
|
||||
return this.get<Resource[]>('/');
|
||||
}
|
||||
|
||||
// 5. 参数类型必须明确定义在 types.ts
|
||||
static async create(request: CreateResourceRequest): Promise<Resource> {
|
||||
return this.post<Resource>('/', request);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 方法命名规范
|
||||
| 操作 | 命名 | 示例 |
|
||||
|------|------|------|
|
||||
| 获取列表 | `list*` 或 `getAll` | `listAPIKeys()` |
|
||||
| 获取单个 | `get*` 或 `getById` | `getAPIKey(id)` |
|
||||
| 创建 | `create*` | `createAPIKey(request)` |
|
||||
| 更新 | `update*` | `updateAPIKey(id, request)` |
|
||||
| 删除 | `delete*` | `deleteAPIKey(id)` |
|
||||
| 特殊操作 | 动词开头 | `payMerchantOrder()` |
|
||||
|
||||
#### 方法排序规范
|
||||
```typescript
|
||||
export class SomeService extends BaseService {
|
||||
protected static readonly basePath = '/api/v1/resource';
|
||||
|
||||
// 1. CRUD 操作(按 Create → Read → Update → Delete)
|
||||
static async create() { }
|
||||
static async list() { }
|
||||
static async get() { }
|
||||
static async update() { }
|
||||
static async delete() { }
|
||||
|
||||
// 2. 其他业务方法(按业务逻辑分组)
|
||||
static async someAction() { }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 类型定义规范
|
||||
|
||||
#### types.ts 文件结构
|
||||
```typescript
|
||||
// 1. 类型别名(Type Aliases)
|
||||
export type ResourceStatus = 'active' | 'inactive' | 'pending';
|
||||
|
||||
// 2. 枚举(Enums) - 使用 const enum 提升性能
|
||||
export const enum ResourceLevel {
|
||||
Basic = 1,
|
||||
Premium = 2,
|
||||
Enterprise = 3,
|
||||
}
|
||||
|
||||
// 3. 接口(Interfaces) - 按业务逻辑分组
|
||||
/**
|
||||
* 资源信息
|
||||
*/
|
||||
export interface Resource {
|
||||
/** 资源 ID */
|
||||
id: number;
|
||||
/** 资源名称 */
|
||||
name: string;
|
||||
/** 状态 */
|
||||
status: ResourceStatus;
|
||||
/** 创建时间 */
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建资源请求
|
||||
*/
|
||||
export interface CreateResourceRequest {
|
||||
/** 资源名称(最大 50 字符) */
|
||||
name: string;
|
||||
/** 描述(可选,最大 200 字符) */
|
||||
description?: string;
|
||||
}
|
||||
```
|
||||
|
||||
#### 注释规范
|
||||
- ✅ 所有 interface 必须有 JSDoc 描述
|
||||
- ✅ 所有字段必须有行内注释
|
||||
- ✅ 包含约束信息(长度、范围、格式等)
|
||||
- ✅ 可选字段使用 `?` 标记
|
||||
|
||||
---
|
||||
|
||||
### 3. JSDoc 注释规范
|
||||
|
||||
**标准模板**:
|
||||
```typescript
|
||||
/**
|
||||
* [一句话描述方法功能]
|
||||
*
|
||||
* @param paramName - 参数说明
|
||||
* @returns 返回值说明
|
||||
* @throws {ErrorType} 错误条件说明
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 使用示例
|
||||
* const result = await Service.method({ param: 'value' });
|
||||
* ```
|
||||
*
|
||||
* @remarks [可选]
|
||||
* - 业务规则或注意事项
|
||||
*/
|
||||
```
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
/**
|
||||
* 创建商户 API Key
|
||||
*
|
||||
* @param request - API Key 配置
|
||||
* @returns 创建的 API Key 信息
|
||||
* @throws {UnauthorizedError} 当未登录时
|
||||
* @throws {ValidationError} 当参数验证失败时
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const apiKey = await MerchantService.createAPIKey({
|
||||
* app_name: '我的应用',
|
||||
* app_homepage_url: 'https://example.com'
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @remarks
|
||||
* - app_name 最大 20 字符
|
||||
* - 需要登录权限
|
||||
*/
|
||||
static async createAPIKey(request: CreateAPIKeyRequest): Promise<MerchantAPIKey>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 模块 index.ts 规范
|
||||
|
||||
**标准格式**:
|
||||
```typescript
|
||||
/**
|
||||
* [模块名] 服务模块
|
||||
*
|
||||
* @description
|
||||
* 提供 [业务领域] 相关的功能,包括:
|
||||
* - 功能点1
|
||||
* - 功能点2
|
||||
* - 功能点3
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { ServiceName } from '@/lib/services';
|
||||
*
|
||||
* // 使用示例(展示最常用的1-2个方法)
|
||||
* const result = await ServiceName.commonMethod();
|
||||
* ```
|
||||
*
|
||||
* @remarks [可选]
|
||||
* - 特殊说明(如权限要求等)
|
||||
*/
|
||||
|
||||
export { ServiceName } from './service-name.service';
|
||||
export type * from './types';
|
||||
// 或明确导出
|
||||
export type {
|
||||
Type1,
|
||||
Type2,
|
||||
} from './types';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 创建新服务指南
|
||||
|
||||
### Step 1: 创建目录和文件
|
||||
```bash
|
||||
mkdir lib/services/resource
|
||||
touch lib/services/resource/types.ts
|
||||
touch lib/services/resource/resource.service.ts
|
||||
touch lib/services/resource/index.ts
|
||||
```
|
||||
|
||||
### Step 2: 定义类型(types.ts)
|
||||
```typescript
|
||||
export interface Resource {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface CreateResourceRequest {
|
||||
name: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: 实现服务(resource.service.ts)
|
||||
```typescript
|
||||
import { BaseService } from '../core/base.service';
|
||||
import type { Resource, CreateResourceRequest } from './types';
|
||||
|
||||
export class ResourceService extends BaseService {
|
||||
protected static readonly basePath = '/api/v1/resources';
|
||||
|
||||
static async list(): Promise<Resource[]> {
|
||||
return this.get<Resource[]>('/');
|
||||
}
|
||||
|
||||
static async create(request: CreateResourceRequest): Promise<Resource> {
|
||||
return this.post<Resource>('/', request);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: 导出模块(index.ts)
|
||||
```typescript
|
||||
/**
|
||||
* 资源服务模块
|
||||
*
|
||||
* @description
|
||||
* 提供资源管理相关的功能,包括:
|
||||
* - 资源列表查询
|
||||
* - 资源创建
|
||||
*/
|
||||
|
||||
export { ResourceService } from './resource.service';
|
||||
export type * from './types';
|
||||
```
|
||||
|
||||
### Step 5: 注册到统一入口(services/index.ts)
|
||||
```typescript
|
||||
import { ResourceService } from './resource';
|
||||
|
||||
const services = {
|
||||
// ... existing services
|
||||
resource: ResourceService, // 新增
|
||||
};
|
||||
|
||||
export default services;
|
||||
export { ResourceService } from './resource';
|
||||
export type * from './resource';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
```typescript
|
||||
import services from '@/lib/services';
|
||||
|
||||
// 调用服务
|
||||
const user = await services.auth.getUserInfo();
|
||||
const transactions = await services.transaction.getTransactions({
|
||||
page: 1,
|
||||
page_size: 20
|
||||
});
|
||||
|
||||
// 错误处理
|
||||
import { UnauthorizedError, ValidationError } from '@/lib/services';
|
||||
|
||||
try {
|
||||
await services.merchant.createAPIKey(request);
|
||||
} catch (error) {
|
||||
if (error instanceof UnauthorizedError) {
|
||||
router.push('/login');
|
||||
} else if (error instanceof ValidationError) {
|
||||
toast.error(error.message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
### 禁止事项
|
||||
- ❌ 使用 `any` 类型
|
||||
- ❌ 直接使用 `apiClient`(除非在 `BaseService` 内部)
|
||||
- ❌ 绕过 BaseService 实现 HTTP 请求
|
||||
- ❌ 在业务组件中直接导入 axios
|
||||
|
||||
### 必须遵循
|
||||
- ✅ 所有服务继承 `BaseService`
|
||||
- ✅ 使用 `protected static readonly basePath`
|
||||
- ✅ 方法必须有完整的 JSDoc 注释
|
||||
- ✅ 类型定义必须在 `types.ts` 中
|
||||
- ✅ 通过 `services` 对象调用服务
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [TypeScript 官方文档](https://www.typescriptlang.org/)
|
||||
- [Axios 文档](https://axios-http.com/)
|
||||
- [JSDoc 规范](https://jsdoc.app/)
|
||||
@@ -27,7 +27,6 @@ export type { AdminUser } from './types';
|
||||
|
||||
/**
|
||||
* 管理员服务
|
||||
* 处理系统配置和用户积分配置管理相关的 API 请求
|
||||
*
|
||||
* @remarks
|
||||
* 所有接口都需要管理员权限
|
||||
@@ -199,28 +198,6 @@ export class AdminService extends BaseService {
|
||||
* @throws {UnauthorizedError} 当未登录时
|
||||
* @throws {ForbiddenError} 当无管理员权限时
|
||||
* @throws {ValidationError} 当参数验证失败时
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 下发订单同步任务(带时间范围)
|
||||
* await AdminService.dispatchTask({
|
||||
* task_type: 'order_sync',
|
||||
* start_time: '2025-12-01T00:00:00Z',
|
||||
* end_time: '2025-12-27T23:59:59Z'
|
||||
* });
|
||||
*
|
||||
* // 下发用户积分更新任务
|
||||
* await AdminService.dispatchTask({
|
||||
* task_type: 'user_gamification',
|
||||
* user_id: 123
|
||||
* });
|
||||
*
|
||||
* // 下发争议自动退款任务
|
||||
* await AdminService.dispatchTask({
|
||||
* task_type: 'dispute_auto_refund'
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @remarks
|
||||
* - 不同任务类型需要不同的参数
|
||||
* - order_sync 支持 start_time 和 end_time 参数
|
||||
|
||||
@@ -44,6 +44,5 @@ const (
|
||||
ErrS3KeyTooLongFormat = "s3 key exceeds maximum length of %d"
|
||||
ErrS3KeyStartsWithSlash = "s3 key must not start with /"
|
||||
ErrS3KeyContainsNullBytes = "s3 key must not contain null bytes"
|
||||
//
|
||||
ErrQueryUnusedUploadsFailed = "查询未使用的上传文件失败: %w"
|
||||
ErrQueryUnusedUploadsFailed = "查询未使用的上传文件失败: %w"
|
||||
)
|
||||
|
||||
+2
-17
@@ -19,21 +19,6 @@ package common
|
||||
|
||||
// 通用业务错误消息常量
|
||||
const (
|
||||
BannedAccount = "账号已被封禁"
|
||||
AmountMustBeGreaterThanZero = "金额必须大于0"
|
||||
AmountDecimalPlacesExceeded = "金额小数位数不能超过2位"
|
||||
RateMustBeBetweenZeroAndOne = "比率必须在 0 到 1 之间"
|
||||
RateDecimalPlacesExceeded = "比率小数位数不能超过2位"
|
||||
InsufficientBalance = "余额不足"
|
||||
DailyLimitExceeded = "已超过每日限额"
|
||||
PayKeyIncorrect = "支付密钥错误"
|
||||
CannotPaySelf = "不能给自己付款"
|
||||
TestModeCannotProcessOrder = "测试模式下无法处理订单"
|
||||
TestModeOrderRemark = "[测试模式] 此订单为测试订单,未实际扣款"
|
||||
UnAuthorized = "未登录"
|
||||
)
|
||||
|
||||
// 保护期相关错误消息
|
||||
const (
|
||||
GetProtectionDaysFailed = "获取新用户保护期配置失败"
|
||||
BannedAccount = "账号已被封禁"
|
||||
UnAuthorized = "未登录"
|
||||
)
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
Copyright 2025 linux.do
|
||||
Modified by Arctel.net, 2026
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/internal/common"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
// ValidateRates 所有 rate 必须在 [0, 1] 范围内,且小数位数不超过2位
|
||||
func ValidateRates(rates ...decimal.Decimal) error {
|
||||
for _, rate := range rates {
|
||||
// 验证范围:必须在 [0, 1] 之间
|
||||
if rate.LessThan(decimal.Zero) || rate.GreaterThan(decimal.NewFromInt(1)) {
|
||||
return errors.New(common.RateMustBeBetweenZeroAndOne)
|
||||
}
|
||||
|
||||
// 验证小数位数:不超过2位
|
||||
if rate.Exponent() < -2 {
|
||||
return errors.New(common.RateDecimalPlacesExceeded)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateAmount 验证金额:必须大于0,且小数位数不超过2位
|
||||
func ValidateAmount(amount decimal.Decimal) error {
|
||||
if amount.LessThanOrEqual(decimal.Zero) {
|
||||
return errors.New(common.AmountMustBeGreaterThanZero)
|
||||
}
|
||||
if amount.Exponent() < -2 {
|
||||
return errors.New(common.AmountDecimalPlacesExceeded)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user