bug修改

This commit is contained in:
周鹏 committed 2026-08-17 22:04:50 +08:00
1 parent 1d7f665550
commit 17d293ec0a
10 files changed
+190 -18

No files matched your search

@@ -18,8 +18,12 @@ public enum BrokerageWithdrawTypeEnum implements ArrayValuable<Integer> {
WALLET(1, "钱包"),
BANK(2, "银行卡"),
WECHAT(3, "微信"), // 手动打款
ALIPAY(4, "支付宝"),
WECHAT_API(5, "支付宝零钱"), // 自动打款,通过微信转账 API
ALIPAY(4, "支付宝"), // 手动打款
/**
* 历史命名 WECHAT_API;App 端佣金提现实际传 type=5 + 支付宝账号,运营后台手动打款。
* 切勿再走微信转账 API。
*/
WECHAT_API(5, "支付宝"),
;
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BrokerageWithdrawTypeEnum::getType).toArray(Integer[]::new);
@@ -39,13 +43,11 @@ public enum BrokerageWithdrawTypeEnum implements ArrayValuable<Integer> {
}
/**
* 是否通过支付平台的 API 打款
*
* @param type 类型
* @return 是否
* 是否通过支付平台的 API 打款。
* 当前业务均为运营手动打款,不再走微信/支付宝自动转账。
*/
public static boolean isApi(Integer type) {
return WECHAT_API.getType().equals(type);
return false;
}
}
@@ -1,10 +1,15 @@
package cn.iocoder.yudao.module.trade.controller.admin.brokerage;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
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.PayTransferNotifyReqDTO;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawExcelVO;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawRejectReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawRespVO;
@@ -22,9 +27,15 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
@@ -81,6 +92,39 @@ public class BrokerageWithdrawController {
return success(BrokerageWithdrawConvert.INSTANCE.convertPage(pageResult, userMap));
}
@GetMapping("/export-excel")
@Operation(summary = "导出佣金提现 Excel")
@PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:query')")
@ApiAccessLog(operateType = EXPORT)
public void exportBrokerageWithdrawExcel(@Valid BrokerageWithdrawPageReqVO pageVO,
HttpServletResponse response) throws IOException {
pageVO.setPageSize(PageParam.PAGE_SIZE_NONE);
PageResult<BrokerageWithdrawDO> pageResult = brokerageWithdrawService.getBrokerageWithdrawPage(pageVO);
Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(
convertSet(pageResult.getList(), BrokerageWithdrawDO::getUserId));
List<BrokerageWithdrawExcelVO> excelList = new ArrayList<>(pageResult.getList().size());
for (BrokerageWithdrawDO row : pageResult.getList()) {
BrokerageWithdrawExcelVO excel = new BrokerageWithdrawExcelVO();
excel.setId(row.getId());
excel.setUserId(row.getUserId());
excel.setUserNickname(Optional.ofNullable(userMap.get(row.getUserId()))
.map(MemberUserRespDTO::getNickname).orElse(null));
excel.setPriceYuan(row.getPrice() == null ? "0" : MoneyUtils.fenToYuanStr(row.getPrice()));
excel.setFeePriceYuan(row.getFeePrice() == null ? "0" : MoneyUtils.fenToYuanStr(row.getFeePrice()));
excel.setTypeName("支付宝");
excel.setAccountNo(row.getAccountNo());
excel.setName(row.getName());
excel.setStatus(row.getStatus());
excel.setAuditReason(row.getAuditReason());
excel.setAuditTime(row.getAuditTime());
excel.setCreateTime(row.getCreateTime());
excel.setRemark(row.getRemark());
excelList.add(excel);
}
ExcelUtils.write(response, "佣金提现.xls", "数据", BrokerageWithdrawExcelVO.class, excelList);
}
// TODO @luchi:update-transferred,url 改成这个。和 update-paid 、update-refunded 保持一致
@PostMapping("/update-transfer")
@Operation(summary = "更新转账订单为转账成功") // 由 pay-module 支付服务,进行回调,可见 PayNotifyJob
@@ -0,0 +1,57 @@
package cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.trade.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 佣金提现 Excel VO
*/
@Data
public class BrokerageWithdrawExcelVO {
@ExcelProperty("编号")
private Long id;
@ExcelProperty("用户编号")
private Long userId;
@ExcelProperty("用户昵称")
private String userNickname;
@ExcelProperty("提现金额(元)")
private String priceYuan;
@ExcelProperty("手续费(元)")
private String feePriceYuan;
@ExcelProperty("提现方式")
private String typeName;
@ExcelProperty("支付宝账号")
private String accountNo;
@ExcelProperty("真实姓名")
private String name;
@ExcelProperty(value = "状态", converter = DictConvert.class)
@DictFormat(DictTypeConstants.BROKERAGE_WITHDRAW_STATUS)
private Integer status;
@ExcelProperty("审核原因")
private String auditReason;
@ExcelProperty("审核时间")
private LocalDateTime auditTime;
@ExcelProperty("申请时间")
private LocalDateTime createTime;
@ExcelProperty("备注")
private String remark;
}
@@ -30,6 +30,12 @@ public class AppBrokerageWithdrawRespVO {
@Schema(description = "提现时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "审核驳回原因", example = "信息不完整")
private String auditReason;
@Schema(description = "转账失败原因", example = "用户未确认收款")
private String transferErrorMsg;
// ========== 微信转账专属 ==========
@Schema(description = "转账单编号", example = "1024")
@@ -124,17 +124,14 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
.setUserId(withdraw.getUserId()).setUserType(UserTypeEnum.MEMBER.getValue())
.setBizType(PayWalletBizTypeEnum.BROKERAGE_WITHDRAW.getType()).setBizId(withdraw.getId().toString())
.setPrice(withdraw.getPrice()));
// 1.2 微信 API
} else if (BrokerageWithdrawTypeEnum.WECHAT_API.getType().equals(withdraw.getType())) {
// TODO @luchi:这里,要加个转账单号的记录;另外,调用 API 转账,是立马成功,还是有延迟的哈?
createPayTransfer(withdraw);
// 1.3 剩余类型,都是手动打款,所以不处理
// 1.2 历史 type=5 曾走微信转账 API;现业务为支付宝账号提现,统一手动打款
// 若再走 createPayTransfer,无小程序 openid 会 NPE → 管理端「系统异常」
} else {
// TODO 可优化:未来可以考虑,接入支付宝、银联等 API 转账,实现自动打款
log.info("[auditBrokerageWithdrawSuccess][withdraw({}) 类型({}) 手动打款,无需处理]", withdraw.getId(), withdraw.getType());
log.info("[auditBrokerageWithdrawSuccess][withdraw({}) 类型({}) 手动打款,账号={}]",
withdraw.getId(), withdraw.getType(), withdraw.getAccountNo());
}
// 2. 非支付 API,则直接体现成功
// 2. 非支付 API,则直接提现成功
if (!BrokerageWithdrawTypeEnum.isApi(withdraw.getType())) {
brokerageWithdrawMapper.updateByIdAndStatus(withdraw.getId(), BrokerageWithdrawStatusEnum.AUDIT_SUCCESS.getStatus(),
new BrokerageWithdrawDO().setStatus(BrokerageWithdrawStatusEnum.WITHDRAW_SUCCESS.getStatus()));
@@ -29,6 +29,11 @@ public interface ChatApi {
*/
boolean createOrUpdate(@Valid ChatCreateOrUpdateUserReqVO dto);
/**
* 封禁聊天用户并推送下线
*/
boolean banUser(Long userId, String reason);
/**
* 根据userId查询用户详情
*/
@@ -121,6 +121,36 @@ public class ChatApiImpl implements ChatApi {
}
}
/**
* 封禁聊天用户并推送下线
*/
@Override
public boolean banUser(Long userId, String reason) {
if (userId == null) {
return false;
}
ChatApiTool.API api = setApiConfig(ChatApiTool.USER_BAN);
try {
ChatBanUserReqVO dto = new ChatBanUserReqVO(userId, reason);
String body = JSONUtil.toJsonStr(dto);
String res = HttpRequest.post(api.getFullPath()).header(apiCountersign(body)).body(body).execute().body();
log.debug("封禁聊天用户,res:{}", res);
if (ObjectUtil.isEmpty(res)) {
log.error("封禁聊天用户,res为空");
return false;
}
ChatBaseRespVO baseRes = JSONUtil.toBean(res, ChatBaseRespVO.class);
if (ObjectUtil.isEmpty(baseRes) || !baseRes.isSuccess()) {
log.error("封禁聊天用户,返回错误,res:{}", res);
return false;
}
return true;
} catch (Exception e) {
log.error("封禁聊天用户,意外的错误,userId={}", userId, e);
return false;
}
}
/**
* 根据userId查询用户详情
*/
@@ -21,6 +21,7 @@ public class ChatApiTool {
public static final API USER_LOGIN = API.builder().summary("用户登录").url("/third/user/login").method(HttpMethod.POST).build();
public static final API USER_GET = API.builder().summary("根据userId查询用户详情").url("/third/user/getUserByUserId").method(HttpMethod.POST).build();
public static final API USER_SAVE_OR_UPDATE = API.builder().summary("创建或者更新用户").url("/third/user/createOrUpdate").method(HttpMethod.POST).build();
public static final API USER_BAN = API.builder().summary("封禁用户并踢下线").url("/third/user/ban").method(HttpMethod.POST).build();
public static final API GET_USER_ONLINE_STATUS_BY_ID = API.builder().summary("查询用户在线状态").url("/third/user/getUserOnlineStatusById").method(HttpMethod.POST).build();
// 朋友圈
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.member.client.vo.chat.user;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "封禁聊天用户")
public class ChatBanUserReqVO {
@NotNull(message = "用户id不可为空")
@Schema(description = "用户id")
private Long userId;
@Schema(description = "封禁原因")
private String reason;
}
@@ -478,7 +478,7 @@ public class MemberUserServiceImpl implements MemberUserService {
}
/**
* 清理会员全部 OAuth2 access + refresh,App 下次请求会 401 并退出登录。
* 清理会员全部 OAuth2 access + refresh,并通知 IM 立刻踢下线。
*/
private void kickMemberLogin(Long userId) {
try {
@@ -487,6 +487,11 @@ public class MemberUserServiceImpl implements MemberUserService {
log.warn("踢出会员登录态失败, userId={}", userId, e);
throw e;
}
try {
chatApi.banUser(userId, "账号已被禁用");
} catch (Exception e) {
log.warn("通知聊天服务封禁用户失败, userId={}", userId, e);
}
}
@VisibleForTesting
@@ -577,12 +582,15 @@ public class MemberUserServiceImpl implements MemberUserService {
/**
* 获取用户的微信信息
* 有微信号即可解锁;二维码为可选展示字段。
*/
@Override
public MemberWechatBO getMemberWechat(Long memberId) {
MemberUserDO detail = memberUserMapper.selectById(memberId);
if (ObjectUtil.isEmpty(detail)) return null;
if (ObjectUtil.isEmpty(detail.getWechatNum()) || ObjectUtil.isEmpty(detail.getWechatQrCode())) {
if (ObjectUtil.isEmpty(detail)) {
return null;
}
if (ObjectUtil.isEmpty(detail.getWechatNum())) {
return null;
}
MemberWechatBO result = new MemberWechatBO();