台桌状态

This commit is contained in:
2025-05-09 11:07:58 +08:00
parent 4321847add
commit fc255e5969
11 changed files with 141 additions and 18 deletions

View File

@@ -1,8 +1,10 @@
package com.czg.config; package com.czg.config;
import com.czg.account.service.ShopTableService;
import com.czg.order.service.OrderInfoService; import com.czg.order.service.OrderInfoService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -25,6 +27,8 @@ public class RedisKeyExpirationListener implements MessageListener {
private String database; private String database;
@Resource @Resource
private OrderInfoService tbOrderInfoService; private OrderInfoService tbOrderInfoService;
@DubboReference
private ShopTableService tableService;
//redis key失效监听 //redis key失效监听
@@ -46,6 +50,10 @@ public class RedisKeyExpirationListener implements MessageListener {
log.info("监听到订单过期,订单Id: {}", expiredKey); log.info("监听到订单过期,订单Id: {}", expiredKey);
String orderId = expiredKey.substring(RedisCst.classKeyExpired.EXPIRED_ORDER.length()); String orderId = expiredKey.substring(RedisCst.classKeyExpired.EXPIRED_ORDER.length());
tbOrderInfoService.expired(Long.parseLong(orderId)); tbOrderInfoService.expired(Long.parseLong(orderId));
}else if (expiredKey.startsWith(RedisCst.classKeyExpired.EXPIRED_TABLE)) {
log.info("监听到台桌清台过期,台桌Id: {}", expiredKey);
String tableId = expiredKey.substring(RedisCst.classKeyExpired.EXPIRED_TABLE.length());
tableService.expiredTable(Long.parseLong(tableId));
} }
} }
} }

View File

@@ -19,6 +19,8 @@ public interface RedisCst {
class classKeyExpired { class classKeyExpired {
//订单key过期 //订单key过期
public static final String EXPIRED_ORDER = "expired:order:"; public static final String EXPIRED_ORDER = "expired:order:";
//台桌清台key过期
public static final String EXPIRED_TABLE = "expired:table:";
} }

View File

@@ -57,19 +57,30 @@ public class ShopTableDTO implements Serializable {
private BigDecimal predateAmount; private BigDecimal predateAmount;
/** /**
* idle-空闲 using-使用中 subscribe预定closed--关台, opening 开台中cleaning 台桌清理中 * 未绑定unbound
* 空闲idle
* 点餐中ordering
* 未结账unsettled
* 支付中paying
* 已结账 (待清台)settled
* 关台closed
*/ */
private String status; private String status;
/** /**
* 二维码 * 二维码
*/ */
private String qrcode; private String tableCode;
/** /**
* 自动清台 0手动 1自动 * 自动清台 0手动 1自动
*/ */
private Integer autoClear; private Integer autoClear;
/**
* 自动清台时间 单位分钟 默认10
*/
private Integer autoClearTime;
} }

View File

@@ -82,4 +82,13 @@ public class ShopConfig implements Serializable {
* 分店数据同步方式 auto-自动同步 manual-手动同步 * 分店数据同步方式 auto-自动同步 manual-手动同步
*/ */
private String branchDataSyncMethod; private String branchDataSyncMethod;
/**
* 台桌清理类型 'auto 自动 hand 手动' 默认auto
*/
private String tableClearType;
/**
* '自动清台 支付几分钟后 默认10分钟后'
*/
private Integer tableClearTime;
} }

View File

@@ -301,4 +301,15 @@ public class ShopInfo implements Serializable {
@Column(ignore = true) @Column(ignore = true)
private String headShopName; private String headShopName;
/**
* 台桌清理类型 'auto 自动 hand 手动' 默认auto
*/
@Column(ignore = true)
private String tableClearType;
/**
* '自动清台 支付几分钟后 默认10分钟后'
*/
@Column(ignore = true)
private Integer tableClearTime;
} }

View File

@@ -5,6 +5,7 @@ import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id; import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType; import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table; import com.mybatisflex.annotation.Table;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -67,7 +68,14 @@ public class ShopTable implements Serializable {
private BigDecimal predateAmount; private BigDecimal predateAmount;
/** /**
* idle-空闲 using-使用中 subscribe预定closed--关台, opening 开台中cleaning 台桌清理中 * {@link com.czg.enums.ShopTableStatusEnum}
* 未绑定unbound
* 空闲idle
* 点餐中ordering
* 未结账unsettled
* 支付中paying
* 已结账 (待清台)settled
* 关台closed
*/ */
private String status; private String status;
@@ -83,10 +91,9 @@ public class ShopTable implements Serializable {
private String tableCode; private String tableCode;
/** /**
* 自动清台 0手动 1自动 * 清台时间(自动清台时使用)
*/ */
private Integer autoClear; private LocalDateTime clearTime;
/** /**
* 使用时间 * 使用时间
*/ */
@@ -118,7 +125,10 @@ public class ShopTable implements Serializable {
private Integer useNum; private Integer useNum;
public boolean canUseByStatus() { public boolean canUseByStatus() {
return !ShopTableStatusEnum.CLOSED.equalsVal(status) && !ShopTableStatusEnum.CLEANING.equalsVal(status) && !ShopTableStatusEnum.SUBSCRIBE.equalsVal(status); return !ShopTableStatusEnum.UNBOUND.equalsVal(status)
&& !ShopTableStatusEnum.SETTLED.equalsVal(status)
&& !ShopTableStatusEnum.SUBSCRIBE.equalsVal(status)
&& !ShopTableStatusEnum.CLOSED.equalsVal(status);
} }
} }

View File

@@ -34,6 +34,7 @@ public interface ShopTableService extends IService<ShopTable> {
Boolean updateInfo(long shopId, ShopTableDTO shopTableDTO); Boolean updateInfo(long shopId, ShopTableDTO shopTableDTO);
Boolean clear(long shopId, ShopTableClearDTO shopTableClearDTO); Boolean clear(long shopId, ShopTableClearDTO shopTableClearDTO);
Boolean expiredTable(long tableId);
Page<ShopTableVO> pageInfo(Long shopId, Integer areaId, String tableCode, String status, String name, Boolean isBind); Page<ShopTableVO> pageInfo(Long shopId, Integer areaId, String tableCode, String status, String name, Boolean isBind);
} }

View File

@@ -18,6 +18,9 @@ public enum SysParamCodeEnum {
ALI_OSS_ENDPOINT("ali_oss_endpoint", "阿里云endpoint"), ALI_OSS_ENDPOINT("ali_oss_endpoint", "阿里云endpoint"),
ALI_OSS_ROLE_ARN("ali_oss_role_arn", "阿里云roleArn"), ALI_OSS_ROLE_ARN("ali_oss_role_arn", "阿里云roleArn"),
WX_MINI_VIP_URL("wx_mini_vip_url", "小程序会员页面地址"), WX_MINI_VIP_URL("wx_mini_vip_url", "小程序会员页面地址"),
TABLE_CODE_URL("table_code_url", "桌码生成路径"),
; ;
private final String code; private final String code;

View File

@@ -9,20 +9,29 @@ import lombok.Getter;
@Getter @Getter
public enum ShopTableStatusEnum { public enum ShopTableStatusEnum {
// 未绑定
UNBOUND("unbound","未绑定"),
// 空闲 // 空闲
IDLE("idle"), IDLE("idle","空闲"),
// 使用 // 点餐
USING("using"), ORDERING("ordering","点餐中"),
// 未结账
UNSETTLED("unsettled","未结账"),
// 支付中
PAYING("paying","支付中"),
// 已结账 (待清台)
SETTLED("settled","已结账 (待清台)"),
// 关台 // 关台
CLOSED("closed"), CLOSED("closed","关台"),
// 预定 // 预定
SUBSCRIBE("subscribe"), SUBSCRIBE("subscribe","预定"),
// 清台 ;
CLEANING("cleaning");
private final String value; private final String value;
private final String msg;
ShopTableStatusEnum(String value) { ShopTableStatusEnum(String value,String msg) {
this.value = value; this.value = value;
this.msg = msg;
} }
public boolean equalsVal(String val) { public boolean equalsVal(String val) {

View File

@@ -11,6 +11,7 @@ import com.czg.account.dto.table.ShopTableAddDTO;
import com.czg.account.dto.table.ShopTableBindDTO; import com.czg.account.dto.table.ShopTableBindDTO;
import com.czg.account.dto.table.ShopTableClearDTO; import com.czg.account.dto.table.ShopTableClearDTO;
import com.czg.account.dto.table.ShopTableDTO; import com.czg.account.dto.table.ShopTableDTO;
import com.czg.account.entity.ShopAd;
import com.czg.account.entity.ShopTable; import com.czg.account.entity.ShopTable;
import com.czg.account.entity.ShopTableArea; import com.czg.account.entity.ShopTableArea;
import com.czg.account.entity.ShopTableCode; import com.czg.account.entity.ShopTableCode;
@@ -20,22 +21,28 @@ import com.czg.account.service.ShopTableService;
import com.czg.account.vo.ShopTableVO; import com.czg.account.vo.ShopTableVO;
import com.czg.enums.ShopTableStatusEnum; import com.czg.enums.ShopTableStatusEnum;
import com.czg.exception.ApiNotPrintException; import com.czg.exception.ApiNotPrintException;
import com.czg.resp.CzgResult;
import com.czg.sa.StpKit; import com.czg.sa.StpKit;
import com.czg.service.account.mapper.ShopTableMapper; import com.czg.service.account.mapper.ShopTableMapper;
import com.czg.system.enums.SysParamCodeEnum;
import com.czg.system.service.SysParamsService;
import com.czg.utils.PageUtil; import com.czg.utils.PageUtil;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import com.mybatisflex.core.paginate.Page; import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper; import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl; import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.annotation.DubboService;
import java.io.*; import java.io.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -55,6 +62,20 @@ public class ShopTableServiceImpl extends ServiceImpl<ShopTableMapper, ShopTable
private ShopTableAreaService shopAreaService; private ShopTableAreaService shopAreaService;
@Resource @Resource
private ShopTableCodeService shopTableCodeService; private ShopTableCodeService shopTableCodeService;
@DubboReference
private SysParamsService sysParamsService;
@PostConstruct
public void init() {
updateChain()
.set(ShopTable::getClearTime, null)
.set(ShopTable::getStatus, ShopTableStatusEnum.IDLE.getValue())
.isNotNull(ShopTable::getTableCode)
.isNotNull(ShopTable::getClearTime)
.lt(ShopTable::getClearTime, LocalDateTime.now())
.update();
}
@Override @Override
public Boolean add(Long shopId, ShopTableAddDTO shopTableAddDTO) { public Boolean add(Long shopId, ShopTableAddDTO shopTableAddDTO) {
@@ -78,7 +99,7 @@ public class ShopTableServiceImpl extends ServiceImpl<ShopTableMapper, ShopTable
shopTable.setName(name); shopTable.setName(name);
shopTable.setMaxCapacity(shopTableAddDTO.getMaxCapacity()); shopTable.setMaxCapacity(shopTableAddDTO.getMaxCapacity());
shopTable.setAreaId(shopTableAddDTO.getAreaId()); shopTable.setAreaId(shopTableAddDTO.getAreaId());
shopTable.setStatus(ShopTableStatusEnum.CLOSED.getValue()); shopTable.setStatus(ShopTableStatusEnum.UNBOUND.getValue());
tableArrayList.add(shopTable); tableArrayList.add(shopTable);
} }
@@ -174,7 +195,7 @@ public class ShopTableServiceImpl extends ServiceImpl<ShopTableMapper, ShopTable
File qrFile = new File(dir, tableCode + ".png"); File qrFile = new File(dir, tableCode + ".png");
// 生成二维码图片 // 生成二维码图片
QrCodeUtil.generate("https://kysh.sxczgkj.cn/codeplate?code=" + tableCode, 300, 300, qrFile); QrCodeUtil.generate(sysParamsService.getSysParamValue(SysParamCodeEnum.TABLE_CODE_URL.getCode()) + tableCode, 300, 300, qrFile);
} }
// 先保存数据库记录确保生成ZIP前无异常 // 先保存数据库记录确保生成ZIP前无异常
@@ -250,13 +271,17 @@ public class ShopTableServiceImpl extends ServiceImpl<ShopTableMapper, ShopTable
@Override @Override
public Boolean updateInfo(long shopId, ShopTableDTO shopTableDTO) { public Boolean updateInfo(long shopId, ShopTableDTO shopTableDTO) {
ShopTable shopTable = BeanUtil.copyProperties(shopTableDTO, ShopTable.class); ShopTable shopTable = BeanUtil.copyProperties(shopTableDTO, ShopTable.class);
ShopTable shopTableSource = getOne(new QueryWrapper().eq(ShopTable::getShopId, shopId).eq(ShopTable::getId, shopTableDTO.getId()));
if (StrUtil.isBlank(shopTableSource.getTableCode()) && StrUtil.isNotBlank(shopTable.getStatus()) && ShopTableStatusEnum.UNBOUND.equalsVal(shopTable.getStatus())) {
throw new ApiNotPrintException("修改失败,该台桌未绑定桌码,绑定桌码后自动进入空闲状态");
}
return update(shopTable, new QueryWrapper().eq(ShopTable::getShopId, StpKit.USER.getShopId()).eq(ShopTable::getId, shopTableDTO.getId())); return update(shopTable, new QueryWrapper().eq(ShopTable::getShopId, StpKit.USER.getShopId()).eq(ShopTable::getId, shopTableDTO.getId()));
} }
@Override @Override
public Boolean clear(long shopId, ShopTableClearDTO shopTableClearDTO) { public Boolean clear(long shopId, ShopTableClearDTO shopTableClearDTO) {
ShopTable shopTable = getOne(new QueryWrapper().eq(ShopTable::getShopId, shopId).eq(ShopTable::getId, shopTableClearDTO.getId())); ShopTable shopTable = getOne(new QueryWrapper().eq(ShopTable::getShopId, shopId).eq(ShopTable::getId, shopTableClearDTO.getId()));
if (!ShopTableStatusEnum.CLEANING.equalsVal(shopTable.getStatus())) { if (!ShopTableStatusEnum.SETTLED.equalsVal(shopTable.getStatus())) {
throw new ApiNotPrintException("台桌不处于待清台状态"); throw new ApiNotPrintException("台桌不处于待清台状态");
} }
shopTable.setStatus(ShopTableStatusEnum.IDLE.getValue()); shopTable.setStatus(ShopTableStatusEnum.IDLE.getValue());
@@ -269,6 +294,14 @@ public class ShopTableServiceImpl extends ServiceImpl<ShopTableMapper, ShopTable
.eq(ShopTable::getId, shopTableClearDTO.getId())); .eq(ShopTable::getId, shopTableClearDTO.getId()));
} }
@Override
public Boolean expiredTable(long tableId) {
ShopTable shopTable = new ShopTable();
shopTable.setClearTime(null);
shopTable.setStatus(ShopTableStatusEnum.IDLE.getValue());
return update(shopTable, QueryWrapper.create().eq(ShopTable::getId, tableId));
}
@Override @Override
public Page<ShopTableVO> pageInfo(Long shopId, Integer areaId, String tableCode, String status, String name, Boolean isBind) { public Page<ShopTableVO> pageInfo(Long shopId, Integer areaId, String tableCode, String status, String name, Boolean isBind) {
PageHelper.startPage(PageUtil.buildPageHelp()); PageHelper.startPage(PageUtil.buildPageHelp());

View File

@@ -1,7 +1,9 @@
package com.czg.service.order.service.impl; package com.czg.service.order.service.impl;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.exceptions.ValidateException; import cn.hutool.core.exceptions.ValidateException;
import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.*; import cn.hutool.core.util.*;
@@ -14,6 +16,7 @@ import com.czg.config.RabbitPublisher;
import com.czg.config.RedisCst; import com.czg.config.RedisCst;
import com.czg.entity.notify.CzgPayNotifyDTO; import com.czg.entity.notify.CzgPayNotifyDTO;
import com.czg.entity.notify.CzgRefundNotifyDTO; import com.czg.entity.notify.CzgRefundNotifyDTO;
import com.czg.enums.ShopTableStatusEnum;
import com.czg.enums.ShopUserFlowBizEnum; import com.czg.enums.ShopUserFlowBizEnum;
import com.czg.exception.ApiNotPrintException; import com.czg.exception.ApiNotPrintException;
import com.czg.exception.CzgException; import com.czg.exception.CzgException;
@@ -253,6 +256,8 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
} else { } else {
param.setPayMode("未知"); param.setPayMode("未知");
} }
table.setStatus(ShopTableStatusEnum.UNSETTLED.getValue());
shopTableService.updateById(table);
} }
List<OrderDetail> orderDetails = cartService.getCartByTableCode(shopInfo.getId(), param.getTableCode(), param.getPlaceNum()); List<OrderDetail> orderDetails = cartService.getCartByTableCode(shopInfo.getId(), param.getTableCode(), param.getPlaceNum());
AssertUtil.isListEmpty(orderDetails, "下单失败 购物车为空"); AssertUtil.isListEmpty(orderDetails, "下单失败 购物车为空");
@@ -791,6 +796,20 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
} }
} }
} }
if (StrUtil.isNotBlank(orderInfo.getTableCode())) {
ShopTable table = shopTableService.getOneByTableCode(orderInfo.getShopId(), orderInfo.getTableCode());
if (table != null) {
ShopInfo shopInfo = shopInfoService.getById(orderInfo.getShopId());
if ("auto".equals(shopInfo.getTableClearType())) {
table.setStatus(ShopTableStatusEnum.IDLE.getValue());
} else {
DateTime dateTime = DateUtil.offsetMinute(DateUtil.date(), shopInfo.getTableClearTime());
table.setCreateTime(LocalDateTimeUtil.of(dateTime));
redisService.set(RedisCst.classKeyExpired.EXPIRED_TABLE + table.getId(), "", 60L * shopInfo.getTableClearTime());
}
shopTableService.updateById(table);
}
}
orderDetailService.updateChain().set(OrderDetail::getStatus, OrderStatusEnums.DONE.getCode()).eq(OrderDetail::getOrderId, orderInfo.getId()).update(); orderDetailService.updateChain().set(OrderDetail::getStatus, OrderStatusEnums.DONE.getCode()).eq(OrderDetail::getOrderId, orderInfo.getId()).update();
// if (!"after-pay".equals(orderInfo.getPayMode())) { // if (!"after-pay".equals(orderInfo.getPayMode())) {
//发送打票信息 //发送打票信息
@@ -812,6 +831,13 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
updateChain().set(OrderInfo::getStatus, OrderStatusEnums.CANCELLED.getCode()) updateChain().set(OrderInfo::getStatus, OrderStatusEnums.CANCELLED.getCode())
.where(OrderInfo::getId).eq(orderId) .where(OrderInfo::getId).eq(orderId)
.update(); .update();
if (StrUtil.isNotBlank(orderInfo.getTableCode())) {
ShopTable table = shopTableService.getOneByTableCode(orderInfo.getShopId(), orderInfo.getTableCode());
if (table != null) {
shopTableService.updateById(table);
}
}
rabbitPublisher.sendOrderCancelMsg(orderId.toString()); rabbitPublisher.sendOrderCancelMsg(orderId.toString());
} else { } else {
log.info("订单取消失败,订单Id:{},订状态:{}", orderInfo.getId(), orderInfo.getStatus()); log.info("订单取消失败,订单Id:{},订状态:{}", orderInfo.getId(), orderInfo.getStatus());