Browse Source

feat(income): 今日收益、提现相关接口提交

wxd 2 weeks ago
parent
commit
af979911e7

+ 8 - 0
alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsServiceImpl.java

@@ -1515,11 +1515,19 @@ public class SecondGoodsServiceImpl extends ServiceImpl<SecondGoodsMapper, Secon
     @Override
     public IPage<SecondGoodsVo> getCollectGoodsPage(IPage<SecondGoodsVo> page, int userId) {
         LifeUser lifeUser = lifeUserMapper.selectById(userId);
+        // 获取商品屏蔽列表
+        List<SecondGoods> shieldedGoodsList = getShieldedGoodsList(userId);
+        // 提取屏蔽商品ID
+        List<Integer> shieldedGoodsIds = shieldedGoodsList.stream()
+                .map(SecondGoods::getId)
+                .collect(Collectors.toList());
+
         QueryWrapper<SecondGoodsVo> queryWrapper = new QueryWrapper<>();
         queryWrapper
                 // 可以查看已删除的商品数据
 //                .eq("sg.delete_flag", Constants.DeleteFlag.NOT_DELETED)
                 .eq("lc.delete_flag", Constants.DeleteFlag.NOT_DELETED)
+                .notIn(CollectionUtil.isNotEmpty(shieldedGoodsIds), "sg.id", shieldedGoodsIds)
                 .eq("lc.user_id", "user_"+lifeUser.getUserPhone())
                 .orderByDesc("lc.created_time");
         return secondGoodsMapper.getCollectGoodsPage(page, queryWrapper);

+ 23 - 21
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/IncomeManageController.java

@@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.StoreCashOutRecord;
+import shop.alien.storeplatform.dto.CashOutDTO;
 import shop.alien.storeplatform.service.IncomeManageService;
 import shop.alien.storeplatform.util.LoginUserUtil;
 
@@ -78,20 +79,13 @@ public class IncomeManageController {
 
     @ApiOperation("提现申请")
     @ApiOperationSupport(order = 3)
-    @ApiImplicitParams({
-            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true),
-            @ApiImplicitParam(name = "payPassword", value = "支付密码", dataType = "String", paramType = "query", required = true),
-            @ApiImplicitParam(name = "withdrawalMoney", value = "提现金额(单位:分)", dataType = "Integer", paramType = "query", required = true)
-    })
     @PostMapping("/cashOut")
-    public R<?> cashOut(
-            @RequestParam("payPassword") String payPassword,
-            @RequestParam("withdrawalMoney") Integer withdrawalMoney) {
+    public R<?> cashOut(@RequestBody CashOutDTO cashOutDTO) {
         log.info("IncomeManageController.cashOut? payPassword=***, withdrawalMoney={}",
-                 withdrawalMoney);
+                 cashOutDTO.getWithdrawalMoney());
         try {
             Integer storeId = LoginUserUtil.getCurrentStoreId();
-            return incomeManageService.cashOut(storeId, payPassword, withdrawalMoney);
+            return incomeManageService.cashOut(storeId, cashOutDTO.getPayPassword(), cashOutDTO.getWithdrawalMoney());
         } catch (Exception e) {
             log.error("IncomeManageController.cashOut ERROR: {}", e.getMessage(), e);
             return R.fail("提现失败:" + e.getMessage());
@@ -144,7 +138,21 @@ public class IncomeManageController {
         }
     }
 
-    @ApiOperation("团购收益查询")
+    /**
+     * 今日收益查询
+     * <p>
+     * 接口迁移来源: App端 /alienStore/storeIncomeDetailsRecord/groupIncome
+     * 原接口参数: date, storeId, incomeType, page, size
+     * Web端变更: storeId 从 JWT Token 中自动获取
+     * </p>
+     *
+     * @param date 日期(格式:yyyy-MM-dd)
+     * @param incomeType 收入类型, 0:主页, 1:优惠券, 2:代金券, 3:套餐, 4:联名卡
+     * @param page 页码
+     * @param size 每页条数
+     * @return 今日收益数据
+     */
+    @ApiOperation("今日收益查询")
     @ApiOperationSupport(order = 6)
     @ApiImplicitParams({
             @ApiImplicitParam(name = "date", value = "日期(格式:yyyy-MM-dd)", dataType = "String", paramType = "query", required = true),
@@ -170,21 +178,15 @@ public class IncomeManageController {
         }
     }
 
-    @ApiOperation("快速提现申请(免审核)")
+    @ApiOperation("快速提现申请")
     @ApiOperationSupport(order = 7)
-    @ApiImplicitParams({
-            @ApiImplicitParam(name = "payPassword", value = "支付密码", dataType = "String", paramType = "query", required = true),
-            @ApiImplicitParam(name = "withdrawalMoney", value = "提现金额(单位:分)", dataType = "Integer", paramType = "query", required = true)
-    })
     @PostMapping("/applyFastCashOut")
-    public R<?> applyFastCashOut(
-            @RequestParam("payPassword") String payPassword,
-            @RequestParam("withdrawalMoney") Integer withdrawalMoney) {
+    public R<?> applyFastCashOut(@RequestBody CashOutDTO cashOutDTO) {
         log.info("IncomeManageController.applyFastCashOut? payPassword=***, withdrawalMoney={}",
-                withdrawalMoney);
+                cashOutDTO.getWithdrawalMoney());
         try {
             Integer storeId = LoginUserUtil.getCurrentStoreId();
-            return incomeManageService.applyFastCashOut(storeId, payPassword, withdrawalMoney);
+            return incomeManageService.applyFastCashOut(storeId, cashOutDTO.getPayPassword(), cashOutDTO.getWithdrawalMoney());
         } catch (Exception e) {
             log.error("IncomeManageController.applyFastCashOut ERROR: {}", e.getMessage(), e);
             return R.fail("快速提现失败:" + e.getMessage());

+ 27 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/dto/CashOutDTO.java

@@ -0,0 +1,27 @@
+package shop.alien.storeplatform.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 提现申请DTO
+ *
+ * @author ssk
+ * @since 2025-11-25
+ */
+@Data
+@ApiModel("提现申请DTO")
+public class CashOutDTO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "支付密码(6位数字)", required = true, example = "123456")
+    private String payPassword;
+
+    @ApiModelProperty(value = "提现金额(单位:分)", required = true, example = "10000")
+    private Integer withdrawalMoney;
+}
+

+ 5 - 2
alien-store-platform/src/main/java/shop/alien/storeplatform/service/IncomeManageService.java

@@ -70,14 +70,17 @@ public interface IncomeManageService {
     StoreCashOutRecord getCashOutRecordDetail(Integer id);
 
     /**
-     * 团购收益查询
+     * 今日收益查询
+     * <p>
+     * 接口迁移来源: App端 /alienStore/storeIncomeDetailsRecord/groupIncome?date=2025-11-11&storeId=103&incomeType=0&page=1&size=10
+     * </p>
      *
      * @param storeId    门店ID
      * @param date       日期(格式:yyyy-MM-dd)
      * @param incomeType 收入类型, 0:主页, 1:优惠券, 2:代金券, 3:套餐, 4:联名卡
      * @param page       页码
      * @param size       每页条数
-     * @return 团购收益信息
+     * @return 今日收益信息
      */
     Object getGroupIncome(Integer storeId, String date, Integer incomeType, Integer page, Integer size);
 

+ 6 - 3
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/IncomeManageServiceImpl.java

@@ -455,18 +455,21 @@ public class IncomeManageServiceImpl extends ServiceImpl<StoreIncomeDetailsRecor
     }
 
     /**
-     * 团购收益查询
+     * 今日收益查询
+     * <p>
+     * 接口迁移来源: App端 /alienStore/storeIncomeDetailsRecord/groupIncome?date=2025-11-11&storeId=103&incomeType=0&page=1&size=10
+     * </p>
      *
      * @param storeId    门店ID
      * @param date       日期
      * @param incomeType 收入类型
      * @param page       页码
      * @param size       每页条数
-     * @return 团购收益信息
+     * @return 今日收益信息
      */
     @Override
     public Object getGroupIncome(Integer storeId, String date, Integer incomeType, Integer page, Integer size) {
-        log.info("IncomeManageServiceImpl.getGroupIncome - 开始查询团购收益: storeId={}, date={}, incomeType={}, page={}, size={}",
+        log.info("IncomeManageServiceImpl.getGroupIncome - 开始查询今日收益: storeId={}, date={}, incomeType={}, page={}, size={}",
                 storeId, date, incomeType, page, size);
 
         // 1. 构建查询条件:指定日期内、未绑定提现记录

+ 3 - 3
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StoreManageServiceImpl.java

@@ -92,9 +92,9 @@ public class StoreManageServiceImpl implements StoreManageService {
         storeInfoDto.setUserAccount(storeUser.getId().toString());
 
         // 校验店铺信息 如果店铺id 不为空,证明店铺以关联,不可再次申请
-        if (storeUser.getStoreId() != null){
-            throw new IllegalArgumentException("该用户已关联店铺,请勿重复申请");
-        }
+//        if (storeUser.getStoreId() != null){
+//            throw new IllegalArgumentException("该用户已关联店铺,请勿重复申请");
+//        }
 
         // 1. 获取经营板块信息
         Integer businessSection = storeInfoDto.getBusinessSection();

+ 912 - 0
alien-store-platform/接口文档/23-提现申请接口.md

@@ -0,0 +1,912 @@
+# Web端商户提现申请接口文档
+
+## 模块概述
+
+本模块提供提现申请功能,商户可以通过本接口提交提现申请,系统将验证支付密码、账户余额等信息后创建提现记录。Web端采用人工审核模式,提现申请提交后需等待管理员审核。
+
+---
+
+## 接口信息
+
+### 提现申请
+
+#### 接口详情
+
+- **接口名称**: 提现申请
+- **接口路径**: `POST /incomeManage/cashOut`
+- **请求方式**: POST
+- **接口描述**: 商户提交提现申请,扣减账户余额并创建待审核的提现记录
+- **登录验证**: ✅ 需要(使用 `@LoginRequired` 注解)
+
+---
+
+## 请求参数
+
+### 请求体(Request Body)
+
+使用 `CashOutDTO` 对象作为请求体(JSON格式):
+
+| 参数名 | 类型 | 必填 | 说明 | 示例值 |
+|--------|------|------|------|--------|
+| payPassword | String | 是 | 支付密码(MD5加密) | 222222 |
+| withdrawalMoney | Integer | 是 | 提现金额(单位:分) | 50000 |
+
+### 参数说明
+
+#### withdrawalMoney(提现金额)
+- **单位**: 分(Integer)
+- **最小金额**: 10分(0.1元)
+- **最大金额**: 不超过账户余额
+- **示例**: 50000分 = 500.00元
+
+#### payPassword(支付密码)
+- **加密方式**: MD5
+- **说明**: 需要与数据库中存储的支付密码一致
+
+**注意**: `storeId` 从登录用户的 Token 中自动获取,不需要在请求中传递。
+
+---
+
+## 请求示例
+
+```http
+POST /incomeManage/cashOut
+Content-Type: application/json
+Authorization: Bearer YOUR_TOKEN
+
+{
+    "payPassword": "222222",
+    "withdrawalMoney": 50000
+}
+```
+
+```bash
+curl -X POST "http://localhost:8080/incomeManage/cashOut" \
+  -H "Authorization: Bearer YOUR_TOKEN" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "payPassword": "222222",
+    "withdrawalMoney": 50000
+  }'
+```
+
+```javascript
+// JavaScript
+fetch('/incomeManage/cashOut', {
+    method: 'POST',
+    headers: {
+        'Authorization': 'Bearer YOUR_TOKEN',
+        'Content-Type': 'application/json'
+    },
+    body: JSON.stringify({
+        payPassword: '222222',
+        withdrawalMoney: 50000
+    })
+})
+.then(res => res.json())
+.then(data => console.log(data));
+```
+
+---
+
+## 响应参数
+
+### 成功响应
+
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": {
+        "cashOutRecordId": 1001,
+        "withdrawalMoney": "500.00",
+        "status": "pending",
+        "message": "提现申请已提交,等待审核"
+    },
+    "msg": "操作成功"
+}
+```
+
+### 响应字段说明
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| data.cashOutRecordId | Long | 提现记录ID |
+| data.withdrawalMoney | String | 提现金额(单位:元) |
+| data.status | String | 提现状态(pending-待审核) |
+| data.message | String | 提示信息 |
+
+---
+
+### 失败响应
+
+#### 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. 未登录或登录过期
+
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "请先登录"
+}
+```
+
+#### 5. 系统异常
+
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "提现失败:{异常信息}"
+}
+```
+
+---
+
+## 业务逻辑说明
+
+### 处理流程
+
+```
+┌─────────────────────────────────────────────┐
+│  1. 接收提现参数(从JWT Token获取storeId)    │
+│  - payPassword(从DTO)                      │
+│  - withdrawalMoney(从DTO)                  │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  2. 验证支付密码                              │
+│  根据 storeId 和 payPassword 查询用户        │
+│  IF 用户不存在                               │
+│    RETURN "支付密码错误"                     │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  3. 验证账户余额                              │
+│  IF storeUser.money < withdrawalMoney        │
+│    RETURN "余额不足"                         │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  4. 验证提现金额                              │
+│  amountInYuan = withdrawalMoney ÷ 100        │
+│  IF amountInYuan < 0.1                       │
+│    RETURN "金额不能小于0.1元"                │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  5. 创建提现记录                              │
+│  storeCashOutRecord.storeId = storeId        │
+│  storeCashOutRecord.money = withdrawalMoney  │
+│  storeCashOutRecord.paymentStatus = 1(待审核)│
+│  storeCashOutRecord.storeUserId = userId     │
+│  INSERT INTO store_cash_out_record           │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  6. 扣减账户余额                              │
+│  newMoney = storeUser.money - withdrawalMoney│
+│  UPDATE store_user SET money = newMoney      │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  7. 返回提现记录信息                          │
+│  返回提现记录ID、金额、状态等                 │
+└─────────────────────────────────────────────┘
+```
+
+### Web端审核模式
+
+**与App端差异**:
+- **App端**: 直接调用支付宝API,实时转账
+- **Web端**: 创建待审核记录,人工审核后再打款
+
+**审核流程**:
+1. 商户提交提现申请
+2. 系统扣减账户余额,创建待审核记录
+3. 管理员审核提现申请
+4. 审核通过后调用支付宝API打款
+5. 打款成功后更新提现状态
+
+---
+
+## 数据库操作
+
+### 涉及的表
+
+#### 1. store_user(店铺用户表)
+
+**查询SQL**:
+```sql
+SELECT * FROM store_user
+WHERE store_id = ?
+  AND pay_password = ?
+```
+
+**说明**:
+- 验证支付密码
+- 获取账户余额和用户信息
+
+**更新SQL**:
+```sql
+UPDATE store_user
+SET money = money - ?
+WHERE id = ?
+```
+
+**说明**:
+- 扣减账户余额
+- 使用事务保证数据一致性
+
+#### 2. store_cash_out_record(提现记录表)
+
+**插入SQL**:
+```sql
+INSERT INTO store_cash_out_record (
+    store_id,
+    money,
+    cash_out_type,
+    payment_status,
+    delete_flag,
+    store_user_id,
+    pay_date
+) VALUES (?, ?, 0, 1, 0, ?, NOW())
+```
+
+**字段说明**:
+- `store_id`: 门店ID
+- `money`: 提现金额(分)
+- `cash_out_type`: 提现类型,0-全部提现
+- `payment_status`: 支付状态,1-待审核
+- `delete_flag`: 删除标识,0-未删除
+- `store_user_id`: 用户ID
+- `pay_date`: 申请时间
+
+---
+
+## 提现状态说明
+
+### payment_status 状态值
+
+| Status | 说明 | 描述 |
+|--------|------|------|
+| 1 | 待审核 | 提现申请已提交,等待管理员审核 |
+| 2 | 审核不通过 | 管理员拒绝提现申请 |
+| 3 | 已通过 | 管理员审核通过,等待打款 |
+| 4 | 已打款 | 支付宝转账成功 |
+| 5 | 打款失败 | 支付宝转账失败 |
+
+**Web端流程**:
+```
+1(待审核) -> 3(已通过) -> 4(已打款)
+         -> 2(审核不通过)
+         -> 5(打款失败)
+```
+
+---
+
+## 金额计算规则
+
+### 金额单位转换
+
+| 数据库存储(分) | 显示(元) | 转换公式 |
+|------------------|-----------|----------|
+| 50000 | 500.00 | money ÷ 100 |
+| 10 | 0.10 | money ÷ 100 |
+| 100 | 1.00 | money ÷ 100 |
+
+### 转换代码
+
+```java
+// 提现金额转换(分 -> 元)
+BigDecimal amountInYuan = new BigDecimal(withdrawalMoney)
+    .divide(new BigDecimal(100), 2, RoundingMode.DOWN);
+```
+
+**转换规则**:
+- 使用 `BigDecimal` 进行精确计算
+- 除以100转换为元
+- 保留2位小数
+- 舍入模式:`DOWN`(向下取整)
+
+---
+
+## 业务场景
+
+### 场景 1: 正常提现
+
+**前置条件**:
+- 账户余额: 100000分(1000元)
+- 提现金额: 50000分(500元)
+- 支付密码: 正确
+
+**请求**:
+```http
+POST /incomeManage/cashOut
+Content-Type: application/json
+
+{
+    "payPassword": "222222",
+    "withdrawalMoney": 50000
+}
+```
+
+**响应**:
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": {
+        "cashOutRecordId": 1001,
+        "withdrawalMoney": "500.00",
+        "status": "pending",
+        "message": "提现申请已提交,等待审核"
+    }
+}
+```
+
+**结果**:
+- ✅ 创建提现记录
+- ✅ 账户余额变为50000分(500元)
+- ✅ 提现记录状态为"待审核"
+
+---
+
+### 场景 2: 支付密码错误
+
+**前置条件**:
+- 账户余额: 100000分
+- 提现金额: 50000分
+- 支付密码: **错误**
+
+**请求**:
+```http
+POST /incomeManage/cashOut
+Content-Type: application/json
+
+{
+    "payPassword": "wrongpassword",
+    "withdrawalMoney": 50000
+}
+```
+
+**响应**:
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "支付密码错误"
+}
+```
+
+**结果**:
+- ❌ 提现失败
+- ❌ 账户余额不变
+- ❌ 不创建提现记录
+
+---
+
+### 场景 3: 余额不足
+
+**前置条件**:
+- 账户余额: 30000分(300元)
+- 提现金额: 50000分(500元)
+- 支付密码: 正确
+
+**请求**:
+```http
+POST /incomeManage/cashOut
+Content-Type: application/json
+
+{
+    "payPassword": "222222",
+    "withdrawalMoney": 50000
+}
+```
+
+**响应**:
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "余额不足"
+}
+```
+
+**结果**:
+- ❌ 提现失败
+- ❌ 账户余额不变
+- ❌ 不创建提现记录
+
+---
+
+### 场景 4: 金额过小
+
+**前置条件**:
+- 账户余额: 100000分
+- 提现金额: 5分(0.05元,小于最低0.1元)
+- 支付密码: 正确
+
+**请求**:
+```http
+POST /incomeManage/cashOut
+Content-Type: application/json
+
+{
+    "payPassword": "222222",
+    "withdrawalMoney": 5
+}
+```
+
+**响应**:
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "金额不能小于0.1元"
+}
+```
+
+**结果**:
+- ❌ 提现失败
+- ❌ 账户余额不变
+- ❌ 不创建提现记录
+
+---
+
+### 场景 5: 全部提现
+
+**前置条件**:
+- 账户余额: 100000分(1000元)
+- 提现金额: 100000分(全部余额)
+- 支付密码: 正确
+
+**请求**:
+```http
+POST /incomeManage/cashOut
+Content-Type: application/json
+
+{
+    "payPassword": "222222",
+    "withdrawalMoney": 100000
+}
+```
+
+**响应**:
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": {
+        "cashOutRecordId": 1002,
+        "withdrawalMoney": "1000.00",
+        "status": "pending",
+        "message": "提现申请已提交,等待审核"
+    }
+}
+```
+
+**结果**:
+- ✅ 创建提现记录
+- ✅ 账户余额变为0分
+- ✅ 提现记录状态为"待审核"
+
+---
+
+## 事务管理
+
+### 事务范围
+
+接口使用 `@Transactional` 注解保证数据一致性:
+
+```java
+@Transactional
+@Override
+public R<?> cashOut(Integer storeId, String payPassword, Integer withdrawalMoney) {
+    // 1. 查询用户
+    // 2. 验证余额
+    // 3. 创建提现记录
+    // 4. 扣减账户余额
+}
+```
+
+### 事务保证
+
+如果任何一步操作失败,所有操作都会回滚:
+- ✅ 创建提现记录失败 -> 回滚
+- ✅ 扣减余额失败 -> 回滚(提现记录也回滚)
+- ✅ 抛出异常 -> 回滚
+
+### 隔离级别
+
+使用数据库默认隔离级别(MySQL: REPEATABLE_READ)
+
+---
+
+## 安全考虑
+
+### 1. 支付密码验证
+
+- ✅ 支付密码必须正确
+- ✅ 密码存储使用MD5加密
+- ✅ 密码错误不返回详细信息
+
+### 2. 金额验证
+
+- ✅ 提现金额必须大于等于0.1元
+- ✅ 提现金额不能超过账户余额
+- ✅ 使用BigDecimal避免精度问题
+
+### 3. 并发控制
+
+- ✅ 使用事务保证数据一致性
+- ✅ 乐观锁或悲观锁防止并发问题
+- ⚠️ 建议:高并发场景下使用分布式锁
+
+### 4. 日志记录
+
+- ✅ 记录所有提现请求
+- ✅ 记录失败原因
+- ⚠️ 不记录支付密码明文
+
+---
+
+## 性能优化建议
+
+### 1. 数据库索引
+
+确保以下字段有索引:
+
+```sql
+-- store_user表
+ALTER TABLE store_user 
+ADD INDEX idx_store_pay (store_id, pay_password);
+
+-- store_cash_out_record表
+ALTER TABLE store_cash_out_record 
+ADD INDEX idx_store_status (store_id, payment_status);
+```
+
+### 2. 查询优化
+
+- ✅ 使用复合索引加速查询
+- ✅ 一次查询获取所有需要的字段
+- ✅ 避免不必要的JOIN操作
+
+### 3. 事务优化
+
+- ✅ 事务范围尽量小
+- ✅ 避免在事务中调用外部服务
+- ✅ 快速失败原则
+
+---
+
+## 测试用例
+
+### 测试场景1: 正常提现
+
+**请求**:
+```http
+POST /incomeManage/cashOut
+Content-Type: application/json
+
+{
+    "payPassword": "222222",
+    "withdrawalMoney": 50000
+}
+```
+
+**前置条件**:
+- 账户余额 >= 50000分
+- 支付密码正确
+
+**预期结果**:
+- 返回提现记录ID
+- 账户余额减少50000分
+- 提现记录状态为1(待审核)
+
+---
+
+### 测试场景2: 支付密码错误
+
+**请求**:
+```http
+POST /incomeManage/cashOut
+Content-Type: application/json
+
+{
+    "payPassword": "wrongpassword",
+    "withdrawalMoney": 50000
+}
+```
+
+**预期结果**:
+```json
+{
+    "code": 500,
+    "msg": "支付密码错误"
+}
+```
+
+---
+
+### 测试场景3: 余额不足
+
+**请求**:
+```http
+POST /incomeManage/cashOut
+Content-Type: application/json
+
+{
+    "payPassword": "222222",
+    "withdrawalMoney": 1000000
+}
+```
+
+**前置条件**:
+- 账户余额 < 1000000分
+
+**预期结果**:
+```json
+{
+    "code": 500,
+    "msg": "余额不足"
+}
+```
+
+---
+
+### 测试场景4: 金额过小
+
+**请求**:
+```http
+POST /incomeManage/cashOut
+Content-Type: application/json
+
+{
+    "payPassword": "222222",
+    "withdrawalMoney": 5
+}
+```
+
+**预期结果**:
+```json
+{
+    "code": 500,
+    "msg": "金额不能小于0.1元"
+}
+```
+
+---
+
+### 测试场景5: 并发提现
+
+**测试方法**:
+- 同时发起多个提现请求
+- 总金额超过账户余额
+
+**预期结果**:
+- 只有部分请求成功
+- 成功的请求总金额不超过账户余额
+- 数据一致性得到保证
+
+---
+
+## 注意事项
+
+### 1. 提现金额限制
+
+- ⚠️ **最小金额**: 0.1元(10分)
+- ⚠️ **最大金额**: 账户余额
+- ⚠️ **单位**: 必须使用分(Integer)
+
+### 2. 支付密码
+
+- ⚠️ **加密方式**: MD5
+- ⚠️ **验证**: 必须与数据库中的密码完全一致
+- ⚠️ **安全**: 不在日志中记录密码
+
+### 3. 审核流程
+
+- ⚠️ Web端采用人工审核模式
+- ⚠️ 提现申请提交后立即扣减余额
+- ⚠️ 审核不通过需退还余额
+
+### 4. 事务回滚
+
+- ⚠️ 任何步骤失败都会回滚所有操作
+- ⚠️ 账户余额和提现记录保持一致
+- ⚠️ 建议配置事务超时时间
+
+### 5. 与App端差异
+
+- ⚠️ App端直接调用支付宝API
+- ⚠️ Web端创建待审核记录
+- ⚠️ 业务逻辑相同,但审核流程不同
+
+---
+
+## 迁移说明
+
+### 原接口(app端)
+
+- **服务**: `alien-store`
+- **路径**: `/alienStore/storeIncomeDetailsRecord/cashOut`
+- **Controller**: `StoreIncomeDetailsRecordController`
+- **Service**: `StoreIncomeDetailsRecordService.cashOut()`
+- **特点**: 直接调用支付宝API实时转账
+
+### 新接口(web端)
+
+- **服务**: `alien-store-platform`
+- **路径**: `/incomeManage/cashOut`
+- **Controller**: `IncomeManageController`
+- **Service**: `IncomeManageService.cashOut()`
+- **特点**: 创建待审核记录,人工审核后打款
+
+### 差异说明
+
+| 项目 | app端 | web端 | 说明 |
+|------|-------|-------|------|
+| 接口路径 | `/cashOut` | `/cashOut` | 保持一致 |
+| 请求方式 | GET | POST | POST更符合规范 |
+| 请求参数 | @RequestParam | @RequestBody(DTO) | 使用DTO更规范 |
+| storeId | 请求参数传递 | JWT Token获取 | 更安全 |
+| 打款方式 | 实时调用支付宝API | 人工审核后打款 | Web端更安全 |
+| 提现状态 | 1-待审核, 4-已打款 | 1-待审核 | Web端初始状态固定为1 |
+| 登录验证 | 无 | ✅ 有(@LoginRequired) | 增加登录验证 |
+| 事务管理 | ✅ | ✅ | 都使用@Transactional |
+| 日志记录 | 基础 | 详细 | 增强日志记录 |
+
+### 复用的核心组件
+
+1. **Mapper**: 
+   - `StoreUserMapper`
+   - `StoreCashOutRecordMapper`
+2. **Entity**: 
+   - `StoreUser`
+   - `StoreCashOutRecord`
+3. **Util**: `DateUtils`
+
+---
+
+## 常见问题
+
+### Q1: 为什么Web端不直接调用支付宝API?
+
+**答案**: Web端采用人工审核模式,更安全可控。管理员可以审核每笔提现申请,避免风险。
+
+---
+
+### Q2: 提现申请提交后能否取消?
+
+**答案**: 可以。如果提现状态为"待审核",管理员可以拒绝提现申请,系统会退还余额。
+
+---
+
+### Q3: 支付密码忘记了怎么办?
+
+**答案**: 需要联系管理员重置支付密码,或通过"忘记密码"功能重置。
+
+---
+
+### Q4: 提现金额为什么使用分而不是元?
+
+**答案**: 使用分(Integer)可以避免浮点数精度问题,确保金额计算准确。
+
+---
+
+### Q5: 并发提现会有问题吗?
+
+**答案**: 使用了事务保证数据一致性。高并发场景建议使用分布式锁进一步保证。
+
+---
+
+### Q6: 提现失败余额会退还吗?
+
+**答案**: 
+- 验证失败(密码错误、余额不足):不扣减余额
+- 创建记录失败:事务回滚,余额不变
+- 审核不通过:需要管理员手动退还余额
+
+---
+
+## 更新日志
+
+### 2025-11-25
+
+**修改内容**:
+- ✅ 修改请求参数为 DTO 形式(`CashOutDTO`)
+- ✅ 使用 `@RequestBody` 接收 JSON 请求体
+- ✅ 移除 `@ApiImplicitParams` 注解
+- ✅ `storeId` 从 JWT Token 中自动获取
+
+**涉及文件**:
+- `IncomeManageController.java` - 更新
+- `CashOutDTO.java` - 新建
+- `23-提现申请接口.md` - 更新文档
+
+---
+
+### 2025-11-19
+
+**新增接口**:
+- ✅ `POST /incomeManage/cashOut` - 提现申请
+
+**核心功能**:
+- ✅ 验证支付密码
+- ✅ 验证账户余额
+- ✅ 验证提现金额
+- ✅ 创建提现记录(待审核)
+- ✅ 扣减账户余额
+- ✅ 事务管理保证一致性
+- ✅ 详细日志记录
+- ✅ 完善异常处理
+
+**涉及文件**:
+- `IncomeManageController.java` - 更新
+- `IncomeManageService.java` - 更新
+- `IncomeManageServiceImpl.java` - 更新
+
+**代码质量**:
+- ✅ Linter检查:无错误
+- ✅ 日志记录:详细
+- ✅ 异常处理:完善
+- ✅ 代码注释:完整
+- ✅ 事务管理:严格
+
+**与原接口差异**:
+- ✅ 请求方式改为POST
+- ✅ 采用人工审核模式
+- ✅ 不直接调用支付宝API
+- ✅ 业务逻辑保持一致
+
+**开发人员**: ssk
+
+---
+
+**文档版本**: v1.0  
+**最后更新**: 2025-11-19  
+**维护人员**: ssk
+
+
+
+
+

+ 1009 - 0
alien-store-platform/接口文档/24-提现记录查询接口.md

@@ -0,0 +1,1009 @@
+# Web端商户提现记录查询接口文档
+
+## 模块概述
+
+本模块提供提现记录查询功能,支持按时间范围、提现状态等条件筛选提现记录,并提供分页查询和统计信息。
+
+---
+
+## 接口信息
+
+### 提现记录查询
+
+#### 接口详情
+
+- **接口名称**: 提现记录查询
+- **接口路径**: `GET /incomeManage/getCashOutRecordList`
+- **请求方式**: GET
+- **接口描述**: 查询指定门店的提现记录,支持按时间范围和提现状态筛选,返回分页数据和统计信息
+- **登录验证**: ✅ 需要(使用 `@LoginRequired` 注解)
+
+---
+
+## 请求参数
+
+| 参数名 | 类型 | 必填 | 说明 | 示例值 |
+|--------|------|------|------|--------|
+| storeId | Integer | 是 | 门店ID | 103 |
+| cashOutStartTime | String | 否 | 开始时间(格式:yyyy-MM-dd) | 2025-11-10 |
+| cashOutEndTime | String | 否 | 结束时间(格式:yyyy-MM-dd) | 2025-11-11 |
+| paymentStatus | String | 否 | 提现状态 | 1 |
+| page | Integer | 否 | 页码,默认1 | 1 |
+| size | Integer | 否 | 每页条数,默认10 | 10 |
+
+### 参数说明
+
+#### paymentStatus(提现状态)
+- `1`: 待审核
+- `2`: 审核不通过
+- `3`: 已通过
+- `4`: 已打款
+- `5`: 打款失败
+- 不传:查询所有状态
+
+#### 时间范围
+- **cashOutStartTime**: 查询此日期(含)之后创建的提现记录
+- **cashOutEndTime**: 查询此日期(含)之前创建的提现记录
+- 不传时间参数:查询所有时间的记录
+
+---
+
+## 请求示例
+
+### 示例1: 查询所有提现记录
+
+```http
+GET /incomeManage/getCashOutRecordList?storeId=103&page=1&size=10
+```
+
+```bash
+curl "http://localhost:8080/incomeManage/getCashOutRecordList?storeId=103&page=1&size=10" \
+  -H "Authorization: Bearer YOUR_TOKEN"
+```
+
+---
+
+### 示例2: 按时间范围查询
+
+```http
+GET /incomeManage/getCashOutRecordList?storeId=103&cashOutStartTime=2025-11-10&cashOutEndTime=2025-11-11&page=1&size=10
+```
+
+```bash
+curl "http://localhost:8080/incomeManage/getCashOutRecordList?storeId=103&cashOutStartTime=2025-11-10&cashOutEndTime=2025-11-11&page=1&size=10" \
+  -H "Authorization: Bearer YOUR_TOKEN"
+```
+
+---
+
+### 示例3: 查询待审核记录
+
+```http
+GET /incomeManage/getCashOutRecordList?storeId=103&paymentStatus=1&page=1&size=10
+```
+
+```bash
+curl "http://localhost:8080/incomeManage/getCashOutRecordList?storeId=103&paymentStatus=1&page=1&size=10" \
+  -H "Authorization: Bearer YOUR_TOKEN"
+```
+
+---
+
+### 示例4: 组合查询
+
+```http
+GET /incomeManage/getCashOutRecordList?storeId=103&cashOutStartTime=2025-11-10&cashOutEndTime=2025-11-11&paymentStatus=4&page=1&size=10
+```
+
+---
+
+## 响应参数
+
+### 成功响应
+
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": {
+        "cashOutRecordList": [
+            {
+                "id": 1001,
+                "storeId": 103,
+                "storeUserId": 114,
+                "money": 50000,
+                "commission": 0,
+                "cashOutType": 0,
+                "paymentStatus": 1,
+                "orderNo": "TX20251119001",
+                "aliOrderNo": null,
+                "paymentDate": "2025-11-19 10:30:00",
+                "payDate": "2025-11-19 10:30:00",
+                "settlementAccount": "13800138000",
+                "failReason": null,
+                "deleteFlag": 0,
+                "createdTime": "2025-11-19 10:30:00",
+                "updatedTime": "2025-11-19 10:30:00"
+            },
+            {
+                "id": 1000,
+                "storeId": 103,
+                "storeUserId": 114,
+                "money": 30000,
+                "commission": 0,
+                "cashOutType": 0,
+                "paymentStatus": 4,
+                "orderNo": "TX20251118001",
+                "aliOrderNo": "2025111822001234567890123456",
+                "paymentDate": "2025-11-18 15:20:00",
+                "payDate": "2025-11-18 15:25:00",
+                "settlementAccount": "alipay@example.com",
+                "failReason": null,
+                "deleteFlag": 0,
+                "createdTime": "2025-11-18 15:20:00",
+                "updatedTime": "2025-11-18 15:25:00"
+            }
+        ],
+        "cashOutAllMoney": 800.00,
+        "cashOutNum": 15
+    },
+    "msg": "操作成功"
+}
+```
+
+### 响应字段说明
+
+#### 外层字段
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| data.cashOutRecordList | Array | 提现记录列表(当前页) |
+| data.cashOutAllMoney | BigDecimal | 提现总金额(仅统计待审核状态,单位:元) |
+| data.cashOutNum | Integer | 提现记录总数 |
+
+#### cashOutRecordList 字段
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| id | Long | 提现记录ID |
+| storeId | Integer | 门店ID |
+| storeUserId | Integer | 用户ID |
+| money | Integer | 提现金额(单位:分) |
+| commission | Integer | 手续费(单位:分) |
+| cashOutType | Integer | 提现类型(0-全部提现) |
+| paymentStatus | Integer | 提现状态 |
+| orderNo | String | 提现订单号 |
+| aliOrderNo | String | 支付宝订单号 |
+| paymentDate | Date | 申请时间 |
+| payDate | Date | 打款时间 |
+| failReason | String | 失败原因 |
+| settlementAccount | String | 结算账户(支付宝账号或手机号) |
+| deleteFlag | Integer | 删除标识 |
+| createdTime | Date | 创建时间 |
+| updatedTime | Date | 更新时间 |
+
+**⚠️ 重要字段说明**:
+- `settlementAccount`: 通过 LEFT JOIN `store_user` 表查询得到
+  - 优先使用商户的支付宝账号(`alipay_account`)
+  - 如果没有支付宝账号,则使用手机号(`phone`)
+
+---
+
+### 失败响应
+
+#### 1. 参数错误
+
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "查询失败:门店ID不能为空"
+}
+```
+
+#### 2. 未登录或登录过期
+
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "请先登录"
+}
+```
+
+#### 3. 系统异常
+
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "查询失败:{异常信息}"
+}
+```
+
+---
+
+## 业务逻辑说明
+
+### 查询流程
+
+```
+┌─────────────────────────────────────────────┐
+│  1. 接收查询参数                              │
+│  - storeId (必填)                            │
+│  - cashOutStartTime (可选)                   │
+│  - cashOutEndTime (可选)                     │
+│  - paymentStatus (可选)                      │
+│  - page, size (分页)                         │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  2. 构建查询条件                              │
+│  wrapper.eq(storeId)                         │
+│  IF paymentStatus != null                    │
+│    wrapper.eq(paymentStatus)                 │
+│  IF cashOutEndTime != null                   │
+│    wrapper.le(createdTime, endTime 23:59:59) │
+│  IF cashOutStartTime != null                 │
+│    wrapper.ge(createdTime, startTime 00:00:00)│
+│  wrapper.orderByDesc(createdTime)            │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  3. 查询所有符合条件的记录                    │
+│  List<StoreCashOutRecord> recordList         │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  4. 手动分页                                  │
+│  IPage page = ListToPage.setPage(list, p, s)│
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  5. 构建返回VO                                │
+│  vo.cashOutRecordList = page.getRecords()    │
+│  vo.cashOutAllMoney = sum(money[status=1]) ÷ 100 │
+│  vo.cashOutNum = list.size()                 │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  6. 返回结果                                  │
+│  返回分页数据和统计信息                       │
+└─────────────────────────────────────────────┘
+```
+
+### 查询条件说明
+
+#### 1. 必填条件
+- **storeId**: 门店ID(必须)
+
+#### 2. 可选条件(AND关系)
+- **paymentStatus**: 提现状态筛选
+- **cashOutStartTime**: 开始时间(含当天 00:00:00)
+- **cashOutEndTime**: 结束时间(含当天 23:59:59)
+
+#### 3. 排序规则
+- 按创建时间倒序排序(最新的在前)
+
+---
+
+## 数据库查询
+
+### 查询SQL
+
+```sql
+-- 使用自定义Mapper方法 selectCashoutRecordList
+SELECT scor.*, 
+       IF(su.alipay_account IS NOT NULL, su.alipay_account, su.phone) AS settlement_account
+FROM store_cash_out_record scor
+LEFT JOIN store_user su ON su.store_id = scor.store_id AND su.delete_flag = 0
+WHERE scor.store_id = ?
+  AND scor.delete_flag = 0
+  AND (scor.payment_status = ? OR ? IS NULL)
+  AND (scor.created_time <= ? OR ? IS NULL)
+  AND (scor.created_time >= ? OR ? IS NULL)
+ORDER BY scor.created_time DESC
+```
+
+**SQL说明**:
+- 🔔 使用 LEFT JOIN 关联 `store_user` 表获取结算账户信息
+- 📌 `settlement_account` 字段逻辑:
+  - 优先返回 `alipay_account`(支付宝账号)
+  - 如果为空,返回 `phone`(手机号)
+- 🔄 表别名:`scor` = store_cash_out_record,`su` = store_user
+
+### 索引建议
+
+```sql
+-- store_cash_out_record 表索引
+ALTER TABLE store_cash_out_record 
+ADD INDEX idx_store_status_time (store_id, payment_status, created_time);
+
+-- store_user 表索引(用于LEFT JOIN)
+ALTER TABLE store_user
+ADD INDEX idx_store_id_delete (store_id, delete_flag);
+```
+
+---
+
+## 提现状态说明
+
+### payment_status 状态值
+
+| Status | 名称 | 描述 | 可查询 |
+|--------|------|------|--------|
+| 1 | 待审核 | 提现申请已提交,等待管理员审核 | ✅ |
+| 2 | 审核不通过 | 管理员拒绝提现申请 | ✅ |
+| 3 | 已通过 | 管理员审核通过,等待打款 | ✅ |
+| 4 | 已打款 | 支付宝转账成功 | ✅ |
+| 5 | 打款失败 | 支付宝转账失败 | ✅ |
+
+### 状态流转
+
+```
+1(待审核) ──审核通过──→ 3(已通过) ──打款成功──→ 4(已打款)
+    │                       │
+    │                       └──打款失败──→ 5(打款失败)
+    │
+    └──审核拒绝──→ 2(审核不通过)
+```
+
+---
+
+## 金额计算规则
+
+### 金额单位转换
+
+| 数据库存储(分) | 显示(元) | 转换公式 |
+|------------------|-----------|----------|
+| 50000 | 500.00 | money ÷ 100 |
+| 30000 | 300.00 | money ÷ 100 |
+| 80000 | 800.00 | money ÷ 100 |
+
+### cashOutAllMoney 计算
+
+```java
+// 仅统计状态为1-待审核的记录总金额
+int totalMoney = recordList.stream()
+    .filter(item -> "1".equals(item.getPaymentStatus().toString()))
+    .mapToInt(StoreCashOutRecord::getMoney)
+    .sum();
+
+// 转换为元(向下取整)
+BigDecimal cashOutAllMoney = new BigDecimal(totalMoney)
+    .divide(new BigDecimal(100), 2, RoundingMode.DOWN);
+```
+
+**说明**: 
+- 🔔 **重要变更(2025-11-20)**: 仅统计 `paymentStatus = 1`(待审核)状态的记录总金额
+- ⚠️ 统计的是所有符合条件且状态为待审核的记录,不是当前页
+- 使用 `RoundingMode.DOWN`(向下取整)
+
+---
+
+## 业务场景
+
+### 场景 1: 查询所有提现记录
+
+**请求**:
+```http
+GET /incomeManage/getCashOutRecordList?storeId=103&page=1&size=10
+```
+
+**响应**:
+```json
+{
+    "code": 200,
+    "data": {
+        "cashOutRecordList": [...],  // 第1页,10条记录
+        "cashOutAllMoney": 500.00,   // 所有待审核记录总金额
+        "cashOutNum": 25              // 所有记录总数
+    }
+}
+```
+
+**说明**: 
+- 不传任何筛选条件,返回该门店的所有提现记录
+- `cashOutAllMoney` 仅统计状态为"待审核"的记录总金额
+
+---
+
+### 场景 2: 查询指定时间范围
+
+**请求**:
+```http
+GET /incomeManage/getCashOutRecordList?storeId=103&cashOutStartTime=2025-11-10&cashOutEndTime=2025-11-11&page=1&size=10
+```
+
+**响应**:
+```json
+{
+    "code": 200,
+    "data": {
+        "cashOutRecordList": [...],
+        "cashOutAllMoney": 200.00,  // 2025-11-10至2025-11-11期间待审核记录的总金额
+        "cashOutNum": 5              // 期间的总记录数
+    }
+}
+```
+
+**说明**: 
+- 只返回指定时间范围内的提现记录
+- `cashOutAllMoney` 仅统计该时间范围内待审核状态的记录
+
+---
+
+### 场景 3: 查询待审核记录
+
+**请求**:
+```http
+GET /incomeManage/getCashOutRecordList?storeId=103&paymentStatus=1&page=1&size=10
+```
+
+**响应**:
+```json
+{
+    "code": 200,
+    "data": {
+        "cashOutRecordList": [...],
+        "cashOutAllMoney": 300.00,  // 待审核记录总金额
+        "cashOutNum": 3              // 待审核记录总数
+    }
+}
+```
+
+**说明**: 只返回待审核状态的提现记录,`cashOutAllMoney` 统计所有待审核记录的总金额
+
+---
+
+### 场景 4: 组合查询
+
+**请求**:
+```http
+GET /incomeManage/getCashOutRecordList?storeId=103&cashOutStartTime=2025-11-01&cashOutEndTime=2025-11-30&paymentStatus=4&page=1&size=10
+```
+
+**响应**:
+```json
+{
+    "code": 200,
+    "data": {
+        "cashOutRecordList": [...],
+        "cashOutAllMoney": 0.00,     // 11月已打款记录中待审核的总金额(0元,因为已打款状态不是待审核)
+        "cashOutNum": 10              // 11月已打款记录总数
+    }
+}
+```
+
+**说明**: 
+- 查询11月已打款的提现记录
+- ⚠️ **注意**: 由于 `cashOutAllMoney` 只统计待审核状态,而查询条件是已打款状态,所以 `cashOutAllMoney` 为0
+
+---
+
+### 场景 5: 空结果
+
+**前置条件**: 指定条件下无记录
+
+**请求**:
+```http
+GET /incomeManage/getCashOutRecordList?storeId=103&paymentStatus=5&page=1&size=10
+```
+
+**响应**:
+```json
+{
+    "code": 200,
+    "data": {
+        "cashOutRecordList": [],
+        "cashOutAllMoney": 0.00,
+        "cashOutNum": 0
+    }
+}
+```
+
+---
+
+## 分页说明
+
+### 分页逻辑
+
+1. **查询全部**: 首先查询所有符合条件的记录
+2. **手动分页**: 使用 `ListToPage.setPage()` 进行内存分页
+3. **返回当前页**: `cashOutRecordList` 只包含当前页的记录
+4. **统计全部**: `cashOutNum` 统计所有记录,`cashOutAllMoney` 仅统计待审核状态的记录
+
+### 分页参数
+
+- **page**: 页码,从1开始
+- **size**: 每页条数,默认10
+- **total**: 总记录数(返回值中的 `cashOutNum`)
+
+### 分页示例
+
+**场景**: 共25条记录,每页10条
+
+- **第1页** (page=1, size=10): 显示第1-10条
+- **第2页** (page=2, size=10): 显示第11-20条
+- **第3页** (page=3, size=10): 显示第21-25条
+
+**统计信息**: 
+- `cashOutNum = 25` (所有页的总记录数)
+- `cashOutAllMoney` = 所有25条记录中待审核状态的总金额
+
+---
+
+## 前端集成示例
+
+### Vue.js 示例
+
+```javascript
+export default {
+    data() {
+        return {
+            storeId: 103,
+            cashOutStartTime: '',
+            cashOutEndTime: '',
+            paymentStatus: '',
+            page: 1,
+            size: 10,
+            cashOutRecordList: [],
+            cashOutAllMoney: 0,
+            cashOutNum: 0
+        };
+    },
+    methods: {
+        async getCashOutRecordList() {
+            try {
+                const params = {
+                    storeId: this.storeId,
+                    page: this.page,
+                    size: this.size
+                };
+                
+                // 可选参数
+                if (this.cashOutStartTime) {
+                    params.cashOutStartTime = this.cashOutStartTime;
+                }
+                if (this.cashOutEndTime) {
+                    params.cashOutEndTime = this.cashOutEndTime;
+                }
+                if (this.paymentStatus) {
+                    params.paymentStatus = this.paymentStatus;
+                }
+                
+                const response = await this.$axios.get('/incomeManage/getCashOutRecordList', { params });
+                
+                if (response.data.success) {
+                    const data = response.data.data;
+                    this.cashOutRecordList = data.cashOutRecordList;
+                    this.cashOutAllMoney = data.cashOutAllMoney;
+                    this.cashOutNum = data.cashOutNum;
+                }
+            } catch (error) {
+                console.error('查询失败:', error);
+                this.$message.error('查询失败');
+            }
+        },
+        
+        // 翻页
+        handlePageChange(page) {
+            this.page = page;
+            this.getCashOutRecordList();
+        }
+    },
+    mounted() {
+        this.getCashOutRecordList();
+    }
+}
+```
+
+---
+
+### React 示例
+
+```javascript
+import { useState, useEffect } from 'react';
+import axios from 'axios';
+
+function CashOutRecordList() {
+    const [storeId] = useState(103);
+    const [cashOutStartTime, setCashOutStartTime] = useState('');
+    const [cashOutEndTime, setCashOutEndTime] = useState('');
+    const [paymentStatus, setPaymentStatus] = useState('');
+    const [page, setPage] = useState(1);
+    const [size] = useState(10);
+    const [data, setData] = useState({
+        cashOutRecordList: [],
+        cashOutAllMoney: 0,
+        cashOutNum: 0
+    });
+    
+    const getCashOutRecordList = async () => {
+        try {
+            const params = {
+                storeId,
+                page,
+                size,
+                ...(cashOutStartTime && { cashOutStartTime }),
+                ...(cashOutEndTime && { cashOutEndTime }),
+                ...(paymentStatus && { paymentStatus })
+            };
+            
+            const response = await axios.get('/incomeManage/getCashOutRecordList', { params });
+            
+            if (response.data.success) {
+                setData(response.data.data);
+            }
+        } catch (error) {
+            console.error('查询失败:', error);
+        }
+    };
+    
+    useEffect(() => {
+        getCashOutRecordList();
+    }, [page, cashOutStartTime, cashOutEndTime, paymentStatus]);
+    
+    return (
+        <div>
+            <h2>提现记录</h2>
+            <p>总记录数: {data.cashOutNum}</p>
+            <p>总金额: ¥{data.cashOutAllMoney}</p>
+            
+            <ul>
+                {data.cashOutRecordList.map(record => (
+                    <li key={record.id}>
+                        订单号: {record.orderNo} - 金额: ¥{(record.money / 100).toFixed(2)} - 状态: {record.paymentStatus}
+                    </li>
+                ))}
+            </ul>
+        </div>
+    );
+}
+```
+
+---
+
+## 测试用例
+
+### 测试场景1: 正常查询
+
+**请求**:
+```http
+GET /incomeManage/getCashOutRecordList?storeId=103&page=1&size=10
+```
+
+**预期结果**:
+- 返回提现记录列表
+- 包含统计信息(总金额、总数)
+- 按创建时间倒序排序
+
+---
+
+### 测试场景2: 时间范围查询
+
+**请求**:
+```http
+GET /incomeManage/getCashOutRecordList?storeId=103&cashOutStartTime=2025-11-10&cashOutEndTime=2025-11-11
+```
+
+**预期结果**:
+- 只返回2025-11-10至2025-11-11的记录
+- 包含这两天 00:00:00 到 23:59:59 的记录
+
+---
+
+### 测试场景3: 状态筛选
+
+**请求**:
+```http
+GET /incomeManage/getCashOutRecordList?storeId=103&paymentStatus=1
+```
+
+**预期结果**:
+- 只返回待审核状态的记录
+- 其他状态的记录不返回
+
+---
+
+### 测试场景4: 空结果
+
+**请求**:
+```http
+GET /incomeManage/getCashOutRecordList?storeId=999999&page=1&size=10
+```
+
+**预期结果**:
+```json
+{
+    "cashOutRecordList": [],
+    "cashOutAllMoney": 0.00,
+    "cashOutNum": 0
+}
+```
+
+---
+
+### 测试场景5: 分页测试
+
+**前置条件**: 共25条记录
+
+**请求**:
+```http
+GET /incomeManage/getCashOutRecordList?storeId=103&page=3&size=10
+```
+
+**预期结果**:
+- 返回第21-25条记录(5条)
+- `cashOutNum` 仍为25
+- `cashOutAllMoney` 为所有25条记录中待审核状态的总金额
+
+---
+
+## 注意事项
+
+### 1. 时间范围
+
+- ⚠️ **时间格式**: 必须是 `yyyy-MM-dd`
+- ⚠️ **开始时间**: 自动补充 `00:00:00`
+- ⚠️ **结束时间**: 自动补充 `23:59:59`
+- ⚠️ **时区**: 使用服务器时区
+
+### 2. 分页逻辑
+
+- ⚠️ **查询全部**: 先查询所有符合条件的记录
+- ⚠️ **内存分页**: 在内存中进行分页
+- ⚠️ **性能考虑**: 大数据量时可能影响性能
+- ⚠️ **统计准确**: `cashOutNum` 统计所有记录,`cashOutAllMoney` 仅统计待审核状态
+
+### 3. 金额单位
+
+- ⚠️ **数据库**: 分(Integer)
+- ⚠️ **接口返回**: 元(BigDecimal,保留2位小数)
+- ⚠️ **舍入模式**: DOWN(向下取整)
+
+### 4. 提现状态
+
+- ⚠️ **状态值**: 字符串类型
+- ⚠️ **可选参数**: 不传则查询所有状态
+- ⚠️ **多状态**: 目前不支持,需多次调用
+
+### 5. 查询性能
+
+- ⚠️ **索引**: 确保有合适的索引
+- ⚠️ **数据量**: 建议限制时间范围
+- ⚠️ **缓存**: 考虑使用Redis缓存热点数据
+
+---
+
+## 性能优化建议
+
+### 1. 数据库索引
+
+```sql
+-- 推荐的复合索引
+ALTER TABLE store_cash_out_record 
+ADD INDEX idx_store_status_time (store_id, payment_status, created_time);
+
+-- 好处:
+-- 1. 覆盖最常用的查询条件
+-- 2. 支持排序优化
+-- 3. 提升查询速度
+```
+
+### 2. 分页优化
+
+**现状**: 内存分页(查全部->分页)
+
+**优化方案**:
+```java
+// 使用MyBatis-Plus的分页插件
+IPage<StoreCashOutRecord> page = new Page<>(pageNum, pageSize);
+IPage<StoreCashOutRecord> result = storeCashOutRecordMapper.selectPage(page, wrapper);
+```
+
+**优点**:
+- 数据库层面分页
+- 只查询当前页数据
+- 性能更好
+
+### 3. 缓存策略
+
+```java
+// Redis缓存热点数据
+@Cacheable(value = "cashOutRecordList", 
+           key = "#storeId + ':' + #page + ':' + #size")
+public Object getCashOutRecordList(...) {
+    // ...
+}
+```
+
+---
+
+## 迁移说明
+
+### 原接口(app端)
+
+- **服务**: `alien-store`
+- **路径**: `/alienStore/storeCashOutRecord/getCashOutRecordList`
+- **Controller**: `StoreCashOutRecordController`
+- **Service**: `StoreCashOutRecordService.getCashOutRecordList()`
+
+### 新接口(web端)
+
+- **服务**: `alien-store-platform`
+- **路径**: `/incomeManage/getCashOutRecordList`
+- **Controller**: `IncomeManageController`
+- **Service**: `IncomeManageService.getCashOutRecordList()`
+
+### 差异说明
+
+| 项目 | app端 | web端 | 说明 |
+|------|-------|-------|------|
+| 接口路径 | `/getCashOutRecordList` | `/getCashOutRecordList` | 保持一致 |
+| 请求方式 | GET | GET | 保持一致 |
+| 参数名称 | 一致 | 一致 | 完全相同 |
+| 返回格式 | StoreCashOutRecordVo | StoreCashOutRecordVo | 保持一致 |
+| 业务逻辑 | ✅ | ✅ | 完全复用 |
+| 登录验证 | ❌ 无 | ✅ 有 | 增加验证 |
+| 日志记录 | 基础 | 详细 | 增强日志 |
+
+### 复用的核心组件
+
+1. **Mapper**: 
+   - `StoreCashOutRecordMapper.selectCashoutRecordList()` - 自定义查询方法,LEFT JOIN store_user
+2. **Entity**: 
+   - `StoreCashOutRecord` - 提现记录实体(包含 settlementAccount 字段)
+   - `StoreCashOutRecordVo` - 提现记录VO
+3. **Util**: 
+   - `ListToPage` - 手动分页工具
+
+### 自定义Mapper SQL
+
+```java
+@Select("select scor.*, If(su.alipay_account is not null,su.alipay_account,su.phone) settlementAccount\n" +
+        "from store_cash_out_record scor\n" +
+        "left join store_user su on su.store_id = scor.store_id and su.delete_flag = 0\n" +
+        "${ew.customSqlSegment}")
+List<StoreCashOutRecord> selectCashoutRecordList(@Param(Constants.WRAPPER) QueryWrapper<StoreCashOutRecord> wrapper);
+```
+
+**说明**:
+- 使用 `@Select` 注解定义SQL
+- LEFT JOIN `store_user` 获取结算账户
+- `${ew.customSqlSegment}` 动态拼接WHERE、ORDER BY等条件
+
+---
+
+## 常见问题
+
+### Q1: cashOutAllMoney 是当前页还是所有记录的总金额?
+
+**答案**: 所有符合条件且状态为"待审核"(paymentStatus=1)记录的总金额,不是当前页。
+
+**注意**: 从2025-11-20开始,只统计待审核状态的提现金额,与app端逻辑保持一致。
+
+---
+
+### Q2: 如何查询多个状态的记录?
+
+**答案**: 目前不支持,需要多次调用接口分别查询。
+
+---
+
+### Q3: 时间参数必须都传吗?
+
+**答案**: 不需要。可以只传开始时间、只传结束时间,或都不传。
+
+---
+
+### Q4: 分页是数据库分页还是内存分页?
+
+**答案**: 内存分页。先查询所有记录,再在内存中分页。
+
+---
+
+### Q5: 如何获取总页数?
+
+**答案**: `totalPages = Math.ceil(cashOutNum / size)`
+
+---
+
+### Q6: 金额为什么使用DOWN舍入模式?
+
+**答案**: 向下取整更保守,避免显示金额大于实际金额。
+
+---
+
+### Q7: settlementAccount 是什么?如何获取的?
+
+**答案**: 
+- **settlementAccount** 是结算账户,用于显示商户的提现账户信息
+- 获取逻辑:
+  1. 通过 LEFT JOIN `store_user` 表查询
+  2. 优先使用 `alipay_account`(支付宝账号)
+  3. 如果支付宝账号为空,使用 `phone`(手机号)
+- SQL: `IF(su.alipay_account IS NOT NULL, su.alipay_account, su.phone)`
+
+---
+
+### Q8: 为什么要用 QueryWrapper 而不是 LambdaQueryWrapper?
+
+**答案**: 
+- `selectCashoutRecordList` 是自定义Mapper方法,使用了表别名(`scor`)
+- 需要使用 `QueryWrapper` 配合字符串字段名(如 `"scor.store_id"`)
+- `LambdaQueryWrapper` 无法使用表别名
+
+---
+
+## 更新日志
+
+### 2025-11-20
+
+**逻辑更新(重要)**:
+- 🔄 修改查询方法,使用 `selectCashoutRecordList` 代替 `selectList`
+- ✅ 添加 LEFT JOIN `store_user` 表,获取结算账户信息(`settlementAccount`)
+- ✅ 修改 `QueryWrapper` 使用表别名 `scor`(以配合自定义SQL)
+- ✅ 添加 `delete_flag = 0` 条件(软删除过滤)
+- 🔄 修改 `cashOutAllMoney` 计算逻辑,仅统计 `paymentStatus = 1`(待审核)状态的记录
+
+**变更原因**:
+- 同步app端完整业务逻辑
+- 提现记录需要显示结算账户信息(支付宝账号或手机号)
+- 需要过滤已删除的记录
+
+**影响范围**:
+- 响应字段新增 `settlementAccount`(结算账户)
+- 查询会关联 `store_user` 表,可能影响性能(建议添加索引)
+- `cashOutAllMoney` 仅统计待审核状态的提现总金额
+
+**涉及文件**:
+- `IncomeManageServiceImpl.java` - 更新 `getCashOutRecordList` 方法(line 352-390)
+- `StoreCashOutRecordMapper.java` - 使用 `selectCashoutRecordList` 方法
+
+---
+
+### 2025-11-19
+
+**新增接口**:
+- ✅ `GET /incomeManage/getCashOutRecordList` - 提现记录查询
+
+**核心功能**:
+- ✅ 按门店ID查询提现记录
+- ✅ 支持时间范围筛选
+- ✅ 支持提现状态筛选
+- ✅ 分页查询
+- ✅ 统计总金额和总数
+- ✅ 按创建时间倒序排序
+- ✅ 详细日志记录
+- ✅ 完善异常处理
+
+**涉及文件**:
+- `IncomeManageController.java` - 更新
+- `IncomeManageService.java` - 更新
+- `IncomeManageServiceImpl.java` - 更新
+
+**代码质量**:
+- ✅ Linter检查:无错误
+- ✅ 日志记录:详细
+- ✅ 异常处理:完善
+- ✅ 代码注释:完整
+
+**与原接口一致性**:
+- ✅ 请求参数:完全相同
+- ✅ 返回格式:完全相同
+- ✅ 业务逻辑:完全复用
+
+**开发人员**: ssk
+
+---
+
+**文档版本**: v1.0  
+**最后更新**: 2025-11-19  
+**维护人员**: ssk
+
+
+

+ 374 - 0
alien-store-platform/接口文档/25-团购收益查询接口.md

@@ -0,0 +1,374 @@
+# 团购收益查询接口
+
+## 接口信息
+
+- **接口名称**: 团购收益查询
+- **接口路径**: `/incomeManage/getGroupIncome`
+- **请求方式**: `GET`
+- **接口描述**: 查询指定日期内的团购收益详情,包括收益金额、手续费、券数量、退款金额等统计信息,以及分页的收入明细记录列表
+
+---
+
+## 请求参数
+
+### Query参数
+
+| 参数名 | 类型 | 必填 | 描述 | 示例 |
+|--------|------|------|------|------|
+| date | String | 是 | 查询日期(格式:yyyy-MM-dd) | 2025-11-11 |
+| incomeType | Integer | 否 | 收入类型<br/>0: 主页(优惠券+团购券)<br/>1: 优惠券<br/>2: 代金券<br/>3: 套餐<br/>4: 联名卡<br/>不传则查询所有类型 | 0 |
+| page | Integer | 否 | 页码,默认1 | 1 |
+| size | Integer | 否 | 每页条数,默认10 | 10 |
+
+### Headers
+
+| 参数名 | 类型 | 必填 | 描述 |
+|--------|------|------|------|
+| token | String | 是 | 用户登录凭证(JWT Token) |
+
+### 说明
+
+- **门店ID**:通过`LoginUserUtil`从当前登录用户token中自动获取,无需前端传递
+- **收入类型特殊处理**:前端展示逻辑中,当`incomeType=0`时,会同时查询优惠券(code=1)和团购券(code=2)
+- **账期过滤**:仅查询未绑定提现记录的收入(`cashOutId`为空)
+
+---
+
+## 响应数据
+
+### 响应格式
+
+```json
+{
+  "code": 200,
+  "msg": "操作成功",
+  "data": {
+    "storeId": 103,
+    "storeName": "测试门店",
+    "date": "2025-11-11",
+    "commissionRate": 5,
+    "incomeMoney": "100.00",
+    "commissionStr": "5.00",
+    "noYetPaymentMoney": "95.00",
+    "couponCount": 10,
+    "refundMoney": 2.50,
+    "incomeDetailsRecordVoList": [
+      {
+        "id": 1001,
+        "storeId": 103,
+        "orderId": "ORDER123456",
+        "money": 1000,
+        "moneyStr": "10.00",
+        "commission": 50,
+        "incomeType": 1,
+        "refundType": "false",
+        "checkTime": "2025-11-11 10:30:00",
+        "createdTime": "2025-11-11 10:30:00"
+      },
+      {
+        "id": 1002,
+        "storeId": 103,
+        "orderId": "ORDER123457",
+        "money": 2000,
+        "moneyStr": "20.00",
+        "commission": 100,
+        "incomeType": 2,
+        "refundType": "false",
+        "checkTime": "2025-11-11 14:20:00",
+        "createdTime": "2025-11-11 14:20:00"
+      }
+    ]
+  }
+}
+```
+
+### 响应字段说明
+
+#### 主要字段
+
+| 字段名 | 类型 | 描述 |
+|--------|------|------|
+| storeId | Integer | 门店ID |
+| storeName | String | 门店名称 |
+| date | String | 查询日期 |
+| commissionRate | Integer | 佣金率(百分比) |
+| incomeMoney | String | 售价总额(收入+手续费,单位:元) |
+| commissionStr | String | 手续费总额(单位:元) |
+| noYetPaymentMoney | String | 实际收益金额(售价-手续费,单位:元) |
+| couponCount | Integer | 券数量(正常订单数 - 退款订单数) |
+| refundMoney | BigDecimal | 退款金额(单位:元) |
+| incomeDetailsRecordVoList | Array | 收入明细记录列表(分页后的数据) |
+
+#### incomeDetailsRecordVoList数组元素字段
+
+| 字段名 | 类型 | 描述 |
+|--------|------|------|
+| id | Long | 收入明细记录ID |
+| storeId | Integer | 门店ID |
+| orderId | String | 订单ID |
+| money | Integer | 收入金额(单位:分) |
+| moneyStr | String | 收入金额(单位:元,格式化后) |
+| commission | Integer | 手续费(单位:分) |
+| incomeType | Integer | 收入类型(1:优惠券, 2:代金券, 3:套餐, 4:联名卡) |
+| refundType | String | 是否退款("true":已退款, "false":未退款) |
+| checkTime | String | 核销时间 |
+| createdTime | String | 创建时间 |
+
+### 响应说明
+
+1. **金额单位转换**
+   - 数据库存储单位:分(Integer)
+   - 返回给前端单位:元(String,保留2位小数)
+
+2. **收益计算逻辑**
+   - **售价总额(incomeMoney)** = 收入(money) + 手续费(commission)
+   - **手续费总额(commissionStr)** = 所有记录的commission之和
+   - **实际收益(noYetPaymentMoney)** = 收入(money)之和(存入时已减去手续费)
+
+3. **退款处理**
+   - 根据`refundType`字段分组统计
+   - `refundType="false"`: 正常订单,计入明细列表
+   - `refundType="true"`: 退款订单,只统计退款金额
+
+4. **券数量计算**
+   - 券数量 = 正常订单数量 - 退款订单数量
+
+5. **分页说明**
+   - 明细列表(`incomeDetailsRecordVoList`)按照分页参数返回
+   - 统计数据(总金额、券数量、退款金额)基于所有符合条件的记录计算
+
+---
+
+## 业务逻辑
+
+### 查询流程
+
+```
+1. 从token中获取当前登录用户的storeId
+   ↓
+2. 构建查询条件
+   - 时间范围:指定日期的00:00:00 ~ 23:59:59
+   - 账期过滤:cashOutId为空(未绑定提现记录)
+   - 门店过滤:storeId匹配
+   - 收入类型:根据incomeType参数筛选
+   ↓
+3. 查询收入明细记录(基础统计)
+   - 计算总收入(allIncome)
+   - 计算总手续费(commission)
+   ↓
+4. 查询店铺信息
+   - 获取店铺名称、佣金率
+   ↓
+5. 查询明细记录列表(含退款标识)
+   - 通过自定义Mapper查询(getIncomeList)
+   - 包含退款标识字段
+   ↓
+6. 按退款类型分组
+   - refundType="false": 正常订单
+   - refundType="true": 退款订单
+   ↓
+7. 计算统计信息
+   - 售价总额 = 收入 + 手续费
+   - 券数量 = 正常订单数 - 退款订单数
+   - 退款金额 = 退款订单金额之和
+   ↓
+8. 手动分页(ListToPage.setPage)
+   - 对明细列表进行内存分页
+   ↓
+9. 返回结果
+```
+
+### 收入类型处理
+
+```java
+// incomeType参数说明
+if (incomeType == null) {
+    // 不传:查询所有类型
+    // 不添加incomeType筛选条件
+} else if (incomeType == 0) {
+    // 0: 主页(优惠券+团购券)
+    // 查询incomeType IN (1, 2)
+    wrapper.in(StoreIncomeDetailsRecord::getIncomeType, 
+        CouponTypeEnum.COUPON.getCode(),      // 1
+        CouponTypeEnum.GROUP_BUY.getCode());  // 2
+} else {
+    // 1/2/3/4: 具体类型
+    // 查询incomeType = 指定值
+    wrapper.eq(StoreIncomeDetailsRecord::getIncomeType, incomeType);
+}
+```
+
+### 账期过滤规则
+
+- 仅查询未绑定提现记录的收入(`cashOutId IS NULL`)
+- 这部分收入代表"可提现"但尚未提现的金额
+- 已提现的收入(`cashOutId`不为空)不在此接口查询范围
+
+---
+
+## 错误响应
+
+### 门店不存在
+
+```json
+{
+  "code": 500,
+  "msg": "查询失败:门店不存在",
+  "data": null
+}
+```
+
+### 未登录
+
+```json
+{
+  "code": 500,
+  "msg": "查询失败:用户未登录",
+  "data": null
+}
+```
+
+### 日期格式错误
+
+```json
+{
+  "code": 500,
+  "msg": "查询失败:日期格式错误",
+  "data": null
+}
+```
+
+---
+
+## 调用示例
+
+### 请求示例
+
+```bash
+# 查询指定日期的主页收益(优惠券+团购券)
+GET /incomeManage/getGroupIncome?date=2025-11-11&incomeType=0&page=1&size=10
+Headers:
+  token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
+
+# 查询指定日期的优惠券收益
+GET /incomeManage/getGroupIncome?date=2025-11-11&incomeType=1&page=1&size=10
+Headers:
+  token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
+
+# 查询指定日期的所有类型收益
+GET /incomeManage/getGroupIncome?date=2025-11-11&page=1&size=20
+Headers:
+  token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
+```
+
+---
+
+## 代码实现
+
+### Controller
+
+```java
+@ApiOperation("团购收益查询")
+@GetMapping("/getGroupIncome")
+public R<?> getGroupIncome(
+        @RequestParam("date") String date,
+        @RequestParam(value = "incomeType", required = false) Integer incomeType,
+        @RequestParam(value = "page", defaultValue = "1") Integer page,
+        @RequestParam(value = "size", defaultValue = "10") Integer size) {
+    log.info("IncomeManageController.getGroupIncome?date={}, incomeType={}, page={}, size={}",
+            date, incomeType, page, size);
+    try {
+        Integer storeId = LoginUserUtil.getCurrentStoreId();
+        Object result = incomeManageService.getGroupIncome(storeId, date, incomeType, page, size);
+        return R.data(result);
+    } catch (Exception e) {
+        log.error("IncomeManageController.getGroupIncome ERROR: {}", e.getMessage(), e);
+        return R.fail("查询失败:" + e.getMessage());
+    }
+}
+```
+
+### Service关键逻辑
+
+```java
+@Override
+public Object getGroupIncome(Integer storeId, String date, Integer incomeType, Integer page, Integer size) {
+    // 1. 构建查询条件
+    LambdaQueryWrapper<StoreIncomeDetailsRecord> wrapper = new LambdaQueryWrapper<>();
+    wrapper.between(StoreIncomeDetailsRecord::getCreatedTime, date + " 00:00:00", date + " 23:59:59")
+            .isNull(StoreIncomeDetailsRecord::getCashOutId)
+            .eq(StoreIncomeDetailsRecord::getStoreId, storeId);
+    
+    // 2. 收入类型筛选
+    if (null != incomeType && 0 == incomeType) {
+        wrapper.in(StoreIncomeDetailsRecord::getIncomeType,
+                CouponTypeEnum.COUPON.getCode(), CouponTypeEnum.GROUP_BUY.getCode());
+    } else if (null != incomeType) {
+        wrapper.eq(StoreIncomeDetailsRecord::getIncomeType, incomeType);
+    }
+    
+    // 3. 查询并统计
+    // ... 省略详细实现
+    
+    return vo;
+}
+```
+
+---
+
+## 注意事项
+
+1. **日期格式严格要求**
+   - 必须为`yyyy-MM-dd`格式
+   - 例如:`2025-11-11`
+   - 错误格式会导致SQL查询异常
+
+2. **收入类型说明**
+   - 前端展示时,`incomeType=0`表示"主页",实际查询优惠券+团购券
+   - 建议前端根据业务需要选择合适的收入类型参数
+
+3. **分页机制**
+   - 使用内存分页(`ListToPage.setPage`)
+   - 统计数据(总金额、券数量、退款金额)基于全量数据
+   - 明细列表基于分页参数返回
+
+4. **金额精度**
+   - 所有金额计算使用`BigDecimal`
+   - 四舍五入保留2位小数(`RoundingMode.HALF_UP`)
+
+5. **性能考虑**
+   - 单日数据量通常不大,内存分页可接受
+   - 如需跨多日查询,建议在数据库层面实现分页
+
+6. **账期规则**
+   - 收入创建后3天才能提现
+   - 此接口查询的是"可提现"金额
+   - 已提现金额不在查询范围
+
+---
+
+## 相关接口
+
+- [账期查询接口](./18-账期查询接口.md) - 查询可提现和不可提现的收入明细
+- [账户余额查询接口](./19-查询账户余额接口.md) - 查询账户总余额和可提现金额
+- [提现申请接口](./23-提现申请接口.md) - 发起提现申请
+- [提现记录查询接口](./24-提现记录查询接口.md) - 查询提现记录
+
+---
+
+## 更新日志
+
+| 版本 | 日期 | 说明 | 作者 |
+|------|------|------|------|
+| v1.0 | 2025-11-19 | 从app端迁移到web端,使用`LoginUserUtil`获取storeId | ssk |
+
+---
+
+**文档生成时间**: 2025-11-19  
+**接口版本**: v1.0  
+**维护人**: AI Assistant
+
+
+
+
+

+ 931 - 0
alien-store-platform/接口文档/28-快速提现申请接口.md

@@ -0,0 +1,931 @@
+# 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
+
+

+ 687 - 0
alien-store-platform/接口文档/35-提现记录详情查询接口.md

@@ -0,0 +1,687 @@
+# 35. 提现记录详情查询接口
+
+## 接口基本信息
+
+| 项目 | 内容 |
+|------|------|
+| 接口名称 | 提现记录详情查询 |
+| 接口路径 | `/incomeManage/getCashOutRecordDetail` |
+| 请求方式 | GET |
+| 接口说明 | 查询指定提现记录的详细信息 |
+| 是否需要登录 | 是 |
+| 开发者 | ssk |
+| 开发日期 | 2025-11-25 |
+
+---
+
+## 业务说明
+
+### 1. 功能描述
+
+该接口用于查询单条提现记录的详细信息,包括提现金额、手续费、提现状态、收益时间范围、到账时间、审批信息等完整数据。
+
+### 2. 使用场景
+
+- 商户查看提现申请的详细信息
+- 查看提现审批结果和备注
+- 查看提现失败原因
+- 查看提现到账时间和支付宝订单号
+
+### 3. 业务规则
+
+1. **权限控制**:只能查看自己店铺的提现记录
+2. **数据验证**:提现记录必须存在且未被删除
+3. **软删除过滤**:已删除的记录无法查询
+
+### 4. 与列表接口的关系
+
+- **列表接口** (`/incomeManage/getCashOutRecordList`): 分页查询多条提现记录,返回简要信息和统计数据
+- **详情接口** (`/incomeManage/getCashOutRecordDetail`): 查询单条记录的完整详细信息
+
+---
+
+## 请求参数
+
+### Query参数
+
+| 参数名 | 类型 | 必填 | 说明 | 示例 |
+|--------|------|------|------|------|
+| id | Integer | 是 | 提现记录ID | 1001 |
+
+### Headers
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| Authorization | String | 是 | JWT Token(格式:`Bearer {token}`) |
+
+### 说明
+
+- **门店ID**:通过 `LoginUserUtil` 从当前登录用户 Token 中自动获取,用于权限校验
+- **权限校验**:自动验证提现记录是否属于当前登录用户的店铺
+
+---
+
+## 响应参数
+
+### 响应格式
+
+```json
+{
+  "code": 200,
+  "success": true,
+  "message": "操作成功",
+  "data": {
+    "id": 1001,
+    "storeId": 103,
+    "money": 10000,
+    "commission": 500,
+    "cashOutType": 0,
+    "orderNo": "TX202511250001",
+    "aliOrderNo": "2025112522001234567890123456",
+    "incomeStartTime": "2025-10-28 00:00:00",
+    "incomeEndTime": "2025-11-24 23:59:59",
+    "paymentDate": "2025-11-25 15:30:00",
+    "paymentStatus": 4,
+    "alertStatus": 1,
+    "deleteFlag": 0,
+    "createdTime": "2025-11-25 10:00:00",
+    "createdUserId": 113,
+    "updatedTime": "2025-11-25 15:30:00",
+    "updatedUserId": 1,
+    "approveTime": "2025-11-25 11:00:00",
+    "payDate": "2025-11-25 15:30:00",
+    "failReason": null,
+    "storeUserId": 113,
+    "comments": "审核通过",
+    "settlementAccount": "alipay@example.com",
+    "approveFailReason": null
+  }
+}
+```
+
+### 响应字段说明
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| id | Integer | 提现记录ID |
+| storeId | Integer | 门店ID |
+| money | Integer | 提现金额(单位:分) |
+| commission | Integer | 手续费(单位:分) |
+| cashOutType | Integer | 提现类型(0-手动,1-自动) |
+| orderNo | String | 商户订单号 |
+| aliOrderNo | String | 支付宝订单号 |
+| incomeStartTime | Date | 收益开始时间 |
+| incomeEndTime | Date | 收益结束时间 |
+| paymentDate | Date | 到账时间 |
+| paymentStatus | Integer | 提现状态<br/>0-进行中<br/>1-待审核<br/>2-审核不通过<br/>3-已通过<br/>4-已打款<br/>5-打款失败 |
+| alertStatus | Integer | 已到账期弹窗(0-弹,1-不弹) |
+| deleteFlag | Integer | 删除标记(0-未删除,1-已删除) |
+| createdTime | Date | 创建时间(申请时间) |
+| createdUserId | Integer | 创建人ID(申请人) |
+| updatedTime | Date | 修改时间 |
+| updatedUserId | Integer | 修改人ID |
+| approveTime | Date | 审批时间 |
+| payDate | Date | 支付时间 |
+| failReason | String | 失败原因(打款失败时) |
+| storeUserId | Integer | 申请人ID(store_user表) |
+| comments | String | 审批备注 |
+| settlementAccount | String | 结算账户(支付宝账号) |
+| approveFailReason | String | 拒绝原因(审核不通过时) |
+
+---
+
+## 完整示例
+
+### 请求示例
+
+**cURL 示例**
+
+```bash
+curl -X GET 'http://localhost:8081/incomeManage/getCashOutRecordDetail?id=1001' \
+  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
+```
+
+**JavaScript (Axios) 示例**
+
+```javascript
+async function getCashOutRecordDetail(id) {
+    try {
+        const response = await axios.get(
+            `/incomeManage/getCashOutRecordDetail?id=${id}`,
+            {
+                headers: {
+                    'Authorization': 'Bearer ' + token
+                }
+            }
+        );
+        
+        console.log('提现记录详情:', response.data);
+        
+        if (response.data.success) {
+            const record = response.data.data;
+            console.log(`提现金额: ${record.money / 100}元`);
+            console.log(`提现状态: ${getStatusText(record.paymentStatus)}`);
+            console.log(`结算账户: ${record.settlementAccount}`);
+        }
+    } catch (error) {
+        console.error('查询失败:', error.message);
+    }
+}
+
+function getStatusText(status) {
+    const statusMap = {
+        0: '进行中',
+        1: '待审核',
+        2: '审核不通过',
+        3: '已通过',
+        4: '已打款',
+        5: '打款失败'
+    };
+    return statusMap[status] || '未知状态';
+}
+
+// 调用示例
+getCashOutRecordDetail(1001);
+```
+
+**Java (RestTemplate) 示例**
+
+```java
+RestTemplate restTemplate = new RestTemplate();
+String url = "http://localhost:8081/incomeManage/getCashOutRecordDetail?id=1001";
+
+// 设置请求头
+HttpHeaders headers = new HttpHeaders();
+headers.set("Authorization", "Bearer " + token);
+
+HttpEntity<Void> requestEntity = new HttpEntity<>(headers);
+
+// 发送请求
+ResponseEntity<R> response = restTemplate.exchange(
+    url,
+    HttpMethod.GET,
+    requestEntity,
+    new ParameterizedTypeReference<R<StoreCashOutRecord>>() {}
+);
+
+// 处理响应
+R<StoreCashOutRecord> result = response.getBody();
+if (result.getSuccess()) {
+    StoreCashOutRecord record = result.getData();
+    System.out.println("提现金额: " + record.getMoney() / 100.0 + "元");
+    System.out.println("提现状态: " + record.getPaymentStatus());
+    System.out.println("结算账户: " + record.getSettlementAccount());
+}
+```
+
+---
+
+### 响应示例
+
+#### 成功响应 - 已打款
+
+```json
+{
+  "code": 200,
+  "success": true,
+  "message": "操作成功",
+  "data": {
+    "id": 1001,
+    "storeId": 103,
+    "money": 10000,
+    "commission": 500,
+    "cashOutType": 0,
+    "orderNo": "TX202511250001",
+    "aliOrderNo": "2025112522001234567890123456",
+    "incomeStartTime": "2025-10-28 00:00:00",
+    "incomeEndTime": "2025-11-24 23:59:59",
+    "paymentDate": "2025-11-25 15:30:00",
+    "paymentStatus": 4,
+    "alertStatus": 1,
+    "deleteFlag": 0,
+    "createdTime": "2025-11-25 10:00:00",
+    "createdUserId": 113,
+    "updatedTime": "2025-11-25 15:30:00",
+    "updatedUserId": 1,
+    "approveTime": "2025-11-25 11:00:00",
+    "payDate": "2025-11-25 15:30:00",
+    "failReason": null,
+    "storeUserId": 113,
+    "comments": "审核通过",
+    "settlementAccount": "alipay@example.com",
+    "approveFailReason": null
+  }
+}
+```
+
+#### 成功响应 - 审核不通过
+
+```json
+{
+  "code": 200,
+  "success": true,
+  "message": "操作成功",
+  "data": {
+    "id": 1002,
+    "storeId": 103,
+    "money": 5000,
+    "commission": 250,
+    "cashOutType": 0,
+    "orderNo": "TX202511250002",
+    "aliOrderNo": null,
+    "incomeStartTime": "2025-10-28 00:00:00",
+    "incomeEndTime": "2025-11-24 23:59:59",
+    "paymentDate": null,
+    "paymentStatus": 2,
+    "alertStatus": 0,
+    "deleteFlag": 0,
+    "createdTime": "2025-11-25 12:00:00",
+    "createdUserId": 113,
+    "updatedTime": "2025-11-25 13:00:00",
+    "updatedUserId": 1,
+    "approveTime": "2025-11-25 13:00:00",
+    "payDate": null,
+    "failReason": null,
+    "storeUserId": 113,
+    "comments": null,
+    "settlementAccount": "alipay@example.com",
+    "approveFailReason": "账户信息有误,请核实后重新申请"
+  }
+}
+```
+
+#### 失败响应 - 记录不存在
+
+```json
+{
+  "code": 500,
+  "success": false,
+  "message": "查询失败:提现记录不存在",
+  "data": null
+}
+```
+
+#### 失败响应 - 无权查看
+
+```json
+{
+  "code": 500,
+  "success": false,
+  "message": "查询失败:无权查看该提现记录",
+  "data": null
+}
+```
+
+#### 失败响应 - 参数错误
+
+```json
+{
+  "code": 500,
+  "success": false,
+  "message": "查询失败:提现记录ID不能为空",
+  "data": null
+}
+```
+
+---
+
+## 业务流程
+
+```
+┌─────────────┐
+│   前端发起   │
+│   详情查询   │
+└──────┬──────┘
+       │
+       ▼
+┌─────────────────┐
+│  提取 Token     │
+│  获取 storeId   │
+└──────┬──────────┘
+       │
+       ▼
+┌─────────────────┐
+│  参数验证        │
+│  - id 非空      │
+└──────┬──────────┘
+       │
+       ▼
+┌─────────────────┐
+│  查询提现记录    │
+│  根据 id 查询   │
+└──────┬──────────┘
+       │
+       ▼
+┌─────────────────┐
+│  记录存在性检查  │
+│  - 记录不存在   │
+│  - 已被删除     │
+└──────┬──────────┘
+       │
+       ▼
+┌─────────────────┐
+│  权限校验        │
+│  对比 storeId   │
+└──────┬──────────┘
+       │
+       ▼
+┌─────────────────┐
+│  返回记录详情    │
+└─────────────────┘
+```
+
+---
+
+## 核心代码实现
+
+### 1. Controller 层
+
+**文件路径**: `alien-store-platform/src/main/java/shop/alien/storeplatform/controller/IncomeManageController.java`
+
+```java
+@ApiOperation("提现记录详情查询")
+@ApiOperationSupport(order = 5)
+@ApiImplicitParams({
+        @ApiImplicitParam(name = "id", value = "提现记录ID", dataType = "Integer", paramType = "query", required = true)
+})
+@GetMapping("/getCashOutRecordDetail")
+public R<StoreCashOutRecord> getCashOutRecordDetail(@RequestParam("id") Integer id) {
+    log.info("IncomeManageController.getCashOutRecordDetail?id={}", id);
+    try {
+        StoreCashOutRecord result = incomeManageService.getCashOutRecordDetail(id);
+        return R.data(result);
+    } catch (Exception e) {
+        log.error("IncomeManageController.getCashOutRecordDetail ERROR: {}", e.getMessage(), e);
+        return R.fail("查询失败:" + e.getMessage());
+    }
+}
+```
+
+---
+
+### 2. Service 接口层
+
+**文件路径**: `alien-store-platform/src/main/java/shop/alien/storeplatform/service/IncomeManageService.java`
+
+```java
+/**
+ * 提现记录详情查询
+ *
+ * @param id 提现记录ID
+ * @return 提现记录详情
+ */
+StoreCashOutRecord getCashOutRecordDetail(Integer id);
+```
+
+---
+
+### 3. Service 实现层
+
+**文件路径**: `alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/IncomeManageServiceImpl.java`
+
+```java
+/**
+ * 提现记录详情查询
+ *
+ * @param id 提现记录ID
+ * @return 提现记录详情
+ */
+@Override
+public StoreCashOutRecord getCashOutRecordDetail(Integer id) {
+    log.info("IncomeManageServiceImpl.getCashOutRecordDetail - 开始查询提现记录详情: id={}", id);
+
+    // ==================== 1. 参数验证 ====================
+    if (id == null) {
+        log.warn("IncomeManageServiceImpl.getCashOutRecordDetail - 提现记录ID为空");
+        throw new RuntimeException("提现记录ID不能为空");
+    }
+
+    // ==================== 2. 查询提现记录 ====================
+    StoreCashOutRecord record = storeCashOutRecordMapper.selectById(id);
+    if (record == null || record.getDeleteFlag() == 1) {
+        log.warn("IncomeManageServiceImpl.getCashOutRecordDetail - 提现记录不存在: id={}", id);
+        throw new RuntimeException("提现记录不存在");
+    }
+
+    // ==================== 3. 权限校验(只能查看自己店铺的提现记录) ====================
+    Integer storeId = LoginUserUtil.getCurrentStoreId();
+    if (!record.getStoreId().equals(storeId)) {
+        log.warn("IncomeManageServiceImpl.getCashOutRecordDetail - 无权查看他人提现记录: currentStoreId={}, recordStoreId={}",
+                storeId, record.getStoreId());
+        throw new RuntimeException("无权查看该提现记录");
+    }
+
+    log.info("IncomeManageServiceImpl.getCashOutRecordDetail - 查询完成: id={}, storeId={}, money={}, status={}",
+            id, record.getStoreId(), record.getMoney(), record.getPaymentStatus());
+
+    return record;
+}
+```
+
+---
+
+## 技术要点
+
+### 1. 权限控制
+
+```java
+// 从 Token 中获取当前登录用户的店铺ID
+Integer storeId = LoginUserUtil.getCurrentStoreId();
+
+// 对比提现记录的店铺ID
+if (!record.getStoreId().equals(storeId)) {
+    throw new RuntimeException("无权查看该提现记录");
+}
+```
+
+**防止越权访问**:
+- 用户只能查看自己店铺的提现记录
+- storeId 从 Token 中获取,前端无法伪造
+
+---
+
+### 2. 数据验证
+
+```java
+// 验证记录存在性和软删除状态
+if (record == null || record.getDeleteFlag() == 1) {
+    throw new RuntimeException("提现记录不存在");
+}
+```
+
+---
+
+### 3. 提现状态说明
+
+| 状态值 | 状态名称 | 说明 |
+|-------|---------|------|
+| 0 | 进行中 | 提现正在处理中 |
+| 1 | 待审核 | 等待管理员审核 |
+| 2 | 审核不通过 | 审核被拒绝,查看 `approveFailReason` |
+| 3 | 已通过 | 审核通过,等待打款 |
+| 4 | 已打款 | 提现成功,已打款到支付宝 |
+| 5 | 打款失败 | 打款失败,查看 `failReason` |
+
+---
+
+### 4. 金额单位转换
+
+数据库存储的金额单位是**分**,前端展示时需要转换为**元**:
+
+```javascript
+// 转换为元(保留两位小数)
+const moneyInYuan = record.money / 100;
+const commissionInYuan = record.commission / 100;
+const actualAmount = (record.money - record.commission) / 100;
+
+console.log(`提现金额: ${moneyInYuan}元`);
+console.log(`手续费: ${commissionInYuan}元`);
+console.log(`实际到账: ${actualAmount}元`);
+```
+
+---
+
+## 与列表接口的配合使用
+
+### 典型使用流程
+
+```
+┌──────────────────┐
+│  1. 查询列表      │
+│  getCashOutRecordList │
+│  (分页、筛选)     │
+└────────┬─────────┘
+         │
+         ▼
+┌──────────────────┐
+│  2. 选择记录      │
+│  用户点击某条记录  │
+└────────┬─────────┘
+         │
+         ▼
+┌──────────────────┐
+│  3. 查询详情      │
+│  getCashOutRecordDetail │
+│  (完整信息)       │
+└────────┬─────────┘
+         │
+         ▼
+┌──────────────────┐
+│  4. 展示详情页    │
+│  显示所有字段信息  │
+└──────────────────┘
+```
+
+### 前端联动示例
+
+```javascript
+// 步骤1: 查询列表
+async function loadCashOutList() {
+    const response = await axios.get('/incomeManage/getCashOutRecordList', {
+        params: {
+            page: 1,
+            size: 10
+        }
+    });
+    
+    // 渲染列表,每条记录有"查看详情"按钮
+    renderList(response.data.data.cashOutRecordList);
+}
+
+// 步骤2: 点击查看详情
+function viewDetail(recordId) {
+    getCashOutRecordDetail(recordId);
+}
+
+// 步骤3: 加载详情
+async function getCashOutRecordDetail(id) {
+    const response = await axios.get('/incomeManage/getCashOutRecordDetail', {
+        params: { id }
+    });
+    
+    // 展示详情弹窗或跳转详情页
+    showDetailModal(response.data.data);
+}
+```
+
+---
+
+## 测试建议
+
+### 1. 单元测试
+
+```java
+@Test
+public void testGetCashOutRecordDetail_Success() {
+    // 准备测试数据
+    Integer recordId = 1001;
+    
+    // 执行查询
+    StoreCashOutRecord result = incomeManageService.getCashOutRecordDetail(recordId);
+    
+    // 验证结果
+    assertNotNull(result);
+    assertEquals(recordId, result.getId());
+    assertEquals(103, result.getStoreId().intValue());
+}
+
+@Test
+public void testGetCashOutRecordDetail_NotFound() {
+    // 查询不存在的记录
+    assertThrows(RuntimeException.class, () -> {
+        incomeManageService.getCashOutRecordDetail(99999);
+    });
+}
+
+@Test
+public void testGetCashOutRecordDetail_NoPermission() {
+    // 模拟查询其他店铺的记录
+    // ... 验证抛出权限异常
+}
+```
+
+---
+
+### 2. 接口测试(Postman)
+
+**测试场景1: 正常查询**
+1. 先调用列表接口获取记录ID
+2. 使用该ID调用详情接口
+3. 验证返回完整数据
+
+**测试场景2: 记录不存在**
+1. 使用不存在的ID(如99999)
+2. 验证返回错误提示
+
+**测试场景3: 权限校验**
+1. 使用用户A的Token
+2. 查询用户B的提现记录ID
+3. 验证返回"无权查看"错误
+
+---
+
+## 常见问题(FAQ)
+
+### Q1: 详情接口和列表接口有什么区别?
+
+**A**: 
+- **列表接口**: 分页查询多条记录,返回简要信息 + 统计数据
+- **详情接口**: 查询单条记录的**所有字段**,包括审批备注、失败原因等
+
+### Q2: 为什么不在列表接口直接返回所有详情?
+
+**A**: 
+- 性能优化:列表只返回必要字段,减少数据传输
+- 按需加载:只有用户点击查看时才加载完整详情
+- 分离关注点:列表关注概览,详情关注完整信息
+
+### Q3: 能否查看其他店铺的提现记录?
+
+**A**: 不能。系统会自动验证提现记录的 `storeId` 是否与当前登录用户的店铺ID一致,防止越权访问。
+
+### Q4: 已删除的记录能否查询?
+
+**A**: 不能。软删除(`deleteFlag=1`)的记录会被过滤,无法通过详情接口查询。
+
+---
+
+## 相关文档
+
+- [24. 提现记录查询接口(列表)](./24-提现记录查询接口.md)
+- [23. 申请提现接口](./23-申请提现接口.md)
+- [28. 快速提现申请接口](./28-快速提现申请接口.md)
+
+---
+
+## 版本历史
+
+| 版本号 | 日期 | 修改内容 | 修改人 |
+|-------|------|---------|--------|
+| v1.0 | 2025-11-25 | 初始版本,新增提现记录详情查询接口 | ssk |
+
+---
+
+## 总结
+
+提现记录详情查询接口提供了单条提现记录的完整信息查询功能,配合列表接口使用,为商户提供了完整的提现记录管理体验。接口包含完善的权限控制和数据验证,确保数据安全性。
+