Compare commits

...

5 Commits

Author SHA1 Message Date
irving
110e0d8e8c add more logging for 接单
Some checks failed
Build and Push Backend / docker (push) Failing after 6s
2025-10-27 21:56:42 -04:00
irving
e677ca3d33 fix: prevent duplicated clerk get's the same order 2025-10-27 21:51:28 -04:00
irving
8691a56f8a adjust logging, avoid add too much raw sql logging 2025-10-27 21:51:28 -04:00
irving
15e15bc01a add logging for duplicated notifcation 2025-10-27 21:51:28 -04:00
huchuansai
80de65731f fix: 店员筛选for移动端 2025-10-27 18:12:54 +08:00
6 changed files with 122 additions and 36 deletions

View File

@@ -380,6 +380,12 @@ public class PlayClerkUserInfoServiceImpl extends ServiceImpl<PlayClerkUserInfoM
if (StrUtil.isNotBlank(vo.getLevelId())) {
lambdaQueryWrapper.eq(PlayClerkUserInfoEntity::getLevelId, vo.getLevelId());
}
if (StrUtil.isNotBlank(vo.getListingState())) {
lambdaQueryWrapper.eq(PlayClerkUserInfoEntity::getListingState, vo.getListingState());
}
if (StrUtil.isNotBlank(vo.getRandomOrderState())) {
lambdaQueryWrapper.eq(PlayClerkUserInfoEntity::getRandomOrderState, vo.getRandomOrderState());
}
if (StrUtil.isNotBlank(vo.getProvince())) {
lambdaQueryWrapper.eq(PlayClerkUserInfoEntity::getProvince, vo.getProvince());
}

View File

@@ -2,6 +2,7 @@ package com.starry.admin.modules.order.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -854,27 +855,94 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
**/
@Override
public void updateStateTo1(String operatorByType, String operatorBy, String acceptBy, String orderId) {
if (!OrderConstant.OperatorType.CLERK.getCode().equals(operatorByType)
&& !OrderConstant.OperatorType.ADMIN.getCode().equals(operatorByType)) {
boolean isClerkOperator = OrderConstant.OperatorType.CLERK.getCode().equals(operatorByType);
boolean isAdminOperator = OrderConstant.OperatorType.ADMIN.getCode().equals(operatorByType);
if (!isClerkOperator && !isAdminOperator) {
throw new CustomException("禁止操作");
}
PlayOrderInfoEntity orderInfo = this.selectOrderInfoById(orderId);
String previousStatus = orderInfo.getOrderStatus();
if (OrderStatus.COMPLETED.getCode().equals(previousStatus)
|| OrderStatus.CANCELLED.getCode().equals(previousStatus)) {
log.warn("Order already finished, cannot accept. orderId={}, orderNo={}, operatorByType={}, acceptBy={}",
orderId, orderInfo.getOrderNo(), operatorByType, acceptBy);
throw new CustomException("订单已结束,无法接单");
}
PlayClerkUserInfoEntity clerkUserInfoEntity = playClerkUserInfoService.selectById(acceptBy);
if (isClerkOperator && StringUtils.isNotBlank(orderInfo.getAcceptBy())
&& !orderInfo.getAcceptBy().equals(acceptBy)) {
log.warn("Order already accepted by another clerk. orderId={}, orderNo={}, currentAcceptBy={}, requestAcceptBy={}",
orderId, orderInfo.getOrderNo(), orderInfo.getAcceptBy(), acceptBy);
throw new CustomException("订单已被接单");
}
if (isClerkOperator && !OrderStatus.PENDING.getCode().equals(previousStatus)) {
log.warn("Order status is not pending when clerk tries to accept. orderId={}, orderNo={}, status={}, acceptBy={}",
orderId, orderInfo.getOrderNo(), previousStatus, acceptBy);
throw new CustomException("订单已被接单");
}
// 店员接单时,判断店员是否符合资格
if (OrderConstant.OperatorType.CLERK.getCode().equals(operatorByType)
&& OrderConstant.PlaceType.RANDOM.getCode().equals(orderInfo.getPlaceType())) {
// 判断店员等级是否符合规则
if (isClerkOperator && OrderConstant.PlaceType.RANDOM.getCode().equals(orderInfo.getPlaceType())) {
validateClerkQualificationForRandomOrder(orderInfo, clerkUserInfoEntity, acceptBy);
}
PlayOrderInfoEntity entity = new PlayOrderInfoEntity(orderId, OrderStatus.ACCEPTED.getCode());
LocalDateTime acceptTime = LocalDateTime.now();
entity.setAcceptBy(acceptBy);
entity.setAcceptTime(acceptTime);
ClerkEstimatedRevenueVo estimatedRevenueVo = this.getClerkEstimatedRevenue(
acceptBy,
orderInfo.getCouponIds(),
orderInfo.getPlaceType(),
orderInfo.getFirstOrder(),
orderInfo.getFinalAmount());
BigDecimal revenueAmount = estimatedRevenueVo.getRevenueAmount();
entity.setEstimatedRevenue(revenueAmount);
entity.setEstimatedRevenueRatio(estimatedRevenueVo.getRevenueRatio());
LambdaUpdateWrapper<PlayOrderInfoEntity> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(PlayOrderInfoEntity::getId, orderId);
if (isClerkOperator) {
updateWrapper.eq(PlayOrderInfoEntity::getOrderStatus, OrderStatus.PENDING.getCode());
if (StringUtils.isNotBlank(orderInfo.getAcceptBy())) {
updateWrapper.eq(PlayOrderInfoEntity::getAcceptBy, orderInfo.getAcceptBy());
} else {
updateWrapper.isNull(PlayOrderInfoEntity::getAcceptBy);
}
}
int updated = this.baseMapper.update(entity, updateWrapper);
if (updated == 0) {
if (isClerkOperator) {
log.warn("Clerk failed to accept order due to concurrent update. orderId={}, orderNo={}, acceptBy={}",
orderId, orderInfo.getOrderNo(), acceptBy);
throw new CustomException("订单已被接单");
}
log.warn("Failed to update order status to accepted for admin reassignment, orderId={}, orderNo={}",
orderId, orderInfo.getOrderNo());
return;
}
// Set acceptBy on orderInfo for notification
orderInfo.setAcceptBy(acceptBy);
orderInfo.setAcceptTime(acceptTime);
orderInfo.setOrderStatus(OrderStatus.ACCEPTED.getCode());
orderInfo.setEstimatedRevenue(revenueAmount);
orderInfo.setEstimatedRevenueRatio(estimatedRevenueVo.getRevenueRatio());
log.info("Order accepted successfully. orderId={}, orderNo={}, acceptBy={}, operatorByType={}",
orderId, orderInfo.getOrderNo(), acceptBy, operatorByType);
wxCustomMpService.sendOrderMessageAsync(orderInfo);
}
private void validateClerkQualificationForRandomOrder(PlayOrderInfoEntity orderInfo,
PlayClerkUserInfoEntity clerkUserInfoEntity,
String acceptBy) {
if (orderInfo.getLevelId().equals(clerkUserInfoEntity.getLevelId())) {
PlayClerkLevelInfoEntity levelInfo = playClerkLevelInfoService
.selectPlayClerkLevelInfoById(orderInfo.getLevelId());
// throw new CustomException("等级为" + levelInfo.getName() + "的店员才可接单");
}
// 判断店员性别是否符合规则
if (!orderInfo.getSex().equals(clerkUserInfoEntity.getSex())) {
OrderConstant.Gender requiredGender = OrderConstant.Gender.fromCode(orderInfo.getSex());
throw new CustomException("性别为" + requiredGender.getDescription() + "的店员才可接单");
}
// 如果排除已下单的店员
LambdaQueryWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getAcceptBy, acceptBy);
if (OrderConstant.EXCLUDE_HISTORY_YES.equals(orderInfo.getExcludeHistory())
@@ -882,19 +950,6 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
// throw new CustomException("只有未接单的店员才可接单");
}
}
PlayOrderInfoEntity entity = new PlayOrderInfoEntity(orderId, OrderStatus.ACCEPTED.getCode());
entity.setAcceptBy(acceptBy);
entity.setAcceptTime(LocalDateTime.now());
ClerkEstimatedRevenueVo estimatedRevenueVo = this.getClerkEstimatedRevenue(acceptBy, orderInfo.getCouponIds(),
orderInfo.getPlaceType(), orderInfo.getFirstOrder(), orderInfo.getFinalAmount());
entity.setEstimatedRevenue(estimatedRevenueVo.getRevenueAmount());
entity.setEstimatedRevenueRatio(estimatedRevenueVo.getRevenueRatio());
this.baseMapper.updateById(entity);
// Set acceptBy on orderInfo for notification
orderInfo.setAcceptBy(acceptBy);
wxCustomMpService.sendOrderMessageAsync(orderInfo);
}
/**
* 获取通用的订单查询对象 订单作为主表 连接顾客用户表、店员用户表、商品表

View File

@@ -16,10 +16,7 @@ import java.util.List;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
/**
* 微信查询组长工资
@@ -56,4 +53,14 @@ public class WxPersonnelGroupInfoController {
return R.ok(data);
}
@ApiOperation(value = "查询分组信息", notes = "获取所有店员分组")
@ApiResponses({
@ApiResponse(code = 200, message = "操作成功", response = PlayPersonnelGroupInfoEntity.class, responseContainer = "List")})
@ClerkUserLogin
@GetMapping("/group/listAll")
public R findAllGroup() {
List<PlayPersonnelGroupInfoEntity> list = playClerkGroupInfoService.selectAll();
return R.ok(list);
}
}

View File

@@ -193,7 +193,7 @@ public class WxCustomMpService {
* @author admin
* @since 2024/7/31 10:51
**/
public void sendOrderMessage(PlayOrderInfoEntity orderInfo) {
private void sendOrderMessage(PlayOrderInfoEntity orderInfo) {
SysTenantEntity tenant = tenantService.selectSysTenantByTenantId(orderInfo.getTenantId());
PlayClerkUserInfoEntity clerkUserInfo = clerkUserInfoService.selectById(orderInfo.getAcceptBy());
PlayCustomUserInfoEntity customUserInfo = customUserInfoService.selectById(orderInfo.getPurchaserBy());
@@ -206,6 +206,10 @@ public class WxCustomMpService {
data.add(new WxMpTemplateData("time2", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss")));
templateMessage.setData(data);
try {
if (clerkUserInfo != null) {
log.info("发送接单成功通知orderId={}, orderNo={}, recipientType=clerk, openId={}, nickname={}",
orderInfo.getId(), orderInfo.getOrderNo(), clerkUserInfo.getOpenid(), clerkUserInfo.getNickname());
}
proxyWxMpService().getTemplateMsgService().sendTemplateMsg(templateMessage);
} catch (WxErrorException e) {
log.error(e.getMessage(), e);
@@ -215,6 +219,10 @@ public class WxCustomMpService {
templateMessage
.setUrl("https://" + tenant.getTenantKey() + ".julyharbor.com/user/#/orderDetail/" + orderInfo.getId());
try {
if (customUserInfo != null) {
log.info("发送接单成功通知orderId={}, orderNo={}, recipientType=customer, openId={}, nickname={}",
orderInfo.getId(), orderInfo.getOrderNo(), customUserInfo.getOpenid(), customUserInfo.getNickname());
}
proxyWxMpService().getTemplateMsgService().sendTemplateMsg(templateMessage);
} catch (WxErrorException e) {
log.error(e.getMessage(), e);
@@ -387,6 +395,10 @@ public class WxCustomMpService {
data.add(new WxMpTemplateData("time18", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss")));
templateMessage.setData(data);
try {
if (customUserInfo != null) {
log.info("发送取消订单通知orderId={}, orderNo={}, recipientType=customer, openId={}, nickname={}",
orderInfo.getId(), orderInfo.getOrderNo(), customUserInfo.getOpenid(), customUserInfo.getNickname());
}
proxyWxMpService().getTemplateMsgService().sendTemplateMsg(templateMessage);
} catch (WxErrorException e) {
log.error(e.getMessage(), e);
@@ -399,6 +411,10 @@ public class WxCustomMpService {
templateMessage.setUrl(
"https://" + tenant.getTenantKey() + ".julyharbor.com/clerk/#/orderDetail/" + orderInfo.getId());
try {
if (clerkUserInfo != null) {
log.info("发送取消订单通知orderId={}, orderNo={}, recipientType=clerk, openId={}, nickname={}",
orderInfo.getId(), orderInfo.getOrderNo(), clerkUserInfo.getOpenid(), clerkUserInfo.getNickname());
}
proxyWxMpService().getTemplateMsgService().sendTemplateMsg(templateMessage);
} catch (WxErrorException e) {
log.error(e.getMessage(), e);

View File

@@ -22,7 +22,7 @@ mybatis-plus:
# xml文件路径classpath* 代表所有模块的resources目录 classpath 不加星号代表当前模块下的resources目录
mapper-locations: classpath*:mapper/**/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
wx:
mp:

View File

@@ -78,6 +78,8 @@
<appender-ref ref="ERROR_FILE"/>
</root>
<logger name="org.mybatis" level="WARN"/>
<logger name="com.baomidou.mybatisplus" level="WARN"/>
<logger name="com.starry" level="info"/>
</configuration>