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>
</li>
<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>
</ul>
</div>
@ -84,7 +84,7 @@
<div class="test-detail">
<span class="meta text-white badge badge-sm"></span>
<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 class="test-contents d-none">
<div class="detail-head">
@ -92,9 +92,9 @@
<div class="info">
<div class='float-right'><span class='badge badge-default'>#test-id=1</span></div>
<h5 class="test-status text-pass">passTest</h5>
<span class='badge badge-success'>06.24.2026 14:41:23</span>
<span class='badge badge-danger'>06.24.2026 14:41:23</span>
<span class='badge badge-default'>0.026 secs</span>
<span class='badge badge-success'>06.27.2026 17:16:55</span>
<span class='badge badge-danger'>06.27.2026 17:16:55</span>
<span class='badge badge-default'>0.037 secs</span>
</div>
<div class="m-t-10 m-l-5"></div>
</div>
@ -104,7 +104,7 @@
<tbody>
<tr class="event-row">
<td><span class="badge log pass-bg">Pass</span></td>
<td>14:41:23</td>
<td>17:16:55</td>
<td>
Test passed
</td>
@ -128,13 +128,13 @@
<div class="col-md-3">
<div class="card"><div class="card-body">
<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 class="col-md-3">
<div class="card"><div class="card-body">
<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 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.WorkerRedisCacheUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@ -80,6 +81,22 @@ public class CommentController {
@ApiOperation("保存")
public Result save( @RequestBody List<Comment> comment) {
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());
if(StringUtils.isEmpty(e.getParentId())){
e.setLevel(ShopConstant.SHOP_STATUS_LOCK);
@ -175,6 +192,16 @@ public class CommentController {
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)
@ApiOperation("获取店铺评论")
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;
}
@Data
public static class UserCouponPageReq {
private String userId;
private String regionId;
private Integer pageNum = 1;
private Integer pageSize = 10;
}
@PostMapping("/add")
@ApiOperation(value = "添加/发行优惠券")
public Result<Object> addCoupon(@RequestBody MallCoupon coupon) {
@ -118,6 +126,15 @@ public class MallCouponController {
public Result<IPage<MallCoupon>> list(MallCouponQuery 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")
@ApiOperation(value = "开启免单优惠")
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.ShopTakeawayService;
import cc.hiver.mall.service.ShopUserService;
import cc.hiver.mall.utils.UpdateUtils;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
@ -152,27 +153,34 @@ public class ShopController {
@ResponseBody
@ApiOperation("编辑")
public Result<Object> edit(Shop shop) {
// 获取旧店铺信息
// 1. 获取数据库中完整的旧店铺信息
Shop oldShop = shopService.get(shop.getId());
if (oldShop == null) {
throw new RuntimeException("店铺不存在");
}
// 2. 处理商圈相关逻辑(保持你原有的业务逻辑)
if (CharSequenceUtil.isNotBlank(shop.getShopArea())) {
final ShopArea d = shopAreaService.get(shop.getShopArea());
if (d != null) {
shop.setShopAreaTitle(d.getTitle());
oldShop.setShopAreaTitle(d.getTitle());
}
// 获取商圈的id和名称
final ShopArea topShopAreaById = shopAreaService.findTopShopAreaById(shop.getShopArea());
shop.setRegion(topShopAreaById.getTitle());
shop.setRegionId(topShopAreaById.getId());
if (topShopAreaById != null) {
oldShop.setRegion(topShopAreaById.getTitle());
oldShop.setRegionId(topShopAreaById.getId());
}
oldShop.setShopArea(shop.getShopArea()); // 同步更新商圈ID
} else {
shop.setShopArea(null);
shop.setShopAreaTitle("");
shop.setRegion("");
shop.setRegionId(null);
}
if(shop.getSaleCount() == null){
shop.setSaleCount(0);
}
shopService.update(shop);
// 如果前端明确传了空,说明要清空商圈信息
oldShop.setShopArea(null);
oldShop.setShopAreaTitle("");
oldShop.setRegion("");
oldShop.setRegionId(null);
}
UpdateUtils.copyNonNullProperties(shop, oldShop);
// 4. 更新数据库(此时更新的是带有完整数据的 oldShop,不会误清空其他字段)
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);
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);

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")
private String issuerId;
@ApiModelProperty(value = "适用场景")
@ApiModelProperty(value = "适用场景:0-通用,1-外卖/买饭,2-快递/跑腿,3-二手物品交易、4团购")
private Integer applyScene;
@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.planet.hook.PlanetRewardHook;
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.utils.*;
import com.alibaba.fastjson.JSON;
@ -26,9 +27,7 @@ import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.*;
/**
* 处理非核心响应时间不敏感的常规异步队列
@ -98,21 +97,20 @@ public class OrderAsyncConsumer {
}
List<String> targetOpenIds = new ArrayList<>();
List<Worker> targetWorkers = new ArrayList<>();
String orderTypeStr = "抢单大厅单";
if (StringUtils.isNotBlank(delivery.getWorkerId())) {
orderTypeStr = "指定接单";
// 获取指定骑手的关联 userId 和 openid
String openId = getOfficialAccountOpenidByWorkerId(delivery.getWorkerId());
if (StringUtils.isNotBlank(openId)) {
targetOpenIds.add(openId);
WorkerRedisVo workerRedisVo = workerRedisCacheUtil.get(delivery.getRegionId(), delivery.getWorkerId());
Worker worker = getOnlineWorkerInRegion(workerRedisVo, delivery.getRegionId());
if (worker != null) {
targetWorkers.add(worker);
}
} else {
// 如果没有指定人,发给所有人?业务需求:给所有配送员发微信公众号消息
// 这可能需要查出所有 Worker 关联的 user.officialAccountOpenid (实际应用中可能需要加状态过滤)
// 为简化并实现逻辑,这里只查有效的骑手 user
targetOpenIds = getAllWorkerOpenIds();
targetWorkers = getOnlineWorkersInRegion(delivery.getRegionId());
}
targetOpenIds = getOfficialAccountOpenids(targetWorkers);
if (targetOpenIds.isEmpty()) {
log.info("【异步队列处理】没有找到可推送微信模板的openid,目标跳过推送。orderId={}", orderId);
@ -149,19 +147,68 @@ public class OrderAsyncConsumer {
}
private String getOfficialAccountOpenidByWorkerId(String workerId) {
Worker worker = workerService.findByWorkerId(workerId);
if (worker != null && StringUtils.isNotBlank(worker.getUserId())) {
User user = userService.findById(worker.getUserId());
if (user != null) {
return user.getOfficialAccountOpenid();
private Worker getOnlineWorkerInRegion(WorkerRedisVo workerRedisVo, String regionId) {
if (workerRedisVo == null || workerRedisVo.getWorker() == null) {
return null;
}
Worker worker = workerRedisVo.getWorker();
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() {
return mallOrderMapper.getWeChatId();
private boolean isOnlineWorkerInRegion(Worker worker, String regionId) {
return worker != null
&& StringUtils.equals(worker.getRegion(), regionId)
&& Integer.valueOf(1).equals(worker.getIsOnLine());
}
@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.MallRefundRecord;
import cc.hiver.mall.entity.Shop;
import cc.hiver.mall.service.mybatis.MallOrderService;
import cc.hiver.mall.service.mybatis.MallRefundRecordService;
import cc.hiver.mall.utils.WaitOrderCacheUtil;
import cc.hiver.mall.utils.WorkerOrderCacheUtil;
@ -39,6 +40,9 @@ public class OrderDelayConsumer {
@Autowired
private MallRefundRecordService mallRefundRecordService;
@Autowired
private MallOrderService mallOrderService;
@Autowired
private ShopMapper shopMapper;
@ -92,6 +96,9 @@ public class OrderDelayConsumer {
String refundId = msg.getString("refundId");
handleRefundAutoAgree(refundId);
break;
case "Order_Pay_Timeout_1h":
handleOrderPayTimeout(orderId);
break;
default:
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) {
if (delivery == null || StringUtils.isBlank(delivery.getId())) {
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 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 refundCoupon(String orderId);
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);
}

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.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();
//更新单量
worker.setOrderCount(oldCount + 1);
@ -749,7 +749,7 @@ public class MallDeliveryOrderServiceImpl extends ServiceImpl<MallDeliveryOrderM
//增加余额变动记录
DealingsRecord dealingsRecord = new DealingsRecord();
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.setLinkId(deliveryId);
dealingsRecord.setLinkUserId(worker.getWorkerId());
@ -1180,14 +1180,26 @@ public class MallDeliveryOrderServiceImpl extends ServiceImpl<MallDeliveryOrderM
throw new RuntimeException("只有待接单状态的配送单才能重新指定配送员");
}
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<>();
uw.eq(MallDeliveryOrder::getId, deliveryId)
.set(MallDeliveryOrder::getWorkerId, newWorkerId)
.set(MallDeliveryOrder::getWorkerName, newWorkerName)
.set(MallDeliveryOrder::getWorkerPhone, newWorkerPhone);
if(bonus != null){
uw.set(MallDeliveryOrder::getDeliveryFeeMarketplace, bonus);
}
this.update(uw);
delivery.setWorkerId(newWorkerId);
delivery.setWorkerName(newWorkerName);
delivery.setWorkerPhone(newWorkerPhone);
@ -1220,6 +1232,7 @@ public class MallDeliveryOrderServiceImpl extends ServiceImpl<MallDeliveryOrderM
if (StringUtils.isNotBlank(oldWorkerId)) {
workerOrderCacheUtil.remove(oldWorkerId, delivery.getId());
}
cacheDelivery.setDeliveryFeeMarketplace(delivery.getDeliveryFeeMarketplace());
if (cacheDelivery == null) {
cacheDelivery = delivery;
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 java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
@ -166,7 +168,9 @@ public class MallOrderGroupServiceImpl extends ServiceImpl<MallOrderGroupMapper,
LambdaUpdateWrapper<MallOrder> ouw = new LambdaUpdateWrapper<>();
//代表该拼团子订单订单选择到店
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())
.eq(MallOrder::getStatus, ORDER_STATUS_WAIT_GROUP)
.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.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
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_WAIT_RETURN = 11;//售后中
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; // 外卖
@ -338,6 +342,8 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
createDeliveryOrder(order, dto, null, -1, null);
}
triggerOrderPayTimeoutEvent(order.getId());
return buildVO(order, goodsSnapshots, null, null);
}
@ -451,7 +457,15 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
// 拼团过期时间:24小时后
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());
// 设置配送员和佣金信息
@ -481,6 +495,8 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
createDeliveryOrder(order, dto, group.getId(), -1, isFace2Face ? group.getTotalDeliveryFee() : null);
}
triggerOrderPayTimeoutEvent(order.getId());
// 将 groupId 关联到订单 (MallOrder 没有 groupId 字段,通过 MallOrderGroup 关联已足够)
return buildVO(order, goodsSnapshots, group, null);
@ -496,15 +512,15 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
if (group == null) {
throw new RuntimeException("拼团不存在");
}
/*if (group.getStatus() != GROUP_STATUS_FORMING && group.getStatus() != GROUP_STATUS_FACE2FACE) {
throw new RuntimeException("当前拼团状态不可参团");
}*/
if (group.getStatus() == GROUP_STATUS_FAIL) {
throw new RuntimeException("团长已取消拼团");
}
//超出拼团人数 也可以参与
/*if (group.getCurrentMembers() >= group.getTargetMembers()) {
throw new RuntimeException("拼团已满员");
}*/
boolean isFace2Face = (group.getStatus() == GROUP_STATUS_FACE2FACE);
boolean isFace2Face = (group.getIsFace() == 1);
// 面对面团:不再重新计算,直接使用根据 targetMembers 均摊的金额
BigDecimal deliveryFee = BigDecimal.ZERO;
@ -575,6 +591,8 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
createDeliveryOrder(order, dto, group.getId(), -1, null);
}
triggerOrderPayTimeoutEvent(order.getId());
return buildVO(order, goodsSnapshots, group, null);
}
@ -650,7 +668,9 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
triggerDeliveryAsyncEvents(order, deliveryOrder);
autoAcceptShopDeliveryOrder(order, deliveryOrder);
} 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<>();
uw.eq(MallOrder::getId, orderId)
.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) {
/* // 查找属于该订单的拼团并检查成团条件
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);
@ -677,17 +717,9 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
orderVO.setStatus(STATUS_WAIT_GROUP);
userPendingOrderCacheUtil.update(order.getUserId(), orderVO);
// 查找属于该订单的拼团并检查成团条件
LambdaQueryWrapper<MallOrderGroup> gq = new LambdaQueryWrapper<>();
gq.eq(MallOrderGroup::getHeadOrderId, orderId);
MallOrderGroup group = mallOrderGroupMapper.selectOne(gq);
if (group == null) {
if (!group.getHeadOrderId().equals(orderId)) {
// 如果不是团长,按参团人查找
LambdaQueryWrapper<MallOrderGroup> gq2 = new LambdaQueryWrapper<>();
gq2.eq(MallOrderGroup::getShopId, order.getShopId())
.like(MallOrderGroup::getGroupOrderIds, order.getId())
.last("LIMIT 1");
group = mallOrderGroupMapper.selectOne(gq2);
//更新参团人数+1
int newMembers = group.getCurrentMembers() + 1;
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);
}
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
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) {
MallOrder order = getById(orderId);
if (order == null) throw new RuntimeException("订单不存在");
if (order.getStatus() != STATUS_WAIT_PICKUP && order.getStatus() != STATUS_DELIVERING) {
throw new RuntimeException("当前状态不可完成");
if (order.getStatus() != STATUS_WAIT_PICKUP) {
throw new RuntimeException("当前订单不可核销");
}
//优惠券变成已使用
mallUserCouponService.useCoupon(orderId);
@ -1271,8 +1343,9 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
// 3. 生成结束时间:当天的 23:59:59
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);
//待处理差评订单数
Integer pendingBadReviewCount = this.baseMapper.selectPendingBadReviewCount(shopId);
@ -1289,6 +1362,75 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
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
public List<MallOrderVO> getPeiSongOrders(String userId) {
List<MallOrderVO> orders = this.baseMapper.getPeiSongOrders(userId);
@ -1430,7 +1572,7 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
LambdaQueryWrapper<MallRefundRecord> rq = new LambdaQueryWrapper<>();
rq.eq(MallRefundRecord::getOrderId, orderId);
List<MallRefundRecord> sallRefundRecord = mallRefundRecordMapper.selectList(rq);
if(sallRefundRecord != null){
if(!sallRefundRecord.isEmpty()){
sallRefundRecord.forEach(item -> {
if(item.getRefundType() == 1){
//售后商品详情
@ -1969,14 +2111,18 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
//发送1小时延时MQ:超时未处理则自动同意
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){
//商家、配送员都加一条
record.setLinkId(order.getShopId());
if(ratio != null){
record.setRefundAmount(order.getGoodsAmount().add(order.getPackageFee()).subtract(order.getGoodsAmount().add(order.getPackageFee()).multiply(ratio)).setScale(2, RoundingMode.HALF_UP));
BigDecimal shopRefund = BigDecimal.ZERO;
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{
record.setRefundAmount(order.getGoodsAmount().add(order.getPackageFee()).setScale(2, RoundingMode.HALF_UP));
record.setRefundAmount(order.getGoodsAmount().add(order.getPackageFee()));
}
/*if(ratio != null){
@ -1993,8 +2139,8 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
MallRefundRecord mallRefundRecord1 = record;
mallRefundRecord1.setId(SnowFlakeUtil.nextId().toString());
mallRefundRecord1.setLinkId(workerId);
if(ratio != null){
mallRefundRecord1.setRefundAmount(order.getDeliveryFee().subtract(order.getDeliveryFee().multiply(ratio)).setScale(2, RoundingMode.HALF_UP));
if(ratio != null && ratio.compareTo(BigDecimal.ZERO) > 0){
mallRefundRecord1.setRefundAmount(order.getTotalAmount().subtract(shopRefund));
}else{
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.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -30,6 +31,7 @@ import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
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 PAGENUM = 1;
private static final long AFTER_SALE_REFUND_LOCK_SECONDS = 30L;
@Autowired
private MallRefundRecordMapper mallRefundRecordMapper;
@ -89,6 +92,9 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
@Autowired
private WechatPayUtil wechatPayUtil;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private MallOrderDeliveryFeeRefundHelper mallOrderDeliveryFeeRefundHelper;
@ -174,9 +180,13 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
if(mallRefundRecord.getRefundType() == 1){
// 退商品,和商家有关联
mallRefundRecord.setLinkId(mallRefundRecord.getShopId());
if(ratio != null && !ratio.equals(0)){
if(ratio != null && ratio.compareTo(BigDecimal.ZERO) > 0){
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);
//售后放到缓存
@ -187,9 +197,13 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
}else if(mallRefundRecord.getRefundType() == 2){
// 退配送费和配送员有关联
mallRefundRecord.setLinkId(mallRefundRecord.getWorkerId());
if(ratio != null){
if(ratio != null && ratio.compareTo(BigDecimal.ZERO) > 0){
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);
//售后放到缓存
@ -221,9 +235,11 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
//商家、配送员都加一条,和商家+配送员都有关联
mallRefundRecord.setLinkId(mallRefundRecord.getShopId());
mallRefundRecord.setRefundAmount(mallRefundRecord.getGoodsAmount().add(mallRefundRecord.getPackageFee()));
if(ratio != null){
mallRefundRecord.setRefundAmount(mallRefundRecord.getRefundAmount()
.subtract(mallRefundRecord.getRefundAmount().multiply(ratio)).setScale(2, RoundingMode.HALF_UP));
BigDecimal shopRefund = BigDecimal.ZERO;
if(ratio != null && ratio.compareTo(BigDecimal.ZERO) > 0){
shopRefund = mallRefundRecord.getRefundAmount()
.subtract(mallRefundRecord.getRefundAmount().multiply(ratio)).setScale(2, RoundingMode.UP);
mallRefundRecord.setRefundAmount(shopRefund);
}
mallRefundRecordMapper.insert(mallRefundRecord);
//售后放到缓存
@ -235,9 +251,8 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
mallRefundRecord1.setId(SnowFlakeUtil.nextId().toString());
mallRefundRecord1.setLinkId(mallRefundRecord.getWorkerId());
mallRefundRecord1.setRefundAmount(mallRefundRecord.getDeliveryFee());
if(ratio != null){
mallRefundRecord1.setRefundAmount(mallRefundRecord1.getRefundAmount()
.subtract(mallRefundRecord1.getRefundAmount().multiply(ratio)).setScale(2, RoundingMode.HALF_UP));
if(ratio != null && ratio.compareTo(BigDecimal.ZERO) > 0){
mallRefundRecord1.setRefundAmount(order.getTotalAmount().subtract(shopRefund));
}
mallRefundRecordMapper.insert(mallRefundRecord1);
//售后放到缓存
@ -276,6 +291,7 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
@Override
@Transactional(rollbackFor = Exception.class)
public void updateStatus(MallRefundRecord mallRefundRecord) {
validateRefundRecordPending(mallRefundRecord);
LambdaUpdateWrapper<MallRefundRecord> uw = new LambdaUpdateWrapper<>();
uw.eq(MallRefundRecord::getId, mallRefundRecord.getId())
.set(MallRefundRecord::getSuccessTime, new Date())
@ -333,10 +349,10 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
workerOrderCacheUtil.remove(deliveryOrder.getWorkerId(),deliveryOrder.getId());
}
}
BigDecimal totalRefund = result.getRecords().stream()
/*BigDecimal totalRefund = result.getRecords().stream()
.map(item -> item.getRefundAmount()) // 提取 BigDecimal 金额
.reduce(BigDecimal.ZERO, BigDecimal::add);
mallOrderDeliveryFeeRefundHelper.refundOrderWithAddedDeliveryFee(order, totalRefund);
.reduce(BigDecimal.ZERO, BigDecimal::add);*/
mallOrderDeliveryFeeRefundHelper.refundOrderWithAddedDeliveryFee(order, order.getTotalAmount());
if(mallRefundRecord.getLinkId().toUpperCase().startsWith("W")){
//配送员同意退款。扣掉余额
Worker worker = workerServiceImpl.findByWorkerId(mallRefundRecord.getLinkId());
@ -454,7 +470,7 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
.reduce(BigDecimal.ZERO, BigDecimal::add);
}*/
refundWechatForRefundRecord(order, mallRefundRecord);
refundWechatForAfterSaleWithOrderLock(order, mallRefundRecord);
if(mallRefundRecord.getLinkId().toUpperCase().startsWith("W")){
//配送员同意退款。扣掉余额
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) {
if (mallRefundRecord == null) {
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;
}
@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
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
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
WHERE m2.status != 6 and m1.id = #{orderId}
WHERE m2.status != 6 and m2.status != 0 and m1.id = #{orderId}
</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
COUNT(*) as count,
SUM(goods_amount + package_fee) as amount
id,
goods_amount AS goodsAmount,
package_fee AS packageFee,
delivery_type AS deliveryType,
is_pack AS isPack
FROM mall_order
<include refid="commonShopIdWhere"/>
AND `status` IN (5, 12)
AND `status` IN (5, 11, 12)
<if test="startTime != null">
AND create_time BETWEEN #{startTime} AND #{endTime}
AND pay_time BETWEEN #{startTime} AND #{endTime}
</if>
</select>

Loading…
Cancel
Save