feat(柜机格口): 增加格口密码创建时间字段及暂存柜到期处理

为柜机格口添加密码创建时间字段,用于记录密码生成时间
新增定时任务处理暂存柜模式下到期格口的自动重置功能
完善格口密码相关逻辑,包括密码生成、重置和DTO映射
This commit is contained in:
dzq 2025-12-24 17:24:04 +08:00
parent c9e5b18ddc
commit f74f53520b
9 changed files with 242 additions and 9 deletions

View File

@ -16,6 +16,8 @@ import com.agileboot.domain.qywx.authCorpInfo.db.QyAuthCorpInfoEntity;
import com.agileboot.domain.qywx.user.db.QyUserService;
import com.agileboot.domain.shop.order.db.ShopOrderEntity;
import com.agileboot.domain.shop.order.db.ShopOrderGoodsEntity;
import com.agileboot.domain.shop.shop.db.ShopEntity;
import com.agileboot.domain.shop.shop.db.ShopService;
import com.agileboot.domain.shop.order.db.ShopOrderGoodsService;
import com.agileboot.domain.shop.order.db.ShopOrderService;
import com.agileboot.domain.shop.paymentOperationLog.PaymentOperationLogApplicationService;
@ -41,6 +43,7 @@ public class DeadlineOrderJob {
private final ShopOrderGoodsService shopOrderGoodsService;
private final CabinetCellService cabinetCellService;
private final SmartCabinetService smartCabinetService;
private final ShopService shopService;
private final PaymentOperationLogApplicationService paymentOperationLogApplicationService;
private final AuthCorpInfoApplicationService authCorpInfoApplicationService;
private final AccessTokenApplicationService accessTokenApplicationService;
@ -112,12 +115,20 @@ public class DeadlineOrderJob {
}
Integer returnDeadlineDays = smartCabinet.getReturnDeadline();
// 检查归还期限是否有效大于0天
if (returnDeadlineDays == null || returnDeadlineDays <= 0) {
continue;
}
ShopEntity shop = shopService.getById(smartCabinet.getShopId());
if (shop == null) {
log.warn("智能柜[{}]关联的商店[{}]不存在,跳过处理.", smartCabinet.getCabinetId(), smartCabinet.getShopId());
continue;
}
if (shop.getMode() != null && shop.getMode().equals(5)) {
continue;
}
Date orderCreateTime = order.getCreateTime();
if (orderCreateTime == null) {
log.warn("订单[{}]的创建时间为空,无法计算归还期限,订单商品[{}]跳过处理.", order.getOrderId(), orderGoods.getOrderGoodsId());

View File

@ -0,0 +1,159 @@
package com.agileboot.admin.customize.service.job;
import cn.hutool.core.date.DateUtil;
import com.agileboot.domain.cabinet.cell.db.CabinetCellEntity;
import com.agileboot.domain.cabinet.cell.db.CabinetCellService;
import com.agileboot.domain.cabinet.smartCabinet.db.SmartCabinetEntity;
import com.agileboot.domain.cabinet.smartCabinet.db.SmartCabinetService;
import com.agileboot.domain.shop.shop.db.ShopEntity;
import com.agileboot.domain.shop.shop.db.ShopService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
@RequiredArgsConstructor
@Component
@Slf4j
public class StorageCabinetExpiryJob {
private final ShopService shopService;
private final SmartCabinetService smartCabinetService;
private final CabinetCellService cabinetCellService;
/**
* 定时任务处理暂存柜到期格口
* 每天凌晨4点执行检查暂存柜模式shop.mode=5下密码创建时间过期的格口并重置
*/
@Scheduled(cron = "0 0 4 * * *")
@Transactional
public void resetExpiredStorageCells() {
log.info("开始执行暂存柜到期格口重置任务...");
try {
List<ShopEntity> storageShops = getStorageShops();
if (CollectionUtils.isEmpty(storageShops)) {
log.info("未找到暂存柜模式商店.");
return;
}
log.info("查找到 {} 个暂存柜模式商店.", storageShops.size());
int totalResetCount = 0;
for (ShopEntity shop : storageShops) {
int resetCount = processShopStorageCabinets(shop);
totalResetCount += resetCount;
}
log.info("暂存柜到期格口重置任务执行完毕,共重置 {} 个格口.", totalResetCount);
} catch (Exception globalException) {
log.error("执行暂存柜到期格口重置任务时发生全局错误:", globalException);
}
log.info("暂存柜到期格口重置任务执行完毕.");
}
/**
* 获取所有暂存柜模式mode=5的商店
*/
private List<ShopEntity> getStorageShops() {
LambdaQueryWrapper<ShopEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ShopEntity::getMode, 5);
queryWrapper.eq(ShopEntity::getDeleted, false);
return shopService.list(queryWrapper);
}
/**
* 处理单个商店的暂存柜格口
*/
private int processShopStorageCabinets(ShopEntity shop) {
int resetCount = 0;
List<SmartCabinetEntity> cabinets = getCabinetsByShopId(shop.getShopId());
if (CollectionUtils.isEmpty(cabinets)) {
log.debug("商店[{}]下未找到智能柜.", shop.getShopId());
return 0;
}
log.info("商店[{}]下查找到 {} 个智能柜.", shop.getShopId(), cabinets.size());
for (SmartCabinetEntity cabinet : cabinets) {
int cabinetResetCount = processStorageCabinet(cabinet);
resetCount += cabinetResetCount;
}
return resetCount;
}
/**
* 根据商店ID获取智能柜列表
*/
private List<SmartCabinetEntity> getCabinetsByShopId(Long shopId) {
LambdaQueryWrapper<SmartCabinetEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SmartCabinetEntity::getShopId, shopId);
queryWrapper.eq(SmartCabinetEntity::getDeleted, false);
return smartCabinetService.list(queryWrapper);
}
/**
* 处理单个智能柜的到期格口
*/
private int processStorageCabinet(SmartCabinetEntity cabinet) {
Integer returnDeadlineDays = cabinet.getReturnDeadline();
if (returnDeadlineDays == null || returnDeadlineDays <= 0) {
log.debug("智能柜[{}]归还期限无效,跳过处理.", cabinet.getCabinetId());
return 0;
}
List<CabinetCellEntity> expiredCells = getExpiredCells(cabinet.getCabinetId(), returnDeadlineDays);
if (CollectionUtils.isEmpty(expiredCells)) {
log.debug("智能柜[{}]未找到需重置的到期格口.", cabinet.getCabinetId());
return 0;
}
log.info("智能柜[{}]查找到 {} 个需重置的到期格口.", cabinet.getCabinetId(), expiredCells.size());
int resetCount = 0;
for (CabinetCellEntity cell : expiredCells) {
try {
cabinetCellService.resetCellById(cell.getCellId());
log.info("成功重置到期格口格口ID[{}], 格口号[{}], 柜机ID[{}].",
cell.getCellId(), cell.getCellNo(), cabinet.getCabinetId());
resetCount++;
} catch (Exception e) {
log.error("重置格口[{}]失败", cell.getCellId(), e);
}
}
return resetCount;
}
/**
* 获取指定智能柜下密码创建时间过期的格口
* 条件password 不为空usage_status 2password_create_time 早于计算出的过期时间
*/
private List<CabinetCellEntity> getExpiredCells(Long cabinetId, int returnDeadlineDays) {
Date deadlineDate = DateUtil.offsetDay(new Date(), -returnDeadlineDays);
LambdaQueryWrapper<CabinetCellEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CabinetCellEntity::getCabinetId, cabinetId);
queryWrapper.isNotNull(CabinetCellEntity::getPassword);
queryWrapper.ne(CabinetCellEntity::getPassword, "");
queryWrapper.eq(CabinetCellEntity::getUsageStatus, 2);
queryWrapper.isNotNull(CabinetCellEntity::getPasswordCreateTime);
queryWrapper.le(CabinetCellEntity::getPasswordCreateTime, deadlineDate);
queryWrapper.eq(CabinetCellEntity::getDeleted, false);
return cabinetCellService.list(queryWrapper);
}
}

View File

@ -29,6 +29,7 @@ import com.agileboot.domain.cabinet.mainboard.model.CabinetMainboardModelFactory
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.List;
import java.util.Date;
import java.util.Random;
import java.util.stream.Collectors;
import org.springframework.transaction.annotation.Transactional;
@ -293,10 +294,17 @@ public class CabinetCellApplicationService {
// 更新密码和状态
cellModel.setPassword(password);
cellModel.setUsageStatus(2); // 设置为已占用
cellModel.setPasswordCreateTime(new Date());
cellModel.updateById();
// 获取柜子的归还期限
SmartCabinetModel cabinetModel = smartCabinetModelFactory.loadById(cellModel.getCabinetId());
Integer returnDeadline = cabinetModel.getReturnDeadline();
// 返回格口信息包含密码
return new CabinetCellDTO(cellModel);
CabinetCellDTO result = new CabinetCellDTO(cellModel);
result.setReturnDeadline(returnDeadline);
return result;
}
private String generateFourDigitPassword() {

View File

@ -7,6 +7,8 @@ import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ -61,6 +63,10 @@ public class CabinetCellEntity extends BaseEntity<CabinetCellEntity> {
@TableField("password")
private String password;
@ApiModelProperty("格口密码创建时间")
@TableField("password_create_time")
private Date passwordCreateTime;
@ApiModelProperty("是否已租用0-未租用1-已租用")
@TableField("is_rented")
private Integer isRented;

View File

@ -97,6 +97,7 @@ public class CabinetCellServiceImpl extends ServiceImpl<CabinetCellMapper, Cabin
public void resetCellById(Long cellId) {
UpdateWrapper<CabinetCellEntity> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("password", null)
.set("password_create_time", null)
.set("usage_status", 1) // 空闲
.eq("cell_id", cellId)
.eq("deleted", 0);

View File

@ -9,6 +9,7 @@ import com.agileboot.domain.system.user.db.SysUserEntity;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@ExcelSheet(name = "柜机格口信息列表")
@Data
@ -64,6 +65,9 @@ public class CabinetCellDTO {
@ExcelColumn(name = "格口密码")
private String password;
@ExcelColumn(name = "格口密码创建时间")
private Date passwordCreateTime;
@ExcelColumn(name = "使用状态")
private Integer usageStatus;
@ -80,6 +84,9 @@ public class CabinetCellDTO {
@ExcelColumn(name = "封面图URL")
private String coverImg;
@ExcelColumn(name = "归还期限(天)")
private Integer returnDeadline;
@ExcelColumn(name = "操作人")
private String operator;

View File

@ -40,6 +40,7 @@ public class GoodsApplicationService {
public void addGoods(AddGoodsCommand command) {
GoodsModel model = goodsModelFactory.create();
model.loadAddGoodsCommand(command);
model.initBaseEntity();
/* model.checkGoodsNameUnique();
model.checkCategoryExist();*/

View File

@ -1,8 +1,26 @@
-- wxshop.mqtt_server definition
CREATE TABLE `mqtt_server` (
`mqtt_server_id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`server_url` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'MQTT服务器地址',
`username` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '连接账号',
`password` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '连接密码',
`topic_filter` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '订阅主题过滤器',
`publish_topic` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '发布主题',
`creator_id` bigint DEFAULT '0' COMMENT '创建者ID',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater_id` bigint DEFAULT '0' COMMENT '更新者ID',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标志0存在 1删除',
PRIMARY KEY (`mqtt_server_id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='MQTT服务配置表';
-- wxshop.shop definition
CREATE TABLE `shop` (
`shop_id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`shop_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商店名称',
`shop_name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商店名称',
`corpid` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '企业微信id',
`belong_type` tinyint NOT NULL DEFAULT '0' COMMENT '归属类型0-借还柜 1-固资通)',
`mode` tinyint NOT NULL DEFAULT '0' COMMENT '运行模式0-支付模式 1-审批模式 2-借还模式 3-会员模式 4-耗材模式)',
@ -14,14 +32,14 @@ CREATE TABLE `shop` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标志0存在 1删除',
PRIMARY KEY (`shop_id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='商店表,每个柜子属于一个商店';
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='商店表,每个柜子属于一个商店';
-- wxshop.smart_cabinet definition
CREATE TABLE `smart_cabinet` (
`cabinet_id` bigint NOT NULL AUTO_INCREMENT COMMENT '柜机唯一ID',
`cabinet_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '柜机名称',
`cabinet_name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '柜机名称',
`cabinet_type` tinyint NOT NULL DEFAULT '0' COMMENT '柜机类型0主柜 1副柜',
`main_cabinet` bigint DEFAULT NULL COMMENT '归属主柜ID',
`balance_enable` tinyint NOT NULL DEFAULT '1' COMMENT '借呗支付1-正常使用 0-禁止使用)',
@ -29,7 +47,7 @@ CREATE TABLE `smart_cabinet` (
`belong_type` tinyint NOT NULL DEFAULT '0' COMMENT '归属类型0-借还柜 1-固资通)',
`shop_id` bigint DEFAULT NULL COMMENT '归属商店ID',
`mqtt_server_id` bigint DEFAULT NULL COMMENT 'MQTT服务ID',
`template_no` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '柜机模版编号',
`template_no` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '柜机模版编号',
`lock_control_no` int NOT NULL COMMENT '锁控板序号',
`location` int NOT NULL COMMENT '柜机位置',
`creator_id` bigint NOT NULL DEFAULT '0' COMMENT '创建者ID',
@ -38,13 +56,30 @@ CREATE TABLE `smart_cabinet` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标志0存在 1删除',
`return_deadline` int NOT NULL DEFAULT '0' COMMENT '归还期限0表示不限制',
`corpid` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '企业微信id',
`corpid` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '企业微信id',
PRIMARY KEY (`cabinet_id`),
KEY `idx_template_no` (`template_no`),
KEY `idx_location` (`location`)
) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='智能柜信息表';
-- wxshop.cabinet_mainboard definition
CREATE TABLE `cabinet_mainboard` (
`mainboard_id` bigint NOT NULL AUTO_INCREMENT COMMENT '主板唯一ID',
`cabinet_id` bigint NOT NULL COMMENT '关联柜机ID',
`lock_control_no` int NOT NULL COMMENT '锁控板序号',
`creator_id` bigint DEFAULT '0' COMMENT '创建者ID',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater_id` bigint DEFAULT '0' COMMENT '更新者ID',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标志0存在 1删除',
PRIMARY KEY (`mainboard_id`),
KEY `idx_cabinet` (`cabinet_id`),
CONSTRAINT `fk_mainboard_cabinet` FOREIGN KEY (`cabinet_id`) REFERENCES `smart_cabinet` (`cabinet_id`)
) ENGINE=InnoDB AUTO_INCREMENT=63 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='机柜主板信息表';
-- wxshop.cabinet_cell definition
CREATE TABLE `cabinet_cell` (
@ -57,6 +92,7 @@ CREATE TABLE `cabinet_cell` (
`stock` int NOT NULL DEFAULT '0' COMMENT '库存数量',
`cell_price` decimal(15,2) NOT NULL DEFAULT '0.00' COMMENT '格口租用价格',
`password` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '格口密码',
`password_create_time` datetime DEFAULT NULL COMMENT '格口密码创建时间',
`is_rented` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否已租用0-未租用1-已租用',
`cell_type` tinyint NOT NULL DEFAULT '1' COMMENT '格口类型1小格 2中格 3大格 4超大格',
`usage_status` tinyint NOT NULL DEFAULT '1' COMMENT '使用状态1空闲 2已占用',

View File

@ -1,3 +1,7 @@
ALTER TABLE `cabinet_cell`
ADD COLUMN `password` VARCHAR(10) DEFAULT NULL COMMENT '格口密码'
AFTER `cell_price`;
AFTER `cell_price`;
ALTER TABLE `cabinet_cell`
ADD COLUMN `password_create_time` datetime DEFAULT NULL COMMENT '格口密码创建时间'
AFTER `password`;