From 4e1415a03359cec778be5605886dc68628022e1a Mon Sep 17 00:00:00 2001 From: JiaoTianBo Date: Mon, 30 Mar 2026 11:55:49 +0800 Subject: [PATCH] =?UTF-8?q?feat(risk):=20=E5=AE=9E=E7=8E=B0=E9=A3=8E?= =?UTF-8?q?=E9=99=A9=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增RiskMapper,定义风险相关SQL映射和查询功能 - 添加CreateRiskRequest、CreateWorkOrderRequest和ProcessWorkOrderRequest请求DTO - 实现RiskController,支持风险的创建、更新、删除、详细查询及列表分页查询 - 提供风险统计接口,统计风险总数、状态分布和等级分布 - 增加风险分配工单及批量更新状态的接口 - 实现RiskService及其实现类,包含风险的增删改查及业务逻辑 - 计算风险得分和风险等级,并支持标签和工单关联管理 - 定义RiskVO和RiskStatisticsVO用于前端数据展示和统计 - 实现风险工单的创建和管理,关联风险状态同步更新 --- .../controller/risk/RiskController.java | 199 +++++++ .../controller/risk/WorkOrderController.java | 241 ++++++++ .../domain/dto/CreateRiskRequest.java | 79 +++ .../domain/dto/CreateWorkOrderRequest.java | 68 +++ .../domain/dto/ProcessWorkOrderRequest.java | 25 + .../cn/yinlihupo/domain/entity/WorkOrder.java | 146 +++++ .../yinlihupo/domain/vo/RiskStatisticsVO.java | 87 +++ .../java/cn/yinlihupo/domain/vo/RiskVO.java | 146 +++++ .../domain/vo/WorkOrderStatisticsVO.java | 65 +++ .../cn/yinlihupo/domain/vo/WorkOrderVO.java | 154 ++++++ .../cn/yinlihupo/mapper/WorkOrderMapper.java | 57 ++ .../yinlihupo/service/risk/RiskService.java | 87 +++ .../service/risk/WorkOrderService.java | 105 ++++ .../service/risk/impl/RiskServiceImpl.java | 416 ++++++++++++++ .../risk/impl/WorkOrderServiceImpl.java | 515 ++++++++++++++++++ src/main/resources/mapper/RiskMapper.xml | 1 + src/main/resources/mapper/WorkOrderMapper.xml | 163 ++++++ 17 files changed, 2554 insertions(+) create mode 100644 src/main/java/cn/yinlihupo/controller/risk/RiskController.java create mode 100644 src/main/java/cn/yinlihupo/controller/risk/WorkOrderController.java create mode 100644 src/main/java/cn/yinlihupo/domain/dto/CreateRiskRequest.java create mode 100644 src/main/java/cn/yinlihupo/domain/dto/CreateWorkOrderRequest.java create mode 100644 src/main/java/cn/yinlihupo/domain/dto/ProcessWorkOrderRequest.java create mode 100644 src/main/java/cn/yinlihupo/domain/entity/WorkOrder.java create mode 100644 src/main/java/cn/yinlihupo/domain/vo/RiskStatisticsVO.java create mode 100644 src/main/java/cn/yinlihupo/domain/vo/RiskVO.java create mode 100644 src/main/java/cn/yinlihupo/domain/vo/WorkOrderStatisticsVO.java create mode 100644 src/main/java/cn/yinlihupo/domain/vo/WorkOrderVO.java create mode 100644 src/main/java/cn/yinlihupo/mapper/WorkOrderMapper.java create mode 100644 src/main/java/cn/yinlihupo/service/risk/RiskService.java create mode 100644 src/main/java/cn/yinlihupo/service/risk/WorkOrderService.java create mode 100644 src/main/java/cn/yinlihupo/service/risk/impl/RiskServiceImpl.java create mode 100644 src/main/java/cn/yinlihupo/service/risk/impl/WorkOrderServiceImpl.java create mode 100644 src/main/resources/mapper/WorkOrderMapper.xml diff --git a/src/main/java/cn/yinlihupo/controller/risk/RiskController.java b/src/main/java/cn/yinlihupo/controller/risk/RiskController.java new file mode 100644 index 0000000..4239602 --- /dev/null +++ b/src/main/java/cn/yinlihupo/controller/risk/RiskController.java @@ -0,0 +1,199 @@ +package cn.yinlihupo.controller.risk; + +import cn.yinlihupo.common.core.BaseResponse; +import cn.yinlihupo.common.page.TableDataInfo; +import cn.yinlihupo.common.util.ResultUtils; +import cn.yinlihupo.common.util.SecurityUtils; +import cn.yinlihupo.domain.dto.CreateRiskRequest; +import cn.yinlihupo.domain.dto.CreateWorkOrderRequest; +import cn.yinlihupo.domain.vo.RiskStatisticsVO; +import cn.yinlihupo.domain.vo.RiskVO; +import cn.yinlihupo.service.risk.RiskService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 风险管理控制器 + * 提供风险新建、查询、统计、分配工单等功能 + */ +@Slf4j +@RestController +@RequestMapping("/api/v1/risk") +@RequiredArgsConstructor +public class RiskController { + + private final RiskService riskService; + + /** + * 创建风险评估 + * + * @param request 创建请求 + * @return 风险ID + */ + @PostMapping + public BaseResponse createRisk(@RequestBody CreateRiskRequest request) { + log.info("创建风险评估, projectId: {}", request.getProjectId()); + + try { + Long riskId = riskService.createRisk(request); + return ResultUtils.success("创建成功", riskId); + } catch (Exception e) { + log.error("创建风险评估失败: {}", e.getMessage(), e); + return ResultUtils.error("创建失败: " + e.getMessage()); + } + } + + /** + * 更新风险 + * + * @param riskId 风险ID + * @param request 更新请求 + * @return 是否成功 + */ + @PutMapping("/{riskId}") + public BaseResponse updateRisk(@PathVariable Long riskId, @RequestBody CreateRiskRequest request) { + log.info("更新风险, riskId: {}", riskId); + + try { + Boolean result = riskService.updateRisk(riskId, request); + return ResultUtils.success("更新成功", result); + } catch (Exception e) { + log.error("更新风险失败: {}", e.getMessage(), e); + return ResultUtils.error("更新失败: " + e.getMessage()); + } + } + + /** + * 删除风险 + * + * @param riskId 风险ID + * @return 是否成功 + */ + @DeleteMapping("/{riskId}") + public BaseResponse deleteRisk(@PathVariable Long riskId) { + log.info("删除风险, riskId: {}", riskId); + + try { + Boolean result = riskService.deleteRisk(riskId); + return ResultUtils.success("删除成功", result); + } catch (Exception e) { + log.error("删除风险失败: {}", e.getMessage(), e); + return ResultUtils.error("删除失败: " + e.getMessage()); + } + } + + /** + * 获取风险详情 + * + * @param riskId 风险ID + * @return 风险详情 + */ + @GetMapping("/{riskId}") + public BaseResponse getRiskDetail(@PathVariable Long riskId) { + log.info("获取风险详情, riskId: {}", riskId); + + try { + RiskVO result = riskService.getRiskDetail(riskId); + return ResultUtils.success("查询成功", result); + } catch (Exception e) { + log.error("获取风险详情失败: {}", e.getMessage(), e); + return ResultUtils.error("查询失败: " + e.getMessage()); + } + } + + /** + * 分页查询风险列表 + * + * @param projectId 项目ID + * @param pageNum 页码 + * @param pageSize 每页大小 + * @param category 分类筛选 + * @param riskLevel 风险等级筛选 + * @param status 状态筛选 + * @param keyword 关键词搜索 + * @return 分页风险列表 + */ + @GetMapping("/list") + public BaseResponse> getRiskList( + @RequestParam(required = false) Long projectId, + @RequestParam(defaultValue = "1") Integer pageNum, + @RequestParam(defaultValue = "10") Integer pageSize, + @RequestParam(required = false) String category, + @RequestParam(required = false) String riskLevel, + @RequestParam(required = false) String status, + @RequestParam(required = false) String keyword) { + + log.info("分页查询风险列表, projectId: {}, pageNum: {}, pageSize: {}", projectId, pageNum, pageSize); + + try { + TableDataInfo result = riskService.getRiskList(projectId, pageNum, pageSize, + category, riskLevel, status, keyword); + return ResultUtils.success("查询成功", result); + } catch (Exception e) { + log.error("查询风险列表失败: {}", e.getMessage(), e); + return ResultUtils.error("查询失败: " + e.getMessage()); + } + } + + /** + * 获取风险统计信息 + * + * @param projectId 项目ID + * @return 统计信息 + */ + @GetMapping("/statistics") + public BaseResponse getRiskStatistics(@RequestParam(required = false) Long projectId) { + log.info("获取风险统计信息, projectId: {}", projectId); + + try { + RiskStatisticsVO result = riskService.getRiskStatistics(projectId); + return ResultUtils.success("查询成功", result); + } catch (Exception e) { + log.error("获取风险统计失败: {}", e.getMessage(), e); + return ResultUtils.error("查询失败: " + e.getMessage()); + } + } + + /** + * 为风险分配工单 + * + * @param riskId 风险ID + * @param request 工单创建请求 + * @return 工单ID + */ + @PostMapping("/{riskId}/assign-workorder") + public BaseResponse assignWorkOrder(@PathVariable Long riskId, @RequestBody CreateWorkOrderRequest request) { + log.info("为风险分配工单, riskId: {}", riskId); + + try { + Long workOrderId = riskService.assignWorkOrder(riskId, request); + return ResultUtils.success("分配成功", workOrderId); + } catch (Exception e) { + log.error("分配工单失败: {}", e.getMessage(), e); + return ResultUtils.error("分配失败: " + e.getMessage()); + } + } + + /** + * 批量更新风险状态 + * + * @param riskIds 风险ID列表 + * @param status 新状态 + * @return 是否成功 + */ + @PutMapping("/batch-status") + public BaseResponse batchUpdateStatus(@RequestBody List riskIds, @RequestParam String status) { + log.info("批量更新风险状态, riskIds: {}, status: {}", riskIds, status); + + try { + Boolean result = riskService.batchUpdateStatus(riskIds, status); + return ResultUtils.success("更新成功", result); + } catch (Exception e) { + log.error("批量更新状态失败: {}", e.getMessage(), e); + return ResultUtils.error("更新失败: " + e.getMessage()); + } + } +} diff --git a/src/main/java/cn/yinlihupo/controller/risk/WorkOrderController.java b/src/main/java/cn/yinlihupo/controller/risk/WorkOrderController.java new file mode 100644 index 0000000..e483b72 --- /dev/null +++ b/src/main/java/cn/yinlihupo/controller/risk/WorkOrderController.java @@ -0,0 +1,241 @@ +package cn.yinlihupo.controller.risk; + +import cn.yinlihupo.common.core.BaseResponse; +import cn.yinlihupo.common.page.TableDataInfo; +import cn.yinlihupo.common.util.ResultUtils; +import cn.yinlihupo.common.util.SecurityUtils; +import cn.yinlihupo.domain.dto.CreateWorkOrderRequest; +import cn.yinlihupo.domain.dto.ProcessWorkOrderRequest; +import cn.yinlihupo.domain.vo.WorkOrderStatisticsVO; +import cn.yinlihupo.domain.vo.WorkOrderVO; +import cn.yinlihupo.service.risk.WorkOrderService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +/** + * 工单管理控制器 + * 提供工单创建、查询、处理等功能 + */ +@Slf4j +@RestController +@RequestMapping("/api/v1/workorder") +@RequiredArgsConstructor +public class WorkOrderController { + + private final WorkOrderService workOrderService; + + /** + * 创建工单 + * + * @param request 创建请求 + * @return 工单ID + */ + @PostMapping + public BaseResponse createWorkOrder(@RequestBody CreateWorkOrderRequest request) { + log.info("创建工单, projectId: {}", request.getProjectId()); + + try { + Long workOrderId = workOrderService.createWorkOrder(request); + return ResultUtils.success("创建成功", workOrderId); + } catch (Exception e) { + log.error("创建工单失败: {}", e.getMessage(), e); + return ResultUtils.error("创建失败: " + e.getMessage()); + } + } + + /** + * 更新工单 + * + * @param workOrderId 工单ID + * @param request 更新请求 + * @return 是否成功 + */ + @PutMapping("/{workOrderId}") + public BaseResponse updateWorkOrder(@PathVariable Long workOrderId, @RequestBody CreateWorkOrderRequest request) { + log.info("更新工单, workOrderId: {}", workOrderId); + + try { + Boolean result = workOrderService.updateWorkOrder(workOrderId, request); + return ResultUtils.success("更新成功", result); + } catch (Exception e) { + log.error("更新工单失败: {}", e.getMessage(), e); + return ResultUtils.error("更新失败: " + e.getMessage()); + } + } + + /** + * 删除工单 + * + * @param workOrderId 工单ID + * @return 是否成功 + */ + @DeleteMapping("/{workOrderId}") + public BaseResponse deleteWorkOrder(@PathVariable Long workOrderId) { + log.info("删除工单, workOrderId: {}", workOrderId); + + try { + Boolean result = workOrderService.deleteWorkOrder(workOrderId); + return ResultUtils.success("删除成功", result); + } catch (Exception e) { + log.error("删除工单失败: {}", e.getMessage(), e); + return ResultUtils.error("删除失败: " + e.getMessage()); + } + } + + /** + * 获取工单详情 + * + * @param workOrderId 工单ID + * @return 工单详情 + */ + @GetMapping("/{workOrderId}") + public BaseResponse getWorkOrderDetail(@PathVariable Long workOrderId) { + log.info("获取工单详情, workOrderId: {}", workOrderId); + + try { + WorkOrderVO result = workOrderService.getWorkOrderDetail(workOrderId); + return ResultUtils.success("查询成功", result); + } catch (Exception e) { + log.error("获取工单详情失败: {}", e.getMessage(), e); + return ResultUtils.error("查询失败: " + e.getMessage()); + } + } + + /** + * 分页查询工单列表 + * + * @param projectId 项目ID + * @param pageNum 页码 + * @param pageSize 每页大小 + * @param orderType 类型筛选 + * @param status 状态筛选 + * @param priority 优先级筛选 + * @param keyword 关键词搜索 + * @return 分页工单列表 + */ + @GetMapping("/list") + public BaseResponse> getWorkOrderList( + @RequestParam(required = false) Long projectId, + @RequestParam(defaultValue = "1") Integer pageNum, + @RequestParam(defaultValue = "10") Integer pageSize, + @RequestParam(required = false) String orderType, + @RequestParam(required = false) String status, + @RequestParam(required = false) String priority, + @RequestParam(required = false) String keyword) { + + log.info("分页查询工单列表, projectId: {}, pageNum: {}, pageSize: {}", projectId, pageNum, pageSize); + + try { + TableDataInfo result = workOrderService.getWorkOrderList(projectId, pageNum, pageSize, + orderType, status, priority, keyword); + return ResultUtils.success("查询成功", result); + } catch (Exception e) { + log.error("查询工单列表失败: {}", e.getMessage(), e); + return ResultUtils.error("查询失败: " + e.getMessage()); + } + } + + /** + * 获取我的工单列表 + * + * @param pageNum 页码 + * @param pageSize 每页大小 + * @param status 状态筛选 + * @param orderType 类型筛选 + * @return 分页工单列表 + */ + @GetMapping("/my") + public BaseResponse> getMyWorkOrders( + @RequestParam(defaultValue = "1") Integer pageNum, + @RequestParam(defaultValue = "10") Integer pageSize, + @RequestParam(required = false) String status, + @RequestParam(required = false) String orderType) { + + log.info("获取我的工单列表, pageNum: {}, pageSize: {}", pageNum, pageSize); + + try { + TableDataInfo result = workOrderService.getMyWorkOrders(pageNum, pageSize, status, orderType); + return ResultUtils.success("查询成功", result); + } catch (Exception e) { + log.error("查询我的工单失败: {}", e.getMessage(), e); + return ResultUtils.error("查询失败: " + e.getMessage()); + } + } + + /** + * 处理工单 + * + * @param request 处理请求 + * @return 是否成功 + */ + @PostMapping("/process") + public BaseResponse processWorkOrder(@RequestBody ProcessWorkOrderRequest request) { + log.info("处理工单, workOrderId: {}", request.getWorkOrderId()); + + try { + Boolean result = workOrderService.processWorkOrder(request); + return ResultUtils.success("处理成功", result); + } catch (Exception e) { + log.error("处理工单失败: {}", e.getMessage(), e); + return ResultUtils.error("处理失败: " + e.getMessage()); + } + } + + /** + * 分配工单给处理人 + * + * @param workOrderId 工单ID + * @param handlerId 处理人ID + * @return 是否成功 + */ + @PutMapping("/{workOrderId}/assign") + public BaseResponse assignWorkOrder(@PathVariable Long workOrderId, @RequestParam Long handlerId) { + log.info("分配工单, workOrderId: {}, handlerId: {}", workOrderId, handlerId); + + try { + Boolean result = workOrderService.assignWorkOrder(workOrderId, handlerId); + return ResultUtils.success("分配成功", result); + } catch (Exception e) { + log.error("分配工单失败: {}", e.getMessage(), e); + return ResultUtils.error("分配失败: " + e.getMessage()); + } + } + + /** + * 获取工单统计信息 + * + * @param projectId 项目ID(可选) + * @return 统计信息 + */ + @GetMapping("/statistics") + public BaseResponse getWorkOrderStatistics(@RequestParam(required = false) Long projectId) { + log.info("获取工单统计信息, projectId: {}", projectId); + + try { + WorkOrderStatisticsVO result = workOrderService.getWorkOrderStatistics(projectId); + return ResultUtils.success("查询成功", result); + } catch (Exception e) { + log.error("获取工单统计失败: {}", e.getMessage(), e); + return ResultUtils.error("查询失败: " + e.getMessage()); + } + } + + /** + * 获取我的工单统计信息 + * + * @return 统计信息 + */ + @GetMapping("/my/statistics") + public BaseResponse getMyWorkOrderStatistics() { + log.info("获取我的工单统计信息"); + + try { + WorkOrderStatisticsVO result = workOrderService.getMyWorkOrderStatistics(); + return ResultUtils.success("查询成功", result); + } catch (Exception e) { + log.error("获取我的工单统计失败: {}", e.getMessage(), e); + return ResultUtils.error("查询失败: " + e.getMessage()); + } + } +} diff --git a/src/main/java/cn/yinlihupo/domain/dto/CreateRiskRequest.java b/src/main/java/cn/yinlihupo/domain/dto/CreateRiskRequest.java new file mode 100644 index 0000000..8f7345e --- /dev/null +++ b/src/main/java/cn/yinlihupo/domain/dto/CreateRiskRequest.java @@ -0,0 +1,79 @@ +package cn.yinlihupo.domain.dto; + +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.List; + +/** + * 创建风险请求DTO + */ +@Data +public class CreateRiskRequest { + + /** + * 项目ID + */ + private Long projectId; + + /** + * 风险分类: technical-技术风险, schedule-进度风险, cost-成本风险, quality-质量风险, resource-资源风险, external-外部风险, other-其他 + */ + private String category; + + /** + * 风险名称 + */ + private String riskName; + + /** + * 风险描述 + */ + private String description; + + /** + * 风险来源: internal-内部, external-外部, manual-手动添加 + */ + private String riskSource; + + /** + * 发生概率(0-100) + */ + private Integer probability; + + /** + * 影响程度(1-5) + */ + private Integer impact; + + /** + * 负责人ID + */ + private Long ownerId; + + /** + * 缓解措施 + */ + private String mitigationPlan; + + /** + * 应急计划 + */ + private String contingencyPlan; + + /** + * 触发条件 + */ + private String triggerCondition; + + /** + * 预期解决日期 + */ + private LocalDate dueDate; + + /** + * 标签 + */ + private List tags; +} diff --git a/src/main/java/cn/yinlihupo/domain/dto/CreateWorkOrderRequest.java b/src/main/java/cn/yinlihupo/domain/dto/CreateWorkOrderRequest.java new file mode 100644 index 0000000..329812a --- /dev/null +++ b/src/main/java/cn/yinlihupo/domain/dto/CreateWorkOrderRequest.java @@ -0,0 +1,68 @@ +package cn.yinlihupo.domain.dto; + +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 创建工单请求DTO + */ +@Data +public class CreateWorkOrderRequest { + + /** + * 项目ID + */ + private Long projectId; + + /** + * 关联风险ID + */ + private Long riskId; + + /** + * 工单类型: bug-缺陷, feature-需求, task-任务, incident-事件, risk_handle-风险处理, other-其他 + */ + private String orderType; + + /** + * 工单标题 + */ + private String title; + + /** + * 工单描述 + */ + private String description; + + /** + * 优先级: critical-紧急, high-高, medium-中, low-低 + */ + private String priority; + + /** + * 处理人ID + */ + private Long handlerId; + + /** + * 处理组ID + */ + private Long handlerGroupId; + + /** + * 截止时间 + */ + private LocalDateTime deadline; + + /** + * 来源: web-网页, mobile-移动端, api-接口, system-系统生成, risk-风险分派 + */ + private String source; + + /** + * 标签 + */ + private List tags; +} diff --git a/src/main/java/cn/yinlihupo/domain/dto/ProcessWorkOrderRequest.java b/src/main/java/cn/yinlihupo/domain/dto/ProcessWorkOrderRequest.java new file mode 100644 index 0000000..16cb7de --- /dev/null +++ b/src/main/java/cn/yinlihupo/domain/dto/ProcessWorkOrderRequest.java @@ -0,0 +1,25 @@ +package cn.yinlihupo.domain.dto; + +import lombok.Data; + +/** + * 处理工单请求DTO + */ +@Data +public class ProcessWorkOrderRequest { + + /** + * 工单ID + */ + private Long workOrderId; + + /** + * 处理状态: processing-处理中, resolved-已解决, closed-已关闭, reopened-已重开 + */ + private String status; + + /** + * 满意度评分(1-5) + */ + private Integer satisfactionScore; +} diff --git a/src/main/java/cn/yinlihupo/domain/entity/WorkOrder.java b/src/main/java/cn/yinlihupo/domain/entity/WorkOrder.java new file mode 100644 index 0000000..5b4e4a3 --- /dev/null +++ b/src/main/java/cn/yinlihupo/domain/entity/WorkOrder.java @@ -0,0 +1,146 @@ +package cn.yinlihupo.domain.entity; + +import cn.yinlihupo.common.handler.JsonbTypeHandler; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 工单实体类 + * 对应数据库表: work_order + */ +@Data +@TableName("work_order") +public class WorkOrder { + + @TableId(type = IdType.AUTO) + private Long id; + + /** + * 工单编号 + */ + private String orderCode; + + /** + * 工单类型: bug-缺陷, feature-需求, task-任务, incident-事件, risk_handle-风险处理, other-其他 + */ + private String orderType; + + /** + * 项目ID + */ + private Long projectId; + + /** + * 关联风险ID + */ + private Long riskId; + + /** + * 工单标题 + */ + private String title; + + /** + * 工单描述 + */ + private String description; + + /** + * 创建人ID + */ + private Long creatorId; + + /** + * 处理人ID + */ + private Long handlerId; + + /** + * 处理组ID + */ + private Long handlerGroupId; + + /** + * 优先级: critical-紧急, high-高, medium-中, low-低 + */ + private String priority; + + /** + * 状态: pending-待处理, assigned-已分派, processing-处理中, resolved-已解决, closed-已关闭, reopened-已重开 + */ + private String status; + + /** + * 来源: web-网页, mobile-移动端, api-接口, system-系统生成, risk-风险分派 + */ + private String source; + + /** + * 截止时间 + */ + private LocalDateTime deadline; + + /** + * 分派时间 + */ + private LocalDateTime assignedTime; + + /** + * 首次响应时间 + */ + private LocalDateTime firstResponseTime; + + /** + * 解决时间 + */ + private LocalDateTime resolvedTime; + + /** + * 关闭时间 + */ + private LocalDateTime closedTime; + + /** + * 满意度评分(1-5) + */ + private Integer satisfactionScore; + + /** + * 标签 + */ + @TableField(typeHandler = JsonbTypeHandler.class) + private List tags; + + /** + * 附件列表 + */ + @TableField(typeHandler = JsonbTypeHandler.class) + private List attachments; + + /** + * 扩展数据 + */ + @TableField(typeHandler = JsonbTypeHandler.class) + private Object extraData; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + + /** + * 删除标记 + */ + @TableLogic + private Integer deleted; +} diff --git a/src/main/java/cn/yinlihupo/domain/vo/RiskStatisticsVO.java b/src/main/java/cn/yinlihupo/domain/vo/RiskStatisticsVO.java new file mode 100644 index 0000000..8675e1c --- /dev/null +++ b/src/main/java/cn/yinlihupo/domain/vo/RiskStatisticsVO.java @@ -0,0 +1,87 @@ +package cn.yinlihupo.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 风险统计VO + */ +@Data +public class RiskStatisticsVO { + + /** + * 风险总数 + */ + private Integer totalCount; + + /** + * 已识别数量 + */ + private Integer identifiedCount; + + /** + * 已分派工单数量 + */ + private Integer assignedCount; + + /** + * 缓解中数量 + */ + private Integer mitigatingCount; + + /** + * 已解决数量 + */ + private Integer resolvedCount; + + /** + * 已关闭数量 + */ + private Integer closedCount; + + /** + * 严重风险数量 + */ + private Integer criticalCount; + + /** + * 高风险数量 + */ + private Integer highCount; + + /** + * 中风险数量 + */ + private Integer mediumCount; + + /** + * 低风险数量 + */ + private Integer lowCount; + + /** + * 按分类统计 + */ + private Object categoryStats; + + /** + * 按等级统计 + */ + private Object levelStats; + + /** + * 处理趋势(近6个月) + */ + private Object trendData; + + /** + * 平均风险得分 + */ + private BigDecimal averageRiskScore; + + /** + * 未解决高风险数量 + */ + private Integer unresolvedHighCount; +} diff --git a/src/main/java/cn/yinlihupo/domain/vo/RiskVO.java b/src/main/java/cn/yinlihupo/domain/vo/RiskVO.java new file mode 100644 index 0000000..9880439 --- /dev/null +++ b/src/main/java/cn/yinlihupo/domain/vo/RiskVO.java @@ -0,0 +1,146 @@ +package cn.yinlihupo.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +/** + * 风险VO + * 用于风险列表展示 + */ +@Data +public class RiskVO { + + /** + * 风险ID + */ + private Long id; + + /** + * 风险编号 + */ + private String riskCode; + + /** + * 项目ID + */ + private Long projectId; + + /** + * 项目名称 + */ + private String projectName; + + /** + * 风险分类 + */ + private String category; + + /** + * 风险名称 + */ + private String riskName; + + /** + * 风险描述 + */ + private String description; + + /** + * 风险来源 + */ + private String riskSource; + + /** + * 发生概率(0-100%) + */ + private BigDecimal probability; + + /** + * 影响程度(1-5) + */ + private BigDecimal impact; + + /** + * 风险得分 + */ + private BigDecimal riskScore; + + /** + * 风险等级 + */ + private String riskLevel; + + /** + * 状态 + */ + private String status; + + /** + * 负责人ID + */ + private Long ownerId; + + /** + * 负责人姓名 + */ + private String ownerName; + + /** + * 负责人头像 + */ + private String ownerAvatar; + + /** + * 关联工单数量 + */ + private Integer workOrderCount; + + /** + * 缓解措施 + */ + private String mitigationPlan; + + /** + * 应急计划 + */ + private String contingencyPlan; + + /** + * 触发条件 + */ + private String triggerCondition; + + /** + * 发现时间 + */ + private LocalDateTime discoverTime; + + /** + * 预期解决日期 + */ + private LocalDate dueDate; + + /** + * 解决时间 + */ + private LocalDateTime resolvedTime; + + /** + * 标签 + */ + private List tags; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; +} diff --git a/src/main/java/cn/yinlihupo/domain/vo/WorkOrderStatisticsVO.java b/src/main/java/cn/yinlihupo/domain/vo/WorkOrderStatisticsVO.java new file mode 100644 index 0000000..42e21e3 --- /dev/null +++ b/src/main/java/cn/yinlihupo/domain/vo/WorkOrderStatisticsVO.java @@ -0,0 +1,65 @@ +package cn.yinlihupo.domain.vo; + +import lombok.Data; + +/** + * 工单统计VO + */ +@Data +public class WorkOrderStatisticsVO { + + /** + * 工单总数 + */ + private Integer totalCount; + + /** + * 待处理数量 + */ + private Integer pendingCount; + + /** + * 已分配数量 + */ + private Integer assignedCount; + + /** + * 处理中数量 + */ + private Integer processingCount; + + /** + * 已完成数量 + */ + private Integer completedCount; + + /** + * 已关闭数量 + */ + private Integer closedCount; + + /** + * 已驳回数量 + */ + private Integer rejectedCount; + + /** + * 超期未完成数量 + */ + private Integer overdueCount; + + /** + * 即将超期数量(7天内) + */ + private Integer aboutToExpireCount; + + /** + * 按类型统计 + */ + private Object typeStats; + + /** + * 按优先级统计 + */ + private Object priorityStats; +} diff --git a/src/main/java/cn/yinlihupo/domain/vo/WorkOrderVO.java b/src/main/java/cn/yinlihupo/domain/vo/WorkOrderVO.java new file mode 100644 index 0000000..1304072 --- /dev/null +++ b/src/main/java/cn/yinlihupo/domain/vo/WorkOrderVO.java @@ -0,0 +1,154 @@ +package cn.yinlihupo.domain.vo; + +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 工单VO + * 用于工单列表展示 + */ +@Data +public class WorkOrderVO { + + /** + * 工单ID + */ + private Long id; + + /** + * 工单编号 + */ + private String orderCode; + + /** + * 工单类型: bug-缺陷, feature-需求, task-任务, incident-事件, risk_handle-风险处理, other-其他 + */ + private String orderType; + + /** + * 项目ID + */ + private Long projectId; + + /** + * 项目名称 + */ + private String projectName; + + /** + * 关联风险ID + */ + private Long riskId; + + /** + * 关联风险名称 + */ + private String riskName; + + /** + * 工单标题 + */ + private String title; + + /** + * 工单描述 + */ + private String description; + + /** + * 创建人ID + */ + private Long creatorId; + + /** + * 创建人姓名 + */ + private String creatorName; + + /** + * 处理人ID + */ + private Long handlerId; + + /** + * 处理人姓名 + */ + private String handlerName; + + /** + * 处理人头像 + */ + private String handlerAvatar; + + /** + * 处理组ID + */ + private Long handlerGroupId; + + /** + * 优先级: critical-紧急, high-高, medium-中, low-低 + */ + private String priority; + + /** + * 状态: pending-待处理, assigned-已分派, processing-处理中, resolved-已解决, closed-已关闭, reopened-已重开 + */ + private String status; + + /** + * 来源: web-网页, mobile-移动端, api-接口, system-系统生成, risk-风险分派 + */ + private String source; + + /** + * 截止时间 + */ + private LocalDateTime deadline; + + /** + * 分派时间 + */ + private LocalDateTime assignedTime; + + /** + * 首次响应时间 + */ + private LocalDateTime firstResponseTime; + + /** + * 解决时间 + */ + private LocalDateTime resolvedTime; + + /** + * 关闭时间 + */ + private LocalDateTime closedTime; + + /** + * 满意度评分(1-5) + */ + private Integer satisfactionScore; + + /** + * 是否超期 + */ + private Boolean isOverdue; + + /** + * 标签 + */ + private List tags; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; +} diff --git a/src/main/java/cn/yinlihupo/mapper/WorkOrderMapper.java b/src/main/java/cn/yinlihupo/mapper/WorkOrderMapper.java new file mode 100644 index 0000000..99dd3da --- /dev/null +++ b/src/main/java/cn/yinlihupo/mapper/WorkOrderMapper.java @@ -0,0 +1,57 @@ +package cn.yinlihupo.mapper; + +import cn.yinlihupo.domain.entity.WorkOrder; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 工单Mapper接口 + */ +@Mapper +public interface WorkOrderMapper extends BaseMapper { + + /** + * 分页查询工单列表(含处理人信息,支持多条件筛选) + */ + List> selectWorkOrderPageWithHandler(@Param("projectId") Long projectId, + @Param("handlerId") Long handlerId, + @Param("orderType") String orderType, + @Param("status") String status, + @Param("priority") String priority, + @Param("keyword") String keyword); + + /** + * 查询我的工单列表 + */ + List> selectMyWorkOrders(@Param("handlerId") Long handlerId, + @Param("status") String status, + @Param("orderType") String orderType); + + /** + * 按状态统计工单数量 + */ + Map countByStatus(@Param("projectId") Long projectId, + @Param("handlerId") Long handlerId); + + /** + * 按类型统计工单数量 + */ + List> countByType(@Param("projectId") Long projectId); + + /** + * 查询即将超期的工单(N天内) + */ + List selectAboutToExpire(@Param("projectId") Long projectId, + @Param("handlerId") Long handlerId, + @Param("days") int days); + + /** + * 查询超期未完成的工单 + */ + List selectOverdueWorkOrders(@Param("projectId") Long projectId, + @Param("handlerId") Long handlerId); +} diff --git a/src/main/java/cn/yinlihupo/service/risk/RiskService.java b/src/main/java/cn/yinlihupo/service/risk/RiskService.java new file mode 100644 index 0000000..d2f3a16 --- /dev/null +++ b/src/main/java/cn/yinlihupo/service/risk/RiskService.java @@ -0,0 +1,87 @@ +package cn.yinlihupo.service.risk; + +import cn.yinlihupo.common.page.TableDataInfo; +import cn.yinlihupo.domain.dto.CreateRiskRequest; +import cn.yinlihupo.domain.dto.CreateWorkOrderRequest; +import cn.yinlihupo.domain.vo.RiskStatisticsVO; +import cn.yinlihupo.domain.vo.RiskVO; + +/** + * 风险服务接口 + */ +public interface RiskService { + + /** + * 创建风险评估 + * + * @param request 创建请求 + * @return 风险ID + */ + Long createRisk(CreateRiskRequest request); + + /** + * 更新风险 + * + * @param riskId 风险ID + * @param request 更新请求 + * @return 是否成功 + */ + Boolean updateRisk(Long riskId, CreateRiskRequest request); + + /** + * 删除风险 + * + * @param riskId 风险ID + * @return 是否成功 + */ + Boolean deleteRisk(Long riskId); + + /** + * 获取风险详情 + * + * @param riskId 风险ID + * @return 风险详情 + */ + RiskVO getRiskDetail(Long riskId); + + /** + * 分页查询风险列表 + * + * @param projectId 项目ID + * @param pageNum 页码 + * @param pageSize 每页大小 + * @param category 分类筛选 + * @param riskLevel 风险等级筛选 + * @param status 状态筛选 + * @param keyword 关键词搜索 + * @return 分页风险列表 + */ + TableDataInfo getRiskList(Long projectId, Integer pageNum, Integer pageSize, + String category, String riskLevel, String status, String keyword); + + /** + * 获取风险统计信息 + * + * @param projectId 项目ID + * @return 统计信息 + */ + RiskStatisticsVO getRiskStatistics(Long projectId); + + /** + * 为风险分配工单 + * + * @param riskId 风险ID + * @param request 工单创建请求 + * @return 工单ID + */ + Long assignWorkOrder(Long riskId, CreateWorkOrderRequest request); + + /** + * 批量更新风险状态 + * + * @param riskIds 风险ID列表 + * @param status 新状态 + * @return 是否成功 + */ + Boolean batchUpdateStatus(java.util.List riskIds, String status); +} diff --git a/src/main/java/cn/yinlihupo/service/risk/WorkOrderService.java b/src/main/java/cn/yinlihupo/service/risk/WorkOrderService.java new file mode 100644 index 0000000..f17a3a9 --- /dev/null +++ b/src/main/java/cn/yinlihupo/service/risk/WorkOrderService.java @@ -0,0 +1,105 @@ +package cn.yinlihupo.service.risk; + +import cn.yinlihupo.common.page.TableDataInfo; +import cn.yinlihupo.domain.dto.CreateWorkOrderRequest; +import cn.yinlihupo.domain.dto.ProcessWorkOrderRequest; +import cn.yinlihupo.domain.vo.WorkOrderStatisticsVO; +import cn.yinlihupo.domain.vo.WorkOrderVO; + +/** + * 工单服务接口 + */ +public interface WorkOrderService { + + /** + * 创建工单 + * + * @param request 创建请求 + * @return 工单ID + */ + Long createWorkOrder(CreateWorkOrderRequest request); + + /** + * 更新工单 + * + * @param workOrderId 工单ID + * @param request 更新请求 + * @return 是否成功 + */ + Boolean updateWorkOrder(Long workOrderId, CreateWorkOrderRequest request); + + /** + * 删除工单 + * + * @param workOrderId 工单ID + * @return 是否成功 + */ + Boolean deleteWorkOrder(Long workOrderId); + + /** + * 获取工单详情 + * + * @param workOrderId 工单ID + * @return 工单详情 + */ + WorkOrderVO getWorkOrderDetail(Long workOrderId); + + /** + * 分页查询工单列表 + * + * @param projectId 项目ID + * @param pageNum 页码 + * @param pageSize 每页大小 + * @param orderType 类型筛选 + * @param status 状态筛选 + * @param priority 优先级筛选 + * @param keyword 关键词搜索 + * @return 分页工单列表 + */ + TableDataInfo getWorkOrderList(Long projectId, Integer pageNum, Integer pageSize, + String orderType, String status, String priority, String keyword); + + /** + * 获取我的工单列表 + * + * @param pageNum 页码 + * @param pageSize 每页大小 + * @param status 状态筛选 + * @param orderType 类型筛选 + * @return 分页工单列表 + */ + TableDataInfo getMyWorkOrders(Integer pageNum, Integer pageSize, + String status, String orderType); + + /** + * 处理工单 + * + * @param request 处理请求 + * @return 是否成功 + */ + Boolean processWorkOrder(ProcessWorkOrderRequest request); + + /** + * 分配工单给处理人 + * + * @param workOrderId 工单ID + * @param handlerId 处理人ID + * @return 是否成功 + */ + Boolean assignWorkOrder(Long workOrderId, Long handlerId); + + /** + * 获取工单统计信息 + * + * @param projectId 项目ID(可选) + * @return 统计信息 + */ + WorkOrderStatisticsVO getWorkOrderStatistics(Long projectId); + + /** + * 获取我的工单统计信息 + * + * @return 统计信息 + */ + WorkOrderStatisticsVO getMyWorkOrderStatistics(); +} diff --git a/src/main/java/cn/yinlihupo/service/risk/impl/RiskServiceImpl.java b/src/main/java/cn/yinlihupo/service/risk/impl/RiskServiceImpl.java new file mode 100644 index 0000000..a11e253 --- /dev/null +++ b/src/main/java/cn/yinlihupo/service/risk/impl/RiskServiceImpl.java @@ -0,0 +1,416 @@ +package cn.yinlihupo.service.risk.impl; + +import cn.hutool.core.util.IdUtil; +import cn.yinlihupo.common.page.TableDataInfo; +import cn.yinlihupo.common.util.SecurityUtils; +import cn.yinlihupo.domain.dto.CreateRiskRequest; +import cn.yinlihupo.domain.dto.CreateWorkOrderRequest; +import cn.yinlihupo.domain.entity.Project; +import cn.yinlihupo.domain.entity.Risk; +import cn.yinlihupo.domain.entity.SysUser; +import cn.yinlihupo.domain.entity.WorkOrder; +import cn.yinlihupo.domain.vo.RiskStatisticsVO; +import cn.yinlihupo.domain.vo.RiskVO; +import cn.yinlihupo.mapper.ProjectMapper; +import cn.yinlihupo.mapper.RiskMapper; +import cn.yinlihupo.mapper.SysUserMapper; +import cn.yinlihupo.mapper.WorkOrderMapper; +import cn.yinlihupo.service.risk.RiskService; +import cn.yinlihupo.service.risk.WorkOrderService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 风险服务实现类 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class RiskServiceImpl implements RiskService { + + private final RiskMapper riskMapper; + private final ProjectMapper projectMapper; + private final SysUserMapper sysUserMapper; + private final WorkOrderMapper workOrderMapper; + private final WorkOrderService workOrderService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createRisk(CreateRiskRequest request) { + log.info("创建风险评估, projectId: {}, riskName: {}", request.getProjectId(), request.getRiskName()); + + // 验证项目存在 + Project project = projectMapper.selectById(request.getProjectId()); + if (project == null || project.getDeleted() == 1) { + throw new RuntimeException("项目不存在"); + } + + Risk risk = new Risk(); + risk.setProjectId(request.getProjectId()); + risk.setRiskCode(generateRiskCode()); + risk.setCategory(request.getCategory()); + risk.setRiskName(request.getRiskName()); + risk.setDescription(request.getDescription()); + risk.setRiskSource(request.getRiskSource() != null ? request.getRiskSource() : "manual"); + + // 计算风险得分和等级 + if (request.getProbability() != null && request.getImpact() != null) { + risk.setProbability(BigDecimal.valueOf(request.getProbability())); + risk.setImpact(BigDecimal.valueOf(request.getImpact())); + BigDecimal score = risk.getProbability() + .multiply(risk.getImpact()) + .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); + risk.setRiskScore(score); + risk.setRiskLevel(calculateRiskLevel(request.getProbability(), request.getImpact())); + } + + risk.setOwnerId(request.getOwnerId()); + risk.setMitigationPlan(request.getMitigationPlan()); + risk.setContingencyPlan(request.getContingencyPlan()); + risk.setTriggerCondition(request.getTriggerCondition()); + risk.setDueDate(request.getDueDate()); + risk.setTags(request.getTags()); + risk.setStatus("identified"); + risk.setDiscoverTime(LocalDateTime.now()); + + riskMapper.insert(risk); + log.info("风险评估创建成功, riskId: {}", risk.getId()); + + return risk.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateRisk(Long riskId, CreateRiskRequest request) { + log.info("更新风险, riskId: {}", riskId); + + Risk risk = riskMapper.selectById(riskId); + if (risk == null || risk.getDeleted() == 1) { + throw new RuntimeException("风险不存在"); + } + + if (request.getCategory() != null) { + risk.setCategory(request.getCategory()); + } + if (request.getRiskName() != null) { + risk.setRiskName(request.getRiskName()); + } + if (request.getDescription() != null) { + risk.setDescription(request.getDescription()); + } + if (request.getProbability() != null && request.getImpact() != null) { + risk.setProbability(BigDecimal.valueOf(request.getProbability())); + risk.setImpact(BigDecimal.valueOf(request.getImpact())); + BigDecimal score = risk.getProbability() + .multiply(risk.getImpact()) + .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); + risk.setRiskScore(score); + risk.setRiskLevel(calculateRiskLevel(request.getProbability(), request.getImpact())); + } + if (request.getOwnerId() != null) { + risk.setOwnerId(request.getOwnerId()); + } + if (request.getMitigationPlan() != null) { + risk.setMitigationPlan(request.getMitigationPlan()); + } + if (request.getContingencyPlan() != null) { + risk.setContingencyPlan(request.getContingencyPlan()); + } + if (request.getTriggerCondition() != null) { + risk.setTriggerCondition(request.getTriggerCondition()); + } + if (request.getDueDate() != null) { + risk.setDueDate(request.getDueDate()); + } + if (request.getTags() != null) { + risk.setTags(request.getTags()); + } + + riskMapper.updateById(risk); + log.info("风险更新成功, riskId: {}", riskId); + + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteRisk(Long riskId) { + log.info("删除风险, riskId: {}", riskId); + + Risk risk = riskMapper.selectById(riskId); + if (risk == null || risk.getDeleted() == 1) { + throw new RuntimeException("风险不存在"); + } + + riskMapper.deleteById(riskId); + log.info("风险删除成功, riskId: {}", riskId); + + return true; + } + + @Override + public RiskVO getRiskDetail(Long riskId) { + log.info("获取风险详情, riskId: {}", riskId); + + Risk risk = riskMapper.selectById(riskId); + if (risk == null || risk.getDeleted() == 1) { + throw new RuntimeException("风险不存在"); + } + + return convertToRiskVO(risk); + } + + @Override + public TableDataInfo getRiskList(Long projectId, Integer pageNum, Integer pageSize, + String category, String riskLevel, String status, String keyword) { + log.info("分页查询风险列表, projectId: {}, pageNum: {}, pageSize: {}", projectId, pageNum, pageSize); + + Page page = new Page<>(pageNum, pageSize); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(Risk::getDeleted, 0); + + if (projectId != null) { + wrapper.eq(Risk::getProjectId, projectId); + } + if (category != null && !category.isEmpty()) { + wrapper.eq(Risk::getCategory, category); + } + if (riskLevel != null && !riskLevel.isEmpty()) { + wrapper.eq(Risk::getRiskLevel, riskLevel); + } + if (status != null && !status.isEmpty()) { + wrapper.eq(Risk::getStatus, status); + } + if (keyword != null && !keyword.isEmpty()) { + wrapper.and(w -> w.like(Risk::getRiskName, keyword) + .or() + .like(Risk::getDescription, keyword)); + } + + // 按风险等级和得分排序 + wrapper.orderByAsc( + r -> "critical".equals(r.getRiskLevel()) ? 1 : + "high".equals(r.getRiskLevel()) ? 2 : + "medium".equals(r.getRiskLevel()) ? 3 : 4 + ).orderByDesc(Risk::getRiskScore); + + Page riskPage = riskMapper.selectPage(page, wrapper); + + List voList = riskPage.getRecords().stream() + .map(this::convertToRiskVO) + .collect(Collectors.toList()); + + return TableDataInfo.build(new Page(riskPage.getCurrent(), riskPage.getSize(), riskPage.getTotal()) + .setRecords(voList)); + } + + @Override + public RiskStatisticsVO getRiskStatistics(Long projectId) { + log.info("获取风险统计信息, projectId: {}", projectId); + + RiskStatisticsVO statistics = new RiskStatisticsVO(); + + // 构建查询条件 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(Risk::getDeleted, 0); + if (projectId != null) { + wrapper.eq(Risk::getProjectId, projectId); + } + + List risks = riskMapper.selectList(wrapper); + + // 统计总数 + statistics.setTotalCount(risks.size()); + + // 按状态统计 + Map statusCount = risks.stream() + .collect(Collectors.groupingBy(Risk::getStatus, Collectors.counting())); + statistics.setIdentifiedCount(statusCount.getOrDefault("identified", 0L).intValue()); + statistics.setAssignedCount(statusCount.getOrDefault("assigned", 0L).intValue()); + statistics.setMitigatingCount(statusCount.getOrDefault("mitigating", 0L).intValue()); + statistics.setResolvedCount(statusCount.getOrDefault("resolved", 0L).intValue()); + statistics.setClosedCount(statusCount.getOrDefault("closed", 0L).intValue()); + + // 按等级统计 + Map levelCount = risks.stream() + .collect(Collectors.groupingBy(Risk::getRiskLevel, Collectors.counting())); + statistics.setCriticalCount(levelCount.getOrDefault("critical", 0L).intValue()); + statistics.setHighCount(levelCount.getOrDefault("high", 0L).intValue()); + statistics.setMediumCount(levelCount.getOrDefault("medium", 0L).intValue()); + statistics.setLowCount(levelCount.getOrDefault("low", 0L).intValue()); + + // 计算平均风险得分 + double avgScore = risks.stream() + .filter(r -> r.getRiskScore() != null) + .mapToDouble(r -> r.getRiskScore().doubleValue()) + .average() + .orElse(0.0); + statistics.setAverageRiskScore(BigDecimal.valueOf(avgScore).setScale(2, RoundingMode.HALF_UP)); + + // 统计未解决的高风险数量 + int unresolvedHigh = (int) risks.stream() + .filter(r -> !"resolved".equals(r.getStatus()) && !"closed".equals(r.getStatus())) + .filter(r -> "critical".equals(r.getRiskLevel()) || "high".equals(r.getRiskLevel())) + .count(); + statistics.setUnresolvedHighCount(unresolvedHigh); + + // 按分类统计 + Map categoryStats = risks.stream() + .collect(Collectors.groupingBy( + r -> r.getCategory() != null ? r.getCategory() : "other", + Collectors.counting() + )); + statistics.setCategoryStats(categoryStats); + + // 按等级统计 + statistics.setLevelStats(levelCount); + + return statistics; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long assignWorkOrder(Long riskId, CreateWorkOrderRequest request) { + log.info("为风险分配工单, riskId: {}", riskId); + + Risk risk = riskMapper.selectById(riskId); + if (risk == null || risk.getDeleted() == 1) { + throw new RuntimeException("风险不存在"); + } + + // 设置工单关联 + request.setRiskId(riskId); + request.setProjectId(risk.getProjectId()); + request.setOrderType("risk_handle"); + request.setSource("risk"); + + // 创建工单 + Long workOrderId = workOrderService.createWorkOrder(request); + + // 更新风险的工单关联 + List workOrderIds = risk.getWorkOrderIds(); + if (workOrderIds == null) { + workOrderIds = new ArrayList<>(); + } + workOrderIds.add(workOrderId); + risk.setWorkOrderIds(workOrderIds); + risk.setStatus("assigned"); + riskMapper.updateById(risk); + + log.info("风险工单分配成功, riskId: {}, workOrderId: {}", riskId, workOrderId); + return workOrderId; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean batchUpdateStatus(List riskIds, String status) { + log.info("批量更新风险状态, riskIds: {}, status: {}", riskIds, status); + + for (Long riskId : riskIds) { + Risk risk = riskMapper.selectById(riskId); + if (risk != null && risk.getDeleted() == 0) { + risk.setStatus(status); + if ("resolved".equals(status)) { + risk.setResolvedTime(LocalDateTime.now()); + } + riskMapper.updateById(risk); + } + } + + log.info("批量更新风险状态完成, 更新数量: {}", riskIds.size()); + return true; + } + + /** + * 生成风险编号 + */ + private String generateRiskCode() { + return "RSK" + IdUtil.fastSimpleUUID().substring(0, 12).toUpperCase(); + } + + /** + * 计算风险等级 + */ + private String calculateRiskLevel(Integer probability, Integer impact) { + if (probability == null || impact == null) { + return "low"; + } + int score = probability * impact; + if (score >= 300) { + return "critical"; + } else if (score >= 200) { + return "high"; + } else if (score >= 100) { + return "medium"; + } else { + return "low"; + } + } + + /** + * 转换为VO + */ + private RiskVO convertToRiskVO(Risk risk) { + RiskVO vo = new RiskVO(); + vo.setId(risk.getId()); + vo.setRiskCode(risk.getRiskCode()); + vo.setProjectId(risk.getProjectId()); + vo.setCategory(risk.getCategory()); + vo.setRiskName(risk.getRiskName()); + vo.setDescription(risk.getDescription()); + vo.setRiskSource(risk.getRiskSource()); + vo.setProbability(risk.getProbability()); + vo.setImpact(risk.getImpact()); + vo.setRiskScore(risk.getRiskScore()); + vo.setRiskLevel(risk.getRiskLevel()); + vo.setStatus(risk.getStatus()); + vo.setOwnerId(risk.getOwnerId()); + vo.setMitigationPlan(risk.getMitigationPlan()); + vo.setContingencyPlan(risk.getContingencyPlan()); + vo.setTriggerCondition(risk.getTriggerCondition()); + vo.setDiscoverTime(risk.getDiscoverTime()); + vo.setDueDate(risk.getDueDate()); + vo.setResolvedTime(risk.getResolvedTime()); + vo.setTags(risk.getTags()); + vo.setCreateTime(risk.getCreateTime()); + vo.setUpdateTime(risk.getUpdateTime()); + + // 查询项目名称 + if (risk.getProjectId() != null) { + Project project = projectMapper.selectById(risk.getProjectId()); + if (project != null) { + vo.setProjectName(project.getProjectName()); + } + } + + // 查询负责人信息 + if (risk.getOwnerId() != null) { + SysUser owner = sysUserMapper.selectById(risk.getOwnerId()); + if (owner != null) { + vo.setOwnerName(owner.getRealName()); + vo.setOwnerAvatar(owner.getAvatar()); + } + } + + // 计算关联工单数量 + if (risk.getWorkOrderIds() != null) { + vo.setWorkOrderCount(risk.getWorkOrderIds().size()); + } else { + vo.setWorkOrderCount(0); + } + + return vo; + } +} diff --git a/src/main/java/cn/yinlihupo/service/risk/impl/WorkOrderServiceImpl.java b/src/main/java/cn/yinlihupo/service/risk/impl/WorkOrderServiceImpl.java new file mode 100644 index 0000000..7b77493 --- /dev/null +++ b/src/main/java/cn/yinlihupo/service/risk/impl/WorkOrderServiceImpl.java @@ -0,0 +1,515 @@ +package cn.yinlihupo.service.risk.impl; + +import cn.hutool.core.util.IdUtil; +import cn.yinlihupo.common.page.TableDataInfo; +import cn.yinlihupo.common.util.SecurityUtils; +import cn.yinlihupo.domain.dto.CreateWorkOrderRequest; +import cn.yinlihupo.domain.dto.ProcessWorkOrderRequest; +import cn.yinlihupo.domain.entity.*; +import cn.yinlihupo.domain.vo.WorkOrderStatisticsVO; +import cn.yinlihupo.domain.vo.WorkOrderVO; +import cn.yinlihupo.mapper.*; +import cn.yinlihupo.service.risk.WorkOrderService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 工单服务实现类 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class WorkOrderServiceImpl implements WorkOrderService { + + private final WorkOrderMapper workOrderMapper; + private final ProjectMapper projectMapper; + private final RiskMapper riskMapper; + private final SysUserMapper sysUserMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createWorkOrder(CreateWorkOrderRequest request) { + log.info("创建工单, projectId: {}, title: {}", request.getProjectId(), request.getTitle()); + + // 验证项目存在 + Project project = projectMapper.selectById(request.getProjectId()); + if (project == null || project.getDeleted() == 1) { + throw new RuntimeException("项目不存在"); + } + + WorkOrder workOrder = new WorkOrder(); + workOrder.setProjectId(request.getProjectId()); + workOrder.setOrderCode(generateOrderCode()); + workOrder.setRiskId(request.getRiskId()); + workOrder.setOrderType(request.getOrderType() != null ? request.getOrderType() : "other"); + workOrder.setTitle(request.getTitle()); + workOrder.setDescription(request.getDescription()); + workOrder.setPriority(request.getPriority() != null ? request.getPriority() : "medium"); + workOrder.setHandlerId(request.getHandlerId()); + workOrder.setHandlerGroupId(request.getHandlerGroupId()); + workOrder.setDeadline(request.getDeadline()); + workOrder.setSource(request.getSource() != null ? request.getSource() : "api"); + workOrder.setTags(request.getTags()); + + // 设置创建人和状态 + workOrder.setCreatorId(SecurityUtils.getCurrentUserId()); + if (request.getHandlerId() != null) { + workOrder.setStatus("assigned"); + workOrder.setAssignedTime(LocalDateTime.now()); + } else { + workOrder.setStatus("pending"); + } + + workOrderMapper.insert(workOrder); + log.info("工单创建成功, workOrderId: {}", workOrder.getId()); + + return workOrder.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateWorkOrder(Long workOrderId, CreateWorkOrderRequest request) { + log.info("更新工单, workOrderId: {}", workOrderId); + + WorkOrder workOrder = workOrderMapper.selectById(workOrderId); + if (workOrder == null || workOrder.getDeleted() == 1) { + throw new RuntimeException("工单不存在"); + } + + if (request.getTitle() != null) { + workOrder.setTitle(request.getTitle()); + } + if (request.getDescription() != null) { + workOrder.setDescription(request.getDescription()); + } + if (request.getPriority() != null) { + workOrder.setPriority(request.getPriority()); + } + if (request.getHandlerId() != null) { + workOrder.setHandlerId(request.getHandlerId()); + if ("pending".equals(workOrder.getStatus())) { + workOrder.setStatus("assigned"); + workOrder.setAssignedTime(LocalDateTime.now()); + } + } + if (request.getHandlerGroupId() != null) { + workOrder.setHandlerGroupId(request.getHandlerGroupId()); + } + if (request.getDeadline() != null) { + workOrder.setDeadline(request.getDeadline()); + } + if (request.getTags() != null) { + workOrder.setTags(request.getTags()); + } + + workOrderMapper.updateById(workOrder); + log.info("工单更新成功, workOrderId: {}", workOrderId); + + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteWorkOrder(Long workOrderId) { + log.info("删除工单, workOrderId: {}", workOrderId); + + WorkOrder workOrder = workOrderMapper.selectById(workOrderId); + if (workOrder == null || workOrder.getDeleted() == 1) { + throw new RuntimeException("工单不存在"); + } + + workOrderMapper.deleteById(workOrderId); + log.info("工单删除成功, workOrderId: {}", workOrderId); + + return true; + } + + @Override + public WorkOrderVO getWorkOrderDetail(Long workOrderId) { + log.info("获取工单详情, workOrderId: {}", workOrderId); + + WorkOrder workOrder = workOrderMapper.selectById(workOrderId); + if (workOrder == null || workOrder.getDeleted() == 1) { + throw new RuntimeException("工单不存在"); + } + + return convertToWorkOrderVO(workOrder); + } + + @Override + public TableDataInfo getWorkOrderList(Long projectId, Integer pageNum, Integer pageSize, + String orderType, String status, String priority, String keyword) { + log.info("分页查询工单列表, projectId: {}, pageNum: {}, pageSize: {}", projectId, pageNum, pageSize); + + Page page = new Page<>(pageNum, pageSize); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(WorkOrder::getDeleted, 0); + + if (projectId != null) { + wrapper.eq(WorkOrder::getProjectId, projectId); + } + if (orderType != null && !orderType.isEmpty()) { + wrapper.eq(WorkOrder::getOrderType, orderType); + } + if (status != null && !status.isEmpty()) { + wrapper.eq(WorkOrder::getStatus, status); + } + if (priority != null && !priority.isEmpty()) { + wrapper.eq(WorkOrder::getPriority, priority); + } + if (keyword != null && !keyword.isEmpty()) { + wrapper.and(w -> w.like(WorkOrder::getTitle, keyword) + .or() + .like(WorkOrder::getDescription, keyword)); + } + + // 按优先级和创建时间排序 + wrapper.orderByAsc( + w -> "critical".equals(w.getPriority()) ? 1 : + "high".equals(w.getPriority()) ? 2 : + "medium".equals(w.getPriority()) ? 3 : 4 + ).orderByDesc(WorkOrder::getCreateTime); + + Page orderPage = workOrderMapper.selectPage(page, wrapper); + + List voList = orderPage.getRecords().stream() + .map(this::convertToWorkOrderVO) + .collect(Collectors.toList()); + + return TableDataInfo.build(new Page(orderPage.getCurrent(), orderPage.getSize(), orderPage.getTotal()) + .setRecords(voList)); + } + + @Override + public TableDataInfo getMyWorkOrders(Integer pageNum, Integer pageSize, + String status, String orderType) { + Long userId = SecurityUtils.getCurrentUserId(); + log.info("查询我的工单列表, userId: {}, pageNum: {}, pageSize: {}", userId, pageNum, pageSize); + + Page page = new Page<>(pageNum, pageSize); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(WorkOrder::getDeleted, 0) + .eq(WorkOrder::getHandlerId, userId); + + if (status != null && !status.isEmpty()) { + wrapper.eq(WorkOrder::getStatus, status); + } + if (orderType != null && !orderType.isEmpty()) { + wrapper.eq(WorkOrder::getOrderType, orderType); + } + + // 按优先级和创建时间排序 + wrapper.orderByAsc( + w -> "critical".equals(w.getPriority()) ? 1 : + "high".equals(w.getPriority()) ? 2 : + "medium".equals(w.getPriority()) ? 3 : 4 + ).orderByDesc(WorkOrder::getCreateTime); + + Page orderPage = workOrderMapper.selectPage(page, wrapper); + + List voList = orderPage.getRecords().stream() + .map(this::convertToWorkOrderVO) + .collect(Collectors.toList()); + + return TableDataInfo.build(new Page(orderPage.getCurrent(), orderPage.getSize(), orderPage.getTotal()) + .setRecords(voList)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean processWorkOrder(ProcessWorkOrderRequest request) { + Long userId = SecurityUtils.getCurrentUserId(); + log.info("处理工单, workOrderId: {}, userId: {}", request.getWorkOrderId(), userId); + + WorkOrder workOrder = workOrderMapper.selectById(request.getWorkOrderId()); + if (workOrder == null || workOrder.getDeleted() == 1) { + throw new RuntimeException("工单不存在"); + } + + // 验证是否为处理人 + if (!userId.equals(workOrder.getHandlerId())) { + throw new RuntimeException("您不是该工单的处理人"); + } + + String oldStatus = workOrder.getStatus(); + String newStatus = request.getStatus(); + + // 更新状态 + if (newStatus != null) { + workOrder.setStatus(newStatus); + + // 记录首次响应时间 + if ("processing".equals(newStatus) && workOrder.getFirstResponseTime() == null) { + workOrder.setFirstResponseTime(LocalDateTime.now()); + } + + // 记录解决时间 + if ("resolved".equals(newStatus)) { + workOrder.setResolvedTime(LocalDateTime.now()); + } + + // 记录关闭时间 + if ("closed".equals(newStatus)) { + workOrder.setClosedTime(LocalDateTime.now()); + } + } + + // 更新满意度评分 + if (request.getSatisfactionScore() != null) { + workOrder.setSatisfactionScore(request.getSatisfactionScore()); + } + + workOrderMapper.updateById(workOrder); + + // 如果是风险工单,更新风险状态 + if ("resolved".equals(newStatus) && workOrder.getRiskId() != null) { + Risk risk = riskMapper.selectById(workOrder.getRiskId()); + if (risk != null && "assigned".equals(risk.getStatus())) { + risk.setStatus("mitigating"); + riskMapper.updateById(risk); + } + } + + log.info("工单处理成功, workOrderId: {}, status: {} -> {}", request.getWorkOrderId(), oldStatus, newStatus); + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean assignWorkOrder(Long workOrderId, Long handlerId) { + log.info("分配工单, workOrderId: {}, handlerId: {}", workOrderId, handlerId); + + WorkOrder workOrder = workOrderMapper.selectById(workOrderId); + if (workOrder == null || workOrder.getDeleted() == 1) { + throw new RuntimeException("工单不存在"); + } + + workOrder.setHandlerId(handlerId); + workOrder.setStatus("assigned"); + workOrder.setAssignedTime(LocalDateTime.now()); + + workOrderMapper.updateById(workOrder); + log.info("工单分配成功, workOrderId: {}", workOrderId); + + return true; + } + + @Override + public WorkOrderStatisticsVO getWorkOrderStatistics(Long projectId) { + log.info("获取工单统计信息, projectId: {}", projectId); + + WorkOrderStatisticsVO statistics = new WorkOrderStatisticsVO(); + + // 构建查询条件 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(WorkOrder::getDeleted, 0); + if (projectId != null) { + wrapper.eq(WorkOrder::getProjectId, projectId); + } + + List orders = workOrderMapper.selectList(wrapper); + + // 统计总数 + statistics.setTotalCount(orders.size()); + + // 按状态统计 + Map statusCount = orders.stream() + .collect(Collectors.groupingBy(WorkOrder::getStatus, Collectors.counting())); + statistics.setPendingCount(statusCount.getOrDefault("pending", 0L).intValue()); + statistics.setAssignedCount(statusCount.getOrDefault("assigned", 0L).intValue()); + statistics.setProcessingCount(statusCount.getOrDefault("processing", 0L).intValue()); + statistics.setCompletedCount(statusCount.getOrDefault("resolved", 0L).intValue()); + statistics.setClosedCount(statusCount.getOrDefault("closed", 0L).intValue()); + statistics.setRejectedCount(0); // 新结构没有 rejected 状态 + + // 统计超期未完成 + LocalDateTime now = LocalDateTime.now(); + int overdueCount = (int) orders.stream() + .filter(o -> !"resolved".equals(o.getStatus()) && !"closed".equals(o.getStatus())) + .filter(o -> o.getDeadline() != null && o.getDeadline().isBefore(now)) + .count(); + statistics.setOverdueCount(overdueCount); + + // 统计即将超期(7天内) + LocalDateTime sevenDaysLater = now.plusDays(7); + int aboutToExpireCount = (int) orders.stream() + .filter(o -> !"resolved".equals(o.getStatus()) && !"closed".equals(o.getStatus())) + .filter(o -> o.getDeadline() != null + && !o.getDeadline().isBefore(now) + && !o.getDeadline().isAfter(sevenDaysLater)) + .count(); + statistics.setAboutToExpireCount(aboutToExpireCount); + + // 按类型统计 + Map typeStats = orders.stream() + .collect(Collectors.groupingBy( + o -> o.getOrderType() != null ? o.getOrderType() : "other", + Collectors.counting() + )); + statistics.setTypeStats(typeStats); + + // 按优先级统计 + Map priorityStats = orders.stream() + .collect(Collectors.groupingBy( + o -> o.getPriority() != null ? o.getPriority() : "medium", + Collectors.counting() + )); + statistics.setPriorityStats(priorityStats); + + return statistics; + } + + @Override + public WorkOrderStatisticsVO getMyWorkOrderStatistics() { + Long userId = SecurityUtils.getCurrentUserId(); + log.info("获取我的工单统计信息, userId: {}", userId); + + WorkOrderStatisticsVO statistics = new WorkOrderStatisticsVO(); + + // 构建查询条件 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(WorkOrder::getDeleted, 0) + .eq(WorkOrder::getHandlerId, userId); + + List orders = workOrderMapper.selectList(wrapper); + + // 统计总数 + statistics.setTotalCount(orders.size()); + + // 按状态统计 + Map statusCount = orders.stream() + .collect(Collectors.groupingBy(WorkOrder::getStatus, Collectors.counting())); + statistics.setPendingCount(statusCount.getOrDefault("pending", 0L).intValue()); + statistics.setAssignedCount(statusCount.getOrDefault("assigned", 0L).intValue()); + statistics.setProcessingCount(statusCount.getOrDefault("processing", 0L).intValue()); + statistics.setCompletedCount(statusCount.getOrDefault("resolved", 0L).intValue()); + statistics.setClosedCount(statusCount.getOrDefault("closed", 0L).intValue()); + statistics.setRejectedCount(0); + + // 统计超期未完成 + LocalDateTime now = LocalDateTime.now(); + int overdueCount = (int) orders.stream() + .filter(o -> !"resolved".equals(o.getStatus()) && !"closed".equals(o.getStatus())) + .filter(o -> o.getDeadline() != null && o.getDeadline().isBefore(now)) + .count(); + statistics.setOverdueCount(overdueCount); + + // 统计即将超期(7天内) + LocalDateTime sevenDaysLater = now.plusDays(7); + int aboutToExpireCount = (int) orders.stream() + .filter(o -> !"resolved".equals(o.getStatus()) && !"closed".equals(o.getStatus())) + .filter(o -> o.getDeadline() != null + && !o.getDeadline().isBefore(now) + && !o.getDeadline().isAfter(sevenDaysLater)) + .count(); + statistics.setAboutToExpireCount(aboutToExpireCount); + + // 按类型统计 + Map typeStats = orders.stream() + .collect(Collectors.groupingBy( + o -> o.getOrderType() != null ? o.getOrderType() : "other", + Collectors.counting() + )); + statistics.setTypeStats(typeStats); + + // 按优先级统计 + Map priorityStats = orders.stream() + .collect(Collectors.groupingBy( + o -> o.getPriority() != null ? o.getPriority() : "medium", + Collectors.counting() + )); + statistics.setPriorityStats(priorityStats); + + return statistics; + } + + /** + * 生成工单编号 + */ + private String generateOrderCode() { + return "WO" + IdUtil.fastSimpleUUID().substring(0, 12).toUpperCase(); + } + + /** + * 转换为VO + */ + private WorkOrderVO convertToWorkOrderVO(WorkOrder workOrder) { + WorkOrderVO vo = new WorkOrderVO(); + vo.setId(workOrder.getId()); + vo.setOrderCode(workOrder.getOrderCode()); + vo.setOrderType(workOrder.getOrderType()); + vo.setProjectId(workOrder.getProjectId()); + vo.setRiskId(workOrder.getRiskId()); + vo.setTitle(workOrder.getTitle()); + vo.setDescription(workOrder.getDescription()); + vo.setCreatorId(workOrder.getCreatorId()); + vo.setHandlerId(workOrder.getHandlerId()); + vo.setHandlerGroupId(workOrder.getHandlerGroupId()); + vo.setPriority(workOrder.getPriority()); + vo.setStatus(workOrder.getStatus()); + vo.setSource(workOrder.getSource()); + vo.setDeadline(workOrder.getDeadline()); + vo.setAssignedTime(workOrder.getAssignedTime()); + vo.setFirstResponseTime(workOrder.getFirstResponseTime()); + vo.setResolvedTime(workOrder.getResolvedTime()); + vo.setClosedTime(workOrder.getClosedTime()); + vo.setSatisfactionScore(workOrder.getSatisfactionScore()); + vo.setTags(workOrder.getTags()); + vo.setCreateTime(workOrder.getCreateTime()); + vo.setUpdateTime(workOrder.getUpdateTime()); + + // 判断是否超期 + if (workOrder.getDeadline() != null + && !"resolved".equals(workOrder.getStatus()) + && !"closed".equals(workOrder.getStatus())) { + vo.setIsOverdue(workOrder.getDeadline().isBefore(LocalDateTime.now())); + } else { + vo.setIsOverdue(false); + } + + // 查询项目名称 + if (workOrder.getProjectId() != null) { + Project project = projectMapper.selectById(workOrder.getProjectId()); + if (project != null) { + vo.setProjectName(project.getProjectName()); + } + } + + // 查询风险名称 + if (workOrder.getRiskId() != null) { + Risk risk = riskMapper.selectById(workOrder.getRiskId()); + if (risk != null) { + vo.setRiskName(risk.getRiskName()); + } + } + + // 查询创建人信息 + if (workOrder.getCreatorId() != null) { + SysUser creator = sysUserMapper.selectById(workOrder.getCreatorId()); + if (creator != null) { + vo.setCreatorName(creator.getRealName()); + } + } + + // 查询处理人信息 + if (workOrder.getHandlerId() != null) { + SysUser handler = sysUserMapper.selectById(workOrder.getHandlerId()); + if (handler != null) { + vo.setHandlerName(handler.getRealName()); + vo.setHandlerAvatar(handler.getAvatar()); + } + } + + return vo; + } +} diff --git a/src/main/resources/mapper/RiskMapper.xml b/src/main/resources/mapper/RiskMapper.xml index abbe876..fb4392c 100644 --- a/src/main/resources/mapper/RiskMapper.xml +++ b/src/main/resources/mapper/RiskMapper.xml @@ -19,6 +19,7 @@ + diff --git a/src/main/resources/mapper/WorkOrderMapper.xml b/src/main/resources/mapper/WorkOrderMapper.xml new file mode 100644 index 0000000..40212dd --- /dev/null +++ b/src/main/resources/mapper/WorkOrderMapper.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +