liujie
2026-01-26 753097b2693fec43c8bbbd766b8c1b32d73d7e43
update
8个文件已添加
3个文件已修改
773 ■■■■■ 已修改文件
admin-server/pom.xml 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-server/src/main/java/com/estate/expert/admin/controller/admin/ProjectProgressController.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-server/src/main/java/com/estate/expert/admin/controller/stat/ReportController.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-server/src/main/java/com/estate/expert/admin/domain/business/projectprogress/ProjectProgress.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-server/src/main/java/com/estate/expert/admin/domain/repository/ProjectProgressMapper.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-server/src/main/java/com/estate/expert/admin/service/ProjectProgressService.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-server/src/main/java/com/estate/expert/admin/service/impl/ProjectProgressServiceImpl.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-server/src/main/java/com/estate/expert/admin/utils/wordtopdf/PdfUtils.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-server/src/main/java/com/estate/expert/admin/utils/wordtopdf/WordToPdfConverter.java 305 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-server/src/main/java/com/estate/expert/admin/utils/wordtopdf/WordToPdfTest.java 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-server/src/main/resources/mapper/ProjectProgressMapper.xml 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-server/pom.xml
@@ -23,6 +23,17 @@
    <dependencies>
        <dependency>
            <groupId>org.jodconverter</groupId>
            <artifactId>jodconverter-core</artifactId>
            <version>4.4.6</version>
        </dependency>
        <dependency>
            <groupId>org.jodconverter</groupId>
            <artifactId>jodconverter-local</artifactId>
            <version>4.4.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
admin-server/src/main/java/com/estate/expert/admin/controller/admin/ProjectProgressController.java
New file
@@ -0,0 +1,85 @@
package com.estate.expert.admin.controller.admin;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.estate.expert.admin.domain.business.accidentbrief.AccidentBrief;
import com.estate.expert.admin.domain.business.projectprogress.ProjectProgress;
import com.estate.expert.admin.domain.common.request.PageReq;
import com.estate.expert.admin.service.AccidentBriefService;
import com.estate.expert.admin.service.ProjectProgressService;
import com.estate.expert.common.rpc.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@Slf4j
@CrossOrigin
@Api(tags = "管理后台/项目进度")
@RequestMapping("/admin/project-progress")
@RestController
@Validated
public class ProjectProgressController {
    @Resource
    private ProjectProgressService projectProgressService;
    @ApiOperation(value = "列表")
    @PostMapping("/list")
    public Result list(@ApiParam(value = "项目名称") @RequestParam(value = "name", required = false) String name,
                       @ApiParam(value = "委托方id") @RequestParam(value = "clientId", required = false) Integer clientId,
                       @ApiParam(value = "时间xxxx - xxxx") @RequestParam(value = "time", required = false) String time,
                       @Validated @RequestBody PageReq pageReq) {
        Page<ProjectProgress> page = new Page<>(pageReq.getPage(), pageReq.getSize());
        Page<ProjectProgress> res = projectProgressService.pageList(page, name, clientId, time);
        return Result.success(res);
    }
    @ApiOperation(value = "详情")
    @GetMapping("/detail/{id}")
    public Result detail(@PathVariable Integer id) {
        ProjectProgress projectProgress = projectProgressService.detail(id);
        return Result.success(projectProgress);
    }
    @ApiOperation(value = "新增")
    @PostMapping("/add")
    public Result add(@Validated @RequestBody ProjectProgress projectProgress) {
        return Result.success(projectProgressService.save(projectProgress));
    }
    @ApiOperation(value = "编辑")
    @PostMapping("/edit")
    public Result edit(@Validated @RequestBody ProjectProgress projectProgress) {
        ProjectProgress res = projectProgressService.getById(projectProgress.getId());
        if (res == null) {
            return Result.error("项目进度不存在");
        }
        BeanUtils.copyProperties(projectProgress, res);
        return Result.success(projectProgressService.updateById(res));
    }
    @ApiOperation(value = "删除")
    @DeleteMapping("/delete/{id}")
    public Result delete(@PathVariable Integer id) {
        ProjectProgress res = projectProgressService.getById(id);
        if (res == null) {
            return Result.error("项目进度不存在");
        }
        res.setDeleted(1);
        return Result.success(projectProgressService.updateById(res));
    }
}
admin-server/src/main/java/com/estate/expert/admin/controller/stat/ReportController.java
@@ -5,9 +5,8 @@
import com.estate.expert.admin.service.CheckTaskService;
import com.estate.expert.admin.service.ReportService;
import com.estate.expert.admin.service.ReportTemplateService;
import com.estate.expert.admin.utils.WordToPdfConverter;
import com.estate.expert.admin.utils.wordtopdf.PdfUtils;
import com.estate.expert.admin.utils.wordtopdf.WordUtil;
import com.estate.expert.admin.utils.wordtopdf.WordToPdfTest;
import com.estate.expert.common.auth.IgnoreAuth;
import com.estate.expert.common.rpc.Result;
import fr.opensagres.poi.xwpf.converter.pdf.PdfConverter;
@@ -37,10 +36,6 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Api(tags = "管理后台/统计/报告下载")
@Slf4j
@@ -79,9 +74,10 @@
    @IgnoreAuth
    public void scoreReport(@ApiParam("检查任务ID") @RequestParam(value = "checkTaskId") Integer checkTaskId,
                          @ApiParam("报告模版ID") @RequestParam(value = "templateId") Integer templateId,
                            @ApiParam("uuid") @RequestParam(value = "uuid",required = false) String uuid,
                          HttpServletResponse response) throws IOException {
        try (var doc = reportService.scoreReport(checkTaskId, templateId)) {
            download(doc, response, "打分评价", checkTaskId, templateId,null);
            download(doc, response, "打分评价", checkTaskId, templateId,uuid);
        }
    }
@@ -102,13 +98,14 @@
    @ApiOperation("模糊评价报告PDF下载(新接口)- 通过Word链接转换")
    @GetMapping("/fuzzyReportPdfNew")
    @IgnoreAuth
    public void fuzzyReportPdfNew(@ApiParam("Word文档URL") @RequestParam(value = "wordUrl") String wordUrl,
                                  @ApiParam("文件名(可选)") @RequestParam(value = "fileName", required = false) String fileName,
    @ResponseBody
    public Result fuzzyReportPdfNew( @RequestParam(value = "uuid", required = false) String uuid,
                                  HttpServletResponse response) throws Exception {
        try {
            String test = pdfUtils.test("1769239688785.docx");
//            String test = pdfUtils.test("F:\\file\\estate\\reports\\1769239688785.docx");
            String s = WordToPdfTest.testSingleFileConversion(uuid);
            return Result.success(s);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
@@ -200,9 +197,9 @@
            out.flush();
            // 保存文件到服务器
//            if(uuid!=null && !uuid.isEmpty()){
//                saveFileToServer(docBytes, docName + ".docx",uuid);
//            }
            if(uuid!=null && !uuid.isEmpty()){
                saveFileToServer(docBytes, docName + ".docx",uuid);
            }
        } catch (Exception e) {
            log.error("文档下载失败", e);
            throw new IOException("文档下载失败: " + e.getMessage(), e);
admin-server/src/main/java/com/estate/expert/admin/domain/business/projectprogress/ProjectProgress.java
New file
@@ -0,0 +1,67 @@
package com.estate.expert.admin.domain.business.projectprogress;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Date;
/**
 * <p>
 * 项目进度
 * </p>
 *
 * @author administrator
 * @since 2026-01-26
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("project_progress")
public class ProjectProgress extends Model<ProjectProgress> {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    @TableField("client_id")
    @NotNull(message = "委托方ID不能为空")
    private Integer clientId;
    @TableField("project_id")
    @NotNull(message = "项目ID不能为空")
    private Integer projectId;
    @ApiModelProperty("进度说明")
    @NotBlank(message = "进度说明不能为空")
    private String detail;
    @TableField("time")
    @ApiModelProperty("进度更新时间")
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @NotNull(message = "进度更新时间不能为空")
    private Date time;
    @ApiModelProperty("进度url链接")
    @NotBlank(message = "进度url链接不能为空")
    private String url;
    @TableField("create_time")
    private Date createTime;
    private Integer deleted;
    @TableField(exist = false)
    @ApiModelProperty("委托方名称")
    private String clientName;
    @TableField(exist = false)
    @ApiModelProperty("项目名称")
    private String projectName;
}
admin-server/src/main/java/com/estate/expert/admin/domain/repository/ProjectProgressMapper.java
New file
@@ -0,0 +1,23 @@
package com.estate.expert.admin.domain.repository;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.estate.expert.admin.domain.business.projectprogress.ProjectProgress;
import org.apache.ibatis.annotations.Param;
/**
 * <p>
 * 项目进度 Mapper 接口
 * </p>
 *
 * @author administrator
 * @since 2026-01-26
 */
public interface ProjectProgressMapper extends BaseMapper<ProjectProgress> {
    Page<ProjectProgress> pageList(@Param("page") Page<ProjectProgress> page, @Param("name") String name, @Param("clientId") Integer clientId, @Param("sTime") String sTime, @Param("eTime") String eTime);
    ProjectProgress detail(@Param("id") Integer id);
}
admin-server/src/main/java/com/estate/expert/admin/service/ProjectProgressService.java
New file
@@ -0,0 +1,22 @@
package com.estate.expert.admin.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.estate.expert.admin.domain.business.projectprogress.ProjectProgress;
/**
 * <p>
 * 项目进度 服务类
 * </p>
 *
 * @author administrator
 * @since 2026-01-26
 */
public interface ProjectProgressService extends IService<ProjectProgress> {
    Page<ProjectProgress> pageList(Page<ProjectProgress> page, String name, Integer clientId, String time);
    ProjectProgress detail(Integer id);
}
admin-server/src/main/java/com/estate/expert/admin/service/impl/ProjectProgressServiceImpl.java
New file
@@ -0,0 +1,39 @@
package com.estate.expert.admin.service.impl;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.estate.expert.admin.domain.business.projectprogress.ProjectProgress;
import com.estate.expert.admin.domain.repository.ProjectProgressMapper;
import com.estate.expert.admin.service.ProjectProgressService;
import org.springframework.stereotype.Service;
/**
 * <p>
 * 项目进度 服务实现类
 * </p>
 *
 * @author administrator
 * @since 2026-01-26
 */
@Service
public class ProjectProgressServiceImpl extends ServiceImpl<ProjectProgressMapper, ProjectProgress> implements ProjectProgressService {
    @Override
    public Page<ProjectProgress> pageList(Page<ProjectProgress> page, String name, Integer clientId, String time) {
        String sTime = null;
        String eTime = null;
        if(time!=null && !time.isEmpty()){
            String[] times = time.split(" - ");
            sTime = times[0] + " 00:00:00";
            eTime = times[1] + " 23:59:59";
        }
        return this.baseMapper.pageList(page,name,clientId,sTime,eTime);
    }
    @Override
    public ProjectProgress detail(Integer id) {
        return this.baseMapper.detail(id);
    }
}
admin-server/src/main/java/com/estate/expert/admin/utils/wordtopdf/PdfUtils.java
@@ -27,12 +27,12 @@
    public  String wordToPdf(String url,String filePath, String fileName) {
        try {
            DocumentType documentType = DocumentType.DOC;
            if(url.contains(".docx")){
                documentType = DocumentType.DOCX;
            }
            if(url.contains(".doc")){
                documentType = DocumentType.DOC;
            }
            if(url.contains(".docx")){
                documentType = DocumentType.DOCX;
            }
            if(url.contains(".xlsx")){
                documentType = DocumentType.XLSX;
            }else {
admin-server/src/main/java/com/estate/expert/admin/utils/wordtopdf/WordToPdfConverter.java
New file
@@ -0,0 +1,305 @@
package com.estate.expert.admin.utils.wordtopdf;
import lombok.extern.slf4j.Slf4j;
import org.jodconverter.core.office.OfficeException;
import org.jodconverter.core.office.OfficeManager;
import org.jodconverter.local.LocalConverter;
import org.jodconverter.local.office.LocalOfficeManager;
import javax.annotation.PreDestroy;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * Word 转 PDF 工具类(使用 LibreOffice/OpenOffice)
 * 支持图表、图片、表格等复杂格式
 */
@Slf4j
public class WordToPdfConverter {
    private OfficeManager officeManager;
    private static WordToPdfConverter instance;
    private final AtomicInteger conversionCount = new AtomicInteger(0);
    // LibreOffice 安装路径(根据您的安装位置调整)
    private static final String LIBRE_OFFICE_PATH = "C:\\Program Files\\LibreOffice";
    //private static final String LIBRE_OFFICE_PATH = "C:\\Program Files (x86)\\LibreOffice";
    // 支持转换的文件格式
    private static final List<String> SUPPORTED_EXTENSIONS = Arrays.asList(
            ".doc", ".docx", ".rtf", ".txt", ".odt"
    );
    private WordToPdfConverter() {
        initializeOfficeManager();
    }
    /**
     * 获取单例实例
     */
    public static synchronized WordToPdfConverter getInstance() {
        if (instance == null) {
            instance = new WordToPdfConverter();
        }
        return instance;
    }
    /**
     * 初始化 Office 管理器
     */
    private void initializeOfficeManager() {
        try {
            log.info("正在初始化 LibreOffice 管理器...");
            // 检查 LibreOffice 是否安装
            File officeHome = new File(LIBRE_OFFICE_PATH);
            if (!officeHome.exists() || !officeHome.isDirectory()) {
                throw new RuntimeException("未找到 LibreOffice 安装目录: " + LIBRE_OFFICE_PATH +
                                         "\n请安装 LibreOffice 或修改 LIBRE_OFFICE_PATH 配置");
            }
            // 创建 Office 管理器
            officeManager = LocalOfficeManager.builder()
                    .officeHome(officeHome)
                    .portNumbers(2002)  // 使用 2002 端口
                    .processTimeout(300000L) // 5分钟超时
                    .taskExecutionTimeout(300000L) // 5分钟任务超时
                    .maxTasksPerProcess(20) // 每个进程最大任务数
                    .install() // 如果未安装则尝试安装
                    .build();
            // 启动服务
            officeManager.start();
            log.info("LibreOffice 管理器启动成功,服务端口: 2002");
        } catch (Exception e) {
            log.error("初始化 LibreOffice 管理器失败", e);
            throw new RuntimeException("无法启动 LibreOffice 服务: " + e.getMessage(), e);
        }
    }
    /**
     * 转换 Word 文件为 PDF
     * @param wordFilePath Word 文件路径
     * @param pdfFilePath  PDF 输出路径
     * @return 是否转换成功
     */
    public boolean convert(String wordFilePath, String pdfFilePath) {
        return convert(new File(wordFilePath), new File(pdfFilePath));
    }
    /**
     * 转换 Word 文件为 PDF
     * @param wordFile Word 文件
     * @param pdfFile  PDF 输出文件
     * @return 是否转换成功
     */
    public boolean convert(File wordFile, File pdfFile) {
        long startTime = System.currentTimeMillis();
        int currentCount = conversionCount.incrementAndGet();
        try {
            // 验证输入文件
            validateInputFile(wordFile);
            // 准备输出目录
            prepareOutputDirectory(pdfFile);
            log.info("开始转换 [{}]: {} -> {}", currentCount,
                    wordFile.getName(), pdfFile.getName());
            // 执行转换
            LocalConverter.builder()
                    .officeManager(officeManager)
                    .build()
                    .convert(wordFile)
                    .to(pdfFile)
                    .execute();
            // 验证输出文件
            boolean success = validateOutputFile(pdfFile);
            long costTime = System.currentTimeMillis() - startTime;
            if (success) {
                log.info("转换成功 [{}]: {} -> {} (耗时: {}ms)",
                        currentCount, wordFile.getName(), pdfFile.getName(), costTime);
            } else {
                log.warn("转换完成但文件可能异常 [{}]: {} (耗时: {}ms)",
                        currentCount, wordFile.getName(), costTime);
            }
            return success;
        } catch (Exception e) {
            log.error("转换失败 [{}]: {}", currentCount, wordFile.getName(), e);
            return false;
        }
    }
    /**
     * 验证输入文件
     */
    private void validateInputFile(File wordFile) {
        if (wordFile == null) {
            throw new IllegalArgumentException("输入文件不能为空");
        }
        if (!wordFile.exists()) {
            throw new IllegalArgumentException("输入文件不存在: " + wordFile.getAbsolutePath());
        }
        if (!wordFile.isFile()) {
            throw new IllegalArgumentException("输入路径不是文件: " + wordFile.getAbsolutePath());
        }
        // 检查文件格式
        String fileName = wordFile.getName().toLowerCase();
        boolean supported = SUPPORTED_EXTENSIONS.stream()
                .anyMatch(fileName::endsWith);
        if (!supported) {
            throw new IllegalArgumentException("不支持的文件格式: " + fileName +
                    "\n支持的格式: " + SUPPORTED_EXTENSIONS);
        }
        // 检查文件大小(限制 100MB)
        long fileSize = wordFile.length();
        long maxSize = 100 * 1024 * 1024; // 100MB
        if (fileSize > maxSize) {
            throw new IllegalArgumentException("文件过大: " + (fileSize / 1024 / 1024) +
                    "MB (最大支持 100MB)");
        }
    }
    /**
     * 准备输出目录
     */
    private void prepareOutputDirectory(File pdfFile) {
        File parentDir = pdfFile.getParentFile();
        if (parentDir != null && !parentDir.exists()) {
            boolean created = parentDir.mkdirs();
            if (!created) {
                throw new RuntimeException("无法创建输出目录: " + parentDir.getAbsolutePath());
            }
        }
    }
    /**
     * 验证输出文件
     */
    private boolean validateOutputFile(File pdfFile) {
        if (!pdfFile.exists()) {
            log.error("输出文件未创建: {}", pdfFile.getAbsolutePath());
            return false;
        }
        if (pdfFile.length() == 0) {
            log.error("输出文件为空: {}", pdfFile.getAbsolutePath());
            return false;
        }
        // 检查文件头是否为 PDF
        try (java.io.FileInputStream fis = new java.io.FileInputStream(pdfFile)) {
            byte[] header = new byte[4];
            int read = fis.read(header);
            if (read >= 4) {
                // PDF 文件头应该是 "%PDF"
                boolean isPdf = header[0] == '%' &&
                                header[1] == 'P' &&
                                header[2] == 'D' &&
                                header[3] == 'F';
                if (!isPdf) {
                    log.warn("输出文件可能不是有效的 PDF 文件: {}", pdfFile.getAbsolutePath());
                }
                return isPdf;
            }
        } catch (Exception e) {
            log.warn("验证 PDF 文件头失败", e);
        }
        return true;
    }
    /**
     * 批量转换
     * @param wordFiles Word 文件列表
     * @param outputDir 输出目录
     * @return 成功转换的文件数量
     */
    public int batchConvert(List<File> wordFiles, File outputDir) {
        if (!outputDir.exists()) {
            outputDir.mkdirs();
        }
        int successCount = 0;
        for (int i = 0; i < wordFiles.size(); i++) {
            File wordFile = wordFiles.get(i);
            try {
                String baseName = getFileBaseName(wordFile);
                File pdfFile = new File(outputDir, baseName + ".pdf");
                log.info("批量转换进度: {}/{} - {}", i + 1, wordFiles.size(), wordFile.getName());
                if (convert(wordFile, pdfFile)) {
                    successCount++;
                }
            } catch (Exception e) {
                log.error("批量转换失败: {}", wordFile.getName(), e);
            }
        }
        log.info("批量转换完成: 成功 {}/{}", successCount, wordFiles.size());
        return successCount;
    }
    /**
     * 获取文件名(不含扩展名)
     */
    private String getFileBaseName(File file) {
        String name = file.getName();
        int dotIndex = name.lastIndexOf('.');
        return (dotIndex == -1) ? name : name.substring(0, dotIndex);
    }
    /**
     * 获取转换统计
     */
    public int getConversionCount() {
        return conversionCount.get();
    }
    /**
     * 重启 Office 服务
     */
    public void restart() {
        log.info("正在重启 LibreOffice 服务...");
        shutdown();
        initializeOfficeManager();
        log.info("LibreOffice 服务重启完成");
    }
    /**
     * 关闭 Office 管理器
     */
    @PreDestroy
    public void shutdown() {
        if (officeManager != null && officeManager.isRunning()) {
            try {
                officeManager.stop();
                log.info("LibreOffice 服务已关闭");
            } catch (OfficeException e) {
                log.error("关闭 LibreOffice 服务时出错", e);
            }
        }
    }
    /**
     * 检查服务状态
     */
    public boolean isServiceRunning() {
        return officeManager != null && officeManager.isRunning();
    }
}
admin-server/src/main/java/com/estate/expert/admin/utils/wordtopdf/WordToPdfTest.java
New file
@@ -0,0 +1,158 @@
package com.estate.expert.admin.utils.wordtopdf;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
 * Word 转 PDF 测试类
 */
@Component
public class WordToPdfTest {
    public static void main(String[] args) {
        System.out.println("=== Word 转 PDF 测试开始 ===");
        // 获取转换器实例
        WordToPdfConverter converter = WordToPdfConverter.getInstance();
        try {
            // 测试1:单文件转换
        } catch (Exception e) {
            System.err.println("测试过程中发生错误: " + e.getMessage());
            e.printStackTrace();
        } finally {
            // 关闭转换器
            converter.shutdown();
        }
        System.out.println("=== Word 转 PDF 测试结束 ===");
    }
    /**
     * 测试单文件转换
     */
    public static String testSingleFileConversion(String uuid) {
        System.out.println("\n--- 测试1:单文件转换 ---");
        WordToPdfConverter converter = WordToPdfConverter.getInstance();
        // 请修改为您的 Word 文件路径
        String wordFilePath = "F:\\file\\estate\\reports\\"+uuid+".docx"; // 包含图表的 Word 文件
        String pdfFilePath = "F:\\file\\estate\\reports\\"+uuid+".pdf";
        File wordFile = new File(wordFilePath);
        if (!wordFile.exists()) {
            System.out.println("测试文件不存在,跳过单文件测试: " + wordFilePath);
            System.out.println("请创建测试文件或修改路径");
            return  "";
        }
        System.out.println("输入文件: " + wordFile.getAbsolutePath());
        System.out.println("输出文件: " + pdfFilePath);
        // 执行转换
        boolean success = converter.convert(wordFile, new File(pdfFilePath));
        if (success) {
            System.out.println("✓ 单文件转换成功");
            System.out.println("PDF 文件已生成: " + pdfFilePath);
            // 检查文件大小
            File pdfFile = new File(pdfFilePath);
            if (pdfFile.exists()) {
                long sizeKB = pdfFile.length() / 1024;
                System.out.println("PDF 文件大小: " + sizeKB + " KB");
            }
        } else {
            System.out.println("✗ 单文件转换失败");
        }
        return "http://221.182.45.100:11225/images/reports/"+uuid+".pdf";
    }
    /**
     * 测试批量转换
     */
    private static void testBatchConversion(WordToPdfConverter converter) {
        System.out.println("\n--- 测试2:批量转换 ---");
        // 准备测试文件列表
        List<File> wordFiles = new ArrayList<>();
        File testDir = new File("F:\\test\\documents");
        if (testDir.exists() && testDir.isDirectory()) {
            File[] files = testDir.listFiles((dir, name) ->
                name.toLowerCase().endsWith(".doc") ||
                name.toLowerCase().endsWith(".docx")
            );
            if (files != null && files.length > 0) {
                for (File file : files) {
                    wordFiles.add(file);
                }
            }
        }
        if (wordFiles.isEmpty()) {
            System.out.println("批量测试目录中没有 Word 文件,跳过批量测试");
            System.out.println("请将测试文件放入: F:\\test\\documents\\");
            return;
        }
        System.out.println("找到 " + wordFiles.size() + " 个 Word 文件");
        // 输出目录
        File outputDir = new File("F:\\test\\batch_output");
        // 执行批量转换
        int successCount = converter.batchConvert(wordFiles, outputDir);
        System.out.println("批量转换结果: 成功 " + successCount + "/" + wordFiles.size());
        System.out.println("输出目录: " + outputDir.getAbsolutePath());
    }
    /**
     * 测试错误处理
     */
    private static void testErrorHandling(WordToPdfConverter converter) {
        System.out.println("\n--- 测试3:错误处理 ---");
        // 测试1:不存在的文件
        System.out.println("测试1:不存在的文件");
        try {
            boolean result = converter.convert(
                new File("F:\\test\\nonexistent.docx"),
                new File("F:\\test\\output\\nonexistent.pdf")
            );
            System.out.println("结果: " + (result ? "成功" : "失败"));
        } catch (Exception e) {
            System.out.println("预期错误: " + e.getMessage());
        }
        // 测试2:不支持的文件格式
        System.out.println("\n测试2:不支持的文件格式");
        try {
            File tempFile = File.createTempFile("test", ".txt");
            boolean result = converter.convert(
                tempFile,
                new File("F:\\test\\output\\test.pdf")
            );
            System.out.println("结果: " + (result ? "成功" : "失败"));
            tempFile.delete();
        } catch (Exception e) {
            System.out.println("预期错误: " + e.getMessage());
        }
        // 测试3:服务状态检查
        System.out.println("\n测试3:服务状态检查");
        boolean isRunning = converter.isServiceRunning();
        System.out.println("转换服务状态: " + (isRunning ? "运行中" : "已停止"));
        // 测试4:转换统计
        System.out.println("\n测试4:转换统计");
        int count = converter.getConversionCount();
        System.out.println("总转换次数: " + count);
    }
}
admin-server/src/main/resources/mapper/ProjectProgressMapper.xml
New file
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.estate.expert.admin.domain.repository.ProjectProgressMapper">
    <select id="pageList" resultType="com.estate.expert.admin.domain.business.projectprogress.ProjectProgress">
        select t1.id,t1.detail,t1.time,t2.name as clientName,t3.name as projectName
        from project_progress t1
        left join client t2 on t1.client_id = t2.id
        left join project t3 on t1.project_id = t3.id
        where t1.deleted= 0
        <if test="name != null and name != ''">
            and t3.name like concat('%',#{name},'%')
        </if>
        <if test="clientId != null">
            and t1.client_id = #{clientId}
        </if>
        <if test="sTime != null and sTime != ''">
            and t1.time between #{sTime} and #{eTime}
        </if>
        order by t1.time desc
    </select>
    <select id="detail" resultType="com.estate.expert.admin.domain.business.projectprogress.ProjectProgress">
        select t1.id,t1.detail,t1.time,t2.name as clientName,t3.name as projectName
        from project_progress t1
                 left join client t2 on t1.client_id = t2.id
                 left join project t3 on t1.project_id = t3.id
        where t1.deleted= 0 and t1.id = #{id}
    </select>
</mapper>