Compare commits

...

8 Commits

22 changed files with 536 additions and 71 deletions

View File

@@ -1222,6 +1222,26 @@ public class BuyOrderController {
return buyOrderService.delivery(expressCompanyCode, buyOrderProductId); return buyOrderService.delivery(expressCompanyCode, buyOrderProductId);
} }
/**
* 批量订单发货仅支持单商品且数量为1的订单快递公司固定顺丰 SF
* 异步执行,立即返回 taskId通过 getBatchDeliveryProgress 查询进度
*
* @param orderIds 订单 ID 列表
* @return R包含 taskId存在不符合条件的订单时 msg 为逗号拼接的订单号
*/
@RequestMapping(value = "/batchDelivery", method = RequestMethod.POST)
public R batchDelivery(@RequestBody List<Integer> orderIds) throws Exception {
return buyOrderService.batchDelivery("SF", orderIds);
}
/**
* 查询批量发货任务进度
*/
@RequestMapping(value = "/getBatchDeliveryProgress", method = RequestMethod.GET)
public R getBatchDeliveryProgress(@RequestParam("taskId") Integer taskId) {
return buyOrderService.getBatchDeliveryProgress(taskId);
}
@RequestMapping("/mytest") @RequestMapping("/mytest")
public R mytest() throws IOException { public R mytest() throws IOException {
String mytest = buyOrderService.mytest(); String mytest = buyOrderService.mytest();

View File

@@ -55,6 +55,19 @@ public interface BuyOrderService extends IService<BuyOrder> {
*/ */
R delivery(String expressCompanyCode, List<Integer> buyOrderDetailId); R delivery(String expressCompanyCode, List<Integer> buyOrderDetailId);
/**
* 批量订单发货仅支持单商品且数量为1的订单
*
* @param expressCompanyCode 快递公司代码
* @param orderIds 订单 ID 列表
*/
R batchDelivery(String expressCompanyCode, List<Integer> orderIds);
/**
* 查询批量发货任务进度
*/
R getBatchDeliveryProgress(Integer taskId);
Page<BuyOrder> orderList(BuyOrderListRequestVo requestVo, Boolean isHT); Page<BuyOrder> orderList(BuyOrderListRequestVo requestVo, Boolean isHT);
Page<BuyOrder> getUserOrderList(UserOrderDto userOrderDto); Page<BuyOrder> getUserOrderList(UserOrderDto userOrderDto);

View File

@@ -0,0 +1,118 @@
package com.peanut.modules.book.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.peanut.common.utils.R;
import com.peanut.modules.book.service.BuyOrderBatchDeliveryAsyncService;
import com.peanut.modules.book.service.BuyOrderService;
import com.peanut.modules.common.dao.BuyOrderBatchDeliveryItemDao;
import com.peanut.modules.common.dao.BuyOrderBatchDeliveryTaskDao;
import com.peanut.modules.common.entity.BuyOrderBatchDeliveryItem;
import com.peanut.modules.common.entity.BuyOrderBatchDeliveryTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@Slf4j
@Service
public class BuyOrderBatchDeliveryAsyncServiceImpl implements BuyOrderBatchDeliveryAsyncService {
@Autowired
private BuyOrderBatchDeliveryTaskDao batchDeliveryTaskDao;
@Autowired
private BuyOrderBatchDeliveryItemDao batchDeliveryItemDao;
@Lazy
@Autowired
private BuyOrderService buyOrderService;
@Async
@Override
public void executeBatchDelivery(Integer taskId) {
BuyOrderBatchDeliveryTask task = batchDeliveryTaskDao.selectById(taskId);
if (task == null) {
return;
}
Date now = new Date();
task.setStatus(BuyOrderBatchDeliveryTask.STATUS_RUNNING);
task.setUpdateTime(now);
batchDeliveryTaskDao.updateById(task);
List<BuyOrderBatchDeliveryItem> items = batchDeliveryItemDao.selectList(
new LambdaQueryWrapper<BuyOrderBatchDeliveryItem>()
.eq(BuyOrderBatchDeliveryItem::getTaskId, taskId)
.orderByAsc(BuyOrderBatchDeliveryItem::getId));
int successCount = 0;
int failCount = 0;
try {
for (BuyOrderBatchDeliveryItem item : items) {
if (item.getStatus() != BuyOrderBatchDeliveryItem.STATUS_PENDING) {
continue;
}
try {
R result = buyOrderService.delivery(
task.getExpressCompanyCode(),
Collections.singletonList(item.getBuyOrderProductId()));
Date itemTime = new Date();
if (Integer.valueOf(0).equals(result.get("code"))) {
item.setStatus(BuyOrderBatchDeliveryItem.STATUS_SUCCESS);
successCount++;
} else {
item.setStatus(BuyOrderBatchDeliveryItem.STATUS_FAILED);
item.setFailMessage(String.valueOf(result.get("msg")));
failCount++;
}
item.setUpdateTime(itemTime);
batchDeliveryItemDao.updateById(item);
} catch (Exception e) {
log.error("批量发货单条失败 taskId={} orderId={}", taskId, item.getOrderId(), e);
item.setStatus(BuyOrderBatchDeliveryItem.STATUS_FAILED);
item.setFailMessage(e.getMessage());
item.setUpdateTime(new Date());
batchDeliveryItemDao.updateById(item);
failCount++;
}
updateTaskProgress(taskId, successCount, failCount);
}
finishTask(taskId, successCount, failCount, null);
} catch (Exception e) {
log.error("批量发货任务异常 taskId={}", taskId, e);
finishTask(taskId, successCount, failCount, e.getMessage());
}
}
private void updateTaskProgress(Integer taskId, int successCount, int failCount) {
BuyOrderBatchDeliveryTask task = batchDeliveryTaskDao.selectById(taskId);
if (task == null) {
return;
}
task.setSuccessCount(successCount);
task.setFailCount(failCount);
task.setUpdateTime(new Date());
batchDeliveryTaskDao.updateById(task);
}
private void finishTask(Integer taskId, int successCount, int failCount, String failMessage) {
BuyOrderBatchDeliveryTask task = batchDeliveryTaskDao.selectById(taskId);
if (task == null) {
return;
}
Date now = new Date();
task.setSuccessCount(successCount);
task.setFailCount(failCount);
task.setUpdateTime(now);
task.setFinishTime(now);
if (failMessage != null) {
task.setStatus(BuyOrderBatchDeliveryTask.STATUS_FAILED);
task.setFailMessage(failMessage);
} else {
task.setStatus(BuyOrderBatchDeliveryTask.STATUS_COMPLETED);
}
batchDeliveryTaskDao.updateById(task);
}
}

View File

@@ -11,6 +11,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper; import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.peanut.common.utils.*; import com.peanut.common.utils.*;
import com.peanut.config.Constants; import com.peanut.config.Constants;
import com.peanut.modules.book.service.BuyOrderBatchDeliveryAsyncService;
import com.peanut.modules.book.service.BuyOrderService; import com.peanut.modules.book.service.BuyOrderService;
import com.peanut.modules.book.service.CityService; import com.peanut.modules.book.service.CityService;
import com.peanut.modules.book.service.CountyService; import com.peanut.modules.book.service.CountyService;
@@ -118,8 +119,14 @@ public class BuyOrderServiceImpl extends ServiceImpl<BuyOrderDao, BuyOrder> impl
private UserVipDao userVipDao; private UserVipDao userVipDao;
@Autowired @Autowired
private VipBuyConfigDao vipBuyConfigDao; private VipBuyConfigDao vipBuyConfigDao;
@Autowired
private BuyOrderBatchDeliveryTaskDao batchDeliveryTaskDao;
@Autowired
private BuyOrderBatchDeliveryItemDao batchDeliveryItemDao;
@Autowired
private BuyOrderBatchDeliveryAsyncService batchDeliveryAsyncService;
// TODO 新版本上线后删除 //private static final int BATCH_DELIVERY_MAX_SIZE = 100;
@Override @Override
public PageUtils list(Map<String, Object> params) throws Exception { public PageUtils list(Map<String, Object> params) throws Exception {
@@ -437,6 +444,22 @@ public class BuyOrderServiceImpl extends ServiceImpl<BuyOrderDao, BuyOrder> impl
List<Integer> collect = buyOrderProductDao.selectJoinList(BuyOrderProduct.class, buyOrderProductMPJLambdaWrapper).stream().map(BuyOrderProduct::getOrderId).collect(Collectors.toList()); List<Integer> collect = buyOrderProductDao.selectJoinList(BuyOrderProduct.class, buyOrderProductMPJLambdaWrapper).stream().map(BuyOrderProduct::getOrderId).collect(Collectors.toList());
wrapper.in(BuyOrder::getOrderId,collect); wrapper.in(BuyOrder::getOrderId,collect);
} }
if(requestVo.getShowOne()!=null && requestVo.getShowOne()==1){
List<Map<String, Object>> list = buyOrderProductService.listMaps(
new QueryWrapper<BuyOrderProduct>()
.select("order_id orderId")
.groupBy("order_id")
.having("COUNT(*) = 1 AND SUM(quantity) = 1")
);
List<Integer> collect = list.stream()
.map(map -> ((Number) map.get("orderId")).intValue())
.collect(Collectors.toList());
if (collect.size() > 0) {
wrapper.in(BuyOrder::getOrderId, collect);
} else {
wrapper.eq(BuyOrder::getOrderId, -1);
}
}
Page<BuyOrder> buyOrderPage = getBaseMapper().selectPage(new Page<BuyOrder>(requestVo.getPageIndex(), requestVo.getPageSize()), wrapper); Page<BuyOrder> buyOrderPage = getBaseMapper().selectPage(new Page<BuyOrder>(requestVo.getPageIndex(), requestVo.getPageSize()), wrapper);
//丰富订单内容 //丰富订单内容
for (BuyOrder b : buyOrderPage.getRecords()){ for (BuyOrder b : buyOrderPage.getRecords()){
@@ -487,6 +510,8 @@ public class BuyOrderServiceImpl extends ServiceImpl<BuyOrderDao, BuyOrder> impl
refundableStatus = true; refundableStatus = true;
} }
} }
}else if("relearn".equals(b.getOrderType()) && paymentDateTime > timestamp-7*24*60*60*1000){
refundableStatus = true;
} }
if(b.getOrderStatus().equals(Constants.ORDER_STATUS_REFUND)){ if(b.getOrderStatus().equals(Constants.ORDER_STATUS_REFUND)){
BuyOrderRefund refundInfo = buyOrderRefundService.getRefundInfoByOrderId(b.getOrderId()); BuyOrderRefund refundInfo = buyOrderRefundService.getRefundInfoByOrderId(b.getOrderId());
@@ -718,6 +743,107 @@ public class BuyOrderServiceImpl extends ServiceImpl<BuyOrderDao, BuyOrder> impl
} }
} }
@Override
public R batchDelivery(String expressCompanyCode, List<Integer> orderIds) {
if (orderIds == null || orderIds.isEmpty()) {
return R.error("订单列表不能为空");
}
// if (orderIds.size() > BATCH_DELIVERY_MAX_SIZE) {
// return R.error("单次批量发货最多" + BATCH_DELIVERY_MAX_SIZE + "单");
// }
List<Integer> distinctOrderIds = orderIds.stream().distinct().collect(Collectors.toList());
List<BatchDeliveryOrderItem> validOrders = new ArrayList<>();
List<String> invalidOrderSns = new ArrayList<>();
for (Integer orderId : distinctOrderIds) {
BuyOrder buyOrder = getById(orderId);
if (buyOrder == null) {
invalidOrderSns.add(String.valueOf(orderId));
continue;
}
List<BuyOrderProduct> products = buyOrderProductService.list(
new LambdaQueryWrapper<BuyOrderProduct>().eq(BuyOrderProduct::getOrderId, orderId));
if (products.size() != 1 || products.get(0).getQuantity() != 1) {
invalidOrderSns.add(buyOrder.getOrderSn());
} else {
BatchDeliveryOrderItem item = new BatchDeliveryOrderItem();
item.orderId = orderId;
item.orderSn = buyOrder.getOrderSn();
item.buyOrderProductId = products.get(0).getId();
validOrders.add(item);
}
}
if (!invalidOrderSns.isEmpty()) {
return R.error(String.join(",", invalidOrderSns));
}
List<Integer> busyOrderIds = batchDeliveryTaskDao.findBusyOrderIds(distinctOrderIds);
if (!busyOrderIds.isEmpty()) {
return R.error("以下订单正在批量发货处理中,请勿重复提交:" + busyOrderIds.stream()
.map(String::valueOf).collect(Collectors.joining(",")));
}
Date now = new Date();
BuyOrderBatchDeliveryTask task = new BuyOrderBatchDeliveryTask();
task.setExpressCompanyCode(expressCompanyCode);
task.setTotalCount(validOrders.size());
task.setSuccessCount(0);
task.setFailCount(0);
task.setStatus(BuyOrderBatchDeliveryTask.STATUS_PENDING);
task.setCreateTime(now);
task.setUpdateTime(now);
batchDeliveryTaskDao.insert(task);
for (BatchDeliveryOrderItem orderItem : validOrders) {
BuyOrderBatchDeliveryItem item = new BuyOrderBatchDeliveryItem();
item.setTaskId(task.getId());
item.setOrderId(orderItem.orderId);
item.setOrderSn(orderItem.orderSn);
item.setBuyOrderProductId(orderItem.buyOrderProductId);
item.setStatus(BuyOrderBatchDeliveryItem.STATUS_PENDING);
item.setCreateTime(now);
item.setUpdateTime(now);
batchDeliveryItemDao.insert(item);
}
batchDeliveryAsyncService.executeBatchDelivery(task.getId());
return R.ok()
.put("taskId", task.getId())
.put("totalCount", task.getTotalCount())
.put("message", "批量发货任务已提交,请轮询进度");
}
@Override
public R getBatchDeliveryProgress(Integer taskId) {
if (taskId == null) {
return R.error("任务ID不能为空");
}
BuyOrderBatchDeliveryTask task = batchDeliveryTaskDao.selectById(taskId);
if (task == null) {
return R.error("任务不存在");
}
List<BuyOrderBatchDeliveryItem> failedItems = batchDeliveryItemDao.selectList(
new LambdaQueryWrapper<BuyOrderBatchDeliveryItem>()
.eq(BuyOrderBatchDeliveryItem::getTaskId, taskId)
.eq(BuyOrderBatchDeliveryItem::getStatus, BuyOrderBatchDeliveryItem.STATUS_FAILED));
int processedCount = task.getSuccessCount() + task.getFailCount();
boolean finished = task.getStatus() >= BuyOrderBatchDeliveryTask.STATUS_COMPLETED;
return R.ok()
.put("taskId", task.getId())
.put("status", task.getStatus())
.put("totalCount", task.getTotalCount())
.put("successCount", task.getSuccessCount())
.put("failCount", task.getFailCount())
.put("processedCount", processedCount)
.put("finished", finished)
.put("failMessage", task.getFailMessage())
.put("failedItems", failedItems);
}
private static class BatchDeliveryOrderItem {
private Integer orderId;
private String orderSn;
private Integer buyOrderProductId;
}
public String mytest() throws IOException { public String mytest() throws IOException {
String html = ""; String html = "";
File file = new File("D:/1.html"); File file = new File("D:/1.html");

View File

@@ -55,4 +55,9 @@ public class BuyOrderListRequestVo {
* 数据起始位置 * 数据起始位置
*/ */
private Integer index; private Integer index;
/**
* 过滤预售
*/
private Integer showOne;
} }

View File

@@ -183,6 +183,7 @@ public class CourseRelearnController {
String timeId = IdWorker.getTimeId().substring(0, 32); String timeId = IdWorker.getTimeId().substring(0, 32);
buyOrder.setOrderSn(timeId); buyOrder.setOrderSn(timeId);
buyOrder.setUserId(uid); buyOrder.setUserId(uid);
buyOrder.setPaymentDate(new Date());
buyOrderService.save(buyOrder); buyOrderService.save(buyOrder);
BigDecimal totalPrice = buyOrder.getRealMoney(); BigDecimal totalPrice = buyOrder.getRealMoney();
//虚拟币支付 //虚拟币支付

View File

@@ -56,8 +56,8 @@ public class JfTransactionDetailsController {
@Transactional @Transactional
public R activityDonateJF(){ public R activityDonateJF(){
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
String startTime = "2025-11-08 00:00:00"; String startTime = "2026-06-18 00:00:00";
String endTime = "2025-11-13 09:22:00"; String endTime = "2026-06-22 10:06:59";
//查询时间段内所有充值的人 //查询时间段内所有充值的人
List<TransactionDetailsEntity> list = transactionDetailsService.list(new LambdaQueryWrapper<TransactionDetailsEntity>() List<TransactionDetailsEntity> list = transactionDetailsService.list(new LambdaQueryWrapper<TransactionDetailsEntity>()
.between(TransactionDetailsEntity::getCreateTime,startTime,endTime) .between(TransactionDetailsEntity::getCreateTime,startTime,endTime)
@@ -76,7 +76,7 @@ public class JfTransactionDetailsController {
//时间段内获得的积分 //时间段内获得的积分
Map<String,Object> jftd = jfService.getMap(new MPJLambdaWrapper<JfTransactionDetails>() Map<String,Object> jftd = jfService.getMap(new MPJLambdaWrapper<JfTransactionDetails>()
.eq(JfTransactionDetails::getUserId,transactionDetail.getUserId()) .eq(JfTransactionDetails::getUserId,transactionDetail.getUserId())
.like(JfTransactionDetails::getRemark,"双11") .like(JfTransactionDetails::getRemark,"618活动充值")
.between(JfTransactionDetails::getCreateTime,startTime,endTime) .between(JfTransactionDetails::getCreateTime,startTime,endTime)
.gt(TransactionDetailsEntity::getChangeAmount,0) .gt(TransactionDetailsEntity::getChangeAmount,0)
.selectSum(TransactionDetailsEntity::getChangeAmount)); .selectSum(TransactionDetailsEntity::getChangeAmount));
@@ -114,19 +114,19 @@ public class JfTransactionDetailsController {
new BigDecimal(changeAmount.toString()).compareTo(new BigDecimal(1000))>=0|| new BigDecimal(changeAmount.toString()).compareTo(new BigDecimal(1000))>=0||
new BigDecimal(changeAmount.toString()).compareTo(new BigDecimal(500))>=0){ new BigDecimal(changeAmount.toString()).compareTo(new BigDecimal(500))>=0){
if (new BigDecimal(changeAmount.toString()).compareTo(new BigDecimal(5000))>=0){ if (new BigDecimal(changeAmount.toString()).compareTo(new BigDecimal(5000))>=0){
shouldJf = shouldJf.add(new BigDecimal(2500)); shouldJf = shouldJf.add(new BigDecimal(2000));
changeAmount = changeAmount.subtract(new BigDecimal(5000)); changeAmount = changeAmount.subtract(new BigDecimal(5000));
}else if (new BigDecimal(changeAmount.toString()).compareTo(new BigDecimal(3000))>=0){ }else if (new BigDecimal(changeAmount.toString()).compareTo(new BigDecimal(3000))>=0){
shouldJf = shouldJf.add(new BigDecimal(1300)); shouldJf = shouldJf.add(new BigDecimal(1000));
changeAmount = changeAmount.subtract(new BigDecimal(3000)); changeAmount = changeAmount.subtract(new BigDecimal(3000));
}else if (new BigDecimal(changeAmount.toString()).compareTo(new BigDecimal(2000))>=0){ }else if (new BigDecimal(changeAmount.toString()).compareTo(new BigDecimal(2000))>=0){
shouldJf = shouldJf.add(new BigDecimal(800)); shouldJf = shouldJf.add(new BigDecimal(600));
changeAmount = changeAmount.subtract(new BigDecimal(2000)); changeAmount = changeAmount.subtract(new BigDecimal(2000));
}else if (new BigDecimal(changeAmount.toString()).compareTo(new BigDecimal(1000))>=0){ }else if (new BigDecimal(changeAmount.toString()).compareTo(new BigDecimal(1000))>=0){
shouldJf = shouldJf.add(new BigDecimal(300)); shouldJf = shouldJf.add(new BigDecimal(240));
changeAmount = changeAmount.subtract(new BigDecimal(1000)); changeAmount = changeAmount.subtract(new BigDecimal(1000));
}else if (new BigDecimal(changeAmount.toString()).compareTo(new BigDecimal(500))>=0){ }else if (new BigDecimal(changeAmount.toString()).compareTo(new BigDecimal(500))>=0){
shouldJf = shouldJf.add(new BigDecimal(120)); shouldJf = shouldJf.add(new BigDecimal(100));
changeAmount = changeAmount.subtract(new BigDecimal(500)); changeAmount = changeAmount.subtract(new BigDecimal(500));
} }
return chgf(changeJf,shouldJf,changeAmount,jf); return chgf(changeJf,shouldJf,changeAmount,jf);

View File

@@ -0,0 +1,9 @@
package com.peanut.modules.common.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.peanut.modules.common.entity.BuyOrderBatchDeliveryItem;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface BuyOrderBatchDeliveryItemDao extends BaseMapper<BuyOrderBatchDeliveryItem> {
}

View File

@@ -0,0 +1,23 @@
package com.peanut.modules.common.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.peanut.modules.common.entity.BuyOrderBatchDeliveryTask;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface BuyOrderBatchDeliveryTaskDao extends BaseMapper<BuyOrderBatchDeliveryTask> {
@Select("<script>" +
"SELECT i.order_id FROM buy_order_batch_delivery_item i " +
"INNER JOIN buy_order_batch_delivery_task t ON t.id = i.task_id " +
"WHERE t.status IN (0, 1) AND i.status IN (0, 1) AND i.order_id IN " +
"<foreach collection='orderIds' item='id' open='(' separator=',' close=')'>" +
"#{id}" +
"</foreach>" +
"</script>")
List<Integer> findBusyOrderIds(@Param("orderIds") List<Integer> orderIds);
}

View File

@@ -62,6 +62,9 @@ public class CouponEntity {
//使用门槛 //使用门槛
private Integer useLevel; private Integer useLevel;
//限定VIP类型逗号分隔如1,11,5,51用户开通其中任意VIP可现金券直接换积分
private String userVipIds;
private Date createTime; private Date createTime;
@TableLogic @TableLogic

View File

@@ -148,6 +148,8 @@ public class BuyOrderServiceImpl extends ServiceImpl<BuyOrderDao, BuyOrder> impl
List<ExpressOrder> expressOrders = expressOrderDao.selectList(new LambdaQueryWrapper<ExpressOrder>().in(ExpressOrder::getId, collect)); List<ExpressOrder> expressOrders = expressOrderDao.selectList(new LambdaQueryWrapper<ExpressOrder>().in(ExpressOrder::getId, collect));
b.setExpressList(expressOrders); b.setExpressList(expressOrders);
} }
}else if("relearn".equals(b.getOrderType()) && paymentDateTime > timestamp-7*24*60*60*1000){
refundableStatus = true;
} }
b.setRefundableStatus(b.getOrderStatus().equals("6") || b.getOrderStatus().equals("7")?false:refundableStatus); b.setRefundableStatus(b.getOrderStatus().equals("6") || b.getOrderStatus().equals("7")?false:refundableStatus);
} }

View File

@@ -271,6 +271,15 @@ public class CouponServiceImpl extends ServiceImpl<CouponDao, CouponEntity> impl
return set; return set;
} }
private boolean matchUserVipIds(String userVipIds, List<UserVip> userVipList) {
Set<Integer> allowedVipTypes = Arrays.stream(userVipIds.split(","))
.map(String::trim)
.filter(StringUtils::isNotEmpty)
.map(Integer::parseInt)
.collect(Collectors.toSet());
return userVipList.stream().anyMatch(userVip -> allowedVipTypes.contains(userVip.getType()));
}
public void getCourseMedicalIds(int courseMedicalId,List<Integer> list){ public void getCourseMedicalIds(int courseMedicalId,List<Integer> list){
CourseMedicine courseMedicine = courseMedicineDao.selectById(courseMedicalId); CourseMedicine courseMedicine = courseMedicineDao.selectById(courseMedicalId);
if (courseMedicine.getIsLast()==1){ if (courseMedicine.getIsLast()==1){
@@ -380,7 +389,7 @@ public class CouponServiceImpl extends ServiceImpl<CouponDao, CouponEntity> impl
List<Map<String,Object>> buyOrderProducts = couponToProductDao.selectJoinMaps(wrapper); List<Map<String,Object>> buyOrderProducts = couponToProductDao.selectJoinMaps(wrapper);
for (Map<String,Object> map : buyOrderProducts) { for (Map<String,Object> map : buyOrderProducts) {
ShopProduct shopProduct = shopProductDao.selectById(map.get("product_id").toString()); ShopProduct shopProduct = shopProductDao.selectById(map.get("product_id").toString());
//预售书+赠送现金券VIP用户将券额转为积分仅产品id=2023时需为妇幼生殖vip(type=10) //预售书+赠送现金券VIP用户将券额转为积分配置user_vip_ids时匹配VIP且现金券换积分不匹配只送券
if ("03".equals(shopProduct.getGoodsType())){ if ("03".equals(shopProduct.getGoodsType())){
MyUserEntity userEntity = userDao.selectById(order.getUserId()); MyUserEntity userEntity = userDao.selectById(order.getUserId());
List<UserVip> userVipList = userVipDao.selectList(new LambdaQueryWrapper<UserVip>() List<UserVip> userVipList = userVipDao.selectList(new LambdaQueryWrapper<UserVip>()
@@ -389,8 +398,15 @@ public class CouponServiceImpl extends ServiceImpl<CouponDao, CouponEntity> impl
int productId = Integer.parseInt(map.get("product_id").toString()); int productId = Integer.parseInt(map.get("product_id").toString());
int couponId = Integer.parseInt(map.get("coupon_id").toString()); int couponId = Integer.parseInt(map.get("coupon_id").toString());
CouponEntity couponEntity = couponDao.selectById(couponId); CouponEntity couponEntity = couponDao.selectById(couponId);
boolean couponToJf = false;
if (StringUtils.isNotEmpty(couponEntity.getUserVipIds())) {
if (matchUserVipIds(couponEntity.getUserVipIds(), userVipList)) {
couponToJf = couponEntity.getCouponType() == 0;
}
} else {
boolean isFyszVip = userVipList.stream().anyMatch(userVip -> Integer.valueOf(10).equals(userVip.getType())); boolean isFyszVip = userVipList.stream().anyMatch(userVip -> Integer.valueOf(10).equals(userVip.getType()));
boolean couponToJf = couponEntity.getCouponType() == 0 && (productId == 2023 ? isFyszVip : userVipList.size() > 0); couponToJf = couponEntity.getCouponType() == 0 && (productId == 2023 ? isFyszVip : userVipList.size() > 0);
}
if (couponToJf){ if (couponToJf){
BigDecimal jf = BigDecimal.ZERO; BigDecimal jf = BigDecimal.ZERO;
for (int i=0;i<Integer.parseInt(map.get("quantity").toString());i++){ for (int i=0;i<Integer.parseInt(map.get("quantity").toString());i++){

View File

@@ -116,7 +116,9 @@ public class UserVipLogServiceImpl extends ServiceImpl<UserVipLogDao, UserVipLog
} }
@Override @Override
public List<Map<String, Object>> getUserVipLogInfo(String date) { public List<Map<String, Object>> getUserVipLogInfo(String date) {
// date报表截止日期如 2026-06-30从 DAO 查出未删除 + 已退款(del_flag=-1)的 VIP 明细及摊销
List<Map<String, Object>> list = this.baseMapper.getUserVipLogInfo(date); List<Map<String, Object>> list = this.baseMapper.getUserVipLogInfo(date);
// 以 uvlId 为 key缓存「本月发生退款」的订单供后续匹配主列表并生成退款行
Map<String, Map<String, Object>> refundMap = new HashMap<>(); Map<String, Map<String, Object>> refundMap = new HashMap<>();
for (Map<String, Object> refund : this.baseMapper.getMonthRefund(date)) { for (Map<String, Object> refund : this.baseMapper.getMonthRefund(date)) {
Object uvlId = refund.get("uvlId"); Object uvlId = refund.get("uvlId");
@@ -124,25 +126,33 @@ public class UserVipLogServiceImpl extends ServiceImpl<UserVipLogDao, UserVipLog
refundMap.put(uvlId.toString(), refund); refundMap.put(uvlId.toString(), refund);
} }
} }
// 最终返回给导出的明细列表(可能比 SQL 原始行数多,因退款会拆成「已付款行 + 已退款行」)
List<Map<String, Object>> result = new ArrayList<>(); List<Map<String, Object>> result = new ArrayList<>();
// 记录已在主列表中处理过的退款 uvlId避免第二步重复追加
Set<String> matchedUvlIds = new HashSet<>(); Set<String> matchedUvlIds = new HashSet<>();
for (Map<String, Object> row : list) { for (Map<String, Object> row : list) {
// 主列表默认标记为已付款
row.put("orderStatus", "已付款"); row.put("orderStatus", "已付款");
Object uvlId = row.get("uvlId"); Object uvlId = row.get("uvlId");
// 若该 VIP 记录在本月有退款,则 refund 非空
Map<String, Object> refund = uvlId == null ? null : refundMap.get(uvlId.toString()); Map<String, Object> refund = uvlId == null ? null : refundMap.get(uvlId.toString());
if (refund != null) { if (refund != null) {
// 本月退款的订单:原「已付款」行当月/剩余摊销清零(退款单独成行体现)
row.put("currentTanxiao",BigDecimal.ZERO); row.put("currentTanxiao",BigDecimal.ZERO);
row.put("notyetTanxiao",BigDecimal.ZERO); row.put("notyetTanxiao",BigDecimal.ZERO);
String exportMonth = date.length() >= 7 ? date.substring(0, 7) : date; String exportMonth = date.length() >= 7 ? date.substring(0, 7) : date;
String payMonth = getPayMonth(row); String payMonth = getPayMonth(row);
// 本月付本月退:保留原已付款行(金额为正),再追加一条负金额退款行
if (exportMonth.equals(payMonth)){ if (exportMonth.equals(payMonth)){
result.add(row); result.add(row);
} }
matchedUvlIds.add(uvlId.toString()); matchedUvlIds.add(uvlId.toString());
// 追加「已退款」行,金额/摊销按 buildRefundRow 规则处理
result.add(buildRefundRow(row, refund, date)); result.add(buildRefundRow(row, refund, date));
}else{ }else{
// 无本月退款:原样加入结果
result.add(row); result.add(row);
} }
@@ -151,10 +161,12 @@ public class UserVipLogServiceImpl extends ServiceImpl<UserVipLogDao, UserVipLog
String exportMonth = date.length() >= 7 ? date.substring(0, 7) : date; String exportMonth = date.length() >= 7 ? date.substring(0, 7) : date;
for (Map<String, Object> refund : refundMap.values()) { for (Map<String, Object> refund : refundMap.values()) {
Object uvlId = refund.get("uvlId"); Object uvlId = refund.get("uvlId");
// 已在主列表匹配过的跳过
if (uvlId != null && matchedUvlIds.contains(uvlId.toString())) { if (uvlId != null && matchedUvlIds.contains(uvlId.toString())) {
continue; continue;
} }
String payMonth = getPayMonth(refund); String payMonth = getPayMonth(refund);
// 早月付款、本月退款且主列表无行:仅用 refund 数据构造一条退款行
if (!payMonth.isEmpty() && !exportMonth.equals(payMonth)) { if (!payMonth.isEmpty() && !exportMonth.equals(payMonth)) {
result.add(buildRefundRow(refund, refund, date)); result.add(buildRefundRow(refund, refund, date));
} }
@@ -162,15 +174,15 @@ public class UserVipLogServiceImpl extends ServiceImpl<UserVipLogDao, UserVipLog
return result; return result;
} }
public Map<String, Object> buildRefundRow(Map<String, Object> row, Map<String, Object> refund, String date) { public Map<String, Object> buildRefundRow(Map<String, Object> row, Map<String, Object> refund, String date) {
// 复制原订单行作为退款行模板
Map<String, Object> refundRow = new HashMap<>(row); Map<String, Object> refundRow = new HashMap<>(row);
log.info("n======o"+(refund.get("refund_no")==null?"":refund.get("refund_no").toString()));
refundRow.put("orderStatus", "已退款"); refundRow.put("orderStatus", "已退款");
// 支付时间改为退款时间,便于导出展示
refundRow.put("payTime", refund.get("refundTime")); refundRow.put("payTime", refund.get("refundTime"));
refundRow.put("refund_no", refund.get("refund_no")); refundRow.put("refund_no", refund.get("refund_no"));
String exportMonth = date.length() >= 7 ? date.substring(0, 7) : date; String exportMonth = date.length() >= 7 ? date.substring(0, 7) : date;
String payMonth = getPayMonth(row); String payMonth = getPayMonth(row);
BigDecimal dayAmount = toBigDecimal(row.get("dayAmount")); BigDecimal dayAmount = toBigDecimal(row.get("dayAmount"));
log.info(date+":=====p:"+payMonth+",e:"+exportMonth+","+refund.get("orderSn").toString());
if (exportMonth.equals(payMonth)) { if (exportMonth.equals(payMonth)) {
// 当月订单当月退款:金额为负,当月/剩余摊销为0 // 当月订单当月退款:金额为负,当月/剩余摊销为0
@@ -183,7 +195,7 @@ public class UserVipLogServiceImpl extends ServiceImpl<UserVipLogDao, UserVipLog
refundRow.put("currentTanxiao", BigDecimal.ZERO); refundRow.put("currentTanxiao", BigDecimal.ZERO);
refundRow.put("notyetTanxiao", BigDecimal.ZERO); refundRow.put("notyetTanxiao", BigDecimal.ZERO);
} else if (!payMonth.isEmpty() && !exportMonth.equals(payMonth)) { } else if (!payMonth.isEmpty() && !exportMonth.equals(payMonth)) {
//其他月份下单、指定月份退款:订单金额为0冲回已摊销部分 // 其他月份下单、指定月份退款:冲回截至上月末的已摊销,当月摊销为负的已摊销额
int alreadyDays = calcAlreadyDaysToPrevMonthEnd(row, date); int alreadyDays = calcAlreadyDaysToPrevMonthEnd(row, date);
BigDecimal alreadyTanxiao = dayAmount.multiply(new BigDecimal(alreadyDays)).setScale(2, RoundingMode.HALF_UP); BigDecimal alreadyTanxiao = dayAmount.multiply(new BigDecimal(alreadyDays)).setScale(2, RoundingMode.HALF_UP);
refundRow.put("price", toBigDecimal(row.get("price")).negate()); refundRow.put("price", toBigDecimal(row.get("price")).negate());
@@ -192,9 +204,12 @@ public class UserVipLogServiceImpl extends ServiceImpl<UserVipLogDao, UserVipLog
refundRow.put("currentDays", 0); refundRow.put("currentDays", 0);
refundRow.put("notyetDays", 0); refundRow.put("notyetDays", 0);
refundRow.put("alreadyTanxiao", 0); refundRow.put("alreadyTanxiao", 0);
// 当月摊销 = 负的「付款月至上月末」累计摊销
refundRow.put("currentTanxiao", alreadyTanxiao.negate()); refundRow.put("currentTanxiao", alreadyTanxiao.negate());
refundRow.put("notyetTanxiao", 0); refundRow.put("notyetTanxiao", 0);
refundRow.put("startTime", refund.get("refundTime"));
} else { } else {
// 兜底:按原行已摊销冲回
refundRow.put("price", toBigDecimal(row.get("price")).negate()); refundRow.put("price", toBigDecimal(row.get("price")).negate());
refundRow.put("fee", toBigDecimal(row.get("fee")).negate()); refundRow.put("fee", toBigDecimal(row.get("fee")).negate());
refundRow.put("alreadyDays", row.get("alreadyDays")); refundRow.put("alreadyDays", row.get("alreadyDays"));

View File

@@ -266,7 +266,6 @@ public class UserVipServiceImpl extends ServiceImpl<UserVipDao, UserVip> impleme
List<UserVip> userVipList = userVipDao.selectList(new LambdaQueryWrapper<UserVip>() List<UserVip> userVipList = userVipDao.selectList(new LambdaQueryWrapper<UserVip>()
.eq(UserVip::getUserId,buyOrder.getUserId()).eq(UserVip::getState,0).in(UserVip::getType,4,9,5,6,10)); .eq(UserVip::getUserId,buyOrder.getUserId()).eq(UserVip::getState,0).in(UserVip::getType,4,9,5,6,10));
for (int i=4;i<=10;i++){ for (int i=4;i<=10;i++){
log.info("openVipForUser====i:"+i);
if (i==7){ if (i==7){
i=9; i=9;
} }

View File

@@ -35,13 +35,16 @@ public class StatisticsBusinessVipController {
@RequestMapping("/getUserVipByMonth") @RequestMapping("/getUserVipByMonth")
public R getUserVipByMonth(@RequestBody Map<String,Object> params) { public R getUserVipByMonth(@RequestBody Map<String,Object> params) {
CountResult result = countInfo(userVipService.getUserVipByMonth(params.get("date").toString())); CountResult result = countInfo(userVipService.getUserVipByMonth(params.get("date").toString()), params.get("date").toString());
List<Map<String,Object>> allResultList = result.resultList; List<Map<String,Object>> allResultList = result.resultList;
Map<String, Integer> banCounts = new HashMap<>(); Map<String, Integer> banCounts = new HashMap<>();
Map<String, Integer> yanCounts = new HashMap<>(); Map<String, Integer> yanCounts = new HashMap<>();
BigDecimal banTotalPrice = BigDecimal.ZERO; BigDecimal banTotalPrice = BigDecimal.ZERO;
BigDecimal yanTotalPrice = BigDecimal.ZERO; BigDecimal yanTotalPrice = BigDecimal.ZERO;
String date = params.get("date").toString();
Map<String, List<Integer>> reBuyRules = buildReBuyRules(date);
for (Map<String,Object> map : allResultList) { for (Map<String,Object> map : allResultList) {
String vipType = map.get("vipType").toString(); String vipType = map.get("vipType").toString();
boolean isBan = map.get("startTime").equals(map.get("uvlStartTime")); boolean isBan = map.get("startTime").equals(map.get("uvlStartTime"));
@@ -61,17 +64,6 @@ public class StatisticsBusinessVipController {
.eq(UserVip::getUserId, map.get("userId").toString()) .eq(UserVip::getUserId, map.get("userId").toString())
.eq(UserVip::getState,1).lt(UserVip::getStartTime,map.get("startTime").toString())) .eq(UserVip::getState,1).lt(UserVip::getStartTime,map.get("startTime").toString()))
.stream().map(UserVip::getType).collect(Collectors.toList()); .stream().map(UserVip::getType).collect(Collectors.toList());
// 定义复购规则
Map<String, List<Integer>> reBuyRules = new HashMap<>();
reBuyRules.put("医学超级", Arrays.asList(4, 5, 6, 9, 10));
reBuyRules.put("国学心理学超级", Arrays.asList(7, 8));
reBuyRules.put("中医学", Arrays.asList(4));
reBuyRules.put("针灸学", Arrays.asList(5));
reBuyRules.put("肿瘤学", Arrays.asList(6));
reBuyRules.put("国学", Arrays.asList(7));
reBuyRules.put("心理学", Arrays.asList(8));
reBuyRules.put("中西汇通学", Arrays.asList(9));
reBuyRules.put("妇幼生殖学", Arrays.asList(10));
// 判断是否复购 // 判断是否复购
List<Integer> required = reBuyRules.get(vipType); List<Integer> required = reBuyRules.get(vipType);
if (required != null && state1UserVips.containsAll(required)) { if (required != null && state1UserVips.containsAll(required)) {
@@ -112,7 +104,7 @@ public class StatisticsBusinessVipController {
row0.createCell(0).setCellValue("办理总人数");row0.createCell(1).setCellValue(r.get("banTotalCount").toString()); row0.createCell(0).setCellValue("办理总人数");row0.createCell(1).setCellValue(r.get("banTotalCount").toString());
row0.createCell(2).setCellValue("延期总人数");row0.createCell(3).setCellValue(r.get("yanTotalCount").toString()); row0.createCell(2).setCellValue("延期总人数");row0.createCell(3).setCellValue(r.get("yanTotalCount").toString());
row0.createCell(4).setCellValue("总办理金额");row0.createCell(5).setCellValue(r.get("banTotalPrice").toString()); row0.createCell(4).setCellValue("总办理金额");row0.createCell(5).setCellValue(r.get("banTotalPrice").toString());
BigDecimal a = new BigDecimal(r.get("banTotalCount").toString()).subtract(new BigDecimal(r2.get("banTotalCount").toString())).divide(new BigDecimal(r.get("banTotalCount").toString()), 4, RoundingMode.HALF_UP); BigDecimal a = calcGrowthRate(r.get("banTotalCount"), r2.get("banTotalCount"));
DecimalFormat df = new DecimalFormat("0.00%"); DecimalFormat df = new DecimalFormat("0.00%");
row0.createCell(6).setCellValue("办理总人数环比增长率:");row0.createCell(7).setCellValue(df.format(a)); row0.createCell(6).setCellValue("办理总人数环比增长率:");row0.createCell(7).setCellValue(df.format(a));
Row row1 = sheet.createRow(rowNum++); Row row1 = sheet.createRow(rowNum++);
@@ -121,19 +113,19 @@ public class StatisticsBusinessVipController {
row1.createCell(0).setCellValue("医学超级");row1.createCell(1).setCellValue(state1Counts.getOrDefault("医学超级",0).toString()); row1.createCell(0).setCellValue("医学超级");row1.createCell(1).setCellValue(state1Counts.getOrDefault("医学超级",0).toString());
row1.createCell(2).setCellValue("医学超级");row1.createCell(3).setCellValue(state0Counts.getOrDefault("医学超级",0).toString()); row1.createCell(2).setCellValue("医学超级");row1.createCell(3).setCellValue(state0Counts.getOrDefault("医学超级",0).toString());
row1.createCell(4).setCellValue("总延期金额");row1.createCell(5).setCellValue(r.get("yanTotalPrice").toString()); row1.createCell(4).setCellValue("总延期金额");row1.createCell(5).setCellValue(r.get("yanTotalPrice").toString());
BigDecimal b = new BigDecimal(r.get("yanTotalCount").toString()).subtract(new BigDecimal(r2.get("yanTotalCount").toString())).divide(new BigDecimal(r.get("yanTotalCount").toString()), 4, RoundingMode.HALF_UP); BigDecimal b = calcGrowthRate(r.get("yanTotalCount"), r2.get("yanTotalCount"));
row1.createCell(6).setCellValue("延期总人数环比增长率:");row1.createCell(7).setCellValue(df.format(b)); row1.createCell(6).setCellValue("延期总人数环比增长率:");row1.createCell(7).setCellValue(df.format(b));
Row row2 = sheet.createRow(rowNum++); Row row2 = sheet.createRow(rowNum++);
row2.createCell(0).setCellValue("国学心理学超级");row2.createCell(1).setCellValue(state1Counts.getOrDefault("国学心理学超级",0).toString()); row2.createCell(0).setCellValue("国学心理学超级");row2.createCell(1).setCellValue(state1Counts.getOrDefault("国学心理学超级",0).toString());
row2.createCell(2).setCellValue("国学心理学超级");row2.createCell(3).setCellValue(state0Counts.getOrDefault("国学心理学超级",0).toString()); row2.createCell(2).setCellValue("国学心理学超级");row2.createCell(3).setCellValue(state0Counts.getOrDefault("国学心理学超级",0).toString());
row2.createCell(4).setCellValue("总金额");row2.createCell(5).setCellValue( row2.createCell(4).setCellValue("总金额");row2.createCell(5).setCellValue(
new BigDecimal(r.get("banTotalPrice").toString()).add(new BigDecimal(r.get("yanTotalPrice").toString())).toString()); new BigDecimal(r.get("banTotalPrice").toString()).add(new BigDecimal(r.get("yanTotalPrice").toString())).toString());
BigDecimal c = new BigDecimal(r.get("banTotalPrice").toString()).subtract(new BigDecimal(r2.get("banTotalPrice").toString())).divide(new BigDecimal(r.get("banTotalPrice").toString()), 4, RoundingMode.HALF_UP); BigDecimal c = calcGrowthRate(r.get("banTotalPrice"), r2.get("banTotalPrice"));
row2.createCell(6).setCellValue("办理金额环比增长率:");row2.createCell(7).setCellValue(df.format(c)); row2.createCell(6).setCellValue("办理金额环比增长率:");row2.createCell(7).setCellValue(df.format(c));
Row row3 = sheet.createRow(rowNum++); Row row3 = sheet.createRow(rowNum++);
row3.createCell(0).setCellValue("中医学");row3.createCell(1).setCellValue(state1Counts.getOrDefault("中医学", 0).toString()); row3.createCell(0).setCellValue("中医学");row3.createCell(1).setCellValue(state1Counts.getOrDefault("中医学", 0).toString());
row3.createCell(2).setCellValue("中医学");row3.createCell(3).setCellValue(state0Counts.getOrDefault("中医学", 0).toString()); row3.createCell(2).setCellValue("中医学");row3.createCell(3).setCellValue(state0Counts.getOrDefault("中医学", 0).toString());
BigDecimal d = new BigDecimal(r.get("yanTotalPrice").toString()).subtract(new BigDecimal(r2.get("yanTotalPrice").toString())).divide(new BigDecimal(r.get("yanTotalPrice").toString()), 4, RoundingMode.HALF_UP); BigDecimal d = calcGrowthRate(r.get("yanTotalPrice"), r2.get("yanTotalPrice"));
row3.createCell(6).setCellValue("延期金额环比增长率:");row3.createCell(7).setCellValue(df.format(d)); row3.createCell(6).setCellValue("延期金额环比增长率:");row3.createCell(7).setCellValue(df.format(d));
Row row4 = sheet.createRow(rowNum++); Row row4 = sheet.createRow(rowNum++);
row4.createCell(0).setCellValue("针灸学");row4.createCell(1).setCellValue(state1Counts.getOrDefault("针灸学", 0).toString()); row4.createCell(0).setCellValue("针灸学");row4.createCell(1).setCellValue(state1Counts.getOrDefault("针灸学", 0).toString());
@@ -290,7 +282,7 @@ public class StatisticsBusinessVipController {
row0.createCell(0).setCellValue("办理总人数");row0.createCell(1).setCellValue(r.get("banTotalCount").toString()); row0.createCell(0).setCellValue("办理总人数");row0.createCell(1).setCellValue(r.get("banTotalCount").toString());
row0.createCell(2).setCellValue("延期总人数");row0.createCell(3).setCellValue(r.get("yanTotalCount").toString()); row0.createCell(2).setCellValue("延期总人数");row0.createCell(3).setCellValue(r.get("yanTotalCount").toString());
row0.createCell(4).setCellValue("总办理金额");row0.createCell(5).setCellValue(r.get("banTotalPrice").toString()); row0.createCell(4).setCellValue("总办理金额");row0.createCell(5).setCellValue(r.get("banTotalPrice").toString());
BigDecimal a = new BigDecimal(r.get("banTotalCount").toString()).subtract(new BigDecimal(r2.get("banTotalCount").toString())).divide(new BigDecimal(r.get("banTotalCount").toString()), 4, RoundingMode.HALF_UP); BigDecimal a = calcGrowthRate(r.get("banTotalCount"), r2.get("banTotalCount"));
DecimalFormat df = new DecimalFormat("0.00%"); DecimalFormat df = new DecimalFormat("0.00%");
row0.createCell(6).setCellValue("办理总人数同比增长率:");row0.createCell(7).setCellValue(df.format(a)); row0.createCell(6).setCellValue("办理总人数同比增长率:");row0.createCell(7).setCellValue(df.format(a));
Row row1 = sheet.createRow(rowNum++); Row row1 = sheet.createRow(rowNum++);
@@ -299,19 +291,19 @@ public class StatisticsBusinessVipController {
row1.createCell(0).setCellValue("医学超级");row1.createCell(1).setCellValue(state1Counts.containsKey("医学超级")?state1Counts.get("医学超级").toString():""); row1.createCell(0).setCellValue("医学超级");row1.createCell(1).setCellValue(state1Counts.containsKey("医学超级")?state1Counts.get("医学超级").toString():"");
row1.createCell(2).setCellValue("医学超级");row1.createCell(3).setCellValue(state0Counts.containsKey("医学超级")?state0Counts.get("医学超级").toString():""); row1.createCell(2).setCellValue("医学超级");row1.createCell(3).setCellValue(state0Counts.containsKey("医学超级")?state0Counts.get("医学超级").toString():"");
row1.createCell(4).setCellValue("总延期金额");row1.createCell(5).setCellValue(r.get("yanTotalPrice").toString()); row1.createCell(4).setCellValue("总延期金额");row1.createCell(5).setCellValue(r.get("yanTotalPrice").toString());
BigDecimal b = new BigDecimal(r.get("yanTotalCount").toString()).subtract(new BigDecimal(r2.get("yanTotalCount").toString())).divide(new BigDecimal(r.get("yanTotalCount").toString()), 4, RoundingMode.HALF_UP); BigDecimal b = calcGrowthRate(r.get("yanTotalCount"), r2.get("yanTotalCount"));
row1.createCell(6).setCellValue("延期总人数同比增长率:");row1.createCell(7).setCellValue(df.format(b)); row1.createCell(6).setCellValue("延期总人数同比增长率:");row1.createCell(7).setCellValue(df.format(b));
Row row2 = sheet.createRow(rowNum++); Row row2 = sheet.createRow(rowNum++);
row2.createCell(0).setCellValue("国学心理学超级");row2.createCell(1).setCellValue(state1Counts.get("国学心理学超级").toString()); row2.createCell(0).setCellValue("国学心理学超级");row2.createCell(1).setCellValue(state1Counts.get("国学心理学超级").toString());
row2.createCell(2).setCellValue("国学心理学超级");row2.createCell(3).setCellValue(state0Counts.get("国学心理学超级").toString()); row2.createCell(2).setCellValue("国学心理学超级");row2.createCell(3).setCellValue(state0Counts.get("国学心理学超级").toString());
row2.createCell(4).setCellValue("总金额");row2.createCell(5).setCellValue( row2.createCell(4).setCellValue("总金额");row2.createCell(5).setCellValue(
new BigDecimal(r.get("banTotalPrice").toString()).add(new BigDecimal(r.get("yanTotalPrice").toString())).toString()); new BigDecimal(r.get("banTotalPrice").toString()).add(new BigDecimal(r.get("yanTotalPrice").toString())).toString());
BigDecimal c = new BigDecimal(r.get("banTotalPrice").toString()).subtract(new BigDecimal(r2.get("banTotalPrice").toString())).divide(new BigDecimal(r.get("banTotalPrice").toString()), 4, RoundingMode.HALF_UP); BigDecimal c = calcGrowthRate(r.get("banTotalPrice"), r2.get("banTotalPrice"));
row2.createCell(6).setCellValue("办理金额同比增长率:");row2.createCell(7).setCellValue(df.format(c)); row2.createCell(6).setCellValue("办理金额同比增长率:");row2.createCell(7).setCellValue(df.format(c));
Row row3 = sheet.createRow(rowNum++); Row row3 = sheet.createRow(rowNum++);
row3.createCell(0).setCellValue("中医学");row3.createCell(1).setCellValue(state1Counts.getOrDefault("中医学", 0).toString()); row3.createCell(0).setCellValue("中医学");row3.createCell(1).setCellValue(state1Counts.getOrDefault("中医学", 0).toString());
row3.createCell(2).setCellValue("中医学");row3.createCell(3).setCellValue(state0Counts.getOrDefault("中医学", 0).toString()); row3.createCell(2).setCellValue("中医学");row3.createCell(3).setCellValue(state0Counts.getOrDefault("中医学", 0).toString());
BigDecimal d = new BigDecimal(r.get("yanTotalPrice").toString()).subtract(new BigDecimal(r2.get("yanTotalPrice").toString())).divide(new BigDecimal(r.get("yanTotalPrice").toString()), 4, RoundingMode.HALF_UP); BigDecimal d = calcGrowthRate(r.get("yanTotalPrice"), r2.get("yanTotalPrice"));
row3.createCell(6).setCellValue("延期金额同比增长率:");row3.createCell(7).setCellValue(df.format(d)); row3.createCell(6).setCellValue("延期金额同比增长率:");row3.createCell(7).setCellValue(df.format(d));
Row row4 = sheet.createRow(rowNum++); Row row4 = sheet.createRow(rowNum++);
row4.createCell(0).setCellValue("针灸学");row4.createCell(1).setCellValue(state1Counts.getOrDefault("针灸学", 0).toString()); row4.createCell(0).setCellValue("针灸学");row4.createCell(1).setCellValue(state1Counts.getOrDefault("针灸学", 0).toString());
@@ -503,7 +495,13 @@ public class StatisticsBusinessVipController {
Map<String,Integer> counts = new HashMap<>(); Map<String,Integer> counts = new HashMap<>();
} }
private static final String MEDICAL_SUPER_CUTOFF = "2026-05";
private CountResult countInfo(List<Map<String,Object>> userVips) { private CountResult countInfo(List<Map<String,Object>> userVips) {
return countInfo(userVips, null);
}
private CountResult countInfo(List<Map<String,Object>> userVips, String date) {
CountResult result = new CountResult(); CountResult result = new CountResult();
// 初始化计数器 // 初始化计数器
result.counts.put("yxSuperCount",0); result.counts.put("yxSuperCount",0);
@@ -517,16 +515,18 @@ public class StatisticsBusinessVipController {
result.counts.put("fyszCount",0); result.counts.put("fyszCount",0);
for (Map<String,Object> map : userVips) { for (Map<String,Object> map : userVips) {
String recordMonth = resolveRecordMonth(map, date);
List<String> medicalSuperTypes = getMedicalSuperTypes(recordMonth);
String[] types = map.get("type").toString().split(","); String[] types = map.get("type").toString().split(",");
Set<String> typeSet = new HashSet<>(Arrays.asList(types)); Set<String> typeSet = new HashSet<>(Arrays.asList(types));
int yxFlag = 0; int yxFlag = 0;
int gxFlag = 0; int gxFlag = 0;
// 超级类型 // 超级类型
if (typeSet.containsAll(Arrays.asList("4","5","6","9","10"))) { if (typeSet.containsAll(medicalSuperTypes)) {
yxFlag = 1; yxFlag = 1;
Map<String,Object> copy = new HashMap<>(map); Map<String,Object> copy = new HashMap<>(map);
copy.put("vipType","医学超级"); copy.put("vipType","医学超级");
copy.put("price",Math.round(Double.parseDouble(map.get("price").toString())*4)); copy.put("price",Math.round(Double.parseDouble(map.get("price").toString())*medicalSuperTypes.size()));
result.resultList.add(copy); result.resultList.add(copy);
result.counts.computeIfPresent("yxSuperCount",(k,v)->v+1); result.counts.computeIfPresent("yxSuperCount",(k,v)->v+1);
result.total.add(map.get("tel").toString()); result.total.add(map.get("tel").toString());
@@ -550,7 +550,7 @@ public class StatisticsBusinessVipController {
else if ("7".equals(type) && gxFlag==0) { copy.put("vipType","国学"); result.counts.computeIfPresent("gxCount",(k,v)->v+1); } else if ("7".equals(type) && gxFlag==0) { copy.put("vipType","国学"); result.counts.computeIfPresent("gxCount",(k,v)->v+1); }
else if ("8".equals(type) && gxFlag==0) { copy.put("vipType","心理学"); result.counts.computeIfPresent("xlCount",(k,v)->v+1); } else if ("8".equals(type) && gxFlag==0) { copy.put("vipType","心理学"); result.counts.computeIfPresent("xlCount",(k,v)->v+1); }
else if ("9".equals(type) && yxFlag==0) { copy.put("vipType","中西汇通学"); result.counts.computeIfPresent("zxhtCount",(k,v)->v+1); } else if ("9".equals(type) && yxFlag==0) { copy.put("vipType","中西汇通学"); result.counts.computeIfPresent("zxhtCount",(k,v)->v+1); }
else if ("10".equals(type) && yxFlag==0) { copy.put("vipType","妇幼生殖学"); result.counts.computeIfPresent("fyszCount",(k,v)->v+1); } else if ("10".equals(type) && yxFlag==0 && isFyszEnabled(recordMonth)) { copy.put("vipType","妇幼生殖学"); result.counts.computeIfPresent("fyszCount",(k,v)->v+1); }
if (copy.get("vipType") != null) { if (copy.get("vipType") != null) {
result.resultList.add(copy); result.resultList.add(copy);
result.total.add(map.get("tel").toString()); result.total.add(map.get("tel").toString());
@@ -560,6 +560,56 @@ public class StatisticsBusinessVipController {
return result; return result;
} }
private String resolveRecordMonth(Map<String, Object> map, String date) {
if (date != null) {
return date;
}
Object payTime = map.get("payTime");
if (payTime != null) {
String payTimeStr = payTime.toString();
return payTimeStr.length() >= 7 ? payTimeStr.substring(0, 7) : payTimeStr;
}
return MEDICAL_SUPER_CUTOFF;
}
private List<String> getMedicalSuperTypes(String recordMonth) {
if (recordMonth.compareTo(MEDICAL_SUPER_CUTOFF) < 0) {
return Arrays.asList("4", "5", "6", "9");
}
return Arrays.asList("4", "5", "6", "9", "10");
}
private boolean isFyszEnabled(String recordMonth) {
return recordMonth.compareTo(MEDICAL_SUPER_CUTOFF) >= 0;
}
private Map<String, List<Integer>> buildReBuyRules(String date) {
Map<String, List<Integer>> reBuyRules = new HashMap<>();
if (date.compareTo(MEDICAL_SUPER_CUTOFF) < 0) {
reBuyRules.put("医学超级", Arrays.asList(4, 5, 6, 9));
} else {
reBuyRules.put("医学超级", Arrays.asList(4, 5, 6, 9, 10));
reBuyRules.put("妇幼生殖学", Arrays.asList(10));
}
reBuyRules.put("国学心理学超级", Arrays.asList(7, 8));
reBuyRules.put("中医学", Arrays.asList(4));
reBuyRules.put("针灸学", Arrays.asList(5));
reBuyRules.put("肿瘤学", Arrays.asList(6));
reBuyRules.put("国学", Arrays.asList(7));
reBuyRules.put("心理学", Arrays.asList(8));
reBuyRules.put("中西汇通学", Arrays.asList(9));
return reBuyRules;
}
private BigDecimal calcGrowthRate(Object current, Object previous) {
BigDecimal currentVal = new BigDecimal(current.toString());
if (currentVal.compareTo(BigDecimal.ZERO) == 0) {
return BigDecimal.ZERO;
}
BigDecimal previousVal = new BigDecimal(previous.toString());
return currentVal.subtract(previousVal).divide(currentVal, 4, RoundingMode.HALF_UP);
}
private void export(XSSFWorkbook wb,Sheet sheet,String fileName,int rowNum,HttpServletResponse response, String[] headers, String[] cellValues, List<Map<String,Object>> list){ private void export(XSSFWorkbook wb,Sheet sheet,String fileName,int rowNum,HttpServletResponse response, String[] headers, String[] cellValues, List<Map<String,Object>> list){
if (sheet!=null){ if (sheet!=null){
// 表头 // 表头

View File

@@ -616,7 +616,6 @@ public class StatisticsController {
BigDecimal refundFeeToSR = BigDecimal.ZERO; BigDecimal refundFeeToSR = BigDecimal.ZERO;
BigDecimal refundMonthTX = BigDecimal.ZERO; BigDecimal refundMonthTX = BigDecimal.ZERO;
BigDecimal refundYetTX = BigDecimal.ZERO; BigDecimal refundYetTX = BigDecimal.ZERO;
if(date.equals("2026-04-30"))log.info("currentTanxiao======="+map.get("currentTanxiao").toString());
for (Map<String,Object> one : monthRefundMap) { for (Map<String,Object> one : monthRefundMap) {
String payMonth = userVipLogService.getPayMonth(one); String payMonth = userVipLogService.getPayMonth(one);
if (exportMonth.equals(payMonth)) { if (exportMonth.equals(payMonth)) {

View File

@@ -114,6 +114,7 @@ public class UserVipController {
"5".equals(params.get("type").toString())?"开通针灸学VIP": "5".equals(params.get("type").toString())?"开通针灸学VIP":
"6".equals(params.get("type").toString())?"开通肿瘤学VIP": "6".equals(params.get("type").toString())?"开通肿瘤学VIP":
"7".equals(params.get("type").toString())?"开通国学VIP": "7".equals(params.get("type").toString())?"开通国学VIP":
"10".equals(params.get("type").toString())?"妇幼生殖学VIP":
"8".equals(params.get("type").toString())?"开通心理学VIP":"开通中西汇通VIP"; "8".equals(params.get("type").toString())?"开通心理学VIP":"开通中西汇通VIP";
if(fee.compareTo(BigDecimal.ZERO)>0){ if(fee.compareTo(BigDecimal.ZERO)>0){
user.setPeanutCoin(user.getPeanutCoin().subtract(fee)); user.setPeanutCoin(user.getPeanutCoin().subtract(fee));
@@ -155,8 +156,8 @@ public class UserVipController {
typeList.add(i); typeList.add(i);
} }
}else if ("1".equals(params.get("type").toString())) {//医学超级 }else if ("1".equals(params.get("type").toString())) {//医学超级
count = 4; count = 5;
for (int i=4;i<8;i++){ for (int i=4;i<=10;i++){
if (i==7){ if (i==7){
i = 9; i = 9;
} }

View File

@@ -235,18 +235,31 @@ public class UserCourseBuyServiceImpl extends ServiceImpl<UserCourseBuyDao, User
result.add(row); result.add(row);
} }
} }
Map<String, Integer> refundChapterCountMap = new HashMap<>();
for (Map<String, Object> refund : refundMap.values()) {
String orderCourseKey = String.valueOf(refund.get("orderSn")) + "|" + String.valueOf(refund.get("ctitle"));
refundChapterCountMap.merge(orderCourseKey, 1, Integer::sum);
}
for (Map<String, Object> refund : refundMap.values()) { for (Map<String, Object> refund : refundMap.values()) {
String key = refundKey(refund); String key = refundKey(refund);
if (matchedKeys.contains(key)) { if (matchedKeys.contains(key)) {
continue; continue;
} }
Map<String, Object> adjustedRefund = new HashMap<>(refund);
String orderCourseKey = String.valueOf(refund.get("orderSn")) + "|" + String.valueOf(refund.get("ctitle"));
int chapterCount = refundChapterCountMap.getOrDefault(orderCourseKey, 1);
if (chapterCount > 1) {
BigDecimal dividedFee = toBigDecimal(refund.get("fee"))
.divide(BigDecimal.valueOf(chapterCount), 2, RoundingMode.HALF_UP);
adjustedRefund.put("fee", dividedFee);
}
String payMonth = getPayMonth(refund); String payMonth = getPayMonth(refund);
if (exportMonth.equals(payMonth)) { if (exportMonth.equals(payMonth)) {
Map<String, Object> paidRow = buildPaidRowFromRefund(refund); Map<String, Object> paidRow = buildPaidRowFromRefund(adjustedRefund);
result.add(paidRow); result.add(paidRow);
result.add(buildRefundRow(paidRow, refund, date)); result.add(buildRefundRow(paidRow, adjustedRefund, date));
} else if (!payMonth.isEmpty() && payMonth.compareTo(exportMonth) < 0) { } else if (!payMonth.isEmpty() && payMonth.compareTo(exportMonth) < 0) {
result.add(buildRefundRow(buildBaseRowFromRefund(refund), refund, date)); result.add(buildRefundRow(buildBaseRowFromRefund(adjustedRefund), adjustedRefund, date));
} }
} }
return result; return result;

View File

@@ -66,7 +66,7 @@ public class MedicalLabelAndMarketController {
if (params.containsKey("medicineMarketId")&&!"".equals(params.get("medicineMarketId").toString())){ if (params.containsKey("medicineMarketId")&&!"".equals(params.get("medicineMarketId").toString())){
wrapper.eq(ShopProductToMedicineMarket::getMedicineMarketId,params.get("medicineMarketId").toString()); wrapper.eq(ShopProductToMedicineMarket::getMedicineMarketId,params.get("medicineMarketId").toString());
} }
wrapper.orderByAsc(ShopProductToMedicineMarket::getSort); wrapper.orderByDesc(ShopProductToMedicineMarket::getSort);
Page<ShopProduct> page = productService.page(new Page<>( Page<ShopProduct> page = productService.page(new Page<>(
Long.parseLong(params.get("current").toString()), Long.parseLong(params.get("limit").toString())),wrapper); Long.parseLong(params.get("current").toString()), Long.parseLong(params.get("limit").toString())),wrapper);
for (ShopProduct shopProduct:page.getRecords()){ for (ShopProduct shopProduct:page.getRecords()){

View File

@@ -185,7 +185,7 @@ public class WxpayServiceImpl extends ServiceImpl<PayWechatOrderDao, PayWechatOr
payWechatOrderService.updateById(payWechatOrderEntity); payWechatOrderService.updateById(payWechatOrderEntity);
// 根据订单号,做幂等处理,并且在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱 // 根据订单号,做幂等处理,并且在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱
BuyOrder order = this.buyOrderService.getOne(new QueryWrapper<BuyOrder>().eq("order_sn", orderNo)); BuyOrder order = this.buyOrderService.getOne(new QueryWrapper<BuyOrder>().eq("order_sn", orderNo));
if ("3".equals(order.getOrderStatus())){ if ("3".equals(order.getOrderStatus())||"6".equals(order.getOrderStatus())||"7".equals(order.getOrderStatus())){
return; return;
} }
//使用优惠券 //使用优惠券

View File

@@ -7,11 +7,11 @@
select r.* select r.*
,ROUND(fee-alreadyTanxiao-currentTanxiao,2) surplusTanxiao ,ROUND(fee-alreadyTanxiao-currentTanxiao,2) surplusTanxiao
from (select e.* from (select e.*
,ROUND(if(alreadyDay+currentDay>=days,fee-alreadyTanxiao,if(alreadyTanxiao+currentDay*dayAmount>fee,fee-alreadyTanxiao,currentDay*dayAmount)),2) currentTanxiao ,ROUND(if(CHAR_LENGTH(IFNULL(endTime,'')) &lt; 10, if(alreadyDay+currentDay>=days,fee-alreadyTanxiao,if(alreadyTanxiao+currentDay*dayAmount>fee,fee-alreadyTanxiao,currentDay*dayAmount)), if(DATEDIFF(#{date}, LEFT(endTime,10)) &gt;= 0, 0, if(alreadyDay+currentDay>=days,fee-alreadyTanxiao,if(alreadyTanxiao+currentDay*dayAmount>fee,fee-alreadyTanxiao,currentDay*dayAmount)))),2) currentTanxiao
from (select w.name,w.tel,w.ctitle,w.cctitle,if(w.startTime is null,'',w.startTime) startTime,if(w.endTime is null,'',w.endTime) endTime,w.totalDays from (select w.name,w.tel,w.ctitle,w.cctitle,if(w.startTime is null,'',w.startTime) startTime,if(w.endTime is null,'',w.endTime) endTime,w.totalDays
,w.type,w.payType,w.payTime,w.orderSn,w.zfbOrder,w.days,w.fee,w.remark,w.dayAmount,w.alreadyDay,w.currentDay ,w.type,w.payType,w.payTime,w.orderSn,w.zfbOrder,w.days,w.fee,w.remark,w.dayAmount,w.alreadyDay,w.currentDay
,IF(beginDay=0,startTime,IF(startTime is NULL,startTime,DATE_ADD(startTime,INTERVAL beginDay-1 day))) startTanxiaoTime ,IF(beginDay=0,startTime,IF(startTime is NULL,startTime,DATE_ADD(startTime,INTERVAL beginDay-1 day))) startTanxiaoTime
,ROUND(if(alreadyDay=days,fee,if(alreadyDayAmount>fee,fee,alreadyDayAmount)),2) alreadyTanxiao ,ROUND(if(CHAR_LENGTH(IFNULL(endTime,'')) &lt; 10, if(alreadyDay=days,fee,if(alreadyDayAmount>fee,fee,alreadyDayAmount)), if(DATEDIFF(#{date}, LEFT(endTime,10)) &gt;= 0, fee, if(alreadyDay=days,fee,if(alreadyDayAmount>fee,fee,alreadyDayAmount)))),2) alreadyTanxiao
from ( from (
select q.*,alreadyDay*dayAmount alreadyDayAmount select q.*,alreadyDay*dayAmount alreadyDayAmount
,IF(days-alreadyDay=0,0,IF(beginDay=0,IF(startTime is NULL,0,IF(days-alreadyDay>=monthDays,if(DATE_FORMAT(startTime,'%Y-%m')=SUBSTR(#{date},1,7),DATEDIFF(#{date},startTime)+1,if(DATE_FORMAT(startTime,'%Y-%m')>SUBSTR(#{date},1,7),0,monthDays)),if(DATE_FORMAT(startTime, '%Y-%m')=SUBSTR(#{date},1,7),if(DATEDIFF(#{date},startTime)+1>days,days,DATEDIFF(#{date},startTime)+1),if(DATE_FORMAT(startTime, '%Y-%m') &lt; SUBSTR(#{date},1,7),days-alreadyDay,0)))), if(alreadyTotalDay+monthDays>beginDay,if(alreadyDay>0,if(days-alreadyDay>monthDays,monthDays,monthDays-(days-alreadyDay)),monthDays-(totalDays-days-alreadyTotalDay)),0) )) currentDay ,IF(days-alreadyDay=0,0,IF(beginDay=0,IF(startTime is NULL,0,IF(days-alreadyDay>=monthDays,if(DATE_FORMAT(startTime,'%Y-%m')=SUBSTR(#{date},1,7),DATEDIFF(#{date},startTime)+1,if(DATE_FORMAT(startTime,'%Y-%m')>SUBSTR(#{date},1,7),0,monthDays)),if(DATE_FORMAT(startTime, '%Y-%m')=SUBSTR(#{date},1,7),if(DATEDIFF(#{date},startTime)+1>days,days,DATEDIFF(#{date},startTime)+1),if(DATE_FORMAT(startTime, '%Y-%m') &lt; SUBSTR(#{date},1,7),days-alreadyDay,0)))), if(alreadyTotalDay+monthDays>beginDay,if(alreadyDay>0,if(days-alreadyDay>monthDays,monthDays,monthDays-(days-alreadyDay)),monthDays-(totalDays-days-alreadyTotalDay)),0) )) currentDay
@@ -41,12 +41,28 @@
</select> </select>
<select id="getIncome" resultType="map"> <select id="getIncome" resultType="map">
select ucbl.pay_type,SUM(ucbl.fee) fee select pay_type, SUM(fee) fee
from (
select ucbl.pay_type, ucbl.fee
from user_course_buy_log ucbl from user_course_buy_log ucbl
left join buy_order bo on bo.order_sn = ucbl.order_sn left join buy_order bo on bo.order_sn = ucbl.order_sn
where DATE_FORMAT(ucbl.pay_time,'%Y-%m') = SUBSTR(#{date},1,7) and ucbl.del_flag = 0 where DATE_FORMAT(ucbl.pay_time,'%Y-%m') = SUBSTR(#{date},1,7) and ucbl.del_flag = 0
and (ucbl.user_id not in (select id from user where tester_flag = 1) or ( ucbl.user_id in (select id from user where tester_flag = 1) and ucbl.pay_type in ('微信','支付宝') and ucbl.create_time>='2026-05-06 00:00:00')) and (ucbl.user_id not in (select id from user where tester_flag = 1) or ( ucbl.user_id in (select id from user where tester_flag = 1) and ucbl.pay_type in ('微信','支付宝') and ucbl.create_time>='2026-05-06 00:00:00'))
GROUP BY ucbl.pay_type union all
select IF(bo.payment_method='1','微信',IF(bo.payment_method='2','支付宝',IF(bo.payment_method='4','天医币','其他'))) pay_type,
bo.real_money fee
from buy_order bo
where bo.order_type = 'relearn' and bo.del_flag = 0
and bo.order_status not in ('5','6')
and DATE_FORMAT(IF(bo.success_time is null,bo.create_time,bo.success_time),'%Y-%m') = SUBSTR(#{date},1,7)
and not exists (
select 1 from user_course_buy_log ucbl
where ucbl.order_sn = bo.order_sn and ucbl.del_flag = 0
)
and (bo.user_id not in (select id from user where tester_flag = 1)
or (bo.user_id in (select id from user where tester_flag = 1) and bo.payment_method in ('1','2') and bo.create_time>='2026-05-06 00:00:00'))
) t
GROUP BY pay_type
</select> </select>
<select id="getRefund" resultType="map"> <select id="getRefund" resultType="map">
select pay_type,SUM(fee) fee select pay_type,SUM(fee) fee
@@ -59,11 +75,11 @@
and DATE_FORMAT(bor.create_time,'%Y-%m') = SUBSTR(#{date},1,7) and DATE_FORMAT(bor.create_time,'%Y-%m') = SUBSTR(#{date},1,7)
and (bo.user_id not in (select id from user where tester_flag = 1) and (bo.user_id not in (select id from user where tester_flag = 1)
or (bo.user_id in (select id from user where tester_flag = 1) and bo.payment_method in ('1','2') and bo.create_time>='2026-05-06 00:00:00')) or (bo.user_id in (select id from user where tester_flag = 1) and bo.payment_method in ('1','2') and bo.create_time>='2026-05-06 00:00:00'))
and exists ( and (bo.order_type = 'relearn' or exists (
select 1 from buy_order_product bop select 1 from buy_order_product bop
inner join shop_product sp on sp.product_id = bop.product_id inner join shop_product sp on sp.product_id = bop.product_id
where bop.order_id = bo.order_id and sp.goods_type = '05' where bop.order_id = bo.order_id and sp.goods_type = '05'
) ))
) t ) t
GROUP BY pay_type GROUP BY pay_type
</select> </select>
@@ -79,11 +95,11 @@
and DATE_FORMAT(IF(bo.success_time is null,bo.create_time,bo.success_time),'%Y-%m') = SUBSTR(#{date},1,7) and DATE_FORMAT(IF(bo.success_time is null,bo.create_time,bo.success_time),'%Y-%m') = SUBSTR(#{date},1,7)
and (bo.user_id not in (select id from user where tester_flag = 1) and (bo.user_id not in (select id from user where tester_flag = 1)
or (bo.user_id in (select id from user where tester_flag = 1) and bo.payment_method in ('1','2') and bo.create_time>='2026-05-06 00:00:00')) or (bo.user_id in (select id from user where tester_flag = 1) and bo.payment_method in ('1','2') and bo.create_time>='2026-05-06 00:00:00'))
and exists ( and (bo.order_type = 'relearn' or exists (
select 1 from buy_order_product bop select 1 from buy_order_product bop
inner join shop_product sp on sp.product_id = bop.product_id inner join shop_product sp on sp.product_id = bop.product_id
where bop.order_id = bo.order_id and sp.goods_type = '05' where bop.order_id = bo.order_id and sp.goods_type = '05'
) ))
) t ) t
GROUP BY pay_type GROUP BY pay_type
</select> </select>
@@ -96,16 +112,49 @@
DATE_FORMAT(IF(bo.success_time is null,bo.create_time,bo.success_time),'%Y-%m-%d %H:%i:%s') payTime, DATE_FORMAT(IF(bo.success_time is null,bo.create_time,bo.success_time),'%Y-%m-%d %H:%i:%s') payTime,
bo.order_sn orderSn,pzo.trade_no zfbOrder, bo.order_sn orderSn,pzo.trade_no zfbOrder,
0 beginDay,IFNULL(spc.days,0) days, 0 beginDay,IFNULL(spc.days,0) days,
ROUND(bor.fee/(select count(1) from buy_order_product bop2 ROUND(
bor.fee / (
case
when bo.order_type='relearn' then
IFNULL((
select count(1)
from shop_product_course spc2
where spc2.product_id = (
case
when bo.remark is null or bo.remark = '' then null
else cast(SUBSTRING_INDEX(bo.remark, ',', 1) as unsigned)
end
)
and spc2.del_flag = 0
),1)
else
IFNULL((
select count(1)
from buy_order_product bop2
inner join shop_product sp2 on sp2.product_id = bop2.product_id inner join shop_product sp2 on sp2.product_id = bop2.product_id
where bop2.order_id = bo.order_id and sp2.goods_type = '05'),2) fee, where bop2.order_id = bo.order_id and sp2.goods_type = '05'
),1)
end
),2
) fee,
if(bo.remark like '%退%',bo.remark,if(bor.remark is null,'',bor.remark)) remark, if(bo.remark like '%退%',bo.remark,if(bor.remark is null,'',bor.remark)) remark,
DATE_FORMAT(bor.create_time,'%Y-%m-%d %H:%i:%s') refundTime, DATE_FORMAT(bor.create_time,'%Y-%m-%d %H:%i:%s') refundTime,
'已退款' orderStatus '已退款' orderStatus
from buy_order_refund bor from buy_order_refund bor
inner join buy_order bo on bo.order_id = bor.order_id and bo.del_flag = 0 inner join buy_order bo on bo.order_id = bor.order_id and bo.del_flag = 0
inner join buy_order_product bop on bop.order_id = bo.order_id left join buy_order_product bop on bop.order_id = bo.order_id and bo.order_type &lt;&gt; 'relearn'
inner join shop_product sp on sp.product_id = bop.product_id and sp.goods_type = '05' left join shop_product sp
on sp.product_id = (
case
when bo.order_type='relearn' then
case
when bo.remark is null or bo.remark = '' then null
else cast(SUBSTRING_INDEX(bo.remark, ',', 1) as unsigned)
end
else bop.product_id
end
)
and sp.goods_type = '05'
left join shop_product_course spc on spc.product_id = sp.product_id and spc.del_flag = 0 left join shop_product_course spc on spc.product_id = sp.product_id and spc.del_flag = 0
left join course c on c.id = spc.course_id left join course c on c.id = spc.course_id
left join course_catalogue cc on cc.id = spc.catalogue_id left join course_catalogue cc on cc.id = spc.catalogue_id
@@ -126,11 +175,11 @@
select r.* select r.*
,ROUND(fee-alreadyTanxiao-currentTanxiao,2) surplusTanxiao ,ROUND(fee-alreadyTanxiao-currentTanxiao,2) surplusTanxiao
from (select e.* from (select e.*
,ROUND(if(alreadyDay+currentDay>=days,fee-alreadyTanxiao,if(alreadyTanxiao+currentDay*dayAmount>fee,fee-alreadyTanxiao,currentDay*dayAmount)),2) currentTanxiao ,ROUND(if(CHAR_LENGTH(IFNULL(endTime,'')) &lt; 10, if(alreadyDay+currentDay>=days,fee-alreadyTanxiao,if(alreadyTanxiao+currentDay*dayAmount>fee,fee-alreadyTanxiao,currentDay*dayAmount)), if(DATEDIFF(#{date}, LEFT(endTime,10)) &gt;= 0, 0, if(alreadyDay+currentDay>=days,fee-alreadyTanxiao,if(alreadyTanxiao+currentDay*dayAmount>fee,fee-alreadyTanxiao,currentDay*dayAmount)))),2) currentTanxiao
from (select w.name,w.tel,w.ctitle,w.cctitle,if(w.startTime is null,'',w.startTime) startTime,if(w.endTime is null,'',w.endTime) endTime,w.totalDays from (select w.name,w.tel,w.ctitle,w.cctitle,if(w.startTime is null,'',w.startTime) startTime,if(w.endTime is null,'',w.endTime) endTime,w.totalDays
,w.type,w.payType,w.payTime,w.orderSn,w.zfbOrder,w.days,w.fee,w.remark,w.dayAmount,w.alreadyDay,w.currentDay ,w.type,w.payType,w.payTime,w.orderSn,w.zfbOrder,w.days,w.fee,w.remark,w.dayAmount,w.alreadyDay,w.currentDay
,IF(beginDay=0,startTime,IF(startTime is NULL,startTime,DATE_ADD(startTime,INTERVAL beginDay-1 day))) startTanxiaoTime ,IF(beginDay=0,startTime,IF(startTime is NULL,startTime,DATE_ADD(startTime,INTERVAL beginDay-1 day))) startTanxiaoTime
,ROUND(if(alreadyDay=days,fee,if(alreadyDayAmount>fee,fee,alreadyDayAmount)),2) alreadyTanxiao ,ROUND(if(CHAR_LENGTH(IFNULL(endTime,'')) &lt; 10, if(alreadyDay=days,fee,if(alreadyDayAmount>fee,fee,alreadyDayAmount)), if(DATEDIFF(#{date}, LEFT(endTime,10)) &gt;= 0, fee, if(alreadyDay=days,fee,if(alreadyDayAmount>fee,fee,alreadyDayAmount)))),2) alreadyTanxiao
from ( from (
select q.*,alreadyDay*dayAmount alreadyDayAmount select q.*,alreadyDay*dayAmount alreadyDayAmount
,IF(days-alreadyDay=0,0,IF(beginDay=0,IF(startTime is NULL,0,IF(days-alreadyDay>=monthDays,if(DATE_FORMAT(startTime,'%Y-%m')=SUBSTR(#{date},1,7),DATEDIFF(#{date},startTime)+1,if(DATE_FORMAT(startTime,'%Y-%m')>SUBSTR(#{date},1,7),0,monthDays)),if(DATE_FORMAT(startTime, '%Y-%m')=SUBSTR(#{date},1,7),if(DATEDIFF(#{date},startTime)+1>days,days,DATEDIFF(#{date},startTime)+1),if(DATE_FORMAT(startTime, '%Y-%m') &lt; SUBSTR(#{date},1,7),days-alreadyDay,0)))), if(alreadyTotalDay+monthDays>beginDay,if(alreadyDay>0,if(days-alreadyDay>monthDays,monthDays,monthDays-(days-alreadyDay)),monthDays-(totalDays-days-alreadyTotalDay)),0) )) currentDay ,IF(days-alreadyDay=0,0,IF(beginDay=0,IF(startTime is NULL,0,IF(days-alreadyDay>=monthDays,if(DATE_FORMAT(startTime,'%Y-%m')=SUBSTR(#{date},1,7),DATEDIFF(#{date},startTime)+1,if(DATE_FORMAT(startTime,'%Y-%m')>SUBSTR(#{date},1,7),0,monthDays)),if(DATE_FORMAT(startTime, '%Y-%m')=SUBSTR(#{date},1,7),if(DATEDIFF(#{date},startTime)+1>days,days,DATEDIFF(#{date},startTime)+1),if(DATE_FORMAT(startTime, '%Y-%m') &lt; SUBSTR(#{date},1,7),days-alreadyDay,0)))), if(alreadyTotalDay+monthDays>beginDay,if(alreadyDay>0,if(days-alreadyDay>monthDays,monthDays,monthDays-(days-alreadyDay)),monthDays-(totalDays-days-alreadyTotalDay)),0) )) currentDay

View File

@@ -15,10 +15,12 @@
IF(DATE_FORMAT(uvl.start_time, '%Y-%m') > SUBSTR(#{date},1,7),0,IF(DATE_FORMAT(uvl.end_time, '%Y-%m') &lt; SUBSTR(#{date},1,7),0,IF(DATE_FORMAT(uvl.end_time, '%Y-%m') > SUBSTR(#{date},1,7),if(DATE_FORMAT(uvl.start_time, '%Y-%m') = SUBSTR(#{date},1,7),DATEDIFF(#{date},uvl.start_time)+1,DAY(#{date})),DATEDIFF(uvl.end_time,concat(SUBSTR(#{date},1,7),'-01'))+1))) currentDays, IF(DATE_FORMAT(uvl.start_time, '%Y-%m') > SUBSTR(#{date},1,7),0,IF(DATE_FORMAT(uvl.end_time, '%Y-%m') &lt; SUBSTR(#{date},1,7),0,IF(DATE_FORMAT(uvl.end_time, '%Y-%m') > SUBSTR(#{date},1,7),if(DATE_FORMAT(uvl.start_time, '%Y-%m') = SUBSTR(#{date},1,7),DATEDIFF(#{date},uvl.start_time)+1,DAY(#{date})),DATEDIFF(uvl.end_time,concat(SUBSTR(#{date},1,7),'-01'))+1))) currentDays,
IF(DATE_FORMAT(uvl.start_time, '%Y-%m') > SUBSTR(#{date},1,7),DATEDIFF(uvl.end_time,uvl.start_time)+1,(IF(DATE_FORMAT(uvl.end_time, '%Y-%m') &lt;= SUBSTR(#{date},1,7),0,DATEDIFF(uvl.end_time,#{date})))) notyetDays IF(DATE_FORMAT(uvl.start_time, '%Y-%m') > SUBSTR(#{date},1,7),DATEDIFF(uvl.end_time,uvl.start_time)+1,(IF(DATE_FORMAT(uvl.end_time, '%Y-%m') &lt;= SUBSTR(#{date},1,7),0,DATEDIFF(uvl.end_time,#{date})))) notyetDays
from user_vip_log uvl LEFT JOIN buy_order a ON a.order_sn=uvl.order_sn from user_vip_log uvl LEFT JOIN buy_order a ON a.order_sn=uvl.order_sn
LEFT JOIN buy_order_refund b ON a.order_id=b.order_id
left join user_vip uv on uv.id = uvl.user_vip_id left join user_vip uv on uv.id = uvl.user_vip_id
left join user u on u.id = uvl.user_id left join user u on u.id = uvl.user_id
left join pay_zfb_order pzo on pzo.relevanceOid = uvl.order_sn and pzo.trade_no is not null left join pay_zfb_order pzo on pzo.relevanceOid = uvl.order_sn and pzo.trade_no is not null
where u.del_flag = 0 and (uvl.del_flag = 0 or (uvl.del_flag = -1 and a.order_status=6)) where u.del_flag = 0 and (uvl.del_flag = 0 or (uvl.del_flag = -1 and a.order_status=6))
and (DATE_FORMAT(b.create_time, '%Y-%m')>=SUBSTR(#{date},1,7) || ISNULL(b.create_time))
AND (uv.del_flag = 0 OR (uv.del_flag = -1 AND a.order_status=6)) AND (uv.del_flag = 0 OR (uv.del_flag = -1 AND a.order_status=6))
and DATE_FORMAT(IF(uvl.pay_time is NULL,uvl.start_time,uvl.pay_time), '%Y-%m') &lt;= SUBSTR(#{date},1,7) and DATE_FORMAT(IF(uvl.pay_time is NULL,uvl.start_time,uvl.pay_time), '%Y-%m') &lt;= SUBSTR(#{date},1,7)
and and
@@ -264,7 +266,8 @@
left join user_vip uv on uv.id = uvl.user_vip_id left join user_vip uv on uv.id = uvl.user_vip_id
left join user u on u.id = uvl.user_id left join user u on u.id = uvl.user_id
left join pay_zfb_order pzo on pzo.relevanceOid = uvl.order_sn and pzo.trade_no is not null left join pay_zfb_order pzo on pzo.relevanceOid = uvl.order_sn and pzo.trade_no is not null
where u.del_flag = 0 and uvl.del_flag = 0 and uv.del_flag = 0 and DATE_FORMAT(IF(uvl.pay_time is NULL,uvl.start_time,uvl.pay_time), '%Y-%m') &lt;= SUBSTR(#{date},1,7) and u.id not in (select id from user where tester_flag = 1) where u.del_flag = 0 and uvl.del_flag = 0 and uv.del_flag = 0 and DATE_FORMAT(IF(uvl.pay_time is NULL,uvl.start_time,uvl.pay_time), '%Y-%m') &lt;= SUBSTR(#{date},1,7)
and (u.id not in (select id from user where tester_flag = 1) or (u.id in (select id from user where tester_flag = 1) and (uvl.pay_type in('微信','支付宝') and uvl.create_time>='2026-05-06 00:00:00')))
order by uvl.end_time asc order by uvl.end_time asc
) t order by currentDays desc ) t order by currentDays desc
) s ) s
@@ -299,7 +302,7 @@
</select> </select>
<select id="getUserVipRefundFeeTotal" resultType="java.math.BigDecimal"> <select id="getUserVipRefundFeeTotal" resultType="java.math.BigDecimal">
select IFNULL(SUM(c.fee),0) select IFNULL(SUM(uvl.price),0)
from user_vip_log uvl from user_vip_log uvl
left join user_vip uv on uv.id = uvl.user_vip_id left join user_vip uv on uv.id = uvl.user_vip_id
left join user u on u.id = uvl.user_id left join user u on u.id = uvl.user_id