diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankStatementController.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankStatementController.java index e8520b3f..28aa89b9 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankStatementController.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankStatementController.java @@ -6,6 +6,7 @@ import com.ruoyi.ccdi.project.domain.excel.CcdiBankStatementExcel; import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementDetailVO; import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementFilterOptionsVO; import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementListVO; +import com.ruoyi.ccdi.project.service.CcdiProjectAccessService; import com.ruoyi.ccdi.project.service.ICcdiBankStatementService; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; @@ -39,13 +40,16 @@ public class CcdiBankStatementController extends BaseController { @Resource private ICcdiBankStatementService bankStatementService; + @Resource + private CcdiProjectAccessService projectAccessService; + /** * 分页查询流水明细 */ @GetMapping("/list") @Operation(summary = "分页查询流水明细") - @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public TableDataInfo list(CcdiBankStatementQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); PageDomain pageDomain = TableSupport.buildPageRequest(); Page page = new Page<>(pageDomain.getPageNum(), pageDomain.getPageSize()); Page result = bankStatementService.selectStatementPage(page, queryDTO); @@ -57,8 +61,8 @@ public class CcdiBankStatementController extends BaseController { */ @GetMapping("/options") @Operation(summary = "查询项目级筛选项") - @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getOptions(Long projectId) { + projectAccessService.assertCanRead(projectId); CcdiBankStatementFilterOptionsVO options = bankStatementService.getFilterOptions(projectId); return AjaxResult.success(options); } @@ -68,8 +72,8 @@ public class CcdiBankStatementController extends BaseController { */ @GetMapping("/detail/{bankStatementId}") @Operation(summary = "查询流水详情") - @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getDetail(@PathVariable Long bankStatementId) { + projectAccessService.assertCanReadByBankStatementId(bankStatementId); CcdiBankStatementDetailVO detail = bankStatementService.getStatementDetail(bankStatementId); return AjaxResult.success(detail); } @@ -81,6 +85,7 @@ public class CcdiBankStatementController extends BaseController { @Operation(summary = "导出流水明细") @PreAuthorize("@ss.hasPermi('ccdi:project:export')") public void export(HttpServletResponse response, CcdiBankStatementQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); List list = bankStatementService.selectStatementListForExport(queryDTO); ExcelUtil util = new ExcelUtil<>(CcdiBankStatementExcel.class); util.exportExcel(response, list, "流水明细"); diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankTagController.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankTagController.java index e5498bbc..e14fbfc1 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankTagController.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankTagController.java @@ -1,6 +1,7 @@ package com.ruoyi.ccdi.project.controller; import com.ruoyi.ccdi.project.domain.dto.CcdiBankTagRebuildDTO; +import com.ruoyi.ccdi.project.service.CcdiProjectAccessService; import com.ruoyi.ccdi.project.service.ICcdiBankTagService; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; @@ -9,6 +10,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -27,12 +29,17 @@ public class CcdiBankTagController extends BaseController { @Resource private ICcdiBankTagService bankTagService; + @Resource + private CcdiProjectAccessService projectAccessService; + /** * 手动提交流水标签重算任务 */ @Operation(summary = "手动重算项目流水标签") @PostMapping("/rebuild") + @PreAuthorize("@ss.hasPermi('ccdi:project:edit')") public AjaxResult rebuild(@Validated @RequestBody CcdiBankTagRebuildDTO dto) { + projectAccessService.assertCanOperate(dto.getProjectId()); String operator = SecurityUtils.getUsername(); log.info("【流水标签】收到手动重算请求: projectId={}, modelCode={}, operator={}", dto.getProjectId(), dto.getModelCode(), operator); diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiEvidenceController.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiEvidenceController.java index c977876b..61132d0a 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiEvidenceController.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiEvidenceController.java @@ -3,6 +3,7 @@ package com.ruoyi.ccdi.project.controller; import com.ruoyi.ccdi.project.domain.dto.CcdiEvidenceQueryDTO; import com.ruoyi.ccdi.project.domain.dto.CcdiEvidenceSaveDTO; import com.ruoyi.ccdi.project.domain.vo.CcdiEvidenceVO; +import com.ruoyi.ccdi.project.service.CcdiProjectAccessService; import com.ruoyi.ccdi.project.service.ICcdiEvidenceService; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; @@ -34,13 +35,17 @@ public class CcdiEvidenceController extends BaseController { @Resource private ICcdiEvidenceService evidenceService; + @Resource + private CcdiProjectAccessService projectAccessService; + /** * 保存证据 */ @PostMapping @Operation(summary = "保存证据") - @PreAuthorize("@ss.hasPermi('ccdi:project:query')") + @PreAuthorize("@ss.hasPermi('ccdi:project:edit')") public AjaxResult saveEvidence(@Validated @RequestBody CcdiEvidenceSaveDTO dto) { + projectAccessService.assertCanOperate(dto.getProjectId()); CcdiEvidenceVO vo = evidenceService.saveEvidence(dto, SecurityUtils.getUsername()); return AjaxResult.success("证据入库成功", vo); } @@ -50,8 +55,8 @@ public class CcdiEvidenceController extends BaseController { */ @GetMapping("/list") @Operation(summary = "查询项目证据列表") - @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult listEvidence(CcdiEvidenceQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); List list = evidenceService.listEvidence(queryDTO); return AjaxResult.success(list); } @@ -61,8 +66,8 @@ public class CcdiEvidenceController extends BaseController { */ @GetMapping("/{evidenceId}") @Operation(summary = "查询证据详情") - @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getEvidence(@PathVariable Long evidenceId) { + projectAccessService.assertCanReadByEvidenceId(evidenceId); CcdiEvidenceVO vo = evidenceService.getEvidence(evidenceId); return AjaxResult.success(vo); } diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiFileUploadController.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiFileUploadController.java index d35a64a1..fc7ac5cc 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiFileUploadController.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiFileUploadController.java @@ -6,6 +6,7 @@ import com.ruoyi.ccdi.project.domain.dto.CcdiPullBankInfoSubmitDTO; import com.ruoyi.ccdi.project.domain.entity.CcdiFileUploadRecord; import com.ruoyi.ccdi.project.domain.vo.CcdiFileUploadStatisticsVO; import com.ruoyi.ccdi.project.domain.vo.CcdiIdCardParseVO; +import com.ruoyi.ccdi.project.service.CcdiProjectAccessService; import com.ruoyi.ccdi.project.service.ICcdiFileUploadService; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; @@ -17,6 +18,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; @@ -40,17 +42,22 @@ public class CcdiFileUploadController extends BaseController { @Resource private ICcdiFileUploadService fileUploadService; + @Resource + private CcdiProjectAccessService projectAccessService; + /** * 批量上传文件(异步) */ @PostMapping("/batch") @Operation(summary = "批量上传文件", description = "异步批量上传流水文件") + @PreAuthorize("@ss.hasPermi('ccdi:project:edit')") public AjaxResult batchUpload(@RequestParam Long projectId, @RequestParam MultipartFile[] files) { // 参数校验 if (projectId == null) { return AjaxResult.error("项目ID不能为空"); } + projectAccessService.assertCanOperate(projectId); if (files == null || files.length == 0) { return AjaxResult.error("请选择要上传的文件"); } @@ -95,6 +102,7 @@ public class CcdiFileUploadController extends BaseController { */ @PostMapping("/parse-id-card-file") @Operation(summary = "解析身份证文件", description = "解析首个sheet第一列的身份证号") + @PreAuthorize("@ss.hasPermi('ccdi:project:edit')") public AjaxResult parseIdCardFile(@RequestParam MultipartFile file) { if (file == null || file.isEmpty()) { return AjaxResult.error("身份证文件不能为空"); @@ -108,10 +116,12 @@ public class CcdiFileUploadController extends BaseController { */ @PostMapping("/pull-bank-info") @Operation(summary = "拉取本行信息", description = "按身份证号批量提交拉取本行信息任务") + @PreAuthorize("@ss.hasPermi('ccdi:project:edit')") public AjaxResult pullBankInfo(@RequestBody CcdiPullBankInfoSubmitDTO dto) { if (dto == null || dto.getProjectId() == null) { return AjaxResult.error("项目ID不能为空"); } + projectAccessService.assertCanOperate(dto.getProjectId()); if (CollectionUtils.isEmpty(dto.getIdCards())) { return AjaxResult.error("身份证号不能为空"); } @@ -138,6 +148,7 @@ public class CcdiFileUploadController extends BaseController { @GetMapping("/list") @Operation(summary = "查询上传记录列表", description = "分页查询文件上传记录") public TableDataInfo list(CcdiFileUploadQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); PageDomain pageDomain = TableSupport.buildPageRequest(); Page page = new Page<>(pageDomain.getPageNum(), pageDomain.getPageSize()); Page result = fileUploadService.selectPage(page, queryDTO); @@ -150,6 +161,7 @@ public class CcdiFileUploadController extends BaseController { @GetMapping("/statistics/{projectId}") @Operation(summary = "查询上传统计", description = "统计各状态的文件数量") public AjaxResult getStatistics(@PathVariable Long projectId) { + projectAccessService.assertCanRead(projectId); CcdiFileUploadStatisticsVO statistics = fileUploadService.countByStatus(projectId); return AjaxResult.success(statistics); } @@ -160,6 +172,7 @@ public class CcdiFileUploadController extends BaseController { @GetMapping("/detail/{id}") @Operation(summary = "查询记录详情", description = "根据ID查询文件上传记录详情") public AjaxResult getDetail(@PathVariable Long id) { + projectAccessService.assertCanReadByFileRecordId(id); CcdiFileUploadRecord record = fileUploadService.getById(id); return AjaxResult.success(record); } @@ -169,7 +182,9 @@ public class CcdiFileUploadController extends BaseController { */ @DeleteMapping("/{id}") @Operation(summary = "删除上传文件", description = "按上传记录ID删除文件并清理流水") + @PreAuthorize("@ss.hasPermi('ccdi:project:edit')") public AjaxResult deleteFile(@PathVariable Long id) { + projectAccessService.assertCanOperateByFileRecordId(id); Long userId = SecurityUtils.getUserId(); String message = fileUploadService.deleteFileUploadRecord(id, userId); return AjaxResult.success(message); diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiFundGraphController.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiFundGraphController.java index e41b75d2..af63cd5a 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiFundGraphController.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiFundGraphController.java @@ -8,6 +8,7 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphEdgeVO; import com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphNodeVO; import com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphStatementVO; import com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphVO; +import com.ruoyi.ccdi.project.service.CcdiProjectAccessService; import com.ruoyi.ccdi.project.service.ICcdiFundGraphService; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; @@ -38,10 +39,14 @@ public class CcdiFundGraphController extends BaseController { @Resource private ICcdiFundGraphService fundGraphService; + @Resource + private CcdiProjectAccessService projectAccessService; + @GetMapping("/search") @Operation(summary = "查询资金流图谱主体") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult searchSubjects(CcdiFundGraphQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); List subjects = fundGraphService.searchSubjects(queryDTO); return AjaxResult.success(subjects); } @@ -50,6 +55,7 @@ public class CcdiFundGraphController extends BaseController { @Operation(summary = "查询一层资金流图谱") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getGraph(CcdiFundGraphQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); CcdiFundGraphVO graph = fundGraphService.getFundGraph(queryDTO); return AjaxResult.success(graph); } @@ -58,6 +64,7 @@ public class CcdiFundGraphController extends BaseController { @Operation(summary = "查询资金边流水明细") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public TableDataInfo getEdgeDetail(CcdiFundGraphEdgeDetailQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); PageDomain pageDomain = TableSupport.buildPageRequest(); Page page = new Page<>(pageDomain.getPageNum(), pageDomain.getPageSize()); Page result = fundGraphService.getEdgeDetails(page, queryDTO); @@ -66,9 +73,10 @@ public class CcdiFundGraphController extends BaseController { @PostMapping("/manual-edge") @Operation(summary = "新增手工资金流向") - @PreAuthorize("@ss.hasPermi('ccdi:project:query')") + @PreAuthorize("@ss.hasPermi('ccdi:project:edit')") public AjaxResult saveManualEdge(@RequestBody CcdiFundGraphManualEdgeSaveDTO saveDTO) { try { + projectAccessService.assertCanOperate(saveDTO == null ? null : saveDTO.getProjectId()); CcdiFundGraphEdgeVO edge = fundGraphService.saveManualEdge(saveDTO, SecurityUtils.getUsername()); return AjaxResult.success(edge); } catch (IllegalArgumentException e) { diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiModelParamController.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiModelParamController.java index 00f85b7e..f52b8a01 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiModelParamController.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiModelParamController.java @@ -11,9 +11,11 @@ import com.ruoyi.ccdi.project.domain.dto.ModelParamSaveAllDTO; import com.ruoyi.ccdi.project.domain.vo.ModelListVO; import com.ruoyi.ccdi.project.domain.vo.ModelParamVO; import com.ruoyi.ccdi.project.domain.vo.ModelParamAllVO; +import com.ruoyi.ccdi.project.service.CcdiProjectAccessService; import com.ruoyi.ccdi.project.service.ICcdiModelParamService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -31,12 +33,17 @@ public class CcdiModelParamController extends BaseController { @Resource private ICcdiModelParamService modelParamService; + @Resource + private CcdiProjectAccessService projectAccessService; + /** * 查询模型列表 */ @Operation(summary = "查询模型列表") @GetMapping("/modelList") + @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult listModels(@RequestParam(required = false) Long projectId) { + assertCanReadProjectParam(projectId); List list = modelParamService.selectModelList(projectId); return success(list); } @@ -46,7 +53,9 @@ public class CcdiModelParamController extends BaseController { */ @Operation(summary = "查询模型参数列表") @GetMapping("/list") + @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult list(@Validated ModelParamQueryDTO queryDTO) { + assertCanReadProjectParam(queryDTO.getProjectId()); List list = modelParamService.selectParamList(queryDTO); return success(list); } @@ -57,7 +66,9 @@ public class CcdiModelParamController extends BaseController { @Operation(summary = "保存模型参数") @Log(title = "模型参数配置", businessType = BusinessType.UPDATE) @PostMapping("/save") + @PreAuthorize("@ss.hasPermi('ccdi:project:edit')") public AjaxResult save(@Validated @RequestBody ModelParamSaveDTO saveDTO) { + assertCanOperateProjectParam(saveDTO.getProjectId()); modelParamService.saveParams(saveDTO); return success("保存成功"); } @@ -67,7 +78,9 @@ public class CcdiModelParamController extends BaseController { */ @Operation(summary = "查询所有模型及其参数") @GetMapping("/listAll") + @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult listAll(@Validated ModelParamAllQueryDTO queryDTO) { + assertCanReadProjectParam(queryDTO.getProjectId()); ModelParamAllVO result = modelParamService.selectAllParams(queryDTO.getProjectId()); return success(result); } @@ -78,8 +91,24 @@ public class CcdiModelParamController extends BaseController { @Operation(summary = "批量保存所有模型参数") @Log(title = "模型参数配置", businessType = BusinessType.UPDATE) @PostMapping("/saveAll") + @PreAuthorize("@ss.hasPermi('ccdi:project:edit')") public AjaxResult saveAll(@Validated @RequestBody ModelParamSaveAllDTO saveAllDTO) { + assertCanOperateProjectParam(saveAllDTO.getProjectId()); modelParamService.saveAllParams(saveAllDTO); return success("保存成功"); } + + private void assertCanReadProjectParam(Long projectId) { + if (projectId == null || projectId <= 0) { + return; + } + projectAccessService.assertCanRead(projectId); + } + + private void assertCanOperateProjectParam(Long projectId) { + if (projectId == null || projectId <= 0) { + return; + } + projectAccessService.assertCanOperate(projectId); + } } diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectController.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectController.java index 7c419956..7a9da790 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectController.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectController.java @@ -85,7 +85,6 @@ public class CcdiProjectController extends BaseController { */ @GetMapping("/{projectId}") @Operation(summary = "查询项目详情") - @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getProject(@PathVariable Long projectId) { CcdiProjectVO vo = projectService.getProjectById(projectId); return AjaxResult.success(vo); @@ -96,7 +95,6 @@ public class CcdiProjectController extends BaseController { */ @GetMapping("/list") @Operation(summary = "查询项目列表") - @PreAuthorize("@ss.hasPermi('ccdi:project:list')") public TableDataInfo listProject(CcdiProjectQueryDTO queryDTO) { PageDomain pageDomain = TableSupport.buildPageRequest(); Page page = new Page<>(pageDomain.getPageNum(), pageDomain.getPageSize()); @@ -109,7 +107,6 @@ public class CcdiProjectController extends BaseController { */ @GetMapping("/history") @Operation(summary = "查询历史项目列表") - @PreAuthorize("@ss.hasPermi('ccdi:project:list')") public AjaxResult listHistoryProjects(CcdiProjectQueryDTO queryDTO) { List result = projectService.listHistoryProjects(queryDTO); return AjaxResult.success(result); @@ -131,7 +128,6 @@ public class CcdiProjectController extends BaseController { */ @GetMapping("/statusCounts") @Operation(summary = "查询项目状态统计") - @PreAuthorize("@ss.hasPermi('ccdi:project:list')") public AjaxResult getStatusCounts() { CcdiProjectStatusCountsVO counts = projectService.getStatusCounts(); return AjaxResult.success(counts); diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewController.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewController.java index 6481acfb..33f1c08e 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewController.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewController.java @@ -23,6 +23,7 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionPageVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleVO; +import com.ruoyi.ccdi.project.service.CcdiProjectAccessService; import com.ruoyi.ccdi.project.service.ICcdiProjectOverviewService; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.common.core.controller.BaseController; @@ -51,6 +52,9 @@ public class CcdiProjectOverviewController extends BaseController { @Resource private ICcdiProjectOverviewService overviewService; + @Resource + private CcdiProjectAccessService projectAccessService; + /** * 查询风险仪表盘 */ @@ -58,6 +62,7 @@ public class CcdiProjectOverviewController extends BaseController { @Operation(summary = "查询风险仪表盘") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getDashboard(Long projectId) { + projectAccessService.assertCanRead(projectId); CcdiProjectOverviewDashboardVO dashboard = overviewService.getDashboard(projectId); return AjaxResult.success(dashboard); } @@ -69,6 +74,7 @@ public class CcdiProjectOverviewController extends BaseController { @Operation(summary = "查询风险人员总览") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getRiskPeople(CcdiProjectRiskPeopleQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); CcdiProjectRiskPeopleOverviewVO overview = overviewService.getRiskPeopleOverview(queryDTO); return AjaxResult.success(overview); } @@ -80,6 +86,7 @@ public class CcdiProjectOverviewController extends BaseController { @Operation(summary = "查询外部人员预警") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getExternalPersons(CcdiProjectExternalPersonQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); CcdiProjectExternalPersonWarningVO warnings = overviewService.getExternalPersonWarnings(queryDTO); return AjaxResult.success(warnings); } @@ -91,6 +98,7 @@ public class CcdiProjectOverviewController extends BaseController { @Operation(summary = "查询外部人员风险汇总") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getExternalRiskSummary(Long projectId) { + projectAccessService.assertCanRead(projectId); CcdiProjectExternalRiskSummaryVO summary = overviewService.getExternalRiskSummary(projectId); return AjaxResult.success(summary); } @@ -102,6 +110,7 @@ public class CcdiProjectOverviewController extends BaseController { @Operation(summary = "查询中高风险人员TOP10") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getTopRiskPeople(Long projectId) { + projectAccessService.assertCanRead(projectId); CcdiProjectTopRiskPeopleVO topRiskPeople = overviewService.getTopRiskPeople(projectId); return AjaxResult.success(topRiskPeople); } @@ -113,6 +122,7 @@ public class CcdiProjectOverviewController extends BaseController { @Operation(summary = "查询风险模型卡片") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getRiskModelCards(Long projectId) { + projectAccessService.assertCanRead(projectId); CcdiProjectRiskModelCardsVO cards = overviewService.getRiskModelCards(projectId); return AjaxResult.success(cards); } @@ -124,6 +134,7 @@ public class CcdiProjectOverviewController extends BaseController { @Operation(summary = "查询风险模型命中人员") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getRiskModelPeople(CcdiProjectRiskModelPeopleQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); CcdiProjectRiskModelPeopleVO people = overviewService.getRiskModelPeople(queryDTO); return AjaxResult.success(people); } @@ -135,6 +146,7 @@ public class CcdiProjectOverviewController extends BaseController { @Operation(summary = "查询外部人员风险模型卡片") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getExternalRiskModelCards(Long projectId) { + projectAccessService.assertCanRead(projectId); CcdiProjectRiskModelCardsVO cards = overviewService.getExternalRiskModelCards(projectId); return AjaxResult.success(cards); } @@ -146,6 +158,7 @@ public class CcdiProjectOverviewController extends BaseController { @Operation(summary = "查询外部人员风险模型命中人员") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getExternalRiskModelPeople(CcdiProjectExternalRiskModelPeopleQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); CcdiProjectRiskModelPeopleVO people = overviewService.getExternalRiskModelPeople(queryDTO); return AjaxResult.success(people); } @@ -157,6 +170,7 @@ public class CcdiProjectOverviewController extends BaseController { @Operation(summary = "查询项目分析详情") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getPersonAnalysisDetail(CcdiProjectPersonAnalysisDetailQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); CcdiProjectPersonAnalysisDetailVO detail = overviewService.getPersonAnalysisDetail(queryDTO); return AjaxResult.success(detail); } @@ -168,6 +182,7 @@ public class CcdiProjectOverviewController extends BaseController { @Operation(summary = "查询涉疑交易明细") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getSuspiciousTransactions(CcdiProjectSuspiciousTransactionQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); CcdiProjectSuspiciousTransactionPageVO pageVO = overviewService.getSuspiciousTransactions(queryDTO); return AjaxResult.success(pageVO); } @@ -179,6 +194,7 @@ public class CcdiProjectOverviewController extends BaseController { @Operation(summary = "查询项目员工负面征信") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getEmployeeCreditNegative(CcdiProjectEmployeeCreditNegativeQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); CcdiProjectEmployeeCreditNegativePageVO pageVO = overviewService.getEmployeeCreditNegative(queryDTO); return AjaxResult.success(pageVO); } @@ -190,6 +206,7 @@ public class CcdiProjectOverviewController extends BaseController { @Operation(summary = "查询异常账户人员信息") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getAbnormalAccountPeople(CcdiProjectAbnormalAccountQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); CcdiProjectAbnormalAccountPageVO pageVO = overviewService.getAbnormalAccountPeople(queryDTO); return AjaxResult.success(pageVO); } @@ -204,6 +221,7 @@ public class CcdiProjectOverviewController extends BaseController { HttpServletResponse response, CcdiProjectSuspiciousTransactionQueryDTO queryDTO ) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); List rows = overviewService.exportSuspiciousTransactions(queryDTO); ExcelUtil util = new ExcelUtil<>(CcdiProjectSuspiciousTransactionExcel.class); @@ -217,6 +235,7 @@ public class CcdiProjectOverviewController extends BaseController { @Operation(summary = "导出风险人员总览") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public void exportRiskPeople(HttpServletResponse response, Long projectId) { + projectAccessService.assertCanRead(projectId); List rows = overviewService.exportRiskPeopleOverview(projectId); ExcelUtil util = new ExcelUtil<>(CcdiProjectRiskPeopleOverviewExcel.class); @@ -230,6 +249,7 @@ public class CcdiProjectOverviewController extends BaseController { @Operation(summary = "导出外部人员预警") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public void exportExternalPersons(HttpServletResponse response, Long projectId) { + projectAccessService.assertCanRead(projectId); List rows = overviewService.exportExternalPersonWarnings(projectId); ExcelUtil util = new ExcelUtil<>(CcdiProjectExternalPersonWarningExcel.class); @@ -243,6 +263,7 @@ public class CcdiProjectOverviewController extends BaseController { @Operation(summary = "导出风险模型命中人员") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public void exportRiskModelPeople(HttpServletResponse response, CcdiProjectRiskModelPeopleQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); List rows = overviewService.exportRiskModelPeople(queryDTO); ExcelUtil util = new ExcelUtil<>(CcdiProjectRiskModelPeopleExcel.class); @@ -259,6 +280,7 @@ public class CcdiProjectOverviewController extends BaseController { HttpServletResponse response, CcdiProjectExternalRiskModelPeopleQueryDTO queryDTO ) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); List rows = overviewService.exportExternalRiskModelPeople(queryDTO); ExcelUtil util = new ExcelUtil<>(CcdiProjectRiskModelPeopleExcel.class); @@ -272,6 +294,7 @@ public class CcdiProjectOverviewController extends BaseController { @Operation(summary = "导出风险明细") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public void exportRiskDetails(HttpServletResponse response, Long projectId) { + projectAccessService.assertCanRead(projectId); overviewService.exportRiskDetails(response, projectId); } @@ -282,6 +305,7 @@ public class CcdiProjectOverviewController extends BaseController { @Operation(summary = "导出结果总览报告") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public void exportOverviewReport(HttpServletResponse response, Long projectId) { + projectAccessService.assertCanRead(projectId); overviewService.exportOverviewReport(response, projectId); } } diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectSpecialCheckController.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectSpecialCheckController.java index 5bb9a026..a85d6636 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectSpecialCheckController.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectSpecialCheckController.java @@ -16,6 +16,7 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectExtendedTransferDetailVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectExtendedTransferListVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityDetailVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityListVO; +import com.ruoyi.ccdi.project.service.CcdiProjectAccessService; import com.ruoyi.ccdi.project.service.ICcdiProjectSpecialCheckService; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; @@ -39,6 +40,9 @@ public class CcdiProjectSpecialCheckController extends BaseController { @Resource private ICcdiProjectSpecialCheckService specialCheckService; + @Resource + private CcdiProjectAccessService projectAccessService; + /** * 查询员工家庭资产负债列表 */ @@ -46,6 +50,7 @@ public class CcdiProjectSpecialCheckController extends BaseController { @Operation(summary = "查询员工家庭资产负债列表") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getFamilyAssetLiabilityList(@Validated CcdiProjectFamilyAssetLiabilityListQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); CcdiProjectFamilyAssetLiabilityListVO result = specialCheckService.getFamilyAssetLiabilityList(queryDTO); return AjaxResult.success(result); } @@ -57,6 +62,7 @@ public class CcdiProjectSpecialCheckController extends BaseController { @Operation(summary = "查询员工家庭资产负债详情") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getFamilyAssetLiabilityDetail(@Validated CcdiProjectFamilyAssetLiabilityDetailQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); CcdiProjectFamilyAssetLiabilityDetailVO result = specialCheckService.getFamilyAssetLiabilityDetail(queryDTO); return AjaxResult.success(result); } @@ -68,6 +74,7 @@ public class CcdiProjectSpecialCheckController extends BaseController { @Operation(summary = "查询采购拓展列表") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getExtendedPurchaseList(@Validated CcdiProjectExtendedPurchaseQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); CcdiProjectExtendedPurchaseListVO result = specialCheckService.getExtendedPurchaseList(queryDTO); return AjaxResult.success(result); } @@ -79,6 +86,7 @@ public class CcdiProjectSpecialCheckController extends BaseController { @Operation(summary = "查询采购拓展详情") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getExtendedPurchaseDetail(@Validated CcdiProjectExtendedPurchaseDetailQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); CcdiProjectExtendedPurchaseDetailVO result = specialCheckService.getExtendedPurchaseDetail(queryDTO); return AjaxResult.success(result); } @@ -90,6 +98,7 @@ public class CcdiProjectSpecialCheckController extends BaseController { @Operation(summary = "查询招聘拓展列表") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getExtendedRecruitmentList(@Validated CcdiProjectExtendedRecruitmentQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); CcdiProjectExtendedRecruitmentListVO result = specialCheckService.getExtendedRecruitmentList(queryDTO); return AjaxResult.success(result); } @@ -101,6 +110,7 @@ public class CcdiProjectSpecialCheckController extends BaseController { @Operation(summary = "查询招聘拓展详情") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getExtendedRecruitmentDetail(@Validated CcdiProjectExtendedRecruitmentDetailQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); CcdiProjectExtendedRecruitmentDetailVO result = specialCheckService.getExtendedRecruitmentDetail(queryDTO); return AjaxResult.success(result); } @@ -112,6 +122,7 @@ public class CcdiProjectSpecialCheckController extends BaseController { @Operation(summary = "查询调动拓展列表") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getExtendedTransferList(@Validated CcdiProjectExtendedTransferQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); CcdiProjectExtendedTransferListVO result = specialCheckService.getExtendedTransferList(queryDTO); return AjaxResult.success(result); } @@ -123,6 +134,7 @@ public class CcdiProjectSpecialCheckController extends BaseController { @Operation(summary = "查询调动拓展详情") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getExtendedTransferDetail(@Validated CcdiProjectExtendedTransferDetailQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); CcdiProjectExtendedTransferDetailVO result = specialCheckService.getExtendedTransferDetail(queryDTO); return AjaxResult.success(result); } diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiRelationGraphController.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiRelationGraphController.java index 64711804..72076c9c 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiRelationGraphController.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiRelationGraphController.java @@ -5,6 +5,7 @@ import com.ruoyi.ccdi.project.domain.dto.CcdiRelationGraphSuspectedEnterpriseQue import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphNodeVO; import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphSuspectedEnterpriseVO; import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphVO; +import com.ruoyi.ccdi.project.service.CcdiProjectAccessService; import com.ruoyi.ccdi.project.service.ICcdiRelationGraphService; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; @@ -29,10 +30,14 @@ public class CcdiRelationGraphController extends BaseController { @Resource private ICcdiRelationGraphService relationGraphService; + @Resource + private CcdiProjectAccessService projectAccessService; + @GetMapping("/search") @Operation(summary = "查询关系图谱主体") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult searchSubjects(CcdiRelationGraphQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); List subjects = relationGraphService.searchSubjects(queryDTO); return AjaxResult.success(subjects); } @@ -41,6 +46,7 @@ public class CcdiRelationGraphController extends BaseController { @Operation(summary = "查询一层关系图谱") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getGraph(CcdiRelationGraphQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); CcdiRelationGraphVO graph = relationGraphService.getRelationGraph(queryDTO); return AjaxResult.success(graph); } @@ -49,6 +55,7 @@ public class CcdiRelationGraphController extends BaseController { @Operation(summary = "查询关系图谱疑似同名企业") @PreAuthorize("@ss.hasPermi('ccdi:project:query')") public AjaxResult getSuspectedEnterprises(CcdiRelationGraphSuspectedEnterpriseQueryDTO queryDTO) { + projectAccessService.assertCanRead(queryDTO.getProjectId()); CcdiRelationGraphSuspectedEnterpriseVO result = relationGraphService.getSuspectedEnterprises(queryDTO); return AjaxResult.success(result); } diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/ProjectAccessScope.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/ProjectAccessScope.java new file mode 100644 index 00000000..8942aacf --- /dev/null +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/ProjectAccessScope.java @@ -0,0 +1,24 @@ +package com.ruoyi.ccdi.project.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * 当前登录用户的项目访问范围。 + */ +@Data +@AllArgsConstructor +public class ProjectAccessScope { + + /** 当前用户名 */ + private String username; + + /** 是否可查看全部项目 */ + private boolean viewAllProjects; + + /** 是否超级管理员 */ + private boolean superAdmin; + + /** 是否项目管理员 */ + private boolean projectManager; +} diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiFundGraphManualEdgeSaveDTO.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiFundGraphManualEdgeSaveDTO.java index 33f79a9d..625910ce 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiFundGraphManualEdgeSaveDTO.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiFundGraphManualEdgeSaveDTO.java @@ -10,6 +10,9 @@ import java.math.BigDecimal; @Data public class CcdiFundGraphManualEdgeSaveDTO { + /** 当前项目ID,仅用于写权限校验,不参与手工资金流归属过滤 */ + private Long projectId; + /** 起点主体object_key;为空时默认使用当前查询中心 */ private String fromObjectKey; diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiRelationGraphSuspectedEnterpriseQueryDTO.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiRelationGraphSuspectedEnterpriseQueryDTO.java index 1fb89b56..e738009d 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiRelationGraphSuspectedEnterpriseQueryDTO.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiRelationGraphSuspectedEnterpriseQueryDTO.java @@ -8,6 +8,9 @@ import lombok.Data; @Data public class CcdiRelationGraphSuspectedEnterpriseQueryDTO { + /** 项目ID */ + private Long projectId; + /** 姓名 */ private String personName; diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectVO.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectVO.java index 424a85e0..5ce11b35 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectVO.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectVO.java @@ -61,4 +61,10 @@ public class CcdiProjectVO { /** 创建者姓名(真实姓名) */ private String createByName; + + /** 是否当前用户创建 */ + private Boolean ownedByCurrentUser; + + /** 当前用户是否可操作 */ + private Boolean canOperate; } diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectMapper.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectMapper.java index 333708ce..0b5e7857 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectMapper.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectMapper.java @@ -3,6 +3,7 @@ package com.ruoyi.ccdi.project.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.ccdi.project.domain.CcdiProject; +import com.ruoyi.ccdi.project.domain.ProjectAccessScope; import com.ruoyi.ccdi.project.domain.dto.CcdiProjectQueryDTO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectHistoryListItemVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectVO; @@ -25,7 +26,9 @@ public interface CcdiProjectMapper extends BaseMapper { * @param queryDTO 查询条件 * @return 分页结果 */ - Page selectProjectPage(Page page, @Param("queryDTO") CcdiProjectQueryDTO queryDTO); + Page selectProjectPage(Page page, + @Param("queryDTO") CcdiProjectQueryDTO queryDTO, + @Param("scope") ProjectAccessScope scope); /** * 查询历史项目列表 @@ -33,7 +36,8 @@ public interface CcdiProjectMapper extends BaseMapper { * @param queryDTO 查询条件 * @return 历史项目列表 */ - List selectHistoryProjects(@Param("queryDTO") CcdiProjectQueryDTO queryDTO); + List selectHistoryProjects(@Param("queryDTO") CcdiProjectQueryDTO queryDTO, + @Param("scope") ProjectAccessScope scope); /** * 更新项目总人数与风险人数 diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/CcdiProjectAccessService.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/CcdiProjectAccessService.java new file mode 100644 index 00000000..88b0ab64 --- /dev/null +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/CcdiProjectAccessService.java @@ -0,0 +1,173 @@ +package com.ruoyi.ccdi.project.service; + +import com.ruoyi.ccdi.project.domain.CcdiProject; +import com.ruoyi.ccdi.project.domain.ProjectAccessScope; +import com.ruoyi.ccdi.project.domain.entity.CcdiBankStatement; +import com.ruoyi.ccdi.project.domain.entity.CcdiEvidence; +import com.ruoyi.ccdi.project.domain.entity.CcdiFileUploadRecord; +import com.ruoyi.ccdi.project.mapper.CcdiBankStatementMapper; +import com.ruoyi.ccdi.project.mapper.CcdiEvidenceMapper; +import com.ruoyi.ccdi.project.mapper.CcdiFileUploadRecordMapper; +import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.List; +import java.util.Objects; + +/** + * 项目访问控制。 + */ +@Service +public class CcdiProjectAccessService { + + private static final String ROLE_ADMIN = "admin"; + + private static final String ROLE_MANAGER = "manager"; + + @Resource + private CcdiProjectMapper projectMapper; + + @Resource + private CcdiBankStatementMapper bankStatementMapper; + + @Resource + private CcdiFileUploadRecordMapper fileUploadRecordMapper; + + @Resource + private CcdiEvidenceMapper evidenceMapper; + + public ProjectAccessScope buildCurrentScope() { + LoginUser loginUser = SecurityUtils.getLoginUser(); + String username = SecurityUtils.getUsername(); + boolean superAdmin = isSuperAdmin(loginUser); + boolean projectManager = hasRole(loginUser, ROLE_MANAGER); + return new ProjectAccessScope(username, superAdmin || projectManager, superAdmin, projectManager); + } + + public boolean canOperate(CcdiProject project) { + if (project == null) { + return false; + } + ProjectAccessScope scope = buildCurrentScope(); + return scope.isSuperAdmin() || Objects.equals(scope.getUsername(), project.getCreateBy()); + } + + public void assertCanRead(Long projectId) { + CcdiProject project = getRequiredProject(projectId); + ProjectAccessScope scope = buildCurrentScope(); + if (scope.isViewAllProjects() || Objects.equals(scope.getUsername(), project.getCreateBy())) { + return; + } + throw new ServiceException("无权查看该项目"); + } + + public void assertCanOperate(Long projectId) { + CcdiProject project = getRequiredProject(projectId); + if (canOperate(project)) { + return; + } + throw new ServiceException("无权操作该项目"); + } + + public void assertCanReadByBankStatementId(Long bankStatementId) { + CcdiBankStatement statement = getRequiredBankStatement(bankStatementId); + assertCanRead(statement.getProjectId()); + } + + public void assertCanReadByFileRecordId(Long fileRecordId) { + CcdiFileUploadRecord record = getRequiredFileRecord(fileRecordId); + assertCanRead(record.getProjectId()); + } + + public void assertCanOperateByFileRecordId(Long fileRecordId) { + CcdiFileUploadRecord record = getRequiredFileRecord(fileRecordId); + assertCanOperate(record.getProjectId()); + } + + public void assertCanReadByEvidenceId(Long evidenceId) { + CcdiEvidence evidence = getRequiredEvidence(evidenceId); + assertCanRead(evidence.getProjectId()); + } + + public void assertSourceProjectsReadable(List sourceProjectIds) { + if (CollectionUtils.isEmpty(sourceProjectIds)) { + return; + } + for (Long sourceProjectId : sourceProjectIds) { + assertCanRead(sourceProjectId); + } + } + + private CcdiProject getRequiredProject(Long projectId) { + if (projectId == null) { + throw new ServiceException("项目ID不能为空"); + } + CcdiProject project = projectMapper.selectById(projectId); + if (project == null) { + throw new ServiceException("项目不存在"); + } + return project; + } + + private CcdiBankStatement getRequiredBankStatement(Long bankStatementId) { + if (bankStatementId == null) { + throw new ServiceException("流水ID不能为空"); + } + CcdiBankStatement statement = bankStatementMapper.selectById(bankStatementId); + if (statement == null) { + throw new ServiceException("流水记录不存在"); + } + return statement; + } + + private CcdiFileUploadRecord getRequiredFileRecord(Long fileRecordId) { + if (fileRecordId == null) { + throw new ServiceException("文件记录ID不能为空"); + } + CcdiFileUploadRecord record = fileUploadRecordMapper.selectById(fileRecordId); + if (record == null) { + throw new ServiceException("文件记录不存在"); + } + return record; + } + + private CcdiEvidence getRequiredEvidence(Long evidenceId) { + if (evidenceId == null) { + throw new ServiceException("证据ID不能为空"); + } + CcdiEvidence evidence = evidenceMapper.selectById(evidenceId); + if (evidence == null) { + throw new ServiceException("证据不存在"); + } + return evidence; + } + + private boolean isSuperAdmin(LoginUser loginUser) { + if (loginUser == null) { + return false; + } + if (SecurityUtils.isAdmin(loginUser.getUserId())) { + return true; + } + return hasRole(loginUser, ROLE_ADMIN); + } + + private boolean hasRole(LoginUser loginUser, String roleKey) { + if (loginUser == null || loginUser.getUser() == null + || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) { + return false; + } + for (SysRole role : loginUser.getUser().getRoles()) { + if (roleKey.equals(role.getRoleKey())) { + return true; + } + } + return false; + } +} diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImpl.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImpl.java index 4c24c505..467fe249 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImpl.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImpl.java @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.ccdi.project.constants.CcdiProjectStatusConstants; import com.ruoyi.ccdi.project.domain.CcdiProject; +import com.ruoyi.ccdi.project.domain.ProjectAccessScope; import com.ruoyi.ccdi.project.domain.dto.CcdiProjectImportHistoryDTO; import com.ruoyi.ccdi.project.domain.dto.CcdiProjectQueryDTO; import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSaveDTO; @@ -14,6 +15,7 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectStatusCountsVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectVO; import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper; import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper; +import com.ruoyi.ccdi.project.service.CcdiProjectAccessService; import com.ruoyi.ccdi.project.service.ICcdiProjectService; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.lsfx.client.LsfxAnalysisClient; @@ -54,6 +56,9 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService { @Resource private ApplicationEventPublisher applicationEventPublisher; + @Resource + private CcdiProjectAccessService projectAccessService; + @Override @Transactional(rollbackFor = Exception.class) public CcdiProjectVO createProject(CcdiProjectSaveDTO dto) { @@ -82,7 +87,7 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService { // 5. 返回VO CcdiProjectVO vo = new CcdiProjectVO(); BeanUtils.copyProperties(project, vo); - fillLatestTagFailure(project, vo); + fillProjectExtraFields(project, vo); return vo; } @@ -96,6 +101,7 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService { if (existingProject == null) { throw new ServiceException("项目不存在"); } + projectAccessService.assertCanOperate(dto.getProjectId()); // 只更新允许修改的字段 existingProject.setProjectName(dto.getProjectName()); @@ -106,39 +112,46 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService { CcdiProjectVO vo = new CcdiProjectVO(); BeanUtils.copyProperties(existingProject, vo); + fillProjectExtraFields(existingProject, vo); return vo; } @Override public boolean deleteProject(Long projectId) { + projectAccessService.assertCanOperate(projectId); return projectMapper.deleteById(projectId) > 0; } @Override public CcdiProjectVO getProjectById(Long projectId) { + projectAccessService.assertCanRead(projectId); CcdiProject project = projectMapper.selectById(projectId); if (project == null) { return null; } CcdiProjectVO vo = new CcdiProjectVO(); BeanUtils.copyProperties(project, vo); - fillLatestTagFailure(project, vo); + fillProjectExtraFields(project, vo); return vo; } @Override public Page selectProjectPage(Page page, CcdiProjectQueryDTO queryDTO) { - return projectMapper.selectProjectPage(page, queryDTO); + ProjectAccessScope scope = projectAccessService.buildCurrentScope(); + Page result = projectMapper.selectProjectPage(page, queryDTO, scope); + fillProjectExtraFields(result.getRecords()); + return result; } @Override public List listHistoryProjects(CcdiProjectQueryDTO queryDTO) { - return projectMapper.selectHistoryProjects(queryDTO); + return projectMapper.selectHistoryProjects(queryDTO, projectAccessService.buildCurrentScope()); } @Override @Transactional(rollbackFor = Exception.class) public CcdiProjectVO importFromHistory(CcdiProjectImportHistoryDTO dto, String operator) { + projectAccessService.assertSourceProjectsReadable(dto.getSourceProjectIds()); CcdiProjectSaveDTO saveDTO = new CcdiProjectSaveDTO(); saveDTO.setProjectName(dto.getProjectName()); saveDTO.setDescription(dto.getDescription()); @@ -158,42 +171,34 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService { @Override public CcdiProjectStatusCountsVO getStatusCounts() { CcdiProjectStatusCountsVO vo = new CcdiProjectStatusCountsVO(); + ProjectAccessScope scope = projectAccessService.buildCurrentScope(); // 统计全部项目 - Long totalCount = projectMapper.selectCount(null); + LambdaQueryWrapper baseWrapper = buildScopeWrapper(scope); + Long totalCount = projectMapper.selectCount(baseWrapper); vo.setAll(totalCount); // 统计进行中项目(状态0) - Long status0Count = projectMapper.selectCount( - new LambdaQueryWrapper() - .eq(CcdiProject::getStatus, CcdiProjectStatusConstants.PROCESSING) - ); + Long status0Count = projectMapper.selectCount(buildScopeWrapper(scope) + .eq(CcdiProject::getStatus, CcdiProjectStatusConstants.PROCESSING)); vo.setStatus0(status0Count); // 统计已完成项目(状态1) - Long status1Count = projectMapper.selectCount( - new LambdaQueryWrapper() - .eq(CcdiProject::getStatus, CcdiProjectStatusConstants.COMPLETED) - ); + Long status1Count = projectMapper.selectCount(buildScopeWrapper(scope) + .eq(CcdiProject::getStatus, CcdiProjectStatusConstants.COMPLETED)); vo.setStatus1(status1Count); // 统计已归档项目(状态2) - Long status2Count = projectMapper.selectCount( - new LambdaQueryWrapper() - .eq(CcdiProject::getStatus, CcdiProjectStatusConstants.ARCHIVED) - ); + Long status2Count = projectMapper.selectCount(buildScopeWrapper(scope) + .eq(CcdiProject::getStatus, CcdiProjectStatusConstants.ARCHIVED)); vo.setStatus2(status2Count); - Long status3Count = projectMapper.selectCount( - new LambdaQueryWrapper() - .eq(CcdiProject::getStatus, CcdiProjectStatusConstants.TAGGING) - ); + Long status3Count = projectMapper.selectCount(buildScopeWrapper(scope) + .eq(CcdiProject::getStatus, CcdiProjectStatusConstants.TAGGING)); vo.setStatus3(status3Count); - Long status4Count = projectMapper.selectCount( - new LambdaQueryWrapper() - .eq(CcdiProject::getStatus, CcdiProjectStatusConstants.TAG_FAILED) - ); + Long status4Count = projectMapper.selectCount(buildScopeWrapper(scope) + .eq(CcdiProject::getStatus, CcdiProjectStatusConstants.TAG_FAILED)); vo.setStatus4(status4Count); return vo; @@ -202,6 +207,7 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService { @Override public void archiveProject(Long projectId, String operator) { CcdiProject project = getRequiredProject(projectId); + projectAccessService.assertCanOperate(projectId); if (CcdiProjectStatusConstants.ARCHIVED.equals(project.getStatus())) { throw new ServiceException("项目已归档,无需重复操作"); } @@ -293,6 +299,38 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService { vo.setLatestTagTaskEndTime(latestFailedTask.getEndTime()); } + private LambdaQueryWrapper buildScopeWrapper(ProjectAccessScope scope) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + if (scope != null && !scope.isViewAllProjects()) { + wrapper.eq(CcdiProject::getCreateBy, scope.getUsername()); + } + return wrapper; + } + + private void fillProjectExtraFields(List records) { + if (records == null || records.isEmpty()) { + return; + } + ProjectAccessScope scope = projectAccessService.buildCurrentScope(); + for (CcdiProjectVO vo : records) { + fillProjectAccessFields(vo, scope); + } + } + + private void fillProjectExtraFields(CcdiProject project, CcdiProjectVO vo) { + fillLatestTagFailure(project, vo); + fillProjectAccessFields(vo, projectAccessService.buildCurrentScope()); + } + + private void fillProjectAccessFields(CcdiProjectVO vo, ProjectAccessScope scope) { + if (vo == null || scope == null) { + return; + } + boolean ownedByCurrentUser = Objects.equals(scope.getUsername(), vo.getCreateBy()); + vo.setOwnedByCurrentUser(ownedByCurrentUser); + vo.setCanOperate(scope.isSuperAdmin() || ownedByCurrentUser); + } + private String resolveOperator(String operator) { return StringUtils.hasText(operator) ? operator : "system"; } diff --git a/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectMapper.xml b/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectMapper.xml index d76061d2..be79b538 100644 --- a/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectMapper.xml +++ b/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectMapper.xml @@ -40,6 +40,9 @@ FROM ccdi_project p LEFT JOIN sys_user u ON p.create_by = u.user_name AND u.del_flag = '0' + + AND p.create_by = #{scope.username} + AND p.project_name LIKE CONCAT('%', #{queryDTO.projectName}, '%') @@ -61,6 +64,9 @@ FROM ccdi_project p p.status in ('1', '2') + + AND p.create_by = #{scope.username} + AND p.project_name LIKE CONCAT('%', #{queryDTO.projectName}, '%') diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImplTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImplTest.java index f5db9a21..b98a7243 100644 --- a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImplTest.java +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImplTest.java @@ -14,6 +14,7 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectStatusCountsVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectVO; import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper; import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper; +import com.ruoyi.ccdi.project.service.CcdiProjectAccessService; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.lsfx.client.LsfxAnalysisClient; import com.ruoyi.lsfx.domain.response.GetTokenResponse; @@ -52,6 +53,9 @@ class CcdiProjectServiceImplTest { @Mock private CcdiProjectMapper projectMapper; + @Mock + private CcdiProjectAccessService projectAccessService; + @Mock private CcdiBankTagTaskMapper bankTagTaskMapper; @@ -204,13 +208,13 @@ class CcdiProjectServiceImplTest { archived.setProjectId(2L); archived.setStatus("2"); - when(projectMapper.selectHistoryProjects(queryDTO)).thenReturn(List.of(completed, archived)); + when(projectMapper.selectHistoryProjects(any(), any())).thenReturn(List.of(completed, archived)); List result = service.listHistoryProjects(queryDTO); assertEquals(2, result.size()); assertEquals(List.of(completed, archived), result); - verify(projectMapper).selectHistoryProjects(queryDTO); + verify(projectMapper).selectHistoryProjects(any(), any()); } @Test diff --git a/docs/plans/backend/2026-07-01-project-manager-role-access-backend-implementation.md b/docs/plans/backend/2026-07-01-project-manager-role-access-backend-implementation.md new file mode 100644 index 00000000..7db6e961 --- /dev/null +++ b/docs/plans/backend/2026-07-01-project-manager-role-access-backend-implementation.md @@ -0,0 +1,19 @@ +# manager 项目权限后端实施计划 + +## 目标 + +新增 `manager` 角色并建立统一项目访问控制:`admin` 保持最高权限,`manager` 可查看全部项目但只能操作本人创建的项目,普通角色只能查看和操作本人创建的项目。 + +## 实施范围 + +1. 新增 `ProjectAccessScope` 和 `CcdiProjectAccessService`,统一提供项目读权限、操作权限、流水/文件/证据等资源反查项目后的权限校验。 +2. 项目列表、历史项目列表、状态统计统一走后端 scope:`admin/manager` 查询全部,普通角色限定 `create_by = 当前用户名`。 +3. `CcdiProjectVO` 返回 `ownedByCurrentUser`、`canOperate`,供前端展示只读态。 +4. 项目详情、结果总览、流水明细、专项排查、关系图谱、资金图谱、证据、上传记录、模型参数等接口在服务调用前执行项目读/写权限校验。 +5. 项目更新、删除、归档、重新分析、上传、拉取本行信息、文件删除、参数保存、证据保存、手工资金流保存均执行 `assertCanOperate`。 +6. 资金图谱手工资金流保持现有全局资金图逻辑,不新增 `project_id`,不按项目过滤手工边;保存时仅用当前项目 ID 做操作权限校验。 +7. 新增幂等 SQL `sql/migration/2026-07-01-add-manager-role-project-scope.sql`,按 `role_key='manager'` 新增/更新角色并授予项目菜单权限,不自动绑定用户。 + +## 验证 + +执行 `mvn -pl ccdi-project -am compile -DskipTests`,重点验证权限服务、Controller 注入、Mapper 参数和 MyBatis XML 条件编译通过。 diff --git a/docs/plans/frontend/2026-07-01-project-manager-role-access-frontend-implementation.md b/docs/plans/frontend/2026-07-01-project-manager-role-access-frontend-implementation.md new file mode 100644 index 00000000..b31f1e00 --- /dev/null +++ b/docs/plans/frontend/2026-07-01-project-manager-role-access-frontend-implementation.md @@ -0,0 +1,18 @@ +# manager 项目权限前端实施计划 + +## 目标 + +前端根据后端返回的 `canOperate` 呈现项目只读体验:他人项目可查看结果和明细,不展示或禁用上传、参数配置、重新分析、归档、证据确认、手工资金流保存等写入入口。 + +## 实施范围 + +1. 项目列表使用 `canOperate` 控制按钮:他人项目仅显示查看入口,重新分析和归档仅在 `canOperate=true` 且状态允许时展示。 +2. 他人项目从列表进入详情时默认打开 `overview`;详情页路由指向上传或参数配置时自动切换到结果总览。 +3. 详情页将 `canOperate` 传给上传、参数配置、结果总览、证据确认、项目分析图谱等子组件。 +4. 上传数据、拉取本行信息、征信导入、文件删除、参数保存、证据确认、手工资金流新增保存均在前端方法入口做只读拦截。 +5. 关系图谱、资金图谱、资金边流水明细请求携带当前 `projectId` 作为后端读权限校验上下文;资金图谱手工资金流数据仍保持全局图谱逻辑,不按项目过滤。 +6. 新建项目、历史导入和快捷创建入口继续按 `ccdi:project:add` 菜单权限展示。 + +## 验证 + +执行 `source ~/.nvm/nvm.sh && cd ruoyi-ui && nvm use && npm run build:prod`,并通过真实浏览器检查列表按钮、详情只读页签、上传/参数/证据/图谱写入入口和查询接口行为。 diff --git a/docs/reports/implementation/2026-07-01-project-manager-role-access.md b/docs/reports/implementation/2026-07-01-project-manager-role-access.md new file mode 100644 index 00000000..2ea0f081 --- /dev/null +++ b/docs/reports/implementation/2026-07-01-project-manager-role-access.md @@ -0,0 +1,22 @@ +# manager 项目权限实施记录 + +## 修改内容 + +- 新增后端项目访问控制服务,统一判断 `admin / manager / 创建者` 的项目读权限,以及 `admin / 创建者` 的项目操作权限。 +- 项目列表、历史项目、状态统计按当前用户角色自动限制范围,并在项目 VO 中返回 `ownedByCurrentUser` 和 `canOperate`。 +- 项目列表、项目详情、流水明细、上传记录、证据读取等基础读接口不再依赖 `ccdi:project:list/query` 菜单权限,避免普通角色登录后被菜单权限提前拦截。 +- 为项目详情、结果总览、流水明细、专项排查、关系图谱、资金图谱、上传、参数、证据、重新分析等接口补充项目级读写权限校验。 +- 新增 `manager` 角色幂等 SQL,按 `role_key='manager'` 维护角色并授予项目相关菜单权限,不绑定具体用户。 +- 前端项目列表、详情页、上传、参数、证据、项目分析图谱按 `canOperate` 展示只读态,并在写入方法入口拦截。 + +## 影响范围 + +- `manager` 可查看所有项目,操作他人项目会被后端拒绝;操作本人项目仍受菜单权限控制。 +- 普通角色可进入项目列表并查看本人创建的项目,直接传入他人项目或资源 ID 会被拒绝;写操作仍受菜单权限和项目归属共同约束。 +- 资金图谱手工资金流保持全局资金图逻辑,不新增项目归属字段,也不按项目过滤手工边;保存入口只使用当前项目 ID 做操作权限校验。 + +## 验证记录 + +- 后端计划执行:`mvn -pl ccdi-project -am compile -DskipTests`。 +- 前端计划执行:`source ~/.nvm/nvm.sh && cd ruoyi-ui && nvm use && npm run build:prod`。 +- 浏览器计划验证:admin、manager、普通角色的列表范围、详情只读态、按钮显隐和接口拒绝行为。 diff --git a/ruoyi-ui/src/views/ccdiProject/components/ProjectTable.vue b/ruoyi-ui/src/views/ccdiProject/components/ProjectTable.vue index d6558043..cf33e7bf 100644 --- a/ruoyi-ui/src/views/ccdiProject/components/ProjectTable.vue +++ b/ruoyi-ui/src/views/ccdiProject/components/ProjectTable.vue @@ -101,10 +101,10 @@ v-if="['0', '3', '4'].includes(scope.row.status)" size="mini" type="text" - icon="el-icon-right" + :icon="canOperate(scope.row) ? 'el-icon-right' : 'el-icon-view'" @click="handleEnter(scope.row)" > - 进入项目 + {{ canOperate(scope.row) ? "进入项目" : "查看项目" }}