[fix]修改提现逻辑

This commit is contained in:
wangqx 2025-09-26 12:58:41 +08:00
parent a379e4b52f
commit dfc5ae4a06
17 changed files with 147 additions and 48 deletions

View File

@ -25,11 +25,14 @@ import com.wzj.soopin.transaction.convert.WithdrawConvert;
import com.wzj.soopin.transaction.domain.bo.ChargeBO;
import com.wzj.soopin.transaction.domain.bo.WithdrawBO;
import com.wzj.soopin.transaction.domain.entity.InitiateBatchTransferResponseNew;
import com.wzj.soopin.transaction.domain.entity.WxAuthResponse;
import com.wzj.soopin.transaction.domain.po.Withdraw;
import com.wzj.soopin.transaction.enums.WithdrawType;
import com.wzj.soopin.transaction.service.IAccountBillService;
import com.wzj.soopin.transaction.service.IChargeService;
import com.wzj.soopin.transaction.service.IWithdrawService;
import com.wzj.soopin.transaction.service.impl.WxAuthService;
import com.wzj.soopin.transaction.service.impl.WxPayService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
@ -79,6 +82,8 @@ public class AppMemberController {
private final IWithdrawService withdrawService;
private final WithdrawConvert withdrawConvert;
private final WxAuthService wxAuthService;
@Operation(summary = "获取会员账户信息详细信息")
@GetMapping(value = "/info")
public R<MemberVO> getInfo(Long memberId) {
@ -158,7 +163,7 @@ public class AppMemberController {
throw new ServiceException("用户未登录");
}
Long memberId = loginUser.getUserId();
//
ValidatorUtils.validate(loginBody);
AuthResponse<AuthUser> response = SocialUtils.loginAuth(
loginBody.getSource(), loginBody.getSocialCode(),
@ -169,8 +174,9 @@ public class AppMemberController {
AuthUser authUserData = response.getData();
String unionId=authUserData.getToken().getUnionId();
Member member = Member.builder().id(memberId).openId(unionId).build();
// WxAuthResponse response1 = wxAuthService.getAccessTokenByCode(loginBody.getSocialCode());
// String OpenId=response1.getOpenid();
Member member = Member.builder().id(memberId).openId(authUserData.getUuid()).build();
service.updateById(member);
// 更新用户的微信信息
// 返回给前端

View File

@ -272,7 +272,7 @@ wechat:
private-key-path: "classpath:cert/apiclient_key.pem" # 商户私钥文件路径
transfer-notify-url: https://wuzhongjie.com.cn/prod-api/api/transfer/callback # 转账回调地址
app-id: wxebcdaea31881caab # 应用ID
secret: your_wechat_secret # 应用密钥
secret: 86fbcab880e4066ac5c75af6f4f003c2 # 应用密钥
mini-program:
# app-id: wx87a5db19138da60d
# secret: 856ca8bae38ccaecc1353c9abedf6b41

View File

@ -272,7 +272,7 @@ wechat:
private-key-path: "/java/cert/apiclient_key.pem" # 商户私钥文件路径
transfer-notify-url: https://wuzhongjie.com.cn/prod-api/api/transfer/callback # 转账回调地址
app-id: wxebcdaea31881caab # 应用ID
secret: your_wechat_secret # 应用密钥
secret: 71826d76bad096ec5407897c6ed1391f # 应用密钥
mini-program:
# app-id: wx87a5db19138da60d
# secret: 856ca8bae38ccaecc1353c9abedf6b41

View File

@ -29,8 +29,7 @@ public class MQMessage {
/**
* 消息类型
*/
private MQMessageType
messageType;
private String messageType;
/**
* 消息内容
*/

View File

@ -405,7 +405,7 @@ public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> impl
MQMessage mqMessage = new MQMessage();
mqMessage.setTag(MessageActionEnum.INTERACTION_COMMENT.name());
mqMessage.setMessageType(MQMessageType.IM);
mqMessage.setMessageType(MQMessageType.IM.name());
if (bo.getFatherCommentId() == 0L) {
// 评论视频通知视频作者
if ( !String.valueOf(loginUser.getUserId()).equals(bo.getVlogerId())) {

View File

@ -76,7 +76,7 @@ public class VlogPushServiceImpl implements IVlogPushService {
MQMessage message = MQMessage.builder()
.topic("MEMBER_VLOG_MSG")
.tag(HOT_VLOG_TAG)
.messageType(MQMessageType.VLOG)
.messageType(MQMessageType.VLOG.name())
.data(vlogId)
.source("vlog_service")
.sendTime(LocalDateTime.now())
@ -94,7 +94,7 @@ public class VlogPushServiceImpl implements IVlogPushService {
MQMessage message = MQMessage.builder()
.topic("MEMBER_VLOG_MSG")
.tag(tag)
.messageType(MQMessageType.VLOG)
.messageType(MQMessageType.VLOG.name())
.data(vlogId)
.source("vlog_service")
.sendTime(LocalDateTime.now())

View File

@ -151,7 +151,7 @@ public class VlogServiceImpl extends ServiceImpl<VlogMapper, Vlog> implements Vl
//发出mq消息异步处理上传
MQMessage message = MQMessage.builder()
.messageType(MQMessageType.VLOG)
.messageType(MQMessageType.VLOG.name())
.data(vlog.getId())
.source("app")
.topic("VLOG_UPLOAD_TOPIC")
@ -330,7 +330,7 @@ public class VlogServiceImpl extends ServiceImpl<VlogMapper, Vlog> implements Vl
params.put("vlogId",vlog.getId());
params.put("firstFrameImg",vlog.getFirstFrameImg());
MQMessage mqMessage = new MQMessage();
mqMessage.setMessageType(MQMessageType.IM);
mqMessage.setMessageType(MQMessageType.IM.name());
mqMessage.setTag(MessageActionEnum.INTERACTION_LIKE.name());
mqMessage.setToUserId(Long.valueOf(vlog.getMemberId()));
mqMessage.setData(params);

View File

@ -87,7 +87,7 @@ public class FansServiceImpl extends ServiceImpl<FansMapper, Fans> implements IF
params.put("nickname", follower.getNickname() == null ? "" : follower.getNickname());
params.put("faceUrl", vlogger.getAvatar() );
MQMessage message = MQMessage.builder()
.messageType(MQMessageType.IM)
.messageType(MQMessageType.IM.name())
.data(params)
.tag(MessageActionEnum.NEW_FOUCS.name())
.toUserId(vlogger.getId())

View File

@ -115,7 +115,7 @@ public class FeedbackServiceImpl extends ServiceImpl<FeedbackMapper, Feedback> i
params.put("faceUrl", loginUser.getAvatar() );
MQMessage mqMessage = new MQMessage();
mqMessage.setMessageType(MQMessageType.IM);
mqMessage.setMessageType(MQMessageType.IM.name());
mqMessage.setTag(MessageActionEnum.INTERACTION_LIKE.name());
mqMessage.setToUserId(aim.getMemberId());
mqMessage.setData(params);

View File

@ -250,7 +250,7 @@ public class AftersaleServiceImpl extends ServiceImpl<AftersaleMapper, Aftersale
params.put("object", o);
MQMessage mqMessage = new MQMessage();
mqMessage.setTag(actionEnum.name());
mqMessage.setMessageType(MQMessageType.IM);
mqMessage.setMessageType(MQMessageType.IM.name());
mqMessage.setToUserId(o.getMemberId());
mqMessage.setData(params);
return mqMessage;

View File

@ -523,7 +523,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
params.put("account", MessageActionEnum.ORDER_PAY.getAccount());
MQMessage mqMessage = new MQMessage();
mqMessage.setTag(MessageActionEnum.ORDER_PAY.name());
mqMessage.setMessageType(MQMessageType.IM);
mqMessage.setMessageType(MQMessageType.IM.name());
mqMessage.setToUserId(order.getMemberId());
mqMessage.setData(params);
MqUtil.sendIMMessage(mqMessage);

View File

@ -182,7 +182,7 @@ public class WxPayController {
})
public ResponseEntity<WxAuthResponse> getOpenId(@RequestParam String code) {
try {
WxAuthResponse response = wxAuthService.getOpenIdByCode(code);
WxAuthResponse response = wxAuthService.getAccessTokenByCode(code);
return new ResponseEntity<>(response, HttpStatus.OK);
} catch (Exception e) {
log.error("获取openid时发生异常: {}", e.getMessage());

View File

@ -13,4 +13,9 @@ public class WxAuthResponse {
private String unionid;
private String errcode;
private String errmsg;
private String access_token;
private String expires_in;
private String refresh_token;
private String scope;
}

View File

@ -62,7 +62,7 @@ public class ChargeServiceImpl extends ServiceImpl<ChargeMapper, Charge> impleme
Map<String, Object> params = new HashMap<>();
MQMessage mqMessage = new MQMessage();
mqMessage.setTag(MessageActionEnum.ORDER_RECHARGE.name());
mqMessage.setMessageType(MQMessageType.IM);
mqMessage.setMessageType(MQMessageType.IM.name());
// 评论视频通知视频作者
params.put("orderID", charge.getId());
params.put("amount", charge.getMoney());

View File

@ -547,7 +547,7 @@ public class EasypayServiceImpl implements IEasypayService {
}
MQMessage mqMessage = new MQMessage();
mqMessage.setTag(MessageActionEnum.ORDER_PAY.name());
mqMessage.setMessageType(MQMessageType.IM);
mqMessage.setMessageType(MQMessageType.IM.name());
mqMessage.setToUserId(order.getMemberId());
mqMessage.setData(params);
return mqMessage;

View File

@ -182,21 +182,9 @@ public class WithdrawServiceImpl extends ServiceImpl<WithdrawMapper, Withdraw> i
withdraw.setCode(SnowFlake.createStr("TX"));
save(withdraw);
BigDecimal newBalance = balance.subtract(withdraw.getMoney());
//锁定用户余额
memberAccountService.updateById(memberAccount.toBuilder().wallet(newBalance).build());
//生成账单
AccountBill memberAccountChangeRecord = AccountBill.builder()
.accountId(withdraw.getMemberId())
.moneyBalance(newBalance)
.beforeBalance(balance)
.afterBalance(newBalance)
.changeType(AccountBillChangeTypeEnum.OUT.getCode())
.changeDesc("提现锁定")
.source(AccountBillSourceEnum.WITHDRAW.getCode())
.build();
accountBillService.save(memberAccountChangeRecord);
accountBillService.reduceMoney(withdraw.getMoney(),withdraw.getMemberId(),AccountBillSourceEnum.WITHDRAW,"");
InitiateBatchTransferResponseNew response = null;
try{

View File

@ -2,17 +2,31 @@ package com.wzj.soopin.transaction.service.impl;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson2.JSONObject;
import com.wzj.soopin.transaction.domain.entity.WxAuthResponse;
import com.wzj.soopin.transaction.config.WechatMiniProgramConfig;
import com.wzj.soopin.transaction.domain.entity.WxAuthResponse;
import com.wzj.soopin.transaction.wechat.WechatPayConfig;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wzj.soopin.transaction.domain.entity.WxAuthResponse;
@Service
@Slf4j
@ -26,23 +40,110 @@ public class WxAuthService {
private RestTemplate restTemplate = new RestTemplate();
// Jackson 解析器Spring 默认注入替代 fastjson减少第三方依赖
@Autowired
private ObjectMapper objectMapper;
/**
* 网页-通过code获取openid
*
* @param code 授权码
* @return 包含openid的响应对象
* 通过 code 换取 OAuth2 access_token优化后
* @param code 授权回调返回的 code
* @return WxAuthResponse 解析后的响应
*/
public WxAuthResponse getOpenIdByCode(String code) {
public WxAuthResponse getAccessTokenByCode(String code) {
// 1. 核心参数校验解决 41002 错误的根本
validateParams(code);
// 2. 拼接 URL脱敏日志避免泄露 secret
String appId = wechatPayConfig.getAppId();
String secret = wechatPayConfig.getSecret();
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
url = String.format(url, wechatPayConfig.getAppId(), wechatPayConfig.getSecret(), code);
WxAuthResponse response = restTemplate.getForObject(url, WxAuthResponse.class);
log.info("获取openid结果: {}", response);
log.info("AppId为: {}", wechatPayConfig.getAppId());
log.info("Secret为: {}", wechatPayConfig.getSecret());
if (response == null || response.getOpenid() == null) {
throw new RuntimeException("Failed to get openid from WeChat");
// 格式化 URL
String requestUrl = String.format(
url,
appId,
secret,
code
);
log.info("调用微信 OAuth2 access_token 接口脱敏URL{}", requestUrl);
// 3. 发起 HTTP GET 请求设置请求头模拟浏览器避免被拦截
HttpHeaders headers = new HttpHeaders();
headers.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36");
HttpEntity<Void> requestEntity = new HttpEntity<>(headers);
try {
// 发起请求RestTemplate 自动处理 URL 编码避免特殊字符问题
ResponseEntity<String> responseEntity = restTemplate.exchange(
requestUrl,
HttpMethod.GET,
requestEntity,
String.class
);
// 4. 校验响应状态码 200 直接抛异常
if (!responseEntity.getStatusCode().is2xxSuccessful()) {
throw new ServiceException(
);
}
String responseBody = responseEntity.getBody();
log.info("微信 OAuth2 接口响应内容:{}", responseBody);
// 5. JSON 解析 Jackson 替代 fastjson Spring 生态兼容且异常可控
try {
return objectMapper.readValue(responseBody, WxAuthResponse.class);
} catch (Exception e) {
throw new ServiceException(
);
}
} catch (ServiceException e) {
// 捕获 HTTP 相关异常连接超时读取超时等
throw new ServiceException(
"OAuth 接口 HTTP 请求失败"
);
} catch (Exception e) {
// 捕获其他未知异常兜底处理
throw new ServiceException(
"获取 OAuth access_token 未知异常"
);
}
return response;
}
/**
* 1. 参数校验解决 appid/secret/code 缺失问题
*/
private void validateParams(String code) {
String appId = wechatMiniProgramConfig.getAppId();
String secret = wechatMiniProgramConfig.getSecret();
// 校验 appid null非空字符串非空白
if (!StringUtils.hasText(appId)) {
throw new ServiceException("微信 OAuth 配置异常appid 缺失null/空字符串)");
}
// 校验 secret
if (!StringUtils.hasText(secret)) {
throw new ServiceException("微信 OAuth 配置异常secret 缺失null/空字符串)");
}
// 校验 code授权回调返回的 code 不能为空
if (!StringUtils.hasText(code)) {
throw new ServiceException("微信 OAuth 授权异常code 缺失null/空字符串)");
}
}
/**
* 2. 敏感信息脱敏避免 secret 明文打印符合安全规范
* 示例secret=abcdefghijklmn 脱敏后=abcdef****mn
*/
private String maskSensitiveInfo(String sensitiveStr) {
if (!StringUtils.hasText(sensitiveStr)) {
return "";
}
int length = sensitiveStr.length();
if (length <= 6) {
return sensitiveStr.substring(0, 2) + "****"; // 短字符串特殊处理
}
return sensitiveStr.substring(0, 6) + "****" + sensitiveStr.substring(length - 2);
}
/**