Initial commit

This commit is contained in:
cjh
2026-06-01 16:31:17 +08:00
commit c7fa70707f
1612 changed files with 181802 additions and 0 deletions

View File

@@ -0,0 +1,176 @@
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-modules</artifactId>
<version>${revision}</version>
</parent>
<artifactId>ai-intelligent-review</artifactId>
<properties>
<lib.dir>${project.basedir}/lib</lib.dir>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-core</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-core</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-annotation</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-tenant</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-web</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-mybatis</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-log</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-excel</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-idempotent</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-rabbitmq</artifactId>
<version>5.4.1</version>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-system</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.29</version>
</dependency>
<!-- 添加PDFBox工具依赖以支持渲染功能 -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox-tools</artifactId>
<version>2.0.29</version>
</dependency>
<!-- 添加pdf2dom依赖以支持PDF转HTML -->
<dependency>
<groupId>net.sf.cssbox</groupId>
<artifactId>pdf2dom</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>lyz-ai-resource-library</artifactId>
</dependency>
<!-- 添加Apache POI依赖以支持Word文件处理 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>5.2.4</version>
</dependency>
<!-- 添加XMLBeans依赖以支持Word文档结构处理 -->
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
<version>5.1.1</version>
</dependency>
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.doc.free</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>lyz-ai-chat-model</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>lyz-ai-resource-template-library</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>lyz-ai-file-interpretation</artifactId>
</dependency>
<!-- 使用本地 aspose-words JAR 包 -->
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-words</artifactId>
<version>20.12</version>
<scope>system</scope>
<systemPath>${lib.dir}/aspose-words-20.12-jdk17-cracked.jar</systemPath>
</dependency>
<!-- https://mvnrepository.com/artifact/org.openpnp/opencv -->
<!-- <dependency>-->
<!-- <groupId>org.openpnp</groupId>-->
<!-- <artifactId>opencv</artifactId>-->
<!-- <version>4.9.0-0</version>-->
<!-- </dependency>-->
<!-- 解决 Apache Commons IO 版本不兼容问题 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.16.1</version>
</dependency>
</dependencies>
<build>
<finalName>ai-intelligent-review</finalName>
</build>
<repositories>
<repository>
<id>com.e-iceblue</id>
<name>e-iceblue</name>
<url>https://repo.e-iceblue.cn/repository/maven-public/</url>
</repository>
<repository>
<id>AsposeJavaAPI</id>
<name>Aspose Java API</name>
<url>https://releases.aspose.com/java/repo/</url>
</repository>
</repositories>
</project>

View File

@@ -0,0 +1,249 @@
package org.dromara.review.Utils;
import com.aspose.words.*;
import org.dromara.common.core.utils.file.AsposeTempFileUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class AsposeUtils {
private static final Path ASPOSE_TEMP_DIR = AsposeTempFileUtils.ensureManagedTempDir("review-aspose");
public static Document loadDocument(String filePath) {
return analysisDocument(new File(filePath));
}
public static HtmlSaveOptions createHtmlSaveOptions() {
HtmlSaveOptions options = new HtmlSaveOptions(SaveFormat.HTML);
options.setTempFolder(ASPOSE_TEMP_DIR.toString());
options.setExportImagesAsBase64(true);
options.setScaleImageToShapeSize(true);
options.setCssStyleSheetType(CssStyleSheetType.EMBEDDED);
options.setExportFontResources(true);
options.setExportFontsAsBase64(true);
options.setExportXhtmlTransitional(true);
options.setPrettyFormat(false);
options.setExportOriginalUrlForLinkedImages(false);
options.setExportPageSetup(true);
options.setExportPageMargins(true);
options.setTableWidthOutputMode(HtmlElementSizeOutputMode.ALL);
options.setEncoding(StandardCharsets.UTF_8);
options.setExportHeadersFootersMode(ExportHeadersFootersMode.PER_SECTION);
options.setExportTocPageNumbers(true);
options.setExportTextBoxAsSvg(false);
// 将 Office 绘图/图元按位图导出,避免浏览器解释 VML/矢量定位时相对表格漂移。
options.setMetafileFormat(HtmlMetafileFormat.PNG);
return options;
}
/**
* 解析文档并应用自定义字体 (从 classpath/resources 读取)
*/
public static Document analysisDocument(File docFile) {
try {
if (!docFile.exists()) {
throw new RuntimeException("源文件不存在: " + docFile.getAbsolutePath());
}
// 1. 从运行目录及其上级目录查找 fonts开发/线上统一)
Path fontsDir = resolveFontsDir();
// 2. 配置字体设置(仅使用 fonts 目录,不使用系统字体)
FontSettings fontSettings = new FontSettings();
if (fontsDir != null) {
fontSettings.setFontsSources(new FontSourceBase[]{
new FolderFontSource(fontsDir.toString(), true)
});
System.out.println("【提示】使用自定义字体目录: " + fontsDir.toAbsolutePath());
} else {
fontSettings.setFontsSources(new FontSourceBase[]{});
System.err.println("【警告】未找到 fonts 目录已禁用系统字体。user.dir=" + System.getProperty("user.dir"));
}
// 3. 配置加载选项
LoadOptions loadOptions = new LoadOptions();
loadOptions.setFontSettings(fontSettings);
loadOptions.setTempFolder(ASPOSE_TEMP_DIR.toString());
// 4. 加载并优化文档
try (InputStream inputStream = new FileInputStream(docFile)) {
Document doc = new Document(inputStream, loadOptions);
// 核心:开启 Word 2019 兼容模式
doc.getCompatibilityOptions().optimizeFor(MsWordVersion.WORD_2019);
// 强制触发布局计算,确保页码引擎就绪
doc.updatePageLayout();
return doc;
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("文档解析异常: " + e.getMessage(), e);
} finally {
AsposeTempFileUtils.cleanupStaleAsposeTempFiles(AsposeUtils.class, ASPOSE_TEMP_DIR);
}
}
private static Path resolveFontsDir() {
Path jarDir = resolveJarDir();
Path userDir = Paths.get(System.getProperty("user.dir"));
Path[] candidates = new Path[]{
jarDir != null ? jarDir.resolve("fonts") : null,
userDir.resolve("fonts"),
userDir.getParent() != null ? userDir.getParent().resolve("fonts") : null
};
for (Path p : candidates) {
if (p != null && Files.isDirectory(p)) {
return p;
}
}
return null;
}
private static Path resolveJarDir() {
try {
CodeSource codeSource = AsposeUtils.class.getProtectionDomain().getCodeSource();
Path fromCodeSource = extractJarPath(codeSource != null ? codeSource.getLocation() : null);
if (fromCodeSource != null) {
return fromCodeSource.getParent();
}
Path fromCmd = extractJarPath(System.getProperty("sun.java.command"));
if (fromCmd != null) {
return fromCmd.getParent();
}
String classPath = System.getProperty("java.class.path");
if (classPath != null) {
String[] entries = classPath.split(File.pathSeparator);
for (String entry : entries) {
Path fromCp = extractJarPath(entry);
if (fromCp != null) {
return fromCp.getParent();
}
}
}
} catch (Exception e) {
return null;
}
return null;
}
private static Path extractJarPath(Object location) {
if (location == null) {
return null;
}
String s = String.valueOf(location).trim();
if (s.isEmpty()) {
return null;
}
int jarIdx = s.toLowerCase().indexOf(".jar");
if (jarIdx > 0) {
s = s.substring(0, jarIdx + 4);
}
int spaceIdx = s.indexOf(' ');
if (spaceIdx > 0) {
s = s.substring(0, spaceIdx);
}
if (s.startsWith("jar:")) {
s = s.substring(4);
}
int bangIdx = s.indexOf('!');
if (bangIdx >= 0) {
s = s.substring(0, bangIdx);
}
if (s.startsWith("file:")) {
s = s.substring(5);
}
s = URLDecoder.decode(s, StandardCharsets.UTF_8);
if (!s.toLowerCase().endsWith(".jar")) {
return null;
}
return Paths.get(s);
}
/**
* 提取文档每页的文本内容(包括表格)
* @return 页码到文本段落的映射
*/
public static Map<Integer, List<String>> extractPageTextMap(File docFile) {
Map<Integer, List<String>> pageTextMap = new HashMap<>();
try {
Document doc = analysisDocument(docFile);
// 确保页面布局已更新,以获得准确的页码
doc.updatePageLayout();
LayoutCollector collector = new LayoutCollector(doc);
// 遍历所有节
for (Section section : doc.getSections()) {
Body body = section.getBody();
// 遍历所有子节点
for (Object node1 : body.getChildNodes(NodeType.ANY, true)) {
Node node = (Node) node1;
// 检查段落是否在表格内,如果是则跳过(避免重复提取)
if (node.getNodeType() == NodeType.PARAGRAPH) {
// 检查段落是否在表格内
boolean isInsideTable = false;
Node current = node;
while (current != null && current.getNodeType() != NodeType.BODY) {
if (current.getNodeType() == NodeType.TABLE) {
isInsideTable = true;
break;
}
current = current.getParentNode();
}
if (isInsideTable) {
continue; // 跳过表格内的段落,因为已在表格处理中提取
}
// 处理段落
Paragraph para = (Paragraph) node;
String text = para.toString(SaveFormat.TEXT).trim();
if (!text.isEmpty()) {
int pageNum = collector.getStartPageIndex(para);
if (pageNum > 0) {
pageTextMap.computeIfAbsent(pageNum, k -> new ArrayList<>()).add(text);
}
}
} else if (node.getNodeType() == NodeType.TABLE) {
// 处理表格 - 按行获取文本
Table table = (Table) node;
for (Row row : table.getRows()) {
StringBuilder rowTextBuilder = new StringBuilder();
// 遍历行中的所有单元格,收集文本
for (Cell cell : row.getCells()) {
for (Paragraph para : cell.getParagraphs()) {
String cellText = para.toString(SaveFormat.TEXT).trim();
if (!cellText.isEmpty()) {
rowTextBuilder.append(cellText).append(" ");
}
}
}
String rowText = rowTextBuilder.toString().trim();
if (!rowText.isEmpty()) {
int pageNum = collector.getStartPageIndex(table);
if (pageNum > 0) {
pageTextMap.computeIfAbsent(pageNum, k -> new ArrayList<>()).add(rowText);
}
}
}
}
}
}
} catch (Exception e) {
throw new RuntimeException("aspose提取文本失败:"+e.getMessage());
} finally {
AsposeTempFileUtils.cleanupStaleAsposeTempFiles(AsposeUtils.class, ASPOSE_TEMP_DIR);
}
return pageTextMap;
}
}

View File

@@ -0,0 +1,149 @@
package org.dromara.review.Utils;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import org.dromara.review.domain.BiddingContentResultDtl;
import org.dromara.review.domain.bo.BiddingContentResultDtlBo;
import org.dromara.review.domain.bo.ConfigFieldNode;
import org.dromara.review.domain.bo.ConfigTreeNode;
import org.dromara.review.enums.ConfigFieldMapping;
import org.dromara.review.enums.ConfigFieldTypeMapping;
import org.dromara.review.enums.ContentTypeEnums;
import org.dromara.review.mapper.BiddingContentResultDtlMapper;
import org.dromara.review.mapper.BiddingContentResultMapper;
import org.dromara.review.mapper.BiddingContentRuleMapper;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
/**
* 配置树形结构构建工具
*/
@RequiredArgsConstructor
@Service
public class ConfigTreeBuilder {
// 使用AtomicInteger生成自增ID
private static final AtomicInteger idGenerator = new AtomicInteger(1);
// 缓存已生成的ID避免重复
private static final Map<String, Integer> fieldIdCache = new ConcurrentHashMap<>();
private final BiddingContentResultDtlMapper biddingContentResultDtlMapper;
private final BiddingContentResultMapper biddingContentResultMapper;
private final BiddingContentRuleMapper biddingContentRuleMapper;
/**
* 构建完整的配置树形结构
*/
public List<ConfigTreeNode> buildConfigTree(Long id,Integer fixed) {
// 获取按内容类型分组的字段映射
List<BiddingContentResultDtlBo> issueCountList = biddingContentResultDtlMapper.selectIssueCount(id,fixed);
Long infoId = biddingContentResultMapper.getInfoId(id);
String rule = biddingContentRuleMapper.selectRuleByInfoId(infoId);
JSONObject jsonObject = JSONObject.parseObject(rule);
// 将查询结果转换为HashMapkey为levelvalue为对应的issueCount
Map<String, Integer> issueCountMap = new HashMap<>();
for (BiddingContentResultDtlBo bo : issueCountList) {
if (bo != null && bo.getLevel() != null && bo.getIssueCount() != null) {
issueCountMap.put(bo.getLevel()+"-"+bo.getLevelName(), bo.getIssueCount());
}
}
Map<Integer, List<String>> fieldsByType = ConfigFieldTypeMapping.getFieldsByContentType();
List<ConfigTreeNode> treeNodes = new ArrayList<>();
// 遍历所有内容类型
for (ContentTypeEnums contentType : ContentTypeEnums.values()) {
Integer type = contentType.getType();
String levelName = contentType.getDesc();
String key = contentType.getKey();
// 获取该内容类型下的所有字段
List<String> fieldNames = fieldsByType.get(type);
if (fieldNames == null || fieldNames.isEmpty()) {
continue; // 如果没有字段,跳过该类型
}
List<ConfigFieldNode> fieldNodes = new ArrayList<>();
Integer totalFieldNum = 0; // 用于累加字段节点的num值
// 构建字段节点列表
for (String fieldName : fieldNames) {
// 获取字段的中文名称
ConfigFieldMapping fieldMapping = ConfigFieldMapping.getByEnglishName(fieldName);
String chineseName = fieldMapping != null ? fieldMapping.getChineseName() : fieldName;
// 生成字段ID
Integer fieldId = generateFieldId(fieldName,type);
// 设置num字段从issueCountMap中获取对应的issueCount
Integer num = issueCountMap.getOrDefault(type+"-"+fieldName, 0);
// 累加到总和中
totalFieldNum += num;
String levelJson = jsonObject.getString(key);
JSONObject jsonObject1 = JSONObject.parseObject(levelJson);
String correctReq ="";
boolean fieldFlag =false;
if(jsonObject1!=null){
correctReq = jsonObject1.getString(fieldName);
// 设置flag字段判断是否存在于issueCountList中
fieldFlag=jsonObject1.getBoolean("enable");
}
// 设置flag字段判断是否存在于issueCountList中
if (!issueCountMap.containsKey(type+"-"+fieldName)) {
fieldFlag =issueCountMap.containsKey(type+"-"+fieldName);
}
// 创建字段节点
ConfigFieldNode fieldNode = new ConfigFieldNode(chineseName, fieldName, fieldId, num, fieldFlag,correctReq);
fieldNodes.add(fieldNode);
}
// 设置父节点的flag字段只要有一个子节点的flag为true父节点的flag就为true
boolean parentFlag = fieldNodes.stream().anyMatch(node -> Boolean.TRUE.equals(node.getFlag()));
// 创建树节点
ConfigTreeNode treeNode = new ConfigTreeNode(type, levelName, fieldNodes, idGenerator.getAndIncrement(), totalFieldNum, parentFlag);
treeNodes.add(treeNode);
}
return treeNodes;
}
/**
* 生成字段ID简单实现可以根据需要修改
*/
private static Integer generateFieldId(String fieldName,Integer type) {
// 简单的哈希算法生成ID
return fieldIdCache.computeIfAbsent(type+"-"+fieldName, key -> idGenerator.getAndIncrement());
}
/**
* 根据字段名查找对应的树节点路径
*/
public static String findFieldPath(String fieldName) {
Integer contentTypeId = ConfigFieldTypeMapping.getContentTypeByField(fieldName);
if (contentTypeId == null) {
return "未知分类/" + fieldName;
}
ContentTypeEnums contentType = Arrays.stream(ContentTypeEnums.values())
.filter(e -> e.getType().equals(contentTypeId))
.findFirst()
.orElse(null);
ConfigFieldMapping fieldMapping = ConfigFieldMapping.getByEnglishName(fieldName);
String chineseName = fieldMapping != null ? fieldMapping.getChineseName() : fieldName;
return (contentType != null ? contentType.getDesc() : "未知分类") + "/" + chineseName;
}
}

View File

@@ -0,0 +1,268 @@
package org.dromara.review.Utils;
import com.aspose.words.*;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.file.AsposeTempFileUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.CodeSource;
/**
* DOC转DOCX工具类
* 使用Aspose.Words库将旧版Word文档(.doc)转换为新版Word文档(.docx)格式,以更好地保留文档格式
* Aspose.Words能够很好地保留文档的格式包括字体、表格、图片、页眉页脚等
*
* @author system
*/
@Slf4j
public class DocToDocxConverter {
private static final Path ASPOSE_TEMP_DIR = AsposeTempFileUtils.ensureManagedTempDir("review-doc-to-docx");
/**
* DOCX文件扩展名常量
*/
private static final String DOCX_EXTENSION = ".docx";
/**
* DOC文件扩展名常量
*/
private static final String DOC_EXTENSION = ".doc";
/**
* 私有构造函数,防止实例化
*/
private DocToDocxConverter() {
// 工具类不允许实例化
}
private static Path resolveFontsDir() {
Path jarDir = resolveJarDir();
Path userDir = Paths.get(System.getProperty("user.dir"));
Path[] candidates = new Path[]{
jarDir != null ? jarDir.resolve("fonts") : null,
userDir.resolve("fonts"),
userDir.getParent() != null ? userDir.getParent().resolve("fonts") : null
};
for (Path p : candidates) {
if (p != null && Files.isDirectory(p)) {
return p;
}
}
return null;
}
private static Path resolveJarDir() {
try {
CodeSource codeSource = DocToDocxConverter.class.getProtectionDomain().getCodeSource();
Path fromCodeSource = extractJarPath(codeSource != null ? codeSource.getLocation() : null);
if (fromCodeSource != null) {
return fromCodeSource.getParent();
}
Path fromCmd = extractJarPath(System.getProperty("sun.java.command"));
if (fromCmd != null) {
return fromCmd.getParent();
}
String classPath = System.getProperty("java.class.path");
if (classPath != null) {
String[] entries = classPath.split(File.pathSeparator);
for (String entry : entries) {
Path fromCp = extractJarPath(entry);
if (fromCp != null) {
return fromCp.getParent();
}
}
}
} catch (Exception e) {
return null;
}
return null;
}
private static Path extractJarPath(Object location) {
if (location == null) {
return null;
}
String s = String.valueOf(location).trim();
if (s.isEmpty()) {
return null;
}
int jarIdx = s.toLowerCase().indexOf(".jar");
if (jarIdx > 0) {
s = s.substring(0, jarIdx + 4);
}
int spaceIdx = s.indexOf(' ');
if (spaceIdx > 0) {
s = s.substring(0, spaceIdx);
}
if (s.startsWith("jar:")) {
s = s.substring(4);
}
int bangIdx = s.indexOf('!');
if (bangIdx >= 0) {
s = s.substring(0, bangIdx);
}
if (s.startsWith("file:")) {
s = s.substring(5);
}
s = URLDecoder.decode(s, StandardCharsets.UTF_8);
if (!s.toLowerCase().endsWith(".jar")) {
return null;
}
return Paths.get(s);
}
/**
* 将DOC格式的字节数组转换为DOCX格式的字节数组
* 使用Aspose.Words库进行转换能够很好地保留文档格式
*
* @param docBytes DOC文件的字节数组
* @return DOCX文件的字节数组
* @throws ServiceException 如果转换失败,抛出异常
*/
public static byte[] convertDocToDocx(byte[] docBytes) {
if (docBytes == null || docBytes.length == 0) {
throw new ServiceException("DOC文件内容为空无法转换");
}
try (ByteArrayInputStream docInputStream = new ByteArrayInputStream(docBytes);
ByteArrayOutputStream docxOutputStream = new ByteArrayOutputStream()) {
// 使用Aspose.Words加载DOC文档自动识别.doc和.docx格式
Path fontsDir = resolveFontsDir();
FontSettings fontSettings = new FontSettings();
if (fontsDir != null) {
fontSettings.setFontsFolder(fontsDir.toString(), true);
log.info("Aspose fonts dir: {}", fontsDir.toAbsolutePath());
} else {
log.warn("Fonts dir not found. Only custom fonts are allowed; user.dir={}", System.getProperty("user.dir"));
}
LoadOptions loadOptions = new LoadOptions();
loadOptions.setFontSettings(fontSettings);
loadOptions.setTempFolder(ASPOSE_TEMP_DIR.toString());
Document document = new Document(docInputStream, loadOptions);
document.getCompatibilityOptions().optimizeFor(MsWordVersion.WORD_2019);
document.updatePageLayout();
// 将文档保存为DOCX格式
// Aspose.Words会自动保留文档的格式包括
// - 字体样式和大小
// - 表格格式和边框
// - 图片和嵌入对象
// - 页眉页脚
// - 页边距和页面设置
document.save(docxOutputStream, SaveFormat.DOCX);
// 获取转换后的DOCX字节数组
byte[] docxBytes = docxOutputStream.toByteArray();
log.info("DOC转DOCX转换成功原始大小: {} bytes, 转换后大小: {} bytes", docBytes.length, docxBytes.length);
return docxBytes;
} catch (Exception e) {
log.error("DOC转DOCX转换失败: {}", e.getMessage(), e);
throw new ServiceException("DOC转DOCX转换失败: " + e.getMessage());
} finally {
AsposeTempFileUtils.cleanupStaleAsposeTempFiles(DocToDocxConverter.class, ASPOSE_TEMP_DIR);
}
}
/**
* 将本地 DOC 文件转换为 DOCX 文件,避免在大文件场景下复制多份字节数组。
*
* @param docPath 原始 DOC 文件路径
* @param docxPath 输出 DOCX 文件路径
*/
public static void convertDocToDocx(Path docPath, Path docxPath) {
if (docPath == null || docxPath == null) {
throw new ServiceException("DOC转换路径不能为空");
}
if (!Files.isRegularFile(docPath)) {
throw new ServiceException("DOC源文件不存在无法转换");
}
try {
Path parent = docxPath.getParent();
if (parent != null) {
Files.createDirectories(parent);
}
Path fontsDir = resolveFontsDir();
FontSettings fontSettings = new FontSettings();
if (fontsDir != null) {
fontSettings.setFontsFolder(fontsDir.toString(), true);
log.info("Aspose fonts dir: {}", fontsDir.toAbsolutePath());
} else {
log.warn("Fonts dir not found. Only custom fonts are allowed; user.dir={}", System.getProperty("user.dir"));
}
LoadOptions loadOptions = new LoadOptions();
loadOptions.setFontSettings(fontSettings);
loadOptions.setTempFolder(ASPOSE_TEMP_DIR.toString());
Document document = new Document(docPath.toString(), loadOptions);
document.getCompatibilityOptions().optimizeFor(MsWordVersion.WORD_2019);
document.updatePageLayout();
document.save(docxPath.toString(), SaveFormat.DOCX);
long sourceSize = Files.size(docPath);
long targetSize = Files.size(docxPath);
log.info("DOC转DOCX转换成功原始大小: {} bytes, 转换后大小: {} bytes", sourceSize, targetSize);
} catch (IOException e) {
throw new ServiceException("DOC转DOCX转换失败: " + e.getMessage());
} catch (Exception e) {
log.error("DOC转DOCX转换失败: {}", e.getMessage(), e);
throw new ServiceException("DOC转DOCX转换失败: " + e.getMessage());
} finally {
AsposeTempFileUtils.cleanupStaleAsposeTempFiles(DocToDocxConverter.class, ASPOSE_TEMP_DIR);
}
}
/**
* 检查文件扩展名是否为.doc不区分大小写
*
* @param fileName 文件名
* @return 如果是.doc文件返回true否则返回false
*/
public static boolean isDocFile(String fileName) {
if (fileName == null || fileName.isEmpty()) {
return false;
}
// 获取文件扩展名(不区分大小写)
String lowerFileName = fileName.toLowerCase();
return lowerFileName.endsWith(DOC_EXTENSION) && !lowerFileName.endsWith(DOCX_EXTENSION);
}
/**
* 将文件名从.doc扩展名改为.docx扩展名
*
* @param fileName 原始文件名
* @return 转换后的文件名(.docx
*/
public static String changeExtensionToDocx(String fileName) {
if (fileName == null || fileName.isEmpty()) {
return fileName;
}
// 如果文件名以.doc结尾但不是.docx则替换为.docx
String lowerFileName = fileName.toLowerCase();
if (lowerFileName.endsWith(DOC_EXTENSION) && !lowerFileName.endsWith(DOCX_EXTENSION)) {
// 保留原始大小写,只替换扩展名
int lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex > 0) {
return fileName.substring(0, lastDotIndex) + DOCX_EXTENSION;
}
}
return fileName;
}
}

View File

@@ -0,0 +1,287 @@
package org.dromara.review.Utils;
import com.alibaba.fastjson.JSONObject;
import com.aspose.words.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.file.AsposeTempFileUtils;
import org.dromara.review.domain.BiddingProposalDtl;
import org.dromara.review.domain.vo.BiddingAnalysisRuleConfigVo;
import org.dromara.review.mapper.BiddingAnalysisRuleConfigMapper;
import org.dromara.review.mapper.BiddingDocumentRecordMapper;
import org.dromara.review.mapper.BiddingProposalDtlMapper;
import org.dromara.review.service.IBiddingDocumentRecordService;
import org.dromara.review.service.impl.BiddingProposalServiceImpl;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.service.ISysOssService;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.FileInputStream;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.CodeSource;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@Service
@RequiredArgsConstructor
@Slf4j
public class UpdateUtils {
private static final Path ASPOSE_TEMP_DIR = AsposeTempFileUtils.ensureManagedTempDir("review-update-utils");
private final BiddingProposalDtlMapper biddingProposalDtlMapper;
private final BiddingDocumentRecordMapper biddingDocumentRecordMapper;
private final IBiddingDocumentRecordService biddingDocumentRecordService;
private final ISysOssService ossService;
private final BiddingAnalysisRuleConfigMapper biddingAnalysisRuleConfigMapper;
public void updateDto(Long dtlId,Long ossId){
//更新投标文件的ossId和pdfId
BiddingProposalDtl biddingProposalDtl = biddingProposalDtlMapper.selectById(dtlId);
biddingProposalDtl.setOssId(ossId);
biddingProposalDtlMapper.updateById(biddingProposalDtl);
// CompletableFuture.runAsync(() -> {
// convertWordToPdfAsync(biddingProposalDtl);
// });
//更新文档记录表的记录
biddingDocumentRecordService.findDocRecordAndUpdateOss(dtlId,ossId);
}
public Integer getCheckRule(Long infoId){
BiddingAnalysisRuleConfigVo ruleByInfoId = biddingAnalysisRuleConfigMapper.getRuleByInfoId(infoId);
String content = ruleByInfoId.getContent();
if(StringUtils.isNotBlank(content)){
JSONObject jsonObject = JSONObject.parseObject(content);
JSONObject jsonObject1 = jsonObject.getJSONObject("ruleJson");
Integer checkDuplicateContent = jsonObject1.getInteger("checkDuplicateContent");
if(checkDuplicateContent!=null){
return checkDuplicateContent;
}
}
return 100;
}
/**
* 异步将Word文档转换为PDF并上传到OSS然后更新数据库
* 此方法通过TaskExecutor在线程池中执行不需要@Async注解
*
*/
private void convertWordToPdfAsync(BiddingProposalDtl dtl) {
try {
Long dtlId = dtl.getId();
Long ossId = dtl.getOssId();
log.info("开始异步转换Word为PDF, dtlId: {}, wordOssId: {}", dtlId, ossId);
// 调用转换方法
Long pdfOssId = convertWordToPdfAndUpload(ossId);
dtl.setPdfOssId(pdfOssId);
biddingProposalDtlMapper.updateById(dtl);
biddingDocumentRecordService.findDocRecordAndUpdatePdf(dtlId,pdfOssId);
log.info("异步转换Word为PDF成功, dtlId: {}, pdfOssId: {}", dtlId, pdfOssId);
} catch (Exception e) {
log.error("异步转换Word为PDF失败:"+e.getMessage());
}
}
/**
* 将Word文档转换为PDF并上传到OSS
*
* @param wordOssId Word文档的OSS ID
* @return PDF文档的OSS ID
* @throws Exception 转换或上传失败时抛出异常
*/
private Long convertWordToPdfAndUpload(Long wordOssId) throws Exception {
File wordFile = null;
File pdfFile = null;
try {
// 1. 下载Word文件到本地
// 构建自定义文件名
SysOssVo sysOss = ossService.getById(wordOssId);
String originalFileName = sysOss.getOriginalName();
// 提取文件名部分,去掉可能包含的目录路径
int lastSeparatorIndex = originalFileName.lastIndexOf('/');
if (lastSeparatorIndex == -1) {
lastSeparatorIndex = originalFileName.lastIndexOf('\\');
}
if (lastSeparatorIndex != -1) {
originalFileName = originalFileName.substring(lastSeparatorIndex + 1);
}
String customFileName = UUID.randomUUID() + "_" + originalFileName;
String wordFilePath = ossService.downloadToLocal(wordOssId,customFileName);
wordFile = new File(wordFilePath);
if (!wordFile.exists()) {
throw new RuntimeException("Word文件下载失败: " + wordFilePath);
}
log.info("Word文件下载成功: {}", wordFilePath);
System.out.println("文件下载成功"+wordFilePath);
System.out.println("开始转换");
// 2. 使用Aspose.Words转换为PDF
// registerWord2412();
Path fontsDir = resolveFontsDir();
log.info("Resolved fonts dir (before load): {}", fontsDir != null ? fontsDir.toAbsolutePath() : "null");
FontSettings fontSettings = new FontSettings();
if (fontsDir != null) {
fontSettings.setFontsFolder(fontsDir.toString(), true);
log.info("Aspose fonts dir: {}", fontsDir.toAbsolutePath());
} else {
log.warn("Fonts dir not found. Only custom fonts are allowed; user.dir={}", System.getProperty("user.dir"));
}
LoadOptions loadOptions = new LoadOptions();
loadOptions.setFontSettings(fontSettings);
loadOptions.setTempFolder(ASPOSE_TEMP_DIR.toString());
Document doc = new Document(wordFilePath, loadOptions);
doc.getCompatibilityOptions().optimizeFor(MsWordVersion.WORD_2019);
doc.updatePageLayout();
System.out.println("开始转换结束"+doc);
// 生成PDF文件路径与Word文件在同一目录
String pdfFilePath = wordFilePath.replace(".docx", ".pdf").replace(".doc", ".pdf");
pdfFile = new File(pdfFilePath);
// 保存为PDF格式
PdfSaveOptions pdfOptions = new PdfSaveOptions();
pdfOptions.setEmbedFullFonts(true);
pdfOptions.setTempFolder(ASPOSE_TEMP_DIR.toString());
doc.save(pdfFilePath, pdfOptions);
log.info("Word转PDF成功: {}", pdfFilePath);
System.out.println("Word转PDF成功"+pdfFilePath);
// 3. 上传PDF到OSS使用原文件名对应的PDF名称
String uploadPdfName = originalFileName;
int lastDotIndex = uploadPdfName.lastIndexOf('.');
if (lastDotIndex > 0) {
uploadPdfName = uploadPdfName.substring(0, lastDotIndex) + ".pdf";
} else {
uploadPdfName = uploadPdfName + ".pdf";
}
SysOssVo pdfOssVo = null;
int i =1;
while (pdfOssVo==null && i<=10){
try (FileInputStream fis = new FileInputStream(pdfFile)) {
pdfOssVo = ossService.upload(fis, uploadPdfName);
}
i++;
// 如果上传失败且还有重试次数等待30秒后重试
if (pdfOssVo == null && i <= 10) {
try {
log.info("上传失败等待30秒后第{}次重试", i);
Thread.sleep(30000); // 30秒延迟
} catch (InterruptedException e) {
log.error("重试延迟被中断: {}", e.getMessage());
Thread.currentThread().interrupt();
}
}
}
System.out.println("pdf的ossId为:"+pdfOssVo.getOssId());
log.info("PDF上传成功, ossId: {}", pdfOssVo.getOssId());
System.out.println("PDF上传成功"+pdfOssVo.getOssId());
return pdfOssVo.getOssId();
} finally {
// 4. 清理临时文件
if (wordFile != null && wordFile.exists()) {
boolean deleted = wordFile.delete();
log.info("清理Word临时文件: {}, 结果: {}", wordFile.getAbsolutePath(), deleted);
}
if (pdfFile != null && pdfFile.exists()) {
boolean deleted = pdfFile.delete();
log.info("清理PDF临时文件: {}, 结果: {}", pdfFile.getAbsolutePath(), deleted);
}
AsposeTempFileUtils.cleanupStaleAsposeTempFiles(UpdateUtils.class, ASPOSE_TEMP_DIR);
}
}
private Path resolveFontsDir() {
Path jarDir = resolveJarDir();
Path userDir = Paths.get(System.getProperty("user.dir"));
Path[] candidates = new Path[]{
jarDir != null ? jarDir.resolve("fonts") : null,
userDir.resolve("fonts"),
userDir.getParent() != null ? userDir.getParent().resolve("fonts") : null
};
for (Path p : candidates) {
if (p != null && Files.isDirectory(p)) {
return p;
}
}
return null;
}
private Path resolveJarDir() {
try {
CodeSource codeSource = UpdateUtils.class.getProtectionDomain().getCodeSource();
Path fromCodeSource = extractJarPath(codeSource != null ? codeSource.getLocation() : null);
if (fromCodeSource != null) {
return fromCodeSource.getParent();
}
Path fromCmd = extractJarPath(System.getProperty("sun.java.command"));
if (fromCmd != null) {
return fromCmd.getParent();
}
String classPath = System.getProperty("java.class.path");
if (classPath != null) {
String[] entries = classPath.split(File.pathSeparator);
for (String entry : entries) {
Path fromCp = extractJarPath(entry);
if (fromCp != null) {
return fromCp.getParent();
}
}
}
} catch (Exception e) {
return null;
}
return null;
}
private Path extractJarPath(Object location) {
if (location == null) {
return null;
}
String s = String.valueOf(location).trim();
if (s.isEmpty()) {
return null;
}
int jarIdx = s.toLowerCase().indexOf(".jar");
if (jarIdx > 0) {
s = s.substring(0, jarIdx + 4);
}
int spaceIdx = s.indexOf(' ');
if (spaceIdx > 0) {
s = s.substring(0, spaceIdx);
}
if (s.startsWith("jar:")) {
s = s.substring(4);
}
int bangIdx = s.indexOf('!');
if (bangIdx >= 0) {
s = s.substring(0, bangIdx);
}
if (s.startsWith("file:")) {
s = s.substring(5);
}
s = URLDecoder.decode(s, StandardCharsets.UTF_8);
if (!s.toLowerCase().endsWith(".jar")) {
return null;
}
return Paths.get(s);
}
}

View File

@@ -0,0 +1,16 @@
package org.dromara.review.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
/**
* 异步任务配置
*
* @author Claude
* @date 2026-03-05
*/
@EnableAsync
@Configuration
public class AsyncConfig {
// 启用@Async注解支持
}

View File

@@ -0,0 +1,52 @@
package org.dromara.review.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@Configuration
public class ReviewDocConvertExecutorConfig {
@Value("${review.doc-convert.executor-threads:2}")
private int executorThreads;
@Value("${review.doc-convert.executor-queue-capacity:20}")
private int queueCapacity;
@Bean(name = "reviewDocConvertExecutor", destroyMethod = "shutdown")
public ExecutorService reviewDocConvertExecutor() {
int threads = Math.max(1, executorThreads);
int queueSize = Math.max(1, queueCapacity);
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(queueSize);
ThreadPoolExecutor executor = new ThreadPoolExecutor(
threads,
threads,
60L,
TimeUnit.SECONDS,
queue,
namedThreadFactory("review-doc-convert-"),
new ThreadPoolExecutor.AbortPolicy()
);
executor.allowCoreThreadTimeOut(true);
return executor;
}
private ThreadFactory namedThreadFactory(String prefix) {
AtomicInteger counter = new AtomicInteger(1);
return runnable -> {
Thread thread = new Thread(runnable);
thread.setName(prefix + counter.getAndIncrement());
thread.setDaemon(false);
return thread;
};
}
}

View File

@@ -0,0 +1,26 @@
package org.dromara.review.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "review.rewrite")
public class ReviewRewriteConcurrencyProperties {
/**
* 改写共享线程池线程数。
*/
private int executorThreads = 10;
/**
* 改写共享线程池队列容量。
*/
private int executorQueueCapacity = 300;
/**
* 单次批量改写的最大并发批次数。
*/
private int batchParallelism = 10;
}

View File

@@ -0,0 +1,29 @@
package org.dromara.review.config;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import java.util.concurrent.ExecutorService;
@Component
public class ReviewRewriteConcurrencySupport {
private final ReviewRewriteConcurrencyProperties properties;
private final ExecutorService rewriteExecutor;
public ReviewRewriteConcurrencySupport(ReviewRewriteConcurrencyProperties properties,
@Qualifier("reviewRewriteExecutor") ExecutorService rewriteExecutor) {
this.properties = properties;
this.rewriteExecutor = rewriteExecutor;
}
public ExecutorService rewriteExecutor() {
return rewriteExecutor;
}
public int batchParallelism(int taskCount) {
int safeTaskCount = Math.max(1, taskCount);
int safeConfigured = Math.max(1, properties.getBatchParallelism());
return Math.min(safeTaskCount, safeConfigured);
}
}

View File

@@ -0,0 +1,52 @@
package org.dromara.review.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@Configuration
public class ReviewRewriteExecutorConfig {
@Value("${review.rewrite.executor-threads:10}")
private int executorThreads;
@Value("${review.rewrite.executor-queue-capacity:300}")
private int queueCapacity;
@Bean(name = "reviewRewriteExecutor", destroyMethod = "shutdown")
public ExecutorService reviewRewriteExecutor() {
int threads = Math.max(1, executorThreads);
int queueSize = Math.max(100, queueCapacity);
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(queueSize);
ThreadPoolExecutor executor = new ThreadPoolExecutor(
threads,
threads,
60L,
TimeUnit.SECONDS,
queue,
namedThreadFactory("review-rewrite-"),
new ThreadPoolExecutor.CallerRunsPolicy()
);
executor.allowCoreThreadTimeOut(true);
return executor;
}
private ThreadFactory namedThreadFactory(String prefix) {
AtomicInteger counter = new AtomicInteger(1);
return runnable -> {
Thread thread = new Thread(runnable);
thread.setName(prefix + counter.getAndIncrement());
thread.setDaemon(false);
return thread;
};
}
}

View File

@@ -0,0 +1,105 @@
package org.dromara.review.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.review.domain.vo.BiddingAnalysisCommonRuleConfigVo;
import org.dromara.review.domain.bo.BiddingAnalysisCommonRuleConfigBo;
import org.dromara.review.service.IBiddingAnalysisCommonRuleConfigService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
* 技术标规律性分析通用规则设置
*
* @author Lion Li
* @date 2025-11-12
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/analysisCommonRuleConfig")
public class BiddingAnalysisCommonRuleConfigController extends BaseController {
private final IBiddingAnalysisCommonRuleConfigService biddingAnalysisCommonRuleConfigService;
/**
* 查询技术标规律性分析通用规则设置列表
*/
@SaCheckPermission("review:analysisCommonRuleConfig:list")
@GetMapping("/list")
public TableDataInfo<BiddingAnalysisCommonRuleConfigVo> list(BiddingAnalysisCommonRuleConfigBo bo, PageQuery pageQuery) {
return biddingAnalysisCommonRuleConfigService.queryPageList(bo, pageQuery);
}
/**
* 导出技术标规律性分析通用规则设置列表
*/
@SaCheckPermission("review:analysisCommonRuleConfig:export")
@Log(title = "技术标规律性分析通用规则设置", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(BiddingAnalysisCommonRuleConfigBo bo, HttpServletResponse response) {
List<BiddingAnalysisCommonRuleConfigVo> list = biddingAnalysisCommonRuleConfigService.queryList(bo);
ExcelUtil.exportExcel(list, "技术标规律性分析通用规则设置", BiddingAnalysisCommonRuleConfigVo.class, response);
}
/**
* 获取技术标规律性分析通用规则设置详细信息
*
* @param id 主键
*/
@SaCheckPermission("review:analysisCommonRuleConfig:query")
@GetMapping("/{id}")
public R<BiddingAnalysisCommonRuleConfigVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(biddingAnalysisCommonRuleConfigService.queryById(id));
}
/**
* 新增技术标规律性分析通用规则设置
*/
@SaCheckPermission("review:analysisCommonRuleConfig:add")
@Log(title = "技术标规律性分析通用规则设置", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody BiddingAnalysisCommonRuleConfigBo bo) {
return toAjax(biddingAnalysisCommonRuleConfigService.insertByBo(bo));
}
/**
* 修改技术标规律性分析通用规则设置
*/
@SaCheckPermission("review:analysisCommonRuleConfig:edit")
@Log(title = "技术标规律性分析通用规则设置", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody BiddingAnalysisCommonRuleConfigBo bo) {
return toAjax(biddingAnalysisCommonRuleConfigService.updateByBo(bo));
}
/**
* 删除技术标规律性分析通用规则设置
*
* @param ids 主键串
*/
@SaCheckPermission("review:analysisCommonRuleConfig:remove")
@Log(title = "技术标规律性分析通用规则设置", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(biddingAnalysisCommonRuleConfigService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@@ -0,0 +1,174 @@
package org.dromara.review.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.common.rabbitmq.constant.RabbitMQConstants;
import org.dromara.common.rabbitmq.service.RabbitMQService;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.review.domain.bo.BiddingAnalysisResultRewrite;
import org.dromara.review.enums.TaskTypeEnums;
import org.dromara.review.rabbitMq.RabbitMQMessage;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.review.domain.vo.BiddingAnalysisResultVo;
import org.dromara.review.domain.bo.BiddingAnalysisResultBo;
import org.dromara.review.service.IBiddingAnalysisResultService;
import org.dromara.review.service.IBiddingReviewTaskService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
/**
* 技术标纪律性分析结果
*
* @author Lion Li
* @date 2025-11-12
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/analysisResult")
public class BiddingAnalysisResultController extends BaseController {
private final IBiddingAnalysisResultService biddingAnalysisResultService;
private final RabbitMQService rabbitMQService;
private final IBiddingReviewTaskService biddingReviewTaskService;
@Value("${ai-check.base-url:http://127.0.0.1:8079}")
private String aiCheckBaseUrl;
/**
* 查询技术标纪律性分析结果列表
*/
@SaCheckPermission("review:analysisResult:list")
@GetMapping("/list")
public TableDataInfo<BiddingAnalysisResultVo> list(BiddingAnalysisResultBo bo, PageQuery pageQuery) {
return biddingAnalysisResultService.queryPageList(bo, pageQuery);
}
/**
* 导出技术标纪律性分析结果列表
*/
@SaCheckPermission("review:analysisResult:export")
@Log(title = "技术标纪律性分析结果", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(BiddingAnalysisResultBo bo, HttpServletResponse response) {
List<BiddingAnalysisResultVo> list = biddingAnalysisResultService.queryList(bo);
ExcelUtil.exportExcel(list, "技术标纪律性分析结果", BiddingAnalysisResultVo.class, response);
}
/**
* 获取技术标纪律性分析结果详细信息
*
* @param id 主键
*/
@SaCheckPermission("review:analysisResult:query")
@GetMapping("/{id}")
public R<BiddingAnalysisResultVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(biddingAnalysisResultService.queryById(id));
}
/**
* 新增技术标纪律性分析结果
*/
@SaCheckPermission("review:analysisResult:add")
@Log(title = "技术标纪律性分析结果", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody BiddingAnalysisResultBo bo) {
return toAjax(biddingAnalysisResultService.insertByBo(bo));
}
/**
* 修改技术标纪律性分析结果
*/
@SaCheckPermission("review:analysisResult:edit")
@Log(title = "技术标纪律性分析结果", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody BiddingAnalysisResultBo bo) {
return toAjax(biddingAnalysisResultService.updateByBo(bo));
}
/**
* 删除技术标纪律性分析结果
*
* @param ids 主键串
*/
@SaCheckPermission("review:analysisResult:remove")
@Log(title = "技术标纪律性分析结果", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(biddingAnalysisResultService.deleteWithValidByIds(List.of(ids), true));
}
@PostMapping("/getAnalysisResult")
@SaCheckPermission("review:analysisResult:getAnalysisResult")
@Log(title = "技术标纪律性分析结果详情")
public R<List<BiddingAnalysisResultVo>> getAnalysisResult(@RequestBody BiddingAnalysisResultBo bo) {
return R.ok(biddingAnalysisResultService.getAnalysisResult(bo));
}
@Log(title = "开始纪律性分析检查")
@RepeatSubmit()
@PostMapping("/startAnalysisLocal")
@SaCheckPermission("review:analysisResult:startAnalysisLocal")
public R<Long> startAnalysisLocal(@RequestBody BiddingAnalysisResultBo bo) {
if (bo == null || bo.getInfoId() == null) {
return R.fail("参数错误: infoId不能为空");
}
boolean hasSingleDtlPair = bo.getProposalDtlId1() != null && bo.getProposalDtlId2() != null;
boolean hasDtlGroups = bo.getProposalDtlIds1() != null && !bo.getProposalDtlIds1().isEmpty()
&& bo.getProposalDtlIds2() != null && !bo.getProposalDtlIds2().isEmpty();
boolean hasProposalPair = bo.getProposalIds() != null && bo.getProposalIds().size() == 2;
if (!hasSingleDtlPair && !hasDtlGroups && !hasProposalPair) {
return R.fail("参数错误: 必须传入两个投标文件明细ID或传入左右两组待比对的投标文件明细ID");
}
if (biddingReviewTaskService.existsInProgressTask(TaskTypeEnums.ANALYSIS.getTaskType(),bo.getInfoId().intValue())) {
return R.fail("当前项目正在审查,不允许重复发起");
}
return R.ok(biddingAnalysisResultService.startAnalysisLocal(bo));
}
@Log(title = "重写文本内容")
@RepeatSubmit()
@PostMapping("/rewrite")
@SaCheckPermission("review:analysisResult:rewrite")
public R<String> rewrite(@RequestBody BiddingAnalysisResultRewrite bo) {
return R.ok(biddingAnalysisResultService.rewrite(bo));
}
@Log(title = "一键改写文本内容")
@RepeatSubmit()
@PostMapping("/rewriteQ")
@SaCheckPermission("review:analysisResult:rewrite")
public R<List<Long>> rewriteQ(@RequestBody BiddingAnalysisResultVo bo) {
return R.ok(biddingAnalysisResultService.rewriteQ(bo));
}
}

View File

@@ -0,0 +1,117 @@
package org.dromara.review.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.review.domain.vo.BiddingAnalysisRuleConfigVo;
import org.dromara.review.domain.bo.BiddingAnalysisRuleConfigBo;
import org.dromara.review.service.IBiddingAnalysisRuleConfigService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
* 技术标规律性分析规则设置
*
* @author Lion Li
* @date 2025-11-12
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/analysisRuleConfig")
public class BiddingAnalysisRuleConfigController extends BaseController {
private final IBiddingAnalysisRuleConfigService biddingAnalysisRuleConfigService;
/**
* 查询技术标规律性分析规则设置列表
*/
@SaCheckPermission("review:analysisRuleConfig:list")
@GetMapping("/list")
public TableDataInfo<BiddingAnalysisRuleConfigVo> list(BiddingAnalysisRuleConfigBo bo, PageQuery pageQuery) {
return biddingAnalysisRuleConfigService.queryPageList(bo, pageQuery);
}
/**
* 导出技术标规律性分析规则设置列表
*/
@SaCheckPermission("review:analysisRuleConfig:export")
@Log(title = "技术标规律性分析规则设置", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(BiddingAnalysisRuleConfigBo bo, HttpServletResponse response) {
List<BiddingAnalysisRuleConfigVo> list = biddingAnalysisRuleConfigService.queryList(bo);
ExcelUtil.exportExcel(list, "技术标规律性分析规则设置", BiddingAnalysisRuleConfigVo.class, response);
}
/**
* 获取技术标规律性分析规则设置详细信息
*
* @param id 主键
*/
@SaCheckPermission("review:analysisRuleConfig:query")
@GetMapping("/{id}")
public R<BiddingAnalysisRuleConfigVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(biddingAnalysisRuleConfigService.queryById(id));
}
/**
* 新增技术标规律性分析规则设置
*/
@SaCheckPermission("review:analysisRuleConfig:add")
@Log(title = "技术标规律性分析规则设置", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody BiddingAnalysisRuleConfigBo bo) {
return toAjax(biddingAnalysisRuleConfigService.insertByBo(bo));
}
/**
* 修改技术标规律性分析规则设置
*/
@SaCheckPermission("review:analysisRuleConfig:edit")
@Log(title = "技术标规律性分析规则设置", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody BiddingAnalysisRuleConfigBo bo) {
return toAjax(biddingAnalysisRuleConfigService.updateByBo(bo));
}
/**
* 删除技术标规律性分析规则设置
*
* @param ids 主键串
*/
@SaCheckPermission("review:analysisRuleConfig:remove")
@Log(title = "技术标规律性分析规则设置", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(biddingAnalysisRuleConfigService.deleteWithValidByIds(List.of(ids), true));
}
/**
* 根据infoId查询单个规则
*/
@Log(title = " 根据infoId查询单个规则", businessType = BusinessType.DELETE)
@GetMapping("/getRuleByInfoId/{infoId}")
@SaCheckPermission("review:analysisRuleConfig:getRuleByInfoId")
public R<BiddingAnalysisRuleConfigVo> getRuleByInfoId(
@PathVariable Long infoId) {
return R.ok(biddingAnalysisRuleConfigService.getRuleByInfoId(infoId));
}
}

View File

@@ -0,0 +1,154 @@
package org.dromara.review.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.review.domain.vo.BiddingContentCommonRuleVo;
import org.dromara.review.domain.bo.BiddingContentCommonRuleBo;
import org.dromara.review.service.IBiddingContentCommonRuleService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
* 技术内容样式检查通用规则设置
*
* @author Lion Li
* @date 2025-11-12
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/contentCommonRule")
public class BiddingContentCommonRuleController extends BaseController {
private final IBiddingContentCommonRuleService biddingContentCommonRuleService;
/**
* 查询技术内容样式检查通用规则设置列表
*/
@SaCheckPermission("review:contentCommonRule:list")
@GetMapping("/list")
public TableDataInfo<BiddingContentCommonRuleVo> list(BiddingContentCommonRuleBo bo, PageQuery pageQuery) {
return biddingContentCommonRuleService.queryPageList(bo, pageQuery);
}
/**
* 导出技术内容样式检查通用规则设置列表
*/
@SaCheckPermission("review:contentCommonRule:export")
@Log(title = "技术内容样式检查通用规则设置", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(BiddingContentCommonRuleBo bo, HttpServletResponse response) {
List<BiddingContentCommonRuleVo> list = biddingContentCommonRuleService.queryList(bo);
ExcelUtil.exportExcel(list, "技术内容样式检查通用规则设置", BiddingContentCommonRuleVo.class, response);
}
/**
* 获取技术内容样式检查通用规则设置详细信息
*
* @param id 主键
*/
@SaCheckPermission("review:contentCommonRule:query")
@GetMapping("/{id}")
public R<BiddingContentCommonRuleVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(biddingContentCommonRuleService.queryById(id));
}
/**
* 新增技术内容样式检查通用规则设置
*/
@SaCheckPermission("review:contentCommonRule:add")
@Log(title = "技术内容样式检查通用规则设置", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Long> add(@RequestBody BiddingContentCommonRuleBo bo) {
return R.ok(biddingContentCommonRuleService.insertByBo(bo));
}
/**
* 修改技术内容样式检查通用规则设置
*/
@SaCheckPermission("review:contentCommonRule:edit")
@Log(title = "技术内容样式检查通用规则设置", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody BiddingContentCommonRuleBo bo) {
return toAjax(biddingContentCommonRuleService.updateByBo(bo));
}
/**
* 修改通用规则模板锁定状态
*/
@SaCheckPermission("review:contentCommonRule:edit")
@Log(title = "技术内容样式检查通用规则设置", businessType = BusinessType.UPDATE)
@PutMapping("/changeLockStatus")
public R<Void> changeLockStatus(@RequestBody BiddingContentCommonRuleBo bo) {
return toAjax(biddingContentCommonRuleService.changeLockStatus(bo));
}
/**
* 删除技术内容样式检查通用规则设置
*
* @param ids 主键串
*/
@SaCheckPermission("review:contentCommonRule:remove")
@Log(title = "技术内容样式检查通用规则设置", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(biddingContentCommonRuleService.deleteWithValidByIds(List.of(ids), true));
}
/**
* 查询技术内容样式检查通用规则设置列表
*/
@PostMapping("/listQuery")
@SaCheckPermission("review:contentCommonRule:listQuery")
public R<List<BiddingContentCommonRuleVo>> listQuery(@RequestBody BiddingContentCommonRuleBo bo) {
return R.ok(biddingContentCommonRuleService.listQuery(bo));
}
/**
* 获取通用规则列表(用于标书编制 - 样式规则选择)
* 返回所有可用的自定义规则模板
*/
@SaCheckPermission("review:contentCommonRule:list")
@GetMapping("/common-rule/list")
public R<List<BiddingContentCommonRuleVo>> getCommonRuleList(BiddingContentCommonRuleBo bo) {
// 查询所有通用规则(未删除的)
List<BiddingContentCommonRuleVo> list = biddingContentCommonRuleService.queryList(bo);
return R.ok(list);
}
/**
* 获取板块下拉列表
*/
@SaCheckPermission("review:contentCommonRule:list")
@GetMapping("/sectorOptions")
public R<List<String>> sectorOptions(BiddingContentCommonRuleBo bo) {
return R.ok(biddingContentCommonRuleService.sectorOptions(bo));
}
/**
* 获取规则下拉列表
*/
@SaCheckPermission("review:contentCommonRule:list")
@GetMapping("/ruleOptions")
public R<List<BiddingContentCommonRuleVo>> ruleOptions(BiddingContentCommonRuleBo bo) {
return R.ok(biddingContentCommonRuleService.ruleOptions(bo));
}
}

View File

@@ -0,0 +1,201 @@
package org.dromara.review.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.common.rabbitmq.constant.RabbitMQConstants;
import org.dromara.common.rabbitmq.service.RabbitMQService;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.review.Utils.ConfigTreeBuilder;
import org.dromara.review.domain.BiddingContentResultDtl;
import org.dromara.review.domain.bo.*;
import org.dromara.review.domain.vo.BiddingContentResultDtlVo;
import org.dromara.review.enums.TaskTypeEnums;
import org.dromara.review.rabbitMq.RabbitMQMessage;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.review.domain.vo.BiddingContentResultVo;
import org.dromara.review.service.IBiddingContentResultService;
import org.dromara.review.service.IBiddingReviewTaskService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
/**
* 技术标内容样式检查结果
*
* @author Lion Li
* @date 2025-11-12
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/contentResult")
public class BiddingContentResultController extends BaseController {
private final IBiddingContentResultService biddingContentResultService;
private final RabbitMQService rabbitMQService;
private final ConfigTreeBuilder configTreeBuilder;
private final IBiddingReviewTaskService biddingReviewTaskService;
/**
* 查询技术标内容样式检查结果列表
*/
@SaCheckPermission("review:contentResult:list")
@GetMapping("/list")
public TableDataInfo<BiddingContentResultVo> list(BiddingContentResultBo bo, PageQuery pageQuery) {
return biddingContentResultService.queryPageList(bo, pageQuery);
}
/**
* 导出技术标内容样式检查结果列表
*/
@SaCheckPermission("review:contentResult:export")
@Log(title = "技术标内容样式检查结果", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(BiddingContentResultBo bo, HttpServletResponse response) {
List<BiddingContentResultVo> list = biddingContentResultService.queryList(bo);
ExcelUtil.exportExcel(list, "技术标内容样式检查结果", BiddingContentResultVo.class, response);
}
/**
* 获取技术标内容样式检查结果详细信息
*
* @param id 主键
*/
@SaCheckPermission("review:contentResult:query")
@GetMapping("/{id}")
public R<BiddingContentResultVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(biddingContentResultService.queryById(id));
}
/**
* 新增技术标内容样式检查结果
*/
@SaCheckPermission("review:contentResult:add")
@Log(title = "技术标内容样式检查结果", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody BiddingContentResultBo bo) {
return toAjax(biddingContentResultService.insertByBo(bo));
}
/**
* 修改技术标内容样式检查结果
*/
@SaCheckPermission("review:contentResult:edit")
@Log(title = "技术标内容样式检查结果", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody BiddingContentResultBo bo) {
return toAjax(biddingContentResultService.updateByBo(bo));
}
/**
* 删除技术标内容样式检查结果
*
* @param ids 主键串
*/
@SaCheckPermission("review:contentResult:remove")
@Log(title = "技术标内容样式检查结果", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(biddingContentResultService.deleteWithValidByIds(List.of(ids), true));
}
/**
* 查询技术标内容样式检查结果列表
*/
@PostMapping("/list")
@SaCheckPermission("review:contentResult:queryList")
@Log(title = "查询技术标内容样式检查结果列表")
public R<List<BiddingContentResultVo>> list(@RequestBody BiddingContentResultBo bo) {
return R.ok(biddingContentResultService.queryListMapper(bo));
}
/**
* 查询技术标内容样式检查结果详情
*/
@PostMapping("/getDetail")
@Log(title = "查询技术标内容样式检查结果详情")
@SaCheckPermission("review:contentResult:getDetail")
public Object getDetail(@RequestBody BiddingContentResultBo bo,PageQuery pageQuery) {
return biddingContentResultService.getDetail(bo,pageQuery);
}
@Log(title = "开始内容检查")
@PostMapping("/startContentLocal")
@SaCheckPermission("review:contentResult:startContentLocal")
public R<Long> startContentLocal(@RequestBody BiddingContentCommonRuleBo bo) {
try {
if (bo == null || bo.getInfoId() == null) {
return R.fail("参数错误: infoId不能为空");
}
if (biddingReviewTaskService.existsInProgressTask(TaskTypeEnums.CONTENT_CHECK.getTaskType(), bo.getInfoId().intValue())) {
return R.fail("当前项目正在审查,不允许重复发起");
}
return R.ok(biddingContentResultService.startContentLocal(bo));
} catch (Exception e) {
return R.fail("检查失败:"+e.getMessage());
}
}
@Log(title = "内容检查结果统计")
@GetMapping("/contentStatistics/{infoId}")
@SaCheckPermission("review:contentResult:contentStatistics")
public R<BiddingContentResultStatisticsBo> contentStatistics(@PathVariable Long infoId) {
try {
return R.ok(biddingContentResultService.contentStatistics(infoId));
} catch (Exception e) {
return R.fail("统计失败:"+e.getMessage());
}
}
@Log(title = "检查项树形接口")
@GetMapping("/getTreeNode/{id}/{fixed}")
@SaCheckPermission("review:contentResult:getTreeNode")
public R<List<ConfigTreeNode>> getTreeNode(@PathVariable Long id,@PathVariable Integer fixed) {
try {
return R.ok(configTreeBuilder.buildConfigTree(id,fixed));
} catch (Exception e) {
return R.fail("统计失败:"+e.getMessage());
}
}
@Log(title = "开始内容一键改写")
@PostMapping("/startContentReWrite")
// @SaCheckPermission("review:contentResult:startContentReWrite")
public R<Long> startContentReWrite(@RequestBody BiddingContentCommonRuleBo bo) {
try {
return R.ok(biddingContentResultService.startContentReWrite(bo));
} catch (Exception e) {
return R.fail("检查失败:"+e.getMessage());
}
}
}

View File

@@ -0,0 +1,112 @@
package org.dromara.review.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.review.domain.vo.BiddingContentRuleVo;
import org.dromara.review.domain.bo.BiddingContentRuleBo;
import org.dromara.review.service.IBiddingContentRuleService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
* 技术标内容检查规则设置
*
* @author Lion Li
* @date 2025-11-12
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/contentRule")
public class BiddingContentRuleController extends BaseController {
private final IBiddingContentRuleService biddingContentRuleService;
/**
* 查询技术标内容检查规则设置列表
*/
@SaCheckPermission("review:contentRule:list")
@GetMapping("/list")
public TableDataInfo<BiddingContentRuleVo> list(BiddingContentRuleBo bo, PageQuery pageQuery) {
return biddingContentRuleService.queryPageList(bo, pageQuery);
}
/**
* 导出技术标内容检查规则设置列表
*/
@SaCheckPermission("review:contentRule:export")
@Log(title = "技术标内容检查规则设置", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(BiddingContentRuleBo bo, HttpServletResponse response) {
List<BiddingContentRuleVo> list = biddingContentRuleService.queryList(bo);
ExcelUtil.exportExcel(list, "技术标内容检查规则设置", BiddingContentRuleVo.class, response);
}
/**
* 获取技术标内容检查规则设置详细信息
*
* @param id 主键
*/
@SaCheckPermission("review:contentRule:query")
@GetMapping("/{id}")
public R<BiddingContentRuleVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(biddingContentRuleService.queryById(id));
}
/**
* 新增技术标内容检查规则设置
*/
@SaCheckPermission("review:contentRule:add")
@Log(title = "技术标内容检查规则设置", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody BiddingContentRuleBo bo) {
biddingContentRuleService.create(bo);
return R.ok();
}
/**
* 修改技术标内容检查规则设置
*/
@SaCheckPermission("review:contentRule:edit")
@Log(title = "技术标内容检查规则设置", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody BiddingContentRuleBo bo) {
return toAjax(biddingContentRuleService.updateByBo(bo));
}
/**
* 删除技术标内容检查规则设置
*
* @param ids 主键串
*/
@SaCheckPermission("review:contentRule:remove")
@Log(title = "技术标内容检查规则设置", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(biddingContentRuleService.deleteWithValidByIds(List.of(ids), true));
}
@GetMapping("/getByInfoId/{infoId}")
@SaCheckPermission("review:contentRule:getByInfoId")
public R<BiddingContentRuleVo> getByInfoId(@PathVariable Long infoId) {
return R.ok(biddingContentRuleService.getByInfoId(infoId));
}
}

View File

@@ -0,0 +1,31 @@
package org.dromara.review.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import lombok.RequiredArgsConstructor;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.review.domain.bo.BiddingDocumentRecordBo;
import org.dromara.review.domain.vo.BiddingDocumentRecordVo;
import org.dromara.review.service.IBiddingDocumentRecordService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/documentRecord")
public class BiddingDocumentRecordController {
private final IBiddingDocumentRecordService biddingDocumentRecordService;
/**
* 查询投标信息列表
*/
@SaCheckPermission("document:record:list")
@GetMapping("/list")
public TableDataInfo<BiddingDocumentRecordVo> list(BiddingDocumentRecordBo bo, PageQuery pageQuery) {
return biddingDocumentRecordService.queryPageList(bo, pageQuery);
}
}

View File

@@ -0,0 +1,408 @@
package org.dromara.review.controller;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.time.Duration;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.xml.stream.*;
import com.aspose.words.*;
import com.alibaba.fastjson.JSON;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.rabbitmq.constant.RabbitMQConstants;
import org.dromara.common.rabbitmq.service.RabbitMQService;
import org.dromara.common.oss.factory.OssFactory;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.review.Utils.DocToDocxConverter;
import org.dromara.review.domain.bo.BatchParagraphUpdateDTO;
import org.dromara.review.domain.bo.DirectOssUploadCompleteBo;
import org.dromara.review.domain.bo.DirectOssUploadInitBo;
import org.dromara.review.domain.bo.BiddingInfoProposalBatchBo;
import org.dromara.review.enums.TaskStatusEnums;
import org.dromara.review.enums.TaskTypeEnums;
import org.dromara.review.domain.vo.BiddingReviewTaskVo;
import org.dromara.review.domain.vo.DirectOssUploadInitVo;
import org.dromara.review.domain.vo.DirectOssUploadStatusVo;
import org.dromara.review.service.IBiddingReviewTaskService;
import org.dromara.review.service.IDirectOssUploadService;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.service.ISysOssService;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.review.domain.bo.BiddingProposalBo;
import org.dromara.review.domain.bo.BiddingProposalDtlBo;
import org.dromara.review.domain.vo.BiddingInfoVo;
import org.dromara.review.domain.bo.BiddingInfoBo;
import org.dromara.review.domain.vo.BiddingProposalDtlVo;
import org.dromara.review.domain.vo.BiddingProposalVo;
import org.dromara.review.service.IBiddingInfoService;
import org.dromara.review.service.IBiddingProposalDtlService;
import org.dromara.review.service.IBiddingProposalService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.web.multipart.MultipartFile;
/**
* 投标信息
*
* @author Lion Li
* @date 2025-11-12
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/info")
@Slf4j
public class BiddingInfoController extends BaseController {
private final IBiddingInfoService biddingInfoService;
private final IBiddingReviewTaskService biddingReviewTaskService;
private final ISysOssService ossService;
private final IBiddingProposalService biddingProposalService;
private final IBiddingProposalDtlService biddingProposalDtlService;
private final RabbitMQService rabbitMQService;
private final IDirectOssUploadService directOssUploadService;
/**
* 查询投标信息列表
*/
@SaCheckPermission("review:info:list")
@GetMapping("/list")
public TableDataInfo<BiddingInfoVo> list(BiddingInfoBo bo, PageQuery pageQuery) {
return biddingInfoService.queryPageList(bo, pageQuery);
}
/**
* 导出投标信息列表
*/
@SaCheckPermission("review:info:export")
@Log(title = "投标信息", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(BiddingInfoBo bo, HttpServletResponse response) {
List<BiddingInfoVo> list = biddingInfoService.queryList(bo);
ExcelUtil.exportExcel(list, "投标信息", BiddingInfoVo.class, response);
}
/**
* 获取投标信息详细信息
*
* @param id 主键
*/
@SaCheckPermission("review:info:query")
@GetMapping("/{id}")
public R<BiddingInfoVo> getByIdMapper(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(biddingInfoService.getByIdMapper(id));
}
/**
* 新增投标信息
*/
@SaCheckPermission("review:info:add")
@Log(title = "投标信息", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Long> add(@Validated(AddGroup.class) @RequestBody BiddingInfoBo bo) {
return R.ok(biddingInfoService.insertByBo(bo));
}
/**
* 修改投标信息
*/
@SaCheckPermission("review:info:edit")
@Log(title = "投标信息", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody BiddingInfoBo bo) {
return toAjax(biddingInfoService.updateByBo(bo));
}
/**
* 批量维护标段及投标单位信息
*/
@SaCheckPermission("review:info:edit")
@Log(title = "投标信息", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/saveProposalBatch")
public R<Void> saveProposalBatch(@Validated @RequestBody BiddingInfoProposalBatchBo bo) {
return toAjax(biddingInfoService.saveProposalBatch(bo));
}
/**
* 删除投标单位及关联数据
*/
@SaCheckPermission("review:info:edit")
@Log(title = "投标信息", businessType = BusinessType.DELETE)
@DeleteMapping("/proposal/{infoId}/{proposalId}")
public R<Void> deleteProposalCascade(@NotNull(message = "infoId不能为空") @PathVariable Long infoId,
@NotNull(message = "proposalId不能为空") @PathVariable Long proposalId) {
return toAjax(biddingInfoService.deleteProposalCascade(infoId, proposalId));
}
/**
* 删除投标文件及关联数据
*/
@SaCheckPermission("review:info:edit")
@Log(title = "投标信息", businessType = BusinessType.DELETE)
@DeleteMapping("/proposalDtl/{infoId}/{proposalDtlId}")
public R<Void> deleteProposalDtlCascade(@NotNull(message = "infoId不能为空") @PathVariable Long infoId,
@NotNull(message = "proposalDtlId不能为空") @PathVariable Long proposalDtlId) {
return toAjax(biddingInfoService.deleteProposalDtlCascade(infoId, proposalDtlId));
}
/**
* 删除投标文件及纪律性分析相关文件
*/
@SaCheckPermission("review:info:edit")
@Log(title = "投标信息", businessType = BusinessType.DELETE)
@DeleteMapping("/{infoId}/{proposalDtlId}")
public R<Void> deleteProposalDtl(@NotNull(message = "infoId不能为空") @PathVariable Long infoId,
@NotNull(message = "proposalDtlId不能为空") @PathVariable Long proposalDtlId) {
return toAjax(biddingInfoService.deleteProposalDtl(infoId, proposalDtlId));
}
/**
* 删除投标信息
*
* @param ids 主键串
*/
@SaCheckPermission("review:info:remove")
@Log(title = "投标信息", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(biddingInfoService.deleteWithValidByIds(List.of(ids), true));
}
/**
* 删除投标信息
*
*/
@SaCheckPermission("review:info:remove")
@Log(title = "投标信息", businessType = BusinessType.DELETE)
@DeleteMapping("/deleteAllData/{id}")
public R<Void> deleteAllData(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return toAjax(biddingInfoService.deleteAllData(id));
}
/**
* 查询任务情况
*
*/
@Log(title = "查询任务情况")
@GetMapping("/getTask/{id}")
public R<BiddingReviewTaskVo> getTask(@PathVariable Long id) {
return R.ok(biddingReviewTaskService.queryById(id));
}
@PostMapping("/ossUpload")
public R<?> ossUpload(@RequestParam("file") MultipartFile file) {
try {
// 保存文件到本地,返回文件信息
if(DocToDocxConverter.isDocFile(file.getOriginalFilename())){
byte[] bytes = DocToDocxConverter.convertDocToDocx(file.getBytes());
InputStream in = new ByteArrayInputStream(bytes);
String docxFile = DocToDocxConverter.changeExtensionToDocx(file.getOriginalFilename());
SysOssVo fileVO = ossService.upload(in,docxFile);
return R.ok(fileVO);
}else{
SysOssVo fileVO = ossService.upload(file);
// 按若依框架统一响应格式返回code=200 成功)
return R.ok(fileVO);
}
} catch (Exception e) {
return R.fail("上传失败"+e.getMessage());
}
}
/**
* 初始化直传 OSS
*/
@PostMapping("/ossUpload/init")
public R<DirectOssUploadInitVo> initDirectUpload(@Validated @RequestBody DirectOssUploadInitBo bo) {
return R.ok(directOssUploadService.initUpload(bo));
}
/**
* 完成直传 OSS
*/
@PostMapping("/ossUpload/complete")
public R<DirectOssUploadStatusVo> completeDirectUpload(@Validated @RequestBody DirectOssUploadCompleteBo bo) {
return R.ok(directOssUploadService.completeUpload(bo));
}
/**
* 查询直传 OSS 状态
*/
@GetMapping("/ossUpload/status/{taskId}")
public R<DirectOssUploadStatusVo> queryDirectUploadStatus(@PathVariable String taskId) {
return R.ok(directOssUploadService.queryStatus(taskId));
}
/**
* 批量修改段落并保存
*
* @param dto 批量修改请求
* @return 新文件的OSS ID
*/
@Log(title = "批量修改段落")
@PostMapping("/batchUpdateParagraph")
public R<Long> batchUpdateParagraph(@RequestBody BatchParagraphUpdateDTO dto) {
try {
if (dto == null || dto.getId() == null) {
return R.fail("参数错误: 当前分析结果id不能为空");
}
String taskType = TaskTypeEnums.ANALYSIS_BATCH_PARAGRAPH.getTaskType();
String currentStatus = biddingReviewTaskService.queryTaskType(taskType, dto.getId());
if (TaskStatusEnums.IN_PROGRESS_WRITE.getTaskName().equals(currentStatus)) {
return R.fail("文件已经在改写,请稍后再去改写");
}
// 1. 生成任务ID
Long taskId = biddingReviewTaskService.createTask(taskType, dto, dto.getId().intValue());
dto.setTaskId(taskId);
rabbitMQService.sendMessage(
RabbitMQConstants.AI_REVIEW_EXCHANGE,
RabbitMQConstants.BATCH_UPDATE_PARAGRAPH_KEY,
JSON.toJSONString(dto)
);
return R.ok("批量修改任务已提交,正在后台处理");
} catch (Exception e) {
return R.fail("批量修改段落失败: " + e.getMessage());
}
}
/**
* 还原文档
*/
@Log(title = "还原文档")
@GetMapping("/reduction/{infoId}/{flag}")
public R reduction(@PathVariable Long infoId,@PathVariable Integer flag) {
try {
biddingInfoService.reduction(infoId,flag);
return R.ok( "还原成功");
} catch (Exception e) {
return R.fail("还原失败: " + e.getMessage());
}
}
/**
* 导出投标文件列表
*/
@Log(title = "导出投标文件", businessType = BusinessType.EXPORT)
@GetMapping("/exportDocuments/{infoId}")
public R<List<ExportDocumentVo>> exportDocuments(@PathVariable Long infoId) {
try {
BiddingProposalBo proposalBo = new BiddingProposalBo();
proposalBo.setInfoId(infoId);
List<BiddingProposalVo> proposals = biddingProposalService.querySimpleList(proposalBo);
if (proposals == null || proposals.isEmpty()) {
return R.fail("未找到投标文件");
}
Map<Long, BiddingProposalVo> proposalMap = proposals.stream()
.filter(proposal -> proposal.getId() != null)
.collect(Collectors.toMap(BiddingProposalVo::getId, proposal -> proposal));
if (proposalMap.isEmpty()) {
return R.fail("未找到投标文件");
}
List<BiddingProposalDtlVo> dtlList = biddingProposalDtlService.queryListByProposalIds(proposalMap.keySet());
if (dtlList == null || dtlList.isEmpty()) {
return R.fail("所有投标单位均无可下载文件");
}
List<Long> ossIds = dtlList.stream()
.map(BiddingProposalDtlVo::getOssId)
.filter(Objects::nonNull)
.distinct()
.toList();
if (ossIds.isEmpty()) {
return R.fail("所有投标单位均无可下载文件");
}
Map<Long, SysOssVo> ossMap = ossService.listByIds(ossIds).stream()
.filter(oss -> oss != null && oss.getOssId() != null)
.collect(Collectors.toMap(SysOssVo::getOssId, oss -> oss, (left, right) -> left));
List<ExportDocumentVo> documents = new ArrayList<>();
for (BiddingProposalDtlVo dtl : dtlList) {
if (dtl.getProposalId() == null || dtl.getOssId() == null) {
continue;
}
BiddingProposalVo proposal = proposalMap.get(dtl.getProposalId());
if (proposal == null) {
continue;
}
SysOssVo ossFile = ossMap.get(dtl.getOssId());
if (ossFile == null) {
continue;
}
ExportDocumentVo document = new ExportDocumentVo();
document.setProposalId(proposal.getId());
document.setProposalName(StringUtils.defaultIfBlank(proposal.getName(), "投标单位_" + proposal.getId()));
document.setOssId(dtl.getOssId());
document.setFileName(StringUtils.defaultIfBlank(ossFile.getOriginalName(), ossFile.getFileName()));
document.setDownloadUrl(buildPresignedDownloadUrl(ossFile, dtl.getOssId()));
documents.add(document);
}
if (documents.isEmpty()) {
return R.fail("所有投标单位均无可下载文件");
}
return R.ok(documents);
} catch (Exception e) {
log.error("导出投标文件失败, infoId={}", infoId, e);
return R.fail("导出失败: " + e.getMessage());
}
}
private String buildPresignedDownloadUrl(SysOssVo ossFile, Long ossId) {
if (StringUtils.isBlank(ossFile.getFileName()) || StringUtils.isBlank(ossFile.getService())) {
return "/resource/oss/download/" + ossId;
}
return OssFactory.instance(ossFile.getService())
.getPrivateUrl(ossFile.getFileName(), Duration.ofSeconds(120));
}
@Data
private static class ExportDocumentVo {
private Long proposalId;
private String proposalName;
private Long ossId;
private String fileName;
private String downloadUrl;
}
}

View File

@@ -0,0 +1,125 @@
package org.dromara.review.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.review.domain.bo.BiddingInfoDocuBo;
import org.dromara.review.domain.vo.BiddingInfoDocuVo;
import org.dromara.review.service.IBiddingInfoDocuService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 标段招标文件
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/infoDocu")
public class BiddingInfoDocuController extends BaseController {
private final IBiddingInfoDocuService biddingInfoDocuService;
/**
* 查询标段招标文件列表
*/
@SaCheckPermission("review:infoDocu:list")
@GetMapping("/list")
public TableDataInfo<BiddingInfoDocuVo> list(BiddingInfoDocuBo bo, PageQuery pageQuery) {
return biddingInfoDocuService.queryPageList(bo, pageQuery);
}
/**
* 查询标段招标文件列表,不分页
*/
@SaCheckPermission("review:infoDocu:list")
@GetMapping("/listAll")
public R<List<BiddingInfoDocuVo>> listAll(BiddingInfoDocuBo bo) {
return R.ok(biddingInfoDocuService.queryList(bo));
}
/**
* 导出标段招标文件列表
*/
@SaCheckPermission("review:infoDocu:export")
@Log(title = "标段招标文件", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(BiddingInfoDocuBo bo, HttpServletResponse response) {
List<BiddingInfoDocuVo> list = biddingInfoDocuService.queryList(bo);
ExcelUtil.exportExcel(list, "标段招标文件", BiddingInfoDocuVo.class, response);
}
/**
* 获取标段招标文件详细信息
*/
@SaCheckPermission("review:infoDocu:query")
@GetMapping("/{id}")
public R<BiddingInfoDocuVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return R.ok(biddingInfoDocuService.queryById(id));
}
/**
* 新增标段招标文件
*/
@SaCheckPermission("review:infoDocu:add")
@Log(title = "标段招标文件", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody BiddingInfoDocuBo bo) {
return toAjax(biddingInfoDocuService.insertByBo(bo));
}
/**
* 批量新增标段招标文件
*/
@SaCheckPermission("review:infoDocu:add")
@Log(title = "标段招标文件", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/batch")
public R<Void> addBatch(@NotEmpty(message = "请求数据不能为空") @RequestBody List<BiddingInfoDocuBo> boList) {
return toAjax(biddingInfoDocuService.insertBatchByBo(boList));
}
/**
* 修改标段招标文件
*/
@SaCheckPermission("review:infoDocu:edit")
@Log(title = "标段招标文件", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody BiddingInfoDocuBo bo) {
return toAjax(biddingInfoDocuService.updateByBo(bo));
}
/**
* 删除标段招标文件
*/
@SaCheckPermission("review:infoDocu:remove")
@Log(title = "标段招标文件", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
return toAjax(biddingInfoDocuService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@@ -0,0 +1,114 @@
package org.dromara.review.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.review.domain.vo.BiddingProposalVo;
import org.dromara.review.domain.bo.BiddingProposalBo;
import org.dromara.review.service.IBiddingProposalService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
* 投标文件
*
* @author Lion Li
* @date 2025-11-12
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/proposal")
public class BiddingProposalController extends BaseController {
private final IBiddingProposalService biddingProposalService;
/**
* 查询投标文件列表
*/
@SaCheckPermission("review:proposal:list")
@GetMapping("/list")
public TableDataInfo<BiddingProposalVo> list(BiddingProposalBo bo, PageQuery pageQuery) {
return biddingProposalService.queryPageList(bo, pageQuery);
}
/**
* 导出投标文件列表
*/
@SaCheckPermission("review:proposal:export")
@Log(title = "投标文件", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(BiddingProposalBo bo, HttpServletResponse response) {
List<BiddingProposalVo> list = biddingProposalService.queryList(bo);
ExcelUtil.exportExcel(list, "投标文件", BiddingProposalVo.class, response);
}
/**
* 获取投标文件详细信息
*
* @param id 主键
*/
@SaCheckPermission("review:proposal:query")
@GetMapping("/{id}")
public R<BiddingProposalVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(biddingProposalService.queryById(id));
}
/**
* 新增投标文件
*/
@SaCheckPermission("review:proposal:add")
@Log(title = "投标文件", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody BiddingProposalBo bo) {
return toAjax(biddingProposalService.insertByBo(bo));
}
/**
* 修改投标文件
*/
@SaCheckPermission("review:proposal:edit")
@Log(title = "投标文件", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody BiddingProposalBo bo) {
return toAjax(biddingProposalService.updateByBo(bo));
}
/**
* 删除投标文件
*
* @param ids 主键串
*/
@SaCheckPermission("review:proposal:remove")
@Log(title = "投标文件", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(biddingProposalService.deleteWithValidByIds(List.of(ids), true));
}
/**
* 投标文件列表
*/
@SaCheckPermission("review:proposal:queryList")
@PostMapping("/queryList")
public R<List<BiddingProposalVo>> queryList(BiddingProposalBo bo) {
return R.ok(biddingProposalService.queryList(bo));
}
}

View File

@@ -0,0 +1,117 @@
package org.dromara.review.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.review.domain.vo.BiddingProposalDtlVo;
import org.dromara.review.domain.bo.BiddingProposalDtlBo;
import org.dromara.review.service.IBiddingProposalDtlService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
* 投标文件
*
* @author Lion Li
* @date 2025-11-12
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/proposalDtl")
public class BiddingProposalDtlController extends BaseController {
private final IBiddingProposalDtlService biddingProposalDtlService;
/**
* 查询投标文件列表
*/
@SaCheckPermission("review:proposalDtl:list")
@GetMapping("/list")
public TableDataInfo<BiddingProposalDtlVo> list(BiddingProposalDtlBo bo, PageQuery pageQuery) {
return biddingProposalDtlService.queryPageList(bo, pageQuery);
}
/**
* 导出投标文件列表
*/
@SaCheckPermission("review:proposalDtl:export")
@Log(title = "投标文件", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(BiddingProposalDtlBo bo, HttpServletResponse response) {
List<BiddingProposalDtlVo> list = biddingProposalDtlService.queryList(bo);
ExcelUtil.exportExcel(list, "投标文件", BiddingProposalDtlVo.class, response);
}
/**
* 获取投标文件详细信息
*
* @param id 主键
*/
@SaCheckPermission("review:proposalDtl:query")
@GetMapping("/{id}")
public R<BiddingProposalDtlVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(biddingProposalDtlService.queryById(id));
}
/**
* 新增投标文件
*/
@SaCheckPermission("review:proposalDtl:add")
@Log(title = "投标文件", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody BiddingProposalDtlBo bo) {
return toAjax(biddingProposalDtlService.insertByBo(bo));
}
/**
* 修改投标文件
*/
@SaCheckPermission("review:proposalDtl:edit")
@Log(title = "投标文件", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody BiddingProposalDtlBo bo) {
return toAjax(biddingProposalDtlService.updateByBo(bo));
}
/**
* 更新投标文件 OSS 关联
*/
@SaCheckPermission("review:proposalDtl:edit")
@Log(title = "投标文件", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/updateOssId")
public R<Void> updateOssId(@NotNull(message = "主键不能为空") @RequestParam Long id,
@NotNull(message = "ossId不能为空") @RequestParam Long ossId) {
return toAjax(biddingProposalDtlService.updateOssId(id, ossId));
}
/**
* 删除投标文件
*
* @param ids 主键串
*/
@SaCheckPermission("review:proposalDtl:remove")
@Log(title = "投标文件", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(biddingProposalDtlService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@@ -0,0 +1,105 @@
package org.dromara.review.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.review.domain.vo.BiddingResponseContentCommonRuleVo;
import org.dromara.review.domain.bo.BiddingResponseContentCommonRuleBo;
import org.dromara.review.service.IBiddingResponseContentCommonRuleService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
* 技术标响应式通用规则
*
* @author Lion Li
* @date 2025-11-12
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/responseContentCommonRule")
public class BiddingResponseContentCommonRuleController extends BaseController {
private final IBiddingResponseContentCommonRuleService biddingResponseContentCommonRuleService;
/**
* 查询技术标响应式通用规则列表
*/
@SaCheckPermission("review:responseContentCommonRule:list")
@GetMapping("/list")
public TableDataInfo<BiddingResponseContentCommonRuleVo> list(BiddingResponseContentCommonRuleBo bo, PageQuery pageQuery) {
return biddingResponseContentCommonRuleService.queryPageList(bo, pageQuery);
}
/**
* 导出技术标响应式通用规则列表
*/
@SaCheckPermission("review:responseContentCommonRule:export")
@Log(title = "技术标响应式通用规则", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(BiddingResponseContentCommonRuleBo bo, HttpServletResponse response) {
List<BiddingResponseContentCommonRuleVo> list = biddingResponseContentCommonRuleService.queryList(bo);
ExcelUtil.exportExcel(list, "技术标响应式通用规则", BiddingResponseContentCommonRuleVo.class, response);
}
/**
* 获取技术标响应式通用规则详细信息
*
* @param id 主键
*/
@SaCheckPermission("review:responseContentCommonRule:query")
@GetMapping("/{id}")
public R<BiddingResponseContentCommonRuleVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(biddingResponseContentCommonRuleService.queryById(id));
}
/**
* 新增技术标响应式通用规则
*/
@SaCheckPermission("review:responseContentCommonRule:add")
@Log(title = "技术标响应式通用规则", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody BiddingResponseContentCommonRuleBo bo) {
return toAjax(biddingResponseContentCommonRuleService.insertByBo(bo));
}
/**
* 修改技术标响应式通用规则
*/
@SaCheckPermission("review:responseContentCommonRule:edit")
@Log(title = "技术标响应式通用规则", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody BiddingResponseContentCommonRuleBo bo) {
return toAjax(biddingResponseContentCommonRuleService.updateByBo(bo));
}
/**
* 删除技术标响应式通用规则
*
* @param ids 主键串
*/
@SaCheckPermission("review:responseContentCommonRule:remove")
@Log(title = "技术标响应式通用规则", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(biddingResponseContentCommonRuleService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@@ -0,0 +1,230 @@
package org.dromara.review.controller;
import java.util.List;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.review.domain.bo.BiddingResponseContentResultDtlBo;
import org.dromara.review.domain.vo.BiddingResponseContentResultDtlVo;
import org.dromara.review.enums.TaskTypeEnums;
import org.dromara.review.tree.FactorNode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.review.domain.vo.BiddingResponseContentResultVo;
import org.dromara.review.domain.bo.BiddingResponseContentResultBo;
import org.dromara.review.service.IBiddingReviewTaskService;
import org.dromara.review.service.IBiddingResponseContentResultService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.client.RestTemplate;
import org.dromara.common.core.utils.StringUtils;
import java.io.IOException;
/**
* 技术标响应式检查结果
*
* @author Lion Li
* @date 2025-11-12
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/responseContentResult")
public class BiddingResponseContentResultController extends BaseController {
private final IBiddingResponseContentResultService biddingResponseContentResultService;
private final IBiddingReviewTaskService biddingReviewTaskService;
@Value("${ai-check.base-url:http://127.0.0.1:8079}")
private String aiCheckBaseUrl;
/**
* 查询技术标响应式检查结果列表
*/
@SaCheckPermission("review:responseContentResult:list")
@GetMapping("/list")
public TableDataInfo<BiddingResponseContentResultVo> list(BiddingResponseContentResultBo bo, PageQuery pageQuery) {
return biddingResponseContentResultService.queryPageList(bo, pageQuery);
}
/**
* 导出技术标响应式检查结果列表
*/
@SaCheckPermission("review:responseContentResult:export")
@Log(title = "技术标响应式检查结果", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(BiddingResponseContentResultBo bo, HttpServletResponse response) {
List<BiddingResponseContentResultVo> list = biddingResponseContentResultService.queryList(bo);
ExcelUtil.exportExcel(list, "技术标响应式检查结果", BiddingResponseContentResultVo.class, response);
}
/**
* 获取技术标响应式检查结果详细信息
*
* @param id 主键
*/
@SaCheckPermission("review:responseContentResult:query")
@GetMapping("/{id}")
public R<BiddingResponseContentResultVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(biddingResponseContentResultService.queryById(id));
}
/**
* 新增技术标响应式检查结果
*/
@SaCheckPermission("review:responseContentResult:add")
@Log(title = "技术标响应式检查结果", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody BiddingResponseContentResultBo bo) {
return toAjax(biddingResponseContentResultService.insertByBo(bo));
}
/**
* 修改技术标响应式检查结果
*/
@SaCheckPermission("review:responseContentResult:edit")
@Log(title = "技术标响应式检查结果", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody BiddingResponseContentResultBo bo) {
return toAjax(biddingResponseContentResultService.updateByBo(bo));
}
/**
* 删除技术标响应式检查结果
*
* @param ids 主键串
*/
@SaCheckPermission("review:responseContentResult:remove")
@Log(title = "技术标响应式检查结果", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(biddingResponseContentResultService.deleteWithValidByIds(List.of(ids), true));
}
@Log(title = "开始响应式检查")
@RepeatSubmit()
@PostMapping("/startResponse")
public R<Void> startResponse(@RequestBody BiddingResponseContentResultBo bo) {
return toAjax(biddingResponseContentResultService.startResponse(bo));
}
@Log(title = "获取响应式检查详情")
@RepeatSubmit()
@PostMapping("/getResponseDetail")
@SaCheckPermission("review:responseContentResult:getResponseDetail")
public R<List<BiddingResponseContentResultDtlVo>> getResponseDetail(@RequestBody BiddingResponseContentResultDtlBo bo) {
return R.ok(biddingResponseContentResultService.getResponseDetail(bo));
}
@Log(title = "获取响应式检查结果列表")
@PostMapping("/getList")
@SaCheckPermission("review:responseContentResult:getList")
public R<JSONObject> getList(@RequestBody BiddingResponseContentResultBo bo) {
return R.ok(biddingResponseContentResultService.getList(bo));
}
@Log(title = "开始响应式检查")
@RepeatSubmit()
@PostMapping("/startResponseLocal")
@SaCheckPermission("review:responseContentResult:startResponseLocal")
public R<Long> startResponseLocal(@RequestBody BiddingResponseContentResultBo bo) {
if (bo == null || bo.getInfoId() == null) {
return R.fail("参数错误: infoId不能为空");
}
if (biddingReviewTaskService.existsInProgressTask(TaskTypeEnums.RESPONSE.getTaskType(), bo.getInfoId().intValue())) {
return R.fail("当前项目正在审查,不允许重复发起");
}
return R.ok(biddingResponseContentResultService.startResponseLocal(bo));
}
@Log(title = "检查招标文件评审模块")
@RepeatSubmit()
@GetMapping("/startReview/{biddingDocuId}")
@SaCheckPermission("review:responseContentResult:startReview")
public R<List<FactorNode>> startReview(@PathVariable Long biddingDocuId) {
return biddingResponseContentResultService.startReview(biddingDocuId);
}
@Log(title = "检查招标文件评审模块")
@RepeatSubmit()
@GetMapping("/startReviewAsync/{biddingDocuId}")
@SaCheckPermission("review:responseContentResult:startReview")
public R<String> startReviewAsync(@PathVariable Long biddingDocuId) {
// 生成任务ID
String taskId = "review_task_" + System.currentTimeMillis() + "_" + biddingDocuId;
// 调用异步方法,不等待结果
biddingResponseContentResultService.startReviewAsync(biddingDocuId, taskId);
// 立即返回任务ID
return R.ok(taskId);
}
@Log(title = "检查招标文件评审模块")
@RepeatSubmit()
@PostMapping(value = "/startReviewContentAsync", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@SaCheckPermission("review:responseContentResult:startReview")
public R<String> startReviewContentAsync(@RequestParam(value = "file", required = false) MultipartFile file,
@RequestParam(value = "text", required = false) String text) {
boolean hasFile = file != null && !file.isEmpty();
boolean hasText = StringUtils.isNotBlank(text);
if (hasFile == hasText) {
return R.fail("文件和文本只能传一个");
}
String taskId = "review_task_" + System.currentTimeMillis();
try {
byte[] fileBytes = hasFile ? file.getBytes() : null;
String originalFilename = hasFile ? file.getOriginalFilename() : null;
biddingResponseContentResultService.startReviewAsync(fileBytes, originalFilename, text, taskId);
return R.ok(taskId);
} catch (IOException e) {
return R.fail("读取上传文件失败: " + e.getMessage());
}
}
@Log(title = "获取异步评审结果")
@GetMapping("/getReviewResult/{taskId}")
@SaCheckPermission("review:responseContentResult:getReviewResult")
public R<List<FactorNode>> getReviewResult(@PathVariable String taskId) {
try {
List<FactorNode> factorNodes = biddingResponseContentResultService.getReviewResult(taskId);
return R.ok(factorNodes);
} catch (Exception e) {
return R.fail(e.getMessage());
}
}
@PostMapping("/reWrite")
public R<JSONObject> reWrite(@RequestBody BiddingResponseContentResultBo bo){
return R.ok(biddingResponseContentResultService.reWrite(bo));
}
}

View File

@@ -0,0 +1,106 @@
package org.dromara.review.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.review.domain.vo.BiddingResponseContentRuleVo;
import org.dromara.review.domain.bo.BiddingResponseContentRuleBo;
import org.dromara.review.service.IBiddingResponseContentRuleService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
* 技术标响应式规则设置
*
* @author Lion Li
* @date 2025-11-12
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/responseContentRule")
public class BiddingResponseContentRuleController extends BaseController {
private final IBiddingResponseContentRuleService biddingResponseContentRuleService;
/**
* 查询技术标响应式规则设置列表
*/
@SaCheckPermission("review:responseContentRule:list")
@GetMapping("/list")
public TableDataInfo<BiddingResponseContentRuleVo> list(BiddingResponseContentRuleBo bo, PageQuery pageQuery) {
return biddingResponseContentRuleService.queryPageList(bo, pageQuery);
}
/**
* 导出技术标响应式规则设置列表
*/
@SaCheckPermission("review:responseContentRule:export")
@Log(title = "技术标响应式规则设置", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(BiddingResponseContentRuleBo bo, HttpServletResponse response) {
List<BiddingResponseContentRuleVo> list = biddingResponseContentRuleService.queryList(bo);
ExcelUtil.exportExcel(list, "技术标响应式规则设置", BiddingResponseContentRuleVo.class, response);
}
/**
* 获取技术标响应式规则设置详细信息
*
* @param id 主键
*/
@SaCheckPermission("review:responseContentRule:query")
@GetMapping("/{id}")
public R<BiddingResponseContentRuleVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(biddingResponseContentRuleService.queryById(id));
}
/**
* 新增技术标响应式规则设置
*/
@SaCheckPermission("review:responseContentRule:add")
@Log(title = "技术标响应式规则设置", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody BiddingResponseContentRuleBo bo) {
return toAjax(biddingResponseContentRuleService.insertByBo(bo));
}
/**
* 修改技术标响应式规则设置
*/
@SaCheckPermission("review:responseContentRule:edit")
@Log(title = "技术标响应式规则设置", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody BiddingResponseContentRuleBo bo) {
biddingResponseContentRuleService.updateByBo(bo);
return R.ok();
}
/**
* 删除技术标响应式规则设置
*
* @param ids 主键串
*/
@SaCheckPermission("review:responseContentRule:remove")
@Log(title = "技术标响应式规则设置", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(biddingResponseContentRuleService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@@ -0,0 +1,116 @@
package org.dromara.review.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.review.domain.vo.BiddingAnalysisResultDtlVo;
import org.dromara.review.domain.bo.BiddingAnalysisResultDtlBo;
import org.dromara.review.service.IBiddingAnalysisResultDtlService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
* 技术标检查结果
*
* @author Lion Li
* @date 2025-11-12
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/result")
public class BiddingResultController extends BaseController {
private final IBiddingAnalysisResultDtlService biddingAnalysisResultDtlService;
/**
* 查询技术标检查结果列表
*/
@SaCheckPermission("review:result:list")
@GetMapping("/list")
public TableDataInfo<BiddingAnalysisResultDtlVo> list(BiddingAnalysisResultDtlBo bo, PageQuery pageQuery) {
return biddingAnalysisResultDtlService.queryPageList(bo, pageQuery);
}
/**
* 导出技术标检查结果列表
*/
@SaCheckPermission("review:result:export")
@Log(title = "技术标检查结果", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(BiddingAnalysisResultDtlBo bo, HttpServletResponse response) {
List<BiddingAnalysisResultDtlVo> list = biddingAnalysisResultDtlService.queryList(bo);
ExcelUtil.exportExcel(list, "技术标检查结果", BiddingAnalysisResultDtlVo.class, response);
}
/**
* 获取技术标检查结果详细信息
*
* @param id 主键
*/
@SaCheckPermission("review:result:query")
@GetMapping("/{id}")
public R<BiddingAnalysisResultDtlVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(biddingAnalysisResultDtlService.queryById(id));
}
/**
* 新增技术标检查结果
*/
@SaCheckPermission("review:result:add")
@Log(title = "技术标检查结果", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody BiddingAnalysisResultDtlBo bo) {
return toAjax(biddingAnalysisResultDtlService.insertByBo(bo));
}
/**
* 修改技术标检查结果
*/
@SaCheckPermission("review:result:edit")
@Log(title = "技术标检查结果", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody BiddingAnalysisResultDtlBo bo) {
return toAjax(biddingAnalysisResultDtlService.updateByBo(bo));
}
/**
* 删除技术标检查结果
*
* @param ids 主键串
*/
@SaCheckPermission("review:result:remove")
@Log(title = "技术标检查结果", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(biddingAnalysisResultDtlService.deleteWithValidByIds(List.of(ids), true));
}
/**
* 获取技术标检查结果
*
*/
@Log(title = "获取技术标检查结果")
@PostMapping("/getBiddingResult")
@SaCheckPermission("review:result:getBiddingResult")
public Object getBiddingResult(@RequestBody BiddingAnalysisResultDtlBo bo,PageQuery pageQuery) {
return biddingAnalysisResultDtlService.getBiddingResult(bo,pageQuery);
}
}

View File

@@ -0,0 +1,141 @@
package org.dromara.review.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.common.core.domain.R;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import lombok.RequiredArgsConstructor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dromara.review.domain.bo.DocumentEditSaveDTO;
import org.dromara.review.domain.bo.DocumentGenerateBo;
import org.dromara.review.domain.vo.DocumentGenerateProgressVo;
import org.dromara.review.service.IDocumentGenerateService;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 技术标文档生成控制器
*
* @author Claude
* @date 2026-03-05
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/tech/proposal/generate")
public class DocumentGenerateController {
private final IDocumentGenerateService documentGenerateService;
private final ObjectMapper objectMapper;
/**
* 开始生成文档
*/
@SaCheckPermission("tech:proposal:generate")
@Log(title = "生成技术标文档", businessType = BusinessType.OTHER)
@PostMapping("/start")
public R<Long> startGenerate(@Validated @RequestBody DocumentGenerateBo bo) {
Long taskId = documentGenerateService.startGenerate(bo.getProposalId());
return R.ok(taskId);
}
/**
* 查询生成进度
*/
@SaCheckPermission("tech:proposal:query")
@GetMapping("/progress/{taskId}")
public R<DocumentGenerateProgressVo> getProgress(@PathVariable Long taskId) {
DocumentGenerateProgressVo progress = documentGenerateService.getProgress(taskId);
return R.ok(progress);
}
/**
* 根据方案ID查询最新的生成任务
*/
@SaCheckPermission("tech:proposal:query")
@GetMapping("/latest/{proposalId}")
public R<DocumentGenerateProgressVo> getLatestByProposalId(@PathVariable Long proposalId) {
DocumentGenerateProgressVo progress = documentGenerateService.getLatestByProposalId(proposalId);
return R.ok(progress);
}
/**
* 下载生成的文档
*/
@SaCheckPermission("tech:proposal:query")
@GetMapping("/download/{taskId}")
public void download(@PathVariable Long taskId, HttpServletResponse response) {
documentGenerateService.download(taskId, response);
}
/**
* 预览生成的文档Word转HTML
*/
@SaCheckPermission("tech:proposal:query")
@GetMapping("/preview/{taskId}")
public R<String> previewDocument(
@PathVariable @NotNull(message = "任务ID不能为空") Long taskId,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
try {
String html = documentGenerateService.convertToHtml(taskId, pageNum, pageSize);
// 使用 R.ok(msg, data) 确保 HTML 内容放在 data 字段而不是 msg 字段
return R.ok("操作成功", html);
} catch (Exception e) {
return R.fail("文档预览失败: " + e.getMessage());
}
}
/**
* 保存前端编辑后的文档,生成一个新文件。
*/
@SaCheckPermission("tech:proposal:query")
@PostMapping("/save-edited")
public R<Long> saveEditedDocument(@RequestBody DocumentEditSaveDTO dto) {
try {
Long taskId = documentGenerateService.saveEditedDocument(dto);
return R.ok("保存任务已提交", taskId);
} catch (Exception e) {
return R.fail("保存编辑文档失败: " + e.getMessage());
}
}
/**
* 保存前端编辑后的文档,允许图片以 multipart 文件流直传。
*/
@SaCheckPermission("tech:proposal:query")
@PostMapping(value = "/save-edited", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<Long> saveEditedDocumentMultipart(@RequestPart("payload") String payload,
MultipartHttpServletRequest request) {
try {
DocumentEditSaveDTO dto = objectMapper.readValue(payload, DocumentEditSaveDTO.class);
Map<String, MultipartFile> imageFiles = request == null
? Collections.emptyMap()
: new LinkedHashMap<>(request.getFileMap());
imageFiles.remove("payload");
Long taskId = documentGenerateService.saveEditedDocument(dto, imageFiles);
return R.ok("保存任务已提交", taskId);
} catch (Exception e) {
return R.fail("保存编辑文档失败: " + e.getMessage());
}
}
/**
* 查询改写保存任务进度
*/
@SaCheckPermission("tech:proposal:query")
@GetMapping("/save-edited/progress/{taskId}")
public R<DocumentGenerateProgressVo> getSaveEditedProgress(@PathVariable Long taskId) {
DocumentGenerateProgressVo progress = documentGenerateService.getSaveEditedProgress(taskId);
return R.ok(progress);
}
}

View File

@@ -0,0 +1,61 @@
package org.dromara.review.controller;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.review.domain.OnlyOfficeCallbackRequest;
import org.dromara.review.domain.RegisterDocumentRequest;
import org.dromara.review.service.impl.OnlyOfficeService;
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;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api/onlyoffice")
@RequiredArgsConstructor
public class OnlyOfficeController {
private final OnlyOfficeService onlyOfficeService;
/**
* 注册文档映射
* 前端在编辑模式打开文档前调用,告知后端 key 与存储路径的对应关系
*/
@PostMapping("/register")
public Map<String, Object> registerDocument(@RequestBody RegisterDocumentRequest request) {
log.info("注册文档映射: key={}, filePath={}", request.getKey(), request.getFilePath());
onlyOfficeService.registerDocument(request);
Map<String, Object> result = new HashMap<>();
result.put("success", true);
return result;
}
/**
* OnlyOffice 回调接口
* 文档状态变化时OnlyOffice 服务器会调用此接口
*
* 重要:必须返回 {"error": 0},否则 OnlyOffice 会不断重试
*/
@PostMapping("/callback")
public Map<String, Object> callback(@RequestBody OnlyOfficeCallbackRequest request) {
log.info("收到 OnlyOffice 回调: status={}, key={}, users={}",
request.getStatus(), request.getKey(), request.getUsers());
try {
onlyOfficeService.handleCallback(request);
} catch (Exception e) {
log.error("处理回调失败: {}", e.getMessage(), e);
// 即使出错也返回成功,避免 OnlyOffice 无限重试
}
// 必须返回 {"error": 0}
Map<String, Object> result = new HashMap<>();
result.put("error", 0);
return result;
}
}

View File

@@ -0,0 +1,105 @@
package org.dromara.review.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.review.domain.vo.RiskSettingsVo;
import org.dromara.review.domain.bo.RiskSettingsBo;
import org.dromara.review.service.IRiskSettingsService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
* 风险设置通用
*
* @author Lion Li
* @date 2025-11-18
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/review/settings")
public class RiskSettingsController extends BaseController {
private final IRiskSettingsService riskSettingsService;
/**
* 查询风险设置通用列表
*/
@SaCheckPermission("review:settings:list")
@GetMapping("/list")
public TableDataInfo<RiskSettingsVo> list(RiskSettingsBo bo, PageQuery pageQuery) {
return riskSettingsService.queryPageList(bo, pageQuery);
}
/**
* 导出风险设置通用列表
*/
@SaCheckPermission("review:settings:export")
@Log(title = "风险设置通用", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(RiskSettingsBo bo, HttpServletResponse response) {
List<RiskSettingsVo> list = riskSettingsService.queryList(bo);
ExcelUtil.exportExcel(list, "风险设置通用", RiskSettingsVo.class, response);
}
/**
* 获取风险设置通用详细信息
*
* @param id 主键
*/
@SaCheckPermission("review:settings:query")
@GetMapping("/{id}")
public R<RiskSettingsVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(riskSettingsService.queryById(id));
}
/**
* 新增风险设置通用
*/
@SaCheckPermission("review:settings:add")
@Log(title = "风险设置通用", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody RiskSettingsBo bo) {
return toAjax(riskSettingsService.insertByBo(bo));
}
/**
* 修改风险设置通用
*/
@SaCheckPermission("review:settings:edit")
@Log(title = "风险设置通用", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody RiskSettingsBo bo) {
return toAjax(riskSettingsService.updateByBo(bo));
}
/**
* 删除风险设置通用
*
* @param ids 主键串
*/
@SaCheckPermission("review:settings:remove")
@Log(title = "风险设置通用", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(riskSettingsService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@@ -0,0 +1,167 @@
package org.dromara.review.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.review.domain.vo.TechProposalVo;
import org.dromara.review.domain.bo.TechProposalBo;
import org.dromara.review.service.ITechProposalService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import java.util.Map;
/**
* 技术标方案Controller
*
* @author Claude
* @date 2026-03-04
*/
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/tech/proposal")
public class TechProposalController extends BaseController {
private final ITechProposalService techProposalService;
/**
* 查询技术标方案列表
*/
@SaCheckPermission("tech:proposal:list")
@GetMapping("/list")
public TableDataInfo<TechProposalVo> list(TechProposalBo bo, PageQuery pageQuery) {
return techProposalService.queryPageList(bo, pageQuery);
}
/**
* 查询技术标方案列表(不分页)
*/
@SaCheckPermission("tech:proposal:list")
@PostMapping("/queryList")
public R<List<TechProposalVo>> queryList(@RequestBody TechProposalBo bo) {
return R.ok(techProposalService.queryList(bo));
}
/**
* 导出技术标方案列表
*/
@SaCheckPermission("tech:proposal:export")
@Log(title = "技术标方案", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(TechProposalBo bo, HttpServletResponse response) {
List<TechProposalVo> list = techProposalService.queryList(bo);
ExcelUtil.exportExcel(list, "技术标方案", TechProposalVo.class, response);
}
/**
* 获取技术标方案详细信息
*
* @param id 主键
*/
@SaCheckPermission("tech:proposal:query")
@GetMapping("/{id}")
public R<TechProposalVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(techProposalService.queryById(id));
}
/**
* 新增技术标方案
*/
@SaCheckPermission("tech:proposal:add")
@Log(title = "技术标方案", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody TechProposalBo bo) {
return toAjax(techProposalService.insertByBo(bo));
}
/**
* 修改技术标方案
*/
@SaCheckPermission("tech:proposal:edit")
@Log(title = "技术标方案", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody TechProposalBo bo) {
return toAjax(techProposalService.updateByBo(bo));
}
/**
* 删除技术标方案
*
* @param ids 主键串
*/
@SaCheckPermission("tech:proposal:remove")
@Log(title = "技术标方案", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(techProposalService.deleteWithValidByIds(List.of(ids), true));
}
/**
* 保存方案的目录选择
*/
@SaCheckPermission("tech:proposal:edit")
@Log(title = "保存方案目录", businessType = BusinessType.UPDATE)
@PostMapping("/{proposalId}/catalogue")
public R<Void> saveCatalogue(
@PathVariable Long proposalId,
@RequestBody Map<String, Object> params
) {
Long catalogueId = Long.valueOf(params.get("catalogueId").toString());
String catalogueJson = params.get("catalogueJson").toString();
techProposalService.saveCatalogue(proposalId, catalogueId, catalogueJson);
return R.ok();
}
/**
* 设置方案的样式规则
*/
@SaCheckPermission("tech:proposal:edit")
@Log(title = "设置样式规则", businessType = BusinessType.UPDATE)
@PostMapping("/{proposalId}/style-rule")
public R<Void> setStyleRule(
@PathVariable Long proposalId,
@RequestParam Long styleRuleId
) {
techProposalService.setStyleRule(proposalId, styleRuleId);
return R.ok();
}
/**
* 查询方案生成进度
*/
@SaCheckPermission("tech:proposal:query")
@GetMapping("/{proposalId}/generate-progress")
public R<TechProposalVo> getGenerateProgress(@PathVariable Long proposalId) {
return R.ok(techProposalService.getGenerateProgress(proposalId));
}
/**
* Ai改写
*/
@SaCheckPermission("tech:proposal:query")
@PostMapping("/reWrite")
public R<String> reWrite(@RequestBody TechProposalBo bo) {
return R.ok(techProposalService.reWrite(bo));
}
}

View File

@@ -0,0 +1,42 @@
package org.dromara.review.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serial;
import java.util.Date;
/**
* AI检查任务临时缓存表替代Redis存储任务中间状态
*/
@Data
@TableName("ai_check_task_temp_cache")
public class AiCheckTaskTempCache {
@Serial
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 缓存名称对应Redis key如 task:123:issuesInfoMap
*/
private String cacheName;
/**
* 缓存键对应Redis hash field如 proposalId:dtlId、resultKeys、value
*/
private String cacheKey;
/**
* 缓存值JSON序列化字符串
*/
private String cacheValue;
private Date createTime;
private Date updateTime;
}

View File

@@ -0,0 +1,63 @@
package org.dromara.review.domain;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* AI提示词对象 ai_prompt
*
* @author Lion Li
* @date 2025-12-22
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("ai_prompt")
public class AiPrompt extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 提示词类型system系统提示词、user用户提示词
*/
private String promptType;
/**
* 提示词名称,用于标识和管理
*/
private String promptName;
/**
* 提示词内容
*/
private String promptContent;
/**
* 是否启用1-启用0-禁用
*/
private Long isEnabled;
/**
* 提示词描述
*/
private String description;
/**
* 版本号,用于版本管理
*/
@Version
private Long version;
}

View File

@@ -0,0 +1,77 @@
package org.dromara.review.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 技术标规律性分析通用规则设置对象 bidding_analysis_common_rule_config
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bidding_analysis_common_rule_config")
public class BiddingAnalysisCommonRuleConfig extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/**
* 检查重复内容0-关闭1-开启)
*/
private Long checkDuplicateContent;
/**
* 重复内容阈值(%)
*/
private Long duplicateContentThreshold;
/**
* 检查重点信息0-关闭1-开启)
*/
private Long checkKeyInformation;
/**
* 重点信息AI检查0-关闭1-开启)
*/
private Long keyInformationAi;
/**
* 智能过滤
*/
private String smartFilter;
/**
* 检查相似图片0-关闭1-开启)
*/
private Long checkSimilarImages;
/**
* 检查图片中相同文字及重点信息0-关闭1-开启)
*/
private Long checkImageText;
/**
* 检查文件属性0-关闭1-开启)
*/
private Boolean checkFileProperties;
/**
* 删除标记0-正常1-删除)
*/
@TableLogic
private Long delFlag;
}

View File

@@ -0,0 +1,105 @@
package org.dromara.review.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 技术标纪律性分析结果对象 bidding_analysis_result
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bidding_analysis_result")
public class BiddingAnalysisResult extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主表Id
*/
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/**
* 标段信息ID
*/
private Long infoId;
/**
* 投标文件id
*/
private Long proposalId1;
/**
* 投标文件id
*/
private Long proposalId2;
/**
* 投标文件明细id
*/
private Long proposalDtlId1;
/**
* 投标文件明细id
*/
private Long proposalDtlId2;
/**
* 风险度
*/
private Long riskDegree;
/**
* 文本相似度
*/
private Long textSimilarity;
/**
* 改写文本相似度
*/
private Long rewriteTextSimilarity;
/**
* 图片相似度
*/
private Long imgSimilarity;
/**
* 文件属性雷同
*/
private Long docSimilarity;
/**
* 重点信息雷同
*/
private Long informationSimilarity;
/**
* 改写风险度
*/
private Long rewriteRiskDegree;
/**
* 删除标记
*/
@TableLogic
private Long delFlag;
private String proposal1Content;
private String proposal2Content;
private Long taskId;
}

View File

@@ -0,0 +1,66 @@
package org.dromara.review.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 技术标纪律性检查结果对象 bidding_analysis_result_dtl
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bidding_analysis_result_dtl")
public class BiddingAnalysisResultDtl extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 分析结果ID
*/
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/**
* 检查结果ID
*/
private Long parentId;
/**
* 检查结果
*/
private String content;
/**
* 删除标记
*/
@TableLogic
private Long delFlag;
/**
* 检查结果文本 1-重点信息 2文件属性 3-文本 4-图片
* */
private Integer level;
private Long ossId1;
private Long ossId2;
private Boolean flag;
private Long pdfOssId1;
private Long pdfOssId2;
private Long proposalDtlId1;
private Long proposalDtlId2;
}

View File

@@ -0,0 +1,47 @@
package org.dromara.review.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 技术标规律性分析规则设置对象 bidding_analysis_rule_config
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bidding_analysis_rule_config")
public class BiddingAnalysisRuleConfig extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/**
* 关联的投标信息ID来自bidding_info表
*/
private Long infoId;
/**
* 规则定义
*/
private String content;
/**
* 删除标记0-正常1-删除)
*/
@TableLogic
private Long delFlag;
}

View File

@@ -0,0 +1,59 @@
package org.dromara.review.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 技术内容样式检查通用规则设置对象 bidding_content_common_rule
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bidding_content_common_rule")
public class BiddingContentCommonRule extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/**
* 配置名称
*/
private String configName;
/**
* 规则内容
*/
private String checkConfig;
/**
* 删除标记
*/
@TableLogic
private Long delFlag;
private Integer templateType;
/**
* 锁定状态0解锁 1锁定
*/
private Integer lockStatus;
private Integer type;
private String sector;
}

View File

@@ -0,0 +1,72 @@
package org.dromara.review.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 技术标内容样式检查结果对象 bidding_content_result
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bidding_content_result")
public class BiddingContentResult extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主表Id
*/
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/**
* 标段信息ID
*/
private Long infoId;
/**
* 存疑文档数
*/
private Long suspiciousDocCount;
/**
* 问题数量
*/
private Long issueCount;
/**
* 已修复数量
*/
private Long fixedCount;
/**
* 修复后问题数量
*/
private Long fixedIssueCount;
/**
* 删除标记
*/
@TableLogic
private Long delFlag;
private String proposal1Content;
private String proposal2Content;
private Long proposalId;
private Long taskId;
}

View File

@@ -0,0 +1,74 @@
package org.dromara.review.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.tenant.core.TenantEntity;
import java.io.Serial;
/**
* 技术标内容检查结果对象 bidding_content_result_dtl
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bidding_content_result_dtl")
public class BiddingContentResultDtl extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 分析结果ID
*/
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/**
* 检查结果ID
*/
private Long parentId;
/**
* 文档内容(原始文本)
*/
private String documentContent;
private String fixedContent;
/**
* 删除标记
*/
@TableLogic
private Long delFlag;
/**
* 检查结果文本 1-重点信息 2文件属性 3-文本 4-图片
* */
private Integer level;
// private Long ossId1;
// private Long ossId2;
private Long proposalDtlId;
private Integer issueCount;
private Integer fixedIssueCount;
// private Integer fixedCount;
// private String smallContent;
private Long ossId;
private Boolean fixedFlag;
private String levelName;
}

View File

@@ -0,0 +1,51 @@
package org.dromara.review.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 技术标内容检查规则设置对象 bidding_content_rule
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bidding_content_rule")
public class BiddingContentRule extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/**
* 规则定义
*/
private String content;
/**
* 关联的投标信息ID来自bidding_info表
*/
private Long infoId;
/**
* 删除标记0-正常1-删除)
*/
@TableLogic
private Long delFlag;
private String ruleName;
private Long commonId;
}

View File

@@ -0,0 +1,46 @@
package org.dromara.review.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.tenant.core.TenantEntity;
import java.io.Serial;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bidding_document_record")
public class BiddingDocumentRecord extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id",type = IdType.AUTO)
private Long id;
private Long infoId;
private Long proposalDtlId;
private Long pdfOssId;
private Long newPdfOssId;
private Long ossId;
private Long newOssId;
private Integer status;
private Integer version;
/**
* 删除标记逻辑删除字段用于标记数据的逻辑删除状态0-正常1-删除
*/
@TableLogic
private Long delFlag;
}

View File

@@ -0,0 +1,72 @@
package org.dromara.review.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
/**
* 投标信息对象 bidding_info
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bidding_info")
public class BiddingInfo extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 序号,主键
*/
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/**
* 清标名称
*/
private String sectionName;
/**
* 清标编号
*/
private String clearingNumber;
/**
* 投标单位家数
*/
private Long bidderCount;
/**
* 最后分析时间
*/
private Date lastAnalysisTime;
/**
* 操作状态
*/
private Integer operationStatus;
/**
* 招标文件id
*/
private Long biddingDocuId;
/**
* 删除标记逻辑删除字段用于标记数据的逻辑删除状态0-正常1-删除
*/
@TableLogic
private Long delFlag;
private String tenantId;
}

View File

@@ -0,0 +1,65 @@
package org.dromara.review.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.tenant.core.TenantEntity;
import java.io.Serial;
/**
* 标段招标文件对象 bidding_info_docu
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bidding_info_docu")
public class BiddingInfoDocu extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 标段ID对应 bidding_info.id
*/
private Long infoId;
/**
* 招标文件OSS ID对应 sys_oss.oss_id
*/
private Long ossId;
/**
* 文件名称
*/
private String docuName;
/**
* 解析规则
*/
private String ruleContent;
/**
* 排序
*/
private Integer sortNum;
/**
* 0-招标文件 1-补遗文件 2-其他文件
*/
private Integer type;
/**
* 逻辑删除 0正常 1删除
*/
@TableLogic
private Long delFlag;
}

View File

@@ -0,0 +1,49 @@
package org.dromara.review.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 投标文件对象 bidding_proposal
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bidding_proposal")
public class BiddingProposal extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 投标单位ID
*/
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/**
* 标段ID
*/
private String name;
/**
* 删除标记
*/
@TableLogic
private Long delFlag;
/**
*主表Id
*/
private Long infoId;
private String tenantId;
}

View File

@@ -0,0 +1,57 @@
package org.dromara.review.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 投标文件对象 bidding_proposal_dtl
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bidding_proposal_dtl")
public class BiddingProposalDtl extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 序号,主键
*/
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/**
* 投标单位名称
*/
private String name;
/**
* 投标单位id
*/
private Long proposalId;
/**
* 删除标记逻辑删除字段用于标记数据的逻辑删除状态0-正常1-删除
*/
@TableLogic
private Long delFlag;
/**
* 文件id
*/
private Long ossId;
/**
* PDF文件id
*/
private Long pdfOssId;
}

View File

@@ -0,0 +1,47 @@
package org.dromara.review.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 技术标响应式通用规则对象 bidding_response_content_common_rule
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bidding_response_content_common_rule")
public class BiddingResponseContentCommonRule extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主表Id
*/
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/**
*
*/
private Long parentId;
/**
* 规则名称
*/
private String name;
/**
* 删除标记
*/
@TableLogic
private Long delFlag;
}

View File

@@ -0,0 +1,59 @@
package org.dromara.review.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 技术标响应式检查结果对象 bidding_response_content_result
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bidding_response_content_result")
public class BiddingResponseContentResult extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主表Id
*/
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/**
* 标段信息ID
*/
private Long infoId;
/**
* 文件id
*/
private Long ossId;
/**
* 检查内容
*/
private String checkContent;
/**
* 检查项名称
*/
private String checkItem;
/**
* 删除标记
*/
@TableLogic
private Long delFlag;
private Long taskId;
}

View File

@@ -0,0 +1,61 @@
package org.dromara.review.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.tenant.core.TenantEntity;
import java.io.Serial;
/**
* 技术标内容检查结果对象 bidding_content_result_dtl
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bidding_response_content_result_dtl")
public class BiddingResponseContentResultDtl extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 分析结果ID
*/
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/**
* 检查结果ID
*/
private Long parentId;
/**
* 检查结果
*/
private String content;
/**
* 删除标记
*/
@TableLogic
private Long delFlag;
private Long proposalId;
private Long ossId1;
private Long ossId2;
private Integer factorId;
@TableField("subItem_id")
private Integer subItemId;
private Boolean plan;
}

View File

@@ -0,0 +1,47 @@
package org.dromara.review.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 技术标响应式规则设置对象 bidding_response_content_rule
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bidding_response_content_rule")
public class BiddingResponseContentRule extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主表Id
*/
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/**
*
*/
private Long infoId;
/**
* 规则定义
*/
private String content;
/**
* 删除标记
*/
@TableLogic
private Long delFlag;
}

View File

@@ -0,0 +1,80 @@
package org.dromara.review.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.io.Serial;
import java.util.Date;
/**
* 技术标审查任务对象 bidding_review_task
*
* @author Lion Li
* @date 2025-12-02
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bidding_review_task")
public class BiddingReviewTask extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
*
*/
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/**
* 任务类型ANALYSIS/CONTENT/RESPONSE
*/
private String taskType;
/**
* 任务参数JSON
*/
private String taskParams;
/**
* 状态PENDING/IN_PROGRESS/COMPLETED/FAILED/TIMEOUT
*/
private String status;
/**
*
*/
private Date startTime;
/**
*
*/
private Date endTime;
/**
*
*/
private String errorMsg;
/**
* 超时时间(秒)
*/
private Long timeout;
/**
*
*/
private Long retryCount;
private Integer infoId;
private Integer delFlag;
}

View File

@@ -0,0 +1,237 @@
package org.dromara.review.domain;
import lombok.Data;
/**
* 文档检查配置实体类。
*
* 与 ai_check 模块的 CheckConfig 字段保持一致,
* 便于直接复用样式规则 JSON 配置。
*/
@Data
public class CheckConfig {
private DefaultChecksConfig defaultChecks;
private ProhibitedItemsConfig prohibitedItems;
private TocRequirementsConfig tocRequirements;
private PageRequirementsConfig pageRequirements;
private ColorRequirementsConfig colorRequirements;
private ImageRequirementsConfig imageRequirements;
private PaperRequirementsConfig paperRequirements;
private TableRequirementsConfig tableRequirements;
private ContentRequirementsConfig contentRequirements;
private PageCountRequirementsConfig pageCountRequirements;
private SensitiveContentCheckConfig sensitiveContentCheck;
private PageNumberRequirementsConfig pageNumberRequirements;
@Data
public static class DefaultChecksConfig {
private Boolean enable;
private SpecialRequirementsConfig specialRequirements;
private FontRequirementsConfig fontRequirements;
private ParagraphRequirementsConfig paragraphRequirements;
}
@Data
public static class SpecialRequirementsConfig {
private Boolean disallowHyperlinks;
private Boolean disallowShapes;
private Boolean disallowWatermark;
}
@Data
public static class FontRequirementsConfig {
private Boolean allowedColorEnable;
private String allowedColor;
private Boolean disallowBold;
private Boolean disallowItalic;
private Boolean disallowUnderline;
private Boolean disallowStrikethrough;
private Boolean disallowEmphasisMark;
private String fontSize;
private Boolean fontSizeEnable;
private String chineseFont;
private Boolean chineseFontEnable;
private String westernFont;
private Boolean westernFontEnable;
}
@Data
public static class ParagraphRequirementsConfig {
private String alignment;
private Boolean alignmentEnable;
private String leftIndent;
private Boolean leftIndentEnable;
private String rightIndent;
private Boolean rightIndentEnable;
private String firstLineIndent;
private Boolean firstLineIndentEnable;
private String lineSpacing;
private Boolean lineSpacingEnable;
private String spacingBefore;
private Boolean spacingBeforeEnable;
private String spacingAfter;
private Boolean spacingAfterEnable;
private Boolean outlineLevelBodyText;
private Boolean requireDocumentGrid;
private String requireSnapToGrid;
}
@Data
public static class ProhibitedItemsConfig {
private Boolean enable;
private Boolean disallowSpaces;
private Boolean disallowBlankLines;
private Boolean disallowBlankPages;
private Boolean disallowHalfwidthPunctuation;
}
@Data
public static class TocRequirementsConfig {
private Boolean enable;
private Boolean noTocRequirements;
private Boolean tocHidePageNumbers;
private Boolean disallowToc;
private FontRequirementsConfig font;
private ParagraphRequirementsConfig paragraph;
}
@Data
public static class PageRequirementsConfig {
private Boolean enable;
private MarginsConfig margins;
private HeaderFooterConfig headerFooter;
}
@Data
public static class MarginsConfig {
private Double topMargin;
private Boolean topMarginEnable;
private Double bottomMargin;
private Boolean bottomMarginEnable;
private Double leftMargin;
private Boolean leftMarginEnable;
private Double rightMargin;
private Boolean rightMarginEnable;
}
@Data
public static class HeaderFooterConfig {
private Boolean noHeader;
private Boolean noFooter;
private Boolean noPageBorders;
private Double headerDistance;
private Boolean headerDistanceEnable;
private Double footerDistance;
private Boolean footerDistanceEnable;
}
@Data
public static class ColorRequirementsConfig {
private Boolean enable;
private Boolean noColorImages;
private Boolean noColorContent;
private Boolean noTableShading;
private Boolean whiteColorPage;
private Boolean noColorGraphics;
private Boolean noTableColorBorders;
}
@Data
public static class ImageRequirementsConfig {
private Boolean enable;
private Boolean disallowImages;
private Boolean noImageRequirements;
private ImagePropertiesConfig imageProperties;
private ParagraphRequirementsConfig paragraph;
private FontRequirementsConfig font;
}
@Data
public static class ImagePropertiesConfig {
private String wrapText;
private Boolean wrapTextEnable;
}
@Data
public static class PaperRequirementsConfig {
private Boolean enable;
private String paperSize;
private Boolean paperSizeEnable;
private String paperOrientation;
private Boolean paperOrientationEnable;
}
@Data
public static class TableRequirementsConfig {
private Boolean enable;
private Boolean disallowTables;
private Boolean noTableRequirements;
private TablePropertiesConfig tableProperties;
private ParagraphRequirementsConfig paragraph;
private FontRequirementsConfig font;
}
@Data
public static class TablePropertiesConfig {
private String tableAlignment;
private Boolean tableAlignmentEnable;
private Boolean borderStyleSolid;
private String borderWidth;
private Boolean borderWidthEnable;
private Boolean noTextEnvironment;
private String cellVerticalAlignment;
private Boolean cellVerticalAlignmentEnable;
}
@Data
public static class ContentRequirementsConfig {
private Boolean enable;
private FontRequirementsConfig font;
private ParagraphRequirementsConfig paragraph;
private FontRequirementsConfig title1;
private FontRequirementsConfig title2;
private FontRequirementsConfig title3;
private FontRequirementsConfig title4;
private FontRequirementsConfig imageTitle;
private FontRequirementsConfig chartName;
private FontRequirementsConfig tableTitle;
}
@Data
public static class PageCountRequirementsConfig {
private Boolean enable;
private Integer pageCount;
private Boolean pageCountEnable;
}
@Data
public static class SensitiveContentCheckConfig {
private Boolean enable;
private Boolean checkFileProperties;
private String sensitiveWords;
private Boolean sensitiveWordsEnable;
private Boolean imageCheck;
private Boolean palaceCheck;
}
@Data
public static class PageNumberRequirementsConfig {
private Boolean enable;
private Boolean disallowPageNumbers;
private Boolean noPageNumberRequirements;
private PageNumberFormatConfig pageNumberFormat;
private FontRequirementsConfig font;
}
@Data
public static class PageNumberFormatConfig {
private String range;
private Boolean rangeEnable;
private String position;
private Boolean positionEnable;
private String numberingFormat;
private Boolean numberingFormatEnable;
private Boolean continuous;
}
}

View File

@@ -0,0 +1,76 @@
package org.dromara.review.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.tenant.core.TenantEntity;
import java.io.Serial;
import java.util.Date;
/**
* 文档生成任务对象 document_generate_task
*
* @author Claude
* @date 2026-03-05
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("document_generate_task")
public class DocumentGenerateTask extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID等于方案ID
*/
@TableId(value = "id")
private Long id;
/**
* 方案ID
*/
private Long proposalId;
/**
* 任务状态: PENDING, PROCESSING, COMPLETED, FAILED
*/
private String status;
/**
* 任务进度 0-100
*/
private Integer progress;
/**
* 生成文档OSS ID
*/
private Long resultOssId;
/**
* 生成文档URL
*/
private String resultUrl;
/**
* 错误信息
*/
private String errorMsg;
/**
* 开始时间
*/
private Date startTime;
/**
* 结束时间
*/
private Date endTime;
/**
* 删除标记
*/
@TableLogic
private Long delFlag;
}

View File

@@ -0,0 +1,50 @@
package org.dromara.review.domain;
import lombok.Data;
import java.util.List;
@Data
public class OnlyOfficeCallbackRequest {
/**
* 文档状态
* 1 - 正在编辑
* 2 - 准备好保存(用户关闭文档)
* 3 - 保存出错
* 4 - 关闭无修改
* 6 - 强制保存
* 7 - 强制保存出错
*/
private Integer status;
/**
* 文档唯一标识(前端初始化时传入的 key
*/
private String key;
/**
* 修改后的文档下载地址(仅 status=2 或 6 时有值)
*/
private String url;
/**
* 当前编辑用户列表
*/
private List<String> users;
/**
* 文档修改者信息
*/
private List<Action> actions;
/**
* 历史记录(可选)
*/
private Object history;
@Data
public static class Action {
private Integer type;
private String userid;
}
}

View File

@@ -0,0 +1,21 @@
package org.dromara.review.domain;
import lombok.Data;
@Data
public class RegisterDocumentRequest {
/**
* 文档唯一标识
*/
private String key;
/**
* 文件存储路径
*/
private String filePath;
private Long infoId;
private Long proposalDtlId;
}

View File

@@ -0,0 +1,42 @@
package org.dromara.review.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 风险设置通用对象 risk_settings
*
* @author Lion Li
* @date 2025-11-18
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("risk_settings")
public class RiskSettings extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 风险分数
*/
private Long score;
/**
* 删除标记0-正常1-删除)
*/
@TableLogic
private Long delFlag;
}

View File

@@ -0,0 +1,131 @@
package org.dromara.review.domain;
import lombok.Data;
/**
* 样式规则配置对象
*
* @author Claude
* @date 2026-03-04
*/
@Data
public class StyleRuleConfig {
// ========== 纸张设置 ==========
/**
* 纸张大小A4, A3
*/
private String paperSize = "A4";
/**
* 纸张方向(纵向, 横向)
*/
private String paperOrientation = "纵向";
// ========== 页边距(厘米) ==========
/**
* 上边距(厘米)
*/
private Double topMargin = 2.5;
/**
* 下边距(厘米)
*/
private Double bottomMargin = 2.5;
/**
* 左边距(厘米)
*/
private Double leftMargin = 3.0;
/**
* 右边距(厘米)
*/
private Double rightMargin = 2.0;
// ========== 页眉页脚距离(厘米) ==========
/**
* 页眉距离(厘米)
*/
private Double headerDistance = 1.9;
/**
* 页脚距离(厘米)
*/
private Double footerDistance = 1.57;
// ========== 正文字体 ==========
/**
* 正文中文字体
*/
private String bodyChineseFont = "宋体";
/**
* 正文西文字体
*/
private String bodyWesternFont = "Times New Roman";
/**
* 正文字号(磅)
*/
private Double bodyFontSize = 12.0;
// ========== 标题1字体 ==========
/**
* 标题1中文字体
*/
private String heading1ChineseFont = "黑体";
/**
* 标题1字号
*/
private Double heading1FontSize = 16.0;
// ========== 标题2字体 ==========
/**
* 标题2中文字体
*/
private String heading2ChineseFont = "黑体";
/**
* 标题2字号
*/
private Double heading2FontSize = 14.0;
// ========== 标题3字体 ==========
/**
* 标题3中文字体
*/
private String heading3ChineseFont = "黑体";
/**
* 标题3字号
*/
private Double heading3FontSize = 12.0;
// ========== 段落格式 ==========
/**
* 行距(磅)
*/
private Double lineSpacing = 20.0;
/**
* 首行缩进(字符数)
*/
private Double firstLineIndent = 2.0;
/**
* 对齐方式(两端对齐, 左对齐, 居中)
*/
private String alignment = "两端对齐";
/**
* 段前间距(磅)
*/
private Double spacingBefore = 0.0;
/**
* 段后间距(磅)
*/
private Double spacingAfter = 0.0;
}

View File

@@ -0,0 +1,146 @@
package org.dromara.review.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 技术标方案对象 tech_proposal
*
* @author Claude
* @date 2026-03-04
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("tech_proposal")
public class TechProposal extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 项目名称
*/
private String projectName;
/**
* 方案名称
*/
private String planName;
/**
* 方案类型(专项施工方案等)
*/
private String planType;
/**
* 方案等级A级/B级/C级/D级
*/
private String planLevel;
/**
* 专业类型(桥梁工程、隧道工程等)
*/
private String majorType;
/**
* 分部分项
*/
private String partType;
/**
* 用户上传的原始文件OSS ID
*/
private Long originalFileOssId;
/**
* 原始文件URL
*/
private String originalFileUrl;
/**
* 原始文件名称
*/
private String originalFileName;
/**
* 选择的目录模板ID关联kb_folder表
*/
private Long selectedCatalogueId;
/**
* 最终确定的目录结构JSON格式
*/
private String catalogueJson;
/**
* 关联的样式规则IDbidding_content_rule表
*/
private Long styleRuleId;
/**
* 是否应用样式规则0-否1-是)
* 默认为 0不应用保留源文档格式以获得更好的效果
*/
private Integer applyStyleRule;
/**
* 是否使用自定义样式规则0-否1-是)
*/
private Integer useCustomStyle;
/**
* AI生成状态: PENDING, PROCESSING, COMPLETED, FAILED
*/
private String autoGenerateStatus;
/**
* AI生成进度 0-100
*/
private Integer autoGenerateProgress;
/**
* 当前处理步骤描述
*/
private String currentStep;
/**
* 生成文档的OSS ID
*/
private Long generatedDocOssId;
/**
* 生成文档的URL
*/
private String generatedDocUrl;
/**
* 编辑状态(编辑中/已完成)
*/
private String editStatus;
/**
* 协同状态(个人编制/协同编制)
*/
private String synergyStatus;
/**
* 删除标记
*/
@TableLogic
private Long delFlag;
/**
* 租户ID
*/
private String tenantId;
}

View File

@@ -0,0 +1,28 @@
package org.dromara.review.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.tenant.core.TenantEntity;
import java.io.Serial;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("tech_proposal_bidding")
public class TechProposalBidding extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
private Long techProposalId;
private String biddingContent;
}

View File

@@ -0,0 +1,56 @@
package org.dromara.review.domain.bo;
import org.dromara.review.domain.AiPrompt;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
/**
* AI提示词业务对象 ai_prompt
*
* @author Lion Li
* @date 2025-12-22
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = AiPrompt.class, reverseConvertGenerate = false)
public class AiPromptBo extends BaseEntity {
/**
* 主键ID
*/
private Long id;
/**
* 提示词类型system系统提示词、user用户提示词
*/
private String promptType;
/**
* 提示词名称,用于标识和管理
*/
private String promptName;
/**
* 提示词内容
*/
private String promptContent;
/**
* 是否启用1-启用0-禁用
*/
private Long isEnabled;
/**
* 提示词描述
*/
@NotBlank(message = "提示词描述不能为空", groups = { AddGroup.class, EditGroup.class })
private String description;
}

View File

@@ -0,0 +1,44 @@
package org.dromara.review.domain.bo;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Data
public class BatchParagraphUpdateDTO {
private Long infoId;
/** 当前编辑的纪律性分析结果主表ID */
private Long id;
private Long taskId;
// 文件1的信息
private Long ossId;
private List<ParagraphModification> modifications;
private Long proposalDtlId;
// 文件2的信息
private Long ossId2;
private List<ParagraphModification> modifications2;
private Long proposalDtlId2;
private Boolean saveAsNew = false;
private String newFileName;
@Data
public static class ParagraphModification {
private String paragraphId;
private Integer pageNum;
private Integer paraIndex;
private String content;
/** bidding_analysis_result_dtl.id有值时同步更新改写结果到明细表 */
private Long id;
/** 该段落所属文件的 OSS ID兼容旧参数当前分组逻辑不再依赖该字段 */
private Long ossId;
/** 该段落所属文件对应的 BiddingProposalDtl.id */
private Long proposalDtlId;
/** 标识该段落属于哪个文件1=文件1rewrittenContent12=文件2rewrittenContent2 */
private Integer isFile;
}
}

View File

@@ -0,0 +1,77 @@
package org.dromara.review.domain.bo;
import org.dromara.review.domain.BiddingAnalysisCommonRuleConfig;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
/**
* 技术标规律性分析通用规则设置业务对象 bidding_analysis_common_rule_config
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = BiddingAnalysisCommonRuleConfig.class, reverseConvertGenerate = false)
public class BiddingAnalysisCommonRuleConfigBo extends BaseEntity {
/**
* 主键ID
*/
private Long id;
/**
* 检查重复内容0-关闭1-开启)
*/
@NotNull(message = "检查重复内容0-关闭1-开启)不能为空", groups = { AddGroup.class, EditGroup.class })
private Long checkDuplicateContent;
/**
* 重复内容阈值(%)
*/
@NotNull(message = "重复内容阈值(%)不能为空", groups = { AddGroup.class, EditGroup.class })
private Long duplicateContentThreshold;
/**
* 检查重点信息0-关闭1-开启)
*/
@NotNull(message = "检查重点信息0-关闭1-开启)不能为空", groups = { AddGroup.class, EditGroup.class })
private Long checkKeyInformation;
/**
* 重点信息AI检查0-关闭1-开启)
*/
@NotNull(message = "重点信息AI检查0-关闭1-开启)不能为空", groups = { AddGroup.class, EditGroup.class })
private Long keyInformationAi;
/**
* 智能过滤
*/
@NotBlank(message = "智能过滤不能为空", groups = { AddGroup.class, EditGroup.class })
private String smartFilter;
/**
* 检查相似图片0-关闭1-开启)
*/
@NotNull(message = "检查相似图片0-关闭1-开启)不能为空", groups = { AddGroup.class, EditGroup.class })
private Long checkSimilarImages;
/**
* 检查图片中相同文字及重点信息0-关闭1-开启)
*/
@NotNull(message = "检查图片中相同文字及重点信息0-关闭1-开启)不能为空", groups = { AddGroup.class, EditGroup.class })
private Long checkImageText;
/**
* 检查文件属性0-关闭1-开启)
*/
@NotNull(message = "检查文件属性0-关闭1-开启)不能为空", groups = { AddGroup.class, EditGroup.class })
private Boolean checkFileProperties;
}

View File

@@ -0,0 +1,132 @@
package org.dromara.review.domain.bo;
import org.dromara.review.domain.BiddingAnalysisResult;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.List;
/**
* 技术标纪律性分析结果业务对象 bidding_analysis_result
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = BiddingAnalysisResult.class, reverseConvertGenerate = false)
public class BiddingAnalysisResultBo extends BaseEntity {
/**
* 主表Id
*/
private Long id;
/**
* 标段信息ID
*/
private Long infoId;
/**
* 投标文件id
*/
@NotNull(message = "投标文件id不能为空", groups = { AddGroup.class, EditGroup.class })
private Long proposalId1;
/**
* 投标文件id
*/
@NotNull(message = "投标文件id不能为空", groups = { AddGroup.class, EditGroup.class })
private Long proposalId2;
/**
* 投标文件明细id
*/
private Long proposalDtlId1;
/**
* 投标文件明细id
*/
private Long proposalDtlId2;
private List<Long> proposalDtlIds;
/**
* 左侧多选的投标文件明细ID
*/
private List<Long> proposalDtlIds1;
/**
* 右侧多选的投标文件明细ID
*/
private List<Long> proposalDtlIds2;
/**
* 风险度
*/
@NotNull(message = "风险度不能为空", groups = { AddGroup.class, EditGroup.class })
private Long riskDegree;
/**
* 文本相似度
*/
@NotNull(message = "文本相似度不能为空", groups = { AddGroup.class, EditGroup.class })
private Long textSimilarity;
/**
* 改写文本相似度
*/
@NotNull(message = "改写文本相似度不能为空", groups = { AddGroup.class, EditGroup.class })
private Long rewriteTextSimilarity;
/**
* 图片相似度
*/
@NotNull(message = "图片相似度不能为空", groups = { AddGroup.class, EditGroup.class })
private Long imgSimilarity;
/**
* 文件属性雷同
*/
@NotNull(message = "文件属性雷同不能为空", groups = { AddGroup.class, EditGroup.class })
private Long docSimilarity;
/**
* 重点信息雷同
*/
@NotNull(message = "重点信息雷同不能为空", groups = { AddGroup.class, EditGroup.class })
private Long informationSimilarity;
/**
* 改写风险度
*/
@NotNull(message = "改写风险度不能为空", groups = { AddGroup.class, EditGroup.class })
private Long rewriteRiskDegree;
private List<Long> proposalIds;
private String ruleJson;
private String smartFilterConfig;
private String proposal1Content;
private String proposal2Content;
private Long biddingDocuId;
private Long userId;
private Long taskId;
}

View File

@@ -0,0 +1,51 @@
package org.dromara.review.domain.bo;
import org.dromara.review.domain.BiddingAnalysisResultDtl;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
/**
* 技术标检查结果业务对象 bidding_result
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = BiddingAnalysisResultDtl.class, reverseConvertGenerate = false)
public class BiddingAnalysisResultDtlBo extends BaseEntity {
/**
* 分析结果ID
*/
private Long id;
/**
* 检查结果ID
*/
private Long parentId;
/**
* 检查结果
*/
@NotBlank(message = "检查结果不能为空", groups = { AddGroup.class, EditGroup.class })
private String content;
private String originalContent1;
private String originalContent2;
private Long level;
private Boolean flag;
private Long proposalDtlId1;
private Long proposalDtlId2;
}

View File

@@ -0,0 +1,12 @@
package org.dromara.review.domain.bo;
import lombok.Data;
@Data
public class BiddingAnalysisResultRewrite {
private String content;
private String content2;
private Long infoId;
private Integer percent;
}

View File

@@ -0,0 +1,40 @@
package org.dromara.review.domain.bo;
import org.dromara.review.domain.BiddingAnalysisRuleConfig;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
/**
* 技术标规律性分析规则设置业务对象 bidding_analysis_rule_config
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = BiddingAnalysisRuleConfig.class, reverseConvertGenerate = false)
public class BiddingAnalysisRuleConfigBo extends BaseEntity {
/**
* 主键ID
*/
private Long id;
/**
* 关联的投标信息ID来自bidding_info表
*/
private Long infoId;
/**
* 规则定义
*/
@NotBlank(message = "规则定义不能为空", groups = { AddGroup.class, EditGroup.class })
private String content;
}

View File

@@ -0,0 +1,66 @@
package org.dromara.review.domain.bo;
import org.dromara.review.domain.BiddingContentCommonRule;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.List;
/**
* 技术内容样式检查通用规则设置业务对象 bidding_content_common_rule
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = BiddingContentCommonRule.class, reverseConvertGenerate = false)
public class BiddingContentCommonRuleBo extends BaseEntity {
/**
* 主键ID
*/
private Long id;
/**
* 配置名称
*/
private String configName;
private List<Long> proposalIds;
private Long infoId;
private Long userId;
//bidding_content_result表的id
private Long resultId;
private String checkConfig;
private Long taskId;
private Integer templateType;
/**
* 锁定状态0解锁 1锁定
*/
private Integer lockStatus;
private Integer type;
private String sector;
}

View File

@@ -0,0 +1,72 @@
package org.dromara.review.domain.bo;
import org.dromara.review.domain.BiddingContentResult;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
/**
* 技术标内容样式检查结果业务对象 bidding_content_result
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = BiddingContentResult.class, reverseConvertGenerate = false)
public class BiddingContentResultBo extends BaseEntity {
/**
* 主表Id
*/
private Long id;
/**
* 标段信息ID
*/
private Long infoId;
/**
* 存疑文档数
*/
@NotNull(message = "存疑文档数不能为空", groups = { AddGroup.class, EditGroup.class })
private Long suspiciousDocCount;
/**
* 问题数量
*/
@NotNull(message = "问题数量不能为空", groups = { AddGroup.class, EditGroup.class })
private Long issueCount;
/**
* 已修复数量
*/
@NotNull(message = "已修复数量不能为空", groups = { AddGroup.class, EditGroup.class })
private Long fixedCount;
/**
* 修复后问题数量
*/
@NotNull(message = "修复后问题数量不能为空", groups = { AddGroup.class, EditGroup.class })
private Long fixedIssueCount;
private Integer proposalId;
private Long proposalDtlId;
private Long level;
private String field;
private Integer pageSize;
private Integer pageNum;
private Integer fixed;
}

View File

@@ -0,0 +1,24 @@
package org.dromara.review.domain.bo;
import lombok.Data;
@Data
public class BiddingContentResultDetailBo {
private Long id;
private String fieldKey;
private String fieldType;
private Long proposalDtlId;
private Long ossId;
private Long pdfOssId;
private String location;
private String locationPage;
private String detail;
private String page;
private String content;
private Integer rowNum;
private String correctReq;
private String paragraphId;
// 用于临时存储从数据库查询的 JSON 字符串(不返回给前端)
private String documentContent;
}

View File

@@ -0,0 +1,49 @@
package org.dromara.review.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.review.domain.BiddingContentResultDtl;
/**
* 技术标检查结果业务对象 bidding_result
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = BiddingContentResultDtl.class, reverseConvertGenerate = false)
public class BiddingContentResultDtlBo extends BaseEntity {
/**
* 分析结果ID
*/
private Long id;
/**
* 检查结果ID
*/
private Long parentId;
/**
* 文档内容(原始文本)
*/
private String documentContent;
private Long level;
private Integer issueCount;
private Long ossId;
private Boolean fixedFlag;
private String levelName;
}

View File

@@ -0,0 +1,14 @@
package org.dromara.review.domain.bo;
import lombok.Data;
@Data
public class BiddingContentResultStatisticsBo {
private Integer proposalCount; //单位数量
private Integer docCount; //文档数量
private Integer suspiciousDocCount; //文档存疑数量
private Integer issueCount; //问题数量
private Integer fixedCount; //已修复数量
}

View File

@@ -0,0 +1,45 @@
package org.dromara.review.domain.bo;
import org.dromara.review.domain.BiddingContentRule;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
/**
* 技术标内容检查规则设置业务对象 bidding_content_rule
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = BiddingContentRule.class, reverseConvertGenerate = false)
public class BiddingContentRuleBo extends BaseEntity {
/**
* 主键ID
*/
private Long id;
/**
* 规则定义
*/
@NotBlank(message = "规则定义不能为空", groups = { AddGroup.class, EditGroup.class })
private String content;
/**
* 关联的投标信息ID来自bidding_info表
*/
@NotNull(message = "关联的投标信息ID来自bidding_info表不能为空", groups = { AddGroup.class, EditGroup.class })
private Long infoId;
private String ruleName;
private Long commonId;
}

View File

@@ -0,0 +1,35 @@
package org.dromara.review.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.review.domain.BiddingContentRule;
import org.dromara.review.domain.BiddingDocumentRecord;
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = BiddingDocumentRecord.class, reverseConvertGenerate = false)
public class BiddingDocumentRecordBo extends BaseEntity {
/**
* 主键ID
*/
private Long id;
private Long infoId;
private Long ossId;
private Integer status;
private Integer version;
private Long proposalDtlId;
private Long pdfOssId;
private Long newPdfOssId;
}

View File

@@ -0,0 +1,64 @@
package org.dromara.review.domain.bo;
import org.dromara.review.domain.BiddingInfo;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
* 投标信息业务对象 bidding_info
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = BiddingInfo.class, reverseConvertGenerate = false)
public class BiddingInfoBo extends BaseEntity {
/**
* 序号,主键
*/
private Long id;
/**
* 清标名称
*/
private String sectionName;
/**
* 清标编号
*/
private String clearingNumber;
/**
* 投标单位家数
*/
private Long bidderCount;
/**
* 最后分析时间
*/
private Date lastAnalysisTime;
/**
* 操作状态
*/
private Integer operationStatus;
/**
* 招标文件id
*/
private Long biddingDocuId;
private String tenantId;
}

View File

@@ -0,0 +1,59 @@
package org.dromara.review.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.review.domain.BiddingInfoDocu;
/**
* 标段招标文件业务对象 bidding_info_docu
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = BiddingInfoDocu.class, reverseConvertGenerate = false)
public class BiddingInfoDocuBo extends BaseEntity {
/**
* 主键
*/
@NotNull(message = "主键不能为空", groups = { EditGroup.class })
private Long id;
/**
* 标段ID对应 bidding_info.id
*/
@NotNull(message = "标段ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long infoId;
/**
* 招标文件OSS ID对应 sys_oss.oss_id
*/
@NotNull(message = "招标文件OSS ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long ossId;
/**
* 文件名称
*/
@NotBlank(message = "文件名称不能为空", groups = { AddGroup.class, EditGroup.class })
private String docuName;
/**
* 解析规则
*/
private String ruleContent;
/**
* 排序
*/
private Integer sortNum;
/**
* 0-招标文件 1-补遗文件 2-其他文件
*/
private Integer type;
}

View File

@@ -0,0 +1,51 @@
package org.dromara.review.domain.bo;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 投标信息批量维护请求。
*/
@Data
public class BiddingInfoProposalBatchBo implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull(message = "infoId不能为空")
private Long infoId;
private String sectionName;
@Valid
@NotEmpty(message = "list不能为空")
private List<ProposalItem> list;
@Data
public static class ProposalItem {
private String operateType;
private Long id;
private String proposalName;
@Valid
private List<ProposalDtlItem> children;
}
@Data
public static class ProposalDtlItem {
private String operateType;
private String fileName;
private Long ossId;
}
}

View File

@@ -0,0 +1,74 @@
package org.dromara.review.domain.bo;
import lombok.*;
import org.dromara.review.domain.BiddingProposal;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.*;
import org.dromara.review.domain.BiddingProposalDtl;
import java.util.List;
/**
* 投标文件业务对象 bidding_proposal
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = BiddingProposal.class, reverseConvertGenerate = false)
public class BiddingProposalBo extends BaseEntity {
/**
* 投标单位ID
*/
private Long id;
/**
* 标段ID
*/
private String name;
/**
*
*/
@NotNull(message = "不能为空", groups = { AddGroup.class, EditGroup.class })
private Long infoId;
//文件id
private List<Long> ossId;
private String tenantId;
private List<BiddingProposalDtl> dtlList;
private List<InsertBo> proposals;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class InsertBo {
private String name;
private List<Long> ossId;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Long> getOssId() {
return ossId;
}
public void setOssId(List<Long> ossId) {
this.ossId = ossId;
}
}
}

View File

@@ -0,0 +1,46 @@
package org.dromara.review.domain.bo;
import org.dromara.review.domain.BiddingProposalDtl;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
/**
* 投标文件业务对象 bidding_proposal_dtl
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = BiddingProposalDtl.class, reverseConvertGenerate = false)
public class BiddingProposalDtlBo extends BaseEntity {
/**
* 序号,主键
*/
private Long id;
/**
* 投标单位名称
*/
private String name;
/**
* 投标单位id
*/
@NotNull(message = "投标单位id不能为空", groups = { AddGroup.class, EditGroup.class })
private Long proposalId;
/**
* 文件id
*/
@NotNull(message = "文件id不能为空", groups = { AddGroup.class, EditGroup.class })
private Long ossId;
}

View File

@@ -0,0 +1,41 @@
package org.dromara.review.domain.bo;
import org.dromara.review.domain.BiddingResponseContentCommonRule;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
/**
* 技术标响应式通用规则业务对象 bidding_response_content_common_rule
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = BiddingResponseContentCommonRule.class, reverseConvertGenerate = false)
public class BiddingResponseContentCommonRuleBo extends BaseEntity {
/**
* 主表Id
*/
private Long id;
/**
*
*/
@NotNull(message = "不能为空", groups = { AddGroup.class, EditGroup.class })
private Long parentId;
/**
* 规则名称
*/
@NotBlank(message = "规则名称不能为空", groups = { AddGroup.class, EditGroup.class })
private String name;
}

View File

@@ -0,0 +1,59 @@
package org.dromara.review.domain.bo;
import org.dromara.review.domain.BiddingResponseContentResult;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
/**
* 技术标响应式检查结果业务对象 bidding_response_content_result
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = BiddingResponseContentResult.class, reverseConvertGenerate = false)
public class BiddingResponseContentResultBo extends BaseEntity {
/**
* 主表Id
*/
private Long id;
/**
* 标段信息ID
*/
private Long infoId;
/**
* 文件id
*/
@NotNull(message = "文件id不能为空", groups = { AddGroup.class, EditGroup.class })
private Long ossId;
/**
* 检查内容
*/
private String checkContent;
/**
* 检查项名称
*/
private String checkItem;
private String ruleJson;
private Long taskId;
private String taskStatus;
private Integer factorId;
private Integer subItemId;
}

View File

@@ -0,0 +1,43 @@
package org.dromara.review.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.review.domain.BiddingResponseContentResultDtl;
/**
* 技术标检查结果业务对象 bidding_result
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = BiddingResponseContentResultDtl.class, reverseConvertGenerate = false)
public class BiddingResponseContentResultDtlBo extends BaseEntity {
/**
* 分析结果ID
*/
private Long id;
/**
* 检查结果ID
*/
private Long parentId;
/**
* 检查结果
*/
@NotBlank(message = "检查结果不能为空", groups = { AddGroup.class, EditGroup.class })
private String content;
private Long proposalId;
}

View File

@@ -0,0 +1,40 @@
package org.dromara.review.domain.bo;
import org.dromara.review.domain.BiddingResponseContentRule;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
/**
* 技术标响应式规则设置业务对象 bidding_response_content_rule
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = BiddingResponseContentRule.class, reverseConvertGenerate = false)
public class BiddingResponseContentRuleBo extends BaseEntity {
/**
* 主表Id
*/
private Long id;
/**
*
*/
private Long infoId;
/**
* 规则定义
*/
@NotBlank(message = "规则定义不能为空", groups = { AddGroup.class, EditGroup.class })
private String content;
}

View File

@@ -0,0 +1,80 @@
package org.dromara.review.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.review.domain.BiddingReviewTask;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.util.Date;
/**
* 技术标审查任务业务对象 bidding_review_task
*
* @author Lion Li
* @date 2025-12-02
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = BiddingReviewTask.class, reverseConvertGenerate = false)
public class BiddingReviewTaskBo extends BaseEntity {
/**
*
*/
private Long id;
/**
* 任务类型ANALYSIS/CONTENT/RESPONSE
*/
private String taskType;
/**
* 任务参数JSON
*/
private String taskParams;
/**
* 状态PENDING/IN_PROGRESS/COMPLETED/FAILED/TIMEOUT
*/
private String status;
/**
*
*/
@NotNull(message = "不能为空", groups = { AddGroup.class, EditGroup.class })
private Date startTime;
/**
*
*/
@NotNull(message = "不能为空", groups = { AddGroup.class, EditGroup.class })
private Date endTime;
/**
*
*/
@NotBlank(message = "不能为空", groups = { AddGroup.class, EditGroup.class })
private String errorMsg;
/**
* 超时时间(秒)
*/
private Long timeout;
/**
*
*/
private Long retryCount;
private Integer infoId;
private Integer delFlag;
}

View File

@@ -0,0 +1,95 @@
package org.dromara.review.domain.bo;
/**
* 配置字段节点DTO
*/
public class ConfigFieldNode {
/**
* 字段中文名称
*/
private String name;
/**
* 字段英文名称
*/
private String field;
/**
* 字段ID可以自定义或使用某种规则生成
*/
private Integer id;
private Integer num;
private Boolean flag; // 新增字段用于标识是否存在于issueCountList中
private String correctReq;
// 构造方法
public ConfigFieldNode() {}
public ConfigFieldNode(String name, String field, Integer id, Integer num, Boolean flag) {
this.name = name;
this.field = field;
this.id = id;
this.num = num;
this.flag = flag;
}
public ConfigFieldNode(String name, String field, Integer id, Integer num, Boolean flag,String correctReq) {
this.name = name;
this.field = field;
this.id = id;
this.num = num;
this.flag = flag;
this.correctReq=correctReq;
}
// getter和setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
public Boolean getFlag() {
return flag;
}
public void setFlag(Boolean flag) {
this.flag = flag;
}
public String getCorrectReq() {
return correctReq;
}
public void setCorrectReq(String correctReq) {
this.correctReq = correctReq;
}
}

View File

@@ -0,0 +1,93 @@
package org.dromara.review.domain.bo;
import java.util.List;
/**
* 树形节点DTO
*/
public class ConfigTreeNode {
/**
* 层级对应ContentTypeEnums的type
*/
private Integer level;
/**
* 层级名称对应ContentTypeEnums的desc
*/
private String name;
private Integer id;
/**
* 子节点列表
*/
private List<ConfigFieldNode> children;
private Integer num; // 新增字段,用于存储 issue_count 问题数量
private Boolean flag; // 新增字段用于标识是否存在于issueCountList中
// 构造方法
public ConfigTreeNode() {}
public ConfigTreeNode(Integer level, String name, List<ConfigFieldNode> children,Integer id,Integer num, Boolean flag) {
this.level = level;
this.name = name;
this.children = children;
this.id = id;
this.num = num; // 默认值为0
this.flag = flag; // 默认值为false
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
// getter和setter
public Integer getLevel() {
return level;
}
public void setLevel(Integer level) {
this.level = level;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<ConfigFieldNode> getChildren() {
return children;
}
public void setChildren(List<ConfigFieldNode> children) {
this.children = children;
}
// 添加 getter 和 setter
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
public Boolean getFlag() {
return flag;
}
public void setFlag(Boolean flag) {
this.flag = flag;
}
}

View File

@@ -0,0 +1,40 @@
package org.dromara.review.domain.bo;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import java.util.List;
/**
* 直传 OSS 完成参数
*/
@Data
public class DirectOssUploadCompleteBo {
/**
* 上传任务 ID
*/
@NotBlank(message = "任务ID不能为空")
private String taskId;
/**
* 分片信息
*/
@NotEmpty(message = "分片信息不能为空")
private List<Part> parts;
@Data
public static class Part {
/**
* 分片号
*/
private Integer partNumber;
/**
* ETag
*/
private String etag;
}
}

View File

@@ -0,0 +1,29 @@
package org.dromara.review.domain.bo;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* 直传 OSS 初始化参数
*/
@Data
public class DirectOssUploadInitBo {
/**
* 原始文件名
*/
@NotBlank(message = "文件名不能为空")
private String fileName;
/**
* 文件大小(字节)
*/
@NotNull(message = "文件大小不能为空")
private Long size;
/**
* 文件内容类型
*/
private String contentType;
}

View File

@@ -0,0 +1,50 @@
package org.dromara.review.domain.bo;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Data
public class DocumentEditSaveDTO {
@NotNull(message = "任务ID不能为空")
private Long taskId;
private String newFileName;
@NotEmpty(message = "修改内容不能为空")
private List<ParagraphModification> modifications;
@Data
public static class ParagraphModification {
@NotBlank(message = "段落ID不能为空")
private String paragraphId;
private Integer pageNum;
private Integer paraIndex;
/**
* 前端编辑后的段落内容,建议传单段 HTML 片段或纯文本。
*/
private String content;
/**
* 需要替换为的新图片 OSS ID传值时表示执行图片替换。
*/
private String imageOssId;
/**
* 前端 multipart 上传图片时的文件字段名。
*/
private String imageFileKey;
/**
* 段落内图片序号1-based不传默认替换第一张。
*/
private Integer imageIndex;
}
}

View File

@@ -0,0 +1,20 @@
package org.dromara.review.domain.bo;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
/**
* 文档生成业务对象
*
* @author Claude
* @date 2026-03-05
*/
@Data
public class DocumentGenerateBo {
/**
* 方案ID
*/
@NotNull(message = "方案ID不能为空")
private Long proposalId;
}

View File

@@ -0,0 +1,37 @@
package org.dromara.review.domain.bo;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* 段落修改请求DTO
*/
@Data
public class ParagraphUpdateDTO {
@NotNull(message = "文件ID不能为空")
private Long ossId;
@NotBlank(message = "段落ID不能为空")
private String paragraphId;
@NotNull(message = "页码不能为空")
private Integer pageNum;
@NotNull(message = "段落索引不能为空")
private Integer paraIndex;
@NotBlank(message = "修改内容不能为空")
private String content;
/**
* 是否保存为新文件true=另存为新文件false=覆盖原文件)
*/
private Boolean saveAsNew = false;
/**
* 新文件名当saveAsNew=true时使用
*/
private String newFileName;
}

View File

@@ -0,0 +1,19 @@
package org.dromara.review.domain.bo;
import org.dromara.review.domain.BiddingProposal;
/**
* 投标文件组合类
*/
public class ProposalPair {
private final BiddingProposal proposal1;
private final BiddingProposal proposal2;
public ProposalPair(BiddingProposal proposal1, BiddingProposal proposal2) {
this.proposal1 = proposal1;
this.proposal2 = proposal2;
}
public BiddingProposal getProposal1() { return proposal1; }
public BiddingProposal getProposal2() { return proposal2; }
}

View File

@@ -0,0 +1,35 @@
package org.dromara.review.domain.bo;
import org.dromara.review.domain.RiskSettings;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
/**
* 风险设置通用业务对象 risk_settings
*
* @author Lion Li
* @date 2025-11-18
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = RiskSettings.class, reverseConvertGenerate = false)
public class RiskSettingsBo extends BaseEntity {
/**
* 主键ID
*/
private Long id;
/**
* 风险分数
*/
@NotNull(message = "风险分数不能为空", groups = { AddGroup.class, EditGroup.class })
private Long score;
}

View File

@@ -0,0 +1,22 @@
package org.dromara.review.domain.bo;
import java.util.List;
import java.util.Map;
public class SimilarityResult {
private final long similarityScore; // 相似度分数
private final List<Map<String, Object>> duplicateContents; // 重复内容列表
public SimilarityResult(long similarityScore, List<Map<String, Object>> duplicateContents) {
this.similarityScore = similarityScore;
this.duplicateContents = duplicateContents;
}
public long getSimilarityScore() {
return similarityScore;
}
public List<Map<String, Object>> getDuplicateContents() {
return duplicateContents;
}
}

View File

@@ -0,0 +1,37 @@
package org.dromara.review.domain.bo;
import lombok.Data;
import java.util.List;
/**
* 智能过滤的对象实体
*/
@Data
public class SmartFilterConfig {
// 忽略与招标文件相同内容
private Boolean ignoreBiddingFileContent;
// 忽略标点符号及小于6个字的内容
private Boolean ignorePunctuationAndShortText;
private Integer minTextLength; // 最小文本长度
// 忽略目录
private Boolean ignoreDirectory;
// 忽略文件中的技术标准
private Boolean ignoreTechnicalStandards;
// 忽略的重点信息类别
private String ignoreKeyInfoTypes;
private Boolean ignoreKeyInfoTypesEnable;
// 重点信息检查中忽略的词
private String ignoreSimilarWords;
private Boolean ignoreSimilarWordsEnable;
// 招标文件内容(用于对比忽略)
private String biddingFileContent;
private Long biddingDocuId;
}

View File

@@ -0,0 +1,26 @@
package org.dromara.review.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.review.domain.TechProposalBidding;
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = TechProposalBidding.class, reverseConvertGenerate = false)
public class TechProposalBiddingBo extends BaseEntity {
/**
* 主键ID
*/
@NotNull(message = "主键不能为空", groups = { EditGroup.class })
private Long id;
private Long techProposalId;
private String biddingContent;
}

View File

@@ -0,0 +1,144 @@
package org.dromara.review.domain.bo;
import org.dromara.review.domain.TechProposal;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
/**
* 技术标方案业务对象 tech_proposal
*
* @author Claude
* @date 2026-03-04
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = TechProposal.class, reverseConvertGenerate = false)
public class TechProposalBo extends BaseEntity {
/**
* 主键ID
*/
@NotNull(message = "主键不能为空", groups = { EditGroup.class })
private Long id;
/**
* 项目名称
*/
@NotBlank(message = "项目名称不能为空", groups = { AddGroup.class, EditGroup.class })
private String projectName;
/**
* 方案名称
*/
@NotBlank(message = "方案名称不能为空", groups = { AddGroup.class, EditGroup.class })
private String planName;
/**
* 方案类型(专项施工方案等)
*/
private String planType;
/**
* 方案等级A级/B级/C级/D级
*/
private String planLevel;
/**
* 专业类型(桥梁工程、隧道工程等)
*/
private String majorType;
/**
* 分部分项
*/
private String partType;
/**
* 用户上传的原始文件OSS ID
*/
private Long originalFileOssId;
/**
* 原始文件URL
*/
private String originalFileUrl;
/**
* 原始文件名称
*/
private String originalFileName;
/**
* 选择的目录模板ID关联kb_folder表
*/
private Long selectedCatalogueId;
/**
* 最终确定的目录结构JSON格式
*/
private String catalogueJson;
/**
* 关联的样式规则IDbidding_content_rule表
*/
private Long styleRuleId;
/**
* 是否应用样式规则0-否1-是)
*/
private Integer applyStyleRule;
/**
* 是否使用自定义样式规则0-否1-是)
*/
private Integer useCustomStyle;
/**
* AI生成状态: PENDING, PROCESSING, COMPLETED, FAILED
*/
private String autoGenerateStatus;
/**
* AI生成进度 0-100
*/
private Integer autoGenerateProgress;
/**
* 当前处理步骤描述
*/
private String currentStep;
/**
* 生成文档的OSS ID
*/
private Long generatedDocOssId;
/**
* 生成文档的URL
*/
private String generatedDocUrl;
/**
* 编辑状态(编辑中/已完成)
*/
private String editStatus;
/**
* 协同状态(个人编制/协同编制)
*/
private String synergyStatus;
/**
* 待改写内容
*/
private String content;
private Long userId;
private Integer isManual;
}

View File

@@ -0,0 +1,48 @@
package org.dromara.review.domain.dto;
import lombok.Data;
import java.util.List;
/**
* 目录匹配结果
*
* @author Claude
* @date 2026-03-05
*/
@Data
public class CatalogueMatchResult {
/**
* 目录节点标题
*/
private String catalogueTitle;
/**
* 匹配的文档ID
*/
private Long documentId;
/**
* 匹配的章节标题
*/
private String sectionTitle;
/**
* 匹配置信度(可选)
*/
private Double confidence;
/**
* 子节点匹配结果
*/
private List<CatalogueMatchResult> children;
public CatalogueMatchResult(String catalogueTitle) {
this.catalogueTitle = catalogueTitle;
}
public CatalogueMatchResult(String catalogueTitle, Long documentId, String sectionTitle) {
this.catalogueTitle = catalogueTitle;
this.documentId = documentId;
this.sectionTitle = sectionTitle;
}
}

View File

@@ -0,0 +1,45 @@
package org.dromara.review.domain.dto;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 文档标题节点
*
* @author Claude
* @date 2026-03-05
*/
@Data
public class DocumentHeadingNode {
/**
* 标题文本
*/
private String title;
/**
* 标题级别1-6
*/
private Integer level;
/**
* 子节点
*/
private List<DocumentHeadingNode> children;
public DocumentHeadingNode(String title, Integer level) {
this.title = title;
this.level = level;
this.children = new ArrayList<>();
}
/**
* 添加子节点
*/
public void addChild(DocumentHeadingNode child) {
if (this.children == null) {
this.children = new ArrayList<>();
}
this.children.add(child);
}
}

View File

@@ -0,0 +1,69 @@
package org.dromara.review.domain.vo;
import org.dromara.review.domain.AiPrompt;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* AI提示词视图对象 ai_prompt
*
* @author Lion Li
* @date 2025-12-22
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = AiPrompt.class)
public class AiPromptVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 提示词类型system系统提示词、user用户提示词
*/
@ExcelProperty(value = "提示词类型system", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "系=统提示词")
private String promptType;
/**
* 提示词名称,用于标识和管理
*/
@ExcelProperty(value = "提示词名称,用于标识和管理")
private String promptName;
/**
* 提示词内容
*/
@ExcelProperty(value = "提示词内容")
private String promptContent;
/**
* 是否启用1-启用0-禁用
*/
@ExcelProperty(value = "是否启用1-启用0-禁用")
private Long isEnabled;
/**
* 提示词描述
*/
@ExcelProperty(value = "提示词描述")
private String description;
}

View File

@@ -0,0 +1,117 @@
package org.dromara.review.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.dromara.review.domain.BiddingAnalysisCommonRuleConfig;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 技术标规律性分析通用规则设置视图对象 bidding_analysis_common_rule_config
*
* @author Lion Li
* @date 2025-11-12
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = BiddingAnalysisCommonRuleConfig.class)
public class BiddingAnalysisCommonRuleConfigVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 检查重复内容0-关闭1-开启)
*/
@ExcelProperty(value = "检查重复内容", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "0=-关闭1-开启")
private Long checkDuplicateContent;
/**
* 重复内容阈值(%)
*/
@ExcelProperty(value = "重复内容阈值(%)")
private Long duplicateContentThreshold;
/**
* 检查重点信息0-关闭1-开启)
*/
@ExcelProperty(value = "检查重点信息", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "0=-关闭1-开启")
private Long checkKeyInformation;
/**
* 重点信息AI检查0-关闭1-开启)
*/
@ExcelProperty(value = "重点信息AI检查", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "0=-关闭1-开启")
private Long keyInformationAi;
/**
* 智能过滤
*/
@ExcelProperty(value = "智能过滤")
private String smartFilter;
/**
* 检查相似图片0-关闭1-开启)
*/
@ExcelProperty(value = "检查相似图片", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "0=-关闭1-开启")
private Long checkSimilarImages;
/**
* 检查图片中相同文字及重点信息0-关闭1-开启)
*/
@ExcelProperty(value = "检查图片中相同文字及重点信息", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "0=-关闭1-开启")
private Long checkImageText;
/**
* 检查文件属性0-关闭1-开启)
*/
@ExcelProperty(value = "检查文件属性", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "0=-关闭1-开启")
private Boolean checkFileProperties;
/**
* 创建者
*/
private Long createBy;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
private Long delFlag;
}

Some files were not shown because too many files have changed in this diff Show More