# Web端商户快速提现申请接口文档

## 模块概述

本模块提供商户快速提现功能，与普通提现（需审核）不同，此接口创建的提现记录直接标记为"已通过"状态（paymentStatus=3），无需人工审核，与app端 `applyCashOut` 接口保持一致。

---

## 接口信息

### 快速提现申请（免审核）

#### 接口详情

- **接口名称**: 快速提现申请（免审核）
- **接口路径**: `POST /incomeManage/applyFastCashOut`
- **请求方式**: POST
- **接口描述**: 为当前登录的商户提交快速提现申请，直接创建为"已通过"状态，无需审核流程
- **登录验证**: ✅ 需要（通过 JWT token 自动获取storeId）

---

## 与普通提现的区别

| 项目 | 普通提现 (/cashOut) | 快速提现 (/applyFastCashOut) |
|------|-------------------|----------------------------|
| 提现状态 | paymentStatus = 1（待审核） | paymentStatus = 3（已通过） |
| 审核流程 | 需要人工审核 | 免审核，直接通过 |
| 账户扣款 | 提交时扣款 | 提交时不扣款 |
| 适用场景 | Web端常规提现 | 与app端保持一致的快速提现 |
| 后续流程 | 需管理员审核后打款 | 可直接进入打款流程 |

---

## 请求参数

### 请求体（Request Body）

使用 `CashOutDTO` 对象作为请求体（JSON格式）：

| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| payPassword | String | 是 | 支付密码 | 123456 |
| withdrawalMoney | Integer | 是 | 提现金额（单位：分） | 50000 |

### 参数说明

#### payPassword（支付密码）
- **用途**: 验证用户身份
- **格式**: 用户设置的支付密码
- **验证**: 必须与数据库中的支付密码匹配
- **安全**: 建议前端加密后传输

#### withdrawalMoney（提现金额）
- **单位**: 分（1元 = 100分）
- **最小值**: 10分（0.1元）
- **最大值**: 不能超过账户余额
- **示例**:
  - 提现100元 → `withdrawalMoney = 10000`
  - 提现50元 → `withdrawalMoney = 5000`
  - 提现0.1元 → `withdrawalMoney = 10`

**注意**: `storeId` 从登录用户的 JWT Token 中自动获取，不需要在请求中传递。

---

## 请求示例

### 示例1: 提现500元

```http
POST /incomeManage/applyFastCashOut
Content-Type: application/json
Authorization: Bearer YOUR_JWT_TOKEN

{
    "payPassword": "123456",
    "withdrawalMoney": 50000
}
```

```bash
curl -X POST "http://localhost:8080/incomeManage/applyFastCashOut" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "payPassword": "123456",
    "withdrawalMoney": 50000
  }'
```

---

### 示例2: 提现全部余额

假设账户余额为1000元（100000分）：

```http
POST /incomeManage/applyFastCashOut
Content-Type: application/json
Authorization: Bearer YOUR_JWT_TOKEN

{
    "payPassword": "123456",
    "withdrawalMoney": 100000
}
```

---

## 响应参数

### 成功响应

```json
{
    "code": 200,
    "success": true,
    "data": {
        "id": 1001,
        "storeId": 103,
        "storeUserId": 114,
        "money": 50000,
        "commission": null,
        "cashOutType": 0,
        "paymentStatus": 3,
        "orderNo": null,
        "aliOrderNo": null,
        "paymentDate": null,
        "payDate": null,
        "failReason": null,
        "incomeStartTime": "2025-11-20 14:30:00",
        "incomeEndTime": "2025-11-20 14:30:00",
        "deleteFlag": 0,
        "createdTime": "2025-11-20 14:30:00",
        "updatedTime": "2025-11-20 14:30:00"
    },
    "msg": "操作成功"
}
```

### 响应字段说明

#### 外层字段

| 字段名 | 类型 | 说明 |
|--------|------|------|
| code | Integer | 状态码（200-成功） |
| success | Boolean | 是否成功 |
| data | Object | 提现记录对象（StoreCashOutRecord） |
| msg | String | 提示信息 |

#### data 字段（StoreCashOutRecord）

| 字段名 | 类型 | 说明 |
|--------|------|------|
| id | Long | 提现记录ID |
| storeId | Integer | 门店ID |
| storeUserId | Integer | 用户ID |
| money | Integer | 提现金额（单位：分） |
| commission | Integer | 手续费（单位：分，可为null） |
| cashOutType | Integer | 提现类型（0-全部提现） |
| paymentStatus | Integer | ⚠️ 提现状态（3-已通过） |
| orderNo | String | 提现订单号（初始为null） |
| aliOrderNo | String | 支付宝订单号（初始为null） |
| paymentDate | Date | 申请时间（初始为null） |
| payDate | Date | 打款时间（初始为null） |
| failReason | String | 失败原因（初始为null） |
| incomeStartTime | Date | 收入开始时间 |
| incomeEndTime | Date | 收入结束时间 |
| deleteFlag | Integer | 删除标识（0-未删除） |
| createdTime | Date | 创建时间 |
| updatedTime | Date | 更新时间 |

**重要说明**:
- 🔔 `paymentStatus = 3` 表示"已通过"，区别于普通提现的"待审核"
- 🔔 订单号、支付宝订单号等字段需要在打款时填充

---

### 失败响应

#### 1. 支付密码错误

```json
{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "支付密码错误"
}
```

#### 2. 余额不足

```json
{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "余额不足"
}
```

#### 3. 提现金额过小

```json
{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "金额不能小于0.1元"
}
```

#### 4. 未登录或token无效

```json
{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "请先登录"
}
```

#### 5. 系统异常

```json
{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "快速提现失败：{异常信息}"
}
```

---

## 业务逻辑说明

### 快速提现流程

```
┌─────────────────────────────────────────────┐
│  1. 接收请求（从DTO获取参数）                 │
│  - payPassword (必填，从DTO)                 │
│  - withdrawalMoney (必填，单位：分，从DTO)   │
│  - JWT token (自动获取storeId)               │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  2. 从 JWT token 中解析门店ID                 │
│  storeId = LoginUserUtil.getCurrentStoreId()│
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  3. 验证支付密码                              │
│  SELECT * FROM store_user                    │
│  WHERE store_id = ? AND pay_password = ?    │
│  IF user == null                             │
│    返回错误: "支付密码错误"                   │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  4. 验证账户余额                              │
│  IF user.money <= withdrawalMoney            │
│    返回错误: "余额不足"                       │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  5. 验证提现金额                              │
│  amountInYuan = withdrawalMoney ÷ 100        │
│  IF amountInYuan < 0.1                       │
│    返回错误: "金额不能小于0.1元"              │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  6. 创建提现记录                              │
│  StoreCashOutRecord record = new ...         │
│  record.paymentStatus = 3  // 已通过         │
│  INSERT INTO store_cash_out_record           │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  7. 返回结果                                  │
│  返回 StoreCashOutRecord 对象                │
└─────────────────────────────────────────────┘
```

### 与原接口的对比

#### 原接口（app端）
```java
@GetMapping("/applyCashOut")
public R applyCashOut(Integer storeId, String payPassword, Integer withdrawalMoney)
```
- 使用 GET 请求
- 需要手动传递 storeId
- URL: `/storeIncomeDetailsRecord/applyCashOut?storeId=103&payPassword=123456&withdrawalMoney=50000`

#### 新接口（web端）
```java
@PostMapping("/applyFastCashOut")
public R<?> applyFastCashOut(@RequestBody CashOutDTO cashOutDTO)
```
- 使用 POST 请求（更安全）
- 从 JWT token 自动获取 storeId
- 使用 DTO 接收 JSON 请求体
- URL: `/incomeManage/applyFastCashOut`

**核心逻辑一致性**:
- ✅ 支付密码验证逻辑相同
- ✅ 余额验证逻辑相同（`money <= withdrawalMoney`）
- ✅ 最小金额验证相同（0.1元）
- ✅ 提现状态相同（paymentStatus = 3）
- ✅ 返回数据格式相同（StoreCashOutRecord对象）

---

## 数据库操作

### 查询用户SQL

```sql
SELECT * FROM store_user
WHERE store_id = ?
  AND pay_password = ?
LIMIT 1
```

### 插入提现记录SQL

```sql
INSERT INTO store_cash_out_record (
    store_id, 
    money, 
    cash_out_type, 
    payment_status, 
    delete_flag, 
    income_start_time, 
    income_end_time, 
    store_user_id,
    created_time,
    updated_time
) VALUES (
    ?,      -- storeId
    ?,      -- withdrawalMoney
    0,      -- cashOutType (0-全部提现)
    3,      -- paymentStatus (3-已通过)
    0,      -- deleteFlag
    NOW(),  -- incomeStartTime
    NOW(),  -- incomeEndTime
    ?,      -- storeUserId
    NOW(),  -- createdTime
    NOW()   -- updatedTime
)
```

---

## 业务场景

### 场景 1: 正常快速提现

**前置条件**: 
- 账户余额: 1000元（100000分）
- 支付密码: 123456

**请求**:
```http
POST /incomeManage/applyFastCashOut
Content-Type: application/json
Authorization: Bearer VALID_TOKEN

{
    "payPassword": "123456",
    "withdrawalMoney": 50000
}
```

**响应**:
```json
{
    "code": 200,
    "success": true,
    "data": {
        "id": 1001,
        "storeId": 103,
        "money": 50000,
        "paymentStatus": 3,
        ...
    },
    "msg": "操作成功"
}
```

**数据库变化**:
- ✅ 新增一条提现记录（paymentStatus=3）
- ❌ 账户余额不变（与普通提现不同）

---

### 场景 2: 支付密码错误

**请求**:
```http
POST /incomeManage/applyFastCashOut
Content-Type: application/json

{
    "payPassword": "wrong_password",
    "withdrawalMoney": 50000
}
```

**响应**:
```json
{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "支付密码错误"
}
```

---

### 场景 3: 余额不足

**前置条件**: 账户余额 100元（10000分）

**请求**:
```http
POST /incomeManage/applyFastCashOut
Content-Type: application/json

{
    "payPassword": "123456",
    "withdrawalMoney": 20000
}
```

**响应**:
```json
{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "余额不足"
}
```

---

### 场景 4: 提现金额过小

**请求**:
```http
POST /incomeManage/applyFastCashOut
Content-Type: application/json

{
    "payPassword": "123456",
    "withdrawalMoney": 5
}
```

**响应**:
```json
{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "金额不能小于0.1元"
}
```

---

## 前端集成示例

### Vue.js 示例

```javascript
export default {
    data() {
        return {
            payPassword: '',
            withdrawalMoney: '',  // 用户输入的元金额
            loading: false
        };
    },
    methods: {
        async applyFastCashOut() {
            // 1. 验证输入
            if (!this.payPassword) {
                this.$message.error('请输入支付密码');
                return;
            }
            
            if (!this.withdrawalMoney || this.withdrawalMoney < 0.1) {
                this.$message.error('提现金额不能小于0.1元');
                return;
            }
            
            // 2. 转换金额单位（元转分）
            const withdrawalMoneyInCents = Math.floor(this.withdrawalMoney * 100);
            
            // 3. 发送请求（使用JSON Body）
            this.loading = true;
            try {
                const response = await this.$axios.post('/incomeManage/applyFastCashOut', {
                    payPassword: this.payPassword,
                    withdrawalMoney: withdrawalMoneyInCents
                });
                
                if (response.data.success) {
                    const cashOutRecord = response.data.data;
                    this.$message.success('快速提现申请成功');
                    console.log('提现记录ID:', cashOutRecord.id);
                    console.log('提现状态:', cashOutRecord.paymentStatus);  // 3-已通过
                    
                    // 刷新列表或跳转
                    this.$router.push('/cashout-list');
                } else {
                    this.$message.error(response.data.msg);
                }
            } catch (error) {
                console.error('快速提现失败:', error);
                this.$message.error('快速提现失败，请稍后重试');
            } finally {
                this.loading = false;
            }
        }
    }
}
```

---

### React 示例

```javascript
import { useState } from 'react';
import axios from 'axios';

function FastCashOut() {
    const [payPassword, setPayPassword] = useState('');
    const [withdrawalMoney, setWithdrawalMoney] = useState('');
    const [loading, setLoading] = useState(false);
    
    const handleSubmit = async () => {
        // 1. 验证输入
        if (!payPassword) {
            alert('请输入支付密码');
            return;
        }
        
        const amount = parseFloat(withdrawalMoney);
        if (!amount || amount < 0.1) {
            alert('提现金额不能小于0.1元');
            return;
        }
        
        // 2. 转换金额单位（元转分）
        const withdrawalMoneyInCents = Math.floor(amount * 100);
        
        // 3. 发送请求（使用JSON Body）
        setLoading(true);
        try {
            const response = await axios.post('/incomeManage/applyFastCashOut', {
                payPassword,
                withdrawalMoney: withdrawalMoneyInCents
            });
            
            if (response.data.success) {
                const cashOutRecord = response.data.data;
                alert('快速提现申请成功');
                console.log('提现记录:', cashOutRecord);
                // 跳转或刷新
            } else {
                alert(response.data.msg);
            }
        } catch (error) {
            console.error('快速提现失败:', error);
            alert('快速提现失败，请稍后重试');
        } finally {
            setLoading(false);
        }
    };
    
    return (
        <div>
            <h2>快速提现（免审核）</h2>
            <div>
                <label>支付密码：</label>
                <input 
                    type="password" 
                    value={payPassword}
                    onChange={(e) => setPayPassword(e.target.value)}
                />
            </div>
            <div>
                <label>提现金额（元）：</label>
                <input 
                    type="number" 
                    min="0.1"
                    step="0.01"
                    value={withdrawalMoney}
                    onChange={(e) => setWithdrawalMoney(e.target.value)}
                />
            </div>
            <button onClick={handleSubmit} disabled={loading}>
                {loading ? '提交中...' : '确认提现'}
            </button>
            <p className="tip">
                ⚠️ 快速提现无需审核，提现记录将直接标记为"已通过"状态
            </p>
        </div>
    );
}
```

---

## 测试用例

### 测试场景1: 正常快速提现

**前置条件**:
- 账户余额: 1000元
- 支付密码: 123456

**请求**:
```http
POST /incomeManage/applyFastCashOut
Content-Type: application/json
Authorization: Bearer VALID_TOKEN

{
    "payPassword": "123456",
    "withdrawalMoney": 50000
}
```

**预期结果**:
- 返回成功
- `data.paymentStatus = 3`（已通过）
- 数据库新增提现记录
- 账户余额不变

---

### 测试场景2: 支付密码错误

**请求**:
```http
POST /incomeManage/applyFastCashOut
Content-Type: application/json

{
    "payPassword": "wrong",
    "withdrawalMoney": 50000
}
```

**预期结果**:
- 返回失败
- 提示 "支付密码错误"
- 数据库无变化

---

### 测试场景3: 余额不足

**前置条件**: 账户余额 100元

**请求**:
```http
POST /incomeManage/applyFastCashOut
Content-Type: application/json

{
    "payPassword": "123456",
    "withdrawalMoney": 20000
}
```

**预期结果**:
- 返回失败
- 提示 "余额不足"

---

### 测试场景4: 最小金额验证

**请求**:
```http
POST /incomeManage/applyFastCashOut
Content-Type: application/json

{
    "payPassword": "123456",
    "withdrawalMoney": 5
}
```

**预期结果**:
- 返回失败
- 提示 "金额不能小于0.1元"

---

### 测试场景5: 边界值测试

**请求**:
```http
POST /incomeManage/applyFastCashOut
Content-Type: application/json

{
    "payPassword": "123456",
    "withdrawalMoney": 10
}
```

**预期结果**:
- 返回成功
- 提现金额 0.1元（最小金额）

---

## 常见问题

### Q1: 快速提现与普通提现有什么区别？

**答案**: 
- **快速提现** (`/applyFastCashOut`): 
  - 提现记录直接标记为"已通过"（paymentStatus=3）
  - 无需人工审核
  - 账户余额不立即扣减
  - 与app端保持一致
- **普通提现** (`/cashOut`):
  - 提现记录标记为"待审核"（paymentStatus=1）
  - 需要人工审核
  - 提交时立即扣减账户余额

---

### Q2: 快速提现会立即扣款吗？

**答案**: 不会。快速提现只创建提现记录，不扣减账户余额。实际扣款在管理员审批打款时进行。

---

### Q3: paymentStatus=3 是什么意思？

**答案**: 
- `paymentStatus = 3` 表示"已通过审核"
- 区别于：
  - `1` - 待审核
  - `2` - 审核不通过
  - `4` - 已打款
  - `5` - 打款失败

---

### Q4: 提现金额的单位是什么？

**答案**: 
- 接口参数使用 **分** 作为单位
- 1元 = 100分
- 例如：提现100元，传 `withdrawalMoney=10000`

---

### Q5: 为什么使用 POST 而不是 GET？

**答案**: 
- POST 更安全，密码不会出现在 URL 中
- 符合 RESTful 规范（数据修改操作）
- 与 web 端其他接口保持一致

---

### Q6: 快速提现失败会回滚吗？

**答案**: 
- 会。接口使用 `@Transactional` 注解
- 如果创建提现记录失败，事务会回滚
- 保证数据一致性

---

## 注意事项

### 1. 金额单位

- ⚠️ **参数单位**: 分（Integer）
- ⚠️ **最小金额**: 10分（0.1元）
- ⚠️ **前端转换**: 用户输入元，需转换为分

### 2. 提现状态

- ⚠️ **状态值**: paymentStatus = 3（已通过）
- ⚠️ **与普通提现不同**: 普通提现为 1（待审核）
- ⚠️ **后续流程**: 管理员可直接进入打款流程

### 3. 账户余额

- ⚠️ **验证逻辑**: `money <= withdrawalMoney` 会失败
- ⚠️ **扣款时机**: 创建记录时不扣款，打款时扣款
- ⚠️ **与普通提现不同**: 普通提现提交时立即扣款

### 4. 安全性

- ⚠️ **密码保护**: 建议前端加密传输
- ⚠️ **日志脱敏**: 日志中密码显示为 `***`
- ⚠️ **Token验证**: 必须提供有效的JWT token

### 5. 事务管理

- ⚠️ **事务保护**: 使用 `@Transactional` 保证原子性
- ⚠️ **异常回滚**: 任何异常都会导致事务回滚
- ⚠️ **数据一致性**: 保证提现记录创建的完整性

---

## 迁移说明

### 原接口（app端）

- **服务**: `alien-store`
- **路径**: `/alienStore/storeIncomeDetailsRecord/applyCashOut`
- **Controller**: `StoreIncomeDetailsRecordController`
- **Service**: `StoreIncomeDetailsRecordService.applyCashOut()`
- **请求方式**: GET
- **参数**: `storeId`, `payPassword`, `withdrawalMoney`

### 新接口（web端）

- **服务**: `alien-store-platform`
- **路径**: `/incomeManage/applyFastCashOut`
- **Controller**: `IncomeManageController`
- **Service**: `IncomeManageService.applyFastCashOut()`
- **请求方式**: POST
- **参数**: `payPassword`, `withdrawalMoney`（storeId从token获取）

### 差异说明

| 项目 | app端 | web端 | 说明 |
|------|-------|-------|------|
| 请求方式 | GET | POST | POST更安全 |
| 请求参数 | @RequestParam | @RequestBody(DTO) | 使用DTO更规范 |
| storeId来源 | 参数 | JWT token | 防伪造 |
| 账户扣款 | 不扣款 | 不扣款 | 保持一致 |
| 提现状态 | paymentStatus=3 | paymentStatus=3 | 保持一致 |
| 返回值 | StoreCashOutRecord | StoreCashOutRecord | 保持一致 |
| 事务管理 | @Transactional | @Transactional | 保持一致 |

### 复用的核心逻辑

1. ✅ 支付密码验证逻辑
2. ✅ 余额验证逻辑（`money <= withdrawalMoney`）
3. ✅ 最小金额验证（0.1元）
4. ✅ 提现状态设置（paymentStatus=3）
5. ✅ 提现记录创建逻辑
6. ✅ 返回数据格式

---

## 更新日志

### 2025-11-25

**修改内容**:
- ✅ 修改请求参数为 DTO 形式（`CashOutDTO`）
- ✅ 使用 `@RequestBody` 接收 JSON 请求体
- ✅ 移除 `@ApiImplicitParams` 注解
- ✅ 更新所有请求示例为 JSON Body 格式
- ✅ 更新前端集成示例

**涉及文件**:
- `IncomeManageController.java` - 更新
- `28-快速提现申请接口.md` - 更新文档

---

### 2025-11-20

**新增接口**:
- ✅ `POST /incomeManage/applyFastCashOut` - 快速提现申请（免审核）

**核心功能**:
- ✅ 从 JWT token 自动获取 storeId
- ✅ 支付密码验证
- ✅ 账户余额验证（`money <= withdrawalMoney`）
- ✅ 最小金额验证（0.1元）
- ✅ 创建提现记录（paymentStatus=3，已通过）
- ✅ 不扣减账户余额（与app端保持一致）
- ✅ 返回 StoreCashOutRecord 对象
- ✅ 事务保护

**与app端一致性**:
- ✅ 业务逻辑完全相同
- ✅ 提现状态相同（paymentStatus=3）
- ✅ 验证规则相同
- ✅ 返回数据格式相同

**涉及文件**:
- `IncomeManageController.java` - 新增 `applyFastCashOut` 接口
- `IncomeManageService.java` - 新增方法定义
- `IncomeManageServiceImpl.java` - 新增方法实现

**代码质量**:
- ✅ Linter检查：无错误
- ✅ 日志记录：详细
- ✅ 异常处理：完善
- ✅ 代码注释：完整
- ✅ 事务管理：正确

**开发人员**: ssk

---

**文档版本**: v1.0  
**最后更新**: 2025-11-20  
**维护人员**: ssk


