dd1a629d50b9c6377e7ca1e883e92748193ac82f..2166c1a14f9629aa49a5f3bb849ce878df4c4892
2024-08-28 yangbin
2
2166c1 对比 | 目录
2024-08-16 yangbin
1
a5c906 对比 | 目录
2024-08-15 yangbin
1
fc38e2 对比 | 目录
已添加33个文件
已修改6个文件
已删除1个文件
2298 ■■■■■ 文件已修改
lxzn-boot-base-core/src/main/java/org/jeecg/config/Swagger2Config.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/controller/FileImgInfoController.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/controller/FilePdfInfoController.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/controller/LanguageModelController.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/entity/FileImgInfo.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/entity/FilePdfInfo.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/entity/LanguageModel.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/mapper/FileImgInfoMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/mapper/FilePdfInfoMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/mapper/LanguageModelMapper.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/mapper/xml/FileImgInfoMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/mapper/xml/FilePdfInfoMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/mapper/xml/LanguageModelMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/service/IFileImgInfoService.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/service/IFilePdfInfoService.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/service/ILanguageModelService.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/service/IWebSocketAiService.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/service/imp/FileImgInfoServiceImp.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/service/imp/FilePdfInfoServiceImp.java 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/service/imp/LanguageModelServiceImpl.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/service/imp/WebSocketAiService.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/vo/FilePdfSelectVo.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/vo/ItemPosition.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/vo/KeyWordPosition.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/vo/KeyWordPositionVo.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/vo/MyTextExtractionStrategy.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/vo/PhotoListVo.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/websocket/AiWebSocket.java 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/utils/AiTestUils.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/utils/DateUtil.java 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/utils/PdfTurnPngUtils.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/utils/PdfUtils.java 263 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-ai/src/main/java/org/jeecg/modules/utils/file/FileUtils.java 327 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-start/src/main/java/org/jeecg/JeecgSystemApplication.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-start/src/main/resources/application-dev.yml 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-start/src/main/resources/application-prod.yml 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-start/src/main/resources/jeecg/jeecg_config.properties 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-start/src/test/java/org/jeecg/modules/ai/AiTest.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-start/src/test/java/org/jeecg/modules/mdcJc/MesTest.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-boot-base-core/src/main/java/org/jeecg/config/Swagger2Config.java
@@ -100,7 +100,7 @@
                .securitySchemes(Collections.singletonList(securityScheme()))
                .securityContexts(securityContexts())
                .globalOperationParameters(setHeaderToken())
                .groupName("MDC");
                .groupName("AI");
    }
    /***
@@ -133,14 +133,14 @@
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                // //大标题
                .title("JeecgBoot åŽå°æœåŠ¡API接口文档")
                .title(" åŽå°æœåŠ¡API接口文档")
                // ç‰ˆæœ¬å·
                .version("1.0")
//                .termsOfServiceUrl("NO terms of service")
                // æè¿°
                .description("后台API接口")
                // ä½œè€…
                .contact(new Contact("北京国炬信息技术有限公司","www.jeccg.com","jeecgos@163.com"))
                .contact(new Contact("西安灵秀","www.xalxzn.com","xalxzn.com"))
                .license("The Apache License, Version 2.0")
                .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
                .build();
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/controller/FileImgInfoController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package org.jeecg.modules.ai.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.common.util.StrUtils;
import org.jeecg.modules.ai.entity.FileImgInfo;
import org.jeecg.modules.ai.entity.FilePdfInfo;
import org.jeecg.modules.ai.service.IFileImgInfoService;
import org.jeecg.modules.ai.service.IFilePdfInfoService;
import org.jeecg.modules.ai.vo.FilePdfSelectVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
 * @author clown
 * * @date 2024/8/9
 */
@Slf4j
@Api(tags = "PDF图片文件管理")
@RestController
@RequestMapping("/ai/fileImg")
public class FileImgInfoController extends JeecgController<FileImgInfo, IFileImgInfoService> {
    @Autowired
    private IFilePdfInfoService filePdfInfoService;
    @AutoLog(value = "分页列表查询")
    @ApiOperation(value = "分页列表查询", notes = "分页列表查询")
    @GetMapping(value = "/list")
    public Result<?> queryPageList(FilePdfSelectVo vo, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
                                   @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize){
        Page page = new Page<>(pageNo, pageSize);
        FilePdfInfo info = null;
        if (StrUtils.isNotBlankOrNull(vo.getPdfFileId())) {
            info = filePdfInfoService.getById(vo.getPdfFileId());
        } else if (StrUtils.isNotBlankOrNull(vo.getPdfName())) {
            info = filePdfInfoService.findNewFilePDF(vo.getPdfName());
        }
        if (info == null) {
            return Result.error("无此文件");
        }
        IPage<FileImgInfo> imgInfos = service.queryPageList(info.getId(), page);
        return Result.OK(imgInfos);
    }
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/controller/FilePdfInfoController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package org.jeecg.modules.ai.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.modules.ai.entity.FileImgInfo;
import org.jeecg.modules.ai.entity.FilePdfInfo;
import org.jeecg.modules.ai.service.IFilePdfInfoService;
import org.jeecg.modules.ai.vo.FilePdfSelectVo;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
 * @author clown
 * * @date 2024/8/7
 */
@Slf4j
@Api(tags = "PDF文件管理")
@RestController
@RequestMapping("/ai/filePdf")
public class FilePdfInfoController extends JeecgController<FilePdfInfo, IFilePdfInfoService> {
    @AutoLog(value = "PDF文件管理-检索查询")
    @ApiOperation(value = "PDF文件管理-检索查询", notes = "PDF文件管理-检索查询")
    @PutMapping(value = "/findImgList")
    public Result<?> findImgPathList(@RequestBody FilePdfSelectVo vo) {
        List<FileImgInfo> infos =  service.findImgPathList(vo);
        /*Thread.sleep();*/
        return Result.OK(infos);
    }
   /* @AutoLog(value = "aiTest")
    @ApiOperation(value = "aiTest", notes = "aiTest")*/
    @GetMapping(value = "/aiTest")
    public Result<?> aiTest(String nameList) {
       /* String name = "{\"id\":\"683a65fd-8feb-4446-ad32-714c4785f667\",\"messages\":[{\"role\":\"user\",\"content\":\"给我讲个故事?\"}],\"stream\":true,\"max_tokens\":500}"; // JSON数据
        String url = "https://836u458t54.vicp.fun/chat/test_chat";
        String aiPost =  AiTestUils.sendJsonToHttpsPost(url, name);*/
        String s = "\"data\": {\"id\": \"683a65fd-8feb-4446-ad32-714c4785f667\", \"created\": 1723105518, \"model\": \"YxCareer0.7\", \"choices\": [{\"index\": 0, \"delta\": {\"role\": \"assistant\", \"content\": \"\"}, \"finish_reason\": null}]}";
        System.out.println(s.replace("data:" ,""));
        return Result.OK("");
    }
    @AutoLog(value = "PDF文件列表")
    @ApiOperation(value = "PDF文件列表", notes = "PDF文件列表")
    @GetMapping(value = "/filePdfList")
    public Result<?> aiFindFiles() {
        List<FilePdfInfo> infos = service.list();
        return Result.OK(infos);
    }
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/controller/LanguageModelController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,78 @@
package org.jeecg.modules.ai.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.modules.ai.entity.LanguageModel;
import org.jeecg.modules.ai.service.ILanguageModelService;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
 * @author clown
 * * @date 2024/8/19
 */
@Slf4j
@Api(tags = "问答")
@RestController
@RequestMapping("/ai/languageModel")
public class LanguageModelController  extends JeecgController<LanguageModel, ILanguageModelService> {
    /**
     * æ–°å¢ž
     *
     * @param languageModel
     * @return
     */
    @AutoLog("问答-新增")
    @ApiOperation(value = "问答-新增", notes = "问答-新增")
    @PostMapping("/addLanguage")
    public Result<?> addLanguageModel(@RequestBody LanguageModel languageModel) {
        Boolean flag = super.service.addLanguageModel(languageModel);
        return flag ? Result.OK("新增成功") : Result.error("新增失败");
    }
    @AutoLog("问答-会话列表")
    @ApiOperation(value = "问答-会话列表", notes = "问答-会话列表")
    @GetMapping("/languageTitles")
    public Result<?> languageModelTitles() {
        List<LanguageModel> languageModels = super.service.findListAiTitles();
        return Result.OK(languageModels);
    }
    @AutoLog("问答-列表")
    @ApiOperation(value = "问答-列表", notes = "问答-列表")
    @GetMapping("/languageProblems")
    public Result<?> languageProblems(String  id) {
        List<LanguageModel> models = super.service.languageProblems(id);
        return Result.OK(models);
    }
    @AutoLog("问答-删除")
    @ApiOperation(value = "问答-删除", notes = "问答-删除")
    @RequestMapping(value = "/deleteLanguage", method = RequestMethod.DELETE)
    public Result deleteLanguage(@RequestParam("id") String id) {
        if (StringUtils.isNotBlank(id)) {
            List<LanguageModel> models = super.service.languageProblems(id);
            if (models == null || models.isEmpty()){
                super.service.removeById(id);
            }
            List<String> strings = new ArrayList<>();
            strings.add(id);
            for (LanguageModel model : models) {
                strings.add(model.getId());
            }
            super.service.removeBatchByIds(strings);
        }
        return Result.ok("删除成功");
    }
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/entity/FileImgInfo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package org.jeecg.modules.ai.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
/**
 * @author clown
 * * @date 2024/8/1
 */
@Data
@TableName("ai_file_img_info")
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class FileImgInfo implements Serializable {
    private static final long serialVersionUID = 1519876512924700514L;
    @TableId(type = IdType.ASSIGN_ID)
    @ApiModelProperty(value = "主键")
    private String id;
    private String fileId;
    /*页码*/
    private Integer pageNumber;
    /*图片路径*/
    private String imgPath;
    /*图片名称*/
    private String imgName;
    private String imgEncodeName;
    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("创建时间")
    private Date createTime;
    @Excel(name = "创建人",width = 15)
    @ApiModelProperty("创建人")
    private String createBy;
    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("更新时间")
    private Date updateTime;
    @Excel(name = "更新人",width = 15)
    @ApiModelProperty("更新人")
    private String updateBy;
    @Excel(name = "删除状态", width = 15, dicCode = "del_flag")
    @ApiModelProperty(value = "删除状态(0,正常,1已删除)")
    private String delFlag;
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/entity/FilePdfInfo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package org.jeecg.modules.ai.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
/**
 * @author clown
 * * @date 2024/8/1
 */
@Data
@TableName("ai_file_pdf_Info")
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class FilePdfInfo implements Serializable {
    private static final long serialVersionUID = 1519876512924700514L;
    @TableId(type = IdType.ASSIGN_ID)
    @ApiModelProperty(value = "主键")
    private String id;
    private String filePath;
    private String fileName;
    // æ–‡ä»¶åŠ å¯†åŽçš„åç§°
    private String fileEncodeName;
    // æ–‡ä»¶å¤§å°ï¼ˆKB)
    private Long fileSize;
    // æ–‡ä»¶åŽç¼€
    private String fileSuffix;
    /*数控系统*/
    private String controlSystem;
    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("创建时间")
    private Date createTime;
    @Excel(name = "创建人",width = 15)
    @ApiModelProperty("创建人")
    private String createBy;
    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("更新时间")
    private Date updateTime;
    @Excel(name = "更新人",width = 15)
    @ApiModelProperty("更新人")
    private String updateBy;
    @Excel(name = "删除状态", width = 15, dicCode = "del_flag")
    @ApiModelProperty(value = "删除状态(0,正常,1已删除)")
    private String delFlag;
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/entity/LanguageModel.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,61 @@
package org.jeecg.modules.ai.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
/**
 * @author clown
 * * @date 2024/8/19
 */
@Data
@TableName("ai_language_model")
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class LanguageModel implements Serializable {
    private static final long serialVersionUID = 1519876512924700514L;
    @TableId(type = IdType.ASSIGN_ID)
    @ApiModelProperty(value = "主键")
    private String id;
    private String aiId;
    private String parentId;
    /*问题*/
    private String problem;
    /*回答*/
    private String answer;
    /*类型   1 åˆ—表  2  é—®ç­”   3 å›žç­”*/
    private Integer aiType;
    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("创建时间")
    private Date createTime;
    @Excel(name = "创建人",width = 15)
    @ApiModelProperty("创建人")
    private String createBy;
    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("更新时间")
    private Date updateTime;
    @Excel(name = "更新人",width = 15)
    @ApiModelProperty("更新人")
    private String updateBy;
    @Excel(name = "删除状态", width = 15, dicCode = "del_flag")
    @ApiModelProperty(value = "删除状态(0,正常,1已删除)")
    private String delFlag;
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/mapper/FileImgInfoMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
package org.jeecg.modules.ai.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.ai.entity.FileImgInfo;
/**
 * @author clown
 * * @date 2024/8/5
 */
public interface FileImgInfoMapper extends BaseMapper<FileImgInfo> {
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/mapper/FilePdfInfoMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
package org.jeecg.modules.ai.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.ai.entity.FilePdfInfo;
/**
 * @author clown
 * * @date 2024/8/5
 */
public interface FilePdfInfoMapper extends BaseMapper<FilePdfInfo> {
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/mapper/LanguageModelMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
package org.jeecg.modules.ai.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.ai.entity.LanguageModel;
/**
 * @author clown
 * * @date 2024/8/19
 */
public interface LanguageModelMapper extends BaseMapper<LanguageModel> {
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/mapper/xml/FileImgInfoMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,5 @@
<?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="org.jeecg.modules.ai.mapper.FileImgInfoMapper">
</mapper>
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/mapper/xml/FilePdfInfoMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,5 @@
<?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="org.jeecg.modules.ai.mapper.FilePdfInfoMapper">
</mapper>
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/mapper/xml/LanguageModelMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,5 @@
<?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="org.jeecg.modules.ai.mapper.LanguageModelMapper">
</mapper>
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/service/IFileImgInfoService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package org.jeecg.modules.ai.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.ai.entity.FileImgInfo;
import org.jeecg.modules.ai.vo.FilePdfSelectVo;
import java.io.File;
import java.util.List;
/**
 * @author clown
 * * @date 2024/8/5
 */
public interface IFileImgInfoService extends IService<FileImgInfo> {
    /**
     * è§£æžpdf文件,生成图片保存记录
     * @param file
     * @return
     */
    boolean analysisPDfFromFile(File file);
    /**
     *
     * @param fileId
     * @param pageNumbers
     * @return
     */
    List<FileImgInfo> findFileImgInfos(String fileId,List<Integer> pageNumbers);
    IPage<FileImgInfo> queryPageList(String fileId, Page page);
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/service/IFilePdfInfoService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package org.jeecg.modules.ai.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.ai.entity.FileImgInfo;
import org.jeecg.modules.ai.entity.FilePdfInfo;
import org.jeecg.modules.ai.vo.FilePdfSelectVo;
import java.io.File;
import java.util.List;
/**
 * @author clown
 * * @date 2024/8/5
 */
public interface IFilePdfInfoService extends IService<FilePdfInfo> {
    /**
     * æ–‡ä»¶
     * @param file
     * @return
     */
    boolean uploadFilePdf(File file);
    /**
     *
     * @param fileVo
     * @return
     */
    List<FileImgInfo> findImgPathList(FilePdfSelectVo fileVo);
    FilePdfInfo findNewFilePDF(String fileName);
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/service/ILanguageModelService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package org.jeecg.modules.ai.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.ai.entity.LanguageModel;
import java.util.List;
/**
 * @author clown
 * * @date 2024/8/19
 */
public interface ILanguageModelService extends IService<LanguageModel> {
    boolean addLanguageModel(LanguageModel languageModel);
    /**
     * æŸ¥è¯¢åˆ—表数据
     * @param aiType
     * @param aiId
     * @return
     */
    LanguageModel selectParentId(Integer aiType,String aiId);
    /**
     *
     * @return
     */
    List<LanguageModel> findListAiTitles();
    /**
     *
     * @return
     */
    List<LanguageModel> languageProblems(String id);
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/service/IWebSocketAiService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
package org.jeecg.modules.ai.service;
/**
 * @author clown
 * * @date 2024/8/8
 */
public interface IWebSocketAiService {
    /**
     * æŸ¥è¯¢
     */
    void selectChatIA();
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/service/imp/FileImgInfoServiceImp.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package org.jeecg.modules.ai.service.imp;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
import com.baomidou.mybatisplus.core.incrementer.ImadcnIdentifierGenerator;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.incrementer.KingbaseKeyGenerator;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.jeecg.modules.ai.entity.FileImgInfo;
import org.jeecg.modules.ai.entity.FilePdfInfo;
import org.jeecg.modules.ai.mapper.FileImgInfoMapper;
import org.jeecg.modules.ai.service.IFileImgInfoService;
import org.jeecg.modules.ai.service.IFilePdfInfoService;
import org.jeecg.modules.ai.vo.FilePdfSelectVo;
import org.jeecg.modules.utils.BeanMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
/**
 * @author clown
 * * @date 2024/8/5
 */
@Service
public class FileImgInfoServiceImp extends ServiceImpl<FileImgInfoMapper,FileImgInfo> implements IFileImgInfoService {
    @Override
    public boolean analysisPDfFromFile(File file) {
        return false;
    }
    @Override
    public List<FileImgInfo> findFileImgInfos(String fileId,List<Integer>  pageNumbers) {
        List<FileImgInfo> mdcPlanCloses = this.getBaseMapper().
                selectList(new LambdaQueryWrapper<FileImgInfo>().
                in(FileImgInfo::getPageNumber, pageNumbers).
                eq(FileImgInfo::getFileId,fileId).
                orderByAsc(FileImgInfo::getPageNumber)
                );
        return mdcPlanCloses;
    }
    @Override
    public IPage<FileImgInfo> queryPageList(String fileId, Page page) {
        LambdaQueryWrapper<FileImgInfo> wrapper =new LambdaQueryWrapper<>();
        wrapper.eq(FileImgInfo::getFileId,fileId);
        return baseMapper.selectPage(page,wrapper);
    }
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/service/imp/FilePdfInfoServiceImp.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,133 @@
package org.jeecg.modules.ai.service.imp;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.api.vo.FileUploadResult;
import org.jeecg.modules.ai.entity.FileImgInfo;
import org.jeecg.modules.ai.entity.FilePdfInfo;
import org.jeecg.modules.ai.mapper.FilePdfInfoMapper;
import org.jeecg.modules.ai.service.IFileImgInfoService;
import org.jeecg.modules.ai.service.IFilePdfInfoService;
import org.jeecg.modules.ai.vo.FilePdfSelectVo;
import org.jeecg.modules.ai.vo.KeyWordPositionVo;
import org.jeecg.modules.ai.vo.PhotoListVo;
import org.jeecg.modules.utils.BeanMapper;
import org.jeecg.modules.utils.DateUtil;
import org.jeecg.modules.utils.PdfTurnPngUtils;
import org.jeecg.modules.utils.PdfUtils;
import org.jeecg.modules.utils.file.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.crypto.KeyGenerator;
import java.io.File;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
/**
 * @author clown
 * * @date 2024/8/5
 */
@Service
public class FilePdfInfoServiceImp extends ServiceImpl<FilePdfInfoMapper,FilePdfInfo> implements IFilePdfInfoService {
    @Autowired
    private IFileImgInfoService fileImgInfoService;
    @Value("${jeecg.path.upload}")
    public String fileUploadFolder;
    @Override
    public boolean uploadFilePdf(File file) {
        FileUploadResult result = FileUtils.uploadFileInFile(file);
        FilePdfInfo info = new FilePdfInfo();
        info.setFileName(result.getFileName());
        info.setFileEncodeName(result.getFileEncodeName());
        info.setFilePath(result.getFilePath());
        info.setFileSize(result.getFileSize());
        info.setFileSuffix(result.getFileSuffix());
        boolean b = save(info);
        if (!b) {
            return false;
        }
        try {
            List<PhotoListVo> pngList = PdfTurnPngUtils.pdf2pngList(file,"png",info.getId());
            if (pngList == null || pngList.isEmpty() ) {
                return false;
            }
            List<FileImgInfo> fileImgs = new ArrayList<>();
            for (PhotoListVo vo : pngList) {
                FileImgInfo img = new FileImgInfo();
                BeanMapper.copy(vo,img);
                img.setFileId(info.getId());
                fileImgs.add(img);
            }
            boolean c = fileImgInfoService.saveBatch(fileImgs);
            if (c) {
                return true;
            } else {
                removeById(info.getId());
                return false;
            }
        } catch (Exception e) {
            removeById(info.getId());
            return false;
        }
    }
    @Override
    public List<FileImgInfo> findImgPathList(FilePdfSelectVo fileVo) {
        FilePdfInfo info = new FilePdfInfo();
        if (StringUtils.isNotBlank(fileVo.getPdfFileId())) {
            info = super.getById(fileVo.getPdfFileId());
        } else {
            //待优化
            List<FilePdfInfo> list = super.list(new LambdaQueryWrapper<FilePdfInfo>().
                    eq(FilePdfInfo::getFileName, fileVo.getPdfName()).
                    orderByDesc(FilePdfInfo::getCreateTime)
            );
            if (list == null || list.isEmpty()) {
                return null;
            }
            BeanMapper.copy(list.get(0),info);
        }
        if (info == null ) {
            return null;
        }
        List<KeyWordPositionVo> workVo = PdfUtils.findListWord(fileUploadFolder + "/" + info.getFilePath() + "/" + info.getFileEncodeName(),fileVo.getPdfContent());
        if (workVo == null || workVo.isEmpty()) {
            return null;
        }
        List<Integer> sb = new ArrayList<>();
        for (KeyWordPositionVo key : workVo) {
            sb.add(key.getPage());
        }
        List<FileImgInfo> list = fileImgInfoService.findFileImgInfos(fileVo.getPdfFileId(),sb);
        return list;
    }
    @Override
    public FilePdfInfo findNewFilePDF(String fileName) {
        FilePdfInfo info = new FilePdfInfo();
        List<FilePdfInfo> list = super.list(new LambdaQueryWrapper<FilePdfInfo>().
                eq(FilePdfInfo::getFileName, fileName).
                orderByDesc(FilePdfInfo::getCreateTime)
        );
        if (list == null || list.isEmpty()) {
            return null;
        }
        BeanMapper.copy(list.get(0),info);
        return info;
    }
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/service/imp/LanguageModelServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,92 @@
package org.jeecg.modules.ai.service.imp;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.ai.entity.LanguageModel;
import org.jeecg.modules.ai.mapper.LanguageModelMapper;
import org.jeecg.modules.ai.service.ILanguageModelService;
import org.jeecg.modules.system.util.SecurityUtil;
import org.jeecg.modules.utils.BeanMapper;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * @author clown
 * * @date 2024/8/19
 */
@Service
public class LanguageModelServiceImpl extends ServiceImpl<LanguageModelMapper,LanguageModel> implements ILanguageModelService {
    @Override
    public boolean addLanguageModel(LanguageModel languageModel) {
        if (languageModel == null || languageModel.getAiType() == null) {
            return false;
        }
        LanguageModel model = new LanguageModel();
        if (languageModel.getAiType() == 1) {
            boolean one =super.save(languageModel);
            if (one) {
                model.setProblem(languageModel.getProblem());
                model.setParentId(languageModel.getId());
                model.setAiType(2);
                super.save(model);
                return true;
            }
        }
        else  if (languageModel.getAiType() == 2 || languageModel.getAiType() == 3) {
            //查询父类ID
            super.save(languageModel);
            return true;
        }
        return false;
    }
    @Override
    public LanguageModel selectParentId(Integer aiType, String aiId) {
        LanguageModel info = new LanguageModel();
        List<LanguageModel> list = super.list(new LambdaQueryWrapper<LanguageModel>().
                eq(LanguageModel::getAiType, aiType).
                eq(LanguageModel::getAiId, aiId).
                orderByDesc(LanguageModel::getCreateTime)
        );
        if (list == null || list.isEmpty()) {
            return null;
        }
        BeanMapper.copy(list.get(0),info);
        return info;
    }
    @Override
    public List<LanguageModel> findListAiTitles() {
        LoginUser user = (LoginUser)SecurityUtils.getSubject().getPrincipal();
        List<LanguageModel> list = super.list(new LambdaQueryWrapper<LanguageModel>().
                eq(LanguageModel::getAiType, 1).
                eq(LanguageModel::getCreateBy, user.getUsername()).
                orderByDesc(LanguageModel::getCreateTime)
        );
        if (list == null || list.isEmpty()) {
            return null;
        }
        return list;
    }
    @Override
    public List<LanguageModel> languageProblems(String id) {
        LoginUser user = (LoginUser)SecurityUtils.getSubject().getPrincipal();
        LambdaQueryWrapper<LanguageModel> wrapperL = new LambdaQueryWrapper<>();
        wrapperL.eq(LanguageModel::getCreateBy, user.getUsername());
        wrapperL.eq(LanguageModel::getParentId, id);
        wrapperL.and((wrapper)->wrapper.eq(LanguageModel::getAiType, 2).
                        or().
                        eq(LanguageModel::getAiType, 3));
        wrapperL.orderByAsc(LanguageModel::getCreateTime);
        List<LanguageModel> list = super.list(wrapperL);
        if (list == null || list.isEmpty()) {
            return null;
        }
        return list;
    }
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/service/imp/WebSocketAiService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package org.jeecg.modules.ai.service.imp;
import org.jeecg.modules.ai.service.IWebSocketAiService;
import org.jeecg.modules.ai.websocket.AiWebSocket;
import org.jeecg.modules.utils.AiTestUils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
/**
 * @author clown
 * * @date 2024/8/8
 */
@Service
public class WebSocketAiService implements IWebSocketAiService {
    @Override
    /*@Scheduled(cron = "0/10 * * * * ?")*/
    public void selectChatIA() {
        String name = "{\"id\":\"683a65fd-8feb-4446-ad32-714c4785f667\",\"messages\":[{\"role\":\"user\",\"content\":\"给我讲个故事?\"}],\"stream\":true,\"max_tokens\":500}"; // JSON数据
        String url = "https://836u458t54.vicp.fun/chat/test_chat";
        String aiPost =  AiTestUils.sendJsonToHttpsPost(url, name);
        /*System.out.println(aiPost);*/
        for (String s : AiWebSocket.getSessionPoolAi().keySet()) {
            AiWebSocket.pushMessage(s,aiPost);
        }
    }
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/vo/FilePdfSelectVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
package org.jeecg.modules.ai.vo;
import lombok.Data;
/**
 * @author clown
 * * @date 2024/8/7
 */
@Data
public class FilePdfSelectVo {
    private String pdfContent;
    private String pdfName;
    private String pdfPath;
    private String pdfFileId;
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/vo/ItemPosition.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
package org.jeecg.modules.ai.vo;
import com.itextpdf.text.Rectangle;
import lombok.Data;
/**
 * @author clown
 * * @date 2024/7/23
 * å­—体的位置信息
 */
@Data
public class ItemPosition {
    private Integer page;
    private String text;
    //这个字的矩形坐标
    private Rectangle rectangle;
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/vo/KeyWordPosition.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package org.jeecg.modules.ai.vo;
import lombok.Data;
import java.util.List;
/**
 * @author clown
 * * @date 2024/7/23
 * éœ€è¦é«˜äº®æ˜¾ç¤ºçš„关键字坐标信息
 */
@Data
public class KeyWordPosition {
    private String text;
    private List<ItemPosition> listItem;
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/vo/KeyWordPositionVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
package org.jeecg.modules.ai.vo;
import com.itextpdf.text.Rectangle;
import lombok.Data;
/**
 * @author clown
 * * @date 2024/8/7
 */
@Data
public class KeyWordPositionVo {
    private Integer page;
    private String text;
    //这个字的矩形坐标
    private Rectangle rectangle;
    private String rectangleString;
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/vo/MyTextExtractionStrategy.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
package org.jeecg.modules.ai.vo;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.parser.ImageRenderInfo;
import com.itextpdf.text.pdf.parser.TextExtractionStrategy;
import com.itextpdf.text.pdf.parser.TextRenderInfo;
import com.itextpdf.text.pdf.parser.Vector;
import java.util.List;
/**
 * @author clown
 * * @date 2024/7/23
 * è®°å½•所有位置+字体信息,这种方式获取坐标信息和字体信息方便一点
 *
 */
public class MyTextExtractionStrategy  implements TextExtractionStrategy {
    private List<ItemPosition> positions;
    private Integer page;
    public MyTextExtractionStrategy() {}
    public MyTextExtractionStrategy(List<ItemPosition> positions, Integer page) {
        this.positions = positions;
        this.page = page;
    }
    @Override
    public void beginTextBlock() {
        // TODO Auto-generated method stub
    }
    @Override
    public void renderText(TextRenderInfo renderInfo) {
        ItemPosition ItemPosition = new ItemPosition();
        Vector bottomLeftPoint = renderInfo.getDescentLine().getStartPoint();
        Vector topRightPoint = renderInfo.getAscentLine().getEndPoint();
        //记录矩形坐标
        Rectangle rectangle = new Rectangle(bottomLeftPoint.get(Vector.I1), bottomLeftPoint.get(Vector.I2),
                topRightPoint.get(Vector.I1), topRightPoint.get(Vector.I2));
        ItemPosition.setPage(page);
        ItemPosition.setRectangle(rectangle);
        ItemPosition.setText(renderInfo.getText());
        positions.add(ItemPosition);
    }
    @Override
    public void endTextBlock() {
        // TODO Auto-generated method stub
    }
    @Override
    public void renderImage(ImageRenderInfo renderInfo) {
        // TODO Auto-generated method stub
    }
    @Override
    public String getResultantText() {
        // TODO Auto-generated method stub
        return null;
    }
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/vo/PhotoListVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package org.jeecg.modules.ai.vo;
import lombok.Data;
/**
 * @author clown
 * * @date 2024/8/6
 */
@Data
public class PhotoListVo {
    private Integer pageNumber;
    // æ–‡ä»¶åç§° ä¸å¸¦åŽç¼€
    private String imgName;
    // æ–‡ä»¶åŠ å¯†åŽçš„åç§°
    private String imgEncodeName;
    // æ–‡ä»¶ä¿å­˜ç›¸å¯¹è·¯å¾„
    private String imgPath;
    // æ–‡ä»¶å¤§å°ï¼ˆKB)
    private Long imgSize;
    // æ–‡ä»¶åŽç¼€
    private String imgSuffix;
}
lxzn-module-ai/src/main/java/org/jeecg/modules/ai/websocket/AiWebSocket.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,191 @@
package org.jeecg.modules.ai.websocket;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.base.BaseMap;
import org.jeecg.common.constant.WebsocketConst;
import org.jeecg.common.modules.redis.client.JeecgRedisClient;
import org.jeecg.modules.ai.service.IWebSocketAiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
 * @author clown
 * * @date 2024/8/8
 */
@Component
@Slf4j
@ServerEndpoint("/websocket/ai/{userId}")
public class AiWebSocket {
    /**线程安全Map*/
    private static ConcurrentHashMap<String, Session> sessionPoolAi = new ConcurrentHashMap<>();
    /**
     * Redis触发监听名字
     */
    public static final String REDIS_TOPIC_NAME_AI = "websocketAiHandler";
    @Resource
    private JeecgRedisClient jeecgRedisClient;
    public static Map<String, Session> getSessionPoolAi() {
        return sessionPoolAi;
    }
    //==========【websocket接受、推送消息等方法 â€”— å…·ä½“服务节点推送ws消息】========================================================================================
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "userId") String userId) {
        try {
            sessionPoolAi.put(userId, session);
            log.info("【系统 WebSocket】有新的连接,总数为:" + sessionPoolAi.size());
        } catch (Exception e) {
        }
    }
    @OnClose
    public void onClose(@PathParam("userId") String userId) {
        try {
            sessionPoolAi.remove(userId);
            log.info("【系统 WebSocket】连接断开,总数为:" + sessionPoolAi.size());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * ws推送消息
     *
     * @param userId
     * @param message
     */
    public static void pushMessage(String userId, String message) {
        Session session = sessionPoolAi.get(userId);
        if (session != null && session.isOpen()) {
            try {
                //update-begin-author:taoyan date:20211012 for: websocket报错 https://gitee.com/jeecg/jeecg-boot/issues/I4C0MU
                synchronized (session){
                    session.getBasicRemote().sendText(message);
                }
                //update-end-author:taoyan date:20211012 for: websocket报错 https://gitee.com/jeecg/jeecg-boot/issues/I4C0MU
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        /*for (Map.Entry<String, Session> item : sessionPoolAi.entrySet()) {
            //userId key值= {用户id + "_"+ ç™»å½•token的md5串}
            //TODO vue2未改key新规则,暂时不影响逻辑
            if (item.getKey().contains(userId)) {
                Session session = item.getValue();
                try {
                    //update-begin-author:taoyan date:20211012 for: websocket报错 https://gitee.com/jeecg/jeecg-boot/issues/I4C0MU
                    synchronized (session){
                        log.info("【系统 WebSocket】推送单人消息:" + message);
                        session.getBasicRemote().sendText(message);
                    }
                    //update-end-author:taoyan date:20211012 for: websocket报错 https://gitee.com/jeecg/jeecg-boot/issues/I4C0MU
                } catch (Exception e) {
                    log.error(e.getMessage(),e);
                }
            }
        }*/
    }
    /**
     * ws遍历群发消息
     */
    public void pushMessage(String message) {
        try {
            for (Map.Entry<String, Session> item : sessionPoolAi.entrySet()) {
                try {
                    item.getValue().getAsyncRemote().sendText(message);
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
            }
            log.info("【系统 WebSocket】群发消息:" + message);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }
    /**
     * ws接受客户端消息
     */
    @OnMessage
    public void onMessage(String message, @PathParam(value = "userId") String userId) {
        if(!"ping".equals(message) && !WebsocketConst.CMD_CHECK.equals(message)){
            log.info("【系统 WebSocket】收到客户端消息:" + message);
        }else{
            log.debug("【系统 WebSocket】收到客户端消息:" + message);
        }
       /* JSONObject obj = new JSONObject();
        //业务类型
        obj.put(WebsocketConst.MSG_CMD, WebsocketConst.CMD_CHECK);
        //消息内容
        obj.put(WebsocketConst.MSG_TXT, "心跳响应");*/
        /*webSocketAiService.selectChatIA(message);*/
        /*this.pushMessage(userId, obj.toJSONString());*/
    }
    /**
     * é…ç½®é”™è¯¯ä¿¡æ¯å¤„理
     *
     * @param session
     * @param t
     */
    @OnError
    public void onError(Session session, Throwable t) {
        log.warn("【系统 WebSocket】消息出现错误");
        //t.printStackTrace();
    }
    //==========【系统 WebSocket接受、推送消息等方法 â€”— å…·ä½“服务节点推送ws消息】========================================================================================
    //==========【采用redis发布订阅模式——推送消息】========================================================================================
    /**
     * åŽå°å‘送消息到redis
     *
     * @param message
     */
    public void sendMessage(String message) {
        BaseMap baseMap = new BaseMap();
        baseMap.put("userId", "");
        baseMap.put("message", message);
        jeecgRedisClient.sendMessage(REDIS_TOPIC_NAME_AI, baseMap);
    }
    /**
     * æ­¤ä¸ºå•点消息 redis
     *
     * @param userId
     * @param message
     */
    public void sendMessage(String userId, String message) {
        BaseMap baseMap = new BaseMap();
        jeecgRedisClient.sendMessage(REDIS_TOPIC_NAME_AI, baseMap);
    }
    /**
     * æ­¤ä¸ºå•点消息(多人) redis
     *
     * @param userIds
     * @param message
     */
    public void sendMessage(String[] userIds, String message) {
    }
    // æ­¤ä¸ºå•点消息
    public void sendOneMessage(String userId, String message) {
    }
}
lxzn-module-ai/src/main/java/org/jeecg/modules/utils/AiTestUils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,101 @@
package org.jeecg.modules.utils;
import javax.net.ssl.*;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public class AiTestUils {
    private static class TrustAnyTrustManager implements X509TrustManager {
        /**
         * è¯¥æ–¹æ³•检查客户端的证书,若不信任该证书则抛出异常。由于我们不需要对客户端进行认证,因此我们只需要执行默认的信任管理器的这
         * ä¸ªæ–¹æ³•。
         * JSSE中,默认的信任管理器类为TrustManager。
         */
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
        }
        /**
         * è¯¥æ–¹æ³•检查服务器的证书,若不信任该证书同样抛出异常。通过自己实现该方法,可以使之信任我们指定的任何证书。
         * åœ¨å®žçŽ°è¯¥æ–¹æ³•æ—¶ï¼Œä¹Ÿå¯ä»¥ç®€å•çš„ä¸åšä»»ä½•å¤„ç†ï¼Œ å³ä¸€ä¸ªç©ºçš„函数体,由于不会抛出异常,它就会信任任何证书。(non-Javadoc)
         */
        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
        }
        /**
         * @return è¿”回受信任的X509证书数组。
         */
        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[]{};
        }
    }
    private static class TrustAnyHostnameVerifier implements HostnameVerifier {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }
    /**
     * post方式请求服务器(https协议)
     * @param url     æ±‚地址
     * @param content å‚æ•°
     * @return
     */
    public static String sendJsonToHttpsPost(String url, String content) {
        try {
            /*
             * ç±»HttpsURLConnection似乎并没有提供方法设置信任管理器。其实,
             * HttpsURLConnection通过SSLSocket来建立与HTTPS的安全连接
             * ï¼ŒSSLSocket对象是由SSLSocketFactory生成的。
             * HttpsURLConnection提供了方法setSSLSocketFactory
             * (SSLSocketFactory)设置它使用的SSLSocketFactory对象。
             * SSLSocketFactory通过SSLContext对象来获得,在初始化SSLContext对象时,可指定信任管理器对象。
             */
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, new TrustManager[]{new TrustAnyTrustManager()},
                    new java.security.SecureRandom());
            URL console = new URL(url);
            HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
            conn.setSSLSocketFactory(sc.getSocketFactory());
            conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
            conn.setDoOutput(true);
            // è®¾ç½®è¯·æ±‚头
            conn.setRequestProperty("Content-Type", "application/json;charset=utf-8");
            conn.connect();
            DataOutputStream out = new DataOutputStream(conn.getOutputStream());
            out.write(content.getBytes());
            // åˆ·æ–°ã€å…³é—­
            out.flush();
            out.close();
            InputStream is = conn.getInputStream();
            if (is != null) {
                ByteArrayOutputStream outStream = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int len = 0;
                while ((len = is.read(buffer)) != -1) {
                    outStream.write(buffer, 0, len);
                }
                is.close();
                return outStream.toString();
            }
        } catch (Exception e) {
        }
        return null;
    }
}
lxzn-module-ai/src/main/java/org/jeecg/modules/utils/DateUtil.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,142 @@
package org.jeecg.modules.utils;
import lombok.extern.slf4j.Slf4j;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
 * Created by Administrator on 2015/8/28.
 */
@Slf4j
public class DateUtil {
    public static final String STR_DATE = "yyyy-MM-dd";
    public static final String STR_YEAR_MONTH = "yyyy-MM";
    public static final String STR_DATE_TIME = "yyyy-MM-dd HH:mm:ss.SSS";
    public static final String STR_DATE_TIME_SMALL = "yyyy-MM-dd HH:mm:ss";
    public static final String STR_DATE_TIME_MIN = "yyyy-MM-dd HH:mm";
    public static final String STR_DATE_TIME_HOUR = "yyyy-MM-dd HH";
    public static final String STR_DATE_TIME_FULL = "yyyyMMddHHmmssSSS";
    public static final String STR_YEARMONTH = "yyyyMM";
    public static final String STR_YEAR = "yyyy";
    /**
     * èŽ·å–å½“å‰æ—¶é—´
     * @return
     */
    public static Date getNow() {
        return new Date(System.currentTimeMillis());
    }
    /**
     * <p>
     * Description: åŽ»æŽ‰æ—¥æœŸæ—¶é—´ä¸­çš„æ—¶é—´éƒ¨åˆ†
     * </p>
     * å¦‚: 2013-11-11 18:56:33 ---> 2013-11-11 00:00:00
     *
     * @param date éœ€è¦ä¿®æ”¹çš„æ—¶é—´
     * @return ä¿®æ”¹åŽçš„æ—¶é—´
     */
    public static Date removeTime(Date date) {
        Date result = null;
        try {
            SimpleDateFormat df = new SimpleDateFormat(STR_DATE);
            String dateStr = df.format(date);
            result = df.parse(dateStr);
        } catch (ParseException e) {
            log.error(e.getMessage(), e.getStackTrace());
            return null;
        }
        return result;
    }
    /**
     * <p>
     * Description: æŒ‰æŒ‡å®šæ ¼å¼èŽ·å–æ—¶é—´å­—ç¬¦ä¸²
     * </p>
     *
     * @param date   è¦è½¬æ¢çš„æ—¥æœŸ
     * @param format æ ¼å¼,例如:yyyy-MM-dd HH:mm:ss.SSS
     * @return è½¬æ¢åŽçš„æ—¶é—´å­—符串
     */
    public static String format(Date date, String format) {
        SimpleDateFormat df = new SimpleDateFormat(format);
        return df.format(date);
    }
    /**
     * èŽ·å–æ—¥æœŸçš„å¤©å­—ç¬¦ä¸²
     * @param date
     * @return
     */
    public static String getDayStr(Date date) {
        Calendar calendar = Calendar.getInstance(); // å¾—到日历
        calendar.setTime(date);// æŠŠå½“前时间赋给日历
        int day = calendar.get(Calendar.DATE);//获取日
        String dayStr = day < 10 ? "0" + day : day + "";
        return dayStr;
    }
    /**
     * èŽ·å–æ—¥æœŸçš„å¹´ä»½å­—ç¬¦ä¸²
     * @param date
     * @return
     */
    public static String getYearStr(Date date) {
        Calendar calendar = Calendar.getInstance(); // å¾—到日历
        calendar.setTime(date);// æŠŠå½“前时间赋给日历
        String yearStr = calendar.get(Calendar.YEAR) + "";//获取日
        return yearStr;
    }
    /**
     * èŽ·å–æ—¥æœŸçš„æœˆä»½å­—ç¬¦ä¸²
     * @param date
     * @return
     */
    public static String getMonthStr(Date date) {
        Calendar calendar = Calendar.getInstance(); // å¾—到日历
        calendar.setTime(date);// æŠŠå½“前时间赋给日历
        int month = calendar.get(Calendar.MONTH);//获取日
        String monthStr = month < 10 ? "0" + month : month + "";
        return monthStr;
    }
    /**
     * èŽ·å–å½“å‰æ—¶é—´ å‰åŽi天
     * ç²¾ç¡®åˆ°ç§’
     * @param i ä¸ºæ­£æ•°ä»£è¡¨åŠ i天,为负数代表减i天
     * @return
     */
    public static Date addDay(Date date, int i){
        Calendar calendar = Calendar.getInstance(); // å¾—到日历
        calendar.setTime(date);// æŠŠå½“前时间赋给日历
        calendar.add(Calendar.DAY_OF_MONTH, i); // è®¾ç½®å¤©æ•°åŠ å‡
        return calendar.getTime();
    }
    /**
     * èŽ·å–å½“å‰æ—¶é—´ å‰åŽi秒
     * ç²¾ç¡®åˆ°ç§’
     * @param i ä¸ºæ­£æ•°ä»£è¡¨åŠ i秒,为负数代表减i秒
     * @return
     */
    public static Date addSeconds(Date date, int i){
        Calendar calendar = Calendar.getInstance(); // å¾—到日历
        calendar.setTime(date);// æŠŠå½“前时间赋给日历
        calendar.add(Calendar.SECOND, i); // è®¾ç½®ç§’数加减
        return calendar.getTime();
    }
    public static Date toDate(String date, String format) {
        SimpleDateFormat df = new SimpleDateFormat(format);
        try {
            return df.parse(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
}
lxzn-module-ai/src/main/java/org/jeecg/modules/utils/PdfTurnPngUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,115 @@
package org.jeecg.modules.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.jeecg.common.util.SHA256Util;
import org.jeecg.modules.ai.vo.PhotoListVo;
import org.jeecg.modules.utils.file.FileUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
 * @author clown
 * * @date 2024/8/1
 */
@Slf4j
@Component
public class PdfTurnPngUtils {
    private static String filePngPath;
    @Value("${jeecg.path.upload}")
    public void setFilePngPath(String filePngPath) {
        PdfTurnPngUtils.filePngPath = filePngPath;
    }
    /**
     * ä½¿ç”¨pdfbox将整个pdf转换成图片
     *
     * @param fileAddress æ–‡ä»¶åœ°å€ å¦‚:C:\\Users\\user\\Desktop\\test
     * @param filename    PDF文件名不带后缀名
     * @param type        å›¾ç‰‡ç±»åž‹ png å’Œjpg
     */
    public static void pdf2png(String fileAddress, String filename, String type) {
        long startTime = System.currentTimeMillis();
        // å°†æ–‡ä»¶åœ°å€å’Œæ–‡ä»¶åæ‹¼æŽ¥æˆè·¯å¾„ æ³¨æ„ï¼šçº¿ä¸ŠçŽ¯å¢ƒä¸èƒ½ä½¿ç”¨\\拼接
        File file = new File(fileAddress + "\\" + filename + ".pdf");
        try {
            // å†™å…¥æ–‡ä»¶
            PDDocument doc = PDDocument.load(file);
            PDFRenderer renderer = new PDFRenderer(doc);
            int pageCount = doc.getNumberOfPages();
            for (int i = 0; i < pageCount ; i++) {
                // dpi为144,越高越清晰,转换越慢
                BufferedImage image = renderer.renderImageWithDPI(i, 144); // Windows native DPI
                // å°†å›¾ç‰‡å†™å‡ºåˆ°è¯¥è·¯å¾„下
                ImageIO.write(image, type,
                        new File(fileAddress + "\\备份\\" + filename + "_" + (i+1) + "." + type));
            }
            long endTime = System.currentTimeMillis();
            System.out.println("共耗时:" + ((endTime - startTime) / 1000.0) + "秒");  //转化用时
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static List<PhotoListVo> pdf2pngList(File filePdf, String type,String pathImg){
        PDDocument doc = null;
        try {
            List<PhotoListVo> files = new ArrayList<>();
            doc = PDDocument.load(filePdf);
            String pdfName = FileUtils.getFilenameNonSuffix(filePdf.getName());
            if (doc == null ) {
                return null;
            }
            PDFRenderer renderer = new PDFRenderer(doc);
            int pageCount = doc.getNumberOfPages();
            Date currentDate = DateUtil.getNow();
            String monthStr = DateUtil.format(currentDate, DateUtil.STR_YEAR_MONTH);
            String relativePath = "/" + monthStr + "/" + DateUtil.getDayStr(currentDate) + "/"+ pathImg + "/";
            //绝对路径
            String absolutePath =  filePngPath + "/" + monthStr + "/" + DateUtil.getDayStr(currentDate) + "/" + pathImg + "/";
            for (int i = 0; i < pageCount ; i++) {
                PhotoListVo pVo = new PhotoListVo();
                // dpi为144,越高越清晰,转换越慢
                BufferedImage image = renderer.renderImageWithDPI(i, 144); // Windows native DPI  144
                // å°†å›¾ç‰‡å†™å‡ºåˆ°è¯¥è·¯å¾„下
                //文件路径
                //相对路径
                String encodeFileName = SHA256Util.getSHA256Str(pdfName + "_" + (i+1) + System.currentTimeMillis());
                String imgPath = absolutePath + "/" + encodeFileName + "." + type;
                File targetFile = new File(imgPath);
                if(!targetFile.getParentFile().exists()){
                    targetFile.getParentFile().mkdirs();
                }
                ImageIO.write(image, type, targetFile);
                pVo.setImgEncodeName(encodeFileName + "." + type);
                pVo.setImgName(pdfName + "_" + (i+1));
                pVo.setImgSize(Long.valueOf(new File(imgPath).length()));
                pVo.setImgSuffix(type);
                pVo.setPageNumber(i+1);
                pVo.setImgPath(relativePath);
                files.add(pVo);
            }
            return files;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
lxzn-module-ai/src/main/java/org/jeecg/modules/utils/PdfUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,263 @@
package org.jeecg.modules.utils;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.parser.PdfTextExtractor;
import org.jeecg.modules.ai.vo.ItemPosition;
import org.jeecg.modules.ai.vo.KeyWordPosition;
import org.jeecg.modules.ai.vo.KeyWordPositionVo;
import org.jeecg.modules.ai.vo.MyTextExtractionStrategy;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
 * @author clown
 * * @date 2024/7/23
 */
public class PdfUtils {
    /**
     * ç”¨äºŽä¾›å¤–部类调用获取关键字所在PDF文件坐标
     * @param filepath
     * @param keyWords
     * @return
     */
    public static List<KeyWordPosition> getKeyWordsByPath(String filepath, String keyWords) {
        List<KeyWordPosition> matchItems = null;
        try{
            PdfReader pdfReader = new PdfReader(filepath);
            matchItems = getKeyWords(pdfReader, keyWords);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return matchItems;
    }
    /**
     * èŽ·å–å…³é”®å­—æ‰€åœ¨PDF坐标
     * @param pdfReader
     * @param keyWords
     * @return
     */
    private static List<KeyWordPosition> getKeyWords(PdfReader pdfReader, String keyWords) {
        int page = 0;
        List<KeyWordPosition> matchItems = new ArrayList<>();
        try{
            int pageNum = pdfReader.getNumberOfPages();
            StringBuilder allText = null;
            //遍历页
            for (page = 1; page <= pageNum; page++) {
                //只记录当页的所有内容,需要记录全部页放在循环外面
                List<ItemPosition> allItems = new ArrayList<>();
                //扫描内容
                MyTextExtractionStrategy myTextExtractionStrategy = new MyTextExtractionStrategy(allItems, page);
                PdfTextExtractor.getTextFromPage(pdfReader, page, myTextExtractionStrategy);
                //当页的文字内容,用于关键词匹配
                allText = new StringBuilder();
                //一个字一个字的遍历
                for (int i=0; i<allItems.size(); i++) {
                    ItemPosition item = allItems.get(i);
                    allText.append(item.getText());
                    //关键字存在连续多个块中
                    if(allText.indexOf(keyWords) != -1) {
                        KeyWordPosition keyWordPosition = new KeyWordPosition();
                        //记录关键词每个字的位置,只记录开始结束标记时会有问题
                        List<ItemPosition> listItem = new ArrayList<>();
                        for(int j=i-keyWords.length()+1; j<=i; j++) {
                            listItem.add(allItems.get(j));
                        }
                        keyWordPosition.setListItem(listItem);
                        keyWordPosition.setText(keyWords);
                        matchItems.add(keyWordPosition);
                        allText.setLength(0);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return matchItems;
    }
    public static void main(String[] args) throws Exception {
        String keyword = "一旦发生机床安全事故";
        String sourcePdf = "F:\\123.pdf";
        String watermarkPdf = "F:\\12_bak.pdf";
        Long start = System.currentTimeMillis();
        System.out.println("开始扫描....");
        List<KeyWordPosition> matchItems = getKeyWordsByPath(sourcePdf, keyword);
        System.out.println(matchItems);
        System.out.println("扫描结束["+(System.currentTimeMillis()-start)+"ms],共找到关键字["+keyword+"]出现["+matchItems.size()+"]次");
        start = System.currentTimeMillis();
        System.out.println("开始添加标记....");
        andRectangleMark(sourcePdf
                , watermarkPdf
                , matchItems
                , BaseColor.RED
                , 2
                , 2);
        //文件整理
        System.out.println("标记添加完成["+(System.currentTimeMillis()-start)+"ms]");
    }
    public static List<KeyWordPositionVo> findListWord(String sourcePath, String keyWord){
        System.out.println("开始扫描....");
        try {
            List<KeyWordPositionVo> matchItems = getKeyWordsByPathOne(sourcePath, keyWord);
            return matchItems;
        }catch (Exception e) {
            return null;
        }
    }
    /**
     * ç”¨äºŽä¾›å¤–部类调用获取关键字所在PDF文件坐标
     * @param filepath
     * @param keyWords
     * @return
     */
    public static List<KeyWordPositionVo> getKeyWordsByPathOne(String filepath, String keyWords) {
        List<KeyWordPositionVo> matchItems = null;
        try{
            PdfReader pdfReader = new PdfReader(filepath);
            matchItems = getKeyWordPage(pdfReader, keyWords);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return matchItems;
    }
    /**
     * èŽ·å–å…³é”®å­—æ‰€åœ¨PDF坐标 ä¸€é¡µåªå–第一条
     * @param pdfReader
     * @param keyWords
     * @return
     */
    private static List<KeyWordPositionVo> getKeyWordPage(PdfReader pdfReader, String keyWords) {
        int page = 0;
        List<KeyWordPositionVo> matchItems = new ArrayList<>();
        try{
            int pageNum = pdfReader.getNumberOfPages();
            StringBuilder allText = null;
            //遍历页
            for (page = 1; page <= pageNum; page++) {
                //只记录当页的所有内容,需要记录全部页放在循环外面
                List<ItemPosition> allItems = new ArrayList<>();
                //扫描内容
                MyTextExtractionStrategy myTextExtractionStrategy = new MyTextExtractionStrategy(allItems, page);
                PdfTextExtractor.getTextFromPage(pdfReader, page, myTextExtractionStrategy);
                //当页的文字内容,用于关键词匹配
                allText = new StringBuilder();
                //一个字一个字的遍历
                for (int i=0; i<allItems.size(); i++) {
                    ItemPosition item = allItems.get(i);
                    allText.append(item.getText());
                    //关键字存在连续多个块中
                    if(allText.indexOf(keyWords) != -1) {
                        KeyWordPosition keyWordPosition = new KeyWordPosition();
                        //记录关键词每个字的位置,只记录开始结束标记时会有问题
                        KeyWordPositionVo vo = new KeyWordPositionVo();
                        for(int j=i-keyWords.length()+1; j<=i; j++) {
                            if (allItems.get(j).getText().indexOf(keyWords)!= -1) {
                                vo.setPage(allItems.get(j).getPage());
                                vo.setRectangle(allItems.get(j).getRectangle());
                                vo.setText(allItems.get(j).getText());
                                vo.setRectangleString(allItems.get(j).getRectangle().toString());
                                matchItems.add(vo);
                                break;
                            }
                        }
                        break;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return matchItems;
    }
    /**
     * æ·»åŠ çŸ©å½¢æ ‡è®°
     * @param oldPath
     * @param newPath
     * @param matchItems å…³é”®è¯
     * @param color æ ‡è®°é¢œè‰²
     * @param lineWidth çº¿æ¡ç²—细
     * @param padding è¾¹æ¡†å†…边距
     * @throws DocumentException
     * @throws IOException
     */
    public static void andRectangleMark(String oldPath, String newPath, List<KeyWordPosition> matchItems, BaseColor color, int lineWidth, int padding) throws DocumentException, IOException{
        // å¾…加水印的文件
        PdfReader reader = new PdfReader(oldPath);
        // åŠ å®Œæ°´å°çš„æ–‡ä»¶
        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(newPath));
        PdfContentByte content;
        // è®¾ç½®å­—体
        // å¾ªçŽ¯å¯¹æ¯é¡µæ’å…¥æ°´å°
        for (KeyWordPosition keyWordPosition:matchItems)
        {
            //一个关键词的所有字坐标
            List<ItemPosition> oneKeywordItems = keyWordPosition.getListItem();
            for(int i=0; i<oneKeywordItems.size(); i++) {
                ItemPosition item = oneKeywordItems.get(i);
                ItemPosition preItem = i==0?null:oneKeywordItems.get(i-1);
                //带确实是否水印
                // æ°´å°çš„èµ·å§‹
                content = stamper.getOverContent(item.getPage());
                // å¼€å§‹å†™å…¥æ°´å°
                content.setLineWidth(lineWidth);
                content.setColorStroke(color);
                System.out.println(item.toString());
                //底线
                content.moveTo(item.getRectangle().getLeft()-padding, item.getRectangle().getBottom()-padding);
                content.lineTo(item.getRectangle().getRight()+padding, item.getRectangle().getBottom()-padding);
                if(i!=0 && preItem!=null && (preItem.getRectangle().getBottom()-padding)==(item.getRectangle().getBottom()-padding) && (preItem.getRectangle().getRight()+padding)!=(item.getRectangle().getLeft()-padding)) {
                    content.moveTo(preItem.getRectangle().getRight()+padding, preItem.getRectangle().getBottom()-padding);
                    content.lineTo(item.getRectangle().getLeft()-padding, item.getRectangle().getBottom()-padding);
                }
                //上线
                content.moveTo(item.getRectangle().getLeft()-padding, item.getRectangle().getTop()+padding);
                content.lineTo(item.getRectangle().getRight()+padding, item.getRectangle().getTop()+padding);
                if(i!=0 && preItem!=null && (preItem.getRectangle().getTop()+padding)==(item.getRectangle().getTop()+padding) && (preItem.getRectangle().getRight()+padding)!=(item.getRectangle().getLeft()-padding)) {
                    content.moveTo(preItem.getRectangle().getRight()+padding, preItem.getRectangle().getTop()+padding);
                    content.lineTo(item.getRectangle().getLeft()-padding, item.getRectangle().getTop()+padding);
                }
                //左线
                if(i==0) {
                    content.moveTo(item.getRectangle().getLeft()-padding, item.getRectangle().getBottom()-padding);
                    content.lineTo(item.getRectangle().getLeft()-padding, item.getRectangle().getTop()+padding);
                }
                //右线
                if(i==(oneKeywordItems.size()-1)) {
                    content.moveTo(item.getRectangle().getRight()+padding, item.getRectangle().getBottom()-padding);
                    content.lineTo(item.getRectangle().getRight()+padding, item.getRectangle().getTop()+padding);
                }
                content.stroke();
            }
        }
        stamper.close();
    }
}
lxzn-module-ai/src/main/java/org/jeecg/modules/utils/file/FileUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,327 @@
package org.jeecg.modules.utils.file;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.api.vo.FileUploadResult;
import org.jeecg.common.util.SHA256Util;
import org.jeecg.modules.utils.DateUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.Date;
@Slf4j
@Component
public class FileUtils {
    private static String fileUploadFolder;
    @Value("${jeecg.path.upload}")
    public void setFileUploadFolder(String fileUploadFolder) {
        FileUtils.fileUploadFolder = fileUploadFolder;
    }
    /**
     * æ–‡ä»¶è·¯å¾„上传保存
     * @param filePath
     * @return
     */
    public static FileUploadResult uploadFilePath(String filePath) {
        File file = new File(filePath);
        if(file == null) {
            return null;
        }
        String fileName = file.getName();
        String suffix = getFileSuffix(fileName);
        Date currentDate = DateUtil.getNow();
        String monthStr = DateUtil.format(currentDate, DateUtil.STR_YEAR_MONTH);
        //相对路径
        String relativePath = "/" + monthStr + "/" + DateUtil.getDayStr(currentDate) + "/";
        //绝对路径
        String absolutePath =  fileUploadFolder + "/" + monthStr + "/" + DateUtil.getDayStr(currentDate) + "/";
        String fileNameNonSuffix = getFilenameNonSuffix(fileName);
        if(fileNameNonSuffix == null)
            return null;
        String encodeFileName = SHA256Util.getSHA256Str(fileNameNonSuffix + System.currentTimeMillis());
        if(StringUtils.isNotBlank(suffix)) {
            encodeFileName = encodeFileName + "." + suffix;
        }
        Long b = uploadFileInput(file, absolutePath, encodeFileName);
        FileUploadResult dto = new FileUploadResult();
        dto.setFileName(fileNameNonSuffix);
        dto.setFilePath(encodeFileName);
        dto.setFilePath(relativePath);
        dto.setFileSize(b);
        dto.setFileSuffix(suffix);
        return dto;
    }
    /**
     * æ–‡ä»¶File上传保存
     * @param file
     * @return
     */
    public static FileUploadResult uploadFileInFile(File file) {
        if(file == null) {
            return null;
        }
        String fileName = file.getName();
        String suffix = getFileSuffix(fileName);
        Date currentDate = DateUtil.getNow();
        String monthStr = DateUtil.format(currentDate, DateUtil.STR_YEAR_MONTH);
        //相对路径
        String relativePath = "/" + monthStr + "/" + DateUtil.getDayStr(currentDate) + "/";
        //绝对路径
        String absolutePath =  fileUploadFolder + "/" + monthStr + "/" + DateUtil.getDayStr(currentDate) + "/";
        String fileNameNonSuffix = getFilenameNonSuffix(fileName);
        if(fileNameNonSuffix == null) {
            return null;
        }
        String encodeFileName = SHA256Util.getSHA256Str(fileNameNonSuffix + System.currentTimeMillis());
        if(StringUtils.isNotBlank(suffix)) {
            encodeFileName = encodeFileName + "." + suffix;
        }
        Long b = uploadFileInput(file, absolutePath, encodeFileName);
        FileUploadResult dto = new FileUploadResult();
        dto.setFileName(fileNameNonSuffix);
        dto.setFileEncodeName(encodeFileName);
        dto.setFilePath(relativePath);
        dto.setFileSize(b);
        dto.setFileSuffix(suffix);
        return dto;
    }
    /**
     * MultipartFile æ–¹å¼ä¿å­˜
     * @param file
     * @return
     */
    public static FileUploadResult uploadMultipartFile(MultipartFile file) {
        if(file == null || file.isEmpty()) {
            return null;
        }
        String fileName = file.getOriginalFilename();
        String suffix = getFileSuffix(fileName);
        Date currentDate = DateUtil.getNow();
        String monthStr = DateUtil.format(currentDate, DateUtil.STR_YEAR_MONTH);
        //相对路径
        String relativePath = "/" + monthStr + "/" + DateUtil.getDayStr(currentDate) + "/";
        //绝对路径
        String absolutePath =  fileUploadFolder + "/" + monthStr + "/" + DateUtil.getDayStr(currentDate) + "/";
        String fileNameNonSuffix = getFilenameNonSuffix(fileName);
        if(fileNameNonSuffix == null)
            return null;
        String encodeFileName = SHA256Util.getSHA256Str(fileNameNonSuffix + System.currentTimeMillis());
        Long fileSize = file.getSize();
        if(StringUtils.isNotBlank(suffix)) {
            encodeFileName = encodeFileName + "." + suffix;
        }
        boolean b = uploadFile(file, absolutePath, encodeFileName);
        if(!b)
            return null;
        FileUploadResult dto = new FileUploadResult();
        dto.setFileName(fileNameNonSuffix);
        dto.setFilePath(encodeFileName);
        dto.setFilePath(relativePath);
        dto.setFileSize(fileSize);
        dto.setFileSuffix(suffix);
        return dto;
    }
    public static void downLoadFile(HttpServletResponse response, String fileName, String filePath, String toFileName) {
        String absolutePath = fileUploadFolder + filePath;
        File file = new File(absolutePath , fileName);
        if(file.exists()) {
            byte[] buffer = new byte[1024];
            FileInputStream fis = null;
            BufferedInputStream bis = null;
            try {
                response.setHeader("Content-Type", "application/octet-stream;charset=utf-8"); // å‘Šè¯‰æµè§ˆå™¨è¾“出内容为流
                response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(toFileName, "UTF-8"));
                fis = new FileInputStream(file);
                bis = new BufferedInputStream(fis);
                OutputStream os = response.getOutputStream();
                int i = bis.read(buffer);
                while (i != -1) {
                    os.write(buffer, 0, i);
                    i = bis.read(buffer);
                }
            }catch (Exception e) {
                log.error(e.getMessage(), e.getStackTrace());
            }finally {
                if(bis != null) {
                    try {
                        bis.close();
                    } catch (IOException e) {
                    }
                }
                if(fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                    }
                }
            }
        }else {
        }
    }
    /**
     * ä¸Šä¼ æ–‡ä»¶å·¥å…·ç±»
     * @param file
     * @param path
     * @param fileNewName  æ–°çš„æ–‡ä»¶å
     * @return
     */
    public static Long uploadFileInput(File file, String path, String fileNewName) {
        File targetFile = new File(path, fileNewName);
        if(!targetFile.getParentFile().exists()){
            targetFile.getParentFile().mkdirs();
        }
        try {
            FileInputStream fis = new FileInputStream(file);
            FileOutputStream fos = new FileOutputStream(targetFile);
            //FileInputStream fis = new FileInputStream(file);
            byte[] bytes = new byte[1024 * 4];
            int index = 0;
            long total = 0;
            while ((index = fis.read(bytes)) != -1) {
                fos.write(bytes, 0, index);
                total =+ index;
            }
            fis.close();
            fos.flush();
            fos.close();
            return total;
        } catch (Exception e) {
            log.error(e.getMessage(), e.getStackTrace());
            return 0L;
        }
    }
    /**
     * ä¸Šä¼ æ–‡ä»¶å·¥å…·ç±»
     * @param multipartFile
     * @param path
     * @param fileNewName  æ–°çš„æ–‡ä»¶å
     * @return
     */
    public static boolean uploadFile(MultipartFile multipartFile, String path, String fileNewName) {
        File targetFile = new File(path, fileNewName);
        if(!targetFile.getParentFile().exists()){
            targetFile.getParentFile().mkdirs();
        }
        try {
            multipartFile.transferTo(targetFile);
            return true;
        } catch (Exception e) {
            log.error(e.getMessage(), e.getStackTrace());
            return false;
        }
    }
    /**
     * èŽ·å–æ–‡ä»¶åŽç¼€  æ— åŽç¼€æ–‡ä»¶è¿”回NULL
     * @param fileName
     * @return
     */
    public static String getFileSuffix(String fileName) {
        if (fileName.contains(".")) {
            String suffix = fileName.substring(fileName.lastIndexOf('.') + 1);
            return suffix;
        }else {
            return null;
        }
    }
    /**
     * èŽ·å–æ–‡ä»¶å ä¸å¸¦åŽç¼€å’Œç‚¹å·
     * @param fileName
     * @return
     */
    public static String getFilenameNonSuffix(String fileName) {
        if (fileName.contains(".")) {
            String filename = fileName.substring(0, fileName.lastIndexOf('.'));
            return filename;
        }else {
            return fileName;
        }
    }
    public static String checkFileEncode(File file) {
        String charset = "GBK";
        byte[] first3Bytes = new byte[3];
        BufferedInputStream bis = null;
        try {
            boolean checked = false;
            bis = new BufferedInputStream(new FileInputStream(file));
            bis.mark(0);
            int read = bis.read(first3Bytes, 0, 3);
            if (read == -1)
                return charset;
            if(first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE ) {
                charset = "UTF-16LE";
                checked = true;
            }else if ( first3Bytes[0] == (byte) 0xFE && first3Bytes[1] == (byte) 0xFF ) {
                charset = "UTF-16BE";
                checked = true;
            }else if (first3Bytes[0] == (byte) 0xEF && first3Bytes[1] == (byte) 0xBB && first3Bytes[2] == (byte) 0xBF ) {
                charset = "UTF-8";
                checked = true;
            }
            bis.reset();
            if (!checked) {
                while ((read = bis.read()) != -1 ) {
                    if ( read >= 0xF0 )
                        break;
                    if ( 0x80 <= read && read <= 0xBF ) // å•独出现BF以下的,也算是GBK
                        break;
                    if ( 0xC0 <= read && read <= 0xDF ) {
                        read = bis.read();
                        if ( 0x80 <= read && read <= 0xBF ) // åŒå­—节 (0xC0 - 0xDF) (0x80 - 0xBF),也可能在GBK编码内  
                             continue;
                        else
                            break;
                    } else if ( 0xE0 <= read && read <= 0xEF ) {
                        // ä¹Ÿæœ‰å¯èƒ½å‡ºé”™ï¼Œä½†æ˜¯å‡ çŽ‡è¾ƒå°
                        read = bis.read();
                        if ( 0x80 <= read && read <= 0xBF ) {
                            read = bis.read();
                            if ( 0x80 <= read && read <= 0xBF ) {
                                 charset = "UTF-8";
                                 break;
                            } else
                                break;
                        } else {
                            break;
                        }
                    }
                }
            }
            return charset;
        }catch (Exception e) {
            return null;
        }finally {
            if(bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                }
            }
        }
    }
}
lxzn-module-system/lxzn-system-start/src/main/java/org/jeecg/JeecgSystemApplication.java
@@ -8,6 +8,7 @@
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -19,6 +20,7 @@
@Slf4j
@SpringBootApplication
//@EnableAutoConfiguration(exclude={MongoAutoConfiguration.class})
@EnableScheduling
public class JeecgSystemApplication extends SpringBootServletInitializer {
    @Override
@@ -27,7 +29,7 @@
    }
    public static void main(String[] args) throws UnknownHostException {
            ConfigurableApplicationContext application = SpringApplication.run(JeecgSystemApplication.class, args);
        ConfigurableApplicationContext application = SpringApplication.run(JeecgSystemApplication.class, args);
        Environment env = application.getEnvironment();
        String ip = InetAddress.getLocalHost().getHostAddress();
        String port = env.getProperty("server.port");
lxzn-module-system/lxzn-system-start/src/main/resources/application-dev.yml
@@ -103,9 +103,9 @@
        connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
      datasource:
        master:
          url: jdbc:postgresql://192.168.253.133:5432/ai_service?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
          url: jdbc:postgresql://192.168.3.184:5432/ai_service_weiyi?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
          username: postgres
          password: ABC@13579
          password: 123456
          driverClassName: org.postgresql.Driver
  #redis é…ç½®
  redis:
@@ -147,7 +147,7 @@
    pc: http://localhost:3100
    app: http://localhost:8051
  path:
    #文件上传根目录 è®¾ç½®
    #文件上传根目录 è®¾ç½®   /home/weiyi/anaconda3/envs/aiservice
    upload: C://opt//upFiles
    #webapp文件路径
    webapp: C://opt//upFiles
@@ -263,8 +263,3 @@
      # appSecret
      client-secret: ??
      agent-id: ??
webservice:
  url: http://localhost:8081/services/EquipmentService?wsdl
  namespace: http://service.server.webservice.example.com
  statusMethod: equipmentStatus
  rateMethod: equipmentRate
lxzn-module-system/lxzn-system-start/src/main/resources/application-prod.yml
@@ -127,16 +127,16 @@
        connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
      datasource:
        master:
          url: jdbc:sqlserver://195.0.1.10:1433;databasename=SSGX_kns
          username: sa
          password: Sa123456789
          driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
         url: jdbc:postgresql://192.168.253.133:5432/ai_service?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
         username: postgres
         password: ABC@13579
         driverClassName: org.postgresql.Driver
  #redis é…ç½®
  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    password:
    password: ''
#mybatis plus è®¾ç½®
mybatis-plus:
  mapper-locations: classpath*:org/jeecg/modules/**/xml/*Mapper.xml
@@ -170,7 +170,7 @@
    pc: http://localhost:3100
    app: http://localhost:8051
  path:
    #文件上传根目录 è®¾ç½®
    #文件上传根目录设置
    upload: C://opt//upFiles
    #webapp文件路径
    webapp: C://opt//upFiles
lxzn-module-system/lxzn-system-start/src/main/resources/jeecg/jeecg_config.properties
@@ -1,7 +1,7 @@
#code_generate_project_path
project_path=F:\\java_workspace
#bussi_package[User defined]
bussi_package=org.jeecg.modules.mdc
bussi_package=org.jeecg.modules.ai
#default code path
lxzn-module-system/lxzn-system-start/src/test/java/org/jeecg/modules/ai/AiTest.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,80 @@
package org.jeecg.modules.ai;
import org.jeecg.JeecgSystemApplication;
import org.jeecg.modules.ai.service.IFilePdfInfoService;
import org.jeecg.modules.ai.service.IWebSocketAiService;
import org.jeecg.modules.ai.vo.FilePdfSelectVo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.File;
/**
 * @author clown
 * * @date 2024/7/16
 */
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,classes = JeecgSystemApplication.class)
public class AiTest {
    @Autowired
    private IFilePdfInfoService filePdfInfoService;
    @Autowired
    private IWebSocketAiService webSocketAiService;
    @Test
    public void selectChatIA() {
        webSocketAiService.selectChatIA();
    }
    @Test
    public void filePdf() {
        // æŒ‡å®šæ–‡ä»¶å¤¹è·¯å¾„
        String directoryPath = "F:\\file";
        // åˆ›å»ºFile对象表示文件夹
        File directory = new File(directoryPath);
        // èŽ·å–æ–‡ä»¶å¤¹ä¸­æ‰€æœ‰æ–‡ä»¶å’Œæ–‡ä»¶å¤¹
        File[] files = directory.listFiles();
        // æ£€æŸ¥æ–‡ä»¶æ•°ç»„是否为空以及是否存在文件
        if (files != null && files.length > 0) {
            // éåŽ†æ–‡ä»¶æ•°ç»„ï¼Œæ‰“å°æ¯ä¸ªæ–‡ä»¶çš„åå­—
            for (File file : files) {
                if (file.isFile()) { // ç¡®ä¿æ˜¯æ–‡ä»¶è€Œéžæ–‡ä»¶å¤¹
                    System.out.println(file.getName());
                    filePdfInfoService.uploadFilePdf
                            (new File(directoryPath + "\\" + file.getName()));
                }
            }
        } else {
            System.out.println("文件夹为空或不存在。");
        }
    }
    @Test
    public void filePdfImg() {
        FilePdfSelectVo vo = new FilePdfSelectVo();
        vo.setPdfContent("刀长");
        vo.setPdfFileId("1821103772084142082");
        filePdfInfoService.findImgPathList(vo);
    }
    @Test
    public void sendJsonToHttpsPost() {
      /*  String name = "{\"id\":\"683a65fd-8feb-4446-ad32-714c4785f667\",\"messages\":[{\"role\":\"user\",\"content\":\"给我讲个故事?\"}],\"stream\":true,\"max_tokens\":500}"; // JSON数据
        String url = "https://836u458t54.vicp.fun/chat/test_chat";
        String aiPost =  AiTestUils.sendJsonToHttpsPost(url, name);
        System.out.println(aiPost);*/
        String s = "\"data\": {\"id\": \"683a65fd-8feb-4446-ad32-714c4785f667\", \"created\": 1723105518, \"model\": \"YxCareer0.7\", \"choices\": [{\"index\": 0, \"delta\": {\"role\": \"assistant\", \"content\": \"\"}, \"finish_reason\": null}]}";
        System.out.println(s);
    }
}
lxzn-module-system/lxzn-system-start/src/test/java/org/jeecg/modules/mdcJc/MesTest.java
ÎļþÒÑɾ³ý
pom.xml
@@ -125,8 +125,31 @@
            <artifactId>pegdown</artifactId>
            <version>${pegdown.version}</version>
        </dependency>
        <!--使用itextpdf,比pdfbox简单些-->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.13.2</version>
        </dependency>
        <!--必须加入这个,否则中文会乱码或为空-->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>
        <dependency>
            <groupId>net.sourceforge.tess4j</groupId>
            <artifactId>tess4j</artifactId>
            <version>4.5.1</version>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <!-- spring-cloud-->