From 6780dbc86202bdc3131d8c46b4c88549808b1b10 Mon Sep 17 00:00:00 2001 From: yy2205 <2238220225@qq.com> Date: Mon, 21 Jul 2025 09:19:21 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=BE=E4=BD=B3=E7=82=9C=E4=B8=80=E6=AC=A1?= =?UTF-8?q?=E6=8F=90=E4=BA=A4=E5=B7=B2=E4=BF=AE=E6=94=B9=E7=9A=84=E6=89=80?= =?UTF-8?q?=E6=9C=89=E5=90=8E=E7=AB=AF=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/common/util/http/HttpUtils.java | 4 +- .../dal/mysql/process/ProcessMapper.java | 4 + .../service/process/ProcessServiceImpl.java | 3 +- .../yudao-module-tblist-biz/pom.xml | 45 ++ .../EcganalysisparasController.java | 53 ++- .../vo/EcgImgProcessParams.java | 112 +++++ .../vo/EcgImgProcessReqVO.java | 54 +++ .../vo/ImageProcessOptions.java | 38 ++ .../ecganalysisparas/vo/UploadFileReq.java | 31 ++ .../ecganalysisparas/vo/XmlToImgParam.java | 29 ++ .../PatientexamlistController.java | 51 ++- .../ecganalysisparas/EcganalysisparasDO.java | 2 +- .../EcganalysisparasMapper.java | 3 +- .../PatientexamlistMapper.java | 2 +- .../EcganalysisparasService.java | 50 +++ .../EcganalysisparasServiceImpl.java | 411 +++++++++++++++++- .../PatientexamlistService.java | 27 ++ .../PatientexamlistServiceImpl.java | 124 ++++++ .../EcganalysisparasMapper.xml | 5 + .../reporttemplate/ReporttemplateMapper.java | 16 +- .../reporttemplate/ReporttemplateMapper.xml | 4 + .../mapper/ultrasonic/ultrasonicMapper.xml | 1 + yudao-server/pom.xml | 18 + .../main/resources/application-222ecg.yaml | 253 +++++++++++ .../main/resources/application-LQXPACS.yaml | 253 +++++++++++ .../src/main/resources/application-local.yaml | 19 +- .../main/resources/application-online111.yaml | 206 +++++++++ .../src/main/resources/application.yaml | 32 +- .../src/main/resources/logback-spring.xml | 2 +- .../java/cn/iocoder/yudao/CommonTest.java | 86 ++++ 30 files changed, 1884 insertions(+), 54 deletions(-) create mode 100644 yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/EcgImgProcessParams.java create mode 100644 yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/EcgImgProcessReqVO.java create mode 100644 yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/ImageProcessOptions.java create mode 100644 yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/UploadFileReq.java create mode 100644 yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/XmlToImgParam.java create mode 100644 yudao-server/src/main/resources/application-222ecg.yaml create mode 100644 yudao-server/src/main/resources/application-LQXPACS.yaml create mode 100644 yudao-server/src/main/resources/application-online111.yaml create mode 100644 yudao-server/src/test/java/cn/iocoder/yudao/CommonTest.java diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java index f139cbbec..fbae80179 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java @@ -188,8 +188,8 @@ public class HttpUtils { // 默认设置POST方法 con.setRequestMethod("POST"); con.setRequestProperty("Content-Type","application/json"); - con.setConnectTimeout(10000); // 连接超时时间,单位为毫秒 - con.setReadTimeout(10000); // 读取超时时间,单位为毫秒 + con.setConnectTimeout(20000); // 连接超时时间,单位为毫秒 + con.setReadTimeout(20000); // 读取超时时间,单位为毫秒 con.setRequestProperty("User-Agent", "Mozilla/5.0 (compatible; MyClient/1.0)"); con.setRequestProperty("Connection", "keep-alive"); // 发送POST请求必须设置如下两行 diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/process/ProcessMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/process/ProcessMapper.java index 28fe0db65..47cbd9565 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/process/ProcessMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/process/ProcessMapper.java @@ -29,6 +29,10 @@ public interface ProcessMapper extends BaseMapperX { .likeIfPresent(ProcessDO::getRegId, reqVO.getRegId()) .eqIfPresent(ProcessDO::getExamId, reqVO.getExamId()) .likeIfPresent(ProcessDO::getPname, reqVO.getPname()) + .and(wrapper -> wrapper + .eq(ProcessDO::getOrgId, reqVO.getOrgId()) + .or() + .eq(ProcessDO::getOrgId, "initdefault")) .orderByDesc(ProcessDO::getApplyDateTime)); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/process/ProcessServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/process/ProcessServiceImpl.java index f0ca74a40..3fe9bd955 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/process/ProcessServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/process/ProcessServiceImpl.java @@ -70,7 +70,8 @@ public class ProcessServiceImpl implements ProcessService { @Override public PageResult> getProcessPage(ProcessPageReqVO pageReqVO) { PageResult> res = null; - + AdminUserDO user = userService.getUser(getLoginUserId()); + pageReqVO.setOrgId(user.getOrgId()); //res_temp if (pageReqVO != null) { if (pageReqVO.getProcessDate_le() != null) diff --git a/yudao-module-tblist/yudao-module-tblist-biz/pom.xml b/yudao-module-tblist/yudao-module-tblist-biz/pom.xml index bb5288f51..0859ad67d 100644 --- a/yudao-module-tblist/yudao-module-tblist-biz/pom.xml +++ b/yudao-module-tblist/yudao-module-tblist-biz/pom.xml @@ -16,6 +16,18 @@ system 模块下,我们放通用业务,支撑上层的核心业务。 例如说:用户、部门、权限、数据字典等等 + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + @@ -143,6 +155,39 @@ compile + + + net.sourceforge.tess4j + tess4j + 5.8.0 + + + org.slf4j + slf4j-simple + + + + + + + com.baidu.aip + java-sdk + 4.16.16 + + + org.slf4j + slf4j-simple + + + + + + + org.json + json + 20210307 + + diff --git a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/EcganalysisparasController.java b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/EcganalysisparasController.java index 172bba068..407ef13fc 100644 --- a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/EcganalysisparasController.java +++ b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/EcganalysisparasController.java @@ -13,11 +13,11 @@ import cn.iocoder.yudao.module.system.service.user.AdminUserService; import cn.iocoder.yudao.module.tblist.dal.dataobject.patientexamlist.PatientexamlistDO; import cn.iocoder.yudao.module.tblist.dal.mysql.ecganalysisparas.EcganalysisparasMapper; import cn.iocoder.yudao.module.tblist.dal.mysql.patientexamlist.PatientexamlistMapper; +import cn.iocoder.yudao.module.tblist.service.patientexamlist.PatientexamlistService; import cn.iocoder.yudao.module.tblist.service.positivestatistics.PositivestatisticsService; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.web.bind.annotation.*; @@ -48,6 +48,7 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti import cn.iocoder.yudao.module.tblist.controller.admin.ecganalysisparas.vo.*; import cn.iocoder.yudao.module.tblist.controller.admin.patientexamlist.vo.EcgPictureOcr; +import cn.iocoder.yudao.module.tblist.controller.admin.ecganalysisparas.vo.UploadFileReq; import cn.iocoder.yudao.module.tblist.dal.dataobject.ecganalysisparas.EcganalysisparasDO; import cn.iocoder.yudao.module.tblist.service.ecganalysisparas.EcganalysisparasService; @@ -55,8 +56,8 @@ import javax.annotation.Resource; import javax.annotation.security.PermitAll; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; -import java.net.URLEncoder; import org.springframework.web.multipart.MultipartFile; +import java.util.List; @Tag(name = "管理后台 - 心电分析数据") @RestController @@ -352,10 +353,56 @@ public class EcganalysisparasController { List dataList = ecganalysisparasService.parseEcgDataFromJsonFile(file); // 将数据转换为DO对象 - // List resultList = ecganalysisparasService.parsePhotoCreateData(dataList, EcganalysisparasDO.class); +// List resultList = ecganalysisparasService.parsePhotoCreateData(dataList, EcganalysisparasDO.class); ecganalysisparasService.createEcgFromPhotoJson(dataList); return success(true); } + + @PostMapping("/processImage") + @Operation(summary = "处理心电图片") + @PermitAll + public CommonResult> processImage(EcgImgProcessReqVO reqVO) { + ImageProcessOptions options = reqVO.getOptions(); + String imagePath = reqVO.getImagePath(); + MultipartFile image = reqVO.getImage(); + // 如果未提供选项,使用默认值 + if (options == null) { + options = ImageProcessOptions.defaultOpt(); + } + + try { + Map result; + // 优先使用图片路径 + if (imagePath != null && !imagePath.trim().isEmpty()) { + result = ecganalysisparasService.processEcgImageByPath(imagePath, reqVO.getWatermarkText(), reqVO.getHeight(), reqVO.getWidth(), options); + } else if (image != null && !image.isEmpty()) { + result = ecganalysisparasService.processEcgImage(image, reqVO.getWatermarkText(), reqVO.getHeight(), reqVO.getWidth(), options); + } else { + return error(400, "请提供图片文件或图片路径"); + } + return success(result); + } catch (Exception e) { + return error(500, e.getMessage()); + } + } + + @PostMapping("rpc-processImage") + @PermitAll + public CommonResult rpcProcess(@RequestBody EcgImgProcessParams reqVO) { + try { + String s = HttpUtils.sendPost("http://zzxmc.gw12320.com/processImage", JSONObject.toJSONString(reqVO)); +// String s = HttpUtils.sendPost("https://zzxmc.gw12320.com/processImage", reqVO); + return success(s); + }catch (IOException e) { + throw new RuntimeException(e); + } + + } + @PostMapping("rpc-uploadPdf") + @PermitAll + public CommonResult> rpcUploadPdf(@RequestBody UploadFileReq reqVO) { + return success(ecganalysisparasService.signPdf(reqVO)); + } } \ No newline at end of file diff --git a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/EcgImgProcessParams.java b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/EcgImgProcessParams.java new file mode 100644 index 000000000..9380921de --- /dev/null +++ b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/EcgImgProcessParams.java @@ -0,0 +1,112 @@ +package cn.iocoder.yudao.module.tblist.controller.admin.ecganalysisparas.vo; + +import org.springframework.web.multipart.MultipartFile; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.type.TypeReference; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import com.fasterxml.jackson.databind.DeserializationFeature; + +import javax.validation.constraints.NotBlank; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class EcgImgProcessParams { + /** + * 检查单号id + */ + @NotBlank(message = "检查单号不允许为空") + private String examId; + @NotBlank(message = "机构编码不允许为空") + private String orgId; + /** + * 图片文件 + */ + private MultipartFile image; + /** + * 图片文件路径 + */ + private String imagePath; + /** + * 水印文字 + */ + private String watermarkText; + /** + * 从诊断开始的x坐标 + */ + private Integer startX; + /** + * 从诊断开始的y坐标 + */ + private Integer startY; + /** + * 从诊断开始的高度空白覆盖 + */ + private Integer height; + /** + * 从诊断开始的宽度空白覆盖 + */ + private Integer width; + + /**(description = "是否启用OCR识别", example = "1")*/ + private Integer enableOcr; + + /**(description = "是否启用区域覆盖", example = "1")*/ + private Integer enableAreaCover; + + /**(description = "是否启用水印", example = "1")*/ + private Integer enableWatermark; + + /**(description = "是否转换为PDF", example = "0")*/ + private Integer enablePdf; + + /**(description = "水印字体", example = "宋体")*/ + private String watermarkFont; + + /**(description = "水印字体大小", example = "20")*/ + private Integer watermarkFontSize; + + /**(description = "水印颜色透明度(0-255)", example = "128")*/ + private Integer watermarkAlpha; + + /**(description = "覆盖区域颜色", example = "#FFFFFF")*/ + private String coverColor; + + public void defaultInit(){ + this.enableOcr = 0; + this.enableAreaCover = 1; + this.enableWatermark = 1; + this.enablePdf = 1; + this.watermarkFont = "宋体"; + this.watermarkFontSize = 40; + } + + /** + * 将JSON字符串转换为EcgImgProcessParams对象 + * @param jsonStr JSON字符串 + * @return EcgImgProcessParams对象 + * @throws Exception 转换异常 + */ + public static EcgImgProcessParams fromJson(String jsonStr) throws Exception { + try { + ObjectMapper mapper = new ObjectMapper(); + // 配置忽略未知字段 + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + return mapper.readValue(jsonStr, new TypeReference() {}); + } catch (Exception e) { + throw new Exception("JSON解析错误: " + e.getMessage(), e); + } + } + + /** + * 将对象转换为JSON字符串 + * @return JSON字符串 + * @throws Exception 转换异常 + */ + public String toJson() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + return mapper.writeValueAsString(this); + } +} diff --git a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/EcgImgProcessReqVO.java b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/EcgImgProcessReqVO.java new file mode 100644 index 000000000..4925abfe9 --- /dev/null +++ b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/EcgImgProcessReqVO.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.tblist.controller.admin.ecganalysisparas.vo; + +import cn.iocoder.yudao.module.tblist.controller.admin.ecganalysisparas.vo.ImageProcessOptions; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.web.multipart.MultipartFile; + +import javax.validation.constraints.NotBlank; + +/** + * 心电图诊断覆盖接收类 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class EcgImgProcessReqVO { + /** + * 检查单号id + */ + @NotBlank(message = "检查单号不允许为空") + private String examId; + @NotBlank(message = "机构编码不允许为空") + private String orgId; + /** + * 图片文件 + */ + private MultipartFile image; + /** + * 图片文件路径 + */ + private String imagePath; + /** + * 水印文字 + */ + private String watermarkText; + /** + * 从诊断开始的x坐标 + */ + private Integer startX; + /** + * 从诊断开始的y坐标 + */ + private Integer startY; + /** + * 从诊断开始的高度空白覆盖 + */ + private Integer height; + /** + * 从诊断开始的宽度空白覆盖 + */ + private Integer width; + private ImageProcessOptions options; +} diff --git a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/ImageProcessOptions.java b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/ImageProcessOptions.java new file mode 100644 index 000000000..1f8ddbfe8 --- /dev/null +++ b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/ImageProcessOptions.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.tblist.controller.admin.ecganalysisparas.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Schema(description = "图片处理选项") +@AllArgsConstructor +@NoArgsConstructor +public class ImageProcessOptions { + + @Schema(description = "是否启用OCR识别", example = "true") + private Boolean enableOcr = true; + + @Schema(description = "是否启用区域覆盖", example = "true") + private Boolean enableAreaCover = true; + + @Schema(description = "是否启用水印", example = "true") + private Boolean enableWatermark = true; + + @Schema(description = "水印字体", example = "宋体") + private String watermarkFont = "宋体"; + + @Schema(description = "水印字体大小", example = "20") + private Integer watermarkFontSize = 30; + + @Schema(description = "水印颜色透明度(0-255)", example = "128") + private Integer watermarkAlpha = 128; + + @Schema(description = "覆盖区域颜色", example = "#FFFFFF") + private String coverColor = "#FFFFFF"; +// private String coverColor ; + public static ImageProcessOptions defaultOpt(){ + return new ImageProcessOptions(true,true,true,"宋体",40,128,"#FFFFFF"); + } +} \ No newline at end of file diff --git a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/UploadFileReq.java b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/UploadFileReq.java new file mode 100644 index 000000000..813e1b463 --- /dev/null +++ b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/UploadFileReq.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.tblist.controller.admin.ecganalysisparas.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class UploadFileReq { + private String filename; + private String savePath; + private String fileStream; // 逗号分隔的字节字符串 + private String fileType; + + @Schema(description = "检查ID:体检编号、住院号、门诊号等", example = "26467") + private String examId; + @Schema(description = "机构ID", example = "29289") + private String orgId; + + @Override + public String toString() { + return "UploadFileRequest{" + + "filename='" + filename + '\'' + + ", savePath='" + savePath + '\'' + + ", fileStream='" + (fileStream != null ? fileStream.length() + " bytes" : "null") + '\'' + + ", fileType='" + fileType + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/XmlToImgParam.java b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/XmlToImgParam.java new file mode 100644 index 000000000..0c2e0d2b0 --- /dev/null +++ b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/ecganalysisparas/vo/XmlToImgParam.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.tblist.controller.admin.ecganalysisparas.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class XmlToImgParam { + /** + * xml的文件名称 + */ + private String xml_name; + /** + * 所在文件夹名称,也是机构名称 + */ + private String folder_name; + /** + * 诊断信息;需要带上 诊断信息: + */ + private String diagnosis; + /** + * 医生签名信息;需要带上:签名信息: + */ + private String signature; +} diff --git a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/patientexamlist/PatientexamlistController.java b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/patientexamlist/PatientexamlistController.java index bf815eb3f..fef8ae5f0 100644 --- a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/patientexamlist/PatientexamlistController.java +++ b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/controller/admin/patientexamlist/PatientexamlistController.java @@ -58,6 +58,7 @@ import cn.iocoder.yudao.module.tblist.dal.dataobject.patientexamlist.Patientexam import cn.iocoder.yudao.module.tblist.service.patientexamlist.PatientexamlistService; import javax.annotation.Resource; +import javax.annotation.security.PermitAll; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; @@ -110,6 +111,13 @@ public class PatientexamlistController { patientexamlistService.updatePatientexamlist(updateReqVO); return success(true); } + @PostMapping("/updateExamImage") + @Operation(summary = "更新pdf图片") + @PermitAll + public CommonResult updatePatientExamImage(@RequestBody PatientexamlistSaveReqVO updateReqVO) { + Boolean b = patientexamlistService.updatePatientexamPdfurl(updateReqVO); + return success(b); + } @PutMapping("/updateExamItemName") @Operation(summary = "更新ExamItemName") @@ -373,21 +381,14 @@ public class PatientexamlistController { @GetMapping("/examine") @Operation(summary = "超声审核更新数据") @LogRecord(type = "超声审核", subType = "审核", bizNo = "1002", success = "审核ID为{{#id}}的患者") - public CommonResult examine(@RequestParam("id") String id,@RequestParam("doctorid") String doctorid,@RequestParam("doctorname") String doctorname) { + public CommonResult examine(@RequestParam("id") String id, + @RequestParam("doctorid") String doctorid, + @RequestParam("doctorname") String doctorname, + @RequestParam("ecgid") String ecgid, + @RequestParam("doctorDiagResult") String doctorDiagResult) { - LocalDateTime dateTime = LocalDateTime.parse(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), - DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); - //获取当前登陆用户 -// AdminUserDO user = userService.getUser(getLoginUserId()); - PatientexamlistSaveReqVO updateReqVO = new PatientexamlistSaveReqVO(); - updateReqVO.setId(id); - updateReqVO.setReviewDoctorId(doctorid); - updateReqVO.setReviewDoctor(doctorname); - updateReqVO.setReviewDate(dateTime); - updateReqVO.setReviewStatus("1"); - updateReqVO.setReportstatus("已审核"); - patientexamlistService.updatePatientexamlist(updateReqVO); - return success(true); + String pdfPath = patientexamlistService.examineOption(id, doctorid, doctorname, ecgid, doctorDiagResult); + return success(pdfPath); } @GetMapping("/WholeDiagFlagCount") @@ -627,14 +628,15 @@ public class PatientexamlistController { .eq("deviceType", "ECG") .eq("orgId", orgId); - Map counts = patientexamlistMapper.selectMaps(queryWrapper).get(0); - - statistics.setTotalCount(((Number) counts.get("totalCount")).intValue()); - statistics.setAnalyzedCount(((Number) counts.get("analyzedCount")).intValue()); - statistics.setUnanalyzedCount(((Number) counts.get("unanalyzedCount")).intValue()); - statistics.setAppliedCount(((Number) counts.get("appliedCount")).intValue()); - statistics.setCriticalCount(((Number) counts.get("criticalCount")).intValue()); - + List> maps = patientexamlistMapper.selectMaps(queryWrapper); + if(maps!=null && !maps.isEmpty()) { + Map counts = maps.get(0); + statistics.setTotalCount(getInt(counts, "totalCount")); + statistics.setAnalyzedCount(getInt(counts, "analyzedCount")); + statistics.setUnanalyzedCount(getInt(counts, "unanalyzedCount")); + statistics.setAppliedCount(getInt(counts, "appliedCount")); + statistics.setCriticalCount(getInt(counts, "criticalCount")); + } // 获取阳性数量 int positiveCount = 0; positiveCount= positiveness(orgId); @@ -643,6 +645,11 @@ public class PatientexamlistController { return success(statistics); } + private int getInt(Map map, String key) { + if (map == null) return 0; // 处理map为null的情况 + Object value = map.get(key); + return (value instanceof Number) ? ((Number) value).intValue() : 0; + } ///计算阳性患者的数量 只要是阳性就算 不重复的患者 private int positiveness(String orgId) diff --git a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/dal/dataobject/ecganalysisparas/EcganalysisparasDO.java b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/dal/dataobject/ecganalysisparas/EcganalysisparasDO.java index b64953252..f572c5deb 100644 --- a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/dal/dataobject/ecganalysisparas/EcganalysisparasDO.java +++ b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/dal/dataobject/ecganalysisparas/EcganalysisparasDO.java @@ -47,7 +47,7 @@ public class EcganalysisparasDO { /** * 采集时间 */ - @TableField("collectionTime") + @TableField("CollectionTime") private LocalDateTime collectionTime; /** * 心率 diff --git a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/dal/mysql/ecganalysisparas/EcganalysisparasMapper.java b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/dal/mysql/ecganalysisparas/EcganalysisparasMapper.java index 0ae396d2e..4a3998b8b 100644 --- a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/dal/mysql/ecganalysisparas/EcganalysisparasMapper.java +++ b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/dal/mysql/ecganalysisparas/EcganalysisparasMapper.java @@ -87,7 +87,6 @@ public interface EcganalysisparasMapper extends BaseMapperX @Select(" ${sql} ") List> use_selectList(@Param("sql") String sql); - @Select("SELECT * FROM tb_ecganalysisparas WHERE examId = #{examId} AND (isDelete = '0' or isDelete is null)") - EcganalysisparasDO selectOneByExamId(@Param("examId") String examId); + EcganalysisparasDO selectOneByExamId(@Param("examId") String examId,@Param("orgId") String orgId); } \ No newline at end of file diff --git a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/dal/mysql/patientexamlist/PatientexamlistMapper.java b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/dal/mysql/patientexamlist/PatientexamlistMapper.java index 5faefef55..9577598db 100644 --- a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/dal/mysql/patientexamlist/PatientexamlistMapper.java +++ b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/dal/mysql/patientexamlist/PatientexamlistMapper.java @@ -111,7 +111,7 @@ public interface PatientexamlistMapper extends BaseMapperX { LambdaQueryWrapperX queryWrapper = new LambdaQueryWrapperX<>(); // 添加 orgId 条件 - queryWrapper.eqIfPresent(PatientexamlistDO::getOrgId, reqVO.getOrgId()); +// queryWrapper.eqIfPresent(PatientexamlistDO::getOrgId, reqVO.getOrgId()); // 添加 deviceType 条件 if (reqVO.getDeviceType() != null) { diff --git a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/service/ecganalysisparas/EcganalysisparasService.java b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/service/ecganalysisparas/EcganalysisparasService.java index 03fca0a36..ac9fafb15 100644 --- a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/service/ecganalysisparas/EcganalysisparasService.java +++ b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/service/ecganalysisparas/EcganalysisparasService.java @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.tblist.service.ecganalysisparas; import java.util.*; import java.io.IOException; +import java.awt.image.BufferedImage; +import java.awt.Point; import cn.iocoder.yudao.module.tblist.controller.admin.ecganalysisparas.vo.*; import cn.iocoder.yudao.module.tblist.controller.admin.patientexamlist.vo.EcgPictureOcr; @@ -122,4 +124,52 @@ public interface EcganalysisparasService extends IService { */ void createEcgFromPhotoJson(List list); + /** + * 处理心电图片 + * + * @param image 图片文件 + * @param watermarkText 水印文本 + * @param height 覆盖区域高度 + * @param width 覆盖区域宽度 + * @param options 处理选项 + * @return 处理结果 + */ + Map processEcgImage(MultipartFile image, String watermarkText, int height,int width, ImageProcessOptions options); + + /** + * 通过图片路径处理心电图片 + * + * @param imagePath 图片路径 + * @param watermarkText 水印文本 + * @param height 覆盖区域高度 + * @param width 覆盖区域宽度 + * @param options 处理选项 + * @return 处理结果 + */ + Map processEcgImageByPath(String imagePath, String watermarkText, int height, int width, ImageProcessOptions options); + + /** + * 查找关键字在图片中的位置 + * + * @param image 图片对象 + * @param keyword 关键字 + * @return 位置坐标 + */ + Point findKeywordPosition(BufferedImage image, String keyword); + + /** + * 处理图片指定区域 + * + * @param image 原始图片 + * @param position 位置 + * @param height 高度 + * @param width 覆盖区域宽度 + * @param watermarkText 水印文字 + * @param options 处理选项 + * @return 处理后的图片 + */ + BufferedImage processImageArea(BufferedImage image, Point position, int height,int width, String watermarkText, ImageProcessOptions options); + + + Map signPdf(UploadFileReq reqVO); } \ No newline at end of file diff --git a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/service/ecganalysisparas/EcganalysisparasServiceImpl.java b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/service/ecganalysisparas/EcganalysisparasServiceImpl.java index 67faf447e..5b50cf59c 100644 --- a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/service/ecganalysisparas/EcganalysisparasServiceImpl.java +++ b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/service/ecganalysisparas/EcganalysisparasServiceImpl.java @@ -1,7 +1,9 @@ package cn.iocoder.yudao.module.tblist.service.ecganalysisparas; +import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.lang.UUID; import cn.iocoder.yudao.framework.common.exception.ErrorCode; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; import cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO; import cn.iocoder.yudao.module.infra.service.config.ConfigService; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; @@ -13,10 +15,12 @@ import cn.iocoder.yudao.module.system.service.dept.DeptService; import cn.iocoder.yudao.module.system.service.doctor.DoctorService; import cn.iocoder.yudao.module.system.service.org.OrgUnitService; import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import cn.iocoder.yudao.module.tblist.controller.admin.patientexamlist.vo.PatientexamlistSaveReqVO; import cn.iocoder.yudao.module.tblist.dal.dataobject.patientexamlist.PatientexamlistDO; import cn.iocoder.yudao.module.tblist.dal.mysql.patientexamlist.PatientexamlistMapper; import cn.iocoder.yudao.module.tblist.dal.orgDo.OrgDO; import cn.iocoder.yudao.module.tblist.dal.orgMapper.OrgMapper; +import cn.iocoder.yudao.module.tblist.service.patientexamlist.PatientexamlistService; import cn.iocoder.yudao.module.tblist.service.patientexamlist.org.OrgService; import cn.iocoder.yudao.module.tblist.service.positivestatistics.PositivestatisticsService; import com.alibaba.fastjson.JSON; @@ -24,6 +28,8 @@ import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -39,6 +45,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; +import java.util.List; import java.util.stream.Collectors; import cn.iocoder.yudao.module.tblist.controller.admin.ecganalysisparas.vo.*; @@ -51,9 +58,25 @@ import cn.iocoder.yudao.module.tblist.dal.mysql.ecganalysisparas.Ecganalysispara import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +import net.sourceforge.tess4j.Tesseract; +import net.sourceforge.tess4j.Word; +import java.io.File; +import java.io.FileOutputStream; +import java.nio.file.Files; +import java.nio.file.Path; + + /** * 心电分析数据 Service 实现类 * @@ -61,6 +84,7 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti */ @Service @Validated +@Slf4j public class EcganalysisparasServiceImpl extends ServiceImpl implements EcganalysisparasService { @Resource @@ -88,7 +112,8 @@ public class EcganalysisparasServiceImpl extends ServiceImpl vo.setRegId(cleanValue(value, false))); fieldMappings.put("orgName", (value) -> vo.setOrgName(cleanValue(value, false))); fieldMappings.put("ecgDataFilePath", (value) -> vo.setEcgDataFilePath(cleanValue(value, false))); + fieldMappings.put("ecgJsonDataFilePath", (value) -> vo.setEcgJsonDataFilePath(cleanValue(value, false))); fieldMappings.put("qrs", (value) -> vo.setQrs(cleanValue(value, true))); fieldMappings.put("collectionTime", (value) -> { try { @@ -492,20 +521,27 @@ public class EcganalysisparasServiceImpl extends ServiceImpl { String orgName = e.getOrgName().trim(); OrgDO orgDO = orgMapper.selectOne(new LambdaQueryWrapperX() .eq(OrgDO::getOrgName, orgName)); if(orgDO != null){ e.setOrgId(orgDO.getOrgID()); - e.setEcgJsonDataFilePath("https://zzxmc.gw12320.com/ecgimage/"+e.getOrgName()+"/"+e.getEcgDataFilePath()); +// e.setEcgJsonDataFilePath("https://zzxmc.gw12320.com/ecgimage/"+e.getOrgName()+"/"+e.getEcgDataFilePath()); + String ecgDataPath = convertToHttpPath(e.getEcgDataFilePath()); + String ecgJsonDataPath = convertToHttpPath(e.getEcgJsonDataFilePath()); + e.setEcgJsonDataFilePath(ecgDataPath); + e.setEcgDataFilePath(ecgJsonDataPath); } - e.setRegId(UUID.randomUUID().toString()); + e.setRegId(UUID.randomUUID().toString());//设置患者id e.setCreateDate(LocalDateTime.now()); }); // 处理有效记录 List ecgDO = parsePhotoCreateData(list, EcganalysisparasDO.class); - + +// log.debug(ecgDO.toString()); + log.debug("ecgDO{}",list); // List patientDO = parsePhotoCreateData(list, PatientexamlistDO.class); List patientDOList = list.stream().map(ecg -> { PatientexamlistDO patient = new PatientexamlistDO(); @@ -532,7 +568,7 @@ public class EcganalysisparasServiceImpl extends ServiceImpl ecgIterator = ecgDO.iterator(); while (ecgIterator.hasNext()) { EcganalysisparasDO e = ecgIterator.next(); - EcganalysisparasDO ecganalysisparasDO = ecganalysisparasMapper.selectOneByExamId(e.getExamId()); + EcganalysisparasDO ecganalysisparasDO = ecganalysisparasMapper.selectOneByExamId(e.getExamId(),e.getOrgId()); if (ecganalysisparasDO != null) { ecgIterator.remove(); } else { @@ -549,11 +585,13 @@ public class EcganalysisparasServiceImpl extends ServiceImpl() .eq(PatientexamlistDO::getExamId, patient.getExamId()) + .eq(PatientexamlistDO::getOrgId, patient.getOrgId()) ); if (patientexamlistDO != null) { patientIterator.remove(); // 安全地移除元素 } else { patient.setId(UUID.randomUUID().toString()); + patient.setReportstatus("待分析"); } } patientexamlistMapper.insertBatch(patientDOList); @@ -635,7 +673,13 @@ public class EcganalysisparasServiceImpl extends ServiceImpl ch == '.').count() > 1) { + int dotCount = 0; + for (char c : str.toCharArray()) { + if (c == '.') { + dotCount++; + } + } + if (dotCount > 1) { return false; } @@ -653,4 +697,355 @@ public class EcganalysisparasServiceImpl extends ServiceImpl processEcgImage(MultipartFile image, String watermarkText, int height,int width, ImageProcessOptions options) { + try { + // 将图片转换为BufferedImage + BufferedImage bufferedImage = ImageIO.read(image.getInputStream()); + if (bufferedImage == null) { + throw new RuntimeException("无法读取上传的图片文件"); + } + + return processImageInternal(bufferedImage, watermarkText, height, width,options); + } catch (Exception e) { + throw new RuntimeException("图片处理失败: " + e.getMessage()); + } + } + + @Override + public Map processEcgImageByPath(String imagePath, String watermarkText, int height,int width,ImageProcessOptions options) { + try { + // 验证图片路径 + File imageFile = new File(imagePath); + if (!imageFile.exists()) { + throw new RuntimeException("图片文件不存在: " + imagePath); + } + if (!imageFile.isFile()) { + throw new RuntimeException("指定路径不是文件: " + imagePath); + } + if (!imageFile.canRead()) { + throw new RuntimeException("无法读取图片文件: " + imagePath); + } + + // 读取图片 + BufferedImage bufferedImage = ImageIO.read(imageFile); + if (bufferedImage == null) { + throw new RuntimeException("无法读取图片文件,可能不是有效的图片格式: " + imagePath); + } + + return processImageInternal(bufferedImage, watermarkText, height, width, options); + } catch (Exception e) { + throw new RuntimeException("图片处理失败: " + e.getMessage()); + } + } + + /** + * 内部处理方法,处理图片并保存 + */ + private Map processImageInternal(BufferedImage bufferedImage, String watermarkText, int height, int width,ImageProcessOptions options) { + try { + // 查找关键字位置 + Point position = null; + if (options.getEnableOcr()) { + position = findKeywordPosition(bufferedImage, "诊断"); + if (position == null) { + throw new RuntimeException("未找到'诊断'关键字"); + } + } + + // 处理图片 + BufferedImage processedImage = bufferedImage; + if (position != null && (options.getEnableAreaCover() || options.getEnableWatermark())) { + processedImage = processImageArea(bufferedImage, position, height,width, watermarkText, options); + } + + // 生成保存路径 + String saveDir = configService.getConfigByKey("ecg.image.save.path").getValue(); + if (saveDir == null || saveDir.trim().isEmpty()) { + throw new RuntimeException("图片保存路径未配置"); + } + + // 确保目录存在 + File dir = new File(saveDir); + if (!dir.exists()) { + dir.mkdirs(); + } + + // 生成文件名 + String newFilename = UUID.randomUUID().toString() + ".png"; + String savePath = saveDir + File.separator + newFilename; + + // 保存图片 + File outputFile = new File(savePath); + ImageIO.write(processedImage, "png", outputFile); + + Map result = new HashMap<>(); + result.put("imagePath", savePath); + if (position != null) { + result.put("position", position); + } + + return result; + } catch (Exception e) { + throw new RuntimeException("图片处理失败: " + e.getMessage()); + } + } + + @Override + public Point findKeywordPosition(BufferedImage image, String keyword) { + Path tempFile = null; + try { + // 创建临时文件 + tempFile = Files.createTempFile("ocr_", ".png"); + try (FileOutputStream fos = new FileOutputStream(tempFile.toFile())) { + ImageIO.write(image, "png", fos); + } + + // 使用Tesseract进行OCR识别 + Tesseract tesseract = getTesseract(); + List words = tesseract.getWords(image, 1); + + // 打印所有识别到的文字,用于调试 + System.out.println("OCR识别结果:"); + for (Word word : words) { + System.out.println("文字: " + word.getText() + ", 位置: " + word.getBoundingBox()); + } + + // 查找关键字,增加多种匹配模式 + for (Word word : words) { + String text = word.getText().trim(); + // 检查多种可能的匹配模式 + if (text.contains(keyword) || + text.contains("诊断") || + text.contains("诊 断") || + text.contains("诊 断") || + text.equals("诊断") || + text.equals("诊 断")) { + + // 获取文字位置信息 + Rectangle rect = word.getBoundingBox(); + System.out.println("找到关键字: " + text + ", 位置: " + rect); + return new Point(rect.x, rect.y); + } + } + + System.out.println("未找到关键字"); + return null; + } catch (Exception e) { + System.out.println("OCR识别失败: " + e.getMessage()); + throw new RuntimeException("OCR识别失败: " + e.getMessage()); + } finally { + // 清理临时文件 + if (tempFile != null) { + try { + Files.deleteIfExists(tempFile); + } catch (IOException e) { + // 忽略删除临时文件失败的错误 + } + } + } + } + + @Override + public BufferedImage processImageArea(BufferedImage image, Point position, int height, int width, String watermarkText, ImageProcessOptions options) { + // 创建新的图片对象 + BufferedImage processedImage = new BufferedImage( + image.getWidth(), + image.getHeight(), + BufferedImage.TYPE_INT_RGB + ); + + // 复制原始图片 + Graphics2D g2d = processedImage.createGraphics(); + g2d.drawImage(image, 0, 0, null); + + // 设置覆盖区域 + if (options.getEnableAreaCover()) { + Color coverColor = Color.decode(options.getCoverColor()); + g2d.setColor(coverColor); + // 从关键字位置开始向右覆盖 + int coverWidth = image.getWidth() - position.x; // 计算从关键字位置到图片右边缘的宽度 +// g2d.fillRect(position.x, position.y, coverWidth, height); + g2d.fillRect(position.x, position.y, width, height); + } + + // 添加水印 + if (options.getEnableWatermark() && watermarkText != null && !watermarkText.isEmpty()) { + // 在覆盖区域的中间位置添加水印 +// int watermarkX = position.x + (image.getWidth() - position.x) / 2; + int watermarkX = position.x ; + addWatermark(g2d, watermarkText, watermarkX, position.y , options); + } + + g2d.dispose(); + return processedImage; + } + + /** + * 添加水印 + */ + private void addWatermark(Graphics2D g2d, String text, int x, int y, ImageProcessOptions options) { + // 设置水印样式 +// g2d.setColor(new Color(128, 128, 128, options.getWatermarkAlpha())); + g2d.setColor(Color.BLACK); + g2d.setFont(new Font(options.getWatermarkFont(), Font.BOLD, options.getWatermarkFontSize())); + + // 获取水印文本的宽度,用于居中显示 + FontMetrics metrics = g2d.getFontMetrics(); + int textWidth = metrics.stringWidth(text); + + // 计算居中位置 + int centerX = x - (textWidth / 2); + + // 绘制水印 + g2d.drawString(text, centerX, y+50); + } + + /** + * 将BufferedImage转换为Base64字符串 + */ + private String convertToBase64(BufferedImage image) throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ImageIO.write(image, "png", outputStream); + return Base64.getEncoder().encodeToString(outputStream.toByteArray()); + } + + private Tesseract getTesseract() { + Tesseract tesseract = new Tesseract(); + // 从配置中获取Tesseract数据文件路径 + ConfigDO tessdataPath = configService.getConfigByKey("tesseract.tessdata.path"); + if (tessdataPath == null) { + throw new RuntimeException("Tesseract数据文件路径未配置"); + } + tesseract.setDatapath(tessdataPath.getValue()); + // 设置语言为中文 + tesseract.setLanguage("chi_sim"); + // 设置识别模式为单行文本 + tesseract.setPageSegMode(1); + return tesseract; + } + @Override + @Transactional + public Map signPdf(UploadFileReq reqVO) { + try { +// String remoteUrl = "http://localhost:58080/uploadFile"; + String remoteUrl = "http://zzxmc.gw12320.com/uploadFile"; + + // 构造请求参数 + Map params = new HashMap<>(); + if (reqVO.getFileStream() != null) params.put("fileStream", reqVO.getFileStream()); + if (reqVO.getFilename() != null) params.put("filename", reqVO.getFilename()); + if (reqVO.getSavePath() != null) params.put("savePath", reqVO.getSavePath()); + if (reqVO.getFileType() != null) params.put("fileType", reqVO.getFileType()); + + // 使用HttpUtils.sendPost发送请求 + String responseStr = HttpUtils.sendPost(remoteUrl, JSONObject.toJSONString(params)); + System.out.println(responseStr); + + // 解析返回结果 + Map resultMap; + try { + resultMap = JSONObject.parseObject(responseStr, Map.class); + } catch (Exception ex) { + resultMap = new HashMap<>(); + resultMap.put("raw", responseStr); + } + + // 判断远程调用是否成功 + String status = (String) resultMap.get("status"); + if ("success".equals(status)) { + // 远程调用成功,进行下一步处理 + System.out.println("远程文件上传成功,开始下一步处理..."); + + // 获取上传成功的文件信息 + String filePath = (String) resultMap.get("path"); + String filename = (String) resultMap.get("filename"); + String relativePath = (String) resultMap.get("relativePath"); + + // 将本地文件路径转换为HTTP网络路径 + String httpPath = convertToHttpPath(filePath); + System.out.println("原始路径: " + filePath); + System.out.println("转换后HTTP路径: " + httpPath); + + // TODO: 在这里添加你的下一步处理逻辑 + // 例如:调用其他服务、更新数据库、发送通知等 + PatientexamlistSaveReqVO saveReqVO = new PatientexamlistSaveReqVO(); + saveReqVO.setExamId(reqVO.getExamId()); + saveReqVO.setOrgId(reqVO.getOrgId()); + saveReqVO.setPdfurl(httpPath); + patientexamlistService.updatePatientexamPdfurl(saveReqVO); + // 构造返回结果,包含下一步处理的信息 + Map finalResult = new HashMap<>(); + finalResult.put("uploadSuccess", true); + finalResult.put("filePath", filePath); + finalResult.put("filename", filename); + finalResult.put("relativePath", relativePath); + finalResult.put("nextStep", "文件上传成功,已进入下一步处理"); + + System.out.println(finalResult); + return finalResult; + + } else { + // 远程调用失败 + System.out.println("远程文件上传失败"); + String errorMessage = (String) resultMap.get("message"); + if (errorMessage == null) { + errorMessage = "远程服务返回失败状态"; + } + + Map errorResult = new HashMap<>(); + errorResult.put("uploadSuccess", false); + errorResult.put("error", errorMessage); + errorResult.put("remoteResponse", resultMap); + + System.out.println(errorResult); + return errorResult; + } + + } catch (Exception e) { + throw exception(new ErrorCode(10004,e.getMessage())); + } + } + + /** + * 将本地文件路径转换为HTTP网络路径 + * @param filePath 本地文件路径,如:F:\陕西省咸阳市礼泉县心电图FTP\ecgimage\signature\15451248_156465.pdf + * @return HTTP网络路径,如:https://zzxmc.gw12320.com/ecgimage/signature/15451248_156465.pdf + */ + private String convertToHttpPath(String filePath) { + if (filePath == null || filePath.trim().isEmpty()) { + return filePath; + } + + try { + // 统一路径分隔符为 / + String normalizedPath = filePath.replace('\\', '/').replace("//", "/"); + + // 查找 ecgimage 目录的位置 + int ecgimageIndex = normalizedPath.toLowerCase().indexOf("/ecgimage/"); + if (ecgimageIndex == -1) { + // 如果没有找到 ecgimage 目录,尝试查找 signature 目录 + int signatureIndex = normalizedPath.toLowerCase().indexOf("/signature/"); + if (signatureIndex == -1) { + // 如果都没有找到,返回原始路径 + System.out.println("警告:未找到 ecgimage 或 signature 目录,返回原始路径: " + filePath); + return filePath; + } + // 从 signature 目录开始提取相对路径 + String relativePath = normalizedPath.substring(signatureIndex); + return "https://zzxmc.gw12320.com" + relativePath; + } + + // 从 ecgimage 目录开始提取相对路径 + String relativePath = normalizedPath.substring(ecgimageIndex); + return "https://zzxmc.gw12320.com" + relativePath; + + } catch (Exception e) { + System.out.println("路径转换失败: " + e.getMessage() + ", 原始路径: " + filePath); + return filePath; // 转换失败时返回原始路径 + } + } + + } \ No newline at end of file diff --git a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/service/patientexamlist/PatientexamlistService.java b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/service/patientexamlist/PatientexamlistService.java index f58a25735..1037cce14 100644 --- a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/service/patientexamlist/PatientexamlistService.java +++ b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/service/patientexamlist/PatientexamlistService.java @@ -2,6 +2,9 @@ package cn.iocoder.yudao.module.tblist.service.patientexamlist; import java.util.*; +import cn.iocoder.yudao.framework.common.exception.ErrorCode; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; +import cn.iocoder.yudao.module.tblist.controller.admin.ecganalysisparas.vo.UploadFileReq; import cn.iocoder.yudao.module.tblist.controller.admin.patientexamlist.vo.*; import cn.iocoder.yudao.module.tblist.dal.dataobject.patientexamlist.PatientexamlistDO; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -9,9 +12,14 @@ import cn.iocoder.yudao.module.tblist.dal.dataobject.withdrawrecord.WithdrawReco import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.IService; +import org.springframework.web.bind.annotation.RequestParam; import javax.validation.Valid; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + /** * PACS检查列表 Service 接口 * @@ -42,6 +50,25 @@ public interface PatientexamlistService extends IService { */ void updatePatientexamlist(@Valid PatientexamlistSaveReqVO updateReqVO); + /** + * 审核时修改状态和重新生成心电图 + * @param id 患者表主键id + * @param doctorid 医生id + * @param doctorname 医生姓名 + */ + String examineOption(String id, String doctorid, String doctorname,String ecgid,String doctorDiagResult); + /** + * 发送请求到心电图存放的服务器,以更新心电图 + * @param id 心电图id + */ + String sendReqUpdateEcgImg(String id, String doctorDiagResult); + /** + * 更新PACS检查列表,根据机构id(orgId)和检查单号id(examId)查询数据 + * + * @param updateReqVO 更新信息 + */ + Boolean updatePatientexamPdfurl(@Valid PatientexamlistSaveReqVO updateReqVO); + /** * 删除PACS检查列表 * diff --git a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/service/patientexamlist/PatientexamlistServiceImpl.java b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/service/patientexamlist/PatientexamlistServiceImpl.java index 5d12b2308..00bb1c5be 100644 --- a/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/service/patientexamlist/PatientexamlistServiceImpl.java +++ b/yudao-module-tblist/yudao-module-tblist-biz/src/main/java/cn/iocoder/yudao/module/tblist/service/patientexamlist/PatientexamlistServiceImpl.java @@ -1,6 +1,9 @@ package cn.iocoder.yudao.module.tblist.service.patientexamlist; +import cn.hutool.core.bean.BeanUtil; import cn.iocoder.yudao.framework.common.exception.ErrorCode; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO; import cn.iocoder.yudao.module.infra.service.config.ConfigService; @@ -8,10 +11,14 @@ import cn.iocoder.yudao.module.system.dal.dataobject.dicomworklist.*; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.dal.mysql.dicomworklist.DicomworklistMapper; import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import cn.iocoder.yudao.module.tblist.controller.admin.ecganalysisparas.vo.UploadFileReq; +import cn.iocoder.yudao.module.tblist.controller.admin.ecganalysisparas.vo.XmlToImgParam; +import cn.iocoder.yudao.module.tblist.dal.dataobject.ecganalysisparas.EcganalysisparasDO; import cn.iocoder.yudao.module.tblist.dal.dataobject.withdrawrecord.WithdrawRecordDO; import cn.iocoder.yudao.module.tblist.dal.mysql.ecganalysisparas.EcganalysisparasMapper; import cn.iocoder.yudao.module.tblist.dal.mysql.positivestatistics.PositivestatisticsMapper; import cn.iocoder.yudao.module.tblist.dal.mysql.withdrawrecord.WithdrawRecordMapper; +import cn.iocoder.yudao.module.tblist.service.ecganalysisparas.EcganalysisparasService; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; @@ -113,6 +120,123 @@ public class PatientexamlistServiceImpl extends ServiceImpl().eq(EcganalysisparasDO::getId, id)); + if (BeanUtil.isEmpty(ecganalysisparasDO)){ + throw exception(new ErrorCode(508,"心电图数据异常")); + } + String ecgDataFilePath = ecganalysisparasDO.getEcgDataFilePath(); // 图片地址 + String ecgJsonDataFilePath = ecganalysisparasDO.getEcgJsonDataFilePath(); // xml地址 + if (ecgDataFilePath.length() <= 0 || !ecgDataFilePath.contains("/")){ + throw exception(new ErrorCode(508,"心电图图片数据异常")); + } + String[] split = ecgDataFilePath.split("/"); + String xmlName = split[split.length -1]; + +// 获取患者id,根据患者id,找到机构名称和图片名称 + String regId = ecganalysisparasDO.getRegId(); + PatientexamlistDO patientexamlistDO = patientexamlistMapper.selectOne( + new LambdaQueryWrapperX() + .eq(PatientexamlistDO::getRegId, regId) + ); + if (BeanUtil.isEmpty(patientexamlistDO)){ + throw exception(new ErrorCode(509,"心电图对应的患者数据不存在")); + } + String orgName = patientexamlistDO.getOrgName(); + String orgId = patientexamlistDO.getOrgId(); + XmlToImgParam param = new XmlToImgParam(); + param.setDiagnosis("诊断信息:"+doctorDiagResult) + .setFolder_name(orgName) + .setXml_name(xmlName); +// param.setDiagnosis("诊断信息: "+doctorDiagResult) +// .setFolder_name("察右前旗乌拉哈乡卫生院") +// .setXml_name("241205007.xml"); +// 发送请求,修改心电图 + try { + ConfigDO rpcUrl = configService.getConfigByKey("rpc.ecg.generate-img.key"); + String s = HttpUtils.sendPost(rpcUrl.getValue(), JSONObject.toJSONString(param)); + JSONObject jsonObject = JSONObject.parseObject(s); + String pdf_path = jsonObject.getString("pdf_path"); + String httpPath = convertToHttpPath(pdf_path); + PatientexamlistSaveReqVO saveReqVO = new PatientexamlistSaveReqVO(); + saveReqVO.setExamId(patientexamlistDO.getExamId()); + saveReqVO.setOrgId(orgId); + saveReqVO.setPdfurl(httpPath); + updatePatientexamPdfurl(saveReqVO); + return httpPath; + } catch (IOException e) { + throw exception(new ErrorCode(509,"重新生成诊断心电图图片失败")); + } + } + /** + * 将本地文件路径转换为HTTP网络路径 + * @param filePath 本地文件路径,如:F:\陕西省咸阳市礼泉县心电图FTP\ecgimage\signature\15451248_156465.pdf + * @return HTTP网络路径,如:https://zzxmc.gw12320.com/ecgimage/signature/15451248_156465.pdf + */ + private String convertToHttpPath(String filePath) { + if (filePath == null || filePath.trim().isEmpty()) { + return filePath; + } + + try { + // 统一路径分隔符为 / + String normalizedPath = filePath.replace('\\', '/').replace("//", "/"); + + // 查找 ecgimage 目录的位置 + int ecgimageIndex = normalizedPath.toLowerCase().indexOf("/ecgimage/"); + if (ecgimageIndex == -1) { + // 如果没有找到 ecgimage 目录,尝试查找 signature 目录 + int signatureIndex = normalizedPath.toLowerCase().indexOf("/signature/"); + if (signatureIndex == -1) { + // 如果都没有找到,返回原始路径 + System.out.println("警告:未找到 ecgimage 或 signature 目录,返回原始路径: " + filePath); + return filePath; + } + // 从 signature 目录开始提取相对路径 + String relativePath = normalizedPath.substring(signatureIndex); + return "https://zzxmc.gw12320.com" + relativePath; + } + + // 从 ecgimage 目录开始提取相对路径 + String relativePath = normalizedPath.substring(ecgimageIndex); + return "https://zzxmc.gw12320.com" + relativePath; + + } catch (Exception e) { + System.out.println("路径转换失败: " + e.getMessage() + ", 原始路径: " + filePath); + return filePath; // 转换失败时返回原始路径 + } + } + @Override + public Boolean updatePatientexamPdfurl(PatientexamlistSaveReqVO updateReqVO) { + PatientexamlistDO patientexamlistDO = patientexamlistMapper.selectByExamIdKey(updateReqVO.getExamId(), updateReqVO.getOrgId()); + if (BeanUtil.isEmpty(patientexamlistDO)){ + return false; + } + patientexamlistDO.setPdfurl(updateReqVO.getPdfurl()); + patientexamlistMapper.updateById(patientexamlistDO); + return true; + } + @Override public void deletePatientexamlist(String id) { // 校验存在 diff --git a/yudao-module-tblist/yudao-module-tblist-biz/src/main/resources/mapper/ecganalysisparas/EcganalysisparasMapper.xml b/yudao-module-tblist/yudao-module-tblist-biz/src/main/resources/mapper/ecganalysisparas/EcganalysisparasMapper.xml index 0586079f2..ce9a26740 100644 --- a/yudao-module-tblist/yudao-module-tblist-biz/src/main/resources/mapper/ecganalysisparas/EcganalysisparasMapper.xml +++ b/yudao-module-tblist/yudao-module-tblist-biz/src/main/resources/mapper/ecganalysisparas/EcganalysisparasMapper.xml @@ -9,4 +9,9 @@ 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/ --> + \ No newline at end of file diff --git a/yudao-module-ultrasoniccom/yudao-module-ultrasoniccom-biz/src/main/java/cn/iocoder/yudao/module/ultrasoniccom/dal/reporttemplate/ReporttemplateMapper.java b/yudao-module-ultrasoniccom/yudao-module-ultrasoniccom-biz/src/main/java/cn/iocoder/yudao/module/ultrasoniccom/dal/reporttemplate/ReporttemplateMapper.java index f49d9a8a5..5229af37c 100644 --- a/yudao-module-ultrasoniccom/yudao-module-ultrasoniccom-biz/src/main/java/cn/iocoder/yudao/module/ultrasoniccom/dal/reporttemplate/ReporttemplateMapper.java +++ b/yudao-module-ultrasoniccom/yudao-module-ultrasoniccom-biz/src/main/java/cn/iocoder/yudao/module/ultrasoniccom/dal/reporttemplate/ReporttemplateMapper.java @@ -22,6 +22,7 @@ public interface ReporttemplateMapper extends BaseMapperX { return selectList(new LambdaQueryWrapperX() .neIfPresent(ReporttemplateDO::getIsdelete, '1') .eq(ReporttemplateDO::getOrgId, orgId) + .or(i->i.eq(ReporttemplateDO::getOrgId,"initdefault")) .orderByAsc(ReporttemplateDO::getTemplateName)); } @@ -34,13 +35,24 @@ public interface ReporttemplateMapper extends BaseMapperX { .orderByAsc(ReporttemplateDO::getTemplateName)); } - default List selectNodes_Root(String orgId) { + /* default List selectNodes_Root(String orgId) { return selectList(new LambdaQueryWrapperX() .neIfPresent(ReporttemplateDO::getIsdelete, '1') .eq(ReporttemplateDO::getDataType, '1') .eq(ReporttemplateDO::getOrgId, orgId) .orderByAsc(ReporttemplateDO::getTemplateName)); - } + }*/ + default List selectNodes_Root(String orgId) { + return selectList(new LambdaQueryWrapperX() + .neIfPresent(ReporttemplateDO::getIsdelete, '1') + .eq(ReporttemplateDO::getDataType, '1') + .and(wrapper -> wrapper + .eq(ReporttemplateDO::getOrgId, orgId) + .or() + .eq(ReporttemplateDO::getOrgId, "initdefault") + ) + .orderByAsc(ReporttemplateDO::getTemplateName)); + } //私有通用 @Select(" ${sql} ") diff --git a/yudao-module-ultrasoniccom/yudao-module-ultrasoniccom-biz/src/main/resources/mapper/reporttemplate/ReporttemplateMapper.xml b/yudao-module-ultrasoniccom/yudao-module-ultrasoniccom-biz/src/main/resources/mapper/reporttemplate/ReporttemplateMapper.xml index fd7369e8e..94d31533b 100644 --- a/yudao-module-ultrasoniccom/yudao-module-ultrasoniccom-biz/src/main/resources/mapper/reporttemplate/ReporttemplateMapper.xml +++ b/yudao-module-ultrasoniccom/yudao-module-ultrasoniccom-biz/src/main/resources/mapper/reporttemplate/ReporttemplateMapper.xml @@ -9,4 +9,8 @@ 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/ --> + \ No newline at end of file diff --git a/yudao-module-ultrasoniccom/yudao-module-ultrasoniccom-biz/src/main/resources/mapper/ultrasonic/ultrasonicMapper.xml b/yudao-module-ultrasoniccom/yudao-module-ultrasoniccom-biz/src/main/resources/mapper/ultrasonic/ultrasonicMapper.xml index 72978eb6b..b98d2fdab 100644 --- a/yudao-module-ultrasoniccom/yudao-module-ultrasoniccom-biz/src/main/resources/mapper/ultrasonic/ultrasonicMapper.xml +++ b/yudao-module-ultrasoniccom/yudao-module-ultrasoniccom-biz/src/main/resources/mapper/ultrasonic/ultrasonicMapper.xml @@ -17,5 +17,6 @@ AND regId = #{regId} + or orgId = 'initdefault' \ No newline at end of file diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index 06e89b1d0..372be260c 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -21,6 +21,24 @@ https://github.com/YunaiV/ruoyi-vue-pro + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.junit.vintage + junit-vintage-engine + + + cn.iocoder.boot yudao-module-ultrasoniccom-biz diff --git a/yudao-server/src/main/resources/application-222ecg.yaml b/yudao-server/src/main/resources/application-222ecg.yaml new file mode 100644 index 000000000..8e228689e --- /dev/null +++ b/yudao-server/src/main/resources/application-222ecg.yaml @@ -0,0 +1,253 @@ +server: + port: 8299 + +--- #################### 数据库相关配置 #################### + +spring: + # 数据源配置项 + autoconfigure: + exclude: + - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源 + - org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 默认 local 环境,不开启 Quartz 的自动配置 + - de.codecentric.boot.admin.server.config.AdminServerAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置 + - de.codecentric.boot.admin.server.ui.config.AdminServerUiAutoConfiguration # 禁用 Spring Boot Admin 的 Server UI 的自动配置 + - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置 + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 1 # 初始连接数 + min-idle: 1 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + #validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + validation-query: SELECT 1 # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + url: jdbc:mysql://127.0.0.1:3306/cloudecg?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 + # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai # MySQL Connector/J 5.X 连接的示例 + # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 + # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 + # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true;useUnicode=true;characterEncoding=utf-8 # SQLServer 连接的示例 + # url: jdbc:dm://127.0.0.1:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 + username: admin + password: flowadmin + # username: sa # SQL Server 连接的示例 + # password: Yudao@2024 # SQL Server 连接的示例 + # username: SYSDBA # DM 连接的示例 + # password: SYSDBA001 # DM 连接的示例 + slave: # 模拟从库,可根据自己需要修改 + lazy: true # 开启懒加载,保证启动速度 + url: jdbc:mysql://127.0.0.1:3306/cloudecg?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true + username: admin + password: flowadmin + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + redis: + host: 127.0.0.1 # 地址 + port: 6379 # 端口 + database: 0 # 数据库索引 + #password: 123456 # 密码,建议生产环境开启 + +--- #################### 定时任务相关配置 #################### + +# Quartz 配置项,对应 QuartzProperties 配置类 +spring: + quartz: + auto-startup: true # 本地开发环境,尽量不要开启 Job + scheduler-name: schedulerName # Scheduler 名字。默认为 schedulerName + job-store-type: jdbc # Job 存储器类型。默认为 memory 表示内存,可选 jdbc 使用数据库。 + wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true + properties: # 添加 Quartz Scheduler 附加属性,更多可以看 http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/configuration.html 文档 + org: + quartz: + # Scheduler 相关配置 + scheduler: + instanceName: schedulerName + instanceId: AUTO # 自动生成 instance ID + # JobStore 相关配置 + jobStore: + # JobStore 实现类。可见博客:https://blog.csdn.net/weixin_42458219/article/details/122247162 + class: org.springframework.scheduling.quartz.LocalDataSourceJobStore + isClustered: true # 是集群模式 + clusterCheckinInterval: 15000 # 集群检查频率,单位:毫秒。默认为 15000,即 15 秒 + misfireThreshold: 60000 # misfire 阀值,单位:毫秒。 + # 线程池相关配置 + threadPool: + threadCount: 25 # 线程池大小。默认为 10 。 + threadPriority: 5 # 线程优先级 + class: org.quartz.simpl.SimpleThreadPool # 线程池类型 + jdbc: # 使用 JDBC 的 JobStore 的时候,JDBC 的配置 + initialize-schema: NEVER # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ,我们手动创建表结构。 + +--- #################### 消息队列相关 #################### + +# rocketmq 配置项,对应 RocketMQProperties 配置类 +rocketmq: + name-server: 127.0.0.1:9876 # RocketMQ Namesrv + +spring: + # RabbitMQ 配置项,对应 RabbitProperties 配置类 + rabbitmq: + host: 127.0.0.1 # RabbitMQ 服务的地址 + port: 5672 # RabbitMQ 服务的端口 + username: rabbit # RabbitMQ 服务的账号 + password: rabbit # RabbitMQ 服务的密码 + # Kafka 配置项,对应 KafkaProperties 配置类 + kafka: + bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 + +--- #################### 监控相关配置 #################### + +# Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + +# Spring Boot Admin 配置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关配置 + client: + url: http://127.0.0.1:${server.port}/${spring.boot.admin.context-path} # 设置 Spring Boot Admin Server 地址 + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + # Spring Boot Admin Server 服务端的相关配置 + context-path: /admin # 配置 Spring + +# 日志文件配置 +logging: + file: + name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径 + level: + # 配置自己写的 MyBatis Mapper 打印日志 + cn.iocoder.yudao.module.bpm.dal.mysql: debug + cn.iocoder.yudao.module.infra.dal.mysql: debug + cn.iocoder.yudao.module.infra.dal.mysql.logger.ApiErrorLogMapper: INFO # 配置 ApiErrorLogMapper 的日志级别为 info,避免和 GlobalExceptionHandler 重复打印 + cn.iocoder.yudao.module.infra.dal.mysql.job.JobLogMapper: INFO # 配置 JobLogMapper 的日志级别为 info + cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper: INFO # 配置 FileConfigMapper 的日志级别为 info + cn.iocoder.yudao.module.pay.dal.mysql: debug + cn.iocoder.yudao.module.pay.dal.mysql.notify.PayNotifyTaskMapper: INFO # 配置 PayNotifyTaskMapper 的日志级别为 info + cn.iocoder.yudao.module.system.dal.mysql: debug + cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper: INFO # 配置 SmsChannelMapper 的日志级别为 info + cn.iocoder.yudao.module.tool.dal.mysql: debug + cn.iocoder.yudao.module.member.dal.mysql: debug + cn.iocoder.yudao.module.trade.dal.mysql: debug + cn.iocoder.yudao.module.promotion.dal.mysql: debug + cn.iocoder.yudao.module.statistics.dal.mysql: debug + cn.iocoder.yudao.module.crm.dal.mysql: debug + cn.iocoder.yudao.module.erp.dal.mysql: debug + cn.iocoder.yudao.module.ultrasoniccom.dal: debug + cn.iocoder.yudao.module.tblist.dal: debug + org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 + +debug: false + +--- #################### 微信公众号、小程序相关配置 #################### +wx: + mp: # 公众号配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档 + # app-id: wx041349c6f39b268b # 测试号(牛希尧提供的) + # secret: 5abee519483bc9f8cb37ce280e814bd0 + app-id: wx5b23ba7a5589ecbb # 测试号(自己的) + secret: 2a7b3b20c537e52e74afd395eb85f61f + # app-id: wxa69ab825b163be19 # 测试号(Kongdy 提供的) + # secret: bd4f9fab889591b62aeac0d7b8d8b4a0 + # 存储配置,解决 AccessToken 的跨节点的共享 + config-storage: + type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 + key-prefix: wx # Redis Key 的前缀 + http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 + miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档 + # appid: wx62056c0d5e8db250 # 测试号(牛希尧提供的) + # secret: 333ae72f41552af1e998fe1f54e1584a + appid: wx63c280fe3248a3e7 # wenhualian的接口测试号 + secret: 6f270509224a7ae1296bbf1c8cb97aed + # appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的) + # secret: 4a1a04e07f6a4a0751b39c3064a92c8b + config-storage: + type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 + key-prefix: wa # Redis Key 的前缀 + http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 + +--- #################### 芋道相关配置 #################### + +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + captcha: + enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试; + security: + mock-enable: true + xss: + enable: false + exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系 + - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 + - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 + pay: + order-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/order # 支付渠道的【支付】回调地址 + refund-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/refund # 支付渠道的【退款】回调地址 + access-log: # 访问日志的配置项 + enable: false + demo: false # 关闭演示模式 + tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc + +justauth: + enabled: true + type: + DINGTALK: # 钉钉 + client-id: dingvrnreaje3yqvzhxg + client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI + ignore-check-redirect-uri: true + WECHAT_ENTERPRISE: # 企业微信 + client-id: wwd411c69a39ad2e54 + client-secret: 1wTb7hYxnpT2TUbIeHGXGo7T0odav1ic10mLdyyATOw + agent-id: 1000004 + ignore-check-redirect-uri: true + # noinspection SpringBootApplicationYaml + WECHAT_MINI_APP: # 微信小程序 + client-id: ${wx.miniapp.appid} + client-secret: ${wx.miniapp.secret} + ignore-check-redirect-uri: true + ignore-check-state: true # 微信小程序,不会使用到 state,所以不进行校验 + WECHAT_MP: # 微信公众号 + client-id: ${wx.mp.app-id} + client-secret: ${wx.mp.secret} + ignore-check-redirect-uri: true + cache: + type: REDIS + prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: + timeout: 5m # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 diff --git a/yudao-server/src/main/resources/application-LQXPACS.yaml b/yudao-server/src/main/resources/application-LQXPACS.yaml new file mode 100644 index 000000000..1041b4df8 --- /dev/null +++ b/yudao-server/src/main/resources/application-LQXPACS.yaml @@ -0,0 +1,253 @@ +server: + port: 8075 + +--- #################### 数据库相关配置 #################### + +spring: + # 数据源配置项 + autoconfigure: + exclude: + - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源 + - org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 默认 local 环境,不开启 Quartz 的自动配置 + - de.codecentric.boot.admin.server.config.AdminServerAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置 + - de.codecentric.boot.admin.server.ui.config.AdminServerUiAutoConfiguration # 禁用 Spring Boot Admin 的 Server UI 的自动配置 + - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置 + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 1 # 初始连接数 + min-idle: 1 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + #validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + validation-query: SELECT 1 # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + url: jdbc:mysql://127.0.0.1:3306/pacs?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 + # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai # MySQL Connector/J 5.X 连接的示例 + # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 + # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 + # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true;useUnicode=true;characterEncoding=utf-8 # SQLServer 连接的示例 + # url: jdbc:dm://127.0.0.1:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 + username: admin + password: flowadmin + # username: sa # SQL Server 连接的示例 + # password: Yudao@2024 # SQL Server 连接的示例 + # username: SYSDBA # DM 连接的示例 + # password: SYSDBA001 # DM 连接的示例 + slave: # 模拟从库,可根据自己需要修改 + lazy: true # 开启懒加载,保证启动速度 + url: jdbc:mysql://127.0.0.1:3306/pacs?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true + username: admin + password: flowadmin + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + redis: + host: 127.0.0.1 # 地址 + port: 6379 # 端口 + database: 0 # 数据库索引 + password: dell@redis # 密码,建议生产环境开启 + +--- #################### 定时任务相关配置 #################### + +# Quartz 配置项,对应 QuartzProperties 配置类 +spring: + quartz: + auto-startup: true # 本地开发环境,尽量不要开启 Job + scheduler-name: schedulerName # Scheduler 名字。默认为 schedulerName + job-store-type: jdbc # Job 存储器类型。默认为 memory 表示内存,可选 jdbc 使用数据库。 + wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true + properties: # 添加 Quartz Scheduler 附加属性,更多可以看 http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/configuration.html 文档 + org: + quartz: + # Scheduler 相关配置 + scheduler: + instanceName: schedulerName + instanceId: AUTO # 自动生成 instance ID + # JobStore 相关配置 + jobStore: + # JobStore 实现类。可见博客:https://blog.csdn.net/weixin_42458219/article/details/122247162 + class: org.springframework.scheduling.quartz.LocalDataSourceJobStore + isClustered: true # 是集群模式 + clusterCheckinInterval: 15000 # 集群检查频率,单位:毫秒。默认为 15000,即 15 秒 + misfireThreshold: 60000 # misfire 阀值,单位:毫秒。 + # 线程池相关配置 + threadPool: + threadCount: 25 # 线程池大小。默认为 10 。 + threadPriority: 5 # 线程优先级 + class: org.quartz.simpl.SimpleThreadPool # 线程池类型 + jdbc: # 使用 JDBC 的 JobStore 的时候,JDBC 的配置 + initialize-schema: NEVER # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ,我们手动创建表结构。 + +--- #################### 消息队列相关 #################### + +# rocketmq 配置项,对应 RocketMQProperties 配置类 +rocketmq: + name-server: 127.0.0.1:9876 # RocketMQ Namesrv + +spring: + # RabbitMQ 配置项,对应 RabbitProperties 配置类 + rabbitmq: + host: 127.0.0.1 # RabbitMQ 服务的地址 + port: 5672 # RabbitMQ 服务的端口 + username: rabbit # RabbitMQ 服务的账号 + password: rabbit # RabbitMQ 服务的密码 + # Kafka 配置项,对应 KafkaProperties 配置类 + kafka: + bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 + +--- #################### 监控相关配置 #################### + +# Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + +# Spring Boot Admin 配置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关配置 + client: + url: http://127.0.0.1:${server.port}/${spring.boot.admin.context-path} # 设置 Spring Boot Admin Server 地址 + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + # Spring Boot Admin Server 服务端的相关配置 + context-path: /admin # 配置 Spring + +# 日志文件配置 +logging: + file: + name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径 + level: + # 配置自己写的 MyBatis Mapper 打印日志 + cn.iocoder.yudao.module.bpm.dal.mysql: debug + cn.iocoder.yudao.module.infra.dal.mysql: debug + cn.iocoder.yudao.module.infra.dal.mysql.logger.ApiErrorLogMapper: INFO # 配置 ApiErrorLogMapper 的日志级别为 info,避免和 GlobalExceptionHandler 重复打印 + cn.iocoder.yudao.module.infra.dal.mysql.job.JobLogMapper: INFO # 配置 JobLogMapper 的日志级别为 info + cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper: INFO # 配置 FileConfigMapper 的日志级别为 info + cn.iocoder.yudao.module.pay.dal.mysql: debug + cn.iocoder.yudao.module.pay.dal.mysql.notify.PayNotifyTaskMapper: INFO # 配置 PayNotifyTaskMapper 的日志级别为 info + cn.iocoder.yudao.module.system.dal.mysql: debug + cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper: INFO # 配置 SmsChannelMapper 的日志级别为 info + cn.iocoder.yudao.module.tool.dal.mysql: debug + cn.iocoder.yudao.module.member.dal.mysql: debug + cn.iocoder.yudao.module.trade.dal.mysql: debug + cn.iocoder.yudao.module.promotion.dal.mysql: debug + cn.iocoder.yudao.module.statistics.dal.mysql: debug + cn.iocoder.yudao.module.crm.dal.mysql: debug + cn.iocoder.yudao.module.erp.dal.mysql: debug + cn.iocoder.yudao.module.ultrasoniccom.dal: debug + cn.iocoder.yudao.module.tblist.dal: debug + org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 + +debug: false + +--- #################### 微信公众号、小程序相关配置 #################### +wx: + mp: # 公众号配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档 + # app-id: wx041349c6f39b268b # 测试号(牛希尧提供的) + # secret: 5abee519483bc9f8cb37ce280e814bd0 + app-id: wx5b23ba7a5589ecbb # 测试号(自己的) + secret: 2a7b3b20c537e52e74afd395eb85f61f + # app-id: wxa69ab825b163be19 # 测试号(Kongdy 提供的) + # secret: bd4f9fab889591b62aeac0d7b8d8b4a0 + # 存储配置,解决 AccessToken 的跨节点的共享 + config-storage: + type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 + key-prefix: wx # Redis Key 的前缀 + http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 + miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档 + # appid: wx62056c0d5e8db250 # 测试号(牛希尧提供的) + # secret: 333ae72f41552af1e998fe1f54e1584a + appid: wx63c280fe3248a3e7 # wenhualian的接口测试号 + secret: 6f270509224a7ae1296bbf1c8cb97aed + # appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的) + # secret: 4a1a04e07f6a4a0751b39c3064a92c8b + config-storage: + type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 + key-prefix: wa # Redis Key 的前缀 + http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 + +--- #################### 芋道相关配置 #################### + +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + captcha: + enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试; + security: + mock-enable: true + xss: + enable: false + exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系 + - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 + - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 + pay: + order-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/order # 支付渠道的【支付】回调地址 + refund-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/refund # 支付渠道的【退款】回调地址 + access-log: # 访问日志的配置项 + enable: false + demo: false # 关闭演示模式 + tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc + +justauth: + enabled: true + type: + DINGTALK: # 钉钉 + client-id: dingvrnreaje3yqvzhxg + client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI + ignore-check-redirect-uri: true + WECHAT_ENTERPRISE: # 企业微信 + client-id: wwd411c69a39ad2e54 + client-secret: 1wTb7hYxnpT2TUbIeHGXGo7T0odav1ic10mLdyyATOw + agent-id: 1000004 + ignore-check-redirect-uri: true + # noinspection SpringBootApplicationYaml + WECHAT_MINI_APP: # 微信小程序 + client-id: ${wx.miniapp.appid} + client-secret: ${wx.miniapp.secret} + ignore-check-redirect-uri: true + ignore-check-state: true # 微信小程序,不会使用到 state,所以不进行校验 + WECHAT_MP: # 微信公众号 + client-id: ${wx.mp.app-id} + client-secret: ${wx.mp.secret} + ignore-check-redirect-uri: true + cache: + type: REDIS + prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: + timeout: 5m # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index 25ea8b627..671305c58 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -48,23 +48,28 @@ spring: primary: master datasource: master: - url: jdbc:mysql://114.55.171.231:3306/cloudpacs?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 +# url: jdbc:mysql://114.55.171.231:3306/cloudpacs?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 + url: jdbc:mysql://localhost:3306/ecg?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai # MySQL Connector/J 5.X 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true;useUnicode=true;characterEncoding=utf-8 # SQLServer 连接的示例 # url: jdbc:dm://127.0.0.1:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 - username: admin - password: flowadmin +# username: admin +# password: flowadmin + username: root + password: root # username: sa # SQL Server 连接的示例 # password: Yudao@2024 # SQL Server 连接的示例 # username: SYSDBA # DM 连接的示例 # password: SYSDBA001 # DM 连接的示例 slave: # 模拟从库,可根据自己需要修改 lazy: true # 开启懒加载,保证启动速度 - url: jdbc:mysql://114.55.171.231:3306/cloudpacs?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true - username: admin - password: flowadmin + url: jdbc:mysql://localhost:3306/ecg?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true + # username: admin + # password: flowadmin + username: root + password: root # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 redis: @@ -175,7 +180,7 @@ logging: cn.iocoder.yudao.module.ultrasoniccom.dal: debug cn.iocoder.yudao.module.tblist.dal: debug org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 - + cn.iocoder.yudao.module.tblist: debug debug: false --- #################### 微信公众号、小程序相关配置 #################### diff --git a/yudao-server/src/main/resources/application-online111.yaml b/yudao-server/src/main/resources/application-online111.yaml new file mode 100644 index 000000000..bfe0c0462 --- /dev/null +++ b/yudao-server/src/main/resources/application-online111.yaml @@ -0,0 +1,206 @@ +--- #################### 相关配置 #################### + +server: + port: 8072 + +--- #################### 数据库相关配置 #################### + +spring: + # 数据源配置项 + autoconfigure: + exclude: + - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源 + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 5 # 初始连接数 + min-idle: 10 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + validation-query: SELECT 1 # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + url: jdbc:mysql://192.168.1.12:3307/ecg?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 + username: root + password: AeDqSWZBk6&h + slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改 + lazy: true # 开启懒加载,保证启动速度 + url: jdbc:mysql://192.168.1.12:3307/ecg?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 + username: root + password: AeDqSWZBk6&h + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + redis: + host: 192.168.1.12 # 地址 + port: 6379 # 端口 + database: 2 # 数据库索引 + password: 123456 # 密码,建议生产环境开启 + +--- #################### 定时任务相关配置 #################### + +# Quartz 配置项,对应 QuartzProperties 配置类 +spring: + quartz: + auto-startup: true # 测试环境,需要开启 Job + scheduler-name: schedulerName # Scheduler 名字。默认为 schedulerName + job-store-type: jdbc # Job 存储器类型。默认为 memory 表示内存,可选 jdbc 使用数据库。 + wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true + properties: # 添加 Quartz Scheduler 附加属性,更多可以看 http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/configuration.html 文档 + org: + quartz: + # Scheduler 相关配置 + scheduler: + instanceName: schedulerName + instanceId: AUTO # 自动生成 instance ID + # JobStore 相关配置 + jobStore: + # JobStore 实现类。可见博客:https://blog.csdn.net/weixin_42458219/article/details/122247162 + class: org.springframework.scheduling.quartz.LocalDataSourceJobStore + isClustered: true # 是集群模式 + clusterCheckinInterval: 15000 # 集群检查频率,单位:毫秒。默认为 15000,即 15 秒 + misfireThreshold: 60000 # misfire 阀值,单位:毫秒。 + # 线程池相关配置 + threadPool: + threadCount: 25 # 线程池大小。默认为 10 。 + threadPriority: 5 # 线程优先级 + class: org.quartz.simpl.SimpleThreadPool # 线程池类型 + jdbc: # 使用 JDBC 的 JobStore 的时候,JDBC 的配置 + initialize-schema: NEVER # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ,我们手动创建表结构。 + +--- #################### 消息队列相关 #################### + +# rocketmq 配置项,对应 RocketMQProperties 配置类 +rocketmq: + name-server: 127.0.0.1:9876 # RocketMQ Namesrv + +spring: + # RabbitMQ 配置项,对应 RabbitProperties 配置类 + rabbitmq: + host: 127.0.0.1 # RabbitMQ 服务的地址 + port: 5672 # RabbitMQ 服务的端口 + username: guest # RabbitMQ 服务的账号 + password: guest # RabbitMQ 服务的密码 + # Kafka 配置项,对应 KafkaProperties 配置类 + kafka: + bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 + +--- #################### 监控相关配置 #################### + +# Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + +# Spring Boot Admin 配置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关配置 + client: + url: http://127.0.0.1:${server.port}/${spring.boot.admin.context-path} # 设置 Spring Boot Admin Server 地址 + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + # Spring Boot Admin Server 服务端的相关配置 + context-path: /admin # 配置 Spring + +# 日志文件配置 +logging: + file: + name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径 + +--- #################### 微信公众号相关配置 #################### +wx: # 参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档 + mp: + # 公众号配置(必填) + app-id: wx041349c6f39b268b + secret: 5abee519483bc9f8cb37ce280e814bd0 + # 存储配置,解决 AccessToken 的跨节点的共享 + config-storage: + type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 + key-prefix: wx # Redis Key 的前缀 + http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 + miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档 + appid: wx63c280fe3248a3e7 + secret: 6f270509224a7ae1296bbf1c8cb97aed + config-storage: + type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 + key-prefix: wa # Redis Key 的前缀 + http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 + +--- #################### 芋道相关配置 #################### + +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + xss: + enable: false + exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系 + - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 + - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 + pay: + order-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/order # 支付渠道的【支付】回调地址 + refund-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/refund # 支付渠道的【退款】回调地址 + demo: false # 开启演示模式 + tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc + +justauth: + enabled: true + type: + DINGTALK: # 钉钉 + client-id: dingvrnreaje3yqvzhxg + client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI + ignore-check-redirect-uri: true + WECHAT_ENTERPRISE: # 企业微信 + client-id: wwd411c69a39ad2e54 + client-secret: 1wTb7hYxnpT2TUbIeHGXGo7T0odav1ic10mLdyyATOw + agent-id: 1000004 + ignore-check-redirect-uri: true + # noinspection SpringBootApplicationYaml + WECHAT_MINI_APP: # 微信小程序 + client-id: ${wx.miniapp.appid} + client-secret: ${wx.miniapp.secret} + ignore-check-redirect-uri: true + ignore-check-state: true # 微信小程序,不会使用到 state,所以不进行校验 + WECHAT_MP: # 微信公众号 + client-id: ${wx.mp.app-id} + client-secret: ${wx.mp.secret} + ignore-check-redirect-uri: true + cache: + type: REDIS + prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: + timeout: 5m # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 + diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index a676203f9..bfe30e603 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -3,7 +3,10 @@ spring: name: yudao-server profiles: - active: 222ecg +# active: LQXPACS +# active: 222ecg +# active: online111 + active: local main: allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。 @@ -176,6 +179,16 @@ yudao: - /admin-api/system/zhuoziSSO/thirdInfoLogin - /admin-api/tblist/ecganalysisparas/getIdCardEcgData - /admin-api/tblist/patientexamlist/getplexamidinfo + - /adminecg/admin-api/tblist/ecganalysisparas/parsePhotoCreateData + - /admin-api/tblist/ecganalysisparas/parsePhotoCreateData + - /adminecg/admin-api/tblist/ecganalysisparas/processImage + - /admin-api/tblist/ecganalysisparas/processImage + - /adminecg/admin-api/tblist/patientexamlist/updateExamImage + - /admin-api/tblist/patientexamlist/updateExamImage + - /adminecg/admin-api/tblist/ecganalysisparas/rpc-processImage + - /admin-api/tblist/ecganalysisparas/rpc-processImage + - /adminecg/admin-api/tblist/ecganalysisparas/rpc-uploadPdf + - /admin-api/tblist/ecganalysisparas/rpc-uploadPdf websocket: enable: true # websocket的开关 path: /infra/ws # 路径 @@ -237,6 +250,16 @@ yudao: - /admin-api/system/zhuoziSSO/thirdInfoLogin - /admin-api/tblist/ecganalysisparas/getIdCardEcgData - /admin-api/tblist/patientexamlist/getplexamidinfo + - /admin-api/tblist/ecganalysisparas/parsePhotoCreateData + - /adminecg/admin-api/tblist/ecganalysisparas/parsePhotoCreateData + - /admin-api/tblist/ecganalysisparas/processImage + - /adminecg/admin-api/tblist/ecganalysisparas/processImage + - /adminecg/admin-api/tblist/patientexamlist/updateExamImage + - /admin-api/tblist/patientexamlist/updateExamImage + - /adminecg/admin-api/tblist/ecganalysisparas/rpc-processImage + - /admin-api/tblist/ecganalysisparas/rpc-processImage + - /adminecg/admin-api/tblist/ecganalysisparas/rpc-uploadPdf + - /admin-api/tblist/ecganalysisparas/rpc-uploadPdf ignore-tables: - system_tenant - system_tenant_package @@ -280,6 +303,7 @@ yudao: - tmp_report_data_1 - tmp_report_data_income - tb_patientexamlist + - tb_ecganalysisparas sms-code: # 短信验证码相关的配置项 expire-times: 10m send-frequency: 1m @@ -308,6 +332,6 @@ jeecg: jmreport: saas-mode: tenant #土贵乌拉 需要的配置 项目名称:/admin adminbl adminecg -server: - servlet: - context-path: /adminecg \ No newline at end of file +#server: +# servlet: +# context-path: /adminecg \ No newline at end of file diff --git a/yudao-server/src/main/resources/logback-spring.xml b/yudao-server/src/main/resources/logback-spring.xml index eab68debd..9fd5d2ab1 100644 --- a/yudao-server/src/main/resources/logback-spring.xml +++ b/yudao-server/src/main/resources/logback-spring.xml @@ -65,7 +65,7 @@ - + diff --git a/yudao-server/src/test/java/cn/iocoder/yudao/CommonTest.java b/yudao-server/src/test/java/cn/iocoder/yudao/CommonTest.java new file mode 100644 index 000000000..b9a21f515 --- /dev/null +++ b/yudao-server/src/test/java/cn/iocoder/yudao/CommonTest.java @@ -0,0 +1,86 @@ +package cn.iocoder.yudao; + +import cn.iocoder.yudao.server.YudaoServerApplication; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.TestOnly; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; + + +import java.util.HashMap; +import java.util.Map; + +@SpringBootTest(classes = YudaoServerApplication.class) +@Slf4j +public class CommonTest { + @Test + public void send(){ +// Logger logger = LoggerFactory.getLogger(ScheduledRequestSender.class); + RestTemplate restTemplate = new RestTemplate(); + Integer maxAttempts = 5; + for (int i = 0; i < maxAttempts; i++) { + String url = "https://pacs.gw12320.com/adminecg/admin-api/tblist/ecganalysisparas/parsePhotoCreateData"; + try { +// Thread.sleep(60000); // 暂停1分钟(单位:毫秒) + // 1. 准备请求头(JSON格式) + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"); + headers.set("Accept", "application/json"); + // 可选:添加其他请求头,如认证信息 + // headers.add("Authorization", "Bearer token"); + + // 2. 准备请求体(根据接口需求构造JSON或表单数据) + // 示例:JSON请求体 + Map requestBody = new HashMap<>(); + String body = "[ {\n" + + " \"file_path\" : \"C:\\\\Users\\\\lenovo\\\\IdeaProjects\\\\tianJing\\\\tools\\\\.\\\\礼泉县裴寨卫生院\\\\K021180213001N0013_20250414100509.jpg\",\n" + + " \"extracted_data\" : {\n" + + " \"pr\" : \"130 ms\",\n" + + " \"orgName\" : \"礼泉县裴寨卫生院\",\n" + + " \"collectionTime\" : \"2025-04-14\",\n" + + " \"gender\" : \"男\",\n" + + " \"hr\" : \"86 bpm\",\n" + + " \"ecgDataFilePath\" : \"K021180213001N0013_20250414100509.jpg\",\n" + + " \"pAxle/qrsAxle/tAxle\" : \"69/-33/40 °\",\n" + + " \"qrs\" : \"130ms\",\n" + + " \"rv5/sv1\" : \"1.362/0.145 mv\",\n" + + " \"examId\" : \"20250414100226\",\n" + + " \"name\" : \"郑 玉 祥\",\n" + + " \"qt/qtc\" : \"362/408 ms\",\n" + + " \"department\" : \"\",\n" + + " \"bed_number\" : \"\",\n" + + " \"rv5Sv1\" : \"1.507 mv\"\n" + + " },\n" + + " \"process_time\" : \"2025-05-10T11:42:46.457\",\n" + + " \"directory\" : \"C:\\\\Users\\\\lenovo\\\\IdeaProjects\\\\tianJing\\\\tools\\\\.\\\\礼泉县裴寨卫生院\"\n" + + "}]"; + requestBody.put("file", body); +// requestBody.put("key2", "value2"); + + // 3. 封装请求实体 + HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); + + // 4. 发送POST请求并记录日志 + log.info("第{}次POST请求开始,URL: {}, 请求体: {}", i + 1, url, requestBody); + ResponseEntity responseEntity = restTemplate.postForEntity(url, requestEntity, String.class); + + // 5. 记录响应详情(状态码、响应头、响应体) + log.info("第{}次响应: 状态码={}, 响应体={}", + i + 1, + responseEntity.getStatusCode(), + responseEntity.getBody()); +// Thread.sleep(60000); // 暂停1分钟(单位:毫秒) + } catch (Exception e) { + log.error("第 {}次请求出错: {}", i + 1, e.getMessage()); + } + } + } +}