8 小时以前 9bad721754fe8bbe2e5f459d0706e0fefac569f3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package cn.iocoder.yudao.module.qcreport.service.aiimport.document;
 
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
 
import java.util.List;
 
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.qcreport.enums.ErrorCodeConstants.AI_IMPORT_FILE_UNSUPPORTED;
 
/**
 * 抽取门面:按固定顺序找第一个能处理该文件的适配器。
 * <p>
 * 顺序由各适配器的 {@code @Order} 决定(图片 → PDF → Office),不是「谁更合适」的择优,
 * 而是消除歧义的定序。真实的歧义是 PDF:它既可能被 PDF 适配器认领,
 * 也可能因为 {@code contentType} 上报成 {@code application/pdf} 之外的怪值而落到 Office 的兜底判断上,
 * 定序之后谁先谁后是确定的,不会随 Bean 注册顺序漂移。
 * <p>
 * 只做分发,不做「读不出来就换个适配器再试」的兜底——每个适配器内部已经把该试的路径试过了
 * (PDF 的渲染失败会回落文本通道),在这里再叠一层只会让失败原因变得说不清。
 */
@Service
public class QcReportDocumentExtractService {
 
    @Resource
    private List<QcReportImportAdapter> adapters;
 
    /**
     * 抽取文件内容。
     *
     * @param content          文件字节
     * @param originalFilename 原始文件名(含扩展名)
     * @param contentType      上传时记录的 MIME 类型,可能为 null
     * @throws cn.iocoder.yudao.framework.common.exception.ServiceException 没有适配器认领该格式时
     */
    public QcReportDocumentExtract extract(byte[] content, String originalFilename, String contentType) {
        String extension = extensionOf(originalFilename);
        for (QcReportImportAdapter adapter : adapters) {
            if (adapter.supports(extension, contentType)) {
                return adapter.extract(content, originalFilename);
            }
        }
        throw exception(AI_IMPORT_FILE_UNSUPPORTED, originalFilename,
                extension.isBlank() ? "无" : "." + extension);
    }
 
    private static String extensionOf(String filename) {
        int dot = filename == null ? -1 : filename.lastIndexOf('.');
        return dot < 0 ? "" : filename.substring(dot + 1).toLowerCase();
    }
 
}