feat(risk): 实现全面的风险服务逻辑

- 新增风险创建、更新、删除及详情查询功能
- 支持分页查询风险列表及风险统计信息
- 完成风险分配工单及批量更新风险状态功能
- 集成AI风险评估,包含项目数据构建和识别风险保存
- 实现风险等级计算和风险视图转换逻辑
- 实现权限校验确保项目和风险数据访问安全
This commit is contained in:
2026-03-30 14:47:32 +08:00
parent 3758213989
commit 4d20bf21cc
2 changed files with 96 additions and 20 deletions

View File

@@ -22,6 +22,7 @@ import cn.yinlihupo.mapper.TaskMapper;
import cn.yinlihupo.mapper.WorkOrderMapper;
import cn.yinlihupo.service.risk.RiskService;
import cn.yinlihupo.service.risk.WorkOrderService;
import cn.yinlihupo.service.system.ProjectPermissionService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
@@ -53,6 +54,7 @@ public class RiskServiceImpl implements RiskService {
private final WorkOrderMapper workOrderMapper;
private final WorkOrderService workOrderService;
private final ChatClient chatClient;
private final ProjectPermissionService projectPermissionService;
/**
* AI风险评估系统提示词模板
@@ -268,14 +270,49 @@ public class RiskServiceImpl implements RiskService {
String category, String riskLevel, String status, String keyword) {
log.info("分页查询风险列表, projectId: {}, pageNum: {}, pageSize: {}", projectId, pageNum, pageSize);
// 获取当前用户
Long userId = SecurityUtils.getCurrentUserId();
boolean isAdmin = SecurityUtils.isAdmin();
// 获取用户可见的项目ID列表
List<Long> visibleProjectIds = null;
if (!isAdmin) {
// 非超管只能查看自己参与的项目风险
visibleProjectIds = projectMemberMapper.selectList(
new LambdaQueryWrapper<ProjectMember>()
.eq(ProjectMember::getUserId, userId)
.eq(ProjectMember::getDeleted, 0)
).stream().map(ProjectMember::getProjectId).collect(Collectors.toList());
if (visibleProjectIds.isEmpty()) {
// 没有参与任何项目,返回空结果
return TableDataInfo.build(new Page<RiskVO>(pageNum, pageSize, 0).setRecords(new ArrayList<>()));
}
}
Page<Risk> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<Risk> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Risk::getDeleted, 0);
if (projectId != null) {
wrapper.eq(Risk::getProjectId, projectId);
// 权限过滤:非超管只能查看参与项目的风险
if (!isAdmin && visibleProjectIds != null) {
if (projectId != null) {
// 如果指定了项目ID需要验证用户是否有权限访问该项目
if (!visibleProjectIds.contains(projectId)) {
return TableDataInfo.build(new Page<RiskVO>(pageNum, pageSize, 0).setRecords(new ArrayList<>()));
}
wrapper.eq(Risk::getProjectId, projectId);
} else {
wrapper.in(Risk::getProjectId, visibleProjectIds);
}
} else {
// 超管可以查看所有项目的风险
if (projectId != null) {
wrapper.eq(Risk::getProjectId, projectId);
}
}
if (category != null && !category.isEmpty()) {
wrapper.eq(Risk::getCategory, category);
}
@@ -308,13 +345,48 @@ public class RiskServiceImpl implements RiskService {
public RiskStatisticsVO getRiskStatistics(Long projectId) {
log.info("获取风险统计信息, projectId: {}", projectId);
// 获取当前用户
Long userId = SecurityUtils.getCurrentUserId();
boolean isAdmin = SecurityUtils.isAdmin();
// 获取用户可见的项目ID列表
List<Long> visibleProjectIds = null;
if (!isAdmin) {
// 非超管只能查看自己参与的项目风险
visibleProjectIds = projectMemberMapper.selectList(
new LambdaQueryWrapper<ProjectMember>()
.eq(ProjectMember::getUserId, userId)
.eq(ProjectMember::getDeleted, 0)
).stream().map(ProjectMember::getProjectId).collect(Collectors.toList());
if (visibleProjectIds.isEmpty()) {
// 没有参与任何项目,返回空统计
return new RiskStatisticsVO();
}
}
RiskStatisticsVO statistics = new RiskStatisticsVO();
// 构建查询条件
LambdaQueryWrapper<Risk> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Risk::getDeleted, 0);
if (projectId != null) {
wrapper.eq(Risk::getProjectId, projectId);
// 权限过滤
if (!isAdmin && visibleProjectIds != null) {
if (projectId != null) {
// 如果指定了项目ID需要验证用户是否有权限访问该项目
if (!visibleProjectIds.contains(projectId)) {
return new RiskStatisticsVO();
}
wrapper.eq(Risk::getProjectId, projectId);
} else {
wrapper.in(Risk::getProjectId, visibleProjectIds);
}
} else {
// 超管可以查看所有项目的风险统计
if (projectId != null) {
wrapper.eq(Risk::getProjectId, projectId);
}
}
List<Risk> risks = riskMapper.selectList(wrapper);
@@ -509,16 +581,28 @@ public class RiskServiceImpl implements RiskService {
public RiskAssessmentResult assessProjectRisk(Long projectId) {
log.info("开始AI风险评估, projectId: {}", projectId);
// 获取当前用户
Long userId = SecurityUtils.getCurrentUserId();
boolean isAdmin = SecurityUtils.isAdmin();
log.info("风险评估请求用户: userId={}, isAdmin={}", userId, isAdmin);
// 1. 验证项目存在
Project project = projectMapper.selectById(projectId);
if (project == null || project.getDeleted() == 1) {
throw new RuntimeException("项目不存在");
}
// 2. 构建项目数据上下文
// 2. 权限校验:超管可评估所有项目,普通用户只能评估参与的项目
if (!isAdmin) {
if (!projectPermissionService.isProjectMember(userId, projectId)) {
throw new RuntimeException("您没有权限评估该项目");
}
}
// 3. 构建项目数据上下文
String projectDataContext = buildProjectDataContext(projectId);
// 3. 构建用户提示词
// 4. 构建用户提示词
String userPrompt = """
请根据以下项目数据,进行全面的风险评估分析:
@@ -527,7 +611,7 @@ public class RiskServiceImpl implements RiskService {
请严格按照系统提示词中的JSON格式输出评估结果确保识别的风险具体且可操作。
""".formatted(projectDataContext);
// 4. 调用AI进行风险评估
// 5. 调用AI进行风险评估
log.info("调用AI进行风险评估...");
RiskAssessmentResult result = chatClient.prompt()
.system(RISK_ASSESSMENT_SYSTEM_PROMPT)
@@ -535,7 +619,7 @@ public class RiskServiceImpl implements RiskService {
.call()
.entity(RiskAssessmentResult.class);
// 5. 设置项目信息
// 6. 设置项目信息
result.setProjectId(projectId);
result.setProjectName(project.getProjectName());
result.setAssessmentDate(LocalDate.now());

View File

@@ -172,12 +172,8 @@ public class WorkOrderServiceImpl implements WorkOrderService {
.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);
// 按优先级和创建时间排序使用原生SQL避免Lambda表达式问题
wrapper.last("ORDER BY CASE priority WHEN 'critical' THEN 1 WHEN 'high' THEN 2 WHEN 'medium' THEN 3 ELSE 4 END ASC, create_time DESC");
Page<WorkOrder> orderPage = workOrderMapper.selectPage(page, wrapper);
@@ -208,12 +204,8 @@ public class WorkOrderServiceImpl implements WorkOrderService {
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);
// 按优先级和创建时间排序使用原生SQL避免Lambda表达式问题
wrapper.last("ORDER BY CASE priority WHEN 'critical' THEN 1 WHEN 'high' THEN 2 WHEN 'medium' THEN 3 ELSE 4 END ASC, create_time DESC");
Page<WorkOrder> orderPage = workOrderMapper.selectPage(page, wrapper);