Browse Source

对接拼团数据1

master
wangfukang 2 weeks ago
parent
commit
06e6b1bad0
  1. 16
      hiver-admin/test-output/test-report.html
  2. 2
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallOrderController.java
  3. 29
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/controller/IeController.java
  4. 1
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/dto/IeProfileDTO.java
  5. 12
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/entity/IeRecord.java
  6. 1
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/entity/IeUserProfile.java
  7. 11
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/IeChatService.java
  8. 5
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/IeMatchService.java
  9. 2
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/IeRedisService.java
  10. 121
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/impl/IeChatServiceImpl.java
  11. 161
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/impl/IeMatchServiceImpl.java
  12. 7
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/impl/IeRedisServiceImpl.java
  13. 3
      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/ie/vo/IeUserProfileVO.java
  15. 90
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderAsyncConsumer.java
  16. 9
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderAsyncProducer.java
  17. 129
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java
  18. 18
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java
  19. 2
      hiver-modules/hiver-mall/src/main/resources/mapper/MallCouponMapper.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">五月 19, 2026 15:04:26</span></a>
<a href="#"><span class="badge badge-primary">五月 21, 2026 15:07:44</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:04:27 下午</span> / <span>0.017 secs</span></p>
<p class="text-sm"><span>15:07:45 下午</span> / <span>0.024 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.19.2026 15:04:27</span>
<span class='badge badge-danger'>05.19.2026 15:04:27</span>
<span class='badge badge-default'>0.017 secs</span>
<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>
</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:04:27</td>
<td>15:07:45</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>五月 19, 2026 15:04:26</h3>
<h3>五月 21, 2026 15:07:44</h3>
</div></div>
</div>
<div class="col-md-3">
<div class="card"><div class="card-body">
<p class="m-b-0">Ended</p>
<h3>五月 19, 2026 15:04:27</h3>
<h3>五月 21, 2026 15:07:45</h3>
</div></div>
</div>
<div class="col-md-3">

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

@ -274,7 +274,7 @@ public class MallOrderController {
* 自取订单完成
*/
@PostMapping("/complete")
@ApiOperation(value = "自取订单完成")
@ApiOperation(value = "自取/团购到店订单完成")
public Result completeOrder(@RequestParam String orderId) {
try {
mallOrderService.completeOrder(orderId);

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

@ -5,6 +5,7 @@ import cc.hiver.core.common.utils.SecurityUtil;
import cc.hiver.core.common.vo.Result;
import cc.hiver.core.entity.User;
import cc.hiver.mall.ie.dto.*;
import cc.hiver.mall.ie.entity.IeBlock;
import cc.hiver.mall.ie.entity.IeRecord;
import cc.hiver.mall.ie.entity.IeReport;
import cc.hiver.mall.ie.entity.IeRoomMessage;
@ -77,6 +78,13 @@ public class IeController {
return new ResultUtil<IeMatchVO>().setData(matchService.startMatch(currentUserId(), dto));
}
@RequestMapping(value = "/matches/page", method = RequestMethod.GET)
@ApiOperation("分页查询我的匹配记录")
public Result<IPage<IeMatchVO>> pageMatches(@RequestParam(required = false, defaultValue = "1") Integer pageNumber,
@RequestParam(required = false, defaultValue = "10") Integer pageSize) {
return new ResultUtil<IPage<IeMatchVO>>().setData(matchService.pageMatches(currentUserId(), pageNumber, pageSize));
}
@RequestMapping(value = "/rooms/{roomId}/presence", method = RequestMethod.POST)
@ApiOperation("发送轻互动/输入状态/心跳事件")
public Result<Object> presence(@PathVariable Long roomId, @RequestBody IePresenceDTO dto) {
@ -114,6 +122,13 @@ public class IeController {
return ResultUtil.success("已拉黑");
}
@RequestMapping(value = "/block/{blockedUserId}", method = RequestMethod.DELETE)
@ApiOperation("解除拉黑陪伴对象")
public Result<Object> unblock(@PathVariable Long blockedUserId) {
chatService.unblock(currentUserId(), blockedUserId);
return ResultUtil.success("已解除");
}
@RequestMapping(value = "/offline", method = RequestMethod.GET)
@ApiOperation("拉取离线消息")
public Result<List<String>> offline() {
@ -135,6 +150,13 @@ public class IeController {
return new ResultUtil<IPage<IeRecord>>().setData(chatService.pageRecords(currentUserId(), pageNumber, pageSize));
}
@RequestMapping(value = "/records/{recordId}", method = RequestMethod.DELETE)
@ApiOperation("删除我的聊天记录")
public Result<Object> deleteRecord(@PathVariable Long recordId) {
chatService.deleteRecord(currentUserId(), recordId);
return ResultUtil.success("已删除");
}
@RequestMapping(value = "/reports/page", method = RequestMethod.GET)
@ApiOperation("分页查询我的举报记录")
public Result<IPage<IeReport>> pageReports(@RequestParam(required = false, defaultValue = "1") Integer pageNumber,
@ -142,6 +164,13 @@ public class IeController {
return new ResultUtil<IPage<IeReport>>().setData(chatService.pageReports(currentUserId(), pageNumber, pageSize));
}
@RequestMapping(value = "/blocks/page", method = RequestMethod.GET)
@ApiOperation("分页查询我的黑名单")
public Result<IPage<IeBlock>> pageBlocks(@RequestParam(required = false, defaultValue = "1") Integer pageNumber,
@RequestParam(required = false, defaultValue = "10") Integer pageSize) {
return new ResultUtil<IPage<IeBlock>>().setData(chatService.pageBlocks(currentUserId(), pageNumber, pageSize));
}
private Long currentUserId() {
User user = securityUtil.getCurrUser();
return Long.valueOf(user.getId());

1
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/dto/IeProfileDTO.java

@ -12,6 +12,7 @@ public class IeProfileDTO {
private String gender;
private String intro;
private List<String> interestTags;
private List<String> personaImages;
private String currentMode;
private String targetModePreference;
private String targetGenderPreference;

12
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/entity/IeRecord.java

@ -26,4 +26,16 @@ public class IeRecord extends IeBaseEntity {
private Date lastReadTime;
@TableField(exist = false)
private Integer unreadCount;
@TableField(exist = false)
private String avatarText;
@TableField(exist = false)
private String avatarUrl;
@TableField(exist = false)
private String intro;
@TableField(exist = false)
private String currentMode;
}

1
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/entity/IeUserProfile.java

@ -17,6 +17,7 @@ public class IeUserProfile extends IeBaseEntity {
private String gender;
private String intro;
private String interestTags;
private String personaImages;
private String currentMode;
private String recentPreference;
private String targetModePreference;

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

@ -3,10 +3,7 @@ package cc.hiver.mall.ie.service;
import cc.hiver.mall.ie.dto.IePresenceDTO;
import cc.hiver.mall.ie.dto.IeReportDTO;
import cc.hiver.mall.ie.dto.IeRoomMessageDTO;
import cc.hiver.mall.ie.entity.IeRecord;
import cc.hiver.mall.ie.entity.IeReport;
import cc.hiver.mall.ie.entity.IeRoom;
import cc.hiver.mall.ie.entity.IeRoomMessage;
import cc.hiver.mall.ie.entity.*;
import cc.hiver.mall.ie.vo.IeMessageAckVO;
import com.baomidou.mybatisplus.core.metadata.IPage;
@ -29,11 +26,17 @@ public interface IeChatService {
void block(Long userId, Long blockedUserId, String reason);
void unblock(Long userId, Long blockedUserId);
IPage<IeRoomMessage> pageMessages(Long userId, Long roomId, Integer pageNumber, Integer pageSize);
void markRead(Long userId, Long roomId);
IPage<IeRecord> pageRecords(Long userId, Integer pageNumber, Integer pageSize);
void deleteRecord(Long userId, Long recordId);
IPage<IeReport> pageReports(Long userId, Integer pageNumber, Integer pageSize);
IPage<IeBlock> pageBlocks(Long userId, Integer pageNumber, Integer pageSize);
}

5
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/IeMatchService.java

@ -6,6 +6,7 @@ import cc.hiver.mall.ie.dto.IeStatusDTO;
import cc.hiver.mall.ie.vo.IeHomeVO;
import cc.hiver.mall.ie.vo.IeMatchVO;
import cc.hiver.mall.ie.vo.IeUserProfileVO;
import com.baomidou.mybatisplus.core.metadata.IPage;
public interface IeMatchService {
IeHomeVO home(Long userId);
@ -19,4 +20,8 @@ public interface IeMatchService {
void updateStatus(Long userId, IeStatusDTO dto);
IeMatchVO startMatch(Long userId, IeMatchStartDTO dto);
IPage<IeMatchVO> pageMatches(Long userId, Integer pageNumber, Integer pageSize);
void rewardMatchQuota(Long userId, Integer amount);
}

2
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/IeRedisService.java

@ -33,6 +33,8 @@ public interface IeRedisService {
void pushOfflineMessage(Long userId, String messageJson);
void clearOfflineMessages(Long userId);
List<String> popOfflineMessages(Long userId, int limit);
void cacheWebSocketConnection(Long userId, String sessionId);

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

@ -50,6 +50,9 @@ public class IeChatServiceImpl implements IeChatService {
@Autowired
private IeRecordMapper recordMapper;
@Autowired
private IeUserProfileMapper userProfileMapper;
@Autowired
private IeRedisService redisService;
@ -87,6 +90,8 @@ public class IeChatServiceImpl implements IeChatService {
}
IeRoom room = assertActiveRoom(senderId, dto.getRoomId());
Long receiverId = room.getUserAId().equals(senderId) ? room.getUserBId() : room.getUserAId();
assertNotBlocked(senderId, receiverId);
ensureMessageVisibleRecords(room, senderId, receiverId);
assertReplyWindow(senderId, receiverId, room.getId());
Integer messageType = dto.getMessageType() == null ? 1 : dto.getMessageType();
IeAuditResult audit = auditMessage(room.getId(), senderId, messageType, dto.getContent());
@ -158,7 +163,7 @@ public class IeChatServiceImpl implements IeChatService {
}
private IeAuditResult auditMessage(Long roomId, Long senderId, Integer messageType, String content) {
if (messageType != null && (messageType == 2 || messageType == 4)) {
if (messageType != null && (messageType == 2 || messageType == 4 || messageType == 5)) {
IeAuditResult result = new IeAuditResult();
result.setRawContent(content);
result.setFilteredContent(content);
@ -255,9 +260,30 @@ public class IeChatServiceImpl implements IeChatService {
blockMapper.insert(block);
}
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) {
throw new RuntimeException("你们之间已有黑名单关系,无法继续发送消息");
}
}
@Override
public void unblock(Long userId, Long blockedUserId) {
blockMapper.delete(new LambdaQueryWrapper<IeBlock>()
.eq(IeBlock::getUserId, userId)
.eq(IeBlock::getBlockedUserId, blockedUserId));
}
@Override
public IPage<IeRoomMessage> pageMessages(Long userId, Long roomId, Integer pageNumber, Integer pageSize) {
assertRoomParticipant(userId, roomId);
ensureRoomVisibleRecords(userId, roomId);
markRead(userId, roomId);
Page<IeRoomMessage> page = new Page<>(safePageNumber(pageNumber), safePageSize(pageSize));
IPage<IeRoomMessage> result = roomMessageMapper.selectPage(page, new LambdaQueryWrapper<IeRoomMessage>()
@ -270,6 +296,56 @@ public class IeChatServiceImpl implements IeChatService {
return result;
}
private void ensureRoomVisibleRecords(Long userId, Long roomId) {
IeRoom room = getRoom(roomId);
if (room == null || !room.getUserAId().equals(userId)) {
return;
}
IeUserProfile targetProfile = findProfileByUserId(room.getUserBId());
IeUserProfile selfProfile = findProfileByUserId(room.getUserAId());
createChatRecord(room, room.getUserAId(), room.getUserBId(), targetProfile);
createChatRecord(room, room.getUserBId(), room.getUserAId(), selfProfile);
}
private void ensureMessageVisibleRecords(IeRoom room, Long senderId, Long receiverId) {
if (room == null) {
return;
}
IeUserProfile receiverProfile = findProfileByUserId(receiverId);
IeUserProfile senderProfile = findProfileByUserId(senderId);
createChatRecord(room, senderId, receiverId, receiverProfile);
createChatRecord(room, receiverId, senderId, senderProfile);
}
private IeUserProfile findProfileByUserId(Long userId) {
return userProfileMapper.selectOne(new LambdaQueryWrapper<IeUserProfile>()
.eq(IeUserProfile::getUserId, userId)
.last("limit 1"));
}
private void createChatRecord(IeRoom room, Long userId, Long targetUserId, IeUserProfile targetProfile) {
IeRecord exists = recordMapper.selectOne(new LambdaQueryWrapper<IeRecord>()
.eq(IeRecord::getRoomId, room.getId())
.eq(IeRecord::getUserId, userId)
.last("limit 1"));
if (exists != null) {
return;
}
IeRecord record = new IeRecord();
record.setRoomId(room.getId());
record.setUserId(userId);
record.setTargetUserId(targetUserId);
record.setMode(room.getMode());
record.setMood(room.getMood());
record.setAnonymousName(targetProfile == null || targetProfile.getAnonymousName() == null ? "半匿名漂流者" : targetProfile.getAnonymousName());
record.setDurationSeconds(0);
record.setSummary("你们已经开始聊天,可以从这里回到对话。");
record.setTags(room.getMode() + "," + room.getMood() + ",继续聊天");
record.setRemeetAvailable(1);
record.setCreateTime(new Date());
recordMapper.insert(record);
}
@Override
public void markRead(Long userId, Long roomId) {
IeRecord record = recordMapper.selectOne(new LambdaQueryWrapper<IeRecord>()
@ -292,10 +368,45 @@ public class IeChatServiceImpl implements IeChatService {
.orderByDesc(IeRecord::getCreateTime));
for (IeRecord record : result.getRecords()) {
record.setUnreadCount(unreadMessageCount(userId, record.getRoomId(), record.getTargetUserId()));
fillLatestTargetProfile(record);
}
return result;
}
@Override
@Transactional
public void deleteRecord(Long userId, Long recordId) {
IeRecord record = recordMapper.selectOne(new LambdaQueryWrapper<IeRecord>()
.eq(IeRecord::getId, recordId)
.eq(IeRecord::getUserId, userId)
.last("limit 1"));
if (record == null) {
return;
}
Long roomId = record.getRoomId();
recordMapper.deleteById(record.getId());
redisService.clearOfflineMessages(userId);
}
private void fillLatestTargetProfile(IeRecord record) {
if (record == null || record.getTargetUserId() == null) {
return;
}
IeUserProfile profile = findProfileByUserId(record.getTargetUserId());
if (profile == null || !isCompletedProfile(profile)) {
return;
}
record.setAnonymousName(profile.getAnonymousName());
record.setAvatarText(profile.getAvatarText());
record.setAvatarUrl(profile.getAvatarUrl());
record.setIntro(profile.getIntro());
record.setCurrentMode(profile.getCurrentMode());
}
private boolean isCompletedProfile(IeUserProfile profile) {
return profile != null && profile.getProfileCompleted() != null && profile.getProfileCompleted() == 1;
}
private int unreadMessageCount(Long userId, Long roomId, Long targetUserId) {
if (roomId == null || targetUserId == null) {
return 0;
@ -325,6 +436,14 @@ public class IeChatServiceImpl implements IeChatService {
.orderByDesc(IeReport::getCreateTime));
}
@Override
public IPage<IeBlock> pageBlocks(Long userId, Integer pageNumber, Integer pageSize) {
Page<IeBlock> page = new Page<>(safePageNumber(pageNumber), safePageSize(pageSize));
return blockMapper.selectPage(page, new LambdaQueryWrapper<IeBlock>()
.eq(IeBlock::getUserId, userId)
.orderByDesc(IeBlock::getCreateTime));
}
@Override
@Transactional
public void finishRoomBySystem(Long roomId, Integer status) {

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

@ -16,6 +16,8 @@ import cc.hiver.mall.ie.vo.IeUserProfileVO;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
@ -40,7 +42,7 @@ public class IeMatchServiceImpl implements IeMatchService {
private IeRoomMapper roomMapper;
@Autowired
private IeRecordMapper recordMapper;
private IeBlockMapper blockMapper;
@Autowired
private IeRedisService redisService;
@ -78,8 +80,13 @@ public class IeMatchServiceImpl implements IeMatchService {
return toProfileVO(ensureProfile(userId));
}
IeUserProfile profile = findProfileByUserId(targetUserId);
if (profile == null || profile.getProfileCompleted() == null || profile.getProfileCompleted() != 1) {
throw new RuntimeException("对方资料暂时不可见");
if (profile == null) {
IeUserProfileVO vo = new IeUserProfileVO();
vo.setUserId(targetUserId);
vo.setExists(false);
vo.setInterestTags(new ArrayList<>());
vo.setPersonaImages(new ArrayList<>());
return vo;
}
return toProfileVO(profile);
}
@ -101,6 +108,7 @@ public class IeMatchServiceImpl implements IeMatchService {
profile.setGender(normalizeGender(dto.getGender(), "unknown"));
profile.setIntro(intro);
profile.setInterestTags(tagsToJson(dto.getInterestTags()));
profile.setPersonaImages(imagesToJson(dto.getPersonaImages()));
profile.setCurrentMode(normalizeMode(dto.getCurrentMode(), "i"));
profile.setRecentPreference(profile.getCurrentMode());
profile.setTargetModePreference(normalizeTargetMode(dto.getTargetModePreference()));
@ -187,6 +195,9 @@ public class IeMatchServiceImpl implements IeMatchService {
if (redisService.isRecentlyMatched(userId, candidateUserId)) {
continue;
}
if (hasBlockRelation(userId, candidateUserId)) {
continue;
}
IeUserProfile candidateProfile = findProfileByUserId(candidateUserId);
if (!matchCandidate(candidateProfile, targetMode, targetGender)) {
continue;
@ -225,9 +236,10 @@ public class IeMatchServiceImpl implements IeMatchService {
match.setUpdateTime(new Date());
matchAttemptMapper.insert(match);
IeRoom room = createRoom(match, userId, targetUserId, mode, mood);
createChatRecord(room, userId, targetUserId, targetProfile);
createChatRecord(room, targetUserId, userId, profile);
IeRoom room = findReusableRoom(userId, targetUserId);
if (room == null) {
room = createRoom(match, userId, targetUserId, mode, mood);
}
redisService.markRecentlyMatched(userId, targetUserId);
redisService.markRecentlyMatched(targetUserId, userId);
@ -244,6 +256,73 @@ public class IeMatchServiceImpl implements IeMatchService {
}
}
@Override
public IPage<IeMatchVO> pageMatches(Long userId, Integer pageNumber, Integer pageSize) {
Page<IeMatchAttempt> page = new Page<>(safePageNumber(pageNumber), safePageSize(pageSize));
IPage<IeMatchAttempt> result = matchAttemptMapper.selectPage(page, new LambdaQueryWrapper<IeMatchAttempt>()
.eq(IeMatchAttempt::getUserId, userId)
.orderByDesc(IeMatchAttempt::getCreateTime));
Page<IeMatchVO> voPage = new Page<>(result.getCurrent(), result.getSize(), result.getTotal());
List<IeMatchVO> records = new ArrayList<>();
for (IeMatchAttempt match : result.getRecords()) {
IeMatchVO vo = toMatchVO(match);
fillMatchRoom(vo, userId);
fillLatestTargetProfile(vo);
records.add(vo);
}
voPage.setRecords(records);
return voPage;
}
@Override
@Transactional
public void rewardMatchQuota(Long userId, Integer amount) {
if (userId == null) {
return;
}
int reward = amount == null ? 1 : Math.max(1, amount);
IeUserProfile profile = ensureProfile(userId);
int dailyQuota = profile.getDailyQuota() == null ? IeConstants.DEFAULT_DAILY_QUOTA : profile.getDailyQuota();
profile.setDailyQuota(dailyQuota + reward);
profile.setUpdateTime(new Date());
userProfileMapper.updateById(profile);
}
private int safePageNumber(Integer pageNumber) {
return pageNumber == null || pageNumber < 1 ? 1 : pageNumber;
}
private int safePageSize(Integer pageSize) {
return pageSize == null ? 10 : Math.max(1, Math.min(pageSize, 50));
}
private void fillMatchRoom(IeMatchVO vo, Long userId) {
if (vo == null || vo.getTargetUserId() == null) {
return;
}
IeRoom room = findReusableRoom(userId, vo.getTargetUserId());
if (room == null) {
return;
}
vo.setRoomId(room.getId());
vo.setRoomNo(room.getRoomNo());
}
private void fillLatestTargetProfile(IeMatchVO vo) {
if (vo == null || vo.getTargetUserId() == null) {
return;
}
IeUserProfile profile = findProfileByUserId(vo.getTargetUserId());
if (profile == null) {
return;
}
vo.setAnonymousName(defaultText(profile.getAnonymousName(), vo.getAnonymousName()));
vo.setAvatarText(defaultText(profile.getAvatarText(), vo.getAvatarText()));
vo.setAvatarUrl(profile.getAvatarUrl());
vo.setMode(defaultText(profile.getCurrentMode(), vo.getMode()));
vo.setQuoteText(defaultText(profile.getIntro(), vo.getQuoteText()));
}
private IeRoom createRoom(IeMatchAttempt match, Long userId, Long targetUserId, String mode, String mood) {
Date now = new Date();
IeRoom room = new IeRoom();
@ -263,6 +342,23 @@ public class IeMatchServiceImpl implements IeMatchService {
return room;
}
private IeRoom findReusableRoom(Long userId, Long targetUserId) {
IeRoom room = roomMapper.selectOne(new LambdaQueryWrapper<IeRoom>()
.eq(IeRoom::getStatus, IeConstants.ROOM_STATUS_ACTIVE)
.and(wrapper -> wrapper
.eq(IeRoom::getUserAId, userId)
.eq(IeRoom::getUserBId, targetUserId)
.or()
.eq(IeRoom::getUserAId, targetUserId)
.eq(IeRoom::getUserBId, userId))
.orderByDesc(IeRoom::getUpdateTime)
.last("limit 1"));
if (room != null) {
redisService.cacheRoom(room.getId(), JSONUtil.toJsonStr(room), 20 * 60L);
}
return room;
}
private Date longTermExpireTime(Date startTime) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(startTime == null ? new Date() : startTime);
@ -270,29 +366,6 @@ public class IeMatchServiceImpl implements IeMatchService {
return calendar.getTime();
}
private void createChatRecord(IeRoom room, Long userId, Long targetUserId, IeUserProfile targetProfile) {
IeRecord exists = recordMapper.selectOne(new LambdaQueryWrapper<IeRecord>()
.eq(IeRecord::getRoomId, room.getId())
.eq(IeRecord::getUserId, userId)
.last("limit 1"));
if (exists != null) {
return;
}
IeRecord record = new IeRecord();
record.setRoomId(room.getId());
record.setUserId(userId);
record.setTargetUserId(targetUserId);
record.setMode(room.getMode());
record.setMood(room.getMood());
record.setAnonymousName(defaultText(targetProfile == null ? null : targetProfile.getAnonymousName(), "半匿名漂流者"));
record.setDurationSeconds(0);
record.setSummary("你们已经匹配成功,可以从这里回到聊天继续对话。");
record.setTags(room.getMode() + "," + room.getMood() + ",继续聊天");
record.setRemeetAvailable(1);
record.setCreateTime(new Date());
recordMapper.insert(record);
}
private IeMatchVO fail(Long userId, IeMatchStartDTO dto, String reason) {
IeMatchAttempt match = new IeMatchAttempt();
match.setMatchNo(IdUtil.fastSimpleUUID());
@ -322,6 +395,7 @@ public class IeMatchServiceImpl implements IeMatchService {
vo.setQuoteText(match.getQuoteText());
vo.setStatus(match.getStatus());
vo.setFailReason(match.getFailReason());
vo.setCreateTime(match.getCreateTime());
return vo;
}
@ -384,6 +458,17 @@ public class IeMatchServiceImpl implements IeMatchService {
return new ArrayList<>(set);
}
private boolean hasBlockRelation(Long userId, Long candidateUserId) {
Long count = blockMapper.selectCount(new LambdaQueryWrapper<IeBlock>()
.and(wrapper -> wrapper
.eq(IeBlock::getUserId, userId)
.eq(IeBlock::getBlockedUserId, candidateUserId))
.or(wrapper -> wrapper
.eq(IeBlock::getUserId, candidateUserId)
.eq(IeBlock::getBlockedUserId, userId)));
return count != null && count > 0;
}
private List<Long> statusCandidates(String mode, int limit) {
List<IeUserStatus> statuses = userStatusMapper.selectList(new LambdaQueryWrapper<IeUserStatus>()
.eq(IeUserStatus::getOnlineStatus, 1)
@ -446,6 +531,7 @@ public class IeMatchServiceImpl implements IeMatchService {
vo.setGender(profile.getGender());
vo.setIntro(profile.getIntro());
vo.setInterestTags(jsonToTags(profile.getInterestTags()));
vo.setPersonaImages(jsonToTags(profile.getPersonaImages()));
vo.setCurrentMode(profile.getCurrentMode());
vo.setRecentPreference(profile.getRecentPreference());
vo.setTargetModePreference(profile.getTargetModePreference());
@ -454,6 +540,7 @@ public class IeMatchServiceImpl implements IeMatchService {
vo.setDailyQuota(profile.getDailyQuota());
vo.setUsedQuota(profile.getUsedQuota());
vo.setProfileCompleted(profile.getProfileCompleted() == null ? 0 : profile.getProfileCompleted());
vo.setExists(true);
return vo;
}
@ -552,6 +639,22 @@ public class IeMatchServiceImpl implements IeMatchService {
return JSONUtil.toJsonStr(tags);
}
private String imagesToJson(List<String> images) {
if (images == null || images.isEmpty()) {
return "[]";
}
List<String> safeImages = new ArrayList<>();
for (String image : images) {
if (image != null && !image.trim().isEmpty()) {
safeImages.add(image.trim());
}
if (safeImages.size() >= 5) {
break;
}
}
return JSONUtil.toJsonStr(safeImages);
}
private List<String> jsonToTags(String tags) {
if (tags == null || tags.trim().isEmpty()) {
return new ArrayList<>();

7
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/impl/IeRedisServiceImpl.java

@ -189,6 +189,13 @@ public class IeRedisServiceImpl implements IeRedisService {
return messages == null ? new ArrayList<>() : messages;
}
@Override
public void clearOfflineMessages(Long userId) {
if (userId != null) {
stringRedisTemplate.delete(IeRedisKey.OFFLINE_MESSAGE + userId);
}
}
@Override
public void cacheWebSocketConnection(Long userId, String sessionId) {
String key = IeRedisKey.WS_CONNECTION + userId;

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

@ -2,6 +2,8 @@ package cc.hiver.mall.ie.vo;
import lombok.Data;
import java.util.Date;
@Data
public class IeMatchVO {
private Long matchId;
@ -18,4 +20,5 @@ public class IeMatchVO {
private String roomNo;
private Integer status;
private String failReason;
private Date createTime;
}

2
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/vo/IeUserProfileVO.java

@ -13,6 +13,7 @@ public class IeUserProfileVO {
private String gender;
private String intro;
private List<String> interestTags;
private List<String> personaImages;
private String currentMode;
private String recentPreference;
private String targetModePreference;
@ -21,4 +22,5 @@ public class IeUserProfileVO {
private Integer dailyQuota;
private Integer usedQuota;
private Integer profileCompleted;
private Boolean exists;
}

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

@ -7,9 +7,10 @@ import cc.hiver.core.service.WorkerService;
import cc.hiver.mall.dao.mapper.MallDeliveryOrderMapper;
import cc.hiver.mall.dao.mapper.MallOrderMapper;
import cc.hiver.mall.entity.MallDeliveryOrder;
import cc.hiver.mall.ie.service.IeMatchService;
import cc.hiver.mall.pojo.dto.CreateOrderDTO;
import cc.hiver.mall.serviceimpl.mybatis.MallOrderServiceImpl;
import cc.hiver.mall.utils.WechatSendMessageUtil;
import cc.hiver.mall.utils.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
@ -19,10 +20,12 @@ import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
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;
/**
@ -50,6 +53,27 @@ public class OrderAsyncConsumer {
@Autowired
private WechatSendMessageUtil wechatSendMessageUtil;
@Autowired
private UserPendingOrderCacheUtil userPendingOrderCacheUtil;
@Autowired
private WorkerOrderCacheUtil workerOrderCacheUtil;
@Autowired
private WorkerRedisCacheUtil workerRedisCacheUtil;
@Autowired
private FreeOrderCacheUtil freeOrderCacheUtil;
@Autowired
private RedisLuaScripts redisLuaScripts;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private IeMatchService ieMatchService;
// 微信模板配置
private static final String TEMPLATE_ID = "K15zpZSHBNivouTfpW5FK1XFz8GbYAK8b9dXXOP_Ka0";
@ -140,6 +164,10 @@ public class OrderAsyncConsumer {
log.info("【异步队列处理】刷新订单附属缓存: {}", message);
try {
JSONObject body = JSON.parseObject(message);
if ("WORKER_COMPLETE_AFTER_COMMIT".equals(body.getString("taskType"))) {
handleWorkerCompleteAfterCommit(body);
return;
}
String shopId = body.getString("shopId");
String regionId = body.getString("regionId");
Integer shopSaleCount = body.getInteger("shopSaleCount");
@ -156,4 +184,64 @@ public class OrderAsyncConsumer {
throw new org.springframework.amqp.AmqpRejectAndDontRequeueException(e);
}
}
private void handleWorkerCompleteAfterCommit(JSONObject body) {
JSONArray orders = body.getJSONArray("orders");
if (orders != null) {
for (int i = 0; i < orders.size(); i++) {
JSONObject order = orders.getJSONObject(i);
String orderId = order.getString("orderId");
String userId = order.getString("userId");
if (StringUtils.isNotBlank(userId) && StringUtils.isNotBlank(orderId)) {
userPendingOrderCacheUtil.remove(userId, orderId);
rewardIeMatchQuota(userId);
}
}
}
String workerId = body.getString("workerId");
String deliveryId = body.getString("deliveryId");
if (StringUtils.isNotBlank(workerId) && StringUtils.isNotBlank(deliveryId)) {
workerOrderCacheUtil.remove(workerId, deliveryId);
}
String regionId = body.getString("regionId");
if (StringUtils.isNotBlank(regionId) && StringUtils.isNotBlank(workerId)) {
Worker worker = workerService.findByWorkerId(workerId);
if (worker != null) {
Object workerRedisVo = workerRedisCacheUtil.get(regionId, workerId);
if (workerRedisVo instanceof cc.hiver.mall.pojo.vo.WorkerRedisVo) {
cc.hiver.mall.pojo.vo.WorkerRedisVo vo = (cc.hiver.mall.pojo.vo.WorkerRedisVo) workerRedisVo;
vo.setWorker(worker);
workerRedisCacheUtil.update(regionId, vo);
}
}
}
Integer freeOrderCount = body.getInteger("freeOrderCount");
String freeOrderNumber = body.getString("freeOrderNumber");
if (freeOrderCount != null && freeOrderCount > 0 && StringUtils.isNotBlank(regionId) && StringUtils.isNotBlank(freeOrderNumber)
&& freeOrderCacheUtil.exists(regionId)) {
for (int i = 0; i < freeOrderCount; i++) {
stringRedisTemplate.execute(
redisLuaScripts.getIncrementScript(),
Arrays.asList("free_order:count:" + regionId, "free_order:flag:" + regionId),
freeOrderNumber
);
}
}
}
private void rewardIeMatchQuota(String userId) {
if (StringUtils.isBlank(userId)) {
return;
}
try {
ieMatchService.rewardMatchQuota(Long.valueOf(userId), 1);
} catch (NumberFormatException e) {
log.warn("配送完成异步增加i/e匹配机会失败,用户ID不是数字 userId={}", userId);
} catch (Exception e) {
log.warn("配送完成异步增加i/e匹配机会失败,不影响异步收尾 userId={}, reason={}", userId, e.getMessage());
}
}
}

9
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderAsyncProducer.java

@ -39,6 +39,15 @@ public class OrderAsyncProducer {
rabbitTemplate.convertAndSend(OrderQueueConfig.ORDER_DIRECT_EXCHANGE, OrderQueueConfig.ASYNC_CACHE_ROUTING, JSON.toJSONString(params));
}
public void sendWorkerCompleteAfterCommit(Map<String, Object> params) {
if (params == null) {
params = new HashMap<>();
}
params.put("taskType", "WORKER_COMPLETE_AFTER_COMMIT");
rabbitTemplate.convertAndSend(OrderQueueConfig.ORDER_DIRECT_EXCHANGE, OrderQueueConfig.ASYNC_CACHE_ROUTING, JSON.toJSONString(params));
log.info("【订单MQ】已发出配送完成异步收尾任务, deliveryId={}", params.get("deliveryId"));
}
/**
* 发送退款/售后申请超时延时消息默认1小时后自动同意
* @param refundId 退款记录IDMallRefundRecord.id

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

@ -12,7 +12,6 @@ import cc.hiver.mall.mq.OrderAsyncProducer;
import cc.hiver.mall.pojo.query.MallDeliveryOrderPageQuery;
import cc.hiver.mall.pojo.query.MallRefundRecordPageQuery;
import cc.hiver.mall.pojo.vo.MallOrderVO;
import cc.hiver.mall.pojo.vo.WorkerRedisVo;
import cc.hiver.mall.service.CommentService;
import cc.hiver.mall.service.ShopService;
import cc.hiver.mall.service.mybatis.*;
@ -29,6 +28,8 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import java.math.BigDecimal;
import java.math.RoundingMode;
@ -36,6 +37,7 @@ import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
/**
* 配送订单 Service 实现
@ -514,54 +516,48 @@ public class MallDeliveryOrderServiceImpl extends ServiceImpl<MallDeliveryOrderM
this.update(uw);
Boolean isFantuan = true;
int freeOrderCount = 0;
List<MallOrder> completedOrders = new ArrayList<>();
//如果是有groupId证明是面对面配送,需要更新所有子订单状态为完成
if(StringUtils.isNotBlank(delivery.getGroupId())){
MallOrderGroup group = mallOrderGroupMapper.selectById(delivery.getGroupId());
if(group.getIsFace() == 1){
List<String> orderIdList = Arrays.asList(group.getGroupOrderIds().split(","));
//更新缓存
for (String orderId : orderIdList) {
MallOrder orderInner = mallOrderService.getById(orderId);
if(orderInner.getStatus() == 4){
userPendingOrderCacheUtil.remove(orderInner.getUserId(),orderInner.getId());
}else{
orderIdList.remove(orderId);
}
}
LambdaUpdateWrapper<MallOrder> oqw = new LambdaUpdateWrapper<>();
oqw.in(MallOrder::getId, orderIdList).set(MallOrder::getStatus,ORDER_STATUS_DONE);
mallOrderService.update(oqw);
//更新优惠券已使用
LambdaUpdateWrapper<MallUserCoupon> oqwCoupon = new LambdaUpdateWrapper<>();
oqwCoupon.in(MallUserCoupon::getOrderId, orderIdList).set(MallUserCoupon::getStatus,2);
mallUserCouponService.update(oqwCoupon);
// 面对面拼团:每个子订单单独计数(Key 按 regionId 隔离),避免多单只计 1 次
if(freeOrderCacheUtil.exists(delivery.getRegionId())){
String regionId = delivery.getRegionId();
String orderNumber = String.valueOf(freeOrderCacheUtil.get(regionId).getOrderNumber());
for (int i = 0; i < orderIdList.size(); i++) {
stringRedisTemplate.execute(
redisLuaScripts.getIncrementScript(),
Arrays.asList("free_order:count:" + regionId, "free_order:flag:" + regionId),
orderNumber
);
}
List<String> orderIdList = Arrays.stream(group.getGroupOrderIds().split(","))
.filter(StringUtils::isNotBlank)
.collect(Collectors.toList());
List<MallOrder> orderInnerList = mallOrderService.listByIds(orderIdList);
completedOrders = orderInnerList.stream()
.filter(order -> order != null && order.getStatus() == ORDER_STATUS_DELIVERING)
.collect(Collectors.toList());
List<String> completedOrderIds = completedOrders.stream()
.map(MallOrder::getId)
.collect(Collectors.toList());
if (!completedOrderIds.isEmpty()) {
LambdaUpdateWrapper<MallOrder> oqw = new LambdaUpdateWrapper<>();
oqw.in(MallOrder::getId, completedOrderIds).set(MallOrder::getStatus,ORDER_STATUS_DONE);
mallOrderService.update(oqw);
//更新优惠券已使用
LambdaUpdateWrapper<MallUserCoupon> oqwCoupon = new LambdaUpdateWrapper<>();
oqwCoupon.in(MallUserCoupon::getOrderId, completedOrderIds).set(MallUserCoupon::getStatus,2);
mallUserCouponService.update(oqwCoupon);
freeOrderCount = completedOrderIds.size();
}
isFantuan = false;
}else{
if (StringUtils.isNotBlank(delivery.getOrderId())) {
LambdaUpdateWrapper<MallOrder> oUw = new LambdaUpdateWrapper<>();
oUw.eq(MallOrder::getId, delivery.getOrderId())
.set(MallOrder::getStatus, ORDER_STATUS_DONE);
mallOrderService.update(oUw);
//更新缓存
MallOrder orderInner = mallOrderService.getById(delivery.getOrderId());
userPendingOrderCacheUtil.remove(orderInner.getUserId(),orderInner.getId());
if (orderInner != null) {
completedOrders.add(orderInner);
if(orderInner.getOtherOrder() != null){
isFantuan = false;
if(orderInner.getOtherOrder() != null){
isFantuan = false;
}
}
//优惠券变成已使用
mallUserCouponService.useCoupon(delivery.getOrderId());
@ -575,17 +571,21 @@ public class MallDeliveryOrderServiceImpl extends ServiceImpl<MallDeliveryOrderM
oUw.eq(MallOrder::getId, delivery.getOrderId())
.set(MallOrder::getStatus, ORDER_STATUS_DONE);
mallOrderService.update(oUw);
//更新缓存
MallOrder orderInner = mallOrderService.getById(delivery.getOrderId());
userPendingOrderCacheUtil.remove(orderInner.getUserId(),orderInner.getId());
if (orderInner != null) {
completedOrders.add(orderInner);
if(orderInner.getOtherOrder() != null){
isFantuan = false;
if(orderInner.getOtherOrder() != null){
isFantuan = false;
}
}
//优惠券变成已使用
mallUserCouponService.useCoupon(delivery.getOrderId());
}
}
if (isFantuan && !completedOrders.isEmpty()) {
freeOrderCount = 1;
}
//增加配送员余额
Worker worker = workerServiceImpl.findByWorkerId(delivery.getWorkerId());
//更新余额
@ -624,23 +624,42 @@ public class MallDeliveryOrderServiceImpl extends ServiceImpl<MallDeliveryOrderM
dealingsRecord.setDealingsType(DealingsRecordConstant.DEALINGS_TYPE[0]);
dealingsRecord.setCreateBy(worker.getWorkerId());
dealingsRecordService.save(dealingsRecord);
//更新缓存
workerOrderCacheUtil.remove(workerId,deliveryId);
//更新缓存
WorkerRedisVo workerRedisVo = workerRedisCacheUtil.get(worker.getRegion(), worker.getWorkerId());
if(workerRedisVo != null){
workerRedisVo.setWorker(worker);
workerRedisCacheUtil.update(worker.getRegion(), workerRedisVo);
sendWorkerCompleteAsyncAfterCommit(delivery, worker, completedOrders, freeOrderCount);
}
private void sendWorkerCompleteAsyncAfterCommit(MallDeliveryOrder delivery, Worker worker, List<MallOrder> completedOrders, int freeOrderCount) {
if (delivery == null || worker == null) {
return;
}
//是否开启了免单活动(Key 按 regionId 隔离,避免多区域计数混乱)
if(isFantuan){
if(freeOrderCacheUtil.exists(delivery.getRegionId())){
stringRedisTemplate.execute(
redisLuaScripts.getIncrementScript(),
Arrays.asList("free_order:count:" + delivery.getRegionId(), "free_order:flag:" + delivery.getRegionId()),
String.valueOf(freeOrderCacheUtil.get(delivery.getRegionId()).getOrderNumber())
);
}
Map<String, Object> params = new HashMap<>();
params.put("taskType", "WORKER_COMPLETE_AFTER_COMMIT");
params.put("deliveryId", delivery.getId());
params.put("workerId", worker.getWorkerId());
params.put("regionId", delivery.getRegionId());
List<Map<String, String>> orders = completedOrders == null ? new ArrayList<>() : completedOrders.stream()
.filter(order -> order != null && StringUtils.isNotBlank(order.getId()) && StringUtils.isNotBlank(order.getUserId()))
.map(order -> {
Map<String, String> item = new HashMap<>();
item.put("orderId", order.getId());
item.put("userId", order.getUserId());
return item;
})
.collect(Collectors.toList());
params.put("orders", orders);
if (freeOrderCount > 0 && StringUtils.isNotBlank(delivery.getRegionId()) && freeOrderCacheUtil.exists(delivery.getRegionId())) {
params.put("freeOrderCount", freeOrderCount);
params.put("freeOrderNumber", String.valueOf(freeOrderCacheUtil.get(delivery.getRegionId()).getOrderNumber()));
}
Runnable sendTask = () -> orderAsyncProducer.sendWorkerCompleteAfterCommit(params);
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
sendTask.run();
}
});
} else {
sendTask.run();
}
}

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

@ -10,6 +10,7 @@ import cc.hiver.core.entity.User;
import cc.hiver.core.serviceimpl.JPushServiceImpl;
import cc.hiver.mall.dao.mapper.*;
import cc.hiver.mall.entity.*;
import cc.hiver.mall.ie.service.IeMatchService;
import cc.hiver.mall.mq.OrderAsyncProducer;
import cc.hiver.mall.pojo.dto.CreateOrderDTO;
import cc.hiver.mall.pojo.dto.ShopCacheDTO;
@ -180,6 +181,9 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
@Autowired
private OrderAsyncProducer orderAsyncProducer;
@Autowired
private IeMatchService ieMatchService;
// ================================================================
// 核心下单逻辑
// ================================================================
@ -1177,6 +1181,7 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
//优惠券变成已使用
mallUserCouponService.useCoupon(orderId);
updateOrderStatus(orderId, STATUS_DONE);
rewardIeMatchQuota(order.getUserId());
//更新缓存
userPendingOrderCacheUtil.remove(order.getUserId(),order.getId());
//免单计数(Key 按 regionId 隔离,避免多区域计数混乱)
@ -1189,6 +1194,19 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
}
}
private void rewardIeMatchQuota(String userId) {
if (StringUtils.isBlank(userId)) {
return;
}
try {
ieMatchService.rewardMatchQuota(Long.valueOf(userId), 1);
} catch (NumberFormatException e) {
log.warn("订单完成增加i/e匹配机会失败,用户ID不是数字 userId={}", userId);
} catch (Exception e) {
log.warn("订单完成增加i/e匹配机会失败,不影响订单完成 userId={}, reason={}", userId, e.getMessage());
}
}
@Override
public MallOrder selectMallOrderByGroupId(String groupId) {
return this.baseMapper.selectMallOrderByGroupId(groupId);

2
hiver-modules/hiver-mall/src/main/resources/mapper/MallCouponMapper.xml

@ -73,7 +73,7 @@
AND issuer_type = #{q.issuerType}
</if>
<if test="q.applyScene != null">
AND apply_scene = #{applyScene}
AND apply_scene = #{q.applyScene}
</if>
<if test="q.regionId != null and q.regionId != ''">
AND region_id = #{q.regionId}

Loading…
Cancel
Save