feat(ai-report): 新增委托单位管理和报告审批功能
- 新增委托单位管理模块,支持增删改查、导入导出功能 - 实现委托单位 Excel 模板下载和数据导入功能 - 添加报告审批抽象处理器,支持审批流程日志记录 - 统一文件存储路径配置,将各模块独立路径改为统一根目录 - 新增 PQDIF 文件名存储字段,完善文件管理功能 - 修复代码中的乱码问题和错误提示信息 - 优化系统常量定义和字典配置管理
This commit is contained in:
56
ai-report/report-approval/pom.xml
Normal file
56
ai-report/report-approval/pom.xml
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.njcn.gather</groupId>
|
||||
<artifactId>ai-report</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>report-approval</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.njcn.gather</groupId>
|
||||
<artifactId>report-model</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.njcn.gather</groupId>
|
||||
<artifactId>test-report</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>njcn-common</artifactId>
|
||||
<version>0.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>mybatis-plus</artifactId>
|
||||
<version>0.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>spingboot2.3.12</artifactId>
|
||||
<version>2.3.12</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.njcn.gather.aireport.reportapproval.component;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.njcn.gather.aireport.reportapproval.mapper.ReportApprovalMapper;
|
||||
import com.njcn.gather.aireport.reportmodel.pojo.vo.ReportModelUserNameVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ReportApprovalUserNameResolver {
|
||||
|
||||
private final ReportApprovalMapper reportApprovalMapper;
|
||||
|
||||
public Map<String, String> resolveUserNames(Collection<String> userIds) {
|
||||
if (userIds == null || userIds.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Set<String> validIds = userIds.stream()
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
if (validIds.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
List<ReportModelUserNameVO> users = reportApprovalMapper.selectUserNamesByIds(validIds);
|
||||
if (users == null || users.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return users.stream()
|
||||
.filter(user -> user != null && StrUtil.isNotBlank(user.getId()) && StrUtil.isNotBlank(user.getName()))
|
||||
.collect(Collectors.toMap(ReportModelUserNameVO::getId, ReportModelUserNameVO::getName, (first, second) -> first));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.njcn.gather.aireport.reportapproval.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.OperateType;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.param.ReportApprovalParam;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.vo.ReportApprovalLogVO;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.vo.ReportApprovalPendingVO;
|
||||
import com.njcn.gather.aireport.reportapproval.service.ReportApprovalService;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import com.njcn.web.utils.HttpResultUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Api(tags = "报告审批管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/report-approval")
|
||||
@RequiredArgsConstructor
|
||||
public class ReportApprovalController extends BaseController {
|
||||
|
||||
private final ReportApprovalService reportApprovalService;
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("查询待审报告分页")
|
||||
@GetMapping("/pending-page")
|
||||
public HttpResult<Page<ReportApprovalPendingVO>> pendingPage(@ModelAttribute ReportApprovalParam.PendingQueryParam param) {
|
||||
String methodDescribe = getMethodDescribe("pendingPage");
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS,
|
||||
reportApprovalService.pendingPage(param), methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@ApiOperation("审批通过")
|
||||
@PostMapping("/pass")
|
||||
public HttpResult<Boolean> pass(@RequestBody @Validated ReportApprovalParam.AuditParam param) {
|
||||
String methodDescribe = getMethodDescribe("pass");
|
||||
boolean result = reportApprovalService.pass(param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(result ? CommonResponseEnum.SUCCESS : CommonResponseEnum.FAIL,
|
||||
result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@ApiOperation("审批退回")
|
||||
@PostMapping("/reject")
|
||||
public HttpResult<Boolean> reject(@RequestBody @Validated ReportApprovalParam.AuditParam param) {
|
||||
String methodDescribe = getMethodDescribe("reject");
|
||||
boolean result = reportApprovalService.reject(param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(result ? CommonResponseEnum.SUCCESS : CommonResponseEnum.FAIL,
|
||||
result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("查询审批日志分页")
|
||||
@GetMapping("/log-page")
|
||||
public HttpResult<Page<ReportApprovalLogVO>> logPage(@ModelAttribute ReportApprovalParam.LogQueryParam param) {
|
||||
String methodDescribe = getMethodDescribe("logPage");
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS,
|
||||
reportApprovalService.logPage(param), methodDescribe);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.njcn.gather.aireport.reportapproval.handler;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.aireport.reportapproval.component.ReportApprovalUserNameResolver;
|
||||
import com.njcn.gather.aireport.reportapproval.mapper.ReportApprovalMapper;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.constant.ReportApprovalConst;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.param.ReportApprovalParam;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.po.ReportApprovalLogPO;
|
||||
import com.njcn.web.utils.RequestUtil;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class AbstractReportApprovalHandler implements ReportApprovalHandler {
|
||||
|
||||
private final ReportApprovalMapper reportApprovalMapper;
|
||||
private final ReportApprovalUserNameResolver reportApprovalUserNameResolver;
|
||||
|
||||
protected AbstractReportApprovalHandler(ReportApprovalMapper reportApprovalMapper,
|
||||
ReportApprovalUserNameResolver reportApprovalUserNameResolver) {
|
||||
this.reportApprovalMapper = reportApprovalMapper;
|
||||
this.reportApprovalUserNameResolver = reportApprovalUserNameResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean audit(ReportApprovalParam.AuditParam param, boolean passed) {
|
||||
if (param == null || StrUtil.isBlank(param.getReportId())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "审批参数不能为空");
|
||||
}
|
||||
String reason = trimToNull(param.getApprovalReason());
|
||||
if (reason == null) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "审批原因不能为空");
|
||||
}
|
||||
return doAudit(param.getReportId(), reason, passed);
|
||||
}
|
||||
|
||||
protected abstract boolean doAudit(String reportId, String reason, boolean passed);
|
||||
|
||||
protected void saveLog(String reportId, String reportName, String beforeState, String afterState,
|
||||
String reason, String approverId, LocalDateTime approvalTime) {
|
||||
ReportApprovalLogPO log = new ReportApprovalLogPO();
|
||||
log.setId(UUID.randomUUID().toString());
|
||||
log.setReportType(getReportType());
|
||||
log.setReportId(reportId);
|
||||
log.setReportName(reportName);
|
||||
log.setApprovalResult(afterState);
|
||||
log.setApprovalReason(reason);
|
||||
log.setBeforeState(beforeState);
|
||||
log.setAfterState(afterState);
|
||||
log.setApproverId(approverId);
|
||||
log.setApproverName(resolveUserName(approverId));
|
||||
log.setApprovalTime(approvalTime);
|
||||
log.setStatus(ReportApprovalConst.STATUS_NORMAL);
|
||||
log.setCreateBy(approverId);
|
||||
log.setCreateTime(approvalTime);
|
||||
log.setUpdateBy(approverId);
|
||||
log.setUpdateTime(approvalTime);
|
||||
reportApprovalMapper.insert(log);
|
||||
}
|
||||
|
||||
protected String resolveCurrentUserId() {
|
||||
String userId = RequestUtil.getUserId();
|
||||
return StrUtil.isBlank(userId) ? null : userId;
|
||||
}
|
||||
|
||||
private String resolveUserName(String userId) {
|
||||
if (StrUtil.isBlank(userId)) {
|
||||
return null;
|
||||
}
|
||||
Map<String, String> userNameMap = reportApprovalUserNameResolver.resolveUserNames(Arrays.asList(userId));
|
||||
return userNameMap.containsKey(userId) ? userNameMap.get(userId) : userId;
|
||||
}
|
||||
|
||||
private String trimToNull(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
String text = value.trim();
|
||||
return text.isEmpty() ? null : text;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.njcn.gather.aireport.reportapproval.handler;
|
||||
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.param.ReportApprovalParam;
|
||||
|
||||
public interface ReportApprovalHandler {
|
||||
|
||||
String getReportType();
|
||||
|
||||
boolean audit(ReportApprovalParam.AuditParam param, boolean passed);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.njcn.gather.aireport.reportapproval.handler;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class ReportApprovalHandlerRegistry {
|
||||
|
||||
private final Map<String, ReportApprovalHandler> handlerMap = new LinkedHashMap<String, ReportApprovalHandler>();
|
||||
|
||||
public ReportApprovalHandlerRegistry(List<ReportApprovalHandler> handlers) {
|
||||
if (handlers == null || handlers.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (ReportApprovalHandler handler : handlers) {
|
||||
if (handler != null && StrUtil.isNotBlank(handler.getReportType())) {
|
||||
handlerMap.put(handler.getReportType(), handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ReportApprovalHandler requireHandler(String reportType) {
|
||||
ReportApprovalHandler handler = handlerMap.get(reportType);
|
||||
if (handler == null) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "不支持的报告类型");
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.njcn.gather.aireport.reportapproval.handler;
|
||||
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.aireport.reportapproval.component.ReportApprovalUserNameResolver;
|
||||
import com.njcn.gather.aireport.reportapproval.mapper.ReportApprovalMapper;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.constant.ReportApprovalConst;
|
||||
import com.njcn.gather.aireport.reportmodel.pojo.constant.ReportModelConst;
|
||||
import com.njcn.gather.aireport.reportmodel.pojo.po.ReportModelPO;
|
||||
import com.njcn.gather.aireport.reportmodel.service.ReportModelService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Component
|
||||
public class ReportModelApprovalHandler extends AbstractReportApprovalHandler {
|
||||
|
||||
private final ReportModelService reportModelService;
|
||||
|
||||
public ReportModelApprovalHandler(ReportApprovalMapper reportApprovalMapper,
|
||||
ReportApprovalUserNameResolver reportApprovalUserNameResolver,
|
||||
ReportModelService reportModelService) {
|
||||
super(reportApprovalMapper, reportApprovalUserNameResolver);
|
||||
this.reportModelService = reportModelService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReportType() {
|
||||
return ReportApprovalConst.REPORT_TYPE_REPORT_MODEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doAudit(String reportId, String reason, boolean passed) {
|
||||
ReportModelPO reportModel = reportModelService.requireNormal(reportId);
|
||||
if (!ReportModelConst.STATE_AUDITING.equals(reportModel.getState())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "仅待审状态的报告模板允许审批");
|
||||
}
|
||||
String userId = resolveCurrentUserId();
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
String targetState = passed ? ReportModelConst.STATE_APPROVED : ReportModelConst.STATE_REJECTED;
|
||||
boolean updated = reportModelService.lambdaUpdate()
|
||||
.set(ReportModelPO::getState, targetState)
|
||||
.set(ReportModelPO::getCheckId, userId)
|
||||
.set(ReportModelPO::getCheckTime, now)
|
||||
.set(ReportModelPO::getCheckResult, reason)
|
||||
.set(ReportModelPO::getUpdateBy, userId)
|
||||
.set(ReportModelPO::getUpdateTime, now)
|
||||
.eq(ReportModelPO::getId, reportId)
|
||||
.eq(ReportModelPO::getStatus, ReportModelConst.STATUS_NORMAL)
|
||||
.eq(ReportModelPO::getState, ReportModelConst.STATE_AUDITING)
|
||||
.update();
|
||||
if (!updated) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "报告模板状态已变更,请刷新后重试");
|
||||
}
|
||||
saveLog(reportModel.getId(), reportModel.getName(), ReportModelConst.STATE_AUDITING, targetState, reason, userId, now);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.njcn.gather.aireport.reportapproval.handler;
|
||||
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.aireport.reportapproval.component.ReportApprovalUserNameResolver;
|
||||
import com.njcn.gather.aireport.reportapproval.mapper.ReportApprovalMapper;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.constant.ReportApprovalConst;
|
||||
import com.njcn.gather.aireport.testreport.pojo.constant.TestReportConst;
|
||||
import com.njcn.gather.aireport.testreport.pojo.po.TestReportPO;
|
||||
import com.njcn.gather.aireport.testreport.service.TestReportService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Component
|
||||
public class TestReportApprovalHandler extends AbstractReportApprovalHandler {
|
||||
|
||||
private final TestReportService testReportService;
|
||||
|
||||
public TestReportApprovalHandler(ReportApprovalMapper reportApprovalMapper,
|
||||
ReportApprovalUserNameResolver reportApprovalUserNameResolver,
|
||||
TestReportService testReportService) {
|
||||
super(reportApprovalMapper, reportApprovalUserNameResolver);
|
||||
this.testReportService = testReportService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReportType() {
|
||||
return ReportApprovalConst.REPORT_TYPE_TEST_REPORT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doAudit(String reportId, String reason, boolean passed) {
|
||||
TestReportPO testReport = testReportService.requireNormal(reportId);
|
||||
if (!TestReportConst.STATE_AUDITING.equals(testReport.getState())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "仅待审状态的普测报告允许审批");
|
||||
}
|
||||
String userId = resolveCurrentUserId();
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
String targetState = passed ? TestReportConst.STATE_APPROVED : TestReportConst.STATE_REJECTED;
|
||||
boolean updated = testReportService.lambdaUpdate()
|
||||
.set(TestReportPO::getState, targetState)
|
||||
.set(TestReportPO::getCheckId, userId)
|
||||
.set(TestReportPO::getCheckTime, LocalDate.now())
|
||||
.set(TestReportPO::getCheckResult, reason)
|
||||
.set(TestReportPO::getUpdateBy, userId)
|
||||
.set(TestReportPO::getUpdateTime, now)
|
||||
.eq(TestReportPO::getId, reportId)
|
||||
.eq(TestReportPO::getStatus, TestReportConst.STATUS_NORMAL)
|
||||
.eq(TestReportPO::getState, TestReportConst.STATE_AUDITING)
|
||||
.update();
|
||||
if (!updated) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "普测报告状态已变更,请刷新后重试");
|
||||
}
|
||||
saveLog(testReport.getId(), testReport.getNo(), TestReportConst.STATE_AUDITING, targetState, reason, userId, now);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.njcn.gather.aireport.reportapproval.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.param.ReportApprovalParam;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.po.ReportApprovalLogPO;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.vo.ReportApprovalLogVO;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.vo.ReportApprovalPendingVO;
|
||||
import com.njcn.gather.aireport.reportmodel.pojo.vo.ReportModelUserNameVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ReportApprovalMapper extends BaseMapper<ReportApprovalLogPO> {
|
||||
|
||||
@Select({
|
||||
"<script>",
|
||||
"select id, name from sys_user where id in",
|
||||
"<foreach collection='ids' item='id' open='(' separator=',' close=')'>",
|
||||
"#{id}",
|
||||
"</foreach>",
|
||||
"</script>"
|
||||
})
|
||||
List<ReportModelUserNameVO> selectUserNamesByIds(@Param("ids") Collection<String> ids);
|
||||
|
||||
@Select({
|
||||
"<script>",
|
||||
"select count(1) from (",
|
||||
"<if test='param.reportType == null or param.reportType == \"\" or param.reportType == \"REPORT_MODEL\"'>",
|
||||
"select model.id",
|
||||
"from ai_reportmodel model",
|
||||
"where model.status = 1 and model.state = '02'",
|
||||
"</if>",
|
||||
"<if test='param.reportType == null or param.reportType == \"\"'>",
|
||||
"union all",
|
||||
"</if>",
|
||||
"<if test='param.reportType == null or param.reportType == \"\" or param.reportType == \"TEST_REPORT\"'>",
|
||||
"select report.id",
|
||||
"from ai_testreport report",
|
||||
"where report.status = 1 and report.state = '02'",
|
||||
"</if>",
|
||||
") pending",
|
||||
"</script>"
|
||||
})
|
||||
long countPendingReports(@Param("param") ReportApprovalParam.PendingQueryParam param);
|
||||
|
||||
@Select({
|
||||
"<script>",
|
||||
"select * from (",
|
||||
"<if test='param.reportType == null or param.reportType == \"\" or param.reportType == \"REPORT_MODEL\"'>",
|
||||
"select 'REPORT_MODEL' as reportType, model.id as reportId, model.name as reportName, model.state,",
|
||||
"model.update_by as submitterId, user.name as submitterName, model.update_time as submitTime",
|
||||
"from ai_reportmodel model",
|
||||
"left join sys_user user on user.id = model.update_by",
|
||||
"where model.status = 1 and model.state = '02'",
|
||||
"</if>",
|
||||
"<if test='param.reportType == null or param.reportType == \"\"'>",
|
||||
"union all",
|
||||
"</if>",
|
||||
"<if test='param.reportType == null or param.reportType == \"\" or param.reportType == \"TEST_REPORT\"'>",
|
||||
"select 'TEST_REPORT' as reportType, report.id as reportId, report.no as reportName, report.state,",
|
||||
"report.update_by as submitterId, user.name as submitterName, report.update_time as submitTime",
|
||||
"from ai_testreport report",
|
||||
"left join sys_user user on user.id = report.update_by",
|
||||
"where report.status = 1 and report.state = '02'",
|
||||
"</if>",
|
||||
") pending",
|
||||
"order by pending.submitTime desc",
|
||||
"limit #{offset}, #{size}",
|
||||
"</script>"
|
||||
})
|
||||
List<ReportApprovalPendingVO> selectPendingPage(@Param("offset") long offset,
|
||||
@Param("size") long size,
|
||||
@Param("param") ReportApprovalParam.PendingQueryParam param);
|
||||
|
||||
@Select({
|
||||
"<script>",
|
||||
"select id, report_type as reportType, report_id as reportId, report_name as reportName,",
|
||||
"approval_result as approvalResult, approval_reason as approvalReason, before_state as beforeState,",
|
||||
"after_state as afterState, approver_name as approverName, approval_time as approvalTime",
|
||||
"from ai_report_approval_log",
|
||||
"where status = 1",
|
||||
"<if test='param.reportType != null and param.reportType != \"\"'>",
|
||||
"and report_type = #{param.reportType}",
|
||||
"</if>",
|
||||
"order by approval_time desc, create_time desc",
|
||||
"</script>"
|
||||
})
|
||||
Page<ReportApprovalLogVO> selectLogPage(Page<ReportApprovalLogVO> page,
|
||||
@Param("param") ReportApprovalParam.LogQueryParam param);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.njcn.gather.aireport.reportapproval.pojo.constant;
|
||||
|
||||
public final class ReportApprovalConst {
|
||||
|
||||
public static final String REPORT_TYPE_REPORT_MODEL = "REPORT_MODEL";
|
||||
public static final String REPORT_TYPE_TEST_REPORT = "TEST_REPORT";
|
||||
|
||||
public static final Integer STATUS_NORMAL = 1;
|
||||
|
||||
private ReportApprovalConst() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.njcn.gather.aireport.reportapproval.pojo.param;
|
||||
|
||||
import com.njcn.web.pojo.param.BaseParam;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
public class ReportApprovalParam {
|
||||
|
||||
@Data
|
||||
public static class AuditParam {
|
||||
@ApiModelProperty("报告类型")
|
||||
@NotBlank(message = "报告类型不能为空")
|
||||
private String reportType;
|
||||
|
||||
@ApiModelProperty("报告ID")
|
||||
@NotBlank(message = "报告ID不能为空")
|
||||
private String reportId;
|
||||
|
||||
@ApiModelProperty("审批原因")
|
||||
@NotBlank(message = "审批原因不能为空")
|
||||
@Size(max = 128, message = "审批原因不能超过128个字符")
|
||||
private String approvalReason;
|
||||
}
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class PendingQueryParam extends BaseParam {
|
||||
@ApiModelProperty("报告类型")
|
||||
private String reportType;
|
||||
}
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class LogQueryParam extends BaseParam {
|
||||
@ApiModelProperty("报告类型")
|
||||
private String reportType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.njcn.gather.aireport.reportapproval.pojo.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("ai_report_approval_log")
|
||||
public class ReportApprovalLogPO implements Serializable {
|
||||
private static final long serialVersionUID = 1900859716151337132L;
|
||||
|
||||
@TableId("id")
|
||||
private String id;
|
||||
@TableField("report_type")
|
||||
private String reportType;
|
||||
@TableField("report_id")
|
||||
private String reportId;
|
||||
@TableField("report_name")
|
||||
private String reportName;
|
||||
@TableField("approval_result")
|
||||
private String approvalResult;
|
||||
@TableField("approval_reason")
|
||||
private String approvalReason;
|
||||
@TableField("before_state")
|
||||
private String beforeState;
|
||||
@TableField("after_state")
|
||||
private String afterState;
|
||||
@TableField("approver_id")
|
||||
private String approverId;
|
||||
@TableField("approver_name")
|
||||
private String approverName;
|
||||
@TableField("approval_time")
|
||||
private LocalDateTime approvalTime;
|
||||
@TableField("status")
|
||||
private Integer status;
|
||||
@TableField("create_by")
|
||||
private String createBy;
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
@TableField("update_by")
|
||||
private String updateBy;
|
||||
@TableField("update_time")
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.njcn.gather.aireport.reportapproval.pojo.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class ReportApprovalLogVO {
|
||||
@ApiModelProperty("日志ID")
|
||||
private String id;
|
||||
@ApiModelProperty("报告类型")
|
||||
private String reportType;
|
||||
@ApiModelProperty("报告ID")
|
||||
private String reportId;
|
||||
@ApiModelProperty("报告名称")
|
||||
private String reportName;
|
||||
@ApiModelProperty("审批结果")
|
||||
private String approvalResult;
|
||||
@ApiModelProperty("审批原因")
|
||||
private String approvalReason;
|
||||
@ApiModelProperty("审批前状态")
|
||||
private String beforeState;
|
||||
@ApiModelProperty("审批后状态")
|
||||
private String afterState;
|
||||
@ApiModelProperty("审批人")
|
||||
private String approverName;
|
||||
@ApiModelProperty("审批时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
|
||||
@JsonSerialize(using = LocalDateTimeSerializer.class)
|
||||
private LocalDateTime approvalTime;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.njcn.gather.aireport.reportapproval.pojo.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class ReportApprovalPendingVO {
|
||||
@ApiModelProperty("报告类型")
|
||||
private String reportType;
|
||||
@ApiModelProperty("报告ID")
|
||||
private String reportId;
|
||||
@ApiModelProperty("报告名称")
|
||||
private String reportName;
|
||||
@ApiModelProperty("报告状态")
|
||||
private String state;
|
||||
@ApiModelProperty("提交人ID")
|
||||
private String submitterId;
|
||||
@ApiModelProperty("提交人")
|
||||
private String submitterName;
|
||||
@ApiModelProperty("提交时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
|
||||
@JsonSerialize(using = LocalDateTimeSerializer.class)
|
||||
private LocalDateTime submitTime;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.njcn.gather.aireport.reportapproval.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.param.ReportApprovalParam;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.vo.ReportApprovalLogVO;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.vo.ReportApprovalPendingVO;
|
||||
|
||||
public interface ReportApprovalService {
|
||||
|
||||
boolean pass(ReportApprovalParam.AuditParam param);
|
||||
|
||||
boolean reject(ReportApprovalParam.AuditParam param);
|
||||
|
||||
Page<ReportApprovalPendingVO> pendingPage(ReportApprovalParam.PendingQueryParam param);
|
||||
|
||||
Page<ReportApprovalLogVO> logPage(ReportApprovalParam.LogQueryParam param);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.njcn.gather.aireport.reportapproval.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.aireport.reportapproval.handler.ReportApprovalHandler;
|
||||
import com.njcn.gather.aireport.reportapproval.handler.ReportApprovalHandlerRegistry;
|
||||
import com.njcn.gather.aireport.reportapproval.mapper.ReportApprovalMapper;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.param.ReportApprovalParam;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.vo.ReportApprovalLogVO;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.vo.ReportApprovalPendingVO;
|
||||
import com.njcn.gather.aireport.reportapproval.service.ReportApprovalService;
|
||||
import com.njcn.web.factory.PageFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class ReportApprovalServiceImpl implements ReportApprovalService {
|
||||
|
||||
private final ReportApprovalMapper reportApprovalMapper;
|
||||
private final ReportApprovalHandlerRegistry reportApprovalHandlerRegistry;
|
||||
|
||||
public ReportApprovalServiceImpl(ReportApprovalMapper reportApprovalMapper,
|
||||
ReportApprovalHandlerRegistry reportApprovalHandlerRegistry) {
|
||||
this.reportApprovalMapper = reportApprovalMapper;
|
||||
this.reportApprovalHandlerRegistry = reportApprovalHandlerRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean pass(ReportApprovalParam.AuditParam param) {
|
||||
return audit(param, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean reject(ReportApprovalParam.AuditParam param) {
|
||||
return audit(param, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<ReportApprovalPendingVO> pendingPage(ReportApprovalParam.PendingQueryParam param) {
|
||||
ReportApprovalParam.PendingQueryParam query = param == null ? new ReportApprovalParam.PendingQueryParam() : param;
|
||||
if (StrUtil.isNotBlank(query.getReportType())) {
|
||||
reportApprovalHandlerRegistry.requireHandler(query.getReportType());
|
||||
}
|
||||
long current = PageFactory.getPageNum(query);
|
||||
long size = PageFactory.getPageSize(query);
|
||||
long offset = Math.max(0L, (current - 1L) * size);
|
||||
long total = reportApprovalMapper.countPendingReports(query);
|
||||
List<ReportApprovalPendingVO> records = total <= 0
|
||||
? Collections.<ReportApprovalPendingVO>emptyList()
|
||||
: reportApprovalMapper.selectPendingPage(offset, size, query);
|
||||
Page<ReportApprovalPendingVO> page = new Page<ReportApprovalPendingVO>(current, size, total);
|
||||
page.setRecords(records);
|
||||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<ReportApprovalLogVO> logPage(ReportApprovalParam.LogQueryParam param) {
|
||||
ReportApprovalParam.LogQueryParam query = param == null ? new ReportApprovalParam.LogQueryParam() : param;
|
||||
return reportApprovalMapper.selectLogPage(
|
||||
new Page<ReportApprovalLogVO>(PageFactory.getPageNum(query), PageFactory.getPageSize(query)), query);
|
||||
}
|
||||
|
||||
private boolean audit(ReportApprovalParam.AuditParam param, boolean passed) {
|
||||
if (param == null || StrUtil.isBlank(param.getReportType()) || StrUtil.isBlank(param.getReportId())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "审批参数不能为空");
|
||||
}
|
||||
ReportApprovalHandler handler = reportApprovalHandlerRegistry.requireHandler(param.getReportType());
|
||||
return handler.audit(param, passed);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.njcn.gather.aireport.reportapproval.pojo.vo;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class ReportApprovalVOTimeFormatTest {
|
||||
|
||||
@Test
|
||||
public void pendingAndLogTimeJsonUseStandardTwentyFourHourFormat() throws Exception {
|
||||
ReportApprovalPendingVO pendingVO = new ReportApprovalPendingVO();
|
||||
pendingVO.setSubmitTime(LocalDateTime.of(2026, 6, 29, 10, 0, 0));
|
||||
ReportApprovalLogVO logVO = new ReportApprovalLogVO();
|
||||
logVO.setApprovalTime(LocalDateTime.of(2026, 6, 29, 18, 30, 45));
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
String pendingJson = objectMapper.writeValueAsString(pendingVO);
|
||||
String logJson = objectMapper.writeValueAsString(logVO);
|
||||
|
||||
Assert.assertTrue(pendingJson.contains("\"submitTime\":\"2026-06-29 10:00:00\""));
|
||||
Assert.assertFalse(pendingJson.contains("2026-06-29T10"));
|
||||
Assert.assertTrue(logJson.contains("\"approvalTime\":\"2026-06-29 18:30:45\""));
|
||||
Assert.assertFalse(logJson.contains("2026-06-29T18"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.njcn.gather.aireport.reportapproval.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.gather.aireport.reportapproval.handler.ReportApprovalHandler;
|
||||
import com.njcn.gather.aireport.reportapproval.handler.ReportApprovalHandlerRegistry;
|
||||
import com.njcn.gather.aireport.reportapproval.mapper.ReportApprovalMapper;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.constant.ReportApprovalConst;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.param.ReportApprovalParam;
|
||||
import com.njcn.gather.aireport.reportapproval.pojo.vo.ReportApprovalPendingVO;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class ReportApprovalServiceImplTest {
|
||||
|
||||
@Test
|
||||
public void pendingPageShouldUseDatabasePagingResult() {
|
||||
ReportApprovalMapper mapper = mock(ReportApprovalMapper.class);
|
||||
ReportApprovalHandlerRegistry registry = new ReportApprovalHandlerRegistry(Collections.<ReportApprovalHandler>emptyList());
|
||||
ReportApprovalServiceImpl service = new ReportApprovalServiceImpl(mapper, registry);
|
||||
ReportApprovalParam.PendingQueryParam param = new ReportApprovalParam.PendingQueryParam();
|
||||
param.setPageNum(2);
|
||||
param.setPageSize(5);
|
||||
|
||||
ReportApprovalPendingVO record = new ReportApprovalPendingVO();
|
||||
record.setReportType(ReportApprovalConst.REPORT_TYPE_REPORT_MODEL);
|
||||
record.setReportId("model-001");
|
||||
record.setReportName("模板A");
|
||||
record.setSubmitterId("user-001");
|
||||
record.setSubmitterName("张三");
|
||||
record.setSubmitTime(LocalDateTime.of(2026, 6, 29, 10, 0, 0));
|
||||
List<ReportApprovalPendingVO> records = Arrays.asList(record);
|
||||
when(mapper.countPendingReports(param)).thenReturn(11L);
|
||||
when(mapper.selectPendingPage(5L, 5L, param)).thenReturn(records);
|
||||
|
||||
Page<ReportApprovalPendingVO> page = service.pendingPage(param);
|
||||
|
||||
Assert.assertEquals(2L, page.getCurrent());
|
||||
Assert.assertEquals(5L, page.getSize());
|
||||
Assert.assertEquals(11L, page.getTotal());
|
||||
Assert.assertEquals(1, page.getRecords().size());
|
||||
Assert.assertEquals("user-001", page.getRecords().get(0).getSubmitterId());
|
||||
Assert.assertEquals("张三", page.getRecords().get(0).getSubmitterName());
|
||||
verify(mapper).countPendingReports(param);
|
||||
verify(mapper).selectPendingPage(5L, 5L, param);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void passShouldDelegateToMatchedHandler() {
|
||||
ReportApprovalMapper mapper = mock(ReportApprovalMapper.class);
|
||||
ReportApprovalHandler handler = mock(ReportApprovalHandler.class);
|
||||
when(handler.getReportType()).thenReturn(ReportApprovalConst.REPORT_TYPE_TEST_REPORT);
|
||||
when(handler.audit(org.mockito.ArgumentMatchers.any(ReportApprovalParam.AuditParam.class), eq(true))).thenReturn(true);
|
||||
ReportApprovalHandlerRegistry registry = new ReportApprovalHandlerRegistry(Arrays.asList(handler));
|
||||
ReportApprovalServiceImpl service = new ReportApprovalServiceImpl(mapper, registry);
|
||||
ReportApprovalParam.AuditParam param = new ReportApprovalParam.AuditParam();
|
||||
param.setReportType(ReportApprovalConst.REPORT_TYPE_TEST_REPORT);
|
||||
param.setReportId("report-001");
|
||||
param.setApprovalReason("通过");
|
||||
|
||||
boolean result = service.pass(param);
|
||||
|
||||
Assert.assertTrue(result);
|
||||
verify(handler).audit(param, true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user