Browse Source

对接拼团数据1

master
wangfukang 1 week ago
parent
commit
c9d6a751ed
  1. 16
      hiver-admin/test-output/test-report.html
  2. 13
      hiver-modules/hiver-base/src/main/java/cc/hiver/base/controller/manage/AuthController.java
  3. 239
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/AdminMallOrderGroupController.java
  4. 12
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallDeliveryOrderController.java
  5. 6
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallOrderController.java
  6. 2
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallDeliveryOrderMapper.java
  7. 4
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrderGroup.java
  8. 34
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/controller/IeController.java
  9. 12
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/IeChatService.java
  10. 89
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/impl/IeChatServiceImpl.java
  11. 34
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/impl/IeMatchServiceImpl.java
  12. 1
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/vo/IeHomeVO.java
  13. 5
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/vo/IeMatchVO.java
  14. 2
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallDeliveryOrderService.java
  15. 2
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderService.java
  16. 9
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java
  17. 4
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderGroupServiceImpl.java
  18. 62
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java
  19. 145
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/ShopGroupOrderCacheUtil.java
  20. 50
      hiver-modules/hiver-mall/src/main/resources/mapper/MallDeliveryOrderMapper.xml
  21. 10
      hiver-modules/hiver-mall/src/main/resources/mapper/MallOrderGroupMapper.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">五月 21, 2026 15:07:44</span></a>
<a href="#"><span class="badge badge-primary">五月 29, 2026 16:35:42</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>15:07:45 下午</span> / <span>0.024 secs</span></p>
<p class="text-sm"><span>16:35:42 下午</span> / <span>0.018 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'>05.21.2026 15:07:45</span>
<span class='badge badge-danger'>05.21.2026 15:07:45</span>
<span class='badge badge-default'>0.024 secs</span>
<span class='badge badge-success'>05.29.2026 16:35:42</span>
<span class='badge badge-danger'>05.29.2026 16:35:42</span>
<span class='badge badge-default'>0.018 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>15:07:45</td>
<td>16:35:42</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>五月 21, 2026 15:07:44</h3>
<h3>五月 29, 2026 16:35:42</h3>
</div></div>
</div>
<div class="col-md-3">
<div class="card"><div class="card-body">
<p class="m-b-0">Ended</p>
<h3>五月 21, 2026 15:07:45</h3>
<h3>五月 29, 2026 16:35:43</h3>
</div></div>
</div>
<div class="col-md-3">

13
hiver-modules/hiver-base/src/main/java/cc/hiver/base/controller/manage/AuthController.java

@ -23,12 +23,13 @@ import cc.hiver.core.service.UserRoleService;
import cc.hiver.core.service.UserService;
import cc.hiver.mall.debt.service.DebtService;
import cc.hiver.mall.entity.Shop;
import cc.hiver.mall.entity.ShopTakeaway;
import cc.hiver.mall.entity.ShopUser;
import cc.hiver.mall.invitelog.constant.InviteLogConstant;
import cc.hiver.mall.invitelog.entity.InviteLog;
import cc.hiver.mall.invitelog.service.InviteLogService;
import cc.hiver.mall.pojo.vo.ShopVo;
import cc.hiver.mall.service.ShopService;
import cc.hiver.mall.service.ShopTakeawayService;
import cc.hiver.mall.service.ShopUserService;
import cc.hiver.mall.service.SupplierService;
import cc.hiver.mall.service.mybatis.CustomerService;
@ -101,6 +102,9 @@ public class AuthController {
@Autowired
private ShopService shopService;
@Autowired
private ShopTakeawayService shopTakeawayService;
@Autowired
private ShopUserService shopUserService;
@ -526,16 +530,17 @@ public class AuthController {
final String token = redisTemplate.get(tokenKey);
// 获取店铺的商圈
final Shop shop = shopService.get(shopId);
ShopTakeaway takeaway = shopTakeawayService.selectByPrimaryKey(shopId);
securityUtil.chooseShop(shopId, shop.getRegionId(), token);
final ShopVo shopVo = new ShopVo();
/*final ShopVo shopVo = new ShopVo();
shopVo.setShopIcon(shop.getShopIcon());
shopVo.setShopAddress(shop.getShopAddress());
shopVo.setRemark(shop.getRemark());
final ShopUser shopUser = shopUserService.selectByUserIdAndShopId(userId, shopId);
shopVo.setUserType(String.valueOf(shopUser.getType()));
// 店铺默认规格库id
shopVo.setAttrId(shop.getAttrId());
return new ResultUtil<ShopVo>().setData(shopVo);
shopVo.setAttrId(shop.getAttrId());*/
return new ResultUtil<ShopTakeaway>().setData(takeaway);
}
/*

239
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/AdminMallOrderGroupController.java

@ -1,13 +1,17 @@
package cc.hiver.mall.controller;
import cc.hiver.core.common.redis.RedisTemplateHelper;
import cc.hiver.core.common.utils.ResultUtil;
import cc.hiver.core.common.vo.Result;
import cc.hiver.mall.dao.mapper.MallOrderGoodsMapper;
import cc.hiver.mall.entity.MallOrder;
import cc.hiver.mall.entity.MallOrderGoods;
import cc.hiver.mall.entity.MallOrderGroup;
import cc.hiver.mall.dao.mapper.ShopMapper;
import cc.hiver.mall.dao.mapper.ShopTakeawayMapper;
import cc.hiver.mall.entity.*;
import cc.hiver.mall.pojo.dto.ShopCacheDTO;
import cc.hiver.mall.service.mybatis.MallOrderGroupService;
import cc.hiver.mall.service.mybatis.MallOrderService;
import cc.hiver.mall.utils.ShopGroupOrderCacheUtil;
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;
@ -18,9 +22,8 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@RestController
@ -37,24 +40,105 @@ public class AdminMallOrderGroupController {
@Autowired
private MallOrderGoodsMapper mallOrderGoodsMapper;
@Autowired
private ShopGroupOrderCacheUtil shopGroupOrderCacheUtil;
@Autowired
private RedisTemplateHelper redisTemplateHelper;
@Autowired
private ShopMapper shopMapper;
@Autowired
private ShopTakeawayMapper shopTakeawayMapper;
@Data
public static class OrderGroupQuery {
private String shopId;
private String regionId;
private String headUserId;
private Integer status;
private String productName;
private String keyWord;
private Integer isFace;
private Integer merchantType;
private int pageNumber = 1;
private int pageSize = 10;
}
@Data
public static class OrderGroupListVo {
private String id;
private String shopId;
private String shopName;
private String regionId;
private String headUserId;
private String headOrderId;
private String groupOrderIds;
private Integer targetMembers;
private Integer currentMembers;
private Integer isFace;
private Integer status;
private java.math.BigDecimal groupPrice;
private String workerId;
private java.math.BigDecimal workerCommission;
private java.math.BigDecimal totalDeliveryFee;
private java.util.Date createTime;
private java.util.Date expireTime;
private java.util.Date successTime;
private String productName;
private String productPicture;
private String productId;
private Shop shopItem;
}
@PostMapping("/page")
@ApiOperation(value = "分页查询拼团列表")
public Result<Page<MallOrderGroup>> page(@RequestBody OrderGroupQuery query) {
public Result<Page<OrderGroupListVo>> page(@RequestBody OrderGroupQuery query) {
if (query == null || StringUtils.isBlank(query.getRegionId())) {
return ResultUtil.error("regionId不能为空");
}
if (query.getStatus() == null) {
query.setStatus(0);
}
if (query.getIsFace() == null) {
query.setIsFace(0);
}
List<MallOrderGroup> groups = shopGroupOrderCacheUtil.getAllByRegion(query.getRegionId());
if (groups == null) {
groups = listGroupsFromDb(query);
shopGroupOrderCacheUtil.putAllByRegion(query.getRegionId(), groups);
}
groups = filterGroups(groups, query);
Map<String, Shop> shopMap = buildShopMap(groups, query.getRegionId());
groups = filterByShop(groups, shopMap, query);
groups.sort(Comparator.comparing(MallOrderGroup::getCreateTime,
Comparator.nullsLast(Comparator.naturalOrder())).reversed());
long total = groups.size();
int pageNumber = Math.max(query.getPageNumber(), 1);
int pageSize = Math.max(query.getPageSize(), 1);
int fromIndex = Math.min((pageNumber - 1) * pageSize, groups.size());
int toIndex = Math.min(fromIndex + pageSize, groups.size());
List<OrderGroupListVo> records = groups.subList(fromIndex, toIndex).stream()
.map(group -> toListVo(group, shopMap.get(group.getShopId())))
.collect(Collectors.toList());
Page<OrderGroupListVo> result = new Page<>(pageNumber, pageSize, total);
result.setRecords(records);
return new ResultUtil<Page<OrderGroupListVo>>().setData(result);
}
private List<MallOrderGroup> listGroupsFromDb(OrderGroupQuery query) {
LambdaQueryWrapper<MallOrderGroup> qw = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(query.getShopId())) {
qw.eq(MallOrderGroup::getShopId, query.getShopId());
}
if (StringUtils.isNotBlank(query.getRegionId())) {
qw.eq(MallOrderGroup::getRegionId, query.getRegionId());
}
if (StringUtils.isNotBlank(query.getHeadUserId())) {
qw.eq(MallOrderGroup::getHeadUserId, query.getHeadUserId());
}
@ -68,10 +152,145 @@ public class AdminMallOrderGroupController {
qw.eq(MallOrderGroup::getIsFace, query.getIsFace());
}
qw.orderByDesc(MallOrderGroup::getCreateTime);
return mallOrderGroupService.list(qw);
}
private List<MallOrderGroup> filterGroups(List<MallOrderGroup> groups, OrderGroupQuery query) {
if (groups == null || groups.isEmpty()) {
return new ArrayList<>();
}
return groups.stream()
.filter(group -> group != null)
.filter(group -> StringUtils.isBlank(query.getShopId()) || query.getShopId().equals(group.getShopId()))
.filter(group -> StringUtils.isBlank(query.getRegionId()) || query.getRegionId().equals(group.getRegionId()))
.filter(group -> StringUtils.isBlank(query.getHeadUserId()) || query.getHeadUserId().equals(group.getHeadUserId()))
.filter(group -> query.getStatus() == null || query.getStatus().equals(group.getStatus()))
.filter(group -> query.getIsFace() == null || query.getIsFace().equals(group.getIsFace()))
.collect(Collectors.toList());
}
private Map<String, Shop> buildShopMap(List<MallOrderGroup> groups, String regionId) {
if (groups == null || groups.isEmpty()) {
return Collections.emptyMap();
}
Set<String> shopIds = groups.stream()
.map(MallOrderGroup::getShopId)
.filter(StringUtils::isNotBlank)
.collect(Collectors.toCollection(LinkedHashSet::new));
if (shopIds.isEmpty()) {
return Collections.emptyMap();
}
Map<String, Shop> result = new HashMap<>();
List<String> missingShopIds = new ArrayList<>();
String shopCacheKey = "SHOP_CACHE:" + regionId;
List<Object> cacheFields = new ArrayList<>(shopIds);
List<Object> cacheValues = redisTemplateHelper.hMultiGet(shopCacheKey, cacheFields);
int index = 0;
for (String shopId : shopIds) {
Object value = cacheValues != null && index < cacheValues.size() ? cacheValues.get(index) : null;
Shop shop = parseShopFromCache(value);
if (shop != null) {
result.put(shopId, shop);
} else {
missingShopIds.add(shopId);
}
index++;
}
if (!missingShopIds.isEmpty()) {
List<Shop> dbShops = shopMapper.selectBatchIds(missingShopIds);
if (dbShops != null && !dbShops.isEmpty()) {
List<ShopTakeaway> takeawayList = shopTakeawayMapper.selectListByshopId(missingShopIds);
Map<String, ShopTakeaway> takeawayMap = takeawayList == null ? Collections.emptyMap()
: takeawayList.stream().filter(Objects::nonNull)
.filter(item -> StringUtils.isNotBlank(item.getShopId()))
.collect(Collectors.toMap(ShopTakeaway::getShopId, item -> item, (a, b) -> a));
for (Shop shop : dbShops) {
if (shop != null && StringUtils.isNotBlank(shop.getId())) {
shop.setShopTakeaway(takeawayMap.get(shop.getId()));
result.put(shop.getId(), shop);
}
}
}
}
return result;
}
private Shop parseShopFromCache(Object value) {
if (value == null) {
return null;
}
try {
ShopCacheDTO dto = JSONUtil.toBean(value.toString(), ShopCacheDTO.class);
if (dto == null || dto.getShop() == null) {
return null;
}
Shop shop = dto.getShop();
shop.setProducts(dto.getProducts());
shop.setShopTakeaway(dto.getShopTakeaway());
return shop;
} catch (Exception e) {
log.info("解析店铺缓存失败", e);
return null;
}
}
private List<MallOrderGroup> filterByShop(List<MallOrderGroup> groups, Map<String, Shop> shopMap, OrderGroupQuery query) {
String keyword = StringUtils.isNotBlank(query.getKeyWord()) ? query.getKeyWord() : query.getProductName();
return groups.stream()
.filter(group -> {
Shop shop = shopMap.get(group.getShopId());
if (query.getMerchantType() != null) {
Integer merchantType = shop == null ? null : shop.getMerchantType();
if (query.getMerchantType().equals(1)) {
if (merchantType != null && !query.getMerchantType().equals(merchantType)) {
return false;
}
} else if (!query.getMerchantType().equals(merchantType)) {
return false;
}
}
if (StringUtils.isBlank(keyword)) {
return true;
}
String kw = keyword.trim();
return contains(group.getProductName(), kw)
|| contains(group.getShopName(), kw)
|| (shop != null && contains(shop.getShopName(), kw));
})
.collect(Collectors.toList());
}
private boolean contains(String value, String keyword) {
return value != null && keyword != null && value.contains(keyword);
}
Page<MallOrderGroup> page = new Page<>(query.getPageNumber(), query.getPageSize());
Page<MallOrderGroup> result = mallOrderGroupService.page(page, qw);
return new ResultUtil<Page<MallOrderGroup>>().setData(result);
private OrderGroupListVo toListVo(MallOrderGroup group, Shop shop) {
OrderGroupListVo vo = new OrderGroupListVo();
vo.setId(group.getId());
vo.setShopId(group.getShopId());
vo.setShopName(group.getShopName());
vo.setRegionId(group.getRegionId());
vo.setHeadUserId(group.getHeadUserId());
vo.setHeadOrderId(group.getHeadOrderId());
vo.setGroupOrderIds(group.getGroupOrderIds());
vo.setTargetMembers(group.getTargetMembers());
vo.setCurrentMembers(group.getCurrentMembers());
vo.setIsFace(group.getIsFace());
vo.setStatus(group.getStatus());
vo.setGroupPrice(group.getGroupPrice());
vo.setWorkerId(group.getWorkerId());
vo.setWorkerCommission(group.getWorkerCommission());
vo.setTotalDeliveryFee(group.getTotalDeliveryFee());
vo.setCreateTime(group.getCreateTime());
vo.setExpireTime(group.getExpireTime());
vo.setSuccessTime(group.getSuccessTime());
vo.setProductName(group.getProductName());
vo.setProductPicture(group.getProductPicture());
vo.setProductId(group.getProductId());
vo.setShopItem(shop);
return vo;
}
@Data

12
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallDeliveryOrderController.java

@ -188,6 +188,14 @@ public class MallDeliveryOrderController {
return new ResultUtil<Object>().setData(page);
}
@PostMapping("/pagebyworkerHistory")
@ApiOperation(value = "分页查询配送员配送单")
public Result<Object> pagebyworkerHistory(@RequestBody MallDeliveryOrderPageQuery q) {
// 1. 先从缓存获取
IPage<MallDeliveryOrder> page = mallDeliveryOrderService.pageDeliveryByStatusHis(q);
return new ResultUtil<Object>().setData(page);
}
/**
* 配送员接单
*/
@ -208,7 +216,7 @@ public class MallDeliveryOrderController {
//当前人没有注册兼职,先赋予兼职身份
final User user = securityUtil.getCurrUser();
Worker worker = new Worker();
worker.setWorkerName(user.getNickname());
worker.setWorkerName("半径骑士");
worker.setUserId(user.getId());
worker.setAvgTime(BigDecimal.valueOf(25));
worker.setMobile(user.getMobile());
@ -228,6 +236,8 @@ public class MallDeliveryOrderController {
return ResultUtil.success("配送单已被接取或已取消");
}else if(result == 2){
return ResultUtil.success("该单已指定其他配送员,无法抢单");
}else if(result == 5){
return ResultUtil.success("已被禁止接单,无法抢单");
}else{
return new ResultUtil<Worker>().setData(worker);
}

6
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallOrderController.java

@ -211,9 +211,11 @@ public class MallOrderController {
@ApiOperation(value = "用户取消订单")
public Result cancelOrder(@RequestParam String orderId,
@RequestParam String userId,@RequestParam Integer refundType,
@RequestParam Integer refundTypeStatus) {
@RequestParam Integer refundTypeStatus,
@RequestParam(required = false) String reason,
@RequestParam(required = false) String pictures) {
try {
mallOrderService.cancelOrder(orderId, userId, refundType, refundTypeStatus);
mallOrderService.cancelOrder(orderId, userId, refundType, refundTypeStatus, reason, pictures);
return ResultUtil.success("已取消");
} catch (Exception e) {
log.error("取消失败: {}", e.getMessage(), e);

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

@ -28,6 +28,8 @@ public interface MallDeliveryOrderMapper extends BaseMapper<MallDeliveryOrder> {
*/
IPage<MallDeliveryOrder> selectPageVOByStatus(IPage<?> page, @Param("q") MallDeliveryOrderPageQuery q);
IPage<MallDeliveryOrder> selectPageVOByStatusHis(IPage<?> page, @Param("q") MallDeliveryOrderPageQuery q);
/**
* 统计配送员当前活跃单量待取货+配送中
*/

4
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrderGroup.java

@ -25,6 +25,10 @@ public class MallOrderGroup implements Serializable {
private String id = SnowFlakeUtil.nextId().toString();
@ApiModelProperty(value = "店铺ID")
private String shopId;
@ApiModelProperty(value = "店铺名称")
private String shopName;
@ApiModelProperty(value = "区域ID")
private String regionId;
@ApiModelProperty(value = "团长用户ID")
private String headUserId;
@ApiModelProperty(value = "团长订单ID")

34
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/controller/IeController.java

@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@Api(tags = "i/e随机陪伴接口")
@ -59,6 +60,13 @@ public class IeController {
return new ResultUtil<IeUserProfileVO>().setData(matchService.profileByUserId(currentUserId(), targetUserId));
}
@RequestMapping(value = "/rooms/{roomId}/profile", method = RequestMethod.GET)
@ApiOperation("按房间获取对方半匿名i/e资料")
public Result<IeUserProfileVO> roomTargetProfile(@PathVariable Long roomId) {
Long targetUserId = chatService.roomTargetUserId(currentUserId(), roomId);
return new ResultUtil<IeUserProfileVO>().setData(matchService.profileByUserId(currentUserId(), targetUserId));
}
@RequestMapping(value = "/profile", method = RequestMethod.POST)
@ApiOperation("保存i/e资料")
public Result<IeUserProfileVO> saveProfile(@RequestBody IeProfileDTO dto) {
@ -129,6 +137,32 @@ public class IeController {
return ResultUtil.success("已解除");
}
@RequestMapping(value = "/block/{blockedUserId}/status", method = RequestMethod.GET)
@ApiOperation("查询拉黑状态")
public Result<Map<String, Boolean>> blockStatus(@PathVariable Long blockedUserId) {
return new ResultUtil<Map<String, Boolean>>().setData(chatService.blockStatus(currentUserId(), blockedUserId));
}
@RequestMapping(value = "/rooms/{roomId}/block", method = RequestMethod.POST)
@ApiOperation("按房间拉黑陪伴对象")
public Result<Object> blockRoomTarget(@PathVariable Long roomId, String reason) {
chatService.blockRoomTarget(currentUserId(), roomId, reason);
return ResultUtil.success("已拉黑");
}
@RequestMapping(value = "/rooms/{roomId}/block", method = RequestMethod.DELETE)
@ApiOperation("按房间解除拉黑陪伴对象")
public Result<Object> unblockRoomTarget(@PathVariable Long roomId) {
chatService.unblockRoomTarget(currentUserId(), roomId);
return ResultUtil.success("已解除");
}
@RequestMapping(value = "/rooms/{roomId}/block/status", method = RequestMethod.GET)
@ApiOperation("按房间查询拉黑状态")
public Result<Map<String, Boolean>> roomBlockStatus(@PathVariable Long roomId) {
return new ResultUtil<Map<String, Boolean>>().setData(chatService.roomBlockStatus(currentUserId(), roomId));
}
@RequestMapping(value = "/offline", method = RequestMethod.GET)
@ApiOperation("拉取离线消息")
public Result<List<String>> offline() {

12
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/IeChatService.java

@ -7,6 +7,8 @@ import cc.hiver.mall.ie.entity.*;
import cc.hiver.mall.ie.vo.IeMessageAckVO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import java.util.Map;
public interface IeChatService {
IeRoom getRoom(Long roomId);
@ -28,6 +30,16 @@ public interface IeChatService {
void unblock(Long userId, Long blockedUserId);
Map<String, Boolean> blockStatus(Long userId, Long targetUserId);
void blockRoomTarget(Long userId, Long roomId, String reason);
void unblockRoomTarget(Long userId, Long roomId);
Map<String, Boolean> roomBlockStatus(Long userId, Long roomId);
Long roomTargetUserId(Long userId, Long roomId);
IPage<IeRoomMessage> pageMessages(Long userId, Long roomId, Integer pageNumber, Integer pageSize);
void markRead(Long userId, Long roomId);

89
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/impl/IeChatServiceImpl.java

@ -25,9 +25,7 @@ import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.*;
@Service
public class IeChatServiceImpl implements IeChatService {
@ -185,6 +183,8 @@ public class IeChatServiceImpl implements IeChatService {
@Override
public void sendPresence(Long senderId, IePresenceDTO dto) {
IeRoom room = assertActiveRoom(senderId, dto.getRoomId());
Long receiverId = room.getUserAId().equals(senderId) ? room.getUserBId() : room.getUserAId();
assertNotBlocked(senderId, receiverId);
IePresenceEvent event = new IePresenceEvent();
event.setRoomId(room.getId());
event.setSenderId(senderId);
@ -192,7 +192,6 @@ public class IeChatServiceImpl implements IeChatService {
event.setEventText(dto.getEventText());
event.setCreateTime(new Date());
presenceEventMapper.insert(event);
Long receiverId = room.getUserAId().equals(senderId) ? room.getUserBId() : room.getUserAId();
messagingTemplate.convertAndSendToUser(String.valueOf(receiverId), "/queue/ie/presence", event);
}
@ -258,17 +257,11 @@ public class IeChatServiceImpl implements IeChatService {
block.setReason(reason);
block.setCreateTime(new Date());
blockMapper.insert(block);
broadcastBlockStatus(userId, blockedUserId);
}
private void assertNotBlocked(Long senderId, Long receiverId) {
Long count = blockMapper.selectCount(new LambdaQueryWrapper<IeBlock>()
.and(wrapper -> wrapper
.eq(IeBlock::getUserId, senderId)
.eq(IeBlock::getBlockedUserId, receiverId))
.or(wrapper -> wrapper
.eq(IeBlock::getUserId, receiverId)
.eq(IeBlock::getBlockedUserId, senderId)));
if (count != null && count > 0) {
if (hasBlock(senderId, receiverId) || hasBlock(receiverId, senderId)) {
throw new RuntimeException("你们之间已有黑名单关系,无法继续发送消息");
}
}
@ -278,6 +271,78 @@ public class IeChatServiceImpl implements IeChatService {
blockMapper.delete(new LambdaQueryWrapper<IeBlock>()
.eq(IeBlock::getUserId, userId)
.eq(IeBlock::getBlockedUserId, blockedUserId));
broadcastBlockStatus(userId, blockedUserId);
}
@Override
public void blockRoomTarget(Long userId, Long roomId, String reason) {
IeRoom room = assertActiveRoom(userId, roomId);
Long targetUserId = room.getUserAId().equals(userId) ? room.getUserBId() : room.getUserAId();
block(userId, targetUserId, reason);
}
@Override
public void unblockRoomTarget(Long userId, Long roomId) {
IeRoom room = assertActiveRoom(userId, roomId);
Long targetUserId = room.getUserAId().equals(userId) ? room.getUserBId() : room.getUserAId();
unblock(userId, targetUserId);
}
@Override
public Map<String, Boolean> roomBlockStatus(Long userId, Long roomId) {
IeRoom room = assertActiveRoom(userId, roomId);
Long targetUserId = room.getUserAId().equals(userId) ? room.getUserBId() : room.getUserAId();
return blockStatus(userId, targetUserId);
}
@Override
public Long roomTargetUserId(Long userId, Long roomId) {
IeRoom room = assertActiveRoom(userId, roomId);
return room.getUserAId().equals(userId) ? room.getUserBId() : room.getUserAId();
}
@Override
public Map<String, Boolean> blockStatus(Long userId, Long targetUserId) {
boolean blockedByMe = hasBlock(userId, targetUserId);
boolean blockedByOther = hasBlock(targetUserId, userId);
Map<String, Boolean> status = new HashMap<>();
status.put("blockedByMe", blockedByMe);
status.put("blockedByOther", blockedByOther);
status.put("blocked", blockedByMe || blockedByOther);
return status;
}
private boolean hasBlock(Long userId, Long blockedUserId) {
if (userId == null || blockedUserId == null) {
return false;
}
Long count = blockMapper.selectCount(new LambdaQueryWrapper<IeBlock>()
.eq(IeBlock::getUserId, userId)
.eq(IeBlock::getBlockedUserId, blockedUserId));
return count != null && count > 0;
}
private void broadcastBlockStatus(Long userId, Long targetUserId) {
List<IeRoom> rooms = roomMapper.selectList(new LambdaQueryWrapper<IeRoom>()
.and(wrapper -> wrapper
.eq(IeRoom::getUserAId, userId)
.eq(IeRoom::getUserBId, targetUserId))
.or(wrapper -> wrapper
.eq(IeRoom::getUserAId, targetUserId)
.eq(IeRoom::getUserBId, userId)));
for (IeRoom room : rooms) {
if (room == null || room.getId() == null) {
continue;
}
Map<String, Object> payload = new HashMap<>();
payload.put("type", "blockStatus");
payload.put("roomId", room.getId());
payload.put("userId", userId);
payload.put("targetUserId", targetUserId);
payload.put("userStatus", blockStatus(userId, targetUserId));
payload.put("targetStatus", blockStatus(targetUserId, userId));
messagingTemplate.convertAndSend("/topic/ie/room/" + room.getId(), payload);
}
}
@Override

34
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/impl/IeMatchServiceImpl.java

@ -41,6 +41,12 @@ public class IeMatchServiceImpl implements IeMatchService {
@Autowired
private IeRoomMapper roomMapper;
@Autowired
private IeRecordMapper recordMapper;
@Autowired
private IeRoomMessageMapper roomMessageMapper;
@Autowired
private IeBlockMapper blockMapper;
@ -59,6 +65,7 @@ public class IeMatchServiceImpl implements IeMatchService {
vo.setWaitingCount(redisService.waitingCount(profile.getCurrentMode(), "quiet"));
vo.setDailyQuota(profile.getDailyQuota());
vo.setUsedQuota((int) redisService.todayUsedQuota(userId));
vo.setUnreadCount(totalUnreadCount(userId));
vo.setCurrentMode(profile.getCurrentMode());
vo.setCurrentMood("quiet");
vo.setTargetModePreference(profile.getTargetModePreference());
@ -69,6 +76,25 @@ public class IeMatchServiceImpl implements IeMatchService {
return vo;
}
private int totalUnreadCount(Long userId) {
List<IeRecord> records = recordMapper.selectList(new LambdaQueryWrapper<IeRecord>()
.eq(IeRecord::getUserId, userId));
int total = 0;
for (IeRecord record : records) {
if (record.getRoomId() == null || record.getTargetUserId() == null) {
continue;
}
Long count = roomMessageMapper.selectCount(new LambdaQueryWrapper<IeRoomMessage>()
.eq(IeRoomMessage::getRoomId, record.getRoomId())
.eq(IeRoomMessage::getSenderId, record.getTargetUserId())
.eq(IeRoomMessage::getReceiverId, userId)
.eq(IeRoomMessage::getIsBlocked, 0)
.gt(record.getLastReadTime() != null, IeRoomMessage::getCreateTime, record.getLastReadTime()));
total += count == null ? 0 : count.intValue();
}
return total;
}
@Override
public IeUserProfileVO profile(Long userId) {
return toProfileVO(ensureProfile(userId));
@ -175,7 +201,7 @@ public class IeMatchServiceImpl implements IeMatchService {
Long targetUserId = null;
try {
if (redisService.todayUsedQuota(userId) >= maxQuota) {
return fail(userId, dto, "今日陪伴机会已经用完");
return fail(userId, dto, "今日陪伴机会已经用完,平台下单可增加匹配次数");
}
IeStatusDTO statusDTO = new IeStatusDTO();
BeanUtils.copyProperties(dto == null ? new IeMatchStartDTO() : dto, statusDTO);
@ -217,7 +243,7 @@ public class IeMatchServiceImpl implements IeMatchService {
}
long used = redisService.increaseTodayQuota(userId, maxQuota);
if (used > maxQuota) {
return fail(userId, dto, "今日陪伴机会已经用完");
return fail(userId, dto, "今日陪伴机会已经用完,平台下单可增加匹配次数");
}
IeUserProfile targetProfile = ensureProfile(targetUserId);
@ -247,6 +273,10 @@ public class IeMatchServiceImpl implements IeMatchService {
vo.setRoomId(room.getId());
vo.setRoomNo(room.getRoomNo());
vo.setAvatarUrl(targetProfile.getAvatarUrl());
vo.setGender(targetProfile.getGender());
vo.setIntro(targetProfile.getIntro());
vo.setInterestTags(jsonToTags(targetProfile.getInterestTags()));
vo.setPersonaImages(jsonToTags(targetProfile.getPersonaImages()));
return vo;
} finally {
redisService.unlockMatchUser(userId);

1
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/vo/IeHomeVO.java

@ -10,6 +10,7 @@ public class IeHomeVO {
private Long waitingCount;
private Integer dailyQuota;
private Integer usedQuota;
private Integer unreadCount;
private String currentMode;
private String currentMood;
private String targetModePreference;

5
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/vo/IeMatchVO.java

@ -3,6 +3,7 @@ package cc.hiver.mall.ie.vo;
import lombok.Data;
import java.util.Date;
import java.util.List;
@Data
public class IeMatchVO {
@ -12,6 +13,10 @@ public class IeMatchVO {
private String anonymousName;
private String avatarText;
private String avatarUrl;
private String gender;
private String intro;
private List<String> interestTags;
private List<String> personaImages;
private String mode;
private String mood;
private String stateText;

2
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallDeliveryOrderService.java

@ -27,6 +27,8 @@ public interface MallDeliveryOrderService extends IService<MallDeliveryOrder> {
*/
IPage<MallDeliveryOrder> pageDeliveryByStatus(MallDeliveryOrderPageQuery q);
IPage<MallDeliveryOrder> pageDeliveryByStatusHis(MallDeliveryOrderPageQuery q);
/**
* 配送员接单抢单大厅或指派单
*/

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

@ -50,7 +50,7 @@ public interface MallOrderService extends IService<MallOrder> {
/**
* 用户取消订单仅待支付/待成团状态可取消
*/
void cancelOrder(String orderId, String userId,Integer refundType, Integer refundTypeStatus);
void cancelOrder(String orderId, String userId,Integer refundType, Integer refundTypeStatus, String reason, String pictures);
/**
* 申请退款

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

@ -170,6 +170,15 @@ public class MallDeliveryOrderServiceImpl extends ServiceImpl<MallDeliveryOrderM
return page;
}
@Override
public IPage<MallDeliveryOrder> pageDeliveryByStatusHis(MallDeliveryOrderPageQuery q) {
IPage<MallDeliveryOrder> page = new Page<>(q.getPageNum(), q.getPageSize());
page = this.baseMapper.selectPageVOByStatusHis(page,q);
// 批量查询订单商品明细,避免 N+1 问题,处理面对面拼团子订单
assembleOrderGoodsForDeliveries(page.getRecords());
return page;
}
/**
* 配送员接单
* - 抢单大厅workerId 为空的单任何配送员均可接

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

@ -145,9 +145,7 @@ public class MallOrderGroupServiceImpl extends ServiceImpl<MallOrderGroupMapper,
.in(MallOrder::getId, orderIdList);
List<MallOrder> waitingOrders = mallOrderService.list(oqw);
boolean isFace2Face = (group.getStatus() == GROUP_STATUS_FACE2FACE
|| (group.getStatus() == GROUP_STATUS_SUCCESS
&& this.getById(groupId).getStatus() == GROUP_STATUS_FACE2FACE));
boolean isFace2Face = group.getIsFace() != null && group.getIsFace() == 1;
for (MallOrder order : waitingOrders) {
int targetStatus = (order.getDeliveryType() != null && order.getDeliveryType() == 1) ? 2 : 3;

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

@ -420,6 +420,8 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
group.setIsFace(STATUS_WAIT_PAY);
}
group.setShopId(dto.getShopId());
group.setRegionId(dto.getRegionId());
group.setShopName(order.getShopName());
group.setHeadUserId(dto.getUserId());
group.setTargetMembers(gp.getTargetMembers());
//group.setCurrentMembers(1);
@ -681,6 +683,8 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
//普通拼团团长发起拼团进缓存
if(order.getOrderType() == ORDER_TYPE_GROUP){
group.setCurrentMembers(newMembers);
group.setStatus(GROUP_STATUS_FORMING);
group.setIsFace(0);
shopGroupOrderCacheUtil.put(order.getShopId(), group);
}
}else{
@ -775,7 +779,7 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
@Override
@Transactional(rollbackFor = Exception.class)
public void cancelOrder(String orderId, String userId,Integer refundType,Integer refundTypeStatus) {
public void cancelOrder(String orderId, String userId,Integer refundType,Integer refundTypeStatus, String reason, String pictures) {
MallOrder order = getById(orderId);
if (order == null) throw new RuntimeException("订单不存在");
if (!userId.equals(order.getUserId())) throw new RuntimeException("无权操作该订单");
@ -788,10 +792,10 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
// 拼团 / 面对面团
if (order.getOrderType() == ORDER_TYPE_GROUP || order.getOrderType() == 3) {
handleGroupCancel(order,refundType,refundTypeStatus);
handleGroupCancel(order,refundType,refundTypeStatus, reason, pictures);
} else {
// 直接购买
handleDirectCancel(order, refundType,refundTypeStatus);
handleDirectCancel(order, refundType,refundTypeStatus, reason, pictures);
}
}
@ -802,22 +806,22 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
/**
* 拼团取消总调度判断团长/团员后分流
*/
private void handleGroupCancel(MallOrder order,Integer refundType,Integer refundTypeStatus) {
private void handleGroupCancel(MallOrder order,Integer refundType,Integer refundTypeStatus, String reason, String pictures) {
MallOrderGroup group = findGroupByOrderId(order.getId());
if (group == null) throw new RuntimeException("拼团信息不存在");
boolean isHead = order.getId().equals(group.getHeadOrderId());
if (isHead) {
handleGroupHeadCancel(order, group,refundType,refundTypeStatus);
handleGroupHeadCancel(order, group,refundType,refundTypeStatus, reason, pictures);
} else {
handleGroupMemberCancel(order, group,refundType,refundTypeStatus);
handleGroupMemberCancel(order, group,refundType,refundTypeStatus, reason, pictures);
}
}
/**
* 团长取消订单
*/
private void handleGroupHeadCancel(MallOrder order, MallOrderGroup group,Integer refundType,Integer refundTypeStatus) {
private void handleGroupHeadCancel(MallOrder order, MallOrderGroup group,Integer refundType,Integer refundTypeStatus, String reason, String pictures) {
boolean isFace2Face = (order.getOrderType() == 3);
// ---- 1. 未支付 ----
@ -909,13 +913,13 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
}
// ---- 3. 已成团后 ----
handlePostGroupCancel(order, group,refundType,refundTypeStatus);
handlePostGroupCancel(order, group,refundType,refundTypeStatus, reason, pictures);
}
/**
* 团员取消订单
*/
private void handleGroupMemberCancel(MallOrder order, MallOrderGroup group,Integer refundType,Integer refundTypeStatus) {
private void handleGroupMemberCancel(MallOrder order, MallOrderGroup group,Integer refundType,Integer refundTypeStatus, String reason, String pictures) {
// ---- 1. 未支付 ----
if (order.getStatus() == STATUS_WAIT_PAY) {
updateOrderStatus(order.getId(), STATUS_CANCELLED);
@ -955,13 +959,13 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
}
// ---- 3. 已成团后 ----
handlePostGroupCancel(order, group,refundType,refundTypeStatus);
handlePostGroupCancel(order, group,refundType,refundTypeStatus, reason, pictures);
}
/**
* 成团后取消逻辑团长和团员共用
*/
private void handlePostGroupCancel(MallOrder order, MallOrderGroup group,Integer refundType,Integer refundTypeStatus) {
private void handlePostGroupCancel(MallOrder order, MallOrderGroup group,Integer refundType,Integer refundTypeStatus, String reason, String pictures) {
boolean isFace2Face = (order.getOrderType() == 3);
boolean isHead = order.getId().equals(group.getHeadOrderId());
@ -977,7 +981,7 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
if (!isHead) {
throw new RuntimeException("面对面配送团只有团长可以取消订单");
}
handleFace2FaceDeliveryCancel(order, group, refundType,refundTypeStatus);
handleFace2FaceDeliveryCancel(order, group, refundType,refundTypeStatus, reason, pictures);
return;
}
@ -993,7 +997,7 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
}
}
if (delivery != null && delivery.getStatus() != null && delivery.getStatus() >= 1) {
applyMerchantRefund(delivery.getWorkerId(),order, "用户申请取消订单退款",refundType,refundTypeStatus);
applyMerchantRefund(delivery.getWorkerId(),order, appendCancelReason("用户申请取消订单退款", reason),refundType,refundTypeStatus, pictures);
}else{
// 允许取消
cancelDeliveryOrderByOrderId(order.getId());
@ -1004,14 +1008,14 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
}
} else {
// 自取(待消费)→ 待商家同意退款
applyMerchantRefund(null,order, "用户申请取消订单退款",refundType,refundTypeStatus);
applyMerchantRefund(null,order, appendCancelReason("用户申请取消订单退款", reason),refundType,refundTypeStatus, pictures);
}
}
/**
* 面对面团 + 配送 成团后团长取消批量操作所有子订单
*/
private void handleFace2FaceDeliveryCancel(MallOrder order, MallOrderGroup group,Integer refundType,Integer refundTypeStatus) {
private void handleFace2FaceDeliveryCancel(MallOrder order, MallOrderGroup group,Integer refundType,Integer refundTypeStatus, String reason, String pictures) {
MallDeliveryOrder delivery = findDeliveryByOrderId(order.getId());
if (delivery != null && delivery.getStatus() != null && delivery.getStatus() == 0) {
@ -1044,7 +1048,7 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
if (subOrder != null && subOrder.getStatus() != STATUS_CANCELLED
&& subOrder.getStatus() != STATUS_REFUNDED) {
subOrder.setGroupOrderIds(group.getGroupOrderIds());
applyMerchantRefund(delivery.getWorkerId(),subOrder, "面对面团团长申请取消",refundType,refundTypeStatus);
applyMerchantRefund(delivery.getWorkerId(),subOrder, appendCancelReason("面对面团团长申请取消", reason),refundType,refundTypeStatus, pictures);
}
}
}
@ -1057,7 +1061,7 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
/**
* 直接购买订单取消
*/
private void handleDirectCancel(MallOrder order,Integer refundType,Integer refundTypeStatus) {
private void handleDirectCancel(MallOrder order,Integer refundType,Integer refundTypeStatus, String reason, String pictures) {
// ---- 1. 未支付 ----
if (order.getStatus() == STATUS_WAIT_PAY) {
updateOrderStatus(order.getId(), STATUS_CANCELLED);
@ -1083,7 +1087,7 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
MallDeliveryOrder delivery = findDeliveryByOrderId(order.getId());
if (delivery != null && delivery.getStatus() != null && delivery.getStatus() >= 1) {
// 配送员已接单 → 走商家或者配送员同意流程
applyMerchantRefund(delivery.getWorkerId(),order, "用户申请取消订单", refundType, refundTypeStatus);
applyMerchantRefund(delivery.getWorkerId(),order, appendCancelReason("用户申请取消订单", reason), refundType, refundTypeStatus, pictures);
} else {
// 未接单 → 直接取消并退款
cancelDeliveryOrderByOrderId(order.getId());
@ -1098,7 +1102,7 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
autoRefund(order, "用户取消订单,系统退款");
restoreStock(order.getId());
}else{
applyMerchantRefund(null,order, "用户申请取消订单", refundType, refundTypeStatus);
applyMerchantRefund(null,order, appendCancelReason("用户申请取消订单", reason), refundType, refundTypeStatus, pictures);
}
}
}
@ -1703,12 +1707,19 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
* 创建退款记录
*/
private void createRefundRecord(String workerId,MallOrder order, BigDecimal amount, String reason,Integer refundType,Integer refundTypeStatus) {
createRefundRecord(workerId, order, amount, reason, refundType, refundTypeStatus, null);
}
private void createRefundRecord(String workerId,MallOrder order, BigDecimal amount, String reason,Integer refundType,Integer refundTypeStatus, String pictures) {
MallRefundRecord record = new MallRefundRecord();
record.setOrderId(order.getId());
record.setUserId(order.getUserId());
record.setRefundAmount(amount);
record.setRegionId(order.getRegionId());
record.setReason(reason);
if (StringUtils.isNotBlank(pictures)) {
record.setPictures(pictures);
}
if(order.getGroupOrderIds() != null){
record.setGroupOrderIds(order.getGroupOrderIds());
}
@ -1967,6 +1978,10 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
* 申请商家同意退款订单状态 待商家同意退款(7)退款记录状态 待商家同意(0)
*/
private void applyMerchantRefund(String workerId, MallOrder order, String reason,Integer refundType,Integer refundTypeStatus) {
applyMerchantRefund(workerId, order, reason, refundType, refundTypeStatus, null);
}
private void applyMerchantRefund(String workerId, MallOrder order, String reason,Integer refundType,Integer refundTypeStatus, String pictures) {
updateOrderStatus(order.getId(), STATUS_WAIT_REFUND);
//更新缓存
MallOrderVO vo = userPendingOrderCacheUtil.get(order.getUserId(), order.getId());
@ -1981,7 +1996,14 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
.set(MallDeliveryOrder::getIsReturn, IS_RETURN_EXPRESS);
mallDeliveryOrderMapper.update(null, duw);
}
createRefundRecord(workerId,order, order.getTotalAmount(), reason,refundType,refundTypeStatus);
createRefundRecord(workerId,order, order.getTotalAmount(), reason,refundType,refundTypeStatus, pictures);
}
private String appendCancelReason(String baseReason, String reason) {
if (StringUtils.isBlank(reason)) {
return baseReason;
}
return baseReason + ":" + reason;
}
/**

145
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/ShopGroupOrderCacheUtil.java

@ -15,11 +15,12 @@ import java.util.Map;
/**
* 商家待成团订单缓存工具类
* <p>
* 底层使用 Redis Hash 结构保证按 shopId + orderId 维度的 O(1) 读写效率
* 底层使用 Redis Hash 结构保证按 shopId / regionId + orderId 维度的 O(1) 读写效率
* <pre>
* Key = SHOP_GROUP_ORDERS:{shopId}
* Field = orderId
* Value = MallOrderGroup JSON 序列化
* 店铺维度 Key = SHOP_GROUP_ORDERS:{shopId}
* 区域维度 Key = SHOP_GROUP_ORDERS_REGION:{regionId}
* Field = orderId
* Value = MallOrderGroup JSON 序列化
* </pre>
* <p>
* 使用场景订单状态发生变化时创建更新取消完成等
@ -34,8 +35,14 @@ public class ShopGroupOrderCacheUtil {
/** Redis Key 前缀 */
private static final String KEY_PREFIX = "SHOP_GROUP_ORDERS:";
/** 订单不再属于"待成团"的状态集合 */
private static final int STATUS_DONE = 0;
/** 区域待成团 Redis Key 前缀 */
private static final String REGION_KEY_PREFIX = "SHOP_GROUP_ORDERS_REGION:";
/** 公开待成团状态 */
private static final int STATUS_FORMING = 0;
/** 非面对面拼团 */
private static final int IS_FACE_NORMAL = 0;
@Autowired
private RedisTemplateHelper redisTemplateHelper;
@ -48,6 +55,10 @@ public class ShopGroupOrderCacheUtil {
return KEY_PREFIX + shopId;
}
private String buildRegionKey(String regionId) {
return REGION_KEY_PREFIX + regionId;
}
// ================================================================
// 存放(put)
// ================================================================
@ -64,10 +75,17 @@ public class ShopGroupOrderCacheUtil {
return;
}
try {
String key = buildKey(shopId);
if (!isPublicPending(orderVO)) {
remove(shopId, orderVO.getRegionId(), orderVO.getId());
return;
}
String json = JSONUtil.toJsonStr(orderVO);
redisTemplateHelper.hPut(key, orderVO.getId(), json);
log.info("缓存店铺待成团订单: shopId={}, orderId={}", shopId, orderVO.getId());
redisTemplateHelper.hPut(buildKey(shopId), orderVO.getId(), json);
if (StringUtils.isNotBlank(orderVO.getRegionId())) {
redisTemplateHelper.hPut(buildRegionKey(orderVO.getRegionId()), orderVO.getId(), json);
}
log.info("缓存店铺待成团订单: shopId={}, regionId={}, orderId={}",
shopId, orderVO.getRegionId(), orderVO.getId());
} catch (Exception e) {
log.info("缓存店铺待成团订单失败: shopId={}, orderId={}", shopId, orderVO.getId(), e);
}
@ -84,15 +102,23 @@ public class ShopGroupOrderCacheUtil {
return;
}
try {
String key = buildKey(shopId);
Map<String, String> map = new java.util.LinkedHashMap<>(orders.size());
Map<String, Map<String, String>> regionMap = new java.util.LinkedHashMap<>();
for (MallOrderGroup order : orders) {
if (order != null && StringUtils.isNotBlank(order.getId())) {
map.put(order.getId(), JSONUtil.toJsonStr(order));
if (order != null && StringUtils.isNotBlank(order.getId()) && isPublicPending(order)) {
String json = JSONUtil.toJsonStr(order);
map.put(order.getId(), json);
if (StringUtils.isNotBlank(order.getRegionId())) {
regionMap.computeIfAbsent(order.getRegionId(), k -> new java.util.LinkedHashMap<>())
.put(order.getId(), json);
}
}
}
if (!map.isEmpty()) {
redisTemplateHelper.hPutAll(key, map);
redisTemplateHelper.hPutAll(buildKey(shopId), map);
for (Map.Entry<String, Map<String, String>> entry : regionMap.entrySet()) {
redisTemplateHelper.hPutAll(buildRegionKey(entry.getKey()), entry.getValue());
}
log.info("批量缓存店铺待成团订单: shopId={}, count={}", shopId, map.size());
}
} catch (Exception e) {
@ -118,14 +144,33 @@ public class ShopGroupOrderCacheUtil {
return;
}
try {
String key = buildKey(shopId);
redisTemplateHelper.hDelete(key, orderId);
MallOrderGroup cachedOrder = get(shopId, orderId);
redisTemplateHelper.hDelete(buildKey(shopId), orderId);
if (cachedOrder != null && StringUtils.isNotBlank(cachedOrder.getRegionId())) {
redisTemplateHelper.hDelete(buildRegionKey(cachedOrder.getRegionId()), orderId);
}
log.info("删除待成团订单缓存: shopId={}, orderId={}", shopId, orderId);
} catch (Exception e) {
log.info("删除待成团订单缓存失败: shopId={}, orderId={}", shopId, orderId, e);
}
}
public void remove(String shopId, String regionId, String orderId) {
if (StringUtils.isBlank(shopId) || StringUtils.isBlank(orderId)) {
log.info("ShopGroupOrderCacheUtil.remove 参数无效, shopId={}, orderId={}", shopId, orderId);
return;
}
try {
redisTemplateHelper.hDelete(buildKey(shopId), orderId);
if (StringUtils.isNotBlank(regionId)) {
redisTemplateHelper.hDelete(buildRegionKey(regionId), orderId);
}
log.info("删除待成团订单缓存: shopId={}, regionId={}, orderId={}", shopId, regionId, orderId);
} catch (Exception e) {
log.info("删除待成团订单缓存失败: shopId={}, regionId={}, orderId={}", shopId, regionId, orderId, e);
}
}
/**
* 清除指定店铺所有待成团订单缓存
* <p>
@ -165,8 +210,8 @@ public class ShopGroupOrderCacheUtil {
}
try {
// 如果订单状态已不属于待成团,直接删除
/*if (!isTerminalStatus(orderVO.getStatus())) {
remove(shopId, orderVO.getId());
/*if (!isPublicPending(orderVO)) {
remove(shopId, orderVO.getRegionId(), orderVO.getId());
log.debug("订单已终态,从缓存移除: shopId={}, orderId={}, status={}",
shopId, orderVO.getId(), orderVO.getStatus());
return;
@ -213,6 +258,57 @@ public class ShopGroupOrderCacheUtil {
}
}
/**
* 获取区域维度待成团订单缓存
*/
public List<MallOrderGroup> getAllByRegion(String regionId) {
if (StringUtils.isBlank(regionId)) {
return null;
}
try {
Map<Object, Object> entries = redisTemplateHelper.hGetAll(buildRegionKey(regionId));
if (entries == null || entries.isEmpty()) {
return null;
}
List<MallOrderGroup> result = new ArrayList<>(entries.size());
for (Object value : entries.values()) {
if (value != null) {
MallOrderGroup order = JSONUtil.toBean(value.toString(), MallOrderGroup.class);
if (isPublicPending(order)) {
result.add(order);
}
}
}
return result;
} catch (Exception e) {
log.info("获取区域待成团订单缓存失败: regionId={}", regionId, e);
return null;
}
}
/**
* 批量写入区域维度待成团缓存通常用于回源后预热
*/
public void putAllByRegion(String regionId, List<MallOrderGroup> orders) {
if (StringUtils.isBlank(regionId) || orders == null || orders.isEmpty()) {
return;
}
try {
Map<String, String> map = new java.util.LinkedHashMap<>(orders.size());
for (MallOrderGroup order : orders) {
if (order != null && StringUtils.isNotBlank(order.getId()) && isPublicPending(order)) {
map.put(order.getId(), JSONUtil.toJsonStr(order));
}
}
if (!map.isEmpty()) {
redisTemplateHelper.hPutAll(buildRegionKey(regionId), map);
log.info("批量缓存区域待成团订单: regionId={}, count={}", regionId, map.size());
}
} catch (Exception e) {
log.info("批量缓存区域待成团订单失败: regionId={}", regionId, e);
}
}
/**
* 获取店铺缓存待成团单个订单
*
@ -251,6 +347,14 @@ public class ShopGroupOrderCacheUtil {
return hasKey != null && hasKey;
}
public boolean existsRegion(String regionId) {
if (StringUtils.isBlank(regionId)) {
return false;
}
Boolean hasKey = redisTemplateHelper.hasKey(buildRegionKey(regionId));
return hasKey != null && hasKey;
}
// ================================================================
// 内部工具
// ================================================================
@ -258,10 +362,11 @@ public class ShopGroupOrderCacheUtil {
/**
* 判断订单状态是否为终态不再属于"待成团"
*/
private boolean isTerminalStatus(Integer status) {
if (status == null) {
public boolean isPublicPending(MallOrderGroup order) {
if (order == null || order.getStatus() == null) {
return false;
}
return status == STATUS_DONE;
return order.getStatus() == STATUS_FORMING
&& (order.getIsFace() == null || order.getIsFace() == IS_FACE_NORMAL);
}
}

50
hiver-modules/hiver-mall/src/main/resources/mapper/MallDeliveryOrderMapper.xml

@ -167,6 +167,56 @@
ORDER BY (CASE WHEN d.must_finish_time IS NULL THEN 1 ELSE 0 END) ASC
</select>
<select id="selectPageVOByStatusHis" resultMap="deliveryMap">
SELECT
d.id, d.order_id, d.group_id, d.worker_id, d.shop_id,
d.get_area_id, d.put_area_id, d.delivery_fee, d.delivery_fee_marketplace,
d.status, d.create_time, d.accept_time, d.get_time, d.must_finish_time, d.finish_time,
d.receiver_name, d.receiver_phone, d.receiver_address,
d.shop_name, d.shop_phone, d.shop_address, d.delivery_type, d.number_code,
d.region_id,d.remark, d.all_count, d.phone_number, d.get_codes, d.get_pictures,
d.is_big,d.is_return,d.worker_name,d.arrive_time,d.new_worker,d.worker_phone
FROM mall_delivery_order d
<where>
d.status not in (-1,4)
<if test="q.regionId != null and q.regionId != ''">
AND d.region_id = #{q.regionId}
</if>
<if test="q.workerId != null and q.workerId != ''">
AND d.worker_id = #{q.workerId}
</if>
<if test="q.newWorker != null and q.newWorker != ''">
AND d.new_worker = #{q.newWorker}
</if>
<if test="q.shopId != null and q.shopId != ''">
AND d.shop_id = #{q.shopId}
</if>
<if test="q.status != null">
AND d.status = #{q.status}
</if>
<if test="q.getAreaId != null and q.getAreaId != ''">
AND d.get_area_id = #{q.getAreaId}
</if>
<if test="q.putAreaId != null and q.putAreaId != ''">
AND d.put_area_id = #{q.putAreaId}
</if>
<if test="q.hallOnly != null and q.hallOnly == true">
AND (d.worker_id IS NULL OR d.worker_id = '')
AND d.status = 0
</if>
<if test="q.startDate != null and q.startDate != ''">
AND DATE(d.create_time) &gt;= #{q.startDate}
</if>
<if test="q.endDate != null and q.endDate != ''">
AND DATE(d.create_time) &lt;= #{q.endDate}
</if>
<if test="q.deliveryType != null">
AND d.delivery_type = #{q.deliveryType}
</if>
</where>
ORDER BY d.finish_time desc
</select>
<select id="selectByGroupId" resultMap="deliveryMap">
SELECT
d.id, d.order_id, d.group_id, d.worker_id, d.shop_id,

10
hiver-modules/hiver-mall/src/main/resources/mapper/MallOrderGroupMapper.xml

@ -23,13 +23,15 @@
<result column="product_picture" property="productPicture"/>
<result column="product_id" property="productId"/>
<result column="success_time" property="successTime"/>
<result column="region_id" property="regionId"/>
<result column="shop_name" property="shopName"/>
</resultMap>
<select id="selectMallGroup" resultMap="groupMap">
SELECT id,shop_id,head_user_id,head_order_id,group_order_ids,target_members,
current_members,status,group_price,worker_id,worker_commission,total_delivery_fee,
create_time,expire_time,is_face,product_name,product_picture,product_id,success_time
create_time,expire_time,is_face,product_name,product_picture,product_id,success_time,region_id,shop_name
FROM mall_order_group
<where>
<if test="group.shopId != null and group.shopId != ''">
@ -41,6 +43,12 @@
<if test="group.status != null">
AND status = #{group.status}
</if>
<if test="group.regionId != null and group.regionId != ''">
AND region_id = #{group.regionId}
</if>
<if test="group.shopName != null and group.shopName != ''">
AND shop_name = #{group.shopName}
</if>
</where>
order by create_time asc
</select>

Loading…
Cancel
Save