添加审计报告生成功能

This commit is contained in:
2026-03-10 22:48:08 +08:00
parent d36485c0b3
commit c18fb054e0
22 changed files with 2375 additions and 46 deletions

61
doc/sql/update_260308.sql Normal file
View File

@@ -0,0 +1,61 @@
ALTER TABLE `cms_demo`.`pwl_project`
ADD COLUMN `person_name` varchar(50) NULL COMMENT '针对用户名称' AFTER `company_id`,
ADD COLUMN `position` varchar(50) NULL COMMENT '用户职务' AFTER `person_name`;
CREATE TABLE `audit_evidence` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`case_index` varchar(255) DEFAULT NULL COMMENT '案件编号',
`project_id` int(11) NOT NULL COMMENT '项目编号',
`project_name` varchar(255) DEFAULT NULL COMMENT '项目名称',
`content_type` int(10) unsigned NOT NULL COMMENT '内容类型1到11',
`audited_target` varchar(255) DEFAULT NULL COMMENT '被审计单位或个人',
`audit_matter_type` varchar(50) DEFAULT NULL COMMENT '审计事项类型',
`audit_matter` varchar(100) DEFAULT NULL COMMENT '审计事项描述',
`summary_title` varchar(255) DEFAULT NULL COMMENT '核心问题标题',
`audit_record` varchar(2000) DEFAULT NULL COMMENT '客观的审计核查事实记录',
`audit_finding` varchar(255) DEFAULT NULL COMMENT '审计发现的具体问题',
`evidence_basis` varchar(255) DEFAULT NULL COMMENT '定性依据',
`handling` varchar(255) DEFAULT NULL COMMENT '拟采取的处理措施',
`suggestion` varchar(255) DEFAULT NULL COMMENT '改进或整改建议',
`attachment` varchar(255) DEFAULT NULL COMMENT '随附的证明材料',
`auditors` varchar(50) DEFAULT NULL COMMENT '审计人员姓名',
`compile_date` varchar(20) DEFAULT NULL COMMENT '编制日期',
`provider_opinion` varchar(255) DEFAULT NULL COMMENT '证据提供单位或个人意见',
`provider_date` varchar(20) DEFAULT NULL COMMENT '证据提供日期',
`attachment_pages` int(11) DEFAULT NULL COMMENT '附件页数',
`feedback_deadline` varchar(100) DEFAULT NULL COMMENT '反馈期限',
`data` longtext COMMENT '取证单数据',
`user_id` int(11) DEFAULT NULL COMMENT '用户ID',
`username` varchar(30) DEFAULT NULL COMMENT '用户名',
`status` int(11) DEFAULT NULL COMMENT '状态, 0正常, 1冻结',
`deleted` int(11) DEFAULT NULL COMMENT '是否删除, 0否, 1是',
`tenant_id` int(11) DEFAULT NULL COMMENT '租户id',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COMMENT='审计取证单表';
-- 审计报告表
CREATE TABLE IF NOT EXISTS `audit_report` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键 ID',
`project_id` INT(11) DEFAULT NULL COMMENT '项目编号',
`project_name` VARCHAR(255) DEFAULT NULL COMMENT '项目名称',
`case_index` VARCHAR(100) DEFAULT NULL COMMENT '案引号',
`audited_target` VARCHAR(255) DEFAULT NULL COMMENT '被审计单位',
`report_content` LONGTEXT COMMENT '报告内容JSON 格式,包含 sections 和 previewHtml',
`preview_html` LONGTEXT COMMENT '报告预览 HTML',
`section_count` INT(11) DEFAULT NULL COMMENT '章节数量',
`user_id` INT(11) DEFAULT NULL COMMENT '用户 ID',
`username` VARCHAR(100) DEFAULT NULL COMMENT '用户名',
`status` INT(1) DEFAULT 0 COMMENT '状态0 正常1 冻结',
`deleted` INT(1) DEFAULT 0 COMMENT '是否删除0 否1 是',
`tenant_id` INT(11) DEFAULT NULL COMMENT '租户 id',
`form_commit` INT(11) DEFAULT NULL COMMENT '表单提交标识,用于判断记录是否存在',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
KEY `idx_project_id` (`project_id`),
KEY `idx_form_commit` (`form_commit`),
KEY `idx_project_form` (`project_id`, `form_commit`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计报告表';

View File

@@ -12,6 +12,7 @@ import com.gxwebsoft.common.core.web.ApiResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -26,19 +27,17 @@ import java.util.stream.Collectors;
* 审计内容1控制器 - 八项规定对比分析
*/
@Slf4j
@RequiredArgsConstructor
@Tag(name = "审计内容1-八项规定")
@RestController
@RequestMapping("/api/ai/auditContent1")
public class AuditContent1Controller extends BaseAuditContentController {
@Autowired
private AuditContent1LeaderListService auditContent1LeaderListService;
private final AuditContent1LeaderListService auditContent1LeaderListService;
@Autowired
private AuditContent1ExpenseService auditContent1ExpenseService;
private final AuditContent1ExpenseService auditContent1ExpenseService;
@Autowired
private AuditContent1EightRegService auditContent1EightRegService;
private final AuditContent1EightRegService auditContent1EightRegService;
/**
* 生成领导班子名单数据

View File

@@ -2,18 +2,25 @@ package com.gxwebsoft.ai.controller;
import com.alibaba.fastjson.JSONObject;
import com.gxwebsoft.ai.dto.AuditEvidenceRequest;
import com.gxwebsoft.ai.entity.AuditEvidence;
import com.gxwebsoft.ai.param.AuditEvidenceParam;
import com.gxwebsoft.ai.service.AuditEvidenceService;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.User;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
@@ -40,36 +47,83 @@ import cn.afterturn.easypoi.word.WordExportUtil;
*/
@Slf4j
@Tag(name = "审计取证单")
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/ai/auditEvidence")
public class AuditEvidenceController extends BaseController {
@Autowired
private AuditEvidenceService auditEvidenceService;
@Autowired
private TemplateConfig templateConfig;
private final AuditEvidenceService auditEvidenceService;
private final TemplateConfig templateConfig;
/**
* 生成审计取证单
*/
@Operation(summary = "生成审计取证单")
@PostMapping("/generate")
public ApiResult<?> generateAuditEvidence(@RequestBody AuditEvidenceRequest request) {
public ApiResult<AuditEvidence> generateAuditEvidence(@RequestBody AuditEvidenceRequest request) {
try {
final User loginUser = getLoginUser();
request.setUserName(loginUser.getUsername());
final User loginUser = getLoginUser();
request.setUserName(loginUser.getUsername());
log.info("接收到审计取证单生成请求 - 用户: {}, 项目: {}", request.getUserName(), request.getProjectName());
JSONObject result = auditEvidenceService.generateAuditEvidence(request);
return auditEvidenceService.generateAuditEvidence(request);
/*JSONObject result = auditEvidenceService.generateAuditEvidence(request);
if (Boolean.TRUE.equals(result.getBoolean("success"))) {
return success(result);
} else {
return fail(result.getString("error") != null ? result.getString("error") : "生成审计取证单失败");
}
}*/
} catch (Exception e) {
log.error("生成审计取证单异常", e);
return fail("生成审计取证单异常: " + e.getMessage());
return fail("生成审计取证单异常: " + e.getMessage(), null);
}
}
/**
* 分页查询审计取证单记录
*/
@Operation(summary = "分页查询审计取证单记录")
@PostMapping("/page")
public ApiResult<PageResult<AuditEvidence>> getAuditEvidencePage(
@Parameter(description = "页码,默认为 1") @RequestParam(defaultValue = "1") Integer current,
@Parameter(description = "页面大小,默认为 10") @RequestParam(defaultValue = "10") Integer size,
@RequestBody(required = false) AuditEvidenceParam param) {
try {
// 创建分页对象
param.setPage(current.longValue());
param.setLimit(size.longValue());
PageParam<AuditEvidence, AuditEvidenceParam> page =
new PageParam<>(param);
// 调用服务层分页查询方法
PageResult<AuditEvidence> resultPage =
auditEvidenceService.getAuditEvidencePage(page, param);
return success(resultPage);
} catch (Exception e) {
log.error("查询审计取证单分页记录失败", e);
return fail("查询审计取证单分页记录失败:" + e.getMessage(), null);
}
}
/**
* 保存审计取证单到数据库
*/
@Operation(summary = "保存审计取证单")
@PostMapping("/save")
public ApiResult<AuditEvidence> saveAuditEvidence(@RequestBody AuditEvidenceRequest request) {
try {
final User loginUser = getLoginUser();
request.setUserName(loginUser.getUsername());
request.setUserId(loginUser.getUserId());
request.setTenantId(loginUser.getTenantId());
log.info("接收到审计取证单保存请求 - 用户:{}, 项目:{}", request.getUserName(), request.getProjectName());
return auditEvidenceService.saveAuditEvidence(request);
} catch (Exception e) {
log.error("保存审计取证单异常", e);
return fail("保存审计取证单异常:" + e.getMessage(), null);
}
}

View File

@@ -1,13 +1,19 @@
package com.gxwebsoft.ai.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class AuditEvidenceRequest {
// 基础信息
private String caseIndex; // 案引号
private Integer projectId; // 项目名称
private String projectName; // 项目名称
@Schema(description = "内容类型1到11")
private Integer contentType;
private String auditedTarget; // 被审计单位或个人
@Schema(description = "审计事项对应AuditMatterTypeEnum")
private String auditMatterType;
private String auditMatter; // 审计事项
private String summaryTitle; // 标题
private String auditRecord; // 审计记录
@@ -32,4 +38,6 @@ public class AuditEvidenceRequest {
// 用户信息
private String userName;
private Integer userId;
private Integer tenantId;
}

View File

@@ -0,0 +1,39 @@
package com.gxwebsoft.ai.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "审计报告生成请求")
@Data
public class AuditReportGenRequest {
// 基础信息
private Integer projectId; // 项目名称
@Schema(description = "内容类型1到11")
private Integer contentType;
private String auditedTarget; // 被审计单位或个人
private String summaryTitle; // 标题
private String auditRecord; // 审计记录
private String auditFinding; // 审计发现
private String evidenceBasis; // 定性依据
private String handling; // 处理
private String suggestion; // 建议
private String attachment; // 附件
private String auditors; // 审计人员
private String compileDate; // 编制日期
private String providerOpinion; // 证据提供单位意见
private String providerDate; // 证据提供日期
private String attachmentPages; // 附件页数
private String feedbackDeadline; // 反馈期限
// 导出取证单使用
private String pageIndex;
private String pageTotal;
// 历史内容(用于工作流生成)
private String history;
// 用户信息
private String userName;
private Integer userId;
private Integer tenantId;
}

View File

@@ -0,0 +1,36 @@
package com.gxwebsoft.ai.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 审计报告保存请求 DTO
*/
@Data
@Schema(description = "审计报告保存请求")
public class AuditReportSaveRequest {
@Schema(description = "项目编号")
private Integer projectId;
@Schema(description = "项目名称")
private String projectName;
@Schema(description = "案引号")
private String caseIndex;
@Schema(description = "被审计单位")
private String auditedTarget;
@Schema(description = "报告内容JSON 格式)")
private String reportContent;
@Schema(description = "报告预览 HTML")
private String previewHtml;
@Schema(description = "章节数量")
private Integer sectionCount;
@Schema(description = "表单提交标识")
private Integer formCommit;
}

View File

@@ -0,0 +1,114 @@
package com.gxwebsoft.ai.entity;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 审计取证单表
*
* @author yc
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "AuditEvidence对象", description = "审计取证单表")
@TableName("audit_evidence")
public class AuditEvidence implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "主键ID")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@Schema(description = "案件编号")
private String caseIndex;
@Schema(description = "项目编号")
private Integer projectId;
@Schema(description = "项目名称")
private String projectName;
@Schema(description = "内容类型1到11")
private Integer contentType;
@Schema(description = "被审计单位或个人")
private String auditedTarget;
@Schema(description = "审计事项类型对应AuditMatterTypeEnum")
private String auditMatterType;
@Schema(description = "审计事项描述")
private String auditMatter;
@Schema(description = "核心问题标题")
private String summaryTitle;
@Schema(description = "客观的审计核查事实记录")
private String auditRecord;
@Schema(description = "审计发现的具体问题")
private String auditFinding;
@Schema(description = "定性依据")
private String evidenceBasis;
@Schema(description = "拟采取的处理措施")
private String handling;
@Schema(description = "改进或整改建议")
private String suggestion;
@Schema(description = "随附的证明材料")
private String attachment;
@Schema(description = "审计人员姓名")
private String auditors;
@Schema(description = "编制日期")
private String compileDate;
@Schema(description = "证据提供单位或个人意见")
private String providerOpinion;
@Schema(description = "证据提供日期")
private String providerDate;
@Schema(description = "附件页数")
private Integer attachmentPages;
@Schema(description = "反馈期限")
private String feedbackDeadline;
@Schema(description = "取证单数据")
private String data;
@Schema(description = "用户ID")
private Integer userId;
@Schema(description = "用户名")
private String username;
@Schema(description = "状态, 0正常, 1冻结")
private Integer status;
@Schema(description = "是否删除, 0否, 1是")
@TableLogic
private Integer deleted;
@Schema(description = "租户id")
private Integer tenantId;
@Schema(description = "创建时间")
private LocalDateTime createTime;
@Schema(description = "修改时间")
private LocalDateTime updateTime;
@TableField(exist = false)
private Boolean success;
}

View File

@@ -0,0 +1,75 @@
package com.gxwebsoft.ai.entity;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 审计报告表
*
* @author yc
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "AuditReport 对象", description = "审计报告表")
@TableName("audit_report")
public class AuditReport implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "主键 ID")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@Schema(description = "项目编号")
private Integer projectId;
@Schema(description = "项目名称")
private String projectName;
@Schema(description = "案引号")
private String caseIndex;
@Schema(description = "被审计单位")
private String auditedTarget;
@Schema(description = "报告内容JSON 格式,包含 sections 和 previewHtml")
private String reportContent;
@Schema(description = "报告预览 HTML")
private String previewHtml;
@Schema(description = "章节数量")
private Integer sectionCount;
@Schema(description = "用户 ID")
private Integer userId;
@Schema(description = "用户名")
private String username;
@Schema(description = "状态0 正常1 冻结")
private Integer status;
@Schema(description = "是否删除0 否1 是")
@TableLogic
private Integer deleted;
@Schema(description = "租户 id")
private Integer tenantId;
@Schema(description = "创建时间")
private LocalDateTime createTime;
@Schema(description = "修改时间")
private LocalDateTime updateTime;
@Schema(description = "表单提交标识,用于判断记录是否存在")
private Integer formCommit;
@TableField(exist = false)
private Boolean success;
}

View File

@@ -0,0 +1,57 @@
package com.gxwebsoft.ai.enums;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
@Schema(description = "审计表类型")
public enum AuditMatterTypeEnum {
EIGHT_REGULATION(1,"eightReg", "八项规定"),
EXPENSE(1,"expense", "决策支出表"),
LEADER_LIST(1,"leaderList", "领导班子名单"),
STRATEGY(2,"strategyAudit", "单位发展战略执行"),
DECISION_TABLE(3,"decisionTable", "重大经济决策调查表"),
TRIPLE_ONE(3,"tripleOne", "三重一大"),
TARGET(4,"target", "目标责任制完成情况表"),
BUDGET_EXECUTION(5,"budgetExecution", "预算执行情况审计"),
BUDGET_MANAGE(5,"budgetManage", "预算管理审计"),
STATE_ASSETS(6,"assets", "国有资产管理审计"),
INVESTMENT_SITUATION(7,"investmentSituation", "重大投资情况"),
INTERNAL_CONTROL(8,"internalControl", "内部控制测试表"),
PERSONNEL_ESTABLISHMENT(9,"personnel", "人员编制管理审计"),
PARTY_CONDUCT(10,"partyConduct", "党风廉政建设责任制审计"),
HISTORY(11,"history", "历史审计问题整改");
private final Integer value;
private final String code;
private final String desc;
public static AuditMatterTypeEnum getByCode(String code) {
for (AuditMatterTypeEnum type : AuditMatterTypeEnum.values()) {
if (type.getCode().equals(code)) {
return type;
}
}
return null;
}
public static AuditMatterTypeEnum getByValue(Integer value) {
for (AuditMatterTypeEnum type : AuditMatterTypeEnum.values()) {
if (type.getValue().equals(value)) {
return type;
}
}
return null;
}
public static AuditMatterTypeEnum getByDesc(String desc) {
for (AuditMatterTypeEnum type : AuditMatterTypeEnum.values()) {
if (type.getDesc().equals(desc)) {
return type;
}
}
return null;
}
}

View File

@@ -24,11 +24,12 @@ public enum AuditReportEnum {
INTEGRITY_COMPLIANCE(56, "五、审计内容和重点及审计方法-(六)在经济活动中落实有关党风廉政建设责任和遵守廉洁从业规定情况"),
PREV_AUDIT_ISSUES(57, "五、审计内容和重点及审计方法-(七)对以往审计中发现问题的整改情况"),
OTHER_MATTERS(58, "五、审计内容和重点及审计方法-(八)其他需要审计的事项"),
// 重要风险的识别及应对
RISK_IDENTIFY(61, "六、重要风险的识别及应对-(一)重要风险的识别"),
RISK_RESPONSE(62, "六、重要风险的识别及应对-(二)风险的应对策略"),
// 其他部分
TECHNIQUES(70, "七、审计技术方法"),
SCHEDULE(80, "八、工作步骤与时间安排"),

View File

@@ -0,0 +1,35 @@
package com.gxwebsoft.ai.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.gxwebsoft.ai.entity.AuditEvidence;
import com.gxwebsoft.ai.param.AuditEvidenceParam;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 审计取证单表Mapper
*
* @author yc
*/
public interface AuditEvidenceMapper extends BaseMapper<AuditEvidence> {
/**
* 分页查询
*
* @param page 分页对象
* @param param 查询参数
* @return IPage<AuditEvidence>
*/
IPage<AuditEvidence> selectPageRel(@Param("page") IPage<AuditEvidence> page,
@Param("param") AuditEvidenceParam param);
/**
* 查询全部
*
* @param param 查询参数
* @return List<AuditEvidence>
*/
List<AuditEvidence> selectListRel(@Param("param") AuditEvidenceParam param);
}

View File

@@ -0,0 +1,12 @@
package com.gxwebsoft.ai.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gxwebsoft.ai.entity.AuditReport;
import org.apache.ibatis.annotations.Mapper;
/**
* 审计报告 Mapper 接口
*/
@Mapper
public interface AuditReportMapper extends BaseMapper<AuditReport> {
}

View File

@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gxwebsoft.ai.mapper.AuditEvidenceMapper">
<!-- 关联查询sql -->
<sql id="selectSql">
SELECT a.*
FROM audit_evidence a
<where>
<if test="param.id != null">
AND a.id = #{param.id}
</if>
<if test="param.caseIndex != null">
AND a.case_index = #{param.caseIndex}
</if>
<if test="param.projectName != null">
AND a.project_name LIKE CONCAT('%', #{param.projectName}, '%')
</if>
<if test="param.auditedTarget != null">
AND a.audited_target LIKE CONCAT('%', #{param.auditedTarget}, '%')
</if>
<if test="param.auditMatter != null and param.auditMatter != ''">
AND a.audit_matter LIKE CONCAT('%', #{param.auditMatter}, '%')
</if>
<if test="param.summaryTitle != null and param.summaryTitle != ''">
AND a.summary_title LIKE CONCAT('%', #{param.summaryTitle}, '%')
</if>
<if test="param.contentType != null">
AND a.content_type = #{param.contentType}
</if>
<if test="param.userId != null">
AND a.user_id = #{param.userId}
</if>
<if test="param.username != null">
AND a.username LIKE CONCAT('%', #{param.username}, '%')
</if>
<if test="param.status != null">
AND a.status = #{param.status}
</if>
<if test="param.deleted != null">
AND a.deleted = #{param.deleted}
</if>
<if test="param.deleted == null">
AND a.deleted = 0
</if>
<if test="param.tenantId != null">
AND a.tenant_id = #{param.tenantId}
</if>
<if test="param.startTime != null">
AND a.create_time &gt;= #{param.startTime}
</if>
<if test="param.endTime != null">
AND a.create_time &lt;= #{param.endTime}
</if>
</where>
</sql>
<!-- 分页查询 -->
<select id="selectPageRel" resultType="com.gxwebsoft.ai.entity.AuditEvidence">
<include refid="selectSql"></include>
ORDER BY a.create_time DESC
</select>
<!-- 查询全部 -->
<select id="selectListRel" resultType="com.gxwebsoft.ai.entity.AuditEvidence">
SELECT a.*
FROM audit_evidence a
<where>
<if test="param.id != null">
AND a.id = #{param.id}
</if>
<if test="param.caseIndex != null">
AND a.case_index = #{param.caseIndex}
</if>
<if test="param.projectName != null">
AND a.project_name LIKE CONCAT('%', #{param.projectName}, '%')
</if>
<if test="param.status != null">
AND a.status = #{param.status}
</if>
<if test="param.deleted != null">
AND a.deleted = #{param.deleted}
</if>
<if test="param.deleted == null">
AND a.deleted = 0
</if>
<if test="param.tenantId != null">
AND a.tenant_id = #{param.tenantId}
</if>
<if test="param.startTime != null">
AND a.create_time &gt;= #{param.startTime}
</if>
<if test="param.endTime != null">
AND a.create_time &lt;= #{param.endTime}
</if>
</where>
ORDER BY a.create_time DESC
</select>
</mapper>

View File

@@ -0,0 +1,65 @@
package com.gxwebsoft.ai.param;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import com.gxwebsoft.common.core.web.BaseParam;
import java.time.LocalDateTime;
/**
* 审计取证单查询参数
*
* @author yc
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "AuditEvidenceParam对象", description = "审计取证单查询参数")
public class AuditEvidenceParam extends BaseParam {
@Schema(description = "主键ID")
private Long id;
@Schema(description = "案件编号")
private String caseIndex;
@Schema(description = "项目编号")
private String projectId;
@Schema(description = "项目名称")
private String projectName;
@Schema(description = "被审计单位或个人")
private String auditedTarget;
@Schema(description = "审计事项类型")
private String auditMatterType;
@Schema(description = "审计事项描述")
private String auditMatter;
@Schema(description = "核心问题标题")
private String summaryTitle;
@Schema(description = "内容类型1到11")
private Integer contentType;
@Schema(description = "用户ID")
private Integer userId;
@Schema(description = "用户名")
private String username;
@Schema(description = "删除状态, 0未删除, 1已删除")
private Integer deleted;
@Schema(description = "状态, 0正常, 1冻结")
private Integer status;
@Schema(description = "开始时间")
private LocalDateTime startTime;
@Schema(description = "结束时间")
private LocalDateTime endTime;
}

View File

@@ -2,10 +2,25 @@ package com.gxwebsoft.ai.service;
import com.alibaba.fastjson.JSONObject;
import com.gxwebsoft.ai.dto.AuditEvidenceRequest;
import com.gxwebsoft.ai.entity.AuditEvidence;
import com.gxwebsoft.ai.param.AuditEvidenceParam;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
public interface AuditEvidenceService {
/**
* 生成审计取证单
*/
JSONObject generateAuditEvidence(AuditEvidenceRequest request);
ApiResult<AuditEvidence> generateAuditEvidence(AuditEvidenceRequest request);
/**
* 分页查询审计取证单记录
*/
PageResult<AuditEvidence> getAuditEvidencePage(PageParam<AuditEvidence, AuditEvidenceParam> page, AuditEvidenceParam param);
/**
* 保存审计取证单到数据库(新增或更新)
*/
ApiResult<AuditEvidence> saveAuditEvidence(AuditEvidenceRequest request);
}

View File

@@ -1,6 +1,13 @@
package com.gxwebsoft.ai.service;
import com.alibaba.fastjson.JSONObject;
import com.gxwebsoft.ai.dto.AuditReportSaveRequest;
import com.gxwebsoft.ai.entity.AuditEvidence;
import com.gxwebsoft.ai.entity.AuditReport;
import com.gxwebsoft.common.core.web.ApiResult;
import java.util.List;
import java.util.Map;
public interface AuditReportService {
@@ -11,4 +18,29 @@ public interface AuditReportService {
String kbId, String libraryIds,
String analysisLibrary, String projectLibrary,
String userName);
/**
* 保存审计报告到数据库(新增或更新)
*/
ApiResult<AuditReport> saveAuditReport(AuditReportSaveRequest request);
/**
* 根据 projectId 和 formCommit 查询审计报告
*/
ApiResult<AuditReport> queryAuditReport(Integer projectId, Integer formCommit);
/**
* 根据项目 ID 查询所有审计报告和取证单数据
* @param projectId 项目 ID
* @return Map包含 reports (AuditReport 列表) 和 evidences (AuditEvidence 列表)
*/
Map<String, Object> queryAuditDataByProjectId(Integer projectId, List<Long> selectedEvidenceIds);
/**
* 根据项目 ID 查询审计报告和取证单数据,并对勾选的取证单进行 AI 分析
* @param projectId 项目 ID
* @param selectedEvidenceIds 勾选的取证单 ID 列表(可为 null
* @return 包含 reports、evidences、evaluate、suggestion 的 Map
*/
Map<String, Object> queryAuditDataByProjectIdWithAnalysis(Integer projectId, List<Long> selectedEvidenceIds);
}

View File

@@ -65,7 +65,7 @@ public class AuditContent9PersonnelServiceImpl extends AbstractAuditContentServi
} catch (Exception e) {
log.error("生成人员编制管理审计表失败", e);
return buildErrorResponse("生成失败: " + e.getMessage());
return buildErrorResponse("生成人员编制管理审计失败: " + e.getMessage());
}
}

View File

@@ -1,27 +1,35 @@
package com.gxwebsoft.ai.service.impl;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.ai.dto.AuditEvidenceRequest;
import com.gxwebsoft.ai.entity.AuditEvidence;
import com.gxwebsoft.ai.enums.AuditMatterTypeEnum;
import com.gxwebsoft.ai.mapper.AuditEvidenceMapper;
import com.gxwebsoft.ai.param.AuditEvidenceParam;
import com.gxwebsoft.ai.service.AuditEvidenceService;
import com.gxwebsoft.common.core.Constants;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import java.util.Date;
@Slf4j
@Service
public class AuditEvidenceServiceImpl implements AuditEvidenceService {
public class AuditEvidenceServiceImpl extends ServiceImpl<AuditEvidenceMapper, AuditEvidence> implements AuditEvidenceService {
// Dify工作流配置
private static final String DIFY_WORKFLOW_URL = "http://1.14.159.185:8180/v1/workflows/run";
private static final String DIFY_WORKFLOW_TOKEN = "Bearer app-DEhvF537rQfy6MvH9KeBKUVj";
@Override
public JSONObject generateAuditEvidence(AuditEvidenceRequest request) {
public ApiResult<AuditEvidence> generateAuditEvidence(AuditEvidenceRequest request) {
log.info("开始生成审计取证单 - 用户: {}, 项目: {}", request.getUserName(), request.getProjectName());
long startTime = System.currentTimeMillis();
@@ -33,21 +41,24 @@ public class AuditEvidenceServiceImpl implements AuditEvidenceService {
// 2. 构建工作流请求
JSONObject requestBody = buildWorkflowRequest(knowledgeContext, request.getUserName());
// 3. 调用Dify工作流
JSONObject result = callWorkflow(requestBody, "审计取证单");
// 3. 调用 Dify 工作流
AuditEvidence result = callWorkflow(requestBody);
// 4. 添加处理时间等信息
result.put("success", true);
result.put("processing_time", (System.currentTimeMillis() - startTime) + "ms");
result.put("generated_time", new Date().toString());
// 4. 保存到数据库
// saveToDatabase(result, request);
log.info("审计取证单生成成功 - 处理时间: {}ms", (System.currentTimeMillis() - startTime));
return result;
// 5. 添加处理时间等信息
if (result != null) {
result.setSuccess(true);
log.info("审计取证单生成成功 - ID: {}, 处理时间:{}ms", result.getId(), (System.currentTimeMillis() - startTime));
}
return new ApiResult<>(Constants.RESULT_OK_CODE, Constants.RESULT_OK_MSG, result);
} catch (Exception e) {
log.error("生成审计取证单失败", e);
return buildErrorResponse("生成审计取证单失败: " + e.getMessage());
return new ApiResult<>(1, "生成审计取证单失败: " + e.getMessage());
// return buildErrorResponse("生成审计取证单失败: " + e.getMessage());
}
}
@@ -153,7 +164,7 @@ public class AuditEvidenceServiceImpl implements AuditEvidenceService {
/**
* 调用工作流
*/
private JSONObject callWorkflow(JSONObject requestBody, String workflowName) {
/*private JSONObject callWorkflow(JSONObject requestBody, String workflowName) {
try {
log.info("调用{}工作流,请求体长度: {}", workflowName, requestBody.toString().length());
@@ -189,16 +200,260 @@ public class AuditEvidenceServiceImpl implements AuditEvidenceService {
log.error("调用{}工作流失败", workflowName, e);
throw new RuntimeException("调用" + workflowName + "工作流失败: " + e.getMessage(), e);
}
}*/
private AuditEvidence callWorkflow(JSONObject requestBody) {
try {
log.info("调用审计取证单工作流,请求体长度: {}", requestBody.toString().length());
String result = HttpUtil.createPost(DIFY_WORKFLOW_URL)
.header("Authorization", DIFY_WORKFLOW_TOKEN)
.header("Content-Type", "application/json")
.body(requestBody.toString())
.timeout(5 * 60 * 1000) // 5分钟
.execute()
.body();
log.info("审计取证单工作流返回结果长度: {}", result.length());
JSONObject jsonResponse = JSONObject.parseObject(result);
// 提取输出文本
String outputText = jsonResponse.getJSONObject("data")
.getJSONObject("outputs")
.getString("result");
if (StrUtil.isBlank(outputText)) {
log.warn("审计取证单工作流返回结果为空");
return null;
}
// 解析为JSON对象
AuditEvidence auditEvidence = JSONObject.parseObject(outputText, AuditEvidence.class);
log.info("成功解析审计取证单工作流返回数据");
return auditEvidence;
} catch (Exception e) {
log.error("调用审计取证单工作流失败", e);
throw new RuntimeException("调用审计取证单工作流失败: " + e.getMessage(), e);
}
}
/**
* 构建错误响应
* 保存审计取证单到数据库
*/
private JSONObject buildErrorResponse(String errorMessage) {
JSONObject result = new JSONObject();
result.put("success", false);
result.put("error", errorMessage);
result.put("timestamp", System.currentTimeMillis());
return result;
/*private void saveToDatabase(AuditEvidence auditEvidence, AuditEvidenceRequest request) {
try {
// AuditEvidence auditEvidence = new AuditEvidence();
// 基本信息
auditEvidence.setAuditMatterType(request.getAuditMaterType());
auditEvidence.setContentType(request.getContentType());
auditEvidence.setCaseIndex(request.getCaseIndex());
auditEvidence.setProjectId(request.getProjectId());
auditEvidence.setProjectName(request.getProjectName());
auditEvidence.setData(request.getHistory());
// auditEvidence.setAuditedTarget(result.getString("auditedTarget"));
// auditEvidence.setAuditMatter(result.getString("auditMatter"));
// auditEvidence.setSummaryTitle(result.getString("summaryTitle"));
// 审计内容
// auditEvidence.setAuditRecord(result.getString("auditRecord"));
// auditEvidence.setAuditFinding(result.getString("auditFinding"));
// auditEvidence.setEvidenceBasis(result.getString("evidenceBasis"));
// auditEvidence.setHandling(result.getString("handling"));
// auditEvidence.setSuggestion(result.getString("suggestion"));
// auditEvidence.setAttachment(result.getString("attachment"));
// 时间和人员信息
// auditEvidence.setAuditors(result.getString("auditors"));
// auditEvidence.setCompileDate(result.getString("compileDate"));
// auditEvidence.setProviderOpinion(result.getString("providerOpinion"));
// auditEvidence.setProviderDate(result.getString("providerDate"));
// auditEvidence.setAttachmentPages(result.getString("attachmentPages"));
// auditEvidence.setFeedbackDeadline(result.getString("feedbackDeadline"));
// 用户信息
auditEvidence.setUserId(request.getUserId());
auditEvidence.setUsername(request.getUserName());
auditEvidence.setTenantId(request.getTenantId());
// 状态信息
auditEvidence.setStatus(0); // 正常状态
auditEvidence.setDeleted(0); // 未删除
// 保存到数据库
baseMapper.insert(auditEvidence);
// log.info("审计取证单保存成功 - ID: {}", auditEvidence.getId());
// return auditEvidence;
} catch (Exception e) {
log.error("保存审计取证单到数据库失败", e);
throw new RuntimeException("保存审计取证单到数据库失败: " + e.getMessage(), e);
}
}*/
@Override
public PageResult<AuditEvidence> getAuditEvidencePage(PageParam<AuditEvidence, AuditEvidenceParam> page, AuditEvidenceParam param) {
log.info("查询审计取证单分页列表 - 页码: {}, 页面大小: {}", page.getCurrent(), page.getSize());
try {
// 调用Mapper的分页查询方法
IPage<AuditEvidence> list = baseMapper.selectPageRel(page, param);
log.info("审计取证单分页查询成功 - 总记录数: {}, 当前页记录数: {}",
page.getTotal(), list.getTotal());
// 返回分页结果
return new PageResult<>(list.getRecords(), page.getTotal());
} catch (Exception e) {
log.error("查询审计取证单分页列表失败", e);
throw new RuntimeException("查询审计取证单分页列表失败:" + e.getMessage(), e);
}
}
@Override
public ApiResult<AuditEvidence> saveAuditEvidence(AuditEvidenceRequest request) {
log.info("开始保存审计取证单 - 用户:{}, 项目:{}", request.getUserName(), request.getProjectName());
try {
// 1. 验证并转换 auditMatterType
if (StrUtil.isBlank(request.getAuditMatterType())) {
return new ApiResult<>(1, "审计事项类型不能为空");
}
AuditMatterTypeEnum matterType = AuditMatterTypeEnum.getByCode(request.getAuditMatterType());
if (matterType == null) {
return new ApiResult<>(1, "无效的审计事项类型:" + request.getAuditMatterType());
}
// 2. 检查是否已存在(根据 projectId + auditMatterType
if (request.getProjectId() == null) {
return new ApiResult<>(1, "项目编号不能为空");
}
LambdaQueryWrapper<AuditEvidence> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AuditEvidence::getProjectId, request.getProjectId())
.eq(AuditEvidence::getAuditMatterType, request.getAuditMatterType())
.eq(AuditEvidence::getDeleted, 0);
AuditEvidence existingEvidence = baseMapper.selectOne(queryWrapper);
AuditEvidence auditEvidence;
if (existingEvidence != null) {
// 3. 更新现有记录
log.info("发现已存在的记录,执行更新操作 - ID: {}", existingEvidence.getId());
auditEvidence = existingEvidence;
// 更新字段
auditEvidence.setCaseIndex(request.getCaseIndex());
auditEvidence.setProjectName(request.getProjectName());
auditEvidence.setContentType(request.getContentType());
// 审计内容
auditEvidence.setSummaryTitle(request.getSummaryTitle());
auditEvidence.setAuditRecord(request.getAuditRecord());
auditEvidence.setAuditFinding(request.getAuditFinding());
auditEvidence.setEvidenceBasis(request.getEvidenceBasis());
auditEvidence.setHandling(request.getHandling());
auditEvidence.setSuggestion(request.getSuggestion());
auditEvidence.setAttachment(request.getAttachment());
// 时间和人员信息
auditEvidence.setAuditors(request.getAuditors());
auditEvidence.setCompileDate(request.getCompileDate());
auditEvidence.setProviderOpinion(request.getProviderOpinion());
auditEvidence.setProviderDate(request.getProviderDate());
// 处理附件页数(转换为 Integer
if (request.getAttachmentPages() != null && !request.getAttachmentPages().isEmpty()) {
try {
auditEvidence.setAttachmentPages(Integer.parseInt(request.getAttachmentPages()));
} catch (NumberFormatException e) {
log.warn("附件页数转换失败,使用默认值 0", e);
auditEvidence.setAttachmentPages(0);
}
} else {
auditEvidence.setAttachmentPages(0);
}
auditEvidence.setFeedbackDeadline(request.getFeedbackDeadline());
// 将原始数据保存为 JSON 字符串
auditEvidence.setData(com.alibaba.fastjson.JSONObject.toJSONString(request));
// 更新数据库
baseMapper.updateById(auditEvidence);
log.info("审计取证单更新成功 - ID: {}", auditEvidence.getId());
} else {
// 4. 新增记录
log.info("未发现已存在的记录,执行新增操作");
auditEvidence = new AuditEvidence();
// 基本信息
auditEvidence.setCaseIndex(request.getCaseIndex());
auditEvidence.setProjectId(request.getProjectId());
auditEvidence.setProjectName(request.getProjectName());
auditEvidence.setContentType(request.getContentType());
auditEvidence.setAuditMatterType(request.getAuditMatterType());
auditEvidence.setAuditMatter(request.getAuditMatter());
// 审计内容
auditEvidence.setSummaryTitle(request.getSummaryTitle());
auditEvidence.setAuditRecord(request.getAuditRecord());
auditEvidence.setAuditFinding(request.getAuditFinding());
auditEvidence.setEvidenceBasis(request.getEvidenceBasis());
auditEvidence.setHandling(request.getHandling());
auditEvidence.setSuggestion(request.getSuggestion());
auditEvidence.setAttachment(request.getAttachment());
// 时间和人员信息
auditEvidence.setAuditors(request.getAuditors());
auditEvidence.setCompileDate(request.getCompileDate());
auditEvidence.setProviderOpinion(request.getProviderOpinion());
auditEvidence.setProviderDate(request.getProviderDate());
// 处理附件页数(转换为 Integer
if (request.getAttachmentPages() != null && !request.getAttachmentPages().isEmpty()) {
try {
auditEvidence.setAttachmentPages(Integer.parseInt(request.getAttachmentPages()));
} catch (NumberFormatException e) {
log.warn("附件页数转换失败,使用默认值 0", e);
auditEvidence.setAttachmentPages(0);
}
} else {
auditEvidence.setAttachmentPages(0);
}
auditEvidence.setFeedbackDeadline(request.getFeedbackDeadline());
// 将原始数据保存为 JSON 字符串
auditEvidence.setData(com.alibaba.fastjson.JSONObject.toJSONString(request));
// 用户信息
auditEvidence.setUserId(request.getUserId());
auditEvidence.setUsername(request.getUserName());
auditEvidence.setTenantId(request.getTenantId());
// 状态信息
auditEvidence.setStatus(0); // 正常状态
auditEvidence.setDeleted(0); // 未删除
// 保存到数据库
baseMapper.insert(auditEvidence);
log.info("审计取证单保存成功 - ID: {}", auditEvidence.getId());
}
return new ApiResult<>(Constants.RESULT_OK_CODE, Constants.RESULT_OK_MSG, auditEvidence);
} catch (Exception e) {
log.error("保存审计取证单到数据库失败", e);
return new ApiResult<>(1, "保存审计取证单失败:" + e.getMessage());
}
}
}

View File

@@ -10,29 +10,48 @@ import com.gxwebsoft.ai.config.KnowledgeBaseConfig;
import com.gxwebsoft.ai.factory.KnowledgeBaseClientFactory;
import com.gxwebsoft.ai.service.AuditReportService;
import com.gxwebsoft.ai.util.AiCloudKnowledgeBaseUtil;
import com.gxwebsoft.pwl.entity.PwlProject;
import com.gxwebsoft.pwl.entity.PwlProjectLibrary;
import com.gxwebsoft.pwl.service.PwlProjectLibraryService;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.gxwebsoft.pwl.service.PwlProjectService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.ai.dto.AuditReportSaveRequest;
import com.gxwebsoft.ai.entity.AuditEvidence;
import com.gxwebsoft.ai.entity.AuditReport;
import com.gxwebsoft.ai.mapper.AuditEvidenceMapper;
import com.gxwebsoft.ai.mapper.AuditReportMapper;
import com.gxwebsoft.common.core.Constants;
import com.gxwebsoft.common.core.web.ApiResult;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class AuditReportServiceImpl implements AuditReportService {
public class AuditReportServiceImpl extends ServiceImpl<AuditReportMapper, AuditReport> implements AuditReportService {
@Autowired
private KnowledgeBaseClientFactory clientFactory;
@Autowired
private KnowledgeBaseConfig config;
@Autowired
private PwlProjectService pwlProjectService;
@Autowired
private PwlProjectLibraryService pwlProjectLibraryService;
@Autowired
private AuditEvidenceMapper auditEvidenceMapper;
// 工作流配置
private static final String QUESTION_GENERATION_WORKFLOW_URL = "http://1.14.159.185:8180/v1/workflows/run";
@@ -344,4 +363,331 @@ public class AuditReportServiceImpl implements AuditReportService {
String[] keywords = {"制度", "办法", "规定", "流程", "标准", "规范", "要求"};
return String.join(" ", keywords);
}
@Override
public ApiResult<AuditReport> saveAuditReport(AuditReportSaveRequest request) {
log.info("开始保存审计报告 - 用户:{}, 项目:{}", request.getAuditedTarget(), request.getProjectName());
try {
// 1. 验证必填字段
if (request.getProjectId() == null) {
return new ApiResult<>(1, "项目编号不能为空");
}
if (request.getFormCommit() == null) {
return new ApiResult<>(1, "表单提交标识不能为空");
}
// 2. 检查是否已存在根据projectId + formCommit
LambdaQueryWrapper<AuditReport> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AuditReport::getProjectId, request.getProjectId())
.eq(AuditReport::getFormCommit, request.getFormCommit())
.eq(AuditReport::getDeleted, 0);
AuditReport existingReport = baseMapper.selectOne(queryWrapper);
AuditReport auditReport;
if (existingReport != null) {
// 3. 更新现有记录
log.info("发现已存在的记录,执行更新操作 - ID: {}", existingReport.getId());
auditReport = existingReport;
// 更新字段
auditReport.setProjectName(request.getProjectName());
auditReport.setCaseIndex(request.getCaseIndex());
auditReport.setAuditedTarget(request.getAuditedTarget());
auditReport.setReportContent(request.getReportContent());
auditReport.setPreviewHtml(request.getPreviewHtml());
auditReport.setSectionCount(request.getSectionCount());
// 更新数据库
baseMapper.updateById(auditReport);
log.info("审计报告更新成功 - ID: {}", auditReport.getId());
} else {
// 4. 新增记录
log.info("未发现已存在的记录,执行新增操作");
auditReport = new AuditReport();
// 基本信息
auditReport.setProjectId(request.getProjectId());
auditReport.setProjectName(request.getProjectName());
auditReport.setCaseIndex(request.getCaseIndex());
auditReport.setAuditedTarget(request.getAuditedTarget());
auditReport.setReportContent(request.getReportContent());
auditReport.setPreviewHtml(request.getPreviewHtml());
auditReport.setSectionCount(request.getSectionCount());
auditReport.setFormCommit(request.getFormCommit());
// 保存到数据库
baseMapper.insert(auditReport);
log.info("审计报告保存成功 - ID: {}", auditReport.getId());
}
// 设置 success 标志
auditReport.setSuccess(true);
return new ApiResult<>(Constants.RESULT_OK_CODE, Constants.RESULT_OK_MSG, auditReport);
} catch (Exception e) {
log.error("保存审计报告到数据库失败", e);
return new ApiResult<>(1, "保存审计报告失败:" + e.getMessage());
}
}
@Override
public ApiResult<AuditReport> queryAuditReport(Integer projectId, Integer formCommit) {
log.info("查询审计报告 - 项目 ID: {}, formCommit: {}", projectId, formCommit);
try {
if (projectId == null || formCommit == null) {
return new ApiResult<>(1, "项目编号和表单提交标识不能为空");
}
// 根据projectId + formCommit 查询
LambdaQueryWrapper<AuditReport> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AuditReport::getProjectId, projectId)
.eq(AuditReport::getFormCommit, formCommit)
.eq(AuditReport::getDeleted, 0);
AuditReport auditReport = baseMapper.selectOne(queryWrapper);
if (auditReport != null) {
log.info("查询到审计报告 - ID: {}", auditReport.getId());
auditReport.setSuccess(true);
return new ApiResult<>(Constants.RESULT_OK_CODE, Constants.RESULT_OK_MSG, auditReport);
} else {
log.info("未查询到审计报告");
return new ApiResult<>(Constants.RESULT_OK_CODE, "未查询到记录", null);
}
} catch (Exception e) {
log.error("查询审计报告失败", e);
return new ApiResult<>(1, "查询审计报告失败:" + e.getMessage());
}
}
@Override
public Map<String, Object> queryAuditDataByProjectId(Integer projectId, List<Long> selectedEvidenceIds) {
// 调用新方法,不传入 selectedEvidenceIds兼容旧接口
return queryAuditDataByProjectIdWithAnalysis(projectId, selectedEvidenceIds);
}
/**
* 根据项目 ID 查询审计报告和取证单数据,并对勾选的取证单进行 AI 分析
* @param projectId 项目 ID
* @param selectedEvidenceIds 勾选的取证单 ID 列表(可为 null
* @return 包含 reports、evidences、evaluate、suggestion 的 Map
*/
@Override
public Map<String, Object> queryAuditDataByProjectIdWithAnalysis(Integer projectId, List<Long> selectedEvidenceIds) {
log.info("=== 开始查询审计数据 ===");
log.info("项目 ID: {}, 勾选取证单 IDs: {}", projectId, selectedEvidenceIds);
Map<String, Object> result = new HashMap<>();
try {
if (projectId == null) {
log.warn("项目 ID 为 null返回空结果");
result.put("reports", new ArrayList<>());
result.put("evidences", new ArrayList<>());
result.put("evaluate", "");
result.put("suggestion", "");
return result;
}
// 1. 查询审计报告数据
log.info("正在查询审计报告...");
LambdaQueryWrapper<AuditReport> reportWrapper = new LambdaQueryWrapper<>();
reportWrapper.eq(AuditReport::getProjectId, projectId)
.eq(AuditReport::getDeleted, 0)
.orderByAsc(AuditReport::getFormCommit);
List<AuditReport> reports = baseMapper.selectList(reportWrapper);
log.info("查询到 {} 条审计报告记录", reports.size());
// 2. 查询审计取证单数据 - 查询该项目下所有的取证单
log.info("正在查询审计取证单...");
LambdaQueryWrapper<AuditEvidence> evidenceWrapper = new LambdaQueryWrapper<>();
evidenceWrapper.eq(AuditEvidence::getProjectId, projectId)
.eq(AuditEvidence::getDeleted, 0)
.orderByAsc(AuditEvidence::getId);
List<AuditEvidence> evidences = auditEvidenceMapper.selectList(evidenceWrapper);
log.info("查询到 {} 条审计取证单记录", evidences.size());
result.put("reports", reports);
result.put("evidences", evidences);
// 3. 获取项目信息(用于获取项目名称)
PwlProject project = pwlProjectService.getById(projectId);
String projectName = project != null ? (project.getName() != null ? project.getName() : "") : "";
result.put("projectName", projectName); // 添加项目名称到返回结果
log.info("项目名称:{}", projectName);
// 生成审计引言
String auditIntro = generateAuditIntro(project);
result.put("auditIntro", auditIntro); // 添加审计引言到返回结果
// 生成其他事项说明
String otherMatters = generateOtherMatters(project);
result.put("otherMatters", otherMatters); // 添加其他事项说明到返回结果
// 4. 如果传入了勾选的取证单 ID进行字段叠加
if (selectedEvidenceIds != null && !selectedEvidenceIds.isEmpty()) {
log.info("开始对 {} 条勾选的取证单进行字段叠加", selectedEvidenceIds.size());
// 筛选出勾选的取证单
List<AuditEvidence> selectedEvidences = evidences.stream()
.filter(e -> selectedEvidenceIds.contains(e.getId()))
.collect(Collectors.toList());
log.info("实际筛选出 {} 条取证单进行叠加", selectedEvidences.size());
if (!selectedEvidences.isEmpty()) {
// 叠加字段内容
String evaluate = concatenateField(selectedEvidences, "auditFinding");
String suggestion = concatenateField(selectedEvidences, "suggestion");
result.put("evaluate", evaluate);
result.put("suggestion", suggestion);
log.info("字段叠加完成,评价字数:{},建议字数:{}",
evaluate.length(),
suggestion.length());
} else {
log.info("没有筛选到勾选的取证单,返回空的评价和建议");
result.put("evaluate", "");
result.put("suggestion", "");
}
} else {
// 没有勾选,返回空值
log.info("未传入勾选的取证单 ID返回空的评价和建议");
result.put("evaluate", "");
result.put("suggestion", "");
}
log.info("=== 审计数据查询完成 ===");
return result;
} catch (Exception e) {
log.error("根据项目 ID 查询审计数据失败,异常信息:{}", e.getMessage(), e);
result.put("reports", new ArrayList<>());
result.put("evidences", new ArrayList<>());
result.put("evaluate", "");
result.put("suggestion", "");
return result;
}
}
/**
* 生成审计引言段落
* @param project 项目信息
* @return 审计引言文本
*/
private String generateAuditIntro(PwlProject project) {
if (project == null) {
return "我们接受委托,自 20XX 年 XX 月 XX 日至 20XX 年 XX 月 XX 日,对 XX 同志自 20XX 年 XX 月 XX 日至 20XX 年 XX 月 XX 日担任公司董事长(具体职务)期间的经济责任履行情况进行了就地审计。公司及 XX 同志的责任是提供与本次审计相关的全部资料,并保证其真实、完整。我们的责任是在实施审计工作的基础上发表审计意见。审计工作为发表意见提供了合理基础。现将审计情况报告如下:";
}
String intro = "我们接受%s委托自 20XX 年 XX 月 XX 日至 20XX 年 XX 月 XX 日,对%s自 20XX 年 XX 月 XX 日至 20XX 年 XX 月 XX 日担任公司%s期间的经济责任履行情况进行了就地审计。公司及%s责任是提供与本次审计相关的全部资料并保证其真实、完整。我们的责任是在实施审计工作的基础上发表审计意见。审计工作为发表意见提供了合理基础。现将审计情况报告如下";
return String.format(intro, project.getName(), project.getPersonName(), project.getPosition(), project.getPersonName());
}
/**
* 生成其他事项说明内容
* @param project 项目信息
* @return 其他事项说明文本
*/
private String generateOtherMatters(PwlProject project) {
StringBuilder otherMatters = new StringBuilder();
// 第一段
otherMatters.append("(一)本报告依据委托方及");
if (project != null && StrUtil.isNotBlank(project.getName())) {
otherMatters.append(project.getName());
} else {
otherMatters.append("ABC 公司");
}
otherMatters.append("提供的审计相关资料出具,审计意见受所提供资料真实性及完整性的影响。");
// 第二段
otherMatters.append("(二)本报告仅供委托方 XX 单位了解 XX 同志经济责任履行情况之用,未经本所书面同意不得用作其他用途,因使用不当造成的后果,与执行本次专项审计业务的注册会计师及会计师事务所无关。");
return otherMatters.toString();
}
/**
* 叠加取证单字段内容
* @param selectedEvidences 勾选的取证单列表
* @param fieldName 字段名称auditFinding 或 suggestion
* @return 叠加后的字符串(包含标题和内容)
*/
private String concatenateField(List<AuditEvidence> selectedEvidences, String fieldName) {
log.info("开始叠加字段:{}", fieldName);
StringBuilder result = new StringBuilder();
for (int i = 0; i < selectedEvidences.size(); i++) {
AuditEvidence evidence = selectedEvidences.get(i);
String fieldValue = null;
if ("auditFinding".equals(fieldName)) {
fieldValue = evidence.getAuditFinding();
} else if ("suggestion".equals(fieldName)) {
fieldValue = evidence.getSuggestion();
}
log.info("取证单 ID={}, 序号={}, 字段={}, 值长度={}",
evidence.getId(), i+1, fieldName, fieldValue != null ? fieldValue.length() : 0);
if (StrUtil.isNotBlank(fieldValue)) {
// 添加序号和审计事项作为标题
String auditMatter = evidence.getAuditMatter();
String numText = getChineseNumber(i + 1);
if (StrUtil.isNotBlank(auditMatter)) {
result.append("(").append(numText).append(") ").append(auditMatter).append("\n");
} else {
result.append("(").append(numText).append(") 事项").append(i + 1).append("\n");
}
// 将内容按换行符分割,每段都首行缩进
String[] paragraphs = fieldValue.split("\\n+");
for (String paragraph : paragraphs) {
if (StrUtil.isNotBlank(paragraph.trim())) {
result.append(" ").append(paragraph.trim()).append("\n");
}
}
// 如果不是最后一条,添加空行分隔
if (i < selectedEvidences.size() - 1) {
result.append("\n");
}
} else {
log.warn("取证单 ID={}, 字段 {} 为空或空字符串", evidence.getId(), fieldName);
}
}
log.info("字段 {} 叠加完成,总长度={}", fieldName, result.length());
return result.toString();
}
/**
* 获取中文序号(一、二、三...十)
* @param number 数字1-10
* @return 中文序号字符串
*/
private String getChineseNumber(int number) {
String[] chineseNumbers = {"", "", "", "", "", "", "", "", "", "", ""};
if (number >= 1 && number <= 10) {
return chineseNumbers[number];
} else if (number > 10 && number <= 20) {
return "" + (number == 10 ? "" : chineseNumbers[number - 10]);
} else {
// 超过 20 的数字,直接返回阿拉伯数字
return String.valueOf(number);
}
}
}

View File

@@ -41,6 +41,12 @@ public class PwlProject implements Serializable {
@Schema(description = "项目标识")
private String code;
@Schema(description = "针对用户名称")
private String personName;
@Schema(description = "职务")
private String position;
@Schema(description = "案引号")
private String caseIndex;

View File

@@ -235,3 +235,9 @@ payment:
notify-url: "https://cms-api.websoft.top/api/shop/shop-order/notify"
# 生产环境是否启用环境感知
environment-aware: false
# AI 审计报告模板配置
ai:
template:
word-template-path: classpath:templates/audit_report_template.docx
evidence-template-path: classpath:templates/audit_evidence_template.docx