|
|
@@ -0,0 +1,1005 @@
|
|
|
+package shop.alien.storeplatform.service.impl;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.springframework.beans.BeanUtils;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+import shop.alien.entity.result.R;
|
|
|
+import shop.alien.entity.store.StoreCashOutRecord;
|
|
|
+import shop.alien.entity.store.StoreIncomeDetailsRecord;
|
|
|
+import shop.alien.entity.store.StoreInfo;
|
|
|
+import shop.alien.entity.store.StoreUser;
|
|
|
+import shop.alien.entity.store.vo.StoreCashOutRecordVo;
|
|
|
+import shop.alien.entity.store.vo.StoreIncomeDetailsRecordVo;
|
|
|
+import shop.alien.mapper.StoreCashOutRecordMapper;
|
|
|
+import shop.alien.mapper.StoreIncomeDetailsRecordMapper;
|
|
|
+import shop.alien.mapper.StoreInfoMapper;
|
|
|
+import shop.alien.mapper.StoreUserMapper;
|
|
|
+import shop.alien.storeplatform.service.IncomeManageService;
|
|
|
+import shop.alien.util.common.ListToPage;
|
|
|
+import shop.alien.util.common.constant.CouponTypeEnum;
|
|
|
+import shop.alien.util.date.DateUtils;
|
|
|
+
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.math.RoundingMode;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.LocalTime;
|
|
|
+import java.util.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * web端商户收入管理服务实现
|
|
|
+ *
|
|
|
+ * @author ssk
|
|
|
+ * @since 2025-11-14
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+@RequiredArgsConstructor
|
|
|
+public class IncomeManageServiceImpl extends ServiceImpl<StoreIncomeDetailsRecordMapper, StoreIncomeDetailsRecord> implements IncomeManageService {
|
|
|
+
|
|
|
+ private final StoreIncomeDetailsRecordMapper storeIncomeDetailsRecordMapper;
|
|
|
+ private final StoreInfoMapper storeInfoMapper;
|
|
|
+ private final StoreUserMapper storeUserMapper;
|
|
|
+ private final StoreCashOutRecordMapper storeCashOutRecordMapper;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public com.alibaba.fastjson2.JSONObject getPaymentCycle(Integer storeId, Integer incomeType, Integer paymentType,
|
|
|
+ String startTime, String endTime, int page, int size) {
|
|
|
+ log.info("IncomeManageServiceImpl.getPaymentCycle - 开始查询账期: storeId={}, incomeType={}, paymentType={}, startTime={}, endTime={}, page={}, size={}",
|
|
|
+ storeId, incomeType, paymentType, startTime, endTime, page, size);
|
|
|
+
|
|
|
+ QueryWrapper<StoreIncomeDetailsRecord> wrapper = new QueryWrapper<>();
|
|
|
+ com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
|
|
|
+ Date now = new Date();
|
|
|
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
+
|
|
|
+ // 1. 根据账期类型设置查询条件
|
|
|
+ if (paymentType != null && paymentType == 0) {
|
|
|
+ // 未到账期: 未绑定提现记录 & 当前时间-3天大于创建时间
|
|
|
+ wrapper.isNull("sidr.cash_out_id");
|
|
|
+ wrapper.gt("sidr.created_time", DateUtils.calcDays(new Date(), -3));
|
|
|
+ Date startDate = DateUtils.calcDays(now, -3);
|
|
|
+ jsonObject.put("date", df.format(startDate) + " ~ " + df.format(now));
|
|
|
+ log.debug("IncomeManageServiceImpl.getPaymentCycle - 未到账期: date={}", jsonObject.get("date"));
|
|
|
+ } else if (paymentType != null && paymentType == 1) {
|
|
|
+ // 已到账期: 已绑定提现记录 & 当前时间-4~27天大于创建时间
|
|
|
+// wrapper.isNotNull("sidr.cash_out_id");
|
|
|
+ wrapper.between("sidr.created_time", DateUtils.calcDays(new Date(), -27), DateUtils.calcDays(new Date(), -4));
|
|
|
+ Date startDate = DateUtils.calcDays(now, -27);
|
|
|
+ Date endDate = DateUtils.calcDays(now, -4);
|
|
|
+ jsonObject.put("date", df.format(startDate) + " ~ " + df.format(endDate));
|
|
|
+ log.debug("IncomeManageServiceImpl.getPaymentCycle - 已到账期: date={}", jsonObject.get("date"));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 门店ID条件
|
|
|
+ if (storeId != null) {
|
|
|
+ wrapper.eq("sidr.store_id", storeId);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 时间范围条件(用户选择的时间范围)
|
|
|
+ if (StringUtils.isNotBlank(startTime) && StringUtils.isNotBlank(endTime)) {
|
|
|
+ LocalDate startDate = LocalDate.parse(startTime);
|
|
|
+ LocalDate endDate = LocalDate.parse(endTime);
|
|
|
+ LocalDateTime startOfDay = startDate.atStartOfDay();
|
|
|
+ LocalDateTime endOfDay = endDate.atTime(LocalTime.MAX);
|
|
|
+ wrapper.between("sidr.created_time", startOfDay, endOfDay)
|
|
|
+ .orderByDesc("sidr.created_time");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 收入类型条件
|
|
|
+ if (null != incomeType) {
|
|
|
+ if (0 == incomeType) {
|
|
|
+ // 主页: 包含优惠券和代金券
|
|
|
+ wrapper.in("sidr.income_type", CouponTypeEnum.COUPON.getCode(), CouponTypeEnum.GROUP_BUY.getCode());
|
|
|
+ log.debug("IncomeManageServiceImpl.getPaymentCycle - 收入类型: 主页(优惠券+代金券)");
|
|
|
+ } else {
|
|
|
+ wrapper.eq("sidr.income_type", incomeType);
|
|
|
+ log.debug("IncomeManageServiceImpl.getPaymentCycle - 收入类型: {}", incomeType);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 查询收入明细记录列表
|
|
|
+ List<StoreIncomeDetailsRecordVo> list = storeIncomeDetailsRecordMapper.selectRecordList(wrapper);
|
|
|
+ log.info("IncomeManageServiceImpl.getPaymentCycle - 查询到收入记录数: {}", list.size());
|
|
|
+
|
|
|
+ // 6. 查询门店信息(用于获取抽成比例)
|
|
|
+ StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
|
|
|
+ if (storeInfo == null) {
|
|
|
+ log.warn("IncomeManageServiceImpl.getPaymentCycle - 门店不存在: storeId={}", storeId);
|
|
|
+ throw new RuntimeException("门店不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 7. 处理列表数据:格式化金额、日期
|
|
|
+ for (StoreIncomeDetailsRecordVo storeIncomeDetailsRecord : list) {
|
|
|
+ if (storeIncomeDetailsRecord == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ StoreIncomeDetailsRecordVo vo = new StoreIncomeDetailsRecordVo();
|
|
|
+ BeanUtils.copyProperties(storeIncomeDetailsRecord, vo);
|
|
|
+
|
|
|
+ // 将金额从分转换为元(保留2位小数)
|
|
|
+ storeIncomeDetailsRecord.setMoneyStr(
|
|
|
+ new BigDecimal(vo.getMoney())
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
|
|
|
+ .toString()
|
|
|
+ );
|
|
|
+
|
|
|
+ // 格式化日期
|
|
|
+ String format = df.format(storeIncomeDetailsRecord.getCreatedTime());
|
|
|
+ storeIncomeDetailsRecord.setDate(format);
|
|
|
+
|
|
|
+ // 设置抽成比例
|
|
|
+ vo.setCommissionRate(storeInfo.getCommissionRate());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 8. 手动分页
|
|
|
+ jsonObject.put("data", ListToPage.setPage(list, page, size));
|
|
|
+
|
|
|
+ // 9. 计算总金额(元)
|
|
|
+ int totalMoney = list.stream().mapToInt(StoreIncomeDetailsRecord::getMoney).sum();
|
|
|
+ jsonObject.put("money",
|
|
|
+ new BigDecimal(totalMoney)
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
|
|
|
+ .toString()
|
|
|
+ );
|
|
|
+
|
|
|
+ log.info("IncomeManageServiceImpl.getPaymentCycle - 查询完成: 总记录数={}, 总金额={}元",
|
|
|
+ list.size(), jsonObject.get("money"));
|
|
|
+
|
|
|
+ return jsonObject;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Map<String, Object> getAccountBalance(Integer storeId) {
|
|
|
+ log.info("IncomeManageServiceImpl.getAccountBalance - 开始查询账户余额: storeId={}", storeId);
|
|
|
+
|
|
|
+ Map<String, Object> map = new HashMap<>();
|
|
|
+
|
|
|
+ // 1. 查询店铺用户信息,获取账户总余额
|
|
|
+ LambdaQueryWrapper<StoreUser> storeUserWrapper = new LambdaQueryWrapper<>();
|
|
|
+ storeUserWrapper.eq(StoreUser::getStoreId, storeId);
|
|
|
+ StoreUser storeUser = storeUserMapper.selectOne(storeUserWrapper);
|
|
|
+
|
|
|
+ if (storeUser == null) {
|
|
|
+ log.warn("IncomeManageServiceImpl.getAccountBalance - 店铺用户不存在: storeId={}", storeId);
|
|
|
+ throw new RuntimeException("店铺用户不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 账户总余额(单位:元,保留2位小数)
|
|
|
+ String balance = new BigDecimal(storeUser.getMoney())
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
|
|
|
+ .toString();
|
|
|
+ map.put("balance", balance);
|
|
|
+ log.debug("IncomeManageServiceImpl.getAccountBalance - 账户总余额: {}元", balance);
|
|
|
+
|
|
|
+ // 3. 查询可提现金额(4~27天内的收入,且未绑定提现记录)
|
|
|
+ LambdaQueryWrapper<StoreIncomeDetailsRecord> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.between(StoreIncomeDetailsRecord::getCreatedTime,
|
|
|
+ DateUtils.calcDays(new Date(), -27),
|
|
|
+ DateUtils.calcDays(new Date(), -4))
|
|
|
+ .isNull(StoreIncomeDetailsRecord::getCashOutId) // 未绑定提现记录
|
|
|
+ .eq(StoreIncomeDetailsRecord::getStoreId, storeId);
|
|
|
+
|
|
|
+ List<StoreIncomeDetailsRecord> incomeList = storeIncomeDetailsRecordMapper.selectList(wrapper);
|
|
|
+
|
|
|
+ // 计算可提现收入总额(分)
|
|
|
+ int cashOutMoney = incomeList.stream()
|
|
|
+ .mapToInt(StoreIncomeDetailsRecord::getMoney)
|
|
|
+ .sum();
|
|
|
+
|
|
|
+ log.debug("IncomeManageServiceImpl.getAccountBalance - 4~27天内收入总额: {}分", cashOutMoney);
|
|
|
+
|
|
|
+ // 4. 查询已提现和待审核的金额(需要扣除)
|
|
|
+ // payment_status: 1-待审核, 3-已提现
|
|
|
+ QueryWrapper<StoreCashOutRecord> cashOutWrapper = new QueryWrapper<>();
|
|
|
+ cashOutWrapper.eq("store_id", storeId)
|
|
|
+ .in("payment_status", "1", "3")
|
|
|
+ .eq("delete_flag", "0");
|
|
|
+
|
|
|
+ List<StoreCashOutRecord> storeCashOutRecords = storeCashOutRecordMapper.selectList(cashOutWrapper);
|
|
|
+
|
|
|
+ // 计算已提现和待审核的总金额(分)
|
|
|
+ int totalCashOutAmount = storeCashOutRecords.stream()
|
|
|
+ .collect(Collectors.summingInt(StoreCashOutRecord::getMoney));
|
|
|
+
|
|
|
+ log.debug("IncomeManageServiceImpl.getAccountBalance - 已提现+待审核金额: {}分", totalCashOutAmount);
|
|
|
+
|
|
|
+ // 5. 计算最终可提现金额 = 可提现收入 - (已提现 + 待审核)
|
|
|
+ BigDecimal finalCashOutMoney = new BigDecimal(cashOutMoney)
|
|
|
+ .subtract(BigDecimal.valueOf(totalCashOutAmount))
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
|
|
|
+
|
|
|
+ map.put("cashOutMoney", finalCashOutMoney.toString());
|
|
|
+
|
|
|
+ log.info("IncomeManageServiceImpl.getAccountBalance - 查询完成: 总余额={}元, 可提现金额={}元",
|
|
|
+ balance, finalCashOutMoney);
|
|
|
+
|
|
|
+ return map;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 提现申请
|
|
|
+ *
|
|
|
+ * @param storeId 门店ID
|
|
|
+ * @param payPassword 支付密码
|
|
|
+ * @param withdrawalMoney 提现金额(单位:分)
|
|
|
+ * @return 提现结果
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ @Override
|
|
|
+ public R<?> cashOut(Integer storeId, String payPassword, Integer withdrawalMoney) {
|
|
|
+ log.info("IncomeManageServiceImpl.cashOut - 开始处理提现: storeId={}, withdrawalMoney={}分",
|
|
|
+ storeId, withdrawalMoney);
|
|
|
+
|
|
|
+ // 1. 验证支付密码并查询用户信息
|
|
|
+ LambdaQueryWrapper<StoreUser> userWrapper = new LambdaQueryWrapper<>();
|
|
|
+ userWrapper.eq(StoreUser::getStoreId, storeId)
|
|
|
+ .eq(StoreUser::getPayPassword, payPassword);
|
|
|
+ StoreUser storeUser = storeUserMapper.selectOne(userWrapper);
|
|
|
+
|
|
|
+ if (storeUser == null) {
|
|
|
+ log.warn("IncomeManageServiceImpl.cashOut - 支付密码错误: storeId={}", storeId);
|
|
|
+ return R.fail("支付密码错误");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 验证账户余额是否充足
|
|
|
+ if (storeUser.getMoney() < withdrawalMoney) {
|
|
|
+ log.warn("IncomeManageServiceImpl.cashOut - 余额不足: 账户余额={}分, 提现金额={}分",
|
|
|
+ storeUser.getMoney(), withdrawalMoney);
|
|
|
+ return R.fail("余额不足");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 验证提现金额是否满足最低要求(0.1元 = 10分)
|
|
|
+ BigDecimal amountInYuan = new BigDecimal(withdrawalMoney)
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.DOWN);
|
|
|
+ if (amountInYuan.compareTo(new BigDecimal("0.1")) < 0) {
|
|
|
+ log.warn("IncomeManageServiceImpl.cashOut - 提现金额过小: {}元", amountInYuan);
|
|
|
+ return R.fail("金额不能小于0.1元");
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("IncomeManageServiceImpl.cashOut - 验证通过: 账户余额={}分, 提现金额={}元",
|
|
|
+ storeUser.getMoney(), amountInYuan);
|
|
|
+
|
|
|
+ // 4. 调用支付宝转账
|
|
|
+ // 注意:这里需要集成支付宝API,目前先创建提现申请记录
|
|
|
+ // 实际生产环境需要调用 AliApi.pay() 或 AliApi.payAccount()
|
|
|
+
|
|
|
+ try {
|
|
|
+ // TODO: 调用支付宝转账API
|
|
|
+ // StoreAliPayLog pay;
|
|
|
+ // if (StringUtils.isNotBlank(storeUser.getAlipayAccount())) {
|
|
|
+ // pay = aliApi.pay(storeUser.getName(), storeUser.getIdCard(),
|
|
|
+ // storeUser.getAlipayAccount(), amountInYuan.toString());
|
|
|
+ // } else {
|
|
|
+ // pay = aliApi.payAccount(storeUser.getName(), storeUser.getIdCard(),
|
|
|
+ // null, amountInYuan.toString(), storeUser.getPhone());
|
|
|
+ // }
|
|
|
+
|
|
|
+ // 5. 创建提现记录(待审核状态)
|
|
|
+ // Web端采用人工审核模式,不直接调用支付宝接口
|
|
|
+ StoreCashOutRecord storeCashOutRecord = new StoreCashOutRecord();
|
|
|
+ storeCashOutRecord.setStoreId(storeId);
|
|
|
+ storeCashOutRecord.setMoney(withdrawalMoney);
|
|
|
+ storeCashOutRecord.setCashOutType(0);
|
|
|
+ storeCashOutRecord.setPaymentStatus(1); // 1-待审核
|
|
|
+ storeCashOutRecord.setDeleteFlag(0);
|
|
|
+ storeCashOutRecord.setStoreUserId(storeUser.getId());
|
|
|
+ storeCashOutRecord.setPayDate(new Date());
|
|
|
+
|
|
|
+ // 插入提现记录
|
|
|
+ int insertResult = storeCashOutRecordMapper.insert(storeCashOutRecord);
|
|
|
+ if (insertResult <= 0) {
|
|
|
+ log.error("IncomeManageServiceImpl.cashOut - 创建提现记录失败");
|
|
|
+ throw new RuntimeException("创建提现记录失败");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 扣减账户余额
|
|
|
+ StoreUser updateUser = new StoreUser();
|
|
|
+ updateUser.setId(storeUser.getId());
|
|
|
+ updateUser.setMoney(storeUser.getMoney() - withdrawalMoney);
|
|
|
+ int updateResult = storeUserMapper.updateById(updateUser);
|
|
|
+ if (updateResult <= 0) {
|
|
|
+ log.error("IncomeManageServiceImpl.cashOut - 更新账户余额失败");
|
|
|
+ throw new RuntimeException("更新账户余额失败");
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("IncomeManageServiceImpl.cashOut - 提现申请成功: 提现记录ID={}, 提现金额={}元",
|
|
|
+ storeCashOutRecord.getId(), amountInYuan);
|
|
|
+
|
|
|
+ // 7. 返回结果
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("cashOutRecordId", storeCashOutRecord.getId());
|
|
|
+ result.put("withdrawalMoney", amountInYuan.toString());
|
|
|
+ result.put("status", "pending"); // pending-待审核
|
|
|
+ result.put("message", "提现申请已提交,等待审核");
|
|
|
+
|
|
|
+ return R.data(result);
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("IncomeManageServiceImpl.cashOut - 提现失败: {}", e.getMessage(), e);
|
|
|
+
|
|
|
+ // 如果是已知的运行时异常,直接抛出让事务回滚
|
|
|
+ if (e instanceof RuntimeException) {
|
|
|
+ throw (RuntimeException) e;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 其他异常也需要回滚
|
|
|
+ throw new RuntimeException("提现失败:" + e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 提现记录查询
|
|
|
+ *
|
|
|
+ * @param storeId 门店ID
|
|
|
+ * @param cashOutStartTime 开始时间
|
|
|
+ * @param cashOutEndTime 结束时间
|
|
|
+ * @param paymentStatus 提现状态
|
|
|
+ * @param page 页码
|
|
|
+ * @param size 每页条数
|
|
|
+ * @return 提现记录列表
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public Object getCashOutRecordList(Integer storeId, String cashOutStartTime, String cashOutEndTime,
|
|
|
+ String paymentStatus, Integer page, Integer size) {
|
|
|
+ log.info("IncomeManageServiceImpl.getCashOutRecordList - 开始查询提现记录: storeId={}, cashOutStartTime={}, cashOutEndTime={}, paymentStatus={}, page={}, size={}",
|
|
|
+ storeId, cashOutStartTime, cashOutEndTime, paymentStatus, page, size);
|
|
|
+
|
|
|
+ // 1. 构建查询条件(使用QueryWrapper和表别名scor,以配合selectCashoutRecordList的LEFT JOIN查询)
|
|
|
+ QueryWrapper<StoreCashOutRecord> wrapper = new QueryWrapper<>();
|
|
|
+
|
|
|
+ // 必填条件:门店ID
|
|
|
+ wrapper.eq("scor.store_id", storeId);
|
|
|
+
|
|
|
+ // 可选条件:提现状态
|
|
|
+ if (StringUtils.isNotBlank(paymentStatus)) {
|
|
|
+ wrapper.eq("scor.payment_status", paymentStatus);
|
|
|
+ log.debug("IncomeManageServiceImpl.getCashOutRecordList - 筛选提现状态: {}", paymentStatus);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 可选条件:结束时间
|
|
|
+ if (StringUtils.isNotBlank(cashOutEndTime)) {
|
|
|
+ wrapper.le("scor.created_time", cashOutEndTime + " 23:59:59");
|
|
|
+ log.debug("IncomeManageServiceImpl.getCashOutRecordList - 结束时间: {}", cashOutEndTime);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 可选条件:开始时间
|
|
|
+ if (StringUtils.isNotBlank(cashOutStartTime)) {
|
|
|
+ wrapper.ge("scor.created_time", cashOutStartTime + " 00:00:00");
|
|
|
+ log.debug("IncomeManageServiceImpl.getCashOutRecordList - 开始时间: {}", cashOutStartTime);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 删除标识(软删除)
|
|
|
+ wrapper.eq("scor.delete_flag", 0);
|
|
|
+
|
|
|
+ // 按创建时间倒序排序
|
|
|
+ wrapper.orderByDesc("scor.created_time");
|
|
|
+
|
|
|
+ // 2. 查询所有符合条件的记录(LEFT JOIN store_user,获取结算账户信息)
|
|
|
+ List<StoreCashOutRecord> recordList = storeCashOutRecordMapper.selectCashoutRecordList(wrapper);
|
|
|
+ log.info("IncomeManageServiceImpl.getCashOutRecordList - 查询到提现记录数: {}", recordList.size());
|
|
|
+
|
|
|
+ // 3. 手动分页
|
|
|
+ IPage<StoreCashOutRecord> storeCashOutRecordIPage = ListToPage.setPage(recordList, page, size);
|
|
|
+
|
|
|
+ // 4. 构建返回VO
|
|
|
+ StoreCashOutRecordVo vo = new StoreCashOutRecordVo();
|
|
|
+
|
|
|
+ // 分页后的记录列表
|
|
|
+ vo.setCashOutRecordList(storeCashOutRecordIPage.getRecords());
|
|
|
+
|
|
|
+ // 提现总金额(仅统计状态为1-待审核的记录)
|
|
|
+ int totalMoney = recordList.stream()
|
|
|
+ .filter(item -> "1".equals(item.getPaymentStatus().toString()))
|
|
|
+ .mapToInt(StoreCashOutRecord::getMoney)
|
|
|
+ .sum();
|
|
|
+ BigDecimal cashOutAllMoney = new BigDecimal(totalMoney)
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.DOWN);
|
|
|
+ vo.setCashOutAllMoney(cashOutAllMoney);
|
|
|
+
|
|
|
+ // 提现记录总数
|
|
|
+ vo.setCashOutNum(recordList.size());
|
|
|
+
|
|
|
+ log.info("IncomeManageServiceImpl.getCashOutRecordList - 查询完成: 总记录数={}, 总金额={}元",
|
|
|
+ recordList.size(), cashOutAllMoney);
|
|
|
+
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 团购收益查询
|
|
|
+ *
|
|
|
+ * @param storeId 门店ID
|
|
|
+ * @param date 日期
|
|
|
+ * @param incomeType 收入类型
|
|
|
+ * @param page 页码
|
|
|
+ * @param size 每页条数
|
|
|
+ * @return 团购收益信息
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public Object getGroupIncome(Integer storeId, String date, Integer incomeType, Integer page, Integer size) {
|
|
|
+ log.info("IncomeManageServiceImpl.getGroupIncome - 开始查询团购收益: storeId={}, date={}, incomeType={}, page={}, size={}",
|
|
|
+ storeId, date, incomeType, page, size);
|
|
|
+
|
|
|
+ // 1. 构建查询条件:指定日期内、未绑定提现记录
|
|
|
+ LambdaQueryWrapper<StoreIncomeDetailsRecord> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.between(StoreIncomeDetailsRecord::getCreatedTime, date + " 00:00:00", date + " 23:59:59")
|
|
|
+ .isNull(StoreIncomeDetailsRecord::getCashOutId) // 未绑定提现记录
|
|
|
+ .eq(StoreIncomeDetailsRecord::getStoreId, storeId);
|
|
|
+
|
|
|
+ // 2. 收入类型筛选
|
|
|
+ // 2025-11-17 前端展示逻辑将团购券去掉,仅展示优惠券(前端不传0)
|
|
|
+ if (null != incomeType && 0 == incomeType) {
|
|
|
+ wrapper.in(StoreIncomeDetailsRecord::getIncomeType,
|
|
|
+ CouponTypeEnum.COUPON.getCode(), CouponTypeEnum.GROUP_BUY.getCode());
|
|
|
+ log.debug("IncomeManageServiceImpl.getGroupIncome - 收入类型: 主页(优惠券+团购券)");
|
|
|
+ } else if (null != incomeType) {
|
|
|
+ wrapper.eq(StoreIncomeDetailsRecord::getIncomeType, incomeType);
|
|
|
+ log.debug("IncomeManageServiceImpl.getGroupIncome - 收入类型: {}", incomeType);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 查询店铺信息
|
|
|
+ StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
|
|
|
+ if (storeInfo == null) {
|
|
|
+ log.warn("IncomeManageServiceImpl.getGroupIncome - 门店不存在: storeId={}", storeId);
|
|
|
+ throw new RuntimeException("门店不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 查询收入明细记录
|
|
|
+ List<StoreIncomeDetailsRecord> list = this.list(wrapper);
|
|
|
+ log.info("IncomeManageServiceImpl.getGroupIncome - 查询到收入记录数: {}", list.size());
|
|
|
+
|
|
|
+ // 5. 计算总收入和手续费
|
|
|
+ Integer allIncome = 0;
|
|
|
+ Integer commission = 0;
|
|
|
+ for (StoreIncomeDetailsRecord storeIncomeDetailsRecord : list) {
|
|
|
+ allIncome += storeIncomeDetailsRecord.getMoney();
|
|
|
+ commission += storeIncomeDetailsRecord.getCommission();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 构建返回VO
|
|
|
+ StoreIncomeDetailsRecordVo vo = new StoreIncomeDetailsRecordVo();
|
|
|
+ vo.setCommissionRate(storeInfo.getCommissionRate());
|
|
|
+ vo.setDate(date);
|
|
|
+ vo.setStoreName(storeInfo.getStoreName());
|
|
|
+ vo.setStoreId(storeId);
|
|
|
+
|
|
|
+ // 7. 格式化金额信息
|
|
|
+ // 售价(收入+手续费)
|
|
|
+ vo.setIncomeMoney(
|
|
|
+ new BigDecimal(allIncome)
|
|
|
+ .add(new BigDecimal(commission))
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
|
|
|
+ .toString()
|
|
|
+ );
|
|
|
+
|
|
|
+ // 手续费
|
|
|
+ vo.setCommissionStr(
|
|
|
+ new BigDecimal(commission)
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
|
|
|
+ .toString()
|
|
|
+ );
|
|
|
+
|
|
|
+ // 收益(售价-手续费,存入时已经减完了)
|
|
|
+ vo.setNoYetPaymentMoney(
|
|
|
+ new BigDecimal(allIncome)
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
|
|
|
+ .toString()
|
|
|
+ );
|
|
|
+
|
|
|
+ // 8. 查询明细记录列表(含退款标识)
|
|
|
+ QueryWrapper<StoreIncomeDetailsRecordVo> incomeWrapper = new QueryWrapper<>();
|
|
|
+ incomeWrapper.eq("income.delete_flag", 0)
|
|
|
+ .eq("income.store_id", storeId)
|
|
|
+ .between("income.created_time", date + " 00:00:00", date + " 23:59:59")
|
|
|
+ .orderByDesc("income.created_time");
|
|
|
+
|
|
|
+ if (null != incomeType) {
|
|
|
+ if (0 == incomeType) {
|
|
|
+ incomeWrapper.in("income_type",
|
|
|
+ CouponTypeEnum.COUPON.getCode(), CouponTypeEnum.GROUP_BUY.getCode());
|
|
|
+ } else {
|
|
|
+ incomeWrapper.eq("income_type", incomeType);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ List<StoreIncomeDetailsRecordVo> incomeDetailsRecordVoList =
|
|
|
+ storeIncomeDetailsRecordMapper.getIncomeList(incomeWrapper);
|
|
|
+
|
|
|
+ // 9. 按退款类型分组
|
|
|
+ Map<String, List<StoreIncomeDetailsRecordVo>> collect =
|
|
|
+ incomeDetailsRecordVoList.stream()
|
|
|
+ .collect(Collectors.groupingBy(x -> x.getRefundType()));
|
|
|
+
|
|
|
+ // 10. 设置明细列表(不含退款)和统计信息
|
|
|
+ vo.setIncomeDetailsRecordVoList(new ArrayList<>());
|
|
|
+ vo.setRefundMoney(new BigDecimal(0));
|
|
|
+ vo.setCouponCount(0);
|
|
|
+
|
|
|
+ // 正常收入记录(未退款)
|
|
|
+ if (collect.containsKey("false")) {
|
|
|
+ vo.setIncomeDetailsRecordVoList(
|
|
|
+ ListToPage.setPage(incomeDetailsRecordVoList, page, size).getRecords()
|
|
|
+ );
|
|
|
+ // 计算券数量(正常订单 - 退款订单)
|
|
|
+ vo.setCouponCount(
|
|
|
+ collect.get("false").size() -
|
|
|
+ (collect.containsKey("true") ? collect.get("true").size() : 0)
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // 退款金额统计
|
|
|
+ if (collect.containsKey("true")) {
|
|
|
+ vo.setRefundMoney(
|
|
|
+ new BigDecimal(
|
|
|
+ collect.get("true").stream()
|
|
|
+ .mapToInt(StoreIncomeDetailsRecordVo::getMoney)
|
|
|
+ .sum()
|
|
|
+ ).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("IncomeManageServiceImpl.getGroupIncome - 查询完成: 总收入={}元, 手续费={}元, 券数量={}",
|
|
|
+ vo.getNoYetPaymentMoney(), vo.getCommissionStr(), vo.getCouponCount());
|
|
|
+
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 快速提现申请(免审核,直接通过状态)
|
|
|
+ * 与app端applyCashOut逻辑保持一致
|
|
|
+ *
|
|
|
+ * @param storeId 门店ID
|
|
|
+ * @param payPassword 支付密码
|
|
|
+ * @param withdrawalMoney 提现金额(单位:分)
|
|
|
+ * @return 提现结果
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ @Override
|
|
|
+ public R<?> applyFastCashOut(Integer storeId, String payPassword, Integer withdrawalMoney) {
|
|
|
+ log.info("IncomeManageServiceImpl.applyFastCashOut - 开始处理快速提现: storeId={}, withdrawalMoney={}分",
|
|
|
+ storeId, withdrawalMoney);
|
|
|
+
|
|
|
+ // 1. 验证支付密码并查询用户信息
|
|
|
+ LambdaQueryWrapper<StoreUser> userWrapper = new LambdaQueryWrapper<>();
|
|
|
+ userWrapper.eq(StoreUser::getStoreId, storeId)
|
|
|
+ .eq(StoreUser::getPayPassword, payPassword);
|
|
|
+ StoreUser storeUser = storeUserMapper.selectOne(userWrapper);
|
|
|
+
|
|
|
+ if (storeUser == null) {
|
|
|
+ log.warn("IncomeManageServiceImpl.applyFastCashOut - 支付密码错误: storeId={}", storeId);
|
|
|
+ return R.fail("支付密码错误");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 验证账户余额是否充足
|
|
|
+ if (storeUser.getMoney() <= withdrawalMoney) {
|
|
|
+ log.warn("IncomeManageServiceImpl.applyFastCashOut - 余额不足: 账户余额={}分, 提现金额={}分",
|
|
|
+ storeUser.getMoney(), withdrawalMoney);
|
|
|
+ return R.fail("余额不足");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 验证提现金额是否满足最低要求(0.1元 = 10分)
|
|
|
+ BigDecimal amountInYuan = new BigDecimal(withdrawalMoney)
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.DOWN);
|
|
|
+ if (amountInYuan.compareTo(new BigDecimal("0.1")) < 0) {
|
|
|
+ log.warn("IncomeManageServiceImpl.applyFastCashOut - 提现金额过小: {}元", amountInYuan);
|
|
|
+ return R.fail("金额不能小于0.1元");
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("IncomeManageServiceImpl.applyFastCashOut - 验证通过: 账户余额={}分, 提现金额={}元",
|
|
|
+ storeUser.getMoney(), amountInYuan);
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 4. 创建提现记录(已通过状态,与app端保持一致)
|
|
|
+ StoreCashOutRecord storeCashOutRecord = new StoreCashOutRecord();
|
|
|
+ storeCashOutRecord.setStoreId(storeId);
|
|
|
+ storeCashOutRecord.setMoney(withdrawalMoney);
|
|
|
+ storeCashOutRecord.setCashOutType(0); // 0-全部提现
|
|
|
+ storeCashOutRecord.setPaymentStatus(3); // 3-已通过(与app端保持一致)
|
|
|
+ storeCashOutRecord.setDeleteFlag(0);
|
|
|
+ storeCashOutRecord.setIncomeStartTime(new Date());
|
|
|
+ storeCashOutRecord.setIncomeEndTime(new Date());
|
|
|
+ storeCashOutRecord.setStoreUserId(storeUser.getId());
|
|
|
+
|
|
|
+ // 5. 插入提现记录
|
|
|
+ int insertResult = storeCashOutRecordMapper.insert(storeCashOutRecord);
|
|
|
+ if (insertResult <= 0) {
|
|
|
+ log.error("IncomeManageServiceImpl.applyFastCashOut - 创建提现记录失败");
|
|
|
+ throw new RuntimeException("创建提现记录失败");
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("IncomeManageServiceImpl.applyFastCashOut - 快速提现申请成功: 提现记录ID={}, 提现金额={}元",
|
|
|
+ storeCashOutRecord.getId(), amountInYuan);
|
|
|
+
|
|
|
+ // 6. 返回结果(返回StoreCashOutRecord对象,与app端保持一致)
|
|
|
+ return R.data(storeCashOutRecord);
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("IncomeManageServiceImpl.applyFastCashOut - 快速提现失败: {}", e.getMessage(), e);
|
|
|
+
|
|
|
+ // 如果是已知的运行时异常,直接抛出让事务回滚
|
|
|
+ if (e instanceof RuntimeException) {
|
|
|
+ throw (RuntimeException) e;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 其他异常也需要回滚
|
|
|
+ throw new RuntimeException("快速提现失败:" + e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 已入账/未入账详情查询
|
|
|
+ *
|
|
|
+ * @param storeId 门店ID
|
|
|
+ * @param incomeType 收入类型, 0:主页, 1:优惠券, 2:代金券, 3:套餐, 4:联名卡
|
|
|
+ * @param paymentType 账期类型,0:未入账,1:已入账
|
|
|
+ * @param startTime 开始时间
|
|
|
+ * @param endTime 结束时间
|
|
|
+ * @param page 页数
|
|
|
+ * @param size 条数
|
|
|
+ * @return 入账详情
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public com.alibaba.fastjson2.JSONObject getPaymentDetails(Integer storeId, Integer incomeType, Integer paymentType,
|
|
|
+ String startTime, String endTime, int page, int size) {
|
|
|
+ log.info("IncomeManageServiceImpl.getPaymentDetails - 查询入账详情: storeId={}, incomeType={}, paymentType={}, startTime={}, endTime={}, page={}, size={}",
|
|
|
+ storeId, incomeType, paymentType, startTime, endTime, page, size);
|
|
|
+
|
|
|
+ QueryWrapper<StoreIncomeDetailsRecord> wrapper = new QueryWrapper<>();
|
|
|
+ com.alibaba.fastjson2.JSONObject result = new com.alibaba.fastjson2.JSONObject();
|
|
|
+ Date now = new Date();
|
|
|
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
+
|
|
|
+ // 根据 paymentType 设置时间范围和条件
|
|
|
+ if (paymentType == 0) {
|
|
|
+ // 未入账: 当前时间-3天内的记录
|
|
|
+ wrapper.isNull("sidr.cash_out_id");
|
|
|
+ wrapper.gt("sidr.created_time", DateUtils.calcDays(new Date(), -3));
|
|
|
+ Date startDate = DateUtils.calcDays(now, -3);
|
|
|
+ result.put("date", df.format(startDate) + " ~ " + df.format(now));
|
|
|
+ log.info("IncomeManageServiceImpl.getPaymentDetails - 未入账时间范围: {} ~ {}", df.format(startDate), df.format(now));
|
|
|
+ } else {
|
|
|
+ // 已入账: 当前时间-4~27天的记录
|
|
|
+ wrapper.between("sidr.created_time", DateUtils.calcDays(new Date(), -27), DateUtils.calcDays(new Date(), -4));
|
|
|
+ Date startDate = DateUtils.calcDays(now, -27);
|
|
|
+ Date endDate = DateUtils.calcDays(now, -4);
|
|
|
+ result.put("date", df.format(startDate) + " ~ " + df.format(endDate));
|
|
|
+ log.info("IncomeManageServiceImpl.getPaymentDetails - 已入账时间范围: {} ~ {}", df.format(startDate), df.format(endDate));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 店铺ID条件
|
|
|
+ if (storeId != null) {
|
|
|
+ wrapper.eq("sidr.store_id", storeId);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 自定义时间范围(用户输入的时间范围)
|
|
|
+ LocalDate startDate = LocalDate.parse(startTime);
|
|
|
+ LocalDate endDate = LocalDate.parse(endTime);
|
|
|
+ LocalDateTime startOfDay = startDate.atStartOfDay();
|
|
|
+ LocalDateTime endOfDay = endDate.atTime(LocalTime.MAX);
|
|
|
+ wrapper.between("sidr.created_time", startOfDay, endOfDay)
|
|
|
+ .orderByDesc("sidr.created_time");
|
|
|
+
|
|
|
+ // 收入类型条件
|
|
|
+ if (null != incomeType) {
|
|
|
+ if (0 == incomeType) {
|
|
|
+ // 主页: 包含优惠券和团购券
|
|
|
+ wrapper.in("sidr.income_type", CouponTypeEnum.COUPON.getCode(), CouponTypeEnum.GROUP_BUY.getCode());
|
|
|
+ } else {
|
|
|
+ wrapper.eq("sidr.income_type", incomeType);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询收入明细列表
|
|
|
+ List<StoreIncomeDetailsRecordVo> list = storeIncomeDetailsRecordMapper.selectRecordList(wrapper);
|
|
|
+ log.info("IncomeManageServiceImpl.getPaymentDetails - 查询到记录数: {}", list.size());
|
|
|
+
|
|
|
+ // 查询店铺信息
|
|
|
+ StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
|
|
|
+
|
|
|
+ // 处理每条记录的数据格式
|
|
|
+ for (StoreIncomeDetailsRecordVo record : list) {
|
|
|
+ // 空值检查
|
|
|
+ if (record == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ StoreIncomeDetailsRecordVo vo = new StoreIncomeDetailsRecordVo();
|
|
|
+ BeanUtils.copyProperties(record, vo);
|
|
|
+
|
|
|
+ // 转换金额格式(分转元,保留2位小数)
|
|
|
+ record.setMoneyStr(new BigDecimal(vo.getMoney())
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString());
|
|
|
+
|
|
|
+ // 设置日期
|
|
|
+ String format = df.format(record.getCreatedTime());
|
|
|
+ record.setDate(format);
|
|
|
+
|
|
|
+ // 设置抽成比例
|
|
|
+ record.setCommissionRate(storeInfo.getCommissionRate());
|
|
|
+
|
|
|
+ // 计算售价(收益 + 手续费)
|
|
|
+ record.setIncomeMoney(new BigDecimal(vo.getMoney())
|
|
|
+ .add(new BigDecimal(vo.getCommission()))
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString());
|
|
|
+
|
|
|
+ // 设置手续费
|
|
|
+ record.setCommissionStr(new BigDecimal(vo.getCommission())
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString());
|
|
|
+
|
|
|
+ vo.setCommissionRate(storeInfo.getCommissionRate());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 分页处理
|
|
|
+ result.put("data", ListToPage.setPage(list, page, size));
|
|
|
+
|
|
|
+ // 计算总金额(元)
|
|
|
+ BigDecimal totalMoney = new BigDecimal(list.stream()
|
|
|
+ .mapToInt(StoreIncomeDetailsRecord::getMoney)
|
|
|
+ .sum())
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
|
|
|
+ result.put("money", totalMoney.toString());
|
|
|
+
|
|
|
+ log.info("IncomeManageServiceImpl.getPaymentDetails - 查询完成: 总金额={}元, 记录数={}",
|
|
|
+ totalMoney, list.size());
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 收入汇总统计
|
|
|
+ * 整合四项关键数据:可提现金额、今日收益、已入账、未入账
|
|
|
+ *
|
|
|
+ * @param storeId 门店ID
|
|
|
+ * @return 收入汇总统计数据
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public Map<String, Object> getIncomeSummary(Integer storeId) {
|
|
|
+ log.info("IncomeManageServiceImpl.getIncomeSummary - 开始查询收入汇总: storeId={}", storeId);
|
|
|
+
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+
|
|
|
+ try {
|
|
|
+ // ==================== 1. 账户余额 & 可提现金额 ====================
|
|
|
+ calculateAccountBalance(storeId, result);
|
|
|
+
|
|
|
+ // ==================== 2. 今日收益 ====================
|
|
|
+ calculateTodayIncome(storeId, result);
|
|
|
+
|
|
|
+ // ==================== 3. 未入账统计(近3天) ====================
|
|
|
+ calculateNotPaidIncome(storeId, result);
|
|
|
+
|
|
|
+ // ==================== 4. 已入账统计(4-27天) ====================
|
|
|
+ calculatePaidIncome(storeId, result);
|
|
|
+
|
|
|
+ log.info("IncomeManageServiceImpl.getIncomeSummary - 查询完成: result={}", result);
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("IncomeManageServiceImpl.getIncomeSummary - 查询失败: storeId={}, error={}",
|
|
|
+ storeId, e.getMessage(), e);
|
|
|
+ throw new RuntimeException("查询收入汇总失败:" + e.getMessage(), e);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算账户余额和可提现金额
|
|
|
+ *
|
|
|
+ * 业务逻辑:
|
|
|
+ * 1. 账户余额 = 用户账户中的总余额(store_user.money)
|
|
|
+ * 2. 可提现金额 = 4-27天内未绑定提现的收入 - 待审核/已通过的提现金额
|
|
|
+ *
|
|
|
+ * @param storeId 门店ID
|
|
|
+ * @param result 结果集
|
|
|
+ */
|
|
|
+ private void calculateAccountBalance(Integer storeId, Map<String, Object> result) {
|
|
|
+ log.info("IncomeManageServiceImpl.calculateAccountBalance - 开始计算账户余额和可提现金额");
|
|
|
+
|
|
|
+ // ---------- 1.1 查询账户总余额 ----------
|
|
|
+ LambdaQueryWrapper<StoreUser> userWrapper = new LambdaQueryWrapper<>();
|
|
|
+ userWrapper.eq(StoreUser::getStoreId, storeId);
|
|
|
+ StoreUser storeUser = storeUserMapper.selectOne(userWrapper);
|
|
|
+
|
|
|
+ if (storeUser == null) {
|
|
|
+ log.warn("IncomeManageServiceImpl.calculateAccountBalance - 未找到店铺用户: storeId={}", storeId);
|
|
|
+ result.put("balance", "0.00");
|
|
|
+ result.put("cashOutMoney", "0.00");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 账户总余额(分转元)
|
|
|
+ String balance = new BigDecimal(storeUser.getMoney())
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
|
|
|
+ .toString();
|
|
|
+ result.put("balance", balance);
|
|
|
+ log.info("IncomeManageServiceImpl.calculateAccountBalance - 账户余额: {}元", balance);
|
|
|
+
|
|
|
+ // ---------- 1.2 查询4-27天内未绑定提现的收入 ----------
|
|
|
+ Date now = new Date();
|
|
|
+ Date startDate = DateUtils.calcDays(now, -27); // 27天前
|
|
|
+ Date endDate = DateUtils.calcDays(now, -4); // 4天前
|
|
|
+
|
|
|
+ LambdaQueryWrapper<StoreIncomeDetailsRecord> incomeWrapper = new LambdaQueryWrapper<>();
|
|
|
+ incomeWrapper.between(StoreIncomeDetailsRecord::getCreatedTime, startDate, endDate)
|
|
|
+ .isNull(StoreIncomeDetailsRecord::getCashOutId) // 未绑定提现记录
|
|
|
+ .eq(StoreIncomeDetailsRecord::getStoreId, storeId);
|
|
|
+
|
|
|
+ List<StoreIncomeDetailsRecord> incomeList = this.list(incomeWrapper);
|
|
|
+ int availableIncome = incomeList.stream()
|
|
|
+ .mapToInt(StoreIncomeDetailsRecord::getMoney)
|
|
|
+ .sum();
|
|
|
+
|
|
|
+ log.info("IncomeManageServiceImpl.calculateAccountBalance - 4-27天内可提现收入: {}分, 记录数: {}",
|
|
|
+ availableIncome, incomeList.size());
|
|
|
+
|
|
|
+ // ---------- 1.3 查询待审核和已通过的提现金额 ----------
|
|
|
+ QueryWrapper<StoreCashOutRecord> cashOutWrapper = new QueryWrapper<>();
|
|
|
+ cashOutWrapper.eq("store_id", storeId)
|
|
|
+ .in("payment_status", "1", "3") // 1-待审核, 3-已通过
|
|
|
+ .eq("delete_flag", "0");
|
|
|
+
|
|
|
+ List<StoreCashOutRecord> cashOutRecords = storeCashOutRecordMapper.selectList(cashOutWrapper);
|
|
|
+ int pendingCashOut = cashOutRecords.stream()
|
|
|
+ .collect(Collectors.summingInt(StoreCashOutRecord::getMoney));
|
|
|
+
|
|
|
+ log.info("IncomeManageServiceImpl.calculateAccountBalance - 待审核/已通过提现: {}分, 记录数: {}",
|
|
|
+ pendingCashOut, cashOutRecords.size());
|
|
|
+
|
|
|
+ // ---------- 1.4 计算可提现金额 ----------
|
|
|
+ // 可提现金额 = 可提现收入 - 审核中的提现
|
|
|
+ int cashOutMoney = availableIncome - pendingCashOut;
|
|
|
+ String cashOutMoneyStr = new BigDecimal(Math.max(cashOutMoney, 0)) // 确保不为负数
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
|
|
|
+ .toString();
|
|
|
+ result.put("cashOutMoney", cashOutMoneyStr);
|
|
|
+
|
|
|
+ log.info("IncomeManageServiceImpl.calculateAccountBalance - 可提现金额: {}元", cashOutMoneyStr);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算今日收益
|
|
|
+ *
|
|
|
+ * 业务逻辑:
|
|
|
+ * 统计当天00:00:00 ~ 23:59:59所有收入的总和
|
|
|
+ *
|
|
|
+ * @param storeId 门店ID
|
|
|
+ * @param result 结果集
|
|
|
+ */
|
|
|
+ private void calculateTodayIncome(Integer storeId, Map<String, Object> result) {
|
|
|
+ log.info("IncomeManageServiceImpl.calculateTodayIncome - 开始计算今日收益");
|
|
|
+
|
|
|
+ // ---------- 2.1 获取今天的日期范围 ----------
|
|
|
+ Date now = new Date();
|
|
|
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
+ String today = df.format(now);
|
|
|
+ String startTime = today + " 00:00:00";
|
|
|
+ String endTime = today + " 23:59:59";
|
|
|
+
|
|
|
+ // ---------- 2.2 查询今天的收入记录 ----------
|
|
|
+ LambdaQueryWrapper<StoreIncomeDetailsRecord> todayWrapper = new LambdaQueryWrapper<>();
|
|
|
+ todayWrapper.between(StoreIncomeDetailsRecord::getCreatedTime, startTime, endTime)
|
|
|
+ .eq(StoreIncomeDetailsRecord::getStoreId, storeId);
|
|
|
+
|
|
|
+ List<StoreIncomeDetailsRecord> todayList = this.list(todayWrapper);
|
|
|
+
|
|
|
+ // ---------- 2.3 汇总今日收益 ----------
|
|
|
+ int todayIncome = todayList.stream()
|
|
|
+ .mapToInt(StoreIncomeDetailsRecord::getMoney)
|
|
|
+ .sum();
|
|
|
+
|
|
|
+ String todayIncomeStr = new BigDecimal(todayIncome)
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
|
|
|
+ .toString();
|
|
|
+ result.put("todayIncome", todayIncomeStr);
|
|
|
+
|
|
|
+ log.info("IncomeManageServiceImpl.calculateTodayIncome - 今日收益: {}元, 记录数: {}",
|
|
|
+ todayIncomeStr, todayList.size());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算未入账统计(近3天)
|
|
|
+ *
|
|
|
+ * 业务逻辑:
|
|
|
+ * 1. 时间范围:当前时间 - 3天 ~ 当前时间
|
|
|
+ * 2. 条件:未绑定提现记录(cash_out_id IS NULL)
|
|
|
+ * 3. 说明:这部分收入还未到提现账期,不能提现
|
|
|
+ *
|
|
|
+ * @param storeId 门店ID
|
|
|
+ * @param result 结果集
|
|
|
+ */
|
|
|
+ private void calculateNotPaidIncome(Integer storeId, Map<String, Object> result) {
|
|
|
+ log.info("IncomeManageServiceImpl.calculateNotPaidIncome - 开始计算未入账统计");
|
|
|
+
|
|
|
+ Date now = new Date();
|
|
|
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
+
|
|
|
+ // ---------- 3.1 计算时间范围 ----------
|
|
|
+ Date notPaidStartDate = DateUtils.calcDays(now, -3); // 3天前
|
|
|
+ String notPaidDateRange = df.format(notPaidStartDate) + " ~ " + df.format(now);
|
|
|
+ result.put("notPaidDateRange", notPaidDateRange);
|
|
|
+
|
|
|
+ log.info("IncomeManageServiceImpl.calculateNotPaidIncome - 未入账时间范围: {}", notPaidDateRange);
|
|
|
+
|
|
|
+ // ---------- 3.2 查询未入账收入(近3天,未绑定提现) ----------
|
|
|
+ QueryWrapper<StoreIncomeDetailsRecord> notPaidWrapper = new QueryWrapper<>();
|
|
|
+ notPaidWrapper.isNull("sidr.cash_out_id") // 未绑定提现记录
|
|
|
+ .gt("sidr.created_time", notPaidStartDate) // 大于3天前
|
|
|
+ .eq("sidr.store_id", storeId);
|
|
|
+
|
|
|
+ List<StoreIncomeDetailsRecordVo> notPaidList = storeIncomeDetailsRecordMapper.selectRecordList(notPaidWrapper);
|
|
|
+
|
|
|
+ // ---------- 3.3 汇总未入账金额和数量 ----------
|
|
|
+ int notPaidMoney = notPaidList.stream()
|
|
|
+ .mapToInt(StoreIncomeDetailsRecord::getMoney)
|
|
|
+ .sum();
|
|
|
+
|
|
|
+ String notPaidMoneyStr = new BigDecimal(notPaidMoney)
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
|
|
|
+ .toString();
|
|
|
+
|
|
|
+ result.put("notPaidMoney", notPaidMoneyStr);
|
|
|
+ result.put("notPaidCount", notPaidList.size());
|
|
|
+
|
|
|
+ log.info("IncomeManageServiceImpl.calculateNotPaidIncome - 未入账: {}元, 记录数: {}",
|
|
|
+ notPaidMoneyStr, notPaidList.size());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算已入账统计(4-27天)
|
|
|
+ *
|
|
|
+ * 业务逻辑:
|
|
|
+ * 1. 时间范围:当前时间 - 27天 ~ 当前时间 - 4天
|
|
|
+ * 2. 说明:这部分收入已到提现账期,可以申请提现
|
|
|
+ *
|
|
|
+ * @param storeId 门店ID
|
|
|
+ * @param result 结果集
|
|
|
+ */
|
|
|
+ private void calculatePaidIncome(Integer storeId, Map<String, Object> result) {
|
|
|
+ log.info("IncomeManageServiceImpl.calculatePaidIncome - 开始计算已入账统计");
|
|
|
+
|
|
|
+ Date now = new Date();
|
|
|
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
+
|
|
|
+ // ---------- 4.1 计算时间范围 ----------
|
|
|
+ Date paidStartDate = DateUtils.calcDays(now, -27); // 27天前
|
|
|
+ Date paidEndDate = DateUtils.calcDays(now, -4); // 4天前
|
|
|
+ String paidDateRange = df.format(paidStartDate) + " ~ " + df.format(paidEndDate);
|
|
|
+ result.put("paidDateRange", paidDateRange);
|
|
|
+
|
|
|
+ log.info("IncomeManageServiceImpl.calculatePaidIncome - 已入账时间范围: {}", paidDateRange);
|
|
|
+
|
|
|
+ // ---------- 4.2 查询已入账收入(4-27天前) ----------
|
|
|
+ QueryWrapper<StoreIncomeDetailsRecord> paidWrapper = new QueryWrapper<>();
|
|
|
+ paidWrapper.between("sidr.created_time", paidStartDate, paidEndDate)
|
|
|
+ .eq("sidr.store_id", storeId);
|
|
|
+
|
|
|
+ List<StoreIncomeDetailsRecordVo> paidList = storeIncomeDetailsRecordMapper.selectRecordList(paidWrapper);
|
|
|
+
|
|
|
+ // ---------- 4.3 汇总已入账金额和数量 ----------
|
|
|
+ int paidMoney = paidList.stream()
|
|
|
+ .mapToInt(StoreIncomeDetailsRecord::getMoney)
|
|
|
+ .sum();
|
|
|
+
|
|
|
+ String paidMoneyStr = new BigDecimal(paidMoney)
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
|
|
|
+ .toString();
|
|
|
+
|
|
|
+ result.put("paidMoney", paidMoneyStr);
|
|
|
+ result.put("paidCount", paidList.size());
|
|
|
+
|
|
|
+ log.info("IncomeManageServiceImpl.calculatePaidIncome - 已入账: {}元, 记录数: {}",
|
|
|
+ paidMoneyStr, paidList.size());
|
|
|
+ }
|
|
|
+}
|
|
|
+
|