本模块提供提现申请功能,商户可以通过本接口提交提现申请,系统将验证支付密码、账户余额等信息后创建提现记录。Web端采用人工审核模式,提现申请提交后需等待管理员审核。
POST /incomeManage/cashOut@LoginRequired 注解)使用 CashOutDTO 对象作为请求体(JSON格式):
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|---|---|---|---|---|
| payPassword | String | 是 | 支付密码(MD5加密) | 222222 |
| withdrawalMoney | Integer | 是 | 提现金额(单位:分) | 50000 |
注意: storeId 从登录用户的 Token 中自动获取,不需要在请求中传递。
POST /incomeManage/cashOut
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN
{
"payPassword": "222222",
"withdrawalMoney": 50000
}
curl -X POST "http://localhost:8080/incomeManage/cashOut" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"payPassword": "222222",
"withdrawalMoney": 50000
}'
// 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));
{
"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 | 提示信息 |
{
"code": 500,
"success": false,
"data": null,
"msg": "支付密码错误"
}
{
"code": 500,
"success": false,
"data": null,
"msg": "余额不足"
}
{
"code": 500,
"success": false,
"data": null,
"msg": "金额不能小于0.1元"
}
{
"code": 500,
"success": false,
"data": null,
"msg": "请先登录"
}
{
"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、金额、状态等 │
└─────────────────────────────────────────────┘
与App端差异:
审核流程:
查询SQL:
SELECT * FROM store_user
WHERE store_id = ?
AND pay_password = ?
说明:
更新SQL:
UPDATE store_user
SET money = money - ?
WHERE id = ?
说明:
插入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: 门店IDmoney: 提现金额(分)cash_out_type: 提现类型,0-全部提现payment_status: 支付状态,1-待审核delete_flag: 删除标识,0-未删除store_user_id: 用户IDpay_date: 申请时间| 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 |
// 提现金额转换(分 -> 元)
BigDecimal amountInYuan = new BigDecimal(withdrawalMoney)
.divide(new BigDecimal(100), 2, RoundingMode.DOWN);
转换规则:
BigDecimal 进行精确计算DOWN(向下取整)前置条件:
请求:
POST /incomeManage/cashOut
Content-Type: application/json
{
"payPassword": "222222",
"withdrawalMoney": 50000
}
响应:
{
"code": 200,
"success": true,
"data": {
"cashOutRecordId": 1001,
"withdrawalMoney": "500.00",
"status": "pending",
"message": "提现申请已提交,等待审核"
}
}
结果:
前置条件:
请求:
POST /incomeManage/cashOut
Content-Type: application/json
{
"payPassword": "wrongpassword",
"withdrawalMoney": 50000
}
响应:
{
"code": 500,
"success": false,
"data": null,
"msg": "支付密码错误"
}
结果:
前置条件:
请求:
POST /incomeManage/cashOut
Content-Type: application/json
{
"payPassword": "222222",
"withdrawalMoney": 50000
}
响应:
{
"code": 500,
"success": false,
"data": null,
"msg": "余额不足"
}
结果:
前置条件:
请求:
POST /incomeManage/cashOut
Content-Type: application/json
{
"payPassword": "222222",
"withdrawalMoney": 5
}
响应:
{
"code": 500,
"success": false,
"data": null,
"msg": "金额不能小于0.1元"
}
结果:
前置条件:
请求:
POST /incomeManage/cashOut
Content-Type: application/json
{
"payPassword": "222222",
"withdrawalMoney": 100000
}
响应:
{
"code": 200,
"success": true,
"data": {
"cashOutRecordId": 1002,
"withdrawalMoney": "1000.00",
"status": "pending",
"message": "提现申请已提交,等待审核"
}
}
结果:
接口使用 @Transactional 注解保证数据一致性:
@Transactional
@Override
public R<?> cashOut(Integer storeId, String payPassword, Integer withdrawalMoney) {
// 1. 查询用户
// 2. 验证余额
// 3. 创建提现记录
// 4. 扣减账户余额
}
如果任何一步操作失败,所有操作都会回滚:
使用数据库默认隔离级别(MySQL: REPEATABLE_READ)
确保以下字段有索引:
-- 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);
请求:
POST /incomeManage/cashOut
Content-Type: application/json
{
"payPassword": "222222",
"withdrawalMoney": 50000
}
前置条件:
预期结果:
请求:
POST /incomeManage/cashOut
Content-Type: application/json
{
"payPassword": "wrongpassword",
"withdrawalMoney": 50000
}
预期结果:
{
"code": 500,
"msg": "支付密码错误"
}
请求:
POST /incomeManage/cashOut
Content-Type: application/json
{
"payPassword": "222222",
"withdrawalMoney": 1000000
}
前置条件:
预期结果:
{
"code": 500,
"msg": "余额不足"
}
请求:
POST /incomeManage/cashOut
Content-Type: application/json
{
"payPassword": "222222",
"withdrawalMoney": 5
}
预期结果:
{
"code": 500,
"msg": "金额不能小于0.1元"
}
测试方法:
预期结果:
alien-store/alienStore/storeIncomeDetailsRecord/cashOutStoreIncomeDetailsRecordControllerStoreIncomeDetailsRecordService.cashOut()alien-store-platform/incomeManage/cashOutIncomeManageControllerIncomeManageService.cashOut()| 项目 | app端 | web端 | 说明 |
|---|---|---|---|
| 接口路径 | /cashOut |
/cashOut |
保持一致 |
| 请求方式 | GET | POST | POST更符合规范 |
| 请求参数 | @RequestParam | @RequestBody(DTO) | 使用DTO更规范 |
| storeId | 请求参数传递 | JWT Token获取 | 更安全 |
| 打款方式 | 实时调用支付宝API | 人工审核后打款 | Web端更安全 |
| 提现状态 | 1-待审核, 4-已打款 | 1-待审核 | Web端初始状态固定为1 |
| 登录验证 | 无 | ✅ 有(@LoginRequired) | 增加登录验证 |
| 事务管理 | ✅ | ✅ | 都使用@Transactional |
| 日志记录 | 基础 | 详细 | 增强日志记录 |
StoreUserMapperStoreCashOutRecordMapperStoreUserStoreCashOutRecordDateUtils答案: Web端采用人工审核模式,更安全可控。管理员可以审核每笔提现申请,避免风险。
答案: 可以。如果提现状态为"待审核",管理员可以拒绝提现申请,系统会退还余额。
答案: 需要联系管理员重置支付密码,或通过"忘记密码"功能重置。
答案: 使用分(Integer)可以避免浮点数精度问题,确保金额计算准确。
答案: 使用了事务保证数据一致性。高并发场景建议使用分布式锁进一步保证。
答案:
修改内容:
CashOutDTO)@RequestBody 接收 JSON 请求体@ApiImplicitParams 注解storeId 从 JWT Token 中自动获取涉及文件:
IncomeManageController.java - 更新CashOutDTO.java - 新建23-提现申请接口.md - 更新文档新增接口:
POST /incomeManage/cashOut - 提现申请核心功能:
涉及文件:
IncomeManageController.java - 更新IncomeManageService.java - 更新IncomeManageServiceImpl.java - 更新代码质量:
与原接口差异:
开发人员: ssk
文档版本: v1.0
最后更新: 2025-11-19
维护人员: ssk