车车 3 hónapja
szülő
commit
0811eb397b

+ 53 - 0
yudao-module-iscs/src/main/java/cn/iocoder/yudao/module/iscs/controller/admin/jobticket/TicketOperLogController.java

@@ -0,0 +1,53 @@
+package cn.iocoder.yudao.module.iscs.controller.admin.jobticket;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.iscs.controller.admin.jobticket.vo.TicketOperLogPageReqVO;
+import cn.iocoder.yudao.module.iscs.controller.admin.jobticket.vo.TicketOperLogRespVO;
+import cn.iocoder.yudao.module.iscs.dal.dataobject.jobticket.TicketOperLogDO;
+import cn.iocoder.yudao.module.iscs.service.jobticket.TicketOperLogService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+
+@Tag(name = "管理后台 - 作业操作过程")
+@RestController
+@RequestMapping("/iscs/ticket-oper-log")
+@Validated
+public class TicketOperLogController {
+
+    @Resource
+    private TicketOperLogService ticketOperLogService;
+
+    @GetMapping("/getTicketOperLogPage")
+    @Operation(summary = "获得作业操作过程分页")
+    @PreAuthorize("@ss.hasPermission('iscs:ticket-oper-log:query')")
+    public CommonResult<PageResult<TicketOperLogRespVO>> getTicketOperLogPage(@Valid TicketOperLogPageReqVO pageReqVO) {
+        PageResult<TicketOperLogDO> pageResult = ticketOperLogService.getTicketOperLogPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, TicketOperLogRespVO.class));
+    }
+
+    @GetMapping("/testSocket")
+    @Operation(summary = "测试")
+    public CommonResult<Boolean> testSocket(Long id, String name, String logType) {
+        if (logType.equals("启动作业")) {
+            Boolean b = ticketOperLogService.addLog1(id, name);
+        }
+        if (logType.equals("启动上锁")) {
+            Boolean b = ticketOperLogService.addLog4(id, name);
+        }
+        return success(true);
+    }
+
+
+}

+ 45 - 0
yudao-module-iscs/src/main/java/cn/iocoder/yudao/module/iscs/controller/admin/jobticket/vo/TicketOperLogPageReqVO.java

@@ -0,0 +1,45 @@
+package cn.iocoder.yudao.module.iscs.controller.admin.jobticket.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 作业操作过程分页 Request VO")
+@Data
+public class TicketOperLogPageReqVO extends PageParam {
+
+    @Schema(description = "作业票ID", example = "29092")
+    private Long ticketId;
+
+    @Schema(description = "作业名称", example = "赵六")
+    private String ticketName;
+
+    @Schema(description = "操作类型(0-启动作业 1-操作确认 2-人员变动 3-启动上锁 4-完成上锁 5-启动解锁 6-添加共锁 7-解除共锁 8-完成解锁 9-结束作业)", example = "1")
+    private Integer operationType;
+
+    @Schema(description = "操作时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] operationTime;
+
+    @Schema(description = "操作人id", example = "32330")
+    private Long operationUserId;
+
+    @Schema(description = "操作人姓名", example = "李四")
+    private String operationUserName;
+
+    @Schema(description = "操作内容")
+    private String operationContent;
+
+    @Schema(description = "备注", example = "你说的对")
+    private String remark;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 55 - 0
yudao-module-iscs/src/main/java/cn/iocoder/yudao/module/iscs/controller/admin/jobticket/vo/TicketOperLogRespVO.java

@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.iscs.controller.admin.jobticket.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 作业操作过程 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class TicketOperLogRespVO {
+
+    @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "16702")
+    @ExcelProperty("主键ID")
+    private Long id;
+
+    @Schema(description = "作业票ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "29092")
+    @ExcelProperty("作业票ID")
+    private Long ticketId;
+
+    @Schema(description = "作业名称", example = "赵六")
+    @ExcelProperty("作业名称")
+    private String ticketName;
+
+    @Schema(description = "操作类型(0-启动作业 1-操作确认 2-人员变动 3-启动上锁 4-完成上锁 5-启动解锁 6-添加共锁 7-解除共锁 8-完成解锁 9-结束作业)", example = "1")
+    @ExcelProperty("操作类型(0-启动作业 1-操作确认 2-人员变动 3-启动上锁 4-完成上锁 5-启动解锁 6-添加共锁 7-解除共锁 8-完成解锁 9-结束作业)")
+    private Integer operationType;
+
+    @Schema(description = "操作时间")
+    @ExcelProperty("操作时间")
+    private LocalDateTime operationTime;
+
+    @Schema(description = "操作人id", example = "32330")
+    @ExcelProperty("操作人id")
+    private Long operationUserId;
+
+    @Schema(description = "操作人姓名", example = "李四")
+    @ExcelProperty("操作人姓名")
+    private String operationUserName;
+
+    @Schema(description = "操作内容")
+    @ExcelProperty("操作内容")
+    private String operationContent;
+
+    @Schema(description = "备注", example = "你说的对")
+    @ExcelProperty("备注")
+    private String remark;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

+ 41 - 0
yudao-module-iscs/src/main/java/cn/iocoder/yudao/module/iscs/controller/admin/jobticket/vo/TicketOperLogSaveReqVO.java

@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.module.iscs.controller.admin.jobticket.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 作业操作过程新增/修改 Request VO")
+@Data
+public class TicketOperLogSaveReqVO {
+
+    @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "16702")
+    private Long id;
+
+    @Schema(description = "作业票ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "29092")
+    @NotNull(message = "作业票ID不能为空")
+    private Long ticketId;
+
+    @Schema(description = "作业名称", example = "赵六")
+    private String ticketName;
+
+    @Schema(description = "操作类型(0-启动作业 1-操作确认 2-人员变动 3-启动上锁 4-完成上锁 5-启动解锁 6-添加共锁 7-解除共锁 8-完成解锁 9-结束作业)", example = "1")
+    private Integer operationType;
+
+    @Schema(description = "操作时间")
+    private LocalDateTime operationTime;
+
+    @Schema(description = "操作人id", example = "32330")
+    private Long operationUserId;
+
+    @Schema(description = "操作人姓名", example = "李四")
+    private String operationUserName;
+
+    @Schema(description = "操作内容")
+    private String operationContent;
+
+    @Schema(description = "备注", example = "你说的对")
+    private String remark;
+
+}

+ 65 - 0
yudao-module-iscs/src/main/java/cn/iocoder/yudao/module/iscs/dal/dataobject/jobticket/TicketOperLogDO.java

@@ -0,0 +1,65 @@
+package cn.iocoder.yudao.module.iscs.dal.dataobject.jobticket;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.util.Date;
+
+/**
+ * 作业操作过程 DO
+ *
+ * @author 博士安全
+ */
+@TableName("isc_ticket_oper_log")
+@KeySequence("isc_ticket_oper_log_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class TicketOperLogDO extends BaseDO {
+
+    /**
+     * 主键ID
+     */
+    @TableId
+    private Long id;
+    /**
+     * 作业票ID
+     */
+    private Long ticketId;
+    /**
+     * 作业名称
+     */
+    private String ticketName;
+    /**
+     * 操作类型(0-启动作业 1-操作确认 2-人员变动 3-启动上锁 4-完成上锁 5-启动解锁 6-添加共锁 7-解除共锁 8-完成解锁 9-结束作业)
+     */
+    private Integer operationType;
+    /**
+     * 操作时间
+     */
+    private Date operationTime;
+    /**
+     * 操作人id
+     */
+    private Long operationUserId;
+    /**
+     * 操作人姓名
+     */
+    private String operationUserName;
+    /**
+     * 操作内容
+     */
+    private String operationContent;
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 32 - 0
yudao-module-iscs/src/main/java/cn/iocoder/yudao/module/iscs/dal/mysql/jobticket/TicketOperLogMapper.java

@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.module.iscs.dal.mysql.jobticket;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.iscs.controller.admin.jobticket.vo.TicketOperLogPageReqVO;
+import cn.iocoder.yudao.module.iscs.dal.dataobject.jobticket.TicketOperLogDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 作业操作过程 Mapper
+ *
+ * @author 博士安全
+ */
+@Mapper
+public interface TicketOperLogMapper extends BaseMapperX<TicketOperLogDO> {
+
+    default PageResult<TicketOperLogDO> selectPage(TicketOperLogPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<TicketOperLogDO>()
+                .eqIfPresent(TicketOperLogDO::getTicketId, reqVO.getTicketId())
+                .likeIfPresent(TicketOperLogDO::getTicketName, reqVO.getTicketName())
+                .eqIfPresent(TicketOperLogDO::getOperationType, reqVO.getOperationType())
+                .betweenIfPresent(TicketOperLogDO::getOperationTime, reqVO.getOperationTime())
+                .eqIfPresent(TicketOperLogDO::getOperationUserId, reqVO.getOperationUserId())
+                .likeIfPresent(TicketOperLogDO::getOperationUserName, reqVO.getOperationUserName())
+                .eqIfPresent(TicketOperLogDO::getOperationContent, reqVO.getOperationContent())
+                .eqIfPresent(TicketOperLogDO::getRemark, reqVO.getRemark())
+                .betweenIfPresent(TicketOperLogDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(TicketOperLogDO::getId));
+    }
+
+}

+ 17 - 0
yudao-module-iscs/src/main/java/cn/iocoder/yudao/module/iscs/framework/web/config/WebSocketConfig.java

@@ -0,0 +1,17 @@
+package cn.iocoder.yudao.module.iscs.framework.web.config;
+
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.server.standard.ServerEndpointExporter;
+
+/**
+ * 开启WebSocket
+ */
+@Configuration
+public class WebSocketConfig {
+    @Bean
+    public ServerEndpointExporter serverEndpointExporter(){
+        return new ServerEndpointExporter();
+    }
+}

+ 90 - 0
yudao-module-iscs/src/main/java/cn/iocoder/yudao/module/iscs/service/jobticket/TicketOperLogService.java

@@ -0,0 +1,90 @@
+package cn.iocoder.yudao.module.iscs.service.jobticket;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.iscs.controller.admin.jobticket.vo.TicketOperLogPageReqVO;
+import cn.iocoder.yudao.module.iscs.dal.dataobject.jobticket.TicketOperLogDO;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * 作业操作过程 Service 接口
+ *
+ * @author 博士安全
+ */
+public interface TicketOperLogService extends IService<TicketOperLogDO> {
+
+    /**
+     * 获得作业操作过程分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 作业操作过程分页
+     */
+    PageResult<TicketOperLogDO> getTicketOperLogPage(TicketOperLogPageReqVO pageReqVO);
+
+    /**
+     * 启动作业
+     * @return
+     */
+    Boolean addLog1(Long jobId, String jobName);
+
+    /**
+     * 操作确认
+     * @return
+     */
+    Boolean addLog2(Long jobId, String jobName, String title, Long userId, String userName);
+
+    /**
+     * 人员变动
+     * type 移除 添加
+     * lockType 上锁人 共锁人
+     * @return
+     */
+    Boolean addLog3(Long jobId, String jobName, String type, String lockType, String lockUser);
+
+    /**
+     * 启动上锁
+     * @return
+     */
+    Boolean addLog4(Long jobId, String jobName);
+
+    /**
+     * 完成上锁
+     * @return
+     */
+    Boolean addLog5(Long jobId, String jobName, String pointNames);
+
+    /**
+     * 添加共锁
+     * @return
+     */
+    Boolean addLog6(Long jobId, String jobName, String colockUser);
+    /**
+     * 解除共锁
+     * @return
+     */
+    Boolean addLog7(Long jobId, String jobName, String colockUser);
+
+    /**
+     * 启动解锁
+     * @return
+     */
+    Boolean addLog8(Long jobId, String jobName);
+
+    /**
+     * 完成解锁
+     * @return
+     */
+    Boolean addLog9(Long jobId, String jobName, String pointNames);
+
+    /**
+     * 取消作业
+     * @return
+     */
+    Boolean addLog10(Long jobId, String jobName);
+
+    /**
+     * 结束作业
+     * @return
+     */
+    Boolean addLog11(Long jobId, String jobName);
+
+}

+ 251 - 0
yudao-module-iscs/src/main/java/cn/iocoder/yudao/module/iscs/service/jobticket/TicketOperLogServiceImpl.java

@@ -0,0 +1,251 @@
+package cn.iocoder.yudao.module.iscs.service.jobticket;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.iscs.controller.admin.jobticket.vo.TicketOperLogPageReqVO;
+import cn.iocoder.yudao.module.iscs.dal.dataobject.jobticket.TicketOperLogDO;
+import cn.iocoder.yudao.module.iscs.dal.mysql.jobticket.TicketOperLogMapper;
+import cn.iocoder.yudao.module.iscs.utils.DateUtils;
+import cn.iocoder.yudao.module.iscs.utils.WebSocketJobTicketLog;
+import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
+import cn.iocoder.yudao.module.system.service.user.AdminUserService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.Date;
+
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+/**
+ * 作业操作过程 Service 实现类
+ *
+ * @author 博士安全
+ */
+@Service
+@Validated
+public class TicketOperLogServiceImpl extends ServiceImpl<TicketOperLogMapper, TicketOperLogDO> implements TicketOperLogService {
+
+    @Resource
+    private TicketOperLogMapper ticketOperLogMapper;
+    @Resource
+    private AdminUserService adminUserService;
+
+    @Override
+    public PageResult<TicketOperLogDO> getTicketOperLogPage(TicketOperLogPageReqVO pageReqVO) {
+        return ticketOperLogMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public Boolean addLog1(Long jobId, String jobName) {
+        Date date = new Date();
+        Long userId = getLoginUserId();
+        AdminUserDO user = adminUserService.getUser(userId);
+        String username = user.getNickname();
+        TicketOperLogDO isTicketOperLog = new TicketOperLogDO();
+        isTicketOperLog.setTicketId(jobId);
+        isTicketOperLog.setTicketName(jobName);
+        isTicketOperLog.setOperationType(0);
+        isTicketOperLog.setOperationTime(date);
+        isTicketOperLog.setOperationUserId(userId);
+        isTicketOperLog.setOperationUserName(username);
+        isTicketOperLog.setOperationContent("启动作业:" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, date) + " 作业[" +  jobName + "]已启动。");
+        save(isTicketOperLog);
+        WebSocketJobTicketLog.sendMessage(String.valueOf(jobId), isTicketOperLog.toString());
+        return true;
+    }
+
+    @Override
+    public Boolean addLog2(Long jobId, String jobName, String title, Long userId, String userName) {
+        Date date = new Date();
+        TicketOperLogDO isTicketOperLog = new TicketOperLogDO();
+        isTicketOperLog.setTicketId(jobId);
+        isTicketOperLog.setTicketName(jobName);
+        isTicketOperLog.setOperationType(1);
+        isTicketOperLog.setOperationTime(date);
+        isTicketOperLog.setOperationUserId(userId);
+        isTicketOperLog.setOperationUserName(userName);
+        isTicketOperLog.setOperationContent("执行确认:" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, date) + "[" + userName +"]已确认执行[" +  title + "]。");
+        save(isTicketOperLog);
+        WebSocketJobTicketLog.sendMessage(String.valueOf(jobId), isTicketOperLog.toString());
+        return true;
+    }
+
+    /**
+     * 人员变动
+     * type 0移除 1添加
+     * lockType 0上锁人 1共锁人
+     * @return
+     */
+    @Override
+    public Boolean addLog3(Long jobId, String jobName, String type, String lockType, String lockUser) {
+        Date date = new Date();
+        Long userId = getLoginUserId();
+        AdminUserDO user = adminUserService.getUser(userId);
+        String username = user.getNickname();
+        TicketOperLogDO isTicketOperLog = new TicketOperLogDO();
+        isTicketOperLog.setTicketId(jobId);
+        isTicketOperLog.setTicketName(jobName);
+        isTicketOperLog.setOperationType(2);
+        isTicketOperLog.setOperationTime(date);
+        isTicketOperLog.setOperationUserId(userId);
+        isTicketOperLog.setOperationUserName(username);
+        isTicketOperLog.setOperationContent("⼈员变动:" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, date) + "[" + username +"]已" + type + lockType + "[" +lockUser + "]。");
+        save(isTicketOperLog);
+        WebSocketJobTicketLog.sendMessage(String.valueOf(jobId), isTicketOperLog.toString());
+        return true;
+    }
+
+    @Override
+    public Boolean addLog4(Long jobId, String jobName) {
+        Date date = new Date();
+        Long userId = getLoginUserId();
+        AdminUserDO user = adminUserService.getUser(userId);
+        String username = user.getNickname();
+        TicketOperLogDO isTicketOperLog = new TicketOperLogDO();
+        isTicketOperLog.setTicketId(jobId);
+        isTicketOperLog.setTicketName(jobName);
+        isTicketOperLog.setOperationType(3);
+        isTicketOperLog.setOperationTime(date);
+        isTicketOperLog.setOperationUserId(userId);
+        isTicketOperLog.setOperationUserName(username);
+        isTicketOperLog.setOperationContent("启动上锁:" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, date) + "[" + username +"]启动上锁。");
+        save(isTicketOperLog);
+        WebSocketJobTicketLog.sendMessage(String.valueOf(jobId), isTicketOperLog.toString());
+        return true;
+    }
+
+    @Override
+    public Boolean addLog5(Long jobId, String jobName, String pointNames) {
+        Date date = new Date();
+        Long userId = getLoginUserId();
+        AdminUserDO user = adminUserService.getUser(userId);
+        String username = user.getNickname();
+        TicketOperLogDO isTicketOperLog = new TicketOperLogDO();
+        isTicketOperLog.setTicketId(jobId);
+        isTicketOperLog.setTicketName(jobName);
+        isTicketOperLog.setOperationType(4);
+        isTicketOperLog.setOperationTime(date);
+        isTicketOperLog.setOperationUserId(userId);
+        isTicketOperLog.setOperationUserName(username);
+        isTicketOperLog.setOperationContent("完成上锁:" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, date) + "[" + username +"]完成上锁," + pointNames + "已上锁。");
+        save(isTicketOperLog);
+        WebSocketJobTicketLog.sendMessage(String.valueOf(jobId), isTicketOperLog.toString());
+        return true;
+    }
+
+    @Override
+    public Boolean addLog6(Long jobId, String jobName, String colockUser) {
+        Date date = new Date();
+        Long userId = getLoginUserId();
+        AdminUserDO user = adminUserService.getUser(userId);
+        String username = user.getNickname();
+        TicketOperLogDO isTicketOperLog = new TicketOperLogDO();
+        isTicketOperLog.setTicketId(jobId);
+        isTicketOperLog.setTicketName(jobName);
+        isTicketOperLog.setOperationType(6);
+        isTicketOperLog.setOperationTime(date);
+        isTicketOperLog.setOperationUserId(userId);
+        isTicketOperLog.setOperationUserName(username);
+        isTicketOperLog.setOperationContent("添加共锁:" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, date) + "[" + colockUser + "]已添加共锁。");
+        save(isTicketOperLog);
+        WebSocketJobTicketLog.sendMessage(String.valueOf(jobId), isTicketOperLog.toString());
+        return true;
+    }
+
+    @Override
+    public Boolean addLog7(Long jobId, String jobName, String colockUser) {
+        Date date = new Date();
+        Long userId = getLoginUserId();
+        AdminUserDO user = adminUserService.getUser(userId);
+        String username = user.getNickname();
+        TicketOperLogDO isTicketOperLog = new TicketOperLogDO();
+        isTicketOperLog.setTicketId(jobId);
+        isTicketOperLog.setTicketName(jobName);
+        isTicketOperLog.setOperationType(7);
+        isTicketOperLog.setOperationTime(date);
+        isTicketOperLog.setOperationUserId(userId);
+        isTicketOperLog.setOperationUserName(username);
+        isTicketOperLog.setOperationContent("解除共锁:" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, date) + "[" + colockUser + "]已解除共锁。");
+        save(isTicketOperLog);
+        WebSocketJobTicketLog.sendMessage(String.valueOf(jobId), isTicketOperLog.toString());
+        return true;
+    }
+
+    @Override
+    public Boolean addLog8(Long jobId, String jobName) {
+        Date date = new Date();
+        Long userId = getLoginUserId();
+        AdminUserDO user = adminUserService.getUser(userId);
+        String username = user.getNickname();
+        TicketOperLogDO isTicketOperLog = new TicketOperLogDO();
+        isTicketOperLog.setTicketId(jobId);
+        isTicketOperLog.setTicketName(jobName);
+        isTicketOperLog.setOperationType(5);
+        isTicketOperLog.setOperationTime(date);
+        isTicketOperLog.setOperationUserId(userId);
+        isTicketOperLog.setOperationUserName(username);
+        isTicketOperLog.setOperationContent("启动解锁:" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, date) + "[" + username +"]启动解锁。");
+        save(isTicketOperLog);
+        WebSocketJobTicketLog.sendMessage(String.valueOf(jobId), isTicketOperLog.toString());
+        return true;
+    }
+
+    @Override
+    public Boolean addLog9(Long jobId, String jobName, String pointNames) {
+        Date date = new Date();
+        Long userId = getLoginUserId();
+        AdminUserDO user = adminUserService.getUser(userId);
+        String username = user.getNickname();
+        TicketOperLogDO isTicketOperLog = new TicketOperLogDO();
+        isTicketOperLog.setTicketId(jobId);
+        isTicketOperLog.setTicketName(jobName);
+        isTicketOperLog.setOperationType(8);
+        isTicketOperLog.setOperationTime(date);
+        isTicketOperLog.setOperationUserId(userId);
+        isTicketOperLog.setOperationUserName(username);
+        isTicketOperLog.setOperationContent("完成解锁:" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, date) + "[" + username +"]完成解锁," + pointNames + "已解锁。");
+        save(isTicketOperLog);
+        WebSocketJobTicketLog.sendMessage(String.valueOf(jobId), isTicketOperLog.toString());
+        return true;
+    }
+
+    @Override
+    public Boolean addLog11(Long jobId, String jobName) {
+        Date date = new Date();
+        Long userId = getLoginUserId();
+        AdminUserDO user = adminUserService.getUser(userId);
+        String username = user.getNickname();
+        TicketOperLogDO isTicketOperLog = new TicketOperLogDO();
+        isTicketOperLog.setTicketId(jobId);
+        isTicketOperLog.setTicketName(jobName);
+        isTicketOperLog.setOperationType(9);
+        isTicketOperLog.setOperationTime(date);
+        isTicketOperLog.setOperationUserId(userId);
+        isTicketOperLog.setOperationUserName(username);
+        isTicketOperLog.setOperationContent("结束作业:" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, date) + "[" + username +"]已完成作业[" + jobName + "]。");
+        save(isTicketOperLog);
+        WebSocketJobTicketLog.sendMessage(String.valueOf(jobId), isTicketOperLog.toString());
+        return true;
+    }
+
+    @Override
+    public Boolean addLog10(Long jobId, String jobName) {
+        Date date = new Date();
+        Long userId = getLoginUserId();
+        AdminUserDO user = adminUserService.getUser(userId);
+        String username = user.getNickname();
+        TicketOperLogDO isTicketOperLog = new TicketOperLogDO();
+        isTicketOperLog.setTicketId(jobId);
+        isTicketOperLog.setTicketName(jobName);
+        isTicketOperLog.setOperationType(10);
+        isTicketOperLog.setOperationTime(date);
+        isTicketOperLog.setOperationUserId(userId);
+        isTicketOperLog.setOperationUserName(username);
+        isTicketOperLog.setOperationContent("取消作业:" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, date) + "[" + username +"]已取消作业[" + jobName + "]。");
+        save(isTicketOperLog);
+        WebSocketJobTicketLog.sendMessage(String.valueOf(jobId), isTicketOperLog.toString());
+        return true;
+    }
+
+}

+ 190 - 0
yudao-module-iscs/src/main/java/cn/iocoder/yudao/module/iscs/utils/WebSocketJobTicketLog.java

@@ -0,0 +1,190 @@
+package cn.iocoder.yudao.module.iscs.utils;
+
+
+import jakarta.websocket.*;
+import jakarta.websocket.server.PathParam;
+import jakarta.websocket.server.ServerEndpoint;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+@Component
+@ServerEndpoint("/websocket/jobTicketLog/{code}")
+@Slf4j
+public class WebSocketJobTicketLog {
+    private Session session;
+    private String code;
+    // 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
+    private static int onlineCount = 0;
+    private static CopyOnWriteArraySet<WebSocketJobTicketLog> webSocketSet = new CopyOnWriteArraySet<>();
+    //concurrent包的线程安全set,用来存放每个客户端对应的MyWebSocket对象
+    private static ConcurrentHashMap<String, WebSocketJobTicketLog> webSocketMap2 = new ConcurrentHashMap();
+
+    // 为了保存在线用户信息,在方法中新建一个list存储一下【实际项目依据复杂度,可以存储到数据库或者缓存】
+    private final static List<Session> SESSIONS = Collections.synchronizedList(new ArrayList<>());
+    private Thread heartbeatThread;
+
+    /**
+     * 建立连接
+     *
+     * @param session
+     * @param code
+     */
+    @OnOpen
+    public void onOpen(Session session, @PathParam("code") String code) {
+        this.session = session;
+        this.code = code;
+        webSocketSet.add(this);
+        SESSIONS.add(session);
+        if (webSocketMap2.containsKey(code)) {
+            webSocketMap2.remove(code);
+            webSocketMap2.put(code, this);
+        } else {
+            webSocketMap2.put(code, this);
+            addOnlineCount();
+        }
+        // 心跳机制
+        heartbeatThread = new Thread(() -> {
+            try {
+                sendHeartbeats(session, code);
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        });
+        heartbeatThread.start();
+        log.info("[连接ID:{}] 建立连接, 当前连接数:{}", this.code, webSocketMap2.size());
+        log.info("线程序号{}, 当前存活线程数{}", heartbeatThread.getId(), Thread.activeCount());
+
+    }
+
+    private void sendHeartbeats(Session session, String code) throws IOException {
+        int heartbeatInterval = 60000; // 60 seconds
+        while (true) {
+            try {
+                Thread.sleep(heartbeatInterval);
+                session.getBasicRemote().sendText("heartbeat");
+            } catch (InterruptedException | IOException e) {
+                break;
+            }
+        }
+    }
+
+    /**
+     * 断开连接
+     */
+    @OnClose
+    public void onClose() {
+        webSocketSet.remove(this);
+        if (webSocketMap2.containsKey(code)) {
+            webSocketMap2.remove(code);
+            subOnlineCount();
+        }
+        // 关闭当前线程
+        if (heartbeatThread != null && heartbeatThread.isAlive()) {
+            log.info("关闭线程序号{}, 当前存活线程数{}", heartbeatThread.getId(), Thread.activeCount());
+            heartbeatThread.interrupt(); // Interrupt the heartbeat thread
+            try {
+                heartbeatThread.join(); // Wait for the thread to finish
+            } catch (InterruptedException e) {
+                log.error("Error joining heartbeat thread for connection ID: {}", code, e);
+                log.info("关闭线程序号{}, 当前存活线程数{}", Thread.currentThread().getId(), Thread.activeCount());
+                Thread.currentThread().interrupt(); // Restore the interruption status
+            }
+        }
+        log.info("[连接ID:{}] 断开连接, 当前连接数:{}", code, webSocketMap2.size());
+    }
+
+    /**
+     * 发送错误
+     *
+     * @param session
+     * @param error
+     */
+    @OnError
+    public void onError(Session session, Throwable error) {
+        log.info("[连接ID:{}] 错误原因:{}", this.code, error.getMessage());
+        error.printStackTrace();
+        // 发生错误时,关闭连接
+        // conn.close(500, "连接出错");
+    }
+
+    /**
+     * 收到消息
+     *
+     * @param message
+     */
+    @OnMessage
+    public void onMessage(String message) {
+        // log.info("【websocket消息】收到客户端发来的消息:{}", message);
+        log.info("[连接ID:{}] 收到消息:{}", this.code, message);
+    }
+
+    /**
+     * 发送消息
+     *
+     * @param message
+     * @param code
+     */
+    public static void sendMessage(String code, String message) {
+        WebSocketJobTicketLog webSocketIots = webSocketMap2.get(code);
+        log.info("【websocket消息】推送消息, webSocketServer={}", webSocketIots);
+        if (webSocketIots != null) {
+            log.info("【websocket消息】推送消息, message={}", message);
+            try {
+                webSocketIots.session.getBasicRemote().sendText(message);
+            } catch (Exception e) {
+                e.printStackTrace();
+                log.error("[连接ID:{}] 发送消息失败, 消息:{}", code, message, e);
+            }
+        }
+    }
+
+    /**
+     * 群发消息
+     *
+     * @param message
+     */
+    public static void sendMassMessage(String message) {
+        try {
+            for (Session session : SESSIONS) {
+                if (session.isOpen()) {
+                    session.getBasicRemote().sendText(message);
+                    log.info("[连接ID:{}] 发送消息:{}", session.getRequestParameterMap().get("code"), message);
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 获取当前连接数
+     *
+     * @return
+     */
+    public static synchronized int getOnlineCount() {
+        return onlineCount;
+    }
+
+    /**
+     * 当前连接数加一
+     */
+    public static synchronized void addOnlineCount() {
+        WebSocketJobTicketLog.onlineCount++;
+    }
+
+    /**
+     * 当前连接数减一
+     */
+    public static synchronized void subOnlineCount() {
+        WebSocketJobTicketLog.onlineCount--;
+    }
+
+}
+

+ 12 - 0
yudao-module-iscs/src/main/resources/mapper/TicketOperLogMapper.xml

@@ -0,0 +1,12 @@
+<?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="cn.iocoder.yudao.module.iscs.dal.mysql.jobticket.TicketOperLogMapper">
+
+    <!--
+        一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
+        无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
+        代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
+        文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
+     -->
+
+</mapper>