feat:礼物提现

This commit is contained in:
dzc committed 2026-01-04 17:20:34 +08:00
1 parent b6373210c8
commit 557b7d637e
24 files changed
+1234 -4

No files matched your search

+26 -1
View File
@@ -293,4 +293,29 @@ CREATE TABLE `pay_wallet_user_transition`
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 32 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '钱包(用户之间的相互转账、赠送)';
) ENGINE = InnoDB AUTO_INCREMENT = 32 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '钱包(用户之间的相互转账、赠送)';
-- ----------------------------
-- Table structure for pay_wallet_tx_auth
-- ----------------------------
DROP TABLE IF EXISTS `pay_wallet_tx_auth`;
CREATE TABLE `pay_wallet_tx_auth`
(
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '钱包提现',
`user_id` bigint NOT NULL COMMENT '用户id',
`wallet_id` bigint NOT NULL COMMENT '钱包id',
`biz_type` varchar(36) NOT NULL COMMENT '提现类型',
`biz_id` bigint DEFAULT NULL COMMENT '提现类型绑定id',
`tx_price` int NOT NULL COMMENT '提现金额',
`title` varchar(256) DEFAULT NULL COMMENT '提现title',
`check_status` varchar(36) NOT NULL COMMENT '提现状态',
`check_time` datetime DEFAULT NULL COMMENT '审核时间',
`tx_refuse` varchar(256) DEFAULT NULL COMMENT '提现拒绝信息',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 32 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '提现金额审核';
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.trade.api.brokerage;
import cn.iocoder.yudao.module.trade.api.brokerage.dto.BrokerageAddDTO;
import cn.iocoder.yudao.module.trade.api.brokerage.dto.BrokerageWechatTransferDTO;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
import javax.validation.Valid;
@@ -13,10 +14,18 @@ public interface TradeBrokerageRecordApi {
/**
* 增加佣金【多级分佣】
*
* @param userId 会员编号
* @param bizType 业务类型
* @param list 请求参数列表
*/
void addBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, @Valid List<BrokerageAddDTO> list);
/**
* 微信转账
* @param id 提现单id
* @param userId 会员id
* @param price 金额(分)
*/
BrokerageWechatTransferDTO wechatTransfer(Long id, Long userId, Integer price);
}
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.trade.api.brokerage.dto;
import lombok.Data;
@Data
public class BrokerageWechatTransferDTO {
/**
* 编号
*/
private Long id;
// ========== 其它字段 ==========
/**
* 渠道 package 信息
* <p>
* 特殊:目前只有微信转账有这个东西!!!
*
* @see <a href="https://pay.weixin.qq.com/doc/v3/merchant/4012716430">JSAPI 调起用户确认收款</a>
*/
private String channelPackageInfo;
}
@@ -1,25 +1,89 @@
package cn.iocoder.yudao.module.trade.api.brokerage;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.pay.api.refund.dto.PayTransferCreateRespDTO;
import cn.iocoder.yudao.module.pay.api.transfer.PayTransferApi;
import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO;
import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum;
import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import cn.iocoder.yudao.module.trade.api.brokerage.dto.BrokerageAddDTO;
import cn.iocoder.yudao.module.trade.api.brokerage.dto.BrokerageWechatTransferDTO;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.List;
import java.util.*;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
@Service
@Validated
public class TradeBrokerageRecordApiImpl implements TradeBrokerageRecordApi {
@Resource
private TradeOrderProperties tradeOrderProperties;
@Resource
private BrokerageRecordService brokerageRecordService;
@Resource
private SocialUserApi socialUserApi;
@Resource
private PayTransferApi payTransferApi;
@Override
public void addBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, List<BrokerageAddDTO> list) {
brokerageRecordService.addBrokerage(userId, bizType, BeanUtils.toBean(list, BrokerageAddReqBO.class));
}
/**
* 微信转账
*
* @param id 提现单id
* @param userId 会员id
* @param price 金额(分)
*/
@Override
public BrokerageWechatTransferDTO wechatTransfer(Long id, Long userId, Integer price) {
// 1.1 获取微信 openid
SocialUserRespDTO socialUser = socialUserApi.getSocialUserByUserId(UserTypeEnum.MEMBER.getValue(), userId, SocialTypeEnum.WECHAT_MINI_APP.getType());
if (ObjectUtil.isEmpty(socialUser)) return null;
// 1.2 构建请求
Map<String, String> channelExtras = new HashMap<>();
List<Map<String, String>> sceneReportInfos = new ArrayList<>();
sceneReportInfos.add(new LinkedHashMap<String, String>() {{
put("infoType", "岗位类型");
put("infoContent", "分销员");
}});
sceneReportInfos.add(new LinkedHashMap<String, String>() {{
put("infoType", "报酬说明");
put("infoContent", "邀请新用户有礼,提现" + MoneyUtils.fenToYuanStr(price) + "元");
}});
channelExtras.put("sceneId", "1005");
channelExtras.put("sceneReportInfos", JSONUtil.toJsonStr(sceneReportInfos));
PayTransferCreateReqDTO payTransferCreateReqDTO = new PayTransferCreateReqDTO()
.setAppKey(tradeOrderProperties.getPayAppKey())
.setChannelCode("wx_lite")
.setType(PayTransferTypeEnum.WX_BALANCE.getType())
.setMerchantTransferId(String.valueOf(id))
.setPrice(price)
.setSubject("佣金报酬")
.setOpenid(socialUser.getOpenid())
.setUserIp(getClientIP())
.setChannelExtras(channelExtras);
// 2. 发起请求
PayTransferCreateRespDTO transfer = payTransferApi.createTransfer(payTransferCreateReqDTO);
return BeanUtils.toBean(transfer, BrokerageWechatTransferDTO.class);
}
}
@@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.member.api.config.dto;
import lombok.Data;
import java.util.List;
/**
* 用户信息 Response DTO
*
@@ -14,19 +16,64 @@ public class MemberConfigRespDTO {
* 积分抵扣开关
*/
private Boolean pointTradeDeductEnable;
/**
* 积分抵扣,单位:分
* <p>
* 1 积分抵扣多少分
*/
private Integer pointTradeDeductUnitPrice;
/**
* 积分抵扣最大值
*/
private Integer pointTradeDeductMaxPrice;
/**
* 1 元赠送多少分
*/
private Integer pointTradeGivePoint;
/**
* 普通用户每天可以和几个人聊天
*/
private Integer defaultChatUserCout;
/**
* 普通用户每天可以和人发几次消息
*/
private Integer defaultChatUserMsgCout;
/**
* vip用户每天可以和几个人聊天
*/
private Integer vipChatUserCout;
/**
* vip用户每天可以和人发几次消息
*/
private Integer vipChatUserMsgCout;
/**
* vip查看微信次数
*/
private Integer vipLookWechtCout;
/**
* 礼物提现比例
* `giftCashRatio`可以兑换1人民币
*/
private Integer giftCashRatio;
/**
* 新人入驻天数判断
* 入驻未满`newPersonDays`都是新人用户
*/
private Integer newPersonDays;
/**
* 举报类型
*/
private List<String> reportTypeList;
}
@@ -17,6 +17,7 @@ import cn.iocoder.yudao.module.member.service.opentype.BO.OpenTypeExtBO;
import cn.iocoder.yudao.module.member.service.opentype.OpenTypeService;
import cn.iocoder.yudao.module.member.service.user.BO.MemberTagExtBO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.core.parameters.P;
import org.springframework.stereotype.Service;
@@ -39,6 +40,7 @@ public class MemberRecentTagServiceImp implements MemberRecentTagService {
private MemberRecentTagMapper memberRecentTagMapper;
@Resource
@Lazy
private OpenTypeService openTypeService;
@Resource
private MemberConfigService memberConfigService;
@@ -9,6 +9,8 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
*/
public interface ErrorCodeConstants {
ErrorCode EMPTY = new ErrorCode(1_007_999_999, "{}");
// ========== APP 模块 1-007-000-000 ==========
ErrorCode APP_NOT_FOUND = new ErrorCode(1_007_000_000, "App 不存在");
ErrorCode APP_IS_DISABLE = new ErrorCode(1_007_000_002, "App 已经被禁用");
@@ -79,6 +81,7 @@ public interface ErrorCodeConstants {
// ========== 钱包用户转账模块 1-007-010-000 ==========
ErrorCode WALLET_USER_TRANSFER_NOT_FOUND = new ErrorCode(1_007_010_000, "用户转账单不存在");
ErrorCode WALLET_USER_TRANSFER_NOT_SELF = new ErrorCode(1_007_010_001, "自己不能给自己送出礼物");
ErrorCode WALLET_TX_AUTH_NOT_EXISTS = new ErrorCode(1_007_010_002, "用户申请提现记录不存在");
// ========== 示例订单 1-007-900-000 ==========
ErrorCode DEMO_ORDER_NOT_FOUND = new ErrorCode(1_007_900_000, "示例订单不存在");
@@ -23,6 +23,7 @@ public enum PayWalletBizTypeEnum implements ArrayValuable<Integer> {
BROKERAGE_WITHDRAW(6, "分佣提现"),
GIFT(7, "送礼物"),
RECEIVE_GIFT(8, "收到礼物"),
RECEIVE_GIFT_TX(9, "礼物提现"),
;
/**
@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.pay.enums.wallet;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 钱包提现业务类型
*/
@AllArgsConstructor
@Getter
public enum PayWalletTxAuthBizTypeEnum implements ArrayValuable<String> {
GIFT_TX_AUTH("1", "礼物提现审核"),
;
/**
* 业务分类
*/
private final String type;
/**
* 说明
*/
private final String description;
public static final String[] ARRAYS = Arrays.stream(values()).map(PayWalletTxAuthBizTypeEnum::getType).toArray(String[]::new);
@Override
public String[] array() {
return ARRAYS;
}
public static PayWalletTxAuthBizTypeEnum valueByTypeOf(String type) {
return Arrays.stream(values()).filter(item -> item.getType().equals(type)).findFirst().orElse(null);
}
}
@@ -0,0 +1,42 @@
package cn.iocoder.yudao.module.pay.enums.wallet;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 钱包提现审核状态
*/
@AllArgsConstructor
@Getter
public enum PayWalletTxAuthStatusEnum implements ArrayValuable<String> {
TX_AUTH_AWAIT("1", "待审核"),
AUTH_SUCCESS("2", "审核通过"),
AUTH_FAIL("3", "审核不通过"),
AUTH_TX_SUCCESS("4", "已提现"),
AUTH_TX_ERROR("5", "提现失败"),
;
/**
* 业务分类
*/
private final String type;
/**
* 说明
*/
private final String description;
public static final String[] ARRAYS = Arrays.stream(values()).map(PayWalletTxAuthStatusEnum::getType).toArray(String[]::new);
@Override
public String[] array() {
return ARRAYS;
}
public static PayWalletTxAuthStatusEnum valueOfByType(String type) {
return Arrays.stream(values()).filter(item -> item.getType().equals(type)).findFirst().orElse(null);
}
}
@@ -0,0 +1,118 @@
package cn.iocoder.yudao.module.pay.controller.admin.wallet;
import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO;
import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthCheckReqVO;
import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthPageReqVO;
import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthRespVO;
import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthSaveReqVO;
import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.WalletTxAuthDO;
import cn.iocoder.yudao.module.pay.service.wallet.WalletTxAuthService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.annotation.security.PermitAll;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
@Slf4j
@Tag(name = "管理后台 - 提现金额审核")
@RestController
@RequestMapping("/pay/wallet-tx-auth")
@Validated
public class WalletTxAuthController {
@Resource
private WalletTxAuthService walletTxAuthService;
@PostMapping("/check")
@Operation(summary = "审核")
public CommonResult<Boolean> check(@Valid @RequestBody WalletTxAuthCheckReqVO dto) {
return success(walletTxAuthService.check(dto));
}
@PostMapping("/create")
@Operation(summary = "创建提现金额审核")
@PreAuthorize("@ss.hasPermission('pay:wallet-tx-auth:create')")
public CommonResult<Long> createWalletTxAuth(@Valid @RequestBody WalletTxAuthSaveReqVO createReqVO) {
return success(walletTxAuthService.createWalletTxAuth(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新提现金额审核")
@PreAuthorize("@ss.hasPermission('pay:wallet-tx-auth:update')")
public CommonResult<Boolean> updateWalletTxAuth(@Valid @RequestBody WalletTxAuthSaveReqVO updateReqVO) {
walletTxAuthService.updateWalletTxAuth(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除提现金额审核")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('pay:wallet-tx-auth:delete')")
public CommonResult<Boolean> deleteWalletTxAuth(@RequestParam("id") Long id) {
walletTxAuthService.deleteWalletTxAuth(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得提现金额审核")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('pay:wallet-tx-auth:query')")
public CommonResult<WalletTxAuthRespVO> getWalletTxAuth(@RequestParam("id") Long id) {
WalletTxAuthDO walletTxAuth = walletTxAuthService.getWalletTxAuth(id);
return success(BeanUtils.toBean(walletTxAuth, WalletTxAuthRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得提现金额审核分页")
@PreAuthorize("@ss.hasPermission('pay:wallet-tx-auth:query')")
public CommonResult<PageResult<WalletTxAuthRespVO>> getWalletTxAuthPage(@Valid WalletTxAuthPageReqVO pageReqVO) {
PageResult<WalletTxAuthRespVO> pageResult = walletTxAuthService.getWalletTxAuthPage(pageReqVO);
return success(pageResult);
}
@GetMapping("/export-excel")
@Operation(summary = "导出提现金额审核 Excel")
@PreAuthorize("@ss.hasPermission('pay:wallet-tx-auth:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportWalletTxAuthExcel(@Valid WalletTxAuthPageReqVO pageReqVO, HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<WalletTxAuthRespVO> list = walletTxAuthService.getWalletTxAuthPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "提现金额审核.xls", "数据", WalletTxAuthRespVO.class, list);
}
@PostMapping("/update-transfer")
@Operation(summary = "更新转账记录(回调)")
@PermitAll
public CommonResult<Boolean> updateTransfer(@RequestBody PayRefundNotifyReqDTO notifyReqDTO) {
log.info("[updateTransfer][notifyReqDTO({})]", notifyReqDTO);
walletTxAuthService.updateTransfer(notifyReqDTO);
return success(true);
}
}
@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.ToString;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 提现金额审核 Request VO")
@Data
@ToString(callSuper = true)
public class WalletTxAuthCheckReqVO {
@Schema(description = "提现id")
@NotNull(message = "提现id不能为空")
private Long id;
@Schema(description = "审核状态")
@NotEmpty(message = "审核状态不能为空")
private String checkStatus;
@Schema(description = "拒绝信息")
private String txRefuse;
}
@@ -0,0 +1,50 @@
package cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 提现金额审核分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class WalletTxAuthPageReqVO extends PageParam {
@Schema(description = "用户id", example = "22189")
private Long userId;
@Schema(description = "钱包id", example = "25463")
private Long walletId;
@Schema(description = "提现类型", example = "1")
private String bizType;
@Schema(description = "提现类型绑定id", example = "25829")
private Long bizId;
@Schema(description = "提现金额", example = "18586")
private Integer txPrice;
@Schema(description = "提现title")
private String title;
@Schema(description = "提现状态", example = "2")
private String checkStatus;
@Schema(description = "审核时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] checkTime;
@Schema(description = "提现拒绝信息")
private String txRefuse;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
@@ -0,0 +1,75 @@
package cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 提现金额审核 Response VO")
@Data
@ExcelIgnoreUnannotated
public class WalletTxAuthRespVO {
@Schema(description = "钱包提现", requiredMode = Schema.RequiredMode.REQUIRED, example = "30337")
@ExcelProperty("钱包提现")
private Long id;
@Schema(description = "用户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "22189")
@ExcelProperty("用户id")
private Long userId;
@Schema(description = "钱包id", requiredMode = Schema.RequiredMode.REQUIRED, example = "25463")
@ExcelProperty("钱包id")
private Long walletId;
@Schema(description = "提现类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty("提现类型")
private String bizType;
@Schema(description = "提现类型绑定id", example = "25829")
@ExcelProperty("提现类型绑定id")
private Long bizId;
@Schema(description = "提现金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "18586")
@ExcelProperty("提现金额")
private Integer txPrice;
@Schema(description = "提现title")
@ExcelProperty("提现title")
private String title;
@Schema(description = "提现状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty("提现状态")
private String checkStatus;
@Schema(description = "审核时间")
@ExcelProperty("审核时间")
private LocalDateTime checkTime;
@Schema(description = "提现拒绝信息")
@ExcelProperty("提现拒绝信息")
private String txRefuse;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
/**
* 用户相关
*/
@Schema(description = "用户昵称")
private String memberNickname;
@Schema(description = "用户头像")
private String memberAvatar;
@Schema(description = "用户手机")
private String memberMobile;
}
@@ -0,0 +1,49 @@
package cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 提现金额审核新增/修改 Request VO")
@Data
public class WalletTxAuthSaveReqVO {
@Schema(description = "钱包提现", requiredMode = Schema.RequiredMode.REQUIRED, example = "30337")
private Long id;
@Schema(description = "用户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "22189")
@NotNull(message = "用户id不能为空")
private Long userId;
@Schema(description = "钱包id", requiredMode = Schema.RequiredMode.REQUIRED, example = "25463")
@NotNull(message = "钱包id不能为空")
private Long walletId;
@Schema(description = "提现类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotEmpty(message = "提现类型不能为空")
private String bizType;
@Schema(description = "提现类型绑定id", example = "25829")
private Long bizId;
@Schema(description = "提现金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "18586")
@NotNull(message = "提现金额不能为空")
private Integer txPrice;
@Schema(description = "提现title")
private String title;
@Schema(description = "提现状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotEmpty(message = "提现状态不能为空")
private String checkStatus;
@Schema(description = "审核时间")
private LocalDateTime checkTime;
@Schema(description = "提现拒绝信息")
private String txRefuse;
}
@@ -0,0 +1,62 @@
package cn.iocoder.yudao.module.pay.controller.app.wallet;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent;
import cn.iocoder.yudao.framework.idempotent.core.keyresolver.impl.UserIdempotentKeyResolver;
import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthPageReqVO;
import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthRespVO;
import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.txauth.AppWalletTxAuthGiftApplyReqVo;
import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.WalletTxAuthDO;
import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletTxAuthBizTypeEnum;
import cn.iocoder.yudao.module.pay.service.wallet.WalletTxAuthService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.concurrent.TimeUnit;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserType;
@Tag(name = "用户 APP - 提现金额审核")
@RestController
@RequestMapping("/pay/wallet-tx-auth")
@Validated
public class AppWalletTxAuthController {
@Resource
private WalletTxAuthService walletTxAuthService;
@PostMapping("/gift/apply")
@Operation(summary = "礼物申请提现")
@Idempotent(timeout = 3, timeUnit = TimeUnit.SECONDS, keyResolver = UserIdempotentKeyResolver.class, message = "正在提现中,请稍后重试")
public CommonResult<Long> apply(@RequestBody AppWalletTxAuthGiftApplyReqVo dto) {
dto.setUserId(getLoginUserId());
dto.setUserType(getLoginUserType());
return success(walletTxAuthService.giftApply(dto));
}
@GetMapping("/detail")
@Operation(summary = "获取提现金额审核详情")
public CommonResult<WalletTxAuthDO> detail(@RequestParam("id") Long id) {
return success(walletTxAuthService.getWalletTxAuth(id));
}
@GetMapping("/gift/page")
@Operation(summary = "礼物提现记录")
public CommonResult<PageResult<WalletTxAuthRespVO>> getWalletTxAuthPage(@Valid WalletTxAuthPageReqVO pageReqVO) {
pageReqVO.setUserId(getLoginUserId());
pageReqVO.setBizType(PayWalletTxAuthBizTypeEnum.GIFT_TX_AUTH.getType());
PageResult<WalletTxAuthRespVO> pageResult = walletTxAuthService.getWalletTxAuthPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, WalletTxAuthRespVO.class));
}
}
@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.pay.controller.app.wallet.vo.txauth;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Schema(description = "用户 APP - 用户申请礼物提现")
@Data
public class AppWalletTxAuthGiftApplyReqVo {
@Schema(description = "用户id")
private Long userId;
@Schema(description = "用户类型")
private Integer userType;
@Schema(description = "礼物金额")
@NotNull(message = "礼物金额不能为空")
private Integer giftPrice;
@Schema(description = "微信号")
@NotEmpty(message = "微信号不能为空")
private String wechatNum;
}
@@ -0,0 +1,102 @@
package cn.iocoder.yudao.module.pay.dal.dataobject.wallet;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 提现金额审核 DO
*/
@TableName("pay_wallet_tx_auth")
@KeySequence("pay_wallet_tx_auth_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class WalletTxAuthDO extends BaseDO {
/**
* 钱包提现
*/
@TableId
private Long id;
/**
* 用户id
*/
private Long userId;
/**
* 钱包id
*/
private Long walletId;
/**
* 提现类型
*/
private String bizType;
/**
* 提现类型绑定id
*/
private Long bizId;
/**
* 提现缘都金额
*/
private Integer txYdPrice;
/**
* 提现金额
*/
private Integer txPrice;
/**
* 提现title
*/
private String title;
/**
* 提现状态
*/
private String checkStatus;
/**
* 审核时间
*/
private LocalDateTime checkTime;
/**
* 提现拒绝信息
*/
private String txRefuse;
/**
* 提现转账信息
*/
private Long payTransferId;
/**
* 渠道商户编号
*/
private String channelMchId;
/**
* 渠道 package 信息
*/
private String channelPackageInfo;
/**
* 到账时间
*/
private LocalDateTime dzTime;
}
@@ -140,6 +140,48 @@ public interface PayWalletMapper extends BaseMapperX<PayWalletDO> {
update(null, lambdaUpdateWrapper);
}
/**
* 当消费(提现)时候,更新钱包(礼物)
*/
default int updateWhenGiftConsumption(Long id, Integer price) {
LambdaUpdateWrapper<PayWalletDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<PayWalletDO>()
.setSql(" gift_balance = gift_balance - " + price
+ ", gift_total_expense = gift_total_expense + " + price)
.eq(PayWalletDO::getId, id)
.ge(PayWalletDO::getGiftBalance, price); // cas 逻辑
return update(null, lambdaUpdateWrapper);
}
/**
* 冻结钱包部分余额(礼物金额)
*
* @param id 钱包 id
* @param price 冻结金额
*/
default int freezeGiftPrice(Long id, Integer price) {
LambdaUpdateWrapper<PayWalletDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<PayWalletDO>()
.setSql(" gift_balance = gift_balance - " + price
+ ", gift_freeze_price = gift_freeze_price + " + price)
.eq(PayWalletDO::getId, id)
.ge(PayWalletDO::getGiftBalance, price); // cas 逻辑
return update(null, lambdaUpdateWrapper);
}
/**
* 解冻钱包余额(礼物金额)
*
* @param id 钱包 id
* @param price 解冻金额
*/
default int unfreezeGiftPrice(Long id, Integer price) {
LambdaUpdateWrapper<PayWalletDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<PayWalletDO>()
.setSql(" gift_balance = gift_balance + " + price
+ ", gift_freeze_price = gift_freeze_price - " + price)
.eq(PayWalletDO::getId, id)
.ge(PayWalletDO::getGiftFreezePrice, price); // cas 逻辑
return update(null, lambdaUpdateWrapper);
}
/* *************************************** end 礼物相关 end *************************************** */
@@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.pay.dal.mysql.wallet;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthPageReqVO;
import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.WalletTxAuthDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 提现金额审核 Mapper
*/
@Mapper
public interface WalletTxAuthMapper extends BaseMapperX<WalletTxAuthDO> {
default PageResult<WalletTxAuthDO> selectPage(WalletTxAuthPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<WalletTxAuthDO>()
.eqIfPresent(WalletTxAuthDO::getUserId, reqVO.getUserId())
.eqIfPresent(WalletTxAuthDO::getWalletId, reqVO.getWalletId())
.eqIfPresent(WalletTxAuthDO::getBizType, reqVO.getBizType())
.eqIfPresent(WalletTxAuthDO::getBizId, reqVO.getBizId())
.eqIfPresent(WalletTxAuthDO::getTxPrice, reqVO.getTxPrice())
.eqIfPresent(WalletTxAuthDO::getTitle, reqVO.getTitle())
.eqIfPresent(WalletTxAuthDO::getCheckStatus, reqVO.getCheckStatus())
.betweenIfPresent(WalletTxAuthDO::getCheckTime, reqVO.getCheckTime())
.eqIfPresent(WalletTxAuthDO::getTxRefuse, reqVO.getTxRefuse())
.betweenIfPresent(WalletTxAuthDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(WalletTxAuthDO::getId));
}
}
@@ -66,8 +66,13 @@ public interface PayWalletService {
* @param price 扣减金额
* @return 钱包流水
*/
PayWalletTransactionDO reduceWalletBalance(Long walletId, Long bizId,
PayWalletBizTypeEnum bizType, Integer price);
PayWalletTransactionDO reduceWalletBalance(Long walletId, Long bizId, PayWalletBizTypeEnum bizType, Integer price);
/**
* 减少礼物钱包(提现)
*/
PayWalletTransactionDO reduceWalletGiftBalance(Long walletId, String bizId, PayWalletBizTypeEnum bizType, Integer price);
/**
* 增加钱包余额
@@ -94,6 +99,14 @@ public interface PayWalletService {
*/
void freezePrice(Long id, Integer price);
/**
* 冻结钱包部分余额(送礼物)
*
* @param id 钱包编号
* @param price 冻结金额
*/
void freezeGiftPrice(Long id, Integer price);
/**
* 解冻钱包余额
*
@@ -102,4 +115,12 @@ public interface PayWalletService {
*/
void unfreezePrice(Long id, Integer price);
/**
* 解冻钱包余额(送礼物)
*
* @param id 钱包编号
* @param price 解冻金额
*/
void unfreezeGiftPrice(Long id, Integer price);
}
@@ -249,6 +249,43 @@ public class PayWalletServiceImpl implements PayWalletService {
});
}
/**
* 减少礼物钱包(提现)
*/
@Override
@Transactional(rollbackFor = Exception.class)
@SneakyThrows
public PayWalletTransactionDO reduceWalletGiftBalance(Long walletId, String bizId, PayWalletBizTypeEnum bizType, Integer price) {
// 1. 获取钱包
PayWalletDO payWallet = getWallet(walletId);
if (payWallet == null) {
log.error("[addWalletBalance][用户钱包({})不存在]", walletId);
throw exception(WALLET_NOT_FOUND);
}
// 2. 加锁,更新钱包余额(目的:避免钱包流水的并发更新时,余额变化不连贯)
return lockRedisDAO.lock(walletId, UPDATE_TIMEOUT_MILLIS, () -> {
// 3. 更新钱包金额
switch (bizType) {
case RECEIVE_GIFT: // 收到礼物
walletMapper.updateWhenGiftConsumption(payWallet.getId(), price);
break;
default: {
throw new UnsupportedOperationException("待实现:" + bizType);
}
}
// 4. 生成钱包流水
Integer afterBalance = payWallet.getBalance() - price;
WalletTransactionCreateReqBO transactionCreateReqBO = new WalletTransactionCreateReqBO()
.setWalletId(payWallet.getId())
.setPrice(-price)
.setBalance(afterBalance)
.setBizId(bizId)
.setBizType(bizType.getType())
.setTitle(bizType.getDescription());
return walletTransactionService.createWalletTransaction(transactionCreateReqBO);
});
}
@Override
public void freezePrice(Long id, Integer price) {
int updateCounts = walletMapper.freezePrice(id, price);
@@ -257,6 +294,14 @@ public class PayWalletServiceImpl implements PayWalletService {
}
}
@Override
public void freezeGiftPrice(Long id, Integer price) {
int updateCounts = walletMapper.freezeGiftPrice(id, price);
if (updateCounts == 0) {
throw exception(WALLET_BALANCE_NOT_ENOUGH);
}
}
@Override
public void unfreezePrice(Long id, Integer price) {
int updateCounts = walletMapper.unFreezePrice(id, price);
@@ -265,4 +310,12 @@ public class PayWalletServiceImpl implements PayWalletService {
}
}
@Override
public void unfreezeGiftPrice(Long id, Integer price) {
int updateCounts = walletMapper.unfreezeGiftPrice(id, price);
if (updateCounts == 0) {
throw exception(WALLET_FREEZE_PRICE_NOT_ENOUGH);
}
}
}
@@ -0,0 +1,59 @@
package cn.iocoder.yudao.module.pay.service.wallet;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO;
import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthCheckReqVO;
import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthPageReqVO;
import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthRespVO;
import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthSaveReqVO;
import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.txauth.AppWalletTxAuthGiftApplyReqVo;
import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.WalletTxAuthDO;
/**
* 提现金额审核 Service 接口
*/
public interface WalletTxAuthService {
/**
* 更新转账记录(回调)
*/
void updateTransfer(PayRefundNotifyReqDTO notifyReqDTO);
/**
* 礼物申请提现
*/
Long giftApply(@Valid AppWalletTxAuthGiftApplyReqVo dto);
/**
* 审核
*/
Boolean check(@Valid WalletTxAuthCheckReqVO dto);
/**
* 创建提现金额审核
*/
Long createWalletTxAuth(@Valid WalletTxAuthSaveReqVO createReqVO);
/**
* 更新提现金额审核
*/
void updateWalletTxAuth(@Valid WalletTxAuthSaveReqVO updateReqVO);
/**
* 删除提现金额审核
*/
void deleteWalletTxAuth(Long id);
/**
* 获得提现金额审核
*/
WalletTxAuthDO getWalletTxAuth(Long id);
/**
* 获得提现金额审核分页
*/
PageResult<WalletTxAuthRespVO> getWalletTxAuthPage(WalletTxAuthPageReqVO pageReqVO);
}
@@ -0,0 +1,260 @@
package cn.iocoder.yudao.module.pay.service.wallet;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.member.api.config.MemberConfigApi;
import cn.iocoder.yudao.module.member.api.config.dto.MemberConfigRespDTO;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO;
import cn.iocoder.yudao.module.pay.api.transfer.PayTransferApiImpl;
import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferRespDTO;
import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthCheckReqVO;
import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthPageReqVO;
import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthRespVO;
import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthSaveReqVO;
import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.txauth.AppWalletTxAuthGiftApplyReqVo;
import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.WalletTxAuthDO;
import cn.iocoder.yudao.module.pay.dal.mysql.wallet.WalletTxAuthMapper;
import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum;
import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum;
import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletTxAuthBizTypeEnum;
import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletTxAuthStatusEnum;
import cn.iocoder.yudao.module.trade.api.brokerage.TradeBrokerageRecordApi;
import cn.iocoder.yudao.module.trade.api.brokerage.dto.BrokerageWechatTransferDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
/**
* 提现金额审核 Service 实现类
*
* @author 聊天交友
*/
@Slf4j
@Service
@Validated
public class WalletTxAuthServiceImpl implements WalletTxAuthService {
@Resource
private WalletTxAuthMapper walletTxAuthMapper;
@Resource
private PayWalletService walletService;
@Resource
private PayTransferApiImpl payTransferApi;
@Resource
private MemberConfigApi memberConfigApi;
@Resource
private MemberUserApi memberUserApi;
@Resource
private TradeBrokerageRecordApi tradeBrokerageRecordApi;
/**
* 更新转账记录(回调)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void updateTransfer(PayRefundNotifyReqDTO notifyReqDTO) {
Long id = notifyReqDTO.getPayRefundId();
PayTransferRespDTO transfer = payTransferApi.getTransfer(id);
WalletTxAuthDO detail = walletTxAuthMapper.selectOne(new LambdaQueryWrapperX<WalletTxAuthDO>()
.eq(WalletTxAuthDO::getPayTransferId, id)
);
if (ObjectUtil.isEmpty(detail)) {
log.error("|更新转账记录(回调)|,提现记录不存在,不进行操作notifyReqDTO:{}", JSONUtil.toJsonStr(notifyReqDTO));
return;
}
if (PayTransferStatusEnum.isWaiting(transfer.getStatus())) {
log.info("|更新转账记录(回调)|,转账正在等待中,不进行操作notifyReqDTO:{}", JSONUtil.toJsonStr(notifyReqDTO));
return;
}
if (PayTransferStatusEnum.isInProgress(transfer.getStatus())) {
log.info("|更新转账记录(回调)|,转账正在进行中,不进行操作notifyReqDTO:{}", JSONUtil.toJsonStr(notifyReqDTO));
return;
}
if (PayTransferStatusEnum.isClosed(transfer.getStatus())) {
// 解冻
walletService.unfreezeGiftPrice(detail.getWalletId(), detail.getTxYdPrice());
WalletTxAuthDO updateObj = new WalletTxAuthDO();
updateObj.setId(detail.getId());
updateObj.setCheckStatus(PayWalletTxAuthStatusEnum.AUTH_TX_ERROR.getType());
walletTxAuthMapper.updateById(updateObj);
}
if (PayTransferStatusEnum.isClosed(transfer.getStatus())) {
// 解冻
walletService.unfreezeGiftPrice(detail.getWalletId(), detail.getTxYdPrice());
// 进行扣款
walletService.reduceWalletGiftBalance(detail.getWalletId(), String.valueOf(detail.getId()), PayWalletBizTypeEnum.RECEIVE_GIFT_TX, detail.getTxYdPrice());
WalletTxAuthDO updateObj = new WalletTxAuthDO();
updateObj.setId(detail.getId());
updateObj.setCheckStatus(PayWalletTxAuthStatusEnum.AUTH_TX_SUCCESS.getType());
updateObj.setDzTime(LocalDateTime.now());
walletTxAuthMapper.updateById(updateObj);
}
}
/**
* 礼物申请提现
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Long giftApply(AppWalletTxAuthGiftApplyReqVo dto) {
// 礼物配置
MemberConfigRespDTO config = memberConfigApi.getConfig();
if (ObjectUtil.isEmpty(config) || ObjectUtil.isEmpty(config.getGiftCashRatio())) {
throw exception(EMPTY, "请先配置礼物提现比例");
}
// 查询用户
MemberUserRespDTO user = memberUserApi.getUser(dto.getUserId());
if (ObjectUtil.isEmpty(user)) {
throw exception(EMPTY, "提现用户不存在");
}
// 查询用户钱包
PayWalletDO wallet = walletService.getOrCreateWallet(dto.getUserId(), dto.getUserType());
int afterGiftBalance = wallet.getGiftBalance() - dto.getGiftPrice();
if (afterGiftBalance < 0) {
throw exception(EMPTY, "提现金额大于礼物余额");
}
// 计算提现金额
int txPrice = BigDecimal.valueOf(dto.getGiftPrice()).divide(BigDecimal.valueOf(config.getGiftCashRatio()), 2, RoundingMode.HALF_UP).intValue();
// 添加记录
WalletTxAuthDO walletTxAuth = new WalletTxAuthDO();
walletTxAuth.setUserId(dto.getUserId());
walletTxAuth.setWalletId(wallet.getId());
walletTxAuth.setBizType(PayWalletTxAuthBizTypeEnum.GIFT_TX_AUTH.getType());
walletTxAuth.setBizId(null);
walletTxAuth.setTxPrice(txPrice);
walletTxAuth.setTxYdPrice(dto.getGiftPrice());
walletTxAuth.setTitle("用户" + user.getNickname() + "申请礼物提现:" + MoneyUtils.fenToYuanStr(txPrice) + "元");
walletTxAuth.setCheckStatus(PayWalletTxAuthStatusEnum.TX_AUTH_AWAIT.getType());
walletTxAuthMapper.insert(walletTxAuth);
return walletTxAuth.getWalletId();
}
/**
* 审核
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean check(WalletTxAuthCheckReqVO dto) {
WalletTxAuthDO detail = walletTxAuthMapper.selectById(dto.getId());
if (ObjectUtil.isEmpty(detail)) {
throw exception(WALLET_TX_AUTH_NOT_EXISTS);
}
if (ObjectUtil.notEqual(PayWalletTxAuthStatusEnum.TX_AUTH_AWAIT.getType(), detail.getCheckStatus())) {
throw exception(EMPTY, "已审核,无需重复审核");
}
WalletTxAuthDO updateObj = new WalletTxAuthDO();
updateObj.setId(dto.getId());
updateObj.setCheckStatus(dto.getCheckStatus());
updateObj.setTxRefuse(dto.getTxRefuse());
updateObj.setCheckTime(LocalDateTime.now());
// 审核通过
if (ObjectUtil.equals(PayWalletTxAuthStatusEnum.AUTH_SUCCESS.getType(), dto.getCheckStatus())) {
BrokerageWechatTransferDTO wechatTransfer = tradeBrokerageRecordApi.wechatTransfer(detail.getId(), detail.getUserId(), detail.getTxPrice());
if (ObjectUtil.isEmpty(wechatTransfer)) {
throw exception(EMPTY, "发起微信转账失败");
}
PayTransferRespDTO transfer = payTransferApi.getTransfer(wechatTransfer.getId());
if (ObjectUtil.isEmpty(transfer)) {
throw exception(EMPTY, "查询转账记录失败");
}
updateObj.setPayTransferId(wechatTransfer.getId());
updateObj.setChannelMchId(transfer.getChannelMchId());
updateObj.setChannelPackageInfo(wechatTransfer.getChannelPackageInfo());
// 应该冻结操作金额的数量
walletService.freezeGiftPrice(detail.getWalletId(), detail.getTxYdPrice());
}
walletTxAuthMapper.updateById(updateObj);
return true;
}
@Override
public Long createWalletTxAuth(WalletTxAuthSaveReqVO createReqVO) {
// 插入
WalletTxAuthDO walletTxAuth = BeanUtils.toBean(createReqVO, WalletTxAuthDO.class);
walletTxAuthMapper.insert(walletTxAuth);
// 返回
return walletTxAuth.getId();
}
@Override
public void updateWalletTxAuth(WalletTxAuthSaveReqVO updateReqVO) {
// 校验存在
validateWalletTxAuthExists(updateReqVO.getId());
// 更新
WalletTxAuthDO updateObj = BeanUtils.toBean(updateReqVO, WalletTxAuthDO.class);
walletTxAuthMapper.updateById(updateObj);
}
@Override
public void deleteWalletTxAuth(Long id) {
// 校验存在
validateWalletTxAuthExists(id);
// 删除
walletTxAuthMapper.deleteById(id);
}
private void validateWalletTxAuthExists(Long id) {
if (walletTxAuthMapper.selectById(id) == null) {
throw exception(WALLET_TX_AUTH_NOT_EXISTS);
}
}
@Override
public WalletTxAuthDO getWalletTxAuth(Long id) {
return walletTxAuthMapper.selectById(id);
}
@Override
public PageResult<WalletTxAuthRespVO> getWalletTxAuthPage(WalletTxAuthPageReqVO pageReqVO) {
PageResult<WalletTxAuthRespVO> pageResult = new PageResult<>();
PageResult<WalletTxAuthDO> pageList = walletTxAuthMapper.selectPage(pageReqVO);
List<WalletTxAuthRespVO> listResult = BeanUtils.toBean(pageList.getList(), WalletTxAuthRespVO.class);
disposePageList(listResult);
pageResult.setTotal(pageList.getTotal());
pageResult.setList(listResult);
return pageResult;
}
private void disposePageList(List<WalletTxAuthRespVO> list) {
if (ObjectUtil.isEmpty(list)) return;
List<Long> userIds = list.stream().map(WalletTxAuthRespVO::getUserId).distinct().collect(Collectors.toList());
if (ObjectUtil.isNotEmpty(userIds)) {
List<MemberUserRespDTO> userList = memberUserApi.getUserList(userIds);
Map<Long, MemberUserRespDTO> userMap = IterUtil.toMap(userList, MemberUserRespDTO::getId);
for (WalletTxAuthRespVO item : list) {
MemberUserRespDTO target = userMap.get(item.getUserId());
if (ObjectUtil.isEmpty(target)) continue;
item.setMemberAvatar(target.getAvatar());
item.setMemberNickname(target.getNickname());
item.setMemberMobile(target.getMobile());
}
}
}
}