Compare commits
4 Commits
385ceeecb6
...
6153e6e4f1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6153e6e4f1 | ||
|
|
d681635394 | ||
|
|
b9779e7436 | ||
|
|
07a86fbe66 |
@@ -3,6 +3,7 @@ package com.starry.admin.modules.weichat.service;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.starry.admin.common.exception.ServiceException;
|
||||
import com.starry.admin.common.oss.service.IOssFileService;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
||||
@@ -14,9 +15,11 @@ import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
|
||||
import com.starry.admin.utils.SecurityUtils;
|
||||
import com.starry.common.utils.ConvertUtil;
|
||||
import com.starry.common.utils.IdUtils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Date;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
|
||||
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
|
||||
@@ -68,12 +71,16 @@ public class WxOauthService {
|
||||
if (item == null) {
|
||||
PlayClerkUserInfoEntity entity = ConvertUtil.entityToVo(userInfo, PlayClerkUserInfoEntity.class);
|
||||
entity.setAvatar(generateAvatar(userInfo.getHeadImgUrl()));
|
||||
entity.setWeiChatAvatar(generateAvatar(userInfo.getHeadImgUrl()));
|
||||
entity.setWeiChatAvatar(entity.getAvatar());
|
||||
entity.setId(IdUtils.getUuid());
|
||||
entity.setLevelId(playClerkLevelInfoService.getDefaultLevel().getId());
|
||||
clerkUserInfoService.create(entity);
|
||||
return entity.getId();
|
||||
} else {
|
||||
if (StrUtil.isEmpty(item.getAvatar())) {
|
||||
clerkUserInfoService.update(Wrappers.lambdaUpdate(PlayClerkUserInfoEntity.class).eq(PlayClerkUserInfoEntity::getId, item.getId())
|
||||
.set(PlayClerkUserInfoEntity::getAvatar, generateAvatar(userInfo.getHeadImgUrl())));
|
||||
}
|
||||
return item.getId();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,16 +6,27 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.starry.admin.common.aspect.ClerkUserLogin;
|
||||
import com.starry.admin.common.conf.ThreadLocalRequestDetail;
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
|
||||
import com.starry.admin.modules.withdraw.entity.EarningsLineEntity;
|
||||
import com.starry.admin.modules.withdraw.entity.WithdrawalLogEntity;
|
||||
import com.starry.admin.modules.withdraw.entity.WithdrawalRequestEntity;
|
||||
import com.starry.admin.modules.withdraw.service.IEarningsService;
|
||||
import com.starry.admin.modules.withdraw.service.IWithdrawalLogService;
|
||||
import com.starry.admin.modules.withdraw.service.IWithdrawalService;
|
||||
import com.starry.admin.modules.withdraw.vo.ClerkEarningLineVo;
|
||||
import com.starry.admin.modules.withdraw.vo.ClerkWithdrawBalanceVo;
|
||||
import com.starry.common.result.TypedR;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Resource;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -32,6 +43,8 @@ public class WxWithdrawController {
|
||||
private IWithdrawalService withdrawalService;
|
||||
@Resource
|
||||
private IWithdrawalLogService withdrawalLogService;
|
||||
@Resource
|
||||
private IPlayOrderInfoService orderInfoService;
|
||||
|
||||
@Data
|
||||
public static class CreateWithdrawRequest {
|
||||
@@ -51,18 +64,69 @@ public class WxWithdrawController {
|
||||
|
||||
@ClerkUserLogin
|
||||
@GetMapping("/earnings")
|
||||
public TypedR<java.util.List<EarningsLineEntity>> listEarnings(@RequestParam(value = "status", required = false) String status,
|
||||
public TypedR<java.util.List<ClerkEarningLineVo>> listEarnings(
|
||||
@RequestParam(value = "status", required = false) String status,
|
||||
@RequestParam(value = "pageNum", defaultValue = "1") long pageNum,
|
||||
@RequestParam(value = "pageSize", defaultValue = "10") long pageSize) {
|
||||
@RequestParam(value = "pageSize", defaultValue = "10") long pageSize,
|
||||
@RequestParam(value = "beginTime", required = false) String beginTime,
|
||||
@RequestParam(value = "endTime", required = false) String endTime) {
|
||||
String clerkId = ThreadLocalRequestDetail.getClerkUserInfo().getId();
|
||||
LambdaQueryWrapper<EarningsLineEntity> q = new LambdaQueryWrapper<>();
|
||||
q.eq(EarningsLineEntity::getClerkId, clerkId);
|
||||
if (status != null && !status.isEmpty()) {
|
||||
q.eq(EarningsLineEntity::getStatus, status);
|
||||
}
|
||||
LocalDateTime begin = parseDateTime(beginTime);
|
||||
LocalDateTime end = parseDateTime(endTime);
|
||||
if (begin != null) {
|
||||
q.ge(EarningsLineEntity::getCreatedTime, begin);
|
||||
}
|
||||
if (end != null) {
|
||||
q.le(EarningsLineEntity::getCreatedTime, end);
|
||||
}
|
||||
q.orderByDesc(EarningsLineEntity::getCreatedTime);
|
||||
IPage<EarningsLineEntity> page = earningsService.page(new Page<>(pageNum, pageSize), q);
|
||||
return TypedR.okPage(page);
|
||||
|
||||
List<EarningsLineEntity> records = page.getRecords();
|
||||
List<ClerkEarningLineVo> vos = new ArrayList<>();
|
||||
if (!records.isEmpty()) {
|
||||
List<String> orderIds = records.stream()
|
||||
.map(EarningsLineEntity::getOrderId)
|
||||
.filter(id -> id != null && !id.isEmpty())
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
Map<String, PlayOrderInfoEntity> orderMap = orderIds.isEmpty() ? java.util.Collections.emptyMap()
|
||||
: orderInfoService.lambdaQuery()
|
||||
.in(PlayOrderInfoEntity::getId, orderIds)
|
||||
.list()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(PlayOrderInfoEntity::getId, it -> it));
|
||||
for (EarningsLineEntity line : records) {
|
||||
ClerkEarningLineVo vo = new ClerkEarningLineVo();
|
||||
vo.setId(line.getId());
|
||||
vo.setAmount(line.getAmount());
|
||||
vo.setStatus(line.getStatus());
|
||||
vo.setEarningType(line.getEarningType());
|
||||
vo.setWithdrawalId(line.getWithdrawalId());
|
||||
vo.setUnlockTime(line.getUnlockTime());
|
||||
vo.setCreatedTime(toLocalDateTime(line.getCreatedTime()));
|
||||
vo.setOrderId(line.getOrderId());
|
||||
if (line.getOrderId() != null) {
|
||||
PlayOrderInfoEntity order = orderMap.get(line.getOrderId());
|
||||
if (order != null) {
|
||||
vo.setOrderNo(order.getOrderNo());
|
||||
vo.setOrderStatus(order.getOrderStatus());
|
||||
vo.setOrderEndTime(toLocalDateTime(order.getOrderEndTime()));
|
||||
}
|
||||
}
|
||||
vos.add(vo);
|
||||
}
|
||||
}
|
||||
Page<ClerkEarningLineVo> result = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
|
||||
long totalPage = page.getSize() == 0 ? 0 : (long) Math.ceil((double) page.getTotal() / (double) page.getSize());
|
||||
result.setPages(totalPage);
|
||||
result.setRecords(vos);
|
||||
return TypedR.okPage(result);
|
||||
}
|
||||
|
||||
@ClerkUserLogin
|
||||
@@ -101,4 +165,36 @@ public class WxWithdrawController {
|
||||
.list();
|
||||
return TypedR.ok(list);
|
||||
}
|
||||
|
||||
private LocalDateTime parseDateTime(String value) {
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
String trimmed = value.trim();
|
||||
DateTimeFormatter[] formatters = new DateTimeFormatter[] {
|
||||
DateTimeFormatter.ISO_DATE_TIME,
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"),
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss")
|
||||
};
|
||||
for (DateTimeFormatter formatter : formatters) {
|
||||
try {
|
||||
return LocalDateTime.parse(trimmed, formatter);
|
||||
} catch (DateTimeParseException ignored) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private LocalDateTime toLocalDateTime(Object value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof LocalDateTime) {
|
||||
return (LocalDateTime) value;
|
||||
}
|
||||
if (value instanceof Date) {
|
||||
return ((Date) value).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,6 @@ import org.springframework.util.StringUtils;
|
||||
public class WithdrawalServiceImpl extends ServiceImpl<WithdrawalRequestMapper, WithdrawalRequestEntity>
|
||||
implements IWithdrawalService {
|
||||
|
||||
private static final long PAYEE_CONFIRMATION_MAX_MINUTES = 10L;
|
||||
|
||||
@Resource
|
||||
private IEarningsService earningsService;
|
||||
@Resource
|
||||
@@ -49,21 +47,17 @@ public class WithdrawalServiceImpl extends ServiceImpl<WithdrawalRequestMapper,
|
||||
if (payeeProfile == null || !StringUtils.hasText(payeeProfile.getQrCodeUrl())) {
|
||||
throw new CustomException("请先上传支付宝收款码");
|
||||
}
|
||||
|
||||
if (payeeProfile.getLastConfirmedAt() == null) {
|
||||
throw new CustomException("请确认本次使用的收款码");
|
||||
}
|
||||
Duration sinceConfirm = Duration.between(payeeProfile.getLastConfirmedAt(), now);
|
||||
if (sinceConfirm.isNegative() || sinceConfirm.toMinutes() > PAYEE_CONFIRMATION_MAX_MINUTES) {
|
||||
throw new CustomException("收款码确认已过期,请重新确认");
|
||||
}
|
||||
|
||||
BigDecimal available = earningsService.getAvailableAmount(clerkId, now);
|
||||
if (available.compareTo(amount) < 0) {
|
||||
throw new CustomException("可提现余额不足");
|
||||
}
|
||||
// pick and reserve lines
|
||||
List<EarningsLineEntity> lines = earningsService.findWithdrawable(clerkId, amount, now);
|
||||
if (lines.isEmpty()) throw new CustomException("可提现余额不足");
|
||||
if (lines.isEmpty()) {
|
||||
BigDecimal latestAvailable = earningsService.getAvailableAmount(clerkId, now);
|
||||
throw new CustomException("可提现余额不足或已被锁定,当前可用:" + latestAvailable);
|
||||
}
|
||||
|
||||
// Reserve lines FIRST with temp ID (fail fast before creating request)
|
||||
String tempWithdrawalId = "TEMP_" + IdUtils.getUuid();
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.starry.admin.modules.withdraw.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.starry.admin.modules.withdraw.enums.EarningsType;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
@Data
|
||||
public class ClerkEarningLineVo {
|
||||
private String id;
|
||||
private BigDecimal amount;
|
||||
private String status;
|
||||
private EarningsType earningType;
|
||||
private String withdrawalId;
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private LocalDateTime unlockTime;
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private LocalDateTime createdTime;
|
||||
|
||||
private String orderId;
|
||||
private String orderNo;
|
||||
private String orderStatus;
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private LocalDateTime orderEndTime;
|
||||
}
|
||||
Reference in New Issue
Block a user