Browse Source

对接拼团数据1

master
wangfukang 1 month ago
parent
commit
3a7bf15f7b
  1. 16
      hiver-admin/test-output/test-report.html
  2. 27
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/CommentController.java
  3. 17
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallCouponController.java
  4. 36
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/ShopController.java
  5. 2
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallOrderMapper.java
  6. 2
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallUserCoupon.java
  7. 87
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderAsyncConsumer.java
  8. 15
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderDelayConsumer.java
  9. 40
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/quartz/IeDailyQuotaResetTask.java
  10. 5
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderService.java
  11. 1
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallUserCouponService.java
  12. 19
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java
  13. 6
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderGroupServiceImpl.java
  14. 200
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java
  15. 72
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallRefundRecordServiceImpl.java
  16. 18
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallUserCouponServiceImpl.java
  17. 28
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/UpdateUtils.java
  18. 17
      hiver-modules/hiver-mall/src/main/resources/mapper/MallOrderMapper.xml

16
hiver-admin/test-output/test-report.html

@ -35,7 +35,7 @@
<a href="#"><span class="badge badge-primary">Hiver</span></a> <a href="#"><span class="badge badge-primary">Hiver</span></a>
</li> </li>
<li class="m-r-10"> <li class="m-r-10">
<a href="#"><span class="badge badge-primary">六月 24, 2026 14:41:22</span></a> <a href="#"><span class="badge badge-primary">六月 27, 2026 17:16:55</span></a>
</li> </li>
</ul> </ul>
</div> </div>
@ -84,7 +84,7 @@
<div class="test-detail"> <div class="test-detail">
<span class="meta text-white badge badge-sm"></span> <span class="meta text-white badge badge-sm"></span>
<p class="name">passTest</p> <p class="name">passTest</p>
<p class="text-sm"><span>14:41:23 下午</span> / <span>0.026 secs</span></p> <p class="text-sm"><span>17:16:55 下午</span> / <span>0.037 secs</span></p>
</div> </div>
<div class="test-contents d-none"> <div class="test-contents d-none">
<div class="detail-head"> <div class="detail-head">
@ -92,9 +92,9 @@
<div class="info"> <div class="info">
<div class='float-right'><span class='badge badge-default'>#test-id=1</span></div> <div class='float-right'><span class='badge badge-default'>#test-id=1</span></div>
<h5 class="test-status text-pass">passTest</h5> <h5 class="test-status text-pass">passTest</h5>
<span class='badge badge-success'>06.24.2026 14:41:23</span> <span class='badge badge-success'>06.27.2026 17:16:55</span>
<span class='badge badge-danger'>06.24.2026 14:41:23</span> <span class='badge badge-danger'>06.27.2026 17:16:55</span>
<span class='badge badge-default'>0.026 secs</span> <span class='badge badge-default'>0.037 secs</span>
</div> </div>
<div class="m-t-10 m-l-5"></div> <div class="m-t-10 m-l-5"></div>
</div> </div>
@ -104,7 +104,7 @@
<tbody> <tbody>
<tr class="event-row"> <tr class="event-row">
<td><span class="badge log pass-bg">Pass</span></td> <td><span class="badge log pass-bg">Pass</span></td>
<td>14:41:23</td> <td>17:16:55</td>
<td> <td>
Test passed Test passed
</td> </td>
@ -128,13 +128,13 @@
<div class="col-md-3"> <div class="col-md-3">
<div class="card"><div class="card-body"> <div class="card"><div class="card-body">
<p class="m-b-0">Started</p> <p class="m-b-0">Started</p>
<h3>六月 24, 2026 14:41:22</h3> <h3>六月 27, 2026 17:16:55</h3>
</div></div> </div></div>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<div class="card"><div class="card-body"> <div class="card"><div class="card-body">
<p class="m-b-0">Ended</p> <p class="m-b-0">Ended</p>
<h3>六月 24, 2026 14:41:23</h3> <h3>六月 27, 2026 17:16:55</h3>
</div></div> </div></div>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">

27
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/CommentController.java

@ -32,6 +32,7 @@ import cc.hiver.mall.service.ShopService;
import cc.hiver.mall.utils.CommentCacheUtil; import cc.hiver.mall.utils.CommentCacheUtil;
import cc.hiver.mall.utils.WorkerRedisCacheUtil; import cc.hiver.mall.utils.WorkerRedisCacheUtil;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
@ -80,6 +81,22 @@ public class CommentController {
@ApiOperation("保存") @ApiOperation("保存")
public Result save( @RequestBody List<Comment> comment) { public Result save( @RequestBody List<Comment> comment) {
comment.forEach(e -> { comment.forEach(e -> {
if(e.getScore() == null || e.getScore().compareTo(BigDecimal.ZERO) == 0){
e.setScore(BigDecimal.valueOf(5));
}
if(StringUtils.isEmpty(e.getRemark())){
if(e.getScore().equals(5)){
e.setRemark("该用户觉得很好,给出了5星好评");
}else if(e.getScore().equals(4)){
e.setRemark("该用户觉得还可以,给出了4星评价");
}else if(e.getScore().equals(3)){
e.setRemark("该用户觉得一般,给出了3星评价");
}else if(e.getScore().equals(2)){
e.setRemark("该用户觉得不好,给出了2星评价");
}else{
e.setRemark("该用户觉得很不好,给出了1星差评");
}
}
e.setCreateTime(new Date()); e.setCreateTime(new Date());
if(StringUtils.isEmpty(e.getParentId())){ if(StringUtils.isEmpty(e.getParentId())){
e.setLevel(ShopConstant.SHOP_STATUS_LOCK); e.setLevel(ShopConstant.SHOP_STATUS_LOCK);
@ -175,6 +192,16 @@ public class CommentController {
return ResultUtil.success("删除成功"); return ResultUtil.success("删除成功");
} }
@RequestMapping(value = "/getByOrderId", method = RequestMethod.POST)
@ApiOperation("根据订单id获取评论")
public Result<List<Comment>> getByOrderId( @RequestParam String orderId) {
List<Comment> comments = commentService.list(new LambdaQueryWrapper<Comment>()
.eq(Comment::getOrderId, orderId)
.eq(Comment::getLevel, ShopConstant.SHOP_STATUS_LOCK)
.orderByDesc(Comment::getCreateTime));
return ResultUtil.data(comments);
}
@RequestMapping(value = "/getCommentList", method = RequestMethod.POST) @RequestMapping(value = "/getCommentList", method = RequestMethod.POST)
@ApiOperation("获取店铺评论") @ApiOperation("获取店铺评论")
public Result<Page<Comment>> getCommentList( @RequestBody CommentQuery commentQuery) { public Result<Page<Comment>> getCommentList( @RequestBody CommentQuery commentQuery) {

17
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallCouponController.java

@ -61,6 +61,14 @@ public class MallCouponController {
private String couponId; private String couponId;
} }
@Data
public static class UserCouponPageReq {
private String userId;
private String regionId;
private Integer pageNum = 1;
private Integer pageSize = 10;
}
@PostMapping("/add") @PostMapping("/add")
@ApiOperation(value = "添加/发行优惠券") @ApiOperation(value = "添加/发行优惠券")
public Result<Object> addCoupon(@RequestBody MallCoupon coupon) { public Result<Object> addCoupon(@RequestBody MallCoupon coupon) {
@ -118,6 +126,15 @@ public class MallCouponController {
public Result<IPage<MallCoupon>> list(MallCouponQuery query) { public Result<IPage<MallCoupon>> list(MallCouponQuery query) {
return new ResultUtil<IPage<MallCoupon>>().setData(mallUserCouponService.selectPageVO(query)); return new ResultUtil<IPage<MallCoupon>>().setData(mallUserCouponService.selectPageVO(query));
} }
@PostMapping("/userList")
@ApiOperation(value = "获取用户当前校区可用优惠券列表")
public Result<IPage<MallUserCoupon>> userList(@RequestBody UserCouponPageReq query) {
return new ResultUtil<IPage<MallUserCoupon>>().setData(
mallUserCouponService.selectUserCouponPage(query.getUserId(), query.getRegionId(), query.getPageNum(), query.getPageSize())
);
}
@PostMapping("/addFreeOrder") @PostMapping("/addFreeOrder")
@ApiOperation(value = "开启免单优惠") @ApiOperation(value = "开启免单优惠")
public Result<Object> addFreeOrder(@RequestBody MallOrderFreeVO freeOrder) { public Result<Object> addFreeOrder(@RequestBody MallOrderFreeVO freeOrder) {

36
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/ShopController.java

@ -43,6 +43,7 @@ import cc.hiver.mall.service.ShopAreaService;
import cc.hiver.mall.service.ShopService; import cc.hiver.mall.service.ShopService;
import cc.hiver.mall.service.ShopTakeawayService; import cc.hiver.mall.service.ShopTakeawayService;
import cc.hiver.mall.service.ShopUserService; import cc.hiver.mall.service.ShopUserService;
import cc.hiver.mall.utils.UpdateUtils;
import cn.hutool.core.text.CharSequenceUtil; import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
@ -152,27 +153,34 @@ public class ShopController {
@ResponseBody @ResponseBody
@ApiOperation("编辑") @ApiOperation("编辑")
public Result<Object> edit(Shop shop) { public Result<Object> edit(Shop shop) {
// 获取旧店铺信息 // 1. 获取数据库中完整的旧店铺信息
Shop oldShop = shopService.get(shop.getId()); Shop oldShop = shopService.get(shop.getId());
if (oldShop == null) {
throw new RuntimeException("店铺不存在");
}
// 2. 处理商圈相关逻辑(保持你原有的业务逻辑)
if (CharSequenceUtil.isNotBlank(shop.getShopArea())) { if (CharSequenceUtil.isNotBlank(shop.getShopArea())) {
final ShopArea d = shopAreaService.get(shop.getShopArea()); final ShopArea d = shopAreaService.get(shop.getShopArea());
if (d != null) { if (d != null) {
shop.setShopAreaTitle(d.getTitle()); oldShop.setShopAreaTitle(d.getTitle());
} }
// 获取商圈的id和名称
final ShopArea topShopAreaById = shopAreaService.findTopShopAreaById(shop.getShopArea()); final ShopArea topShopAreaById = shopAreaService.findTopShopAreaById(shop.getShopArea());
shop.setRegion(topShopAreaById.getTitle()); if (topShopAreaById != null) {
shop.setRegionId(topShopAreaById.getId()); oldShop.setRegion(topShopAreaById.getTitle());
oldShop.setRegionId(topShopAreaById.getId());
}
oldShop.setShopArea(shop.getShopArea()); // 同步更新商圈ID
} else { } else {
shop.setShopArea(null); // 如果前端明确传了空,说明要清空商圈信息
shop.setShopAreaTitle(""); oldShop.setShopArea(null);
shop.setRegion(""); oldShop.setShopAreaTitle("");
shop.setRegionId(null); oldShop.setRegion("");
} oldShop.setRegionId(null);
if(shop.getSaleCount() == null){ }
shop.setSaleCount(0); UpdateUtils.copyNonNullProperties(shop, oldShop);
} // 4. 更新数据库(此时更新的是带有完整数据的 oldShop,不会误清空其他字段)
shopService.update(shop); shopService.update(oldShop);
//如果更换店主,那么就重新设置店主及店员信息 //如果更换店主,那么就重新设置店主及店员信息

2
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallOrderMapper.java

@ -27,7 +27,7 @@ public interface MallOrderMapper extends BaseMapper<MallOrder> {
List<HashMap<Integer, String>> selectOrderStatusCount(@Param("shopId") String shopId); List<HashMap<Integer, String>> selectOrderStatusCount(@Param("shopId") String shopId);
HashMap<String, Object> selectOrderRevenueAndCount(@Param("shopId") String shopId,@Param("startTime") LocalDateTime startTime,@Param("endTime") LocalDateTime endTime); List<MallOrder> selectOrderRevenueAndCount(@Param("shopId") String shopId,@Param("startTime") LocalDateTime startTime,@Param("endTime") LocalDateTime endTime);
Integer selectPendingBadReviewCount(@Param("shopId") String shopId); Integer selectPendingBadReviewCount(@Param("shopId") String shopId);

2
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallUserCoupon.java

@ -29,7 +29,7 @@ public class MallUserCoupon extends HiverBaseEntity {
@ApiModelProperty(value = "发放方ID") @ApiModelProperty(value = "发放方ID")
private String issuerId; private String issuerId;
@ApiModelProperty(value = "适用场景") @ApiModelProperty(value = "适用场景:0-通用,1-外卖/买饭,2-快递/跑腿,3-二手物品交易、4团购")
private Integer applyScene; private Integer applyScene;
@ApiModelProperty(value = "使用门槛") @ApiModelProperty(value = "使用门槛")

87
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderAsyncConsumer.java

@ -11,6 +11,7 @@ import cc.hiver.mall.entity.MallOrder;
import cc.hiver.mall.ie.service.IeMatchService; import cc.hiver.mall.ie.service.IeMatchService;
import cc.hiver.mall.planet.hook.PlanetRewardHook; import cc.hiver.mall.planet.hook.PlanetRewardHook;
import cc.hiver.mall.pojo.dto.CreateOrderDTO; import cc.hiver.mall.pojo.dto.CreateOrderDTO;
import cc.hiver.mall.pojo.vo.WorkerRedisVo;
import cc.hiver.mall.serviceimpl.mybatis.MallOrderServiceImpl; import cc.hiver.mall.serviceimpl.mybatis.MallOrderServiceImpl;
import cc.hiver.mall.utils.*; import cc.hiver.mall.utils.*;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
@ -26,9 +27,7 @@ import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.List;
/** /**
* 处理非核心响应时间不敏感的常规异步队列 * 处理非核心响应时间不敏感的常规异步队列
@ -98,21 +97,20 @@ public class OrderAsyncConsumer {
} }
List<String> targetOpenIds = new ArrayList<>(); List<String> targetOpenIds = new ArrayList<>();
List<Worker> targetWorkers = new ArrayList<>();
String orderTypeStr = "抢单大厅单"; String orderTypeStr = "抢单大厅单";
if (StringUtils.isNotBlank(delivery.getWorkerId())) { if (StringUtils.isNotBlank(delivery.getWorkerId())) {
orderTypeStr = "指定接单"; orderTypeStr = "指定接单";
// 获取指定骑手的关联 userId 和 openid WorkerRedisVo workerRedisVo = workerRedisCacheUtil.get(delivery.getRegionId(), delivery.getWorkerId());
String openId = getOfficialAccountOpenidByWorkerId(delivery.getWorkerId()); Worker worker = getOnlineWorkerInRegion(workerRedisVo, delivery.getRegionId());
if (StringUtils.isNotBlank(openId)) { if (worker != null) {
targetOpenIds.add(openId); targetWorkers.add(worker);
} }
} else { } else {
// 如果没有指定人,发给所有人?业务需求:给所有配送员发微信公众号消息 targetWorkers = getOnlineWorkersInRegion(delivery.getRegionId());
// 这可能需要查出所有 Worker 关联的 user.officialAccountOpenid (实际应用中可能需要加状态过滤)
// 为简化并实现逻辑,这里只查有效的骑手 user
targetOpenIds = getAllWorkerOpenIds();
} }
targetOpenIds = getOfficialAccountOpenids(targetWorkers);
if (targetOpenIds.isEmpty()) { if (targetOpenIds.isEmpty()) {
log.info("【异步队列处理】没有找到可推送微信模板的openid,目标跳过推送。orderId={}", orderId); log.info("【异步队列处理】没有找到可推送微信模板的openid,目标跳过推送。orderId={}", orderId);
@ -149,19 +147,68 @@ public class OrderAsyncConsumer {
} }
private String getOfficialAccountOpenidByWorkerId(String workerId) { private Worker getOnlineWorkerInRegion(WorkerRedisVo workerRedisVo, String regionId) {
Worker worker = workerService.findByWorkerId(workerId); if (workerRedisVo == null || workerRedisVo.getWorker() == null) {
if (worker != null && StringUtils.isNotBlank(worker.getUserId())) { return null;
User user = userService.findById(worker.getUserId()); }
if (user != null) { Worker worker = workerRedisVo.getWorker();
return user.getOfficialAccountOpenid(); if (!isOnlineWorkerInRegion(worker, regionId) || StringUtils.isBlank(worker.getUserId())) {
return null;
}
return worker;
}
private List<Worker> getOnlineWorkersInRegion(String regionId) {
List<Worker> workers = new ArrayList<>();
List<WorkerRedisVo> workerRedisVoList = workerRedisCacheUtil.getAll(regionId);
if (workerRedisVoList == null || workerRedisVoList.isEmpty()) {
return workers;
}
for (WorkerRedisVo workerRedisVo : workerRedisVoList) {
Worker worker = getOnlineWorkerInRegion(workerRedisVo, regionId);
if (worker != null) {
workers.add(worker);
}
}
return workers;
}
private List<String> getOfficialAccountOpenids(List<Worker> workers) {
List<String> openids = new ArrayList<>();
if (workers == null || workers.isEmpty()) {
return openids;
}
Set<String> userIds = new LinkedHashSet<>();
for (Worker worker : workers) {
if (worker != null && StringUtils.isNotBlank(worker.getUserId())) {
userIds.add(worker.getUserId());
}
}
if (userIds.isEmpty()) {
return openids;
}
List<User> users = userService.findAll((root, query, cb) -> root.get("id").in(userIds));
Map<String, String> openidMap = new LinkedHashMap<>();
for (User user : users) {
if (user != null && StringUtils.isNotBlank(user.getOfficialAccountOpenid())) {
openidMap.put(user.getId(), user.getOfficialAccountOpenid());
}
}
Set<String> dedupOpenids = new LinkedHashSet<>();
for (Worker worker : workers) {
String openid = openidMap.get(worker.getUserId());
if (StringUtils.isNotBlank(openid)) {
dedupOpenids.add(openid);
} }
} }
return null; openids.addAll(dedupOpenids);
return openids;
} }
private List<String> getAllWorkerOpenIds() { private boolean isOnlineWorkerInRegion(Worker worker, String regionId) {
return mallOrderMapper.getWeChatId(); return worker != null
&& StringUtils.equals(worker.getRegion(), regionId)
&& Integer.valueOf(1).equals(worker.getIsOnLine());
} }
@RabbitListener(queues = OrderQueueConfig.ASYNC_CACHE_QUEUE) @RabbitListener(queues = OrderQueueConfig.ASYNC_CACHE_QUEUE)

15
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderDelayConsumer.java

@ -10,6 +10,7 @@ import cc.hiver.mall.entity.MallDeliveryOrder;
import cc.hiver.mall.entity.MallOrder; import cc.hiver.mall.entity.MallOrder;
import cc.hiver.mall.entity.MallRefundRecord; import cc.hiver.mall.entity.MallRefundRecord;
import cc.hiver.mall.entity.Shop; import cc.hiver.mall.entity.Shop;
import cc.hiver.mall.service.mybatis.MallOrderService;
import cc.hiver.mall.service.mybatis.MallRefundRecordService; import cc.hiver.mall.service.mybatis.MallRefundRecordService;
import cc.hiver.mall.utils.WaitOrderCacheUtil; import cc.hiver.mall.utils.WaitOrderCacheUtil;
import cc.hiver.mall.utils.WorkerOrderCacheUtil; import cc.hiver.mall.utils.WorkerOrderCacheUtil;
@ -39,6 +40,9 @@ public class OrderDelayConsumer {
@Autowired @Autowired
private MallRefundRecordService mallRefundRecordService; private MallRefundRecordService mallRefundRecordService;
@Autowired
private MallOrderService mallOrderService;
@Autowired @Autowired
private ShopMapper shopMapper; private ShopMapper shopMapper;
@ -92,6 +96,9 @@ public class OrderDelayConsumer {
String refundId = msg.getString("refundId"); String refundId = msg.getString("refundId");
handleRefundAutoAgree(refundId); handleRefundAutoAgree(refundId);
break; break;
case "Order_Pay_Timeout_1h":
handleOrderPayTimeout(orderId);
break;
default: default:
log.warn("【订单延时处理】未知的延迟类型: {}", delayType); log.warn("【订单延时处理】未知的延迟类型: {}", delayType);
} }
@ -150,6 +157,14 @@ public class OrderDelayConsumer {
} }
} }
private void handleOrderPayTimeout(String orderId) {
if (StringUtils.isBlank(orderId)) {
log.warn("【订单支付超时处理】orderId 为空,跳过");
return;
}
mallOrderService.autoCancelUnpaidOrder(orderId);
}
private void moveAssignedOrderBackToWaitCache(String workerId, MallDeliveryOrder delivery) { private void moveAssignedOrderBackToWaitCache(String workerId, MallDeliveryOrder delivery) {
if (delivery == null || StringUtils.isBlank(delivery.getId())) { if (delivery == null || StringUtils.isBlank(delivery.getId())) {
return; return;

40
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/quartz/IeDailyQuotaResetTask.java

@ -0,0 +1,40 @@
package cc.hiver.mall.quartz;
import cc.hiver.mall.ie.constant.IeConstants;
import cc.hiver.mall.ie.entity.IeUserProfile;
import cc.hiver.mall.ie.mapper.IeUserProfileMapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* i/e 每日匹配次数重置任务
*/
@Slf4j
@Component
public class IeDailyQuotaResetTask {
@Autowired
private IeUserProfileMapper userProfileMapper;
/**
* 每天凌晨重置当日匹配次数上限避免下单奖励跨天累加
*/
@Scheduled(cron = "0 0 0 * * ?")
public void resetDailyQuota() {
try {
LambdaUpdateWrapper<IeUserProfile> wrapper = new LambdaUpdateWrapper<>();
wrapper.set(IeUserProfile::getDailyQuota, IeConstants.DEFAULT_DAILY_QUOTA)
.set(IeUserProfile::getUsedQuota, 0)
.set(IeUserProfile::getUpdateTime, new Date());
int count = userProfileMapper.update(null, wrapper);
log.info("[i/e每日匹配次数重置] 完成,处理 {} 个用户", count);
} catch (Exception e) {
log.error("[i/e每日匹配次数重置] 任务异常: {}", e.getMessage(), e);
}
}
}

5
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderService.java

@ -52,6 +52,11 @@ public interface MallOrderService extends IService<MallOrder> {
*/ */
void cancelOrder(String orderId, String userId,Integer refundType, Integer refundTypeStatus, String reason, String pictures); void cancelOrder(String orderId, String userId,Integer refundType, Integer refundTypeStatus, String reason, String pictures);
/**
* 系统自动取消超时未支付订单
*/
void autoCancelUnpaidOrder(String orderId);
/** /**
* 申请退款 * 申请退款
*/ */

1
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallUserCouponService.java

@ -16,5 +16,6 @@ public interface MallUserCouponService extends IService<MallUserCoupon> {
boolean useCoupon(String orderId); boolean useCoupon(String orderId);
boolean refundCoupon(String orderId); boolean refundCoupon(String orderId);
IPage<MallCoupon> selectPageVO(MallCouponQuery q); IPage<MallCoupon> selectPageVO(MallCouponQuery q);
IPage<MallUserCoupon> selectUserCouponPage(String userId, String regionId, Integer pageNum, Integer pageSize);
void send(String userPhones,Integer type,String couponId,Integer giveNum); void send(String userPhones,Integer type,String couponId,Integer giveNum);
} }

19
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java

@ -722,7 +722,7 @@ public class MallDeliveryOrderServiceImpl extends ServiceImpl<MallDeliveryOrderM
//增加配送员余额 //增加配送员余额
Worker worker = workerServiceImpl.findByWorkerId(delivery.getWorkerId()); Worker worker = workerServiceImpl.findByWorkerId(delivery.getWorkerId());
//更新余额 //更新余额
worker.setDepoBal(worker.getDepoBal().add(delivery.getDeliveryFee())); worker.setDepoBal(worker.getDepoBal().add(delivery.getDeliveryFee().add(delivery.getDeliveryFeeMarketplace() == null ? BigDecimal.ZERO : delivery.getDeliveryFeeMarketplace())).setScale(2, RoundingMode.UP));
Integer oldCount = worker.getOrderCount() == null ? 0 : worker.getOrderCount(); Integer oldCount = worker.getOrderCount() == null ? 0 : worker.getOrderCount();
//更新单量 //更新单量
worker.setOrderCount(oldCount + 1); worker.setOrderCount(oldCount + 1);
@ -749,7 +749,7 @@ public class MallDeliveryOrderServiceImpl extends ServiceImpl<MallDeliveryOrderM
//增加余额变动记录 //增加余额变动记录
DealingsRecord dealingsRecord = new DealingsRecord(); DealingsRecord dealingsRecord = new DealingsRecord();
dealingsRecord.setDealingsWay(DealingsRecordConstant.TYPE_REMARK[0]); dealingsRecord.setDealingsWay(DealingsRecordConstant.TYPE_REMARK[0]);
dealingsRecord.setAmount(delivery.getDeliveryFee()); dealingsRecord.setAmount(delivery.getDeliveryFee().add(delivery.getDeliveryFeeMarketplace() == null ? BigDecimal.ZERO : delivery.getDeliveryFeeMarketplace()).setScale(2, RoundingMode.UP));
dealingsRecord.setDealingsTime(new Date()); dealingsRecord.setDealingsTime(new Date());
dealingsRecord.setLinkId(deliveryId); dealingsRecord.setLinkId(deliveryId);
dealingsRecord.setLinkUserId(worker.getWorkerId()); dealingsRecord.setLinkUserId(worker.getWorkerId());
@ -1180,14 +1180,26 @@ public class MallDeliveryOrderServiceImpl extends ServiceImpl<MallDeliveryOrderM
throw new RuntimeException("只有待接单状态的配送单才能重新指定配送员"); throw new RuntimeException("只有待接单状态的配送单才能重新指定配送员");
} }
String oldWorkerId = delivery.getWorkerId(); String oldWorkerId = delivery.getWorkerId();
//直接购买 如果第一次没有指定就没有生成 平台给的额外佣金
MallOrder order = mallOrderService.getById(delivery.getOrderId());
BigDecimal bonus = null;
if(order.getOrderType() == 1 && (delivery.getDeliveryFeeMarketplace() == null || delivery.getDeliveryFeeMarketplace().compareTo(BigDecimal.ZERO) == 0)){
bonus = order.getGoodsAmount()
.multiply(new BigDecimal("0.02"))
.setScale(2, RoundingMode.DOWN);
delivery.setDeliveryFeeMarketplace(bonus);
}
LambdaUpdateWrapper<MallDeliveryOrder> uw = new LambdaUpdateWrapper<>(); LambdaUpdateWrapper<MallDeliveryOrder> uw = new LambdaUpdateWrapper<>();
uw.eq(MallDeliveryOrder::getId, deliveryId) uw.eq(MallDeliveryOrder::getId, deliveryId)
.set(MallDeliveryOrder::getWorkerId, newWorkerId) .set(MallDeliveryOrder::getWorkerId, newWorkerId)
.set(MallDeliveryOrder::getWorkerName, newWorkerName) .set(MallDeliveryOrder::getWorkerName, newWorkerName)
.set(MallDeliveryOrder::getWorkerPhone, newWorkerPhone); .set(MallDeliveryOrder::getWorkerPhone, newWorkerPhone);
if(bonus != null){
uw.set(MallDeliveryOrder::getDeliveryFeeMarketplace, bonus);
}
this.update(uw); this.update(uw);
delivery.setWorkerId(newWorkerId); delivery.setWorkerId(newWorkerId);
delivery.setWorkerName(newWorkerName); delivery.setWorkerName(newWorkerName);
delivery.setWorkerPhone(newWorkerPhone); delivery.setWorkerPhone(newWorkerPhone);
@ -1220,6 +1232,7 @@ public class MallDeliveryOrderServiceImpl extends ServiceImpl<MallDeliveryOrderM
if (StringUtils.isNotBlank(oldWorkerId)) { if (StringUtils.isNotBlank(oldWorkerId)) {
workerOrderCacheUtil.remove(oldWorkerId, delivery.getId()); workerOrderCacheUtil.remove(oldWorkerId, delivery.getId());
} }
cacheDelivery.setDeliveryFeeMarketplace(delivery.getDeliveryFeeMarketplace());
if (cacheDelivery == null) { if (cacheDelivery == null) {
cacheDelivery = delivery; cacheDelivery = delivery;
assembleOrderGoodsForDeliveries(Collections.singletonList(cacheDelivery)); assembleOrderGoodsForDeliveries(Collections.singletonList(cacheDelivery));

6
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderGroupServiceImpl.java

@ -29,8 +29,10 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -166,7 +168,9 @@ public class MallOrderGroupServiceImpl extends ServiceImpl<MallOrderGroupMapper,
LambdaUpdateWrapper<MallOrder> ouw = new LambdaUpdateWrapper<>(); LambdaUpdateWrapper<MallOrder> ouw = new LambdaUpdateWrapper<>();
//代表该拼团子订单订单选择到店 //代表该拼团子订单订单选择到店
if(targetStatus == 3){ if(targetStatus == 3){
String latestSeq = merchantOrderSeqUtil.generateOrderSequence(order.getShopId(), 2); String latestSeq = String.format("%s%s",
LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE),
merchantOrderSeqUtil.generateOrderSequence(order.getShopId(), 2));
ouw.eq(MallOrder::getId, order.getId()) ouw.eq(MallOrder::getId, order.getId())
.eq(MallOrder::getStatus, ORDER_STATUS_WAIT_GROUP) .eq(MallOrder::getStatus, ORDER_STATUS_WAIT_GROUP)
.set(MallOrder::getStatus, targetStatus) .set(MallOrder::getStatus, targetStatus)

200
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java

@ -48,6 +48,7 @@ import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -71,6 +72,9 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
private static final int STATUS_REFUNDED = 8; private static final int STATUS_REFUNDED = 8;
private static final int STATUS_WAIT_RETURN = 11;//售后中 private static final int STATUS_WAIT_RETURN = 11;//售后中
private static final int STATUS__RETURNED = 12;//已售后 private static final int STATUS__RETURNED = 12;//已售后
private static final int REFUND_STATUS_AFTER_SALE_AGREED = 4;//同意售后
private static final String DELAY_TYPE_ORDER_PAY_TIMEOUT = "Order_Pay_Timeout_1h";
private static final long ORDER_PAY_TIMEOUT_DELAY_MILLIS = 60 * 60 * 1000L;
// 配送方式常量 // 配送方式常量
private static final int DELIVERY_TYPE_EXPRESS = 1; // 外卖 private static final int DELIVERY_TYPE_EXPRESS = 1; // 外卖
@ -338,6 +342,8 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
createDeliveryOrder(order, dto, null, -1, null); createDeliveryOrder(order, dto, null, -1, null);
} }
triggerOrderPayTimeoutEvent(order.getId());
return buildVO(order, goodsSnapshots, null, null); return buildVO(order, goodsSnapshots, null, null);
} }
@ -451,7 +457,15 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
// 拼团过期时间:24小时后 // 拼团过期时间:24小时后
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR_OF_DAY, 24); if(order.getOtherOrder() != null && order.getOtherOrder() == 2){
//团购拼团24小时过期
cal.add(Calendar.HOUR_OF_DAY, 24);
}
if(order.getOtherOrder() == null){
//团购拼团24小时过期
cal.add(Calendar.HOUR_OF_DAY, 1);
}
group.setExpireTime(cal.getTime()); group.setExpireTime(cal.getTime());
// 设置配送员和佣金信息 // 设置配送员和佣金信息
@ -481,6 +495,8 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
createDeliveryOrder(order, dto, group.getId(), -1, isFace2Face ? group.getTotalDeliveryFee() : null); createDeliveryOrder(order, dto, group.getId(), -1, isFace2Face ? group.getTotalDeliveryFee() : null);
} }
triggerOrderPayTimeoutEvent(order.getId());
// 将 groupId 关联到订单 (MallOrder 没有 groupId 字段,通过 MallOrderGroup 关联已足够) // 将 groupId 关联到订单 (MallOrder 没有 groupId 字段,通过 MallOrderGroup 关联已足够)
return buildVO(order, goodsSnapshots, group, null); return buildVO(order, goodsSnapshots, group, null);
@ -496,15 +512,15 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
if (group == null) { if (group == null) {
throw new RuntimeException("拼团不存在"); throw new RuntimeException("拼团不存在");
} }
/*if (group.getStatus() != GROUP_STATUS_FORMING && group.getStatus() != GROUP_STATUS_FACE2FACE) { if (group.getStatus() == GROUP_STATUS_FAIL) {
throw new RuntimeException("当前拼团状态不可参团"); throw new RuntimeException("团长已取消拼团");
}*/ }
//超出拼团人数 也可以参与 //超出拼团人数 也可以参与
/*if (group.getCurrentMembers() >= group.getTargetMembers()) { /*if (group.getCurrentMembers() >= group.getTargetMembers()) {
throw new RuntimeException("拼团已满员"); throw new RuntimeException("拼团已满员");
}*/ }*/
boolean isFace2Face = (group.getStatus() == GROUP_STATUS_FACE2FACE); boolean isFace2Face = (group.getIsFace() == 1);
// 面对面团:不再重新计算,直接使用根据 targetMembers 均摊的金额 // 面对面团:不再重新计算,直接使用根据 targetMembers 均摊的金额
BigDecimal deliveryFee = BigDecimal.ZERO; BigDecimal deliveryFee = BigDecimal.ZERO;
@ -575,6 +591,8 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
createDeliveryOrder(order, dto, group.getId(), -1, null); createDeliveryOrder(order, dto, group.getId(), -1, null);
} }
triggerOrderPayTimeoutEvent(order.getId());
return buildVO(order, goodsSnapshots, group, null); return buildVO(order, goodsSnapshots, group, null);
} }
@ -650,7 +668,9 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
triggerDeliveryAsyncEvents(order, deliveryOrder); triggerDeliveryAsyncEvents(order, deliveryOrder);
autoAcceptShopDeliveryOrder(order, deliveryOrder); autoAcceptShopDeliveryOrder(order, deliveryOrder);
} else { } else {
String latestSeq = merchantOrderSeqUtil.generateOrderSequence(order.getShopId(), 2); String latestSeq = String.format("%s%s",
LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE),
merchantOrderSeqUtil.generateOrderSequence(order.getShopId(), 2));
LambdaUpdateWrapper<MallOrder> uw = new LambdaUpdateWrapper<>(); LambdaUpdateWrapper<MallOrder> uw = new LambdaUpdateWrapper<>();
uw.eq(MallOrder::getId, orderId) uw.eq(MallOrder::getId, orderId)
.set(MallOrder::getStatus, STATUS_WAIT_PICKUP) .set(MallOrder::getStatus, STATUS_WAIT_PICKUP)
@ -670,6 +690,26 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
} }
} }
} else if (order.getOrderType() == ORDER_TYPE_GROUP || order.getOrderType() == ORDER_TYPE_FACETOFACE) { } else if (order.getOrderType() == ORDER_TYPE_GROUP || order.getOrderType() == ORDER_TYPE_FACETOFACE) {
/* // 查找属于该订单的拼团并检查成团条件
LambdaQueryWrapper<MallOrderGroup> gq = new LambdaQueryWrapper<>();
gq.eq(MallOrderGroup::getHeadOrderId, orderId);
MallOrderGroup group = mallOrderGroupMapper.selectOne(gq);*/
LambdaQueryWrapper<MallOrderGroup> gq2 = new LambdaQueryWrapper<>();
gq2.eq(MallOrderGroup::getShopId, order.getShopId())
.like(MallOrderGroup::getGroupOrderIds, order.getId())
.last("LIMIT 1");
MallOrderGroup group = mallOrderGroupMapper.selectOne(gq2);
if(group.getStatus() == 2){
//拼团失败,给用户退款,同时更新order
updateOrderStatus(orderId, 6, null);
wechatPayUtil.refund(order.getId(), toCent(order.getTotalAmount()), toCent(order.getTotalAmount()));
log.info("拼团失败,已自动发起退款, orderId={}", orderId);
return;
}
// 拼团订单支付成功 -> 改为待成团 // 拼团订单支付成功 -> 改为待成团
updateOrderStatus(orderId, STATUS_WAIT_GROUP, payTime); updateOrderStatus(orderId, STATUS_WAIT_GROUP, payTime);
@ -677,17 +717,9 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
orderVO.setStatus(STATUS_WAIT_GROUP); orderVO.setStatus(STATUS_WAIT_GROUP);
userPendingOrderCacheUtil.update(order.getUserId(), orderVO); userPendingOrderCacheUtil.update(order.getUserId(), orderVO);
// 查找属于该订单的拼团并检查成团条件
LambdaQueryWrapper<MallOrderGroup> gq = new LambdaQueryWrapper<>(); if (!group.getHeadOrderId().equals(orderId)) {
gq.eq(MallOrderGroup::getHeadOrderId, orderId);
MallOrderGroup group = mallOrderGroupMapper.selectOne(gq);
if (group == null) {
// 如果不是团长,按参团人查找 // 如果不是团长,按参团人查找
LambdaQueryWrapper<MallOrderGroup> gq2 = new LambdaQueryWrapper<>();
gq2.eq(MallOrderGroup::getShopId, order.getShopId())
.like(MallOrderGroup::getGroupOrderIds, order.getId())
.last("LIMIT 1");
group = mallOrderGroupMapper.selectOne(gq2);
//更新参团人数+1 //更新参团人数+1
int newMembers = group.getCurrentMembers() + 1; int newMembers = group.getCurrentMembers() + 1;
LambdaUpdateWrapper<MallOrderGroup> guw = new LambdaUpdateWrapper<>(); LambdaUpdateWrapper<MallOrderGroup> guw = new LambdaUpdateWrapper<>();
@ -745,6 +777,14 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
} }
} }
private long toCent(BigDecimal amount) {
return safeAmount(amount).multiply(new BigDecimal(100)).longValue();
}
private BigDecimal safeAmount(BigDecimal amount) {
return amount == null ? BigDecimal.ZERO : amount;
}
/** /**
* 触发配送订单异步事件微信推给骑手加入超时检查死信队列等 * 触发配送订单异步事件微信推给骑手加入超时检查死信队列等
*/ */
@ -777,6 +817,14 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
// 如果需要检测出餐超时,发一条出餐超时延时消息 // 如果需要检测出餐超时,发一条出餐超时延时消息
//triggerShopCookTimeoutEvent(orderId); //triggerShopCookTimeoutEvent(orderId);
} }
private void triggerOrderPayTimeoutEvent(String orderId) {
try {
orderAsyncProducer.sendDelayMessage(orderId, DELAY_TYPE_ORDER_PAY_TIMEOUT, ORDER_PAY_TIMEOUT_DELAY_MILLIS);
} catch (Exception e) {
log.warn("发送订单支付超时延时消息失败,不影响下单 orderId={}, err={}", orderId, e.getMessage());
}
}
@Autowired @Autowired
private cc.hiver.mall.dao.mapper.ShopTakeawayMapper shopTakeawayMapper; private cc.hiver.mall.dao.mapper.ShopTakeawayMapper shopTakeawayMapper;
@ -835,6 +883,30 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
} }
} }
@Override
@Transactional(rollbackFor = Exception.class)
public void autoCancelUnpaidOrder(String orderId) {
if (StringUtils.isBlank(orderId)) {
return;
}
MallOrder order = getById(orderId);
if (order == null) {
log.warn("支付超时自动取消订单失败,订单不存在 orderId={}", orderId);
return;
}
if (order.getStatus() == null || order.getStatus() != STATUS_WAIT_PAY) {
log.info("支付超时自动取消订单跳过,订单已处理 orderId={}, status={}", orderId, order.getStatus());
return;
}
if (order.getOrderType() == ORDER_TYPE_GROUP || order.getOrderType() == ORDER_TYPE_FACETOFACE) {
handleGroupCancel(order, 3, 1, "支付超时自动取消", null);
} else {
handleDirectCancel(order, 3, 1, "支付超时自动取消", null);
}
log.info("支付超时自动取消订单成功 orderId={}", orderId);
}
// ================================================================ // ================================================================
// 拼团取消逻辑 // 拼团取消逻辑
// ================================================================ // ================================================================
@ -1218,8 +1290,8 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
public void completeOrder(String orderId) { public void completeOrder(String orderId) {
MallOrder order = getById(orderId); MallOrder order = getById(orderId);
if (order == null) throw new RuntimeException("订单不存在"); if (order == null) throw new RuntimeException("订单不存在");
if (order.getStatus() != STATUS_WAIT_PICKUP && order.getStatus() != STATUS_DELIVERING) { if (order.getStatus() != STATUS_WAIT_PICKUP) {
throw new RuntimeException("当前状态不可完成"); throw new RuntimeException("当前订单不可核销");
} }
//优惠券变成已使用 //优惠券变成已使用
mallUserCouponService.useCoupon(orderId); mallUserCouponService.useCoupon(orderId);
@ -1271,8 +1343,9 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
// 3. 生成结束时间:当天的 23:59:59 // 3. 生成结束时间:当天的 23:59:59
LocalDateTime endTime = date.atTime(LocalTime.MAX); LocalDateTime endTime = date.atTime(LocalTime.MAX);
//统计营业额和订单量 //统计营业额、订单量、已同意售后笔数和售后商品金额
HashMap<String, Object> orderRevenueAndCount = this.baseMapper.selectOrderRevenueAndCount(shopId,startTime,endTime); List<MallOrder> revenueOrders = this.baseMapper.selectOrderRevenueAndCount(shopId,startTime,endTime);
HashMap<String, Object> orderRevenueAndCount = buildOrderRevenueAndCount(shopId, revenueOrders);
result.put("orderRevenueAndCount", orderRevenueAndCount); result.put("orderRevenueAndCount", orderRevenueAndCount);
//待处理差评订单数 //待处理差评订单数
Integer pendingBadReviewCount = this.baseMapper.selectPendingBadReviewCount(shopId); Integer pendingBadReviewCount = this.baseMapper.selectPendingBadReviewCount(shopId);
@ -1289,6 +1362,75 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
return result; return result;
} }
private HashMap<String, Object> buildOrderRevenueAndCount(String shopId, List<MallOrder> revenueOrders) {
HashMap<String, Object> orderRevenueAndCount = new HashMap<>();
List<MallOrder> orders = revenueOrders == null ? Collections.emptyList() : revenueOrders;
BigDecimal amount = orders.stream()
.map(order -> safeAmount(order.getGoodsAmount()).add(safeAmount(order.getPackageFee())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
Map<String, MallOrder> orderMap = orders.stream()
.filter(order -> StringUtils.isNotBlank(order.getId()))
.collect(Collectors.toMap(MallOrder::getId, order -> order, (left, right) -> left));
orderRevenueAndCount.put("count", orders.size());
orderRevenueAndCount.put("amount", amount);
orderRevenueAndCount.put("afterSaleCount", 0);
orderRevenueAndCount.put("afterSaleAmount", BigDecimal.ZERO);
List<String> orderIds = orders.stream()
.map(MallOrder::getId)
.filter(StringUtils::isNotBlank)
.distinct()
.collect(Collectors.toList());
if (orderIds.isEmpty()) {
return orderRevenueAndCount;
}
LambdaQueryWrapper<MallRefundRecord> refundWrapper = new LambdaQueryWrapper<>();
refundWrapper.eq(MallRefundRecord::getLinkId, shopId)
.eq(MallRefundRecord::getStatus, REFUND_STATUS_AFTER_SALE_AGREED)
.in(MallRefundRecord::getOrderId, orderIds);
List<MallRefundRecord> refundRecords = mallRefundRecordMapper.selectList(refundWrapper);
if (refundRecords == null || refundRecords.isEmpty()) {
return orderRevenueAndCount;
}
List<String> refundOrderIds = refundRecords.stream()
.map(MallRefundRecord::getOrderId)
.filter(StringUtils::isNotBlank)
.distinct()
.collect(Collectors.toList());
if (refundOrderIds.isEmpty()) {
orderRevenueAndCount.put("afterSaleCount", refundRecords.size());
return orderRevenueAndCount;
}
LambdaQueryWrapper<MallReturnOrderGoods> goodsWrapper = new LambdaQueryWrapper<>();
goodsWrapper.in(MallReturnOrderGoods::getOrderId, refundOrderIds);
List<MallReturnOrderGoods> returnGoodsList = mallReturnOrderGoodsMapper.selectList(goodsWrapper);
BigDecimal afterSaleAmount = returnGoodsList == null ? BigDecimal.ZERO : returnGoodsList.stream()
.map(goods -> calculateReturnGoodsAmount(goods, orderMap.get(goods.getOrderId())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
orderRevenueAndCount.put("afterSaleCount", refundRecords.size());
orderRevenueAndCount.put("afterSaleAmount", afterSaleAmount);
return orderRevenueAndCount;
}
private BigDecimal calculateReturnGoodsAmount(MallReturnOrderGoods goods, MallOrder order) {
BigDecimal quantity = goods.getQuantity() == null ? BigDecimal.ZERO : BigDecimal.valueOf(goods.getQuantity());
BigDecimal unitAmount = safeAmount(goods.getPrice());
if (shouldIncludePackageFee(order)) {
unitAmount = unitAmount.add(safeAmount(goods.getPackageFee()));
}
return unitAmount.multiply(quantity);
}
private boolean shouldIncludePackageFee(MallOrder order) {
return order != null
&& (Objects.equals(order.getDeliveryType(), DELIVERY_TYPE_EXPRESS) || Objects.equals(order.getIsPack(), 1));
}
@Override @Override
public List<MallOrderVO> getPeiSongOrders(String userId) { public List<MallOrderVO> getPeiSongOrders(String userId) {
List<MallOrderVO> orders = this.baseMapper.getPeiSongOrders(userId); List<MallOrderVO> orders = this.baseMapper.getPeiSongOrders(userId);
@ -1430,7 +1572,7 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
LambdaQueryWrapper<MallRefundRecord> rq = new LambdaQueryWrapper<>(); LambdaQueryWrapper<MallRefundRecord> rq = new LambdaQueryWrapper<>();
rq.eq(MallRefundRecord::getOrderId, orderId); rq.eq(MallRefundRecord::getOrderId, orderId);
List<MallRefundRecord> sallRefundRecord = mallRefundRecordMapper.selectList(rq); List<MallRefundRecord> sallRefundRecord = mallRefundRecordMapper.selectList(rq);
if(sallRefundRecord != null){ if(!sallRefundRecord.isEmpty()){
sallRefundRecord.forEach(item -> { sallRefundRecord.forEach(item -> {
if(item.getRefundType() == 1){ if(item.getRefundType() == 1){
//售后商品详情 //售后商品详情
@ -1969,14 +2111,18 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
//发送1小时延时MQ:超时未处理则自动同意 //发送1小时延时MQ:超时未处理则自动同意
orderAsyncProducer.sendRefundDelayMessage(record.getId(), order.getId()); orderAsyncProducer.sendRefundDelayMessage(record.getId(), order.getId());
//发送商家通知 //发送商家通知
jPushService.sendPushNotification(shopService.findById(order.getShopId()).getClientId(), "您有一笔订单申请退款,配送员原因",order.getId()); if(order.getOtherOrder() == null){
jPushService.sendPushNotification(shopService.findById(order.getShopId()).getClientId(), "您有一笔订单申请退款,配送员原因",order.getId());
}
}else if(refundTypeStatus == 3){ }else if(refundTypeStatus == 3){
//商家、配送员都加一条 //商家、配送员都加一条
record.setLinkId(order.getShopId()); record.setLinkId(order.getShopId());
if(ratio != null){ BigDecimal shopRefund = BigDecimal.ZERO;
record.setRefundAmount(order.getGoodsAmount().add(order.getPackageFee()).subtract(order.getGoodsAmount().add(order.getPackageFee()).multiply(ratio)).setScale(2, RoundingMode.HALF_UP)); if(ratio != null && ratio.compareTo(BigDecimal.ZERO) > 0){
shopRefund = order.getGoodsAmount().add(order.getPackageFee()).subtract(order.getGoodsAmount().add(order.getPackageFee()).multiply(ratio)).setScale(2, RoundingMode.UP);
record.setRefundAmount(shopRefund);
}else{ }else{
record.setRefundAmount(order.getGoodsAmount().add(order.getPackageFee()).setScale(2, RoundingMode.HALF_UP)); record.setRefundAmount(order.getGoodsAmount().add(order.getPackageFee()));
} }
/*if(ratio != null){ /*if(ratio != null){
@ -1993,8 +2139,8 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
MallRefundRecord mallRefundRecord1 = record; MallRefundRecord mallRefundRecord1 = record;
mallRefundRecord1.setId(SnowFlakeUtil.nextId().toString()); mallRefundRecord1.setId(SnowFlakeUtil.nextId().toString());
mallRefundRecord1.setLinkId(workerId); mallRefundRecord1.setLinkId(workerId);
if(ratio != null){ if(ratio != null && ratio.compareTo(BigDecimal.ZERO) > 0){
mallRefundRecord1.setRefundAmount(order.getDeliveryFee().subtract(order.getDeliveryFee().multiply(ratio)).setScale(2, RoundingMode.HALF_UP)); mallRefundRecord1.setRefundAmount(order.getTotalAmount().subtract(shopRefund));
}else{ }else{
mallRefundRecord1.setRefundAmount(order.getDeliveryFee()); mallRefundRecord1.setRefundAmount(order.getDeliveryFee());
} }

72
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallRefundRecordServiceImpl.java

@ -23,6 +23,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -30,6 +31,7 @@ import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -55,6 +57,7 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
private static final int PAGESIZE = 10; private static final int PAGESIZE = 10;
private static final int PAGENUM = 1; private static final int PAGENUM = 1;
private static final long AFTER_SALE_REFUND_LOCK_SECONDS = 30L;
@Autowired @Autowired
private MallRefundRecordMapper mallRefundRecordMapper; private MallRefundRecordMapper mallRefundRecordMapper;
@ -89,6 +92,9 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
@Autowired @Autowired
private WechatPayUtil wechatPayUtil; private WechatPayUtil wechatPayUtil;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired @Autowired
private MallOrderDeliveryFeeRefundHelper mallOrderDeliveryFeeRefundHelper; private MallOrderDeliveryFeeRefundHelper mallOrderDeliveryFeeRefundHelper;
@ -174,9 +180,13 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
if(mallRefundRecord.getRefundType() == 1){ if(mallRefundRecord.getRefundType() == 1){
// 退商品,和商家有关联 // 退商品,和商家有关联
mallRefundRecord.setLinkId(mallRefundRecord.getShopId()); mallRefundRecord.setLinkId(mallRefundRecord.getShopId());
if(ratio != null && !ratio.equals(0)){ if(ratio != null && ratio.compareTo(BigDecimal.ZERO) > 0){
mallRefundRecord.setRefundAmount(mallRefundRecord.getRefundAmount() mallRefundRecord.setRefundAmount(mallRefundRecord.getRefundAmount()
.subtract(mallRefundRecord.getRefundAmount().multiply(ratio)).setScale(2, RoundingMode.HALF_UP)); .subtract(mallRefundRecord.getRefundAmount().multiply(ratio)).setScale(2, RoundingMode.UP));
}
//订单金额优惠后 变成0.01后退款变成0.01
if(order.getTotalAmount().compareTo(new BigDecimal("0.01")) == 0){
mallRefundRecord.setRefundAmount(BigDecimal.valueOf(0.01));
} }
mallRefundRecordMapper.insert(mallRefundRecord); mallRefundRecordMapper.insert(mallRefundRecord);
//售后放到缓存 //售后放到缓存
@ -187,9 +197,13 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
}else if(mallRefundRecord.getRefundType() == 2){ }else if(mallRefundRecord.getRefundType() == 2){
// 退配送费和配送员有关联 // 退配送费和配送员有关联
mallRefundRecord.setLinkId(mallRefundRecord.getWorkerId()); mallRefundRecord.setLinkId(mallRefundRecord.getWorkerId());
if(ratio != null){ if(ratio != null && ratio.compareTo(BigDecimal.ZERO) > 0){
mallRefundRecord.setRefundAmount(mallRefundRecord.getRefundAmount() mallRefundRecord.setRefundAmount(mallRefundRecord.getRefundAmount()
.subtract(mallRefundRecord.getRefundAmount().multiply(ratio)).setScale(2, RoundingMode.HALF_UP)); .subtract(mallRefundRecord.getRefundAmount().multiply(ratio)).setScale(2, RoundingMode.UP));
}
//订单金额优惠后 变成0.01后退款变成0.01
if(order.getTotalAmount().compareTo(new BigDecimal("0.01")) == 0){
mallRefundRecord.setRefundAmount(BigDecimal.valueOf(0.01));
} }
mallRefundRecordMapper.insert(mallRefundRecord); mallRefundRecordMapper.insert(mallRefundRecord);
//售后放到缓存 //售后放到缓存
@ -221,9 +235,11 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
//商家、配送员都加一条,和商家+配送员都有关联 //商家、配送员都加一条,和商家+配送员都有关联
mallRefundRecord.setLinkId(mallRefundRecord.getShopId()); mallRefundRecord.setLinkId(mallRefundRecord.getShopId());
mallRefundRecord.setRefundAmount(mallRefundRecord.getGoodsAmount().add(mallRefundRecord.getPackageFee())); mallRefundRecord.setRefundAmount(mallRefundRecord.getGoodsAmount().add(mallRefundRecord.getPackageFee()));
if(ratio != null){ BigDecimal shopRefund = BigDecimal.ZERO;
mallRefundRecord.setRefundAmount(mallRefundRecord.getRefundAmount() if(ratio != null && ratio.compareTo(BigDecimal.ZERO) > 0){
.subtract(mallRefundRecord.getRefundAmount().multiply(ratio)).setScale(2, RoundingMode.HALF_UP)); shopRefund = mallRefundRecord.getRefundAmount()
.subtract(mallRefundRecord.getRefundAmount().multiply(ratio)).setScale(2, RoundingMode.UP);
mallRefundRecord.setRefundAmount(shopRefund);
} }
mallRefundRecordMapper.insert(mallRefundRecord); mallRefundRecordMapper.insert(mallRefundRecord);
//售后放到缓存 //售后放到缓存
@ -235,9 +251,8 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
mallRefundRecord1.setId(SnowFlakeUtil.nextId().toString()); mallRefundRecord1.setId(SnowFlakeUtil.nextId().toString());
mallRefundRecord1.setLinkId(mallRefundRecord.getWorkerId()); mallRefundRecord1.setLinkId(mallRefundRecord.getWorkerId());
mallRefundRecord1.setRefundAmount(mallRefundRecord.getDeliveryFee()); mallRefundRecord1.setRefundAmount(mallRefundRecord.getDeliveryFee());
if(ratio != null){ if(ratio != null && ratio.compareTo(BigDecimal.ZERO) > 0){
mallRefundRecord1.setRefundAmount(mallRefundRecord1.getRefundAmount() mallRefundRecord1.setRefundAmount(order.getTotalAmount().subtract(shopRefund));
.subtract(mallRefundRecord1.getRefundAmount().multiply(ratio)).setScale(2, RoundingMode.HALF_UP));
} }
mallRefundRecordMapper.insert(mallRefundRecord1); mallRefundRecordMapper.insert(mallRefundRecord1);
//售后放到缓存 //售后放到缓存
@ -276,6 +291,7 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void updateStatus(MallRefundRecord mallRefundRecord) { public void updateStatus(MallRefundRecord mallRefundRecord) {
validateRefundRecordPending(mallRefundRecord);
LambdaUpdateWrapper<MallRefundRecord> uw = new LambdaUpdateWrapper<>(); LambdaUpdateWrapper<MallRefundRecord> uw = new LambdaUpdateWrapper<>();
uw.eq(MallRefundRecord::getId, mallRefundRecord.getId()) uw.eq(MallRefundRecord::getId, mallRefundRecord.getId())
.set(MallRefundRecord::getSuccessTime, new Date()) .set(MallRefundRecord::getSuccessTime, new Date())
@ -333,10 +349,10 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
workerOrderCacheUtil.remove(deliveryOrder.getWorkerId(),deliveryOrder.getId()); workerOrderCacheUtil.remove(deliveryOrder.getWorkerId(),deliveryOrder.getId());
} }
} }
BigDecimal totalRefund = result.getRecords().stream() /*BigDecimal totalRefund = result.getRecords().stream()
.map(item -> item.getRefundAmount()) // 提取 BigDecimal 金额 .map(item -> item.getRefundAmount()) // 提取 BigDecimal 金额
.reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);*/
mallOrderDeliveryFeeRefundHelper.refundOrderWithAddedDeliveryFee(order, totalRefund); mallOrderDeliveryFeeRefundHelper.refundOrderWithAddedDeliveryFee(order, order.getTotalAmount());
if(mallRefundRecord.getLinkId().toUpperCase().startsWith("W")){ if(mallRefundRecord.getLinkId().toUpperCase().startsWith("W")){
//配送员同意退款。扣掉余额 //配送员同意退款。扣掉余额
Worker worker = workerServiceImpl.findByWorkerId(mallRefundRecord.getLinkId()); Worker worker = workerServiceImpl.findByWorkerId(mallRefundRecord.getLinkId());
@ -454,7 +470,7 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
.reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);
}*/ }*/
refundWechatForRefundRecord(order, mallRefundRecord); refundWechatForAfterSaleWithOrderLock(order, mallRefundRecord);
if(mallRefundRecord.getLinkId().toUpperCase().startsWith("W")){ if(mallRefundRecord.getLinkId().toUpperCase().startsWith("W")){
//配送员同意退款。扣掉余额 //配送员同意退款。扣掉余额
Worker worker = workerServiceImpl.findByWorkerId(mallRefundRecord.getLinkId()); Worker worker = workerServiceImpl.findByWorkerId(mallRefundRecord.getLinkId());
@ -705,6 +721,34 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
} }
} }
private void refundWechatForAfterSaleWithOrderLock(MallOrder order, MallRefundRecord mallRefundRecord) {
tryAfterSaleRefundLock(buildAfterSaleRefundLockKey(mallRefundRecord.getOrderId()));
refundWechatForRefundRecord(order, mallRefundRecord);
}
private String buildAfterSaleRefundLockKey(String orderId) {
return "mall_refund:after_sale:wechat:" + orderId;
}
private void tryAfterSaleRefundLock(String lockKey) {
boolean locked = Boolean.TRUE.equals(stringRedisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", AFTER_SALE_REFUND_LOCK_SECONDS, TimeUnit.SECONDS));
if (!locked) {
throw new RuntimeException("同一订单售后退款处理中,请30秒后再试");
}
}
private void validateRefundRecordPending(MallRefundRecord mallRefundRecord) {
MallRefundRecord dbRecord = this.getById(mallRefundRecord.getId());
if (dbRecord == null) {
throw new RuntimeException("退款/售后记录不存在");
}
int waitStatus = mallRefundRecord.getStatus() != null && mallRefundRecord.getStatus() > 3 ? 3 : 0;
if (dbRecord.getStatus() == null || dbRecord.getStatus() != waitStatus) {
throw new RuntimeException("该退款/售后申请已处理,请刷新页面");
}
}
private boolean shouldRefundAddedDeliveryFee(MallRefundRecord mallRefundRecord) { private boolean shouldRefundAddedDeliveryFee(MallRefundRecord mallRefundRecord) {
if (mallRefundRecord == null) { if (mallRefundRecord == null) {
return false; return false;

18
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallUserCouponServiceImpl.java

@ -137,6 +137,24 @@ public class MallUserCouponServiceImpl extends ServiceImpl<MallUserCouponMapper,
return result; return result;
} }
@Override
public IPage<MallUserCoupon> selectUserCouponPage(String userId, String regionId, Integer pageNum, Integer pageSize) {
if (StringUtils.isEmpty(userId) || StringUtils.isEmpty(regionId)) {
throw new HiverException("用户或校区不能为空");
}
int current = pageNum == null || pageNum < 1 ? 1 : pageNum;
int size = pageSize == null || pageSize < 1 ? 10 : pageSize;
QueryWrapper<MallUserCoupon> qw = new QueryWrapper<>();
qw.eq("user_id", userId)
.eq("region_id", regionId)
.eq("status", 0)
.le("valid_start_time", new Date())
.ge("valid_end_time", new Date())
.orderByAsc("valid_end_time")
.orderByDesc("receive_time");
return this.page(new Page<>(current, size), qw);
}
@Override @Override
public void send(String userPhones, Integer type,String couponId,Integer giveNum) { public void send(String userPhones, Integer type,String couponId,Integer giveNum) {
//精准发放 //精准发放

28
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/UpdateUtils.java

@ -0,0 +1,28 @@
package cc.hiver.mall.utils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import java.util.HashSet;
import java.util.Set;
public class UpdateUtils {
public static void copyNonNullProperties(Object source, Object target) {
BeanUtils.copyProperties(source, target, getNullPropertyNames(source));
}
private static String[] getNullPropertyNames(Object source) {
final BeanWrapper src = new BeanWrapperImpl(source);
java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();
Set<String> emptyNames = new HashSet<>();
for (java.beans.PropertyDescriptor pd : pds) {
Object srcValue = src.getPropertyValue(pd.getName());
if (srcValue == null) {
emptyNames.add(pd.getName());
}
}
String[] result = new String[emptyNames.size()];
return emptyNames.toArray(result);
}
}

17
hiver-modules/hiver-mall/src/main/resources/mapper/MallOrderMapper.xml

@ -69,19 +69,22 @@
FROM mall_order m1 FROM mall_order m1
JOIN mall_order_group g ON FIND_IN_SET(m1.id, g.group_order_ids) > 0 JOIN mall_order_group g ON FIND_IN_SET(m1.id, g.group_order_ids) > 0
JOIN mall_order m2 ON FIND_IN_SET(m2.id, g.group_order_ids) > 0 JOIN mall_order m2 ON FIND_IN_SET(m2.id, g.group_order_ids) > 0
WHERE m2.status != 6 and m1.id = #{orderId} WHERE m2.status != 6 and m2.status != 0 and m1.id = #{orderId}
</select> </select>
<!-- 统计营业额和订单量 (带时间范围) --> <!-- 查询营业额和订单量统计所需订单明细 (带时间范围) -->
<select id="selectOrderRevenueAndCount" parameterType="java.util.Map" resultType="java.util.Map"> <select id="selectOrderRevenueAndCount" parameterType="java.util.Map" resultType="cc.hiver.mall.entity.MallOrder">
SELECT SELECT
COUNT(*) as count, id,
SUM(goods_amount + package_fee) as amount goods_amount AS goodsAmount,
package_fee AS packageFee,
delivery_type AS deliveryType,
is_pack AS isPack
FROM mall_order FROM mall_order
<include refid="commonShopIdWhere"/> <include refid="commonShopIdWhere"/>
AND `status` IN (5, 12) AND `status` IN (5, 11, 12)
<if test="startTime != null"> <if test="startTime != null">
AND create_time BETWEEN #{startTime} AND #{endTime} AND pay_time BETWEEN #{startTime} AND #{endTime}
</if> </if>
</select> </select>

Loading…
Cancel
Save