Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
2025-12-05 20:10:20 +08:00
42 changed files with 1102 additions and 14 deletions

View File

@ -368,3 +368,14 @@ drone:
chat: chat:
server: server:
port: 19099 port: 19099
# rabbitmq 配置
rabbitmq:
exchange-name: dev-normal-exchange
queue-name: dev-normal-queue
routing-key: dev.normal.routing.key
delay-exchange-name: dev-delay-queue
delay-queue-name: dev-delay-exchange
delay-routing-key: dev.delay.routing.key
dead-letter-exchange: dev-dlx-exchange
dead-letter-queue: dev-dlx-queue
dead-letter-routing-key: dev.dlx.routing.key

View File

@ -343,3 +343,14 @@ drone:
chat: chat:
server: server:
port: 18088 port: 18088
# rabbitmq 配置
rabbitmq:
exchange-name: local-normal-exchange
queue-name: local-normal-queue
routing-key: local.normal.routing.key
delay-exchange-name: local-delay-queue
delay-queue-name: local-delay-exchange
delay-routing-key: local.delay.routing.key
dead-letter-exchange: local-dlx-exchange
dead-letter-queue: local-dlx-queue
dead-letter-routing-key: local.dlx.routing.key

View File

@ -358,3 +358,14 @@ drone:
chat: chat:
server: server:
port: 19099 port: 19099
# rabbitmq 配置
rabbitmq:
exchange-name: prod-normal-exchange
queue-name: prod-normal-queue
routing-key: prod.normal.routing.key
delay-exchange-name: prod-delay-queue
delay-queue-name: prod-delay-exchange
delay-routing-key: prod.delay.routing.key
dead-letter-exchange: prod-dlx-exchange
dead-letter-queue: prod-dlx-queue
dead-letter-routing-key: prod.dlx.routing.key

View File

@ -95,7 +95,13 @@ spring:
deserialization: deserialization:
# 允许对象忽略json中不存在的属性 # 允许对象忽略json中不存在的属性
fail_on_unknown_properties: false fail_on_unknown_properties: false
rabbitmq:
host: 192.168.110.2
port: 5672
username: admin
password: yuanjiexny
publisher-returns: true
publisher-confirm-type: correlated
# Sa-Token配置 # Sa-Token配置
sa-token: sa-token:
# token名称 (同时也是cookie名称) # token名称 (同时也是cookie名称)

View File

@ -29,6 +29,10 @@
<dependencies> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- TwelveMonkeys ImageIO 扩展 --> <!-- TwelveMonkeys ImageIO 扩展 -->
<dependency> <dependency>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>

View File

@ -16,6 +16,7 @@ import org.dromara.device.domain.DeviceAccessRecord;
import org.dromara.device.mapper.DeviceAccessRecordMapper; import org.dromara.device.mapper.DeviceAccessRecordMapper;
import org.dromara.device.service.IDeviceAccessRecordService; import org.dromara.device.service.IDeviceAccessRecordService;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Collection; import java.util.Collection;
@ -77,6 +78,16 @@ public class DeviceAccessRecordServiceImpl extends ServiceImpl<DeviceAccessRecor
lqw.eq(bo.getDeviceId() != null, DeviceAccessRecord::getDeviceId, bo.getDeviceId()); lqw.eq(bo.getDeviceId() != null, DeviceAccessRecord::getDeviceId, bo.getDeviceId());
lqw.like(StringUtils.isNotBlank(bo.getDeviceName()), DeviceAccessRecord::getDeviceName, bo.getDeviceName()); lqw.like(StringUtils.isNotBlank(bo.getDeviceName()), DeviceAccessRecord::getDeviceName, bo.getDeviceName());
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), DeviceAccessRecord::getStatus, bo.getStatus()); lqw.eq(StringUtils.isNotBlank(bo.getStatus()), DeviceAccessRecord::getStatus, bo.getStatus());
if(bo.getOperateTime() != null){
LocalDateTime operateTime = bo.getOperateTime();
//一天的开始
LocalDateTime startTime = operateTime.withHour(0).withMinute(0).withSecond(0);
//一天的结束
LocalDateTime endTime = operateTime.withHour(23).withMinute(59).withSecond(59);
lqw.between(DeviceAccessRecord::getOperateTime,startTime,endTime);
}
lqw.eq(bo.getOperateTime() != null, DeviceAccessRecord::getOperateTime, bo.getOperateTime()); lqw.eq(bo.getOperateTime() != null, DeviceAccessRecord::getOperateTime, bo.getOperateTime());
lqw.eq(StringUtils.isNotBlank(bo.getLocation()), DeviceAccessRecord::getLocation, bo.getLocation()); lqw.eq(StringUtils.isNotBlank(bo.getLocation()), DeviceAccessRecord::getLocation, bo.getLocation());
lqw.eq(bo.getOperatorId() != null, DeviceAccessRecord::getOperatorId, bo.getOperatorId()); lqw.eq(bo.getOperatorId() != null, DeviceAccessRecord::getOperatorId, bo.getOperatorId());

View File

@ -55,7 +55,10 @@ public class DeviceInfoServiceImpl extends ServiceImpl<DeviceInfoMapper, DeviceI
*/ */
@Override @Override
public DeviceInfoVo queryById(Long id){ public DeviceInfoVo queryById(Long id){
return baseMapper.selectVoById(id); DeviceInfoVo deviceInfoVo = baseMapper.selectVoById(id);
DeviceType byId = deviceTypeService.getById(deviceInfoVo.getTypeId());
deviceInfoVo.setTypeName(byId.getTypeName());
return deviceInfoVo;
} }
/** /**

View File

@ -165,6 +165,7 @@ public class DeviceTypeServiceImpl extends ServiceImpl<DeviceTypeMapper, DeviceT
List<DeviceType> list = baseMapper.selectList(queryWrapper); List<DeviceType> list = baseMapper.selectList(queryWrapper);
checkIds.addAll(list.stream().map(DeviceType::getId).toList()); checkIds.addAll(list.stream().map(DeviceType::getId).toList());
} }
checkIds.addAll(ids);
List<DeviceInfo> list = deviceInfoService.lambdaQuery().in(DeviceInfo::getTypeId, checkIds).list(); List<DeviceInfo> list = deviceInfoService.lambdaQuery().in(DeviceInfo::getTypeId, checkIds).list();
if(CollectionUtil.isNotEmpty(list)){ if(CollectionUtil.isNotEmpty(list)){
throw new ServiceException("当前类型或子级类型存在设备"); throw new ServiceException("当前类型或子级类型存在设备");

View File

@ -97,11 +97,27 @@ public class JxAqjcgl extends BaseEntity {
*/ */
private String riskGrade; private String riskGrade;
/**
* 整改状态
*/
private String abarbeitungState;
/** /**
* 整改责任人 * 整改责任人
*/ */
private Long abarbeitung; private Long abarbeitung;
/**
* 选择分包还是班组
*/
private String rectificationUnit;
/**
* 整改单位
*/
private Long rectificationId;
/** /**
* 整改日期 * 整改日期
*/ */

View File

@ -57,6 +57,22 @@ public class JxAqjcglBo extends BaseEntity {
*/ */
private String participants; private String participants;
/**
* 整改状态
*/
private String abarbeitungState;
/**
* 选择分包还是班组
*/
private String rectificationUnit;
/**
* 整改单位
*/
private Long rectificationId;
/** /**
* 所属项目(如田东) * 所属项目(如田东)
*/ */

View File

@ -71,6 +71,26 @@ public class JxAqjcglVo implements Serializable {
@ExcelProperty(value = "检查人") @ExcelProperty(value = "检查人")
private Long inspector; private Long inspector;
/**
* 整改状态
*/
private String abarbeitungState;
/**
* 选择分包还是班组
*/
private String rectificationUnit;
/**
* 整改单位
*/
private Long rectificationId;
/**
* 整改单位
*/
private String rectificationName;
/** /**
* 检查人 * 检查人
*/ */

View File

@ -1,9 +1,11 @@
package org.dromara.mechanical.jxaqgl.service.impl; package org.dromara.mechanical.jxaqgl.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.enums.InspectionRectificationUnitType;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -11,6 +13,8 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.utils.BatchNumberGenerator; import org.dromara.common.utils.BatchNumberGenerator;
import org.dromara.contractor.domain.SubContractor;
import org.dromara.contractor.service.ISubContractorService;
import org.dromara.mechanical.jxaqgl.domain.JxAqjcglJcnr; import org.dromara.mechanical.jxaqgl.domain.JxAqjcglJcnr;
import org.dromara.mechanical.jxaqgl.domain.JxAqjcglWtxq; import org.dromara.mechanical.jxaqgl.domain.JxAqjcglWtxq;
import org.dromara.mechanical.jxaqgl.domain.MonthDateRange; import org.dromara.mechanical.jxaqgl.domain.MonthDateRange;
@ -25,6 +29,9 @@ import org.dromara.mechanical.jxzgbh.domain.bo.JxYhzgbhBo;
import org.dromara.mechanical.jxzgbh.domain.bo.JxZgxxBo; import org.dromara.mechanical.jxzgbh.domain.bo.JxZgxxBo;
import org.dromara.mechanical.jxzgbh.service.IJxYhzgbhService; import org.dromara.mechanical.jxzgbh.service.IJxYhzgbhService;
import org.dromara.mechanical.jxzgbh.service.IJxZgxxService; import org.dromara.mechanical.jxzgbh.service.IJxZgxxService;
import org.dromara.project.domain.BusProjectTeam;
import org.dromara.project.service.IBusProjectService;
import org.dromara.project.service.IBusProjectTeamService;
import org.dromara.system.service.impl.SysOssServiceImpl; import org.dromara.system.service.impl.SysOssServiceImpl;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
@ -58,6 +65,12 @@ public class JxAqjcglServiceImpl extends ServiceImpl<JxAqjcglMapper, JxAqjcgl> i
private final SysOssServiceImpl sysOssService; private final SysOssServiceImpl sysOssService;
@Resource
private ISubContractorService contractorService;
@Resource
private IBusProjectTeamService projectTeamService;
@Lazy @Lazy
@Autowired @Autowired
private IJxYhzgbhService jxYhzgbhService; private IJxYhzgbhService jxYhzgbhService;
@ -82,6 +95,23 @@ public class JxAqjcglServiceImpl extends ServiceImpl<JxAqjcglMapper, JxAqjcgl> i
List<JxAqjcglJcnr> jxAqjcglJcnrs = jxAqjcglJcnrService.getBaseMapper().selectList(new LambdaQueryWrapper<JxAqjcglJcnr>().eq(JxAqjcglJcnr::getMasterId, jxAqjcglVo.getId())); List<JxAqjcglJcnr> jxAqjcglJcnrs = jxAqjcglJcnrService.getBaseMapper().selectList(new LambdaQueryWrapper<JxAqjcglJcnr>().eq(JxAqjcglJcnr::getMasterId, jxAqjcglVo.getId()));
jxAqjcglVo.setWtxqBoList(jxAqjcglWtxqs); jxAqjcglVo.setWtxqBoList(jxAqjcglWtxqs);
jxAqjcglVo.setJcnrList(jxAqjcglJcnrs); jxAqjcglVo.setJcnrList(jxAqjcglJcnrs);
// 关联整改单位信息
Long rectificationId = jxAqjcglVo.getRectificationId();
String rectificationUnit = jxAqjcglVo.getRectificationUnit();
if (rectificationId != null && rectificationUnit != null) {
if (rectificationUnit.equals(InspectionRectificationUnitType.CONTRACTOR.getValue())) {
SubContractor contractor = contractorService.getById(rectificationId);
jxAqjcglVo.setRectificationName(contractor.getName());
} else if (rectificationUnit.equals(InspectionRectificationUnitType.TEAM.getValue())) {
BusProjectTeam team = projectTeamService.getById(rectificationId);
if (team != null) {
jxAqjcglVo.setRectificationName(team.getTeamName());
}
}
}
} }
} }
@ -179,6 +209,7 @@ public class JxAqjcglServiceImpl extends ServiceImpl<JxAqjcglMapper, JxAqjcgl> i
JxAqjcgl update = MapstructUtils.convert(bo, JxAqjcgl.class); JxAqjcgl update = MapstructUtils.convert(bo, JxAqjcgl.class);
validEntityBeforeSave(update); validEntityBeforeSave(update);
if ("2".equals(bo.getInspectionResult())){ if ("2".equals(bo.getInspectionResult())){
update.setAbarbeitungState("1");
JxYhzgbh jxYhzgbh = jxYhzgbhService.getBaseMapper().selectOne(new LambdaQueryWrapper<JxYhzgbh>().eq(JxYhzgbh::getMasterId, update.getId())); JxYhzgbh jxYhzgbh = jxYhzgbhService.getBaseMapper().selectOne(new LambdaQueryWrapper<JxYhzgbh>().eq(JxYhzgbh::getMasterId, update.getId()));
if (jxYhzgbh != null) throw new ServiceException("该数据已存在整改数据"); if (jxYhzgbh != null) throw new ServiceException("该数据已存在整改数据");
// 不通过转为整改数据 // 不通过转为整改数据

View File

@ -1,5 +1,7 @@
package org.dromara.mechanical.jxzgbh.domain.vo; package org.dromara.mechanical.jxzgbh.domain.vo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import org.dromara.common.translation.annotation.Translation; import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant; import org.dromara.common.translation.constant.TransConstant;
import org.dromara.mechanical.jxzgbh.domain.JxFcxx; import org.dromara.mechanical.jxzgbh.domain.JxFcxx;
@ -103,4 +105,15 @@ public class JxFcxxVo implements Serializable {
private Integer sort; private Integer sort;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
} }

View File

@ -14,6 +14,7 @@ import lombok.RequiredArgsConstructor;
import org.dromara.mechanical.jxaqgl.domain.JxAqjcgl; import org.dromara.mechanical.jxaqgl.domain.JxAqjcgl;
import org.dromara.mechanical.jxaqgl.domain.JxAqjcglWtxq; import org.dromara.mechanical.jxaqgl.domain.JxAqjcglWtxq;
import org.dromara.mechanical.jxaqgl.domain.MonthDateRange; import org.dromara.mechanical.jxaqgl.domain.MonthDateRange;
import org.dromara.mechanical.jxaqgl.service.IJxAqjcglService;
import org.dromara.mechanical.jxaqgl.service.IJxAqjcglWtxqService; import org.dromara.mechanical.jxaqgl.service.IJxAqjcglWtxqService;
import org.dromara.mechanical.jxzgbh.domain.JxFcxx; import org.dromara.mechanical.jxzgbh.domain.JxFcxx;
import org.dromara.mechanical.jxzgbh.domain.JxZgxx; import org.dromara.mechanical.jxzgbh.domain.JxZgxx;
@ -53,6 +54,8 @@ public class JxYhzgbhServiceImpl extends ServiceImpl<JxYhzgbhMapper, JxYhzgbh> i
private final IJxAqjcglWtxqService jxAqjcglWtxqService; private final IJxAqjcglWtxqService jxAqjcglWtxqService;
private final IJxAqjcglService jxAqjcglService;
/** /**
* 查询机械隐患整改与闭环 * 查询机械隐患整改与闭环
* *
@ -98,6 +101,7 @@ public class JxYhzgbhServiceImpl extends ServiceImpl<JxYhzgbhMapper, JxYhzgbh> i
public TableDataInfo<JxYhzgbhVo> queryPageList(JxYhzgbhBo bo, PageQuery pageQuery) { public TableDataInfo<JxYhzgbhVo> queryPageList(JxYhzgbhBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<JxYhzgbh> lqw = buildQueryWrapper(bo); LambdaQueryWrapper<JxYhzgbh> lqw = buildQueryWrapper(bo);
Page<JxYhzgbhVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); Page<JxYhzgbhVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
result.getRecords().forEach(this::saveValue);
return TableDataInfo.build(result); return TableDataInfo.build(result);
} }
@ -200,6 +204,11 @@ public class JxYhzgbhServiceImpl extends ServiceImpl<JxYhzgbhMapper, JxYhzgbh> i
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Boolean zgPutBo(JxZgxxBo bo) { public Boolean zgPutBo(JxZgxxBo bo) {
// 安全检查记录
if (bo.getMasterId() == null) throw new RuntimeException("主表id不能为空");
JxYhzgbhVo jxYhzgbhVo = this.getBaseMapper().selectVoOne(new LambdaQueryWrapper<JxYhzgbh>().eq(JxYhzgbh::getId, bo.getMasterId()));
jxAqjcglService.getBaseMapper().update(new LambdaUpdateWrapper<JxAqjcgl>().eq(JxAqjcgl::getId, jxYhzgbhVo.getMasterId()).set(JxAqjcgl::getAbarbeitungState, "2"));
Long l = jxZgxxService.getBaseMapper().selectCount(new LambdaQueryWrapper<JxZgxx>().eq(JxZgxx::getMasterId, bo.getMasterId())); Long l = jxZgxxService.getBaseMapper().selectCount(new LambdaQueryWrapper<JxZgxx>().eq(JxZgxx::getMasterId, bo.getMasterId()));
bo.setSort(Integer.parseInt(l+"") + 1); bo.setSort(Integer.parseInt(l+"") + 1);
// 将状态修改为已整改 // 将状态修改为已整改
@ -210,8 +219,14 @@ public class JxYhzgbhServiceImpl extends ServiceImpl<JxYhzgbhMapper, JxYhzgbh> i
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Boolean fcPutBo(JxFcxxBo bo) { public Boolean fcPutBo(JxFcxxBo bo) {
// 不通过修改整改信息状态
if (bo.getReviewOpinion().equals("2")) { if (bo.getReviewOpinion().equals("2")) {
if (bo.getMasterId() == null) throw new RuntimeException("主表id不能为空");
this.getBaseMapper().update(new LambdaUpdateWrapper<JxYhzgbh>().eq(JxYhzgbh::getId, bo.getMasterId()).set(JxYhzgbh::getAbarbeitungState,"1")); this.getBaseMapper().update(new LambdaUpdateWrapper<JxYhzgbh>().eq(JxYhzgbh::getId, bo.getMasterId()).set(JxYhzgbh::getAbarbeitungState,"1"));
// 安全检查记录
JxYhzgbhVo jxYhzgbhVo = this.getBaseMapper().selectVoOne(new LambdaQueryWrapper<JxYhzgbh>().eq(JxYhzgbh::getId, bo.getMasterId()));
jxAqjcglService.getBaseMapper().update(new LambdaUpdateWrapper<JxAqjcgl>().eq(JxAqjcgl::getId, jxYhzgbhVo.getMasterId()).set(JxAqjcgl::getAbarbeitungState, "1"));
} }
this.getBaseMapper().update(new LambdaUpdateWrapper<JxYhzgbh>().eq(JxYhzgbh::getId, bo.getMasterId()).set(JxYhzgbh::getReviewOpinion,bo.getReviewOpinion())); this.getBaseMapper().update(new LambdaUpdateWrapper<JxYhzgbh>().eq(JxYhzgbh::getId, bo.getMasterId()).set(JxYhzgbh::getReviewOpinion,bo.getReviewOpinion()));

View File

@ -4,12 +4,17 @@ import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.web.core.BaseController; import org.dromara.common.web.core.BaseController;
import org.dromara.contractor.domain.dto.contractor.SubContractorQueryReq;
import org.dromara.contractor.domain.vo.contractor.SubContractorVo;
import org.dromara.contractor.service.ISubContractorService;
import org.dromara.project.domain.dto.projectteam.BusProjectTeamQueryReq; import org.dromara.project.domain.dto.projectteam.BusProjectTeamQueryReq;
import org.dromara.project.domain.vo.projectteam.BusProjectTeamAppVo; import org.dromara.project.domain.vo.projectteam.BusProjectTeamAppVo;
import org.dromara.project.domain.vo.projectteam.BusProjectTeamForemanVo; import org.dromara.project.domain.vo.projectteam.BusProjectTeamForemanVo;
import org.dromara.project.domain.vo.projectteam.BusProjectTeamVo; import org.dromara.project.domain.vo.projectteam.BusProjectTeamVo;
import org.dromara.project.domain.vo.projectteam.TeamManageVo; import org.dromara.project.domain.vo.projectteam.TeamManageVo;
import org.dromara.project.service.IBusProjectTeamService; import org.dromara.project.service.IBusProjectTeamService;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysUserService;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
@ -32,6 +37,12 @@ public class BusProjectTeamAppController extends BaseController {
@Resource @Resource
private IBusProjectTeamService projectTeamService; private IBusProjectTeamService projectTeamService;
@Resource
private ISysUserService userService;
@Resource
private ISubContractorService contractorService;
/** /**
* 根据id查询项目班组班组长信息列表 * 根据id查询项目班组班组长信息列表
*/ */
@ -76,4 +87,21 @@ public class BusProjectTeamAppController extends BaseController {
return R.ok(projectTeamService.getManager(teamId)); return R.ok(projectTeamService.getManager(teamId));
} }
/**
* 查询分包单位列表
*/
@GetMapping("/contractorList")
public R<List<SubContractorVo>> contractorList(SubContractorQueryReq req) {
return R.ok(contractorService.queryList(req));
}
/**
* 获取所有分包管理人员
*/
@GetMapping("/listSubContractor")
public R<List<SysUserVo>> listSubContractor(Long projectId, Long contractorId) {
return R.ok(userService.selectUserListByAppUserType("2",projectId,contractorId));
}
} }

View File

@ -0,0 +1,57 @@
package org.dromara.rabbitmq.config;
import jakarta.annotation.Resource;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author xbhog
*/
@Configuration
public class RabbitConfig {
@Resource
private RabbitProperties rabbitProperties;
/**
* 创建交换机
* ExchangeBuilder有四种交换机模式
* Direct Exchange直连交换机根据Routing Key(路由键)进行投递到不同队列。
* Fanout Exchange扇形交换机采用广播模式根据绑定的交换机路由到与之对应的所有队列。
* Topic Exchange主题交换机对路由键进行模式匹配后进行投递符号#表示一个或多个词,*表示一个词。
* Header Exchange头交换机不处理路由键。而是根据发送的消息内容中的headers属性进行匹配。
* durable 交换器是否持久化false 不持久化true 持久化)
**/
@Bean
public TopicExchange exchange() {
return new TopicExchange(rabbitProperties.getExchangeName());
}
/**
* 创建队列
* durable 队列是否持久化 队列调用此方法就是持久化 可查看方法的源码
* deliveryMode 消息是否持久化1 不持久化2 持久化)
**/
@Bean
public Queue queue() {
return new Queue(rabbitProperties.getQueueName(), false);
}
/**
* 绑定交换机和队列
* bing 方法参数可以是队列和交换机
* to 方法参数必须是交换机
* with 方法参数是路由Key 这里是以rabbit.开头
* noargs 就是不要参数的意思
* 这个方法的意思是把rabbit开头的消息 和 上面的队列 和 上面的交换机绑定
**/
@Bean
public Binding binding(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(rabbitProperties.getRoutingKey());
}
}

View File

@ -0,0 +1,60 @@
package org.dromara.rabbitmq.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @author lilemy
* @date 2025-12-05 16:25
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "rabbitmq")
public class RabbitProperties {
/**
* 交换机名称
*/
private String exchangeName;
/**
* 队列名称
*/
private String queueName;
/**
* 路由键名称
*/
private String routingKey;
/**
* 延迟交换机名称
*/
private String delayExchangeName;
/**
* 延迟队列名称
*/
private String delayQueueName;
/**
* 延迟路由键名称
*/
private String delayRoutingKey;
/**
* 死信交换机名称
*/
private String deadLetterExchange;
/**
* 死信队列名称
*/
private String deadLetterQueue;
/**
* 死信路由键名称
*/
private String deadLetterRoutingKey;
}

View File

@ -0,0 +1,75 @@
package org.dromara.rabbitmq.config;
import jakarta.annotation.Resource;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
/**
* RabbitTTL队列
*
* @author xbhog
*/
@Configuration
public class RabbitTtlQueueConfig {
@Resource
private RabbitProperties rabbitProperties;
/**
* 声明延迟队列
*/
@Bean
public Queue delayQueue() {
return QueueBuilder.durable(rabbitProperties.getDelayQueueName())
.deadLetterExchange(rabbitProperties.getDeadLetterExchange())
.deadLetterRoutingKey(rabbitProperties.getDeadLetterRoutingKey())
.build();
}
/**
* 声明延迟交换机
*/
@Bean
public CustomExchange delayExchange() {
return new CustomExchange(rabbitProperties.getDelayExchangeName(), "x-delayed-message",
true, false, Map.of("x-delayed-type", "direct"));
}
/**
* 将延迟队列绑定到延迟交换机
*/
@Bean
public Binding delayBinding(Queue delayQueue, CustomExchange delayExchange) {
return BindingBuilder.bind(delayQueue).to(delayExchange).with(rabbitProperties.getDelayRoutingKey()).noargs();
}
/**
* 声明死信队列
*/
@Bean
public Queue deadLetterQueue() {
return new Queue(rabbitProperties.getDeadLetterQueue());
}
/**
* 声明死信交换机
*/
@Bean
public DirectExchange deadLetterExchange() {
return new DirectExchange(rabbitProperties.getDeadLetterExchange());
}
/**
* 将死信队列绑定到死信交换机
*/
@Bean
public Binding deadLetterBinding(Queue deadLetterQueue, DirectExchange deadLetterExchange) {
return BindingBuilder.bind(deadLetterQueue).to(deadLetterExchange).with(rabbitProperties.getDeadLetterRoutingKey());
}
}

View File

@ -0,0 +1,65 @@
package org.dromara.rabbitmq.consumer;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.rabbitmq.service.IMqDelayTaskService;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @author lilemy
* @date 2025-12-05 14:09
*/
@Slf4j
@Component
public class RabbitConsumer {
@Resource
private IMqDelayTaskService mqDelayTaskService;
/**
* 普通消息
*/
@RabbitListener(queues = "${rabbitmq.queue-name}")
public void listenQueue(Message message) {
log.info("【消费者】Start consuming data{}", new String(message.getBody()));
}
/**
* 处理延迟队列消息
*/
@RabbitListener(queues = "${rabbitmq.delay-queue-name}")
public void receiveDelayMessage(String message) {
log.info("【消费者】Received delayed message{}", message);
if (StringUtils.isNotBlank(message) && isLong(message)) {
mqDelayTaskService.executeTask(Long.parseLong(message));
}
}
/**
* 处理死信队列消息
* 当消息在延迟队列中未能被正确处理例如因消费者逻辑错误、超时未ACK等原因
* 它会被自动转发到死信队列中,以便后续的特殊处理或重新尝试。
*/
@RabbitListener(queues = "${rabbitmq.dead-letter-queue}")
public void receiveDeadMessage(String message) {
log.info("【消费者】Received dead message{}", message);
}
/**
* 判断字符串是否为Long
*
* @param str 字符串
* @return 是否为Long
*/
private static boolean isLong(String str) {
try {
Long.parseLong(str);
return true;
} catch (Exception e) {
return false;
}
}
}

View File

@ -0,0 +1,66 @@
package org.dromara.rabbitmq.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 延迟任务对象 mq_delay_task
*
* @author lilemy
* @date 2025-12-05
*/
@Data
@TableName("mq_delay_task")
public class MqDelayTask implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "id")
private Long id;
/**
* 业务类型
*/
private String bizType;
/**
* 业务ID
*/
private Long bizId;
/**
* 执行的时间点
*/
private LocalDateTime executeTime;
/**
* 任务状态 0未执行 1执行中 2执行成功 3执行失败
*/
private Integer status;
/**
* 重试次数
*/
private Integer retryCount;
/**
* 最大重试次数
*/
private Integer maxRetry;
/**
* 失败原因
*/
private String failReason;
}

View File

@ -0,0 +1,76 @@
package org.dromara.rabbitmq.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.rabbitmq.domain.MqDelayTask;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 延迟任务视图对象 mq_delay_task
*
* @author lilemy
* @date 2025-12-05
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = MqDelayTask.class)
public class MqDelayTaskVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@ExcelProperty(value = "主键")
private Long id;
/**
* 业务类型
*/
@ExcelProperty(value = "业务类型")
private String bizType;
/**
* 业务ID
*/
@ExcelProperty(value = "业务ID")
private Long bizId;
/**
* 执行的时间点
*/
@ExcelProperty(value = "执行的时间点")
private LocalDateTime executeTime;
/**
* 任务状态 0未执行 1执行中 2执行成功 3执行失败
*/
@ExcelProperty(value = "任务状态 0未执行 1执行中 2执行成功 3执行失败")
private Integer status;
/**
* 重试次数
*/
@ExcelProperty(value = "重试次数")
private Integer retryCount;
/**
* 最大重试次数
*/
@ExcelProperty(value = "最大重试次数")
private Integer maxRetry;
/**
* 失败原因
*/
@ExcelProperty(value = "失败原因")
private String failReason;
}

View File

@ -0,0 +1,35 @@
package org.dromara.rabbitmq.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author lilemy
* @date 2025-12-05 18:26
*/
@Getter
@AllArgsConstructor
public enum MqDelayTaskTypeEnum {
/**
* 安全隐患
*/
HIDDEN_DANGER("aqyh");
private final String type;
/**
* 根据业务类型获取枚举
*
* @param type 业务类型
* @return 枚举
*/
public static MqDelayTaskTypeEnum getByType(String type) {
for (MqDelayTaskTypeEnum value : values()) {
if (value.getType().equals(type)) {
return value;
}
}
return null;
}
}

View File

@ -0,0 +1,15 @@
package org.dromara.rabbitmq.mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.rabbitmq.domain.MqDelayTask;
import org.dromara.rabbitmq.domain.vo.MqDelayTaskVo;
/**
* 延迟任务Mapper接口
*
* @author lilemy
* @date 2025-12-05
*/
public interface MqDelayTaskMapper extends BaseMapperPlus<MqDelayTask, MqDelayTaskVo> {
}

View File

@ -0,0 +1,51 @@
package org.dromara.rabbitmq.producer;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.dromara.rabbitmq.config.RabbitProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;
/**
* @author lilemy
* @date 2025-12-05 11:40
*/
@Slf4j
@Component
public class RabbitProducer {
@Resource
private RabbitTemplate rabbitTemplate;
@Resource
private RabbitProperties rabbitProperties;
/**
* 发送消息
*
* @param message 消息
*/
public void send(String message) {
rabbitTemplate.convertAndSend(rabbitProperties.getExchangeName(), rabbitProperties.getRoutingKey(), message);
log.info("【生产者】Message send: {}", message);
}
/**
* 发送延迟消息
*
* @param message 消息
* @param delayMs 延迟时间(毫秒)
*/
public void sendDelayMessage(String message, long delayMs) {
rabbitTemplate.convertAndSend(
rabbitProperties.getDelayExchangeName(),
rabbitProperties.getDelayRoutingKey(),
message,
message1 -> {
message1.getMessageProperties().setDelayLong(delayMs);
return message1;
}
);
log.info("【生产者】Delay Message send: {} delay: {}ms", message, delayMs);
}
}

View File

@ -0,0 +1,27 @@
package org.dromara.rabbitmq.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.rabbitmq.domain.MqDelayTask;
/**
* 延迟任务Service接口
*
* @author lilemy
* @date 2025-12-05
*/
public interface IMqDelayTaskService extends IService<MqDelayTask> {
/**
* 添加延迟任务
*
* @param task 延迟任务
*/
void addDelayTask(MqDelayTask task);
/**
* 执行任务
*
* @param id 主键id
*/
void executeTask(Long id);
}

View File

@ -0,0 +1,114 @@
package org.dromara.rabbitmq.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.rabbitmq.domain.MqDelayTask;
import org.dromara.rabbitmq.enums.MqDelayTaskTypeEnum;
import org.dromara.rabbitmq.mapper.MqDelayTaskMapper;
import org.dromara.rabbitmq.producer.RabbitProducer;
import org.dromara.rabbitmq.service.IMqDelayTaskService;
import org.dromara.safety.service.IHazardHiddenDangerRectifyService;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
/**
* 延迟任务Service业务层处理
*
* @author lilemy
* @date 2025-12-05
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class MqDelayTaskServiceImpl extends ServiceImpl<MqDelayTaskMapper, MqDelayTask>
implements IMqDelayTaskService {
@Resource
private RabbitProducer rabbitProducer;
@Lazy
@Resource
private IHazardHiddenDangerRectifyService hazardHiddenDangerRectifyService;
/**
* 添加延迟任务
*
* @param task 延迟任务
*/
@Override
public void addDelayTask(MqDelayTask task) {
String bizType = task.getBizType();
Long bizId = task.getBizId();
LocalDateTime executeTime = task.getExecuteTime();
if (StringUtils.isBlank(bizType) || bizId == null || executeTime == null) {
throw new ServiceException("参数错误");
}
// 保存延迟任务
boolean save = this.save(task);
if (!save) {
throw new ServiceException("添加延迟任务失败");
}
// 计算超时时间
ZoneId zone = ZoneId.systemDefault();
long diffMillis = Duration.between(
LocalDateTime.now().atZone(zone),
executeTime.atZone(zone)
).toMillis();
if (diffMillis <= 0) {
throw new ServiceException("延迟时间不能小于当前时间");
}
// 添加延迟任务
rabbitProducer.sendDelayMessage(String.valueOf(task.getId()), diffMillis);
}
/**
* 执行任务
*
* @param id 主键id
*/
@Override
public void executeTask(Long id) {
MqDelayTask task = this.getById(id);
if (task == null) {
return;
}
if (task.getStatus() == 1 || task.getStatus() == 2) {
return;
}
try {
// 执行中
task.setStatus(1);
this.updateById(task);
// 业务逻辑:发送通知
String bizType = task.getBizType();
MqDelayTaskTypeEnum type = MqDelayTaskTypeEnum.getByType(bizType);
switch (type) {
case HIDDEN_DANGER -> hazardHiddenDangerRectifyService.sendTimeoutNotify(task.getBizId());
case null, default -> {
}
}
// 成功
task.setStatus(2);
this.updateById(task);
} catch (Exception e) {
// 更新失败状态
task.setRetryCount(task.getRetryCount() + 1);
task.setFailReason(e.getMessage());
task.setStatus(3);
this.updateById(task);
// 重试机制
if (task.getRetryCount() < task.getMaxRetry()) {
// 1分钟后重试
rabbitProducer.sendDelayMessage(String.valueOf(id), 60 * 1000);
}
}
}
}

View File

@ -87,4 +87,11 @@ public interface IHazardHiddenDangerRectifyService extends IService<HazardHidden
* @return 是否删除成功 * @return 是否删除成功
*/ */
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid); Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 发送超时通知
*
* @param bizId 业务id
*/
void sendTimeoutNotify(Long bizId);
} }

View File

@ -6,6 +6,7 @@ import org.dromara.safety.domain.vo.HazardRuleNotifyObjectVo;
import org.dromara.safety.domain.vo.HazardRuleVo; import org.dromara.safety.domain.vo.HazardRuleVo;
import java.util.List; import java.util.List;
import java.util.Set;
/** /**
* 隐患规则通知对象Service接口 * 隐患规则通知对象Service接口
@ -31,6 +32,15 @@ public interface IHazardRuleNotifyObjectService extends IService<HazardRuleNotif
*/ */
List<HazardRuleNotifyObjectVo> queryVo(List<HazardRuleNotifyObject> objectList); List<HazardRuleNotifyObjectVo> queryVo(List<HazardRuleNotifyObject> objectList);
/**
* 根据规则查询所有通知对象id
*
* @param ruleId 规则id
* @param projectId 项目id
* @return 通知对象id
*/
Set<Long> queryNotifyObjectIds(Long ruleId, Long projectId);
/** /**
* 补充数据 * 补充数据
* *

View File

@ -5,12 +5,15 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.sse.dto.SseMessageDto;
import org.dromara.common.sse.utils.SseMessageUtils;
import org.dromara.safety.domain.HazardHiddenDanger; import org.dromara.safety.domain.HazardHiddenDanger;
import org.dromara.safety.domain.HazardHiddenDangerRectify; import org.dromara.safety.domain.HazardHiddenDangerRectify;
import org.dromara.safety.domain.bo.HazardHiddenDangerBo; import org.dromara.safety.domain.bo.HazardHiddenDangerBo;
@ -24,6 +27,8 @@ import org.dromara.safety.domain.vo.RectifyTimesVo;
import org.dromara.safety.mapper.HazardHiddenDangerRectifyMapper; import org.dromara.safety.mapper.HazardHiddenDangerRectifyMapper;
import org.dromara.safety.service.IHazardHiddenDangerRectifyService; import org.dromara.safety.service.IHazardHiddenDangerRectifyService;
import org.dromara.safety.service.IHazardHiddenDangerService; import org.dromara.safety.service.IHazardHiddenDangerService;
import org.dromara.safety.service.IHazardRuleNotifyObjectService;
import org.dromara.websocket.ChatServerHandler;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -32,6 +37,7 @@ import java.time.LocalDateTime;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
* 隐患整改情况Service业务层处理 * 隐患整改情况Service业务层处理
@ -39,6 +45,7 @@ import java.util.Map;
* @author lilemy * @author lilemy
* @date 2025-12-04 * @date 2025-12-04
*/ */
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Service @Service
public class HazardHiddenDangerRectifyServiceImpl extends ServiceImpl<HazardHiddenDangerRectifyMapper, HazardHiddenDangerRectify> public class HazardHiddenDangerRectifyServiceImpl extends ServiceImpl<HazardHiddenDangerRectifyMapper, HazardHiddenDangerRectify>
@ -46,6 +53,10 @@ public class HazardHiddenDangerRectifyServiceImpl extends ServiceImpl<HazardHidd
private final IHazardHiddenDangerService hazardHiddenDangerService; private final IHazardHiddenDangerService hazardHiddenDangerService;
private final ChatServerHandler chatServerHandler;
private final IHazardRuleNotifyObjectService hazardRuleNotifyObjectService;
/** /**
* 查询隐患整改情况 * 查询隐患整改情况
* *
@ -297,4 +308,48 @@ public class HazardHiddenDangerRectifyServiceImpl extends ServiceImpl<HazardHidd
} }
return baseMapper.deleteByIds(ids) > 0; return baseMapper.deleteByIds(ids) > 0;
} }
/**
* 发送超时通知
*
* @param bizId 业务id
*/
@Override
public void sendTimeoutNotify(Long bizId) {
HazardHiddenDanger hiddenDanger = hazardHiddenDangerService.getById(bizId);
if (hiddenDanger == null) {
throw new ServiceException("未找到该数据");
}
if (!HazardHiddenDanger.RECTIFY.equals(hiddenDanger.getStatus())) {
return;
}
// 发送消息
String dangerCode = hiddenDanger.getDangerCode();
Long rectifyUserId = hiddenDanger.getRectifyUserId();
Long projectId = hiddenDanger.getProjectId();
String titleRectify = "您的安全隐患工单[" + dangerCode + "]已超时,请及时处理!";
try {
chatServerHandler.sendSystemMessageToUser(rectifyUserId, titleRectify, "2");
SseMessageUtils.sendMessage(rectifyUserId, titleRectify);
} catch (Exception e) {
log.error("异步发送系统消息失败用户ID: {}, 消息: {}", rectifyUserId, titleRectify, e);
}
// 发送给需要通知的对象
Set<Long> userIds = hazardRuleNotifyObjectService.queryNotifyObjectIds(hiddenDanger.getDangerLevelId(), projectId);
SseMessageDto sseDto = new SseMessageDto();
String title = "安全隐患工单[" + dangerCode + "]未进行整改,请及时关注!";
for (Long userId : userIds) {
try {
chatServerHandler.sendSystemMessageToUser(userId, title, "2");
} catch (Exception e) {
log.error("异步发送系统消息失败用户ID: {}, 消息: {}", userId, title, e);
}
}
sseDto.setUserIds(userIds.stream().toList());
sseDto.setMessage(title);
sseDto.setRoute("");
sseDto.setProjectId(projectId);
sseDto.setIsRecord(true);
SseMessageUtils.publishMessage(sseDto);
}
} }

View File

@ -8,11 +8,17 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.sse.dto.SseMessageDto;
import org.dromara.common.sse.utils.SseMessageUtils;
import org.dromara.rabbitmq.domain.MqDelayTask;
import org.dromara.rabbitmq.enums.MqDelayTaskTypeEnum;
import org.dromara.rabbitmq.service.IMqDelayTaskService;
import org.dromara.safety.domain.HazardHiddenDanger; import org.dromara.safety.domain.HazardHiddenDanger;
import org.dromara.safety.domain.HazardHiddenDangerRectify; import org.dromara.safety.domain.HazardHiddenDangerRectify;
import org.dromara.safety.domain.HazardRule; import org.dromara.safety.domain.HazardRule;
@ -24,7 +30,9 @@ import org.dromara.safety.domain.vo.HiddenDangerCountVo;
import org.dromara.safety.mapper.HazardHiddenDangerMapper; import org.dromara.safety.mapper.HazardHiddenDangerMapper;
import org.dromara.safety.mapper.HazardHiddenDangerRectifyMapper; import org.dromara.safety.mapper.HazardHiddenDangerRectifyMapper;
import org.dromara.safety.service.IHazardHiddenDangerService; import org.dromara.safety.service.IHazardHiddenDangerService;
import org.dromara.safety.service.IHazardRuleNotifyObjectService;
import org.dromara.safety.service.IHazardRuleService; import org.dromara.safety.service.IHazardRuleService;
import org.dromara.websocket.ChatServerHandler;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -32,6 +40,8 @@ import java.time.LocalDateTime;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
/** /**
* 隐患信息Service业务层处理 * 隐患信息Service业务层处理
@ -39,6 +49,7 @@ import java.util.Map;
* @author Lion Li * @author Lion Li
* @date 2025-12-03 * @date 2025-12-03
*/ */
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Service @Service
public class HazardHiddenDangerServiceImpl extends ServiceImpl<HazardHiddenDangerMapper, HazardHiddenDanger> public class HazardHiddenDangerServiceImpl extends ServiceImpl<HazardHiddenDangerMapper, HazardHiddenDanger>
@ -50,6 +61,12 @@ public class HazardHiddenDangerServiceImpl extends ServiceImpl<HazardHiddenDange
private final HazardHiddenDangerRectifyMapper hazardHiddenDangerRectifyMapper; private final HazardHiddenDangerRectifyMapper hazardHiddenDangerRectifyMapper;
private final IHazardRuleNotifyObjectService hazardRuleNotifyObjectService;
private final ChatServerHandler chatServerHandler;
private final IMqDelayTaskService mqDelayTaskService;
/** /**
* 查询隐患信息 * 查询隐患信息
* *
@ -225,20 +242,65 @@ public class HazardHiddenDangerServiceImpl extends ServiceImpl<HazardHiddenDange
} else if (responseUnit.equals("1")) { } else if (responseUnit.equals("1")) {
hazardHiddenDanger.setRectifyTime(now.plusMinutes(byId.getResponseTime())); hazardHiddenDanger.setRectifyTime(now.plusMinutes(byId.getResponseTime()));
} }
LocalDateTime rectifyTime = hazardHiddenDanger.getRectifyTime();
if (rectifyTime.isBefore(LocalDateTime.now())) {
throw new ServiceException("整改期限不能小于当前时间");
}
// 修改数据 // 修改数据
if (baseMapper.updateById(hazardHiddenDanger) <= 0) { if (baseMapper.updateById(hazardHiddenDanger) <= 0) {
throw new ServiceException("数据修改失败"); throw new ServiceException("数据修改失败");
} }
// 添加整改任务 // 添加整改任务
Long rectifyUserId = hazardHiddenDanger.getRectifyUserId();
HazardHiddenDangerRectify rectify = new HazardHiddenDangerRectify(); HazardHiddenDangerRectify rectify = new HazardHiddenDangerRectify();
rectify.setHiddenDangerId(dto.getId()); rectify.setHiddenDangerId(dto.getId());
rectify.setRectifyDeadline(hazardHiddenDanger.getRectifyTime()); rectify.setRectifyDeadline(rectifyTime);
rectify.setRectifyUserId(hazardHiddenDanger.getRectifyUserId()); rectify.setRectifyUserId(rectifyUserId);
rectify.setReviewUserId(hazardHiddenDanger.getEvaluator()); rectify.setReviewUserId(hazardHiddenDanger.getEvaluator());
rectify.setRectifyCount(1); rectify.setRectifyCount(1);
if (hazardHiddenDangerRectifyMapper.insert(rectify) <= 0) { if (hazardHiddenDangerRectifyMapper.insert(rectify) <= 0) {
throw new ServiceException("数据保存失败"); throw new ServiceException("数据保存失败");
} }
Long projectId = hazardHiddenDanger.getProjectId();
String dangerCode = hazardHiddenDanger.getDangerCode();
// 通知对应人员
CompletableFuture.runAsync(() -> {
// 发送给整改人员
String titleRectify = "您有新的安全隐患工单[" + dangerCode + "]需要整改,请及时处理!";
try {
chatServerHandler.sendSystemMessageToUser(rectifyUserId, titleRectify, "2");
SseMessageUtils.sendMessage(rectifyUserId, titleRectify);
} catch (Exception e) {
log.error("异步发送系统消息失败用户ID: {}, 消息: {}", rectifyUserId, titleRectify, e);
}
// 发送给需要通知的对象
Set<Long> userIds = hazardRuleNotifyObjectService.queryNotifyObjectIds(hazardHiddenDanger.getDangerLevelId(), projectId);
SseMessageDto sseDto = new SseMessageDto();
String title = "您有新的安全隐患工单[" + dangerCode + "],请及时查看!";
for (Long userId : userIds) {
try {
chatServerHandler.sendSystemMessageToUser(userId, title, "2");
} catch (Exception e) {
log.error("异步发送系统消息失败用户ID: {}, 消息: {}", userId, title, e);
}
}
sseDto.setUserIds(userIds.stream().toList());
sseDto.setMessage(title);
sseDto.setRoute("");
sseDto.setProjectId(projectId);
sseDto.setIsRecord(true);
SseMessageUtils.publishMessage(sseDto);
// 发送整改期限数据到消息队列
MqDelayTask task = new MqDelayTask();
task.setBizType(MqDelayTaskTypeEnum.HIDDEN_DANGER.getType());
task.setBizId(dto.getId());
task.setExecuteTime(rectifyTime);
try {
mqDelayTaskService.addDelayTask(task);
} catch (Exception e) {
log.error("添加延迟任务失败,延迟任务: {}", task, e);
}
});
return true; return true;
} }
} }

View File

@ -8,6 +8,7 @@ import org.dromara.safety.domain.vo.HazardRuleNotifyObjectVo;
import org.dromara.safety.domain.vo.HazardRuleVo; import org.dromara.safety.domain.vo.HazardRuleVo;
import org.dromara.safety.mapper.HazardRuleNotifyObjectMapper; import org.dromara.safety.mapper.HazardRuleNotifyObjectMapper;
import org.dromara.safety.service.IHazardRuleNotifyObjectService; import org.dromara.safety.service.IHazardRuleNotifyObjectService;
import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.vo.SysDeptVo; import org.dromara.system.domain.vo.SysDeptVo;
import org.dromara.system.domain.vo.SysPostVo; import org.dromara.system.domain.vo.SysPostVo;
import org.dromara.system.domain.vo.SysRoleVo; import org.dromara.system.domain.vo.SysRoleVo;
@ -18,9 +19,7 @@ import org.dromara.system.service.ISysRoleService;
import org.dromara.system.service.ISysUserService; import org.dromara.system.service.ISysUserService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -135,6 +134,52 @@ public class HazardRuleNotifyObjectServiceImpl extends ServiceImpl<HazardRuleNot
return voList; return voList;
} }
/**
* 根据规则查询所有通知对象id
*
* @param ruleId 规则id
* @param projectId 项目id
* @return 通知对象id
*/
@Override
public Set<Long> queryNotifyObjectIds(Long ruleId, Long projectId) {
List<HazardRuleNotifyObject> objectList = this.lambdaQuery()
.eq(HazardRuleNotifyObject::getRuleId, ruleId)
.list();
if (CollUtil.isEmpty(objectList)) {
return Collections.emptySet();
}
Set<Long> userIds = new HashSet<>();
Map<String, List<HazardRuleNotifyObject>> map = objectList.stream()
.collect(Collectors.groupingBy(HazardRuleNotifyObject::getNotifyType));
for (Map.Entry<String, List<HazardRuleNotifyObject>> entry : map.entrySet()) {
String key = entry.getKey();
List<HazardRuleNotifyObject> value = entry.getValue();
List<Long> ids = value.stream()
.map(HazardRuleNotifyObject::getNotifyId)
.toList();
switch (key) {
case "1" -> userIds.addAll(ids);
case "2" -> {
List<SysUser> userVos = userService.selectUserByRoleIdsAndProjectId(ids, projectId);
Set<Long> userSet = userVos.stream().map(SysUser::getUserId).collect(Collectors.toSet());
userIds.addAll(userSet);
}
case "3" -> {
List<SysUserVo> userVos = userService.selectUserListByDeptList(ids);
Set<Long> userSet = userVos.stream().map(SysUserVo::getUserId).collect(Collectors.toSet());
userIds.addAll(userSet);
}
case "4" -> {
List<SysUserVo> userVos = userService.selectUserListByPostList(ids);
Set<Long> userSet = userVos.stream().map(SysUserVo::getUserId).collect(Collectors.toSet());
userIds.addAll(userSet);
}
}
}
return userIds;
}
/** /**
* 补充数据 * 补充数据
* *

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.core.constant.HttpStatus; import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
@ -11,6 +12,7 @@ import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.safety.domain.HazardHiddenDanger;
import org.dromara.safety.domain.HazardRule; import org.dromara.safety.domain.HazardRule;
import org.dromara.safety.domain.HazardRuleNotifyObject; import org.dromara.safety.domain.HazardRuleNotifyObject;
import org.dromara.safety.domain.bo.HazardRuleBo; import org.dromara.safety.domain.bo.HazardRuleBo;
@ -18,8 +20,10 @@ import org.dromara.safety.domain.bo.HazardRuleNotifyObjectBo;
import org.dromara.safety.domain.vo.HazardRuleNotifyObjectVo; import org.dromara.safety.domain.vo.HazardRuleNotifyObjectVo;
import org.dromara.safety.domain.vo.HazardRuleVo; import org.dromara.safety.domain.vo.HazardRuleVo;
import org.dromara.safety.mapper.HazardRuleMapper; import org.dromara.safety.mapper.HazardRuleMapper;
import org.dromara.safety.service.IHazardHiddenDangerService;
import org.dromara.safety.service.IHazardRuleNotifyObjectService; import org.dromara.safety.service.IHazardRuleNotifyObjectService;
import org.dromara.safety.service.IHazardRuleService; import org.dromara.safety.service.IHazardRuleService;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -39,6 +43,10 @@ public class HazardRuleServiceImpl extends ServiceImpl<HazardRuleMapper, HazardR
private final IHazardRuleNotifyObjectService hazardRuleNotifyObjectService; private final IHazardRuleNotifyObjectService hazardRuleNotifyObjectService;
@Lazy
@Resource
private IHazardHiddenDangerService hazardHiddenDangerService;
/** /**
* 查询隐患分级通知规则 * 查询隐患分级通知规则
* *
@ -84,7 +92,7 @@ public class HazardRuleServiceImpl extends ServiceImpl<HazardRuleMapper, HazardR
LambdaQueryWrapper<HazardRule> lqw = Wrappers.lambdaQuery(); LambdaQueryWrapper<HazardRule> lqw = Wrappers.lambdaQuery();
lqw.orderByDesc(HazardRule::getId); lqw.orderByDesc(HazardRule::getId);
lqw.eq(bo.getProjectId() != null, HazardRule::getProjectId, bo.getProjectId()); lqw.eq(bo.getProjectId() != null, HazardRule::getProjectId, bo.getProjectId());
lqw.eq(StringUtils.isNotBlank(bo.getHazardLevel()), HazardRule::getHazardLevel, bo.getHazardLevel()); lqw.like(StringUtils.isNotBlank(bo.getHazardLevel()), HazardRule::getHazardLevel, bo.getHazardLevel());
lqw.eq(bo.getHazardWeight() != null, HazardRule::getHazardWeight, bo.getHazardWeight()); lqw.eq(bo.getHazardWeight() != null, HazardRule::getHazardWeight, bo.getHazardWeight());
lqw.eq(bo.getResponseTime() != null, HazardRule::getResponseTime, bo.getResponseTime()); lqw.eq(bo.getResponseTime() != null, HazardRule::getResponseTime, bo.getResponseTime());
lqw.eq(StringUtils.isNotBlank(bo.getResponseUnit()), HazardRule::getResponseUnit, bo.getResponseUnit()); lqw.eq(StringUtils.isNotBlank(bo.getResponseUnit()), HazardRule::getResponseUnit, bo.getResponseUnit());
@ -202,6 +210,11 @@ public class HazardRuleServiceImpl extends ServiceImpl<HazardRuleMapper, HazardR
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) { public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) { if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验 //TODO 做一些业务上的校验,判断是否需要校验
if (hazardHiddenDangerService.lambdaQuery()
.in(HazardHiddenDanger::getDangerLevelId, ids)
.count() > 0) {
throw new ServiceException("请先删除该等级下的隐患信息", HttpStatus.ERROR);
}
} }
return baseMapper.deleteByIds(ids) > 0; return baseMapper.deleteByIds(ids) > 0;
} }

View File

@ -41,6 +41,11 @@ public class SysRole extends TenantEntity {
*/ */
private String roleKey; private String roleKey;
/**
* 角色标识
*/
private String roleIdentity;
/** /**
* 角色排序 * 角色排序
*/ */

View File

@ -47,6 +47,11 @@ public class SysRoleBo extends BaseEntity {
@Size(min = 0, max = 100, message = "权限字符长度不能超过{max}个字符") @Size(min = 0, max = 100, message = "权限字符长度不能超过{max}个字符")
private String roleKey; private String roleKey;
/**
* 角色身份
*/
private String roleIdentity;
/** /**
* 显示顺序 * 显示顺序
*/ */

View File

@ -17,4 +17,5 @@ public class ProjectRolesItem {
private String dataScope; private String dataScope;
private String status; private String status;
private String isSpecial; private String isSpecial;
private String roleIdentity;
} }

View File

@ -126,4 +126,9 @@ public class SysRoleVo implements Serializable {
* 角色类型 1-web 2-app * 角色类型 1-web 2-app
*/ */
private String roleSource; private String roleSource;
/**
* 角色身份
*/
private String roleIdentity;
} }

View File

@ -248,6 +248,14 @@ public interface ISysUserService {
*/ */
List<SysUserVo> selectUserListByDept(Long deptId); List<SysUserVo> selectUserListByDept(Long deptId);
/**
* 通过部门id列表查询当前部门所有用户
*
* @param deptIds 部门id列表
* @return 列表
*/
List<SysUserVo> selectUserListByDeptList(List<Long> deptIds);
/** /**
* 通过岗位id列表查询岗位所有用户 * 通过岗位id列表查询岗位所有用户
* *

View File

@ -839,6 +839,23 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
return baseMapper.selectVoList(lqw); return baseMapper.selectVoList(lqw);
} }
/**
* 通过部门id列表查询当前部门所有用户
*
* @param deptIds 部门id列表
* @return 列表
*/
@Override
public List<SysUserVo> selectUserListByDeptList(List<Long> deptIds) {
if (CollUtil.isEmpty(deptIds)) {
return List.of();
}
LambdaQueryWrapper<SysUser> lqw = Wrappers.lambdaQuery();
lqw.in(SysUser::getDeptId, deptIds);
lqw.orderByAsc(SysUser::getUserId);
return baseMapper.selectVoList(lqw);
}
/** /**
* 通过岗位id列表查询岗位所有用户 * 通过岗位id列表查询岗位所有用户
* *

View File

@ -333,6 +333,16 @@ public class XzdContractMachineryServiceImpl extends ServiceImpl<XzdContractMach
}); });
iXzdContractAdvanceInfoService.saveBatch(yskx); iXzdContractAdvanceInfoService.saveBatch(yskx);
} }
//印章信息
xzdBusinessChangeService.remove(new LambdaQueryWrapper<XzdBusinessChange>().eq(XzdBusinessChange::getContractChangeId, update.getId()));
if (bo.getSealInfo() != null && !bo.getSealInfo().isEmpty()){
for (XzdBusinessChange sealInfo : bo.getSealInfo()) {
sealInfo.setContractChangeId(update.getId());
sealInfo.setType("13");
}
xzdBusinessChangeService.saveBatch(bo.getSealInfo());
}
/** /**
* 扣款与奖励项 * 扣款与奖励项
*/ */
@ -366,7 +376,6 @@ public class XzdContractMachineryServiceImpl extends ServiceImpl<XzdContractMach
xzdEquipmentLeasingService.saveBatch(bo.getJqlist()); xzdEquipmentLeasingService.saveBatch(bo.getJqlist());
} }
return baseMapper.updateById(update) > 0; return baseMapper.updateById(update) > 0;
} }

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.rabbitmq.mapper.MqDelayTaskMapper">
</mapper>

View File

@ -21,7 +21,8 @@
r.create_time, r.create_time,
r.remark, r.remark,
r.is_special, r.is_special,
r.role_source r.role_source,
r.role_identity
from sys_role r from sys_role r
left join sys_user_role sur on sur.role_id = r.role_id left join sys_user_role sur on sur.role_id = r.role_id
left join sys_user u on u.user_id = sur.user_id left join sys_user u on u.user_id = sur.user_id

View File

@ -2107,15 +2107,15 @@ insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component,
values (1996413868087308295, '隐患整改情况导出', 1996413868087308290, '5', '#', '', 1, 0, 'F', '0', '0', values (1996413868087308295, '隐患整改情况导出', 1996413868087308290, '5', '#', '', 1, 0, 'F', '0', '0',
'safety:hiddenDangerRectify:export', '#', 103, 1, sysdate(), null, null, ''); 'safety:hiddenDangerRectify:export', '#', 103, 1, sysdate(), null, null, '');
DROP TABLE IF EXISTS mq_delay_task;
CREATE TABLE `hazard_delay_task` CREATE TABLE `mq_delay_task`
( (
`id` BIGINT PRIMARY KEY COMMENT '主键', `id` BIGINT PRIMARY KEY COMMENT '主键',
`biz_type` VARCHAR(50) NOT NULL COMMENT '业务类型', `biz_type` VARCHAR(64) NOT NULL COMMENT '业务类型',
`biz_id` BIGINT NOT NULL COMMENT '业务ID', `biz_id` BIGINT NOT NULL COMMENT '业务ID',
`execute_time` DATETIME NOT NULL COMMENT '执行的时间点', `execute_time` DATETIME NOT NULL COMMENT '执行的时间点',
`status` TINYINT NOT NULL DEFAULT 0 COMMENT '任务状态 0未执行 1执行中 2执行成功 3执行失败', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '任务状态 0未执行 1执行中 2执行成功 3执行失败',
`retry_count` INT NOT NULL DEFAULT 0 COMMENT '重试次数', `retry_count` INT NOT NULL DEFAULT 0 COMMENT '重试次数',
`max_retry` INT NOT NULL DEFAULT 3 COMMENT '最大重试次数', `max_retry` INT NOT NULL DEFAULT 3 COMMENT '最大重试次数',
`fail_reason` VARCHAR(1024) NULL COMMENT '失败原因' `fail_reason` VARCHAR(1024) NULL COMMENT '失败原因'
); ) comment '延迟任务表';