# Conflicts: # dlp-drugtesting-biz/src/main/java/digital/laboratory/platform/inspection/service/impl/InspectRecordServiceImpl.javamaster
						commit
						1d6eb3fcd5
					
				@ -0,0 +1,63 @@ | 
				
			|||||||
 | 
					package digital.laboratory.platform.inspection.config; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.springframework.boot.context.properties.ConfigurationProperties; | 
				
			||||||
 | 
					import org.springframework.stereotype.Component; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** | 
				
			||||||
 | 
					 * 贵阳禁毒-情报平台推送数据配置 | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					@Component | 
				
			||||||
 | 
					@ConfigurationProperties(prefix = "gyjd.labscare.api") | 
				
			||||||
 | 
					public class ApiPathProperties { | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * api的ip | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    private String host; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 委托书推送的接口 | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    private String entrustLetter; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 确认书推送接口 | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    private String confirmLetter; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 生物检材定性记录 | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    private String biologyQualitativeRecord; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getHost() { | 
				
			||||||
 | 
					        return host; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setHost(String host) { | 
				
			||||||
 | 
					        this.host = host; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getEntrustLetter() { | 
				
			||||||
 | 
					        return host + entrustLetter; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setEntrustLetter(String entrustLetter) { | 
				
			||||||
 | 
					        this.entrustLetter = entrustLetter; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getConfirmLetter() { | 
				
			||||||
 | 
					        return host + confirmLetter; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setConfirmLetter(String confirmLetter) { | 
				
			||||||
 | 
					        this.confirmLetter = confirmLetter; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getBiologyQualitativeRecord() { | 
				
			||||||
 | 
					        return host + biologyQualitativeRecord; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setBiologyQualitativeRecord(String biologyQualitativeRecord) { | 
				
			||||||
 | 
					        this.biologyQualitativeRecord = biologyQualitativeRecord; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,37 @@ | 
				
			|||||||
 | 
					package digital.laboratory.platform.inspection.event; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.Getter; | 
				
			||||||
 | 
					import org.springframework.context.ApplicationEvent; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.time.Clock; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** | 
				
			||||||
 | 
					 * 推送数据到LabsCare 数据平台上的spring 事件 | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					@Getter | 
				
			||||||
 | 
					public class PushDataToLabsCareEvent extends ApplicationEvent { | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 实验id | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    private final String testId; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 推送的数据类型, 1 鉴定委托书数据 | 2 鉴定事项确认书数据 |  3 生物检材定性记录报告数据 | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    private final Integer pushType; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public PushDataToLabsCareEvent(Object source, String testId, Integer pushType) { | 
				
			||||||
 | 
					        super(source); | 
				
			||||||
 | 
					        this.testId = testId; | 
				
			||||||
 | 
					        this.pushType = pushType; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public PushDataToLabsCareEvent(Object source, Clock clock, String testId, Integer pushType) { | 
				
			||||||
 | 
					        super(source, clock); | 
				
			||||||
 | 
					        this.testId = testId; | 
				
			||||||
 | 
					        this.pushType = pushType; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,51 @@ | 
				
			|||||||
 | 
					package digital.laboratory.platform.inspection.listener; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import digital.laboratory.platform.inspection.event.PushDataToLabsCareEvent; | 
				
			||||||
 | 
					import digital.laboratory.platform.inspection.service.PushDataToLabsCareService; | 
				
			||||||
 | 
					import digital.laboratory.platform.inspection.threadpool.GlobalThreadPool; | 
				
			||||||
 | 
					import lombok.extern.slf4j.Slf4j; | 
				
			||||||
 | 
					import org.springframework.context.ApplicationListener; | 
				
			||||||
 | 
					import org.springframework.stereotype.Component; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import javax.annotation.Resource; | 
				
			||||||
 | 
					import java.util.concurrent.CompletableFuture; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** | 
				
			||||||
 | 
					 * 监听有关推送数据到LabsCare 平台的事件, 采用异步 | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					@Slf4j | 
				
			||||||
 | 
					@Component | 
				
			||||||
 | 
					public class PushDataToLabsCareEventListener implements ApplicationListener<PushDataToLabsCareEvent> { | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Resource | 
				
			||||||
 | 
					    private PushDataToLabsCareService pushDataToLabsCareService; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 处理委托数据推送事件 | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param event 委托数据推送事件对象 | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    @Override | 
				
			||||||
 | 
					    public void onApplicationEvent(PushDataToLabsCareEvent event) { | 
				
			||||||
 | 
					        CompletableFuture.runAsync(() -> { | 
				
			||||||
 | 
					            // 处理不同的推送类型
 | 
				
			||||||
 | 
					            switch (event.getPushType()) { | 
				
			||||||
 | 
					                case 3: // 推送委托书数据
 | 
				
			||||||
 | 
					                    try { | 
				
			||||||
 | 
					                        pushDataToLabsCareService.pushBiologyQualitativeRecordData(event.getTestId()); | 
				
			||||||
 | 
					                    } catch (Exception e) { | 
				
			||||||
 | 
					                        log.error("实验id为 {} 的生物定性检验记录数据推送失败!", event.getTestId(), e); | 
				
			||||||
 | 
					                    } | 
				
			||||||
 | 
					                    break; | 
				
			||||||
 | 
					                default: | 
				
			||||||
 | 
					                    log.warn("未知的推送类型: {}", event.getPushType()); | 
				
			||||||
 | 
					                    break; | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        }, GlobalThreadPool.getInstance()).exceptionally(e -> { | 
				
			||||||
 | 
					            log.error("推送数据到 LabsCare 失败", e); | 
				
			||||||
 | 
					            return null; | 
				
			||||||
 | 
					        }); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,99 @@ | 
				
			|||||||
 | 
					package digital.laboratory.platform.inspection.service; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.deepoove.poi.XWPFTemplate; | 
				
			||||||
 | 
					import digital.laboratory.platform.sys.entity.Area; | 
				
			||||||
 | 
					import digital.laboratory.platform.sys.entity.SysOrg; | 
				
			||||||
 | 
					import digital.laboratory.platform.sys.entity.SysUser; | 
				
			||||||
 | 
					import org.springframework.web.multipart.MultipartFile; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import javax.servlet.http.HttpServletResponse; | 
				
			||||||
 | 
					import java.io.ByteArrayInputStream; | 
				
			||||||
 | 
					import java.util.List; | 
				
			||||||
 | 
					import java.util.Map; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** | 
				
			||||||
 | 
					 * 通用的feign请求封装接口服务层接口 | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					public interface CommonFeignService { | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 远程调用获取用户机构 | 
				
			||||||
 | 
					     * @param orgId | 
				
			||||||
 | 
					     * @return | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    SysOrg remoteGetSysOrg(String orgId); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 根据权限和机构远程获取用户列表 | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param orgId    组织ID | 
				
			||||||
 | 
					     * @param permission 权限列表 | 
				
			||||||
 | 
					     * @return 用户列表 | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    List<SysUser> remoteGetUsersByPermission(String orgId, List<String> permission); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 通过机构ID远程获取机构所在省市信息 | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param orgId 机构ID | 
				
			||||||
 | 
					     * @return 包含机构所在省市信息的Area对象列表 | 
				
			||||||
 | 
					     * @throws RuntimeException 当根据机构ID获取机构所在省市信息失败时抛出 | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    List<Area> remoteGetProvinceCityInfo(String orgId); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 远程调用获取用户信息 | 
				
			||||||
 | 
					     * @param username | 
				
			||||||
 | 
					     * @return | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    SysUser remoteGetUserByUsername(String username); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 远程调用获取用户信息 | 
				
			||||||
 | 
					     * @param userId | 
				
			||||||
 | 
					     * @return | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    SysUser remoteGetUserById(String userId); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 远程调用生成word,并转成pdf | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param template | 
				
			||||||
 | 
					     * @param originalFilename 文件名 | 
				
			||||||
 | 
					     * @param savePath         保存到minio路径 | 
				
			||||||
 | 
					     * @return | 
				
			||||||
 | 
					     * @throws Exception | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    boolean remoteGenerateWord2PDF(XWPFTemplate template, String originalFilename, String savePath) throws Exception; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 远程调用根据文件路径获取文件 | 
				
			||||||
 | 
					     * @param filePath minio上的文件路径 | 
				
			||||||
 | 
					     * @return | 
				
			||||||
 | 
					     * @throws Exception | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    ByteArrayInputStream remoteGetFile(String filePath) throws Exception; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 远程调用根据文件路径获取文件 | 
				
			||||||
 | 
					     * @param filePath minio上的文件路径 | 
				
			||||||
 | 
					     * @param fileName 名称 | 
				
			||||||
 | 
					     * @param httpServletResponse | 
				
			||||||
 | 
					     * @throws Exception | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    void remoteGetFile(String filePath, String fileName, HttpServletResponse httpServletResponse) throws Exception; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 远程调用根据文件路径获取文件列表 | 
				
			||||||
 | 
					     * @param filePath minio上的文件路径 | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    List<String> remoteGetFileList(String filePath); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 远程调用-上传文件 | 
				
			||||||
 | 
					     * @param file 上传的文件对象 | 
				
			||||||
 | 
					     * @param path 上传到minio的位置 | 
				
			||||||
 | 
					     * @return | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    Map<String, String> remoteUploadFile(MultipartFile file, String path); | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,17 @@ | 
				
			|||||||
 | 
					package digital.laboratory.platform.inspection.service; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** | 
				
			||||||
 | 
					 * @author ChenJiangBao | 
				
			||||||
 | 
					 * @version 1.0 | 
				
			||||||
 | 
					 * @description: 推送数据到labscare平台接口 | 
				
			||||||
 | 
					 * @date 2025/3/31 9:28 | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					public interface PushDataToLabsCareService { | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 推送生物检材定性记录 | 
				
			||||||
 | 
					     * @param testId 实验id | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    void pushBiologyQualitativeRecordData(String testId); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,262 @@ | 
				
			|||||||
 | 
					package digital.laboratory.platform.inspection.service.impl; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import cn.hutool.core.io.IoUtil; | 
				
			||||||
 | 
					import cn.hutool.core.io.file.FileNameUtil; | 
				
			||||||
 | 
					import cn.hutool.core.util.StrUtil; | 
				
			||||||
 | 
					import com.deepoove.poi.XWPFTemplate; | 
				
			||||||
 | 
					import digital.laboratory.platform.common.core.constant.CommonConstants; | 
				
			||||||
 | 
					import digital.laboratory.platform.common.core.util.R; | 
				
			||||||
 | 
					import digital.laboratory.platform.common.feign.RemoteWord2PDFService; | 
				
			||||||
 | 
					import digital.laboratory.platform.common.oss.service.OssFile; | 
				
			||||||
 | 
					import digital.laboratory.platform.inspection.service.CommonFeignService; | 
				
			||||||
 | 
					import digital.laboratory.platform.sys.dto.UserInfo; | 
				
			||||||
 | 
					import digital.laboratory.platform.sys.entity.Area; | 
				
			||||||
 | 
					import digital.laboratory.platform.sys.entity.SysOrg; | 
				
			||||||
 | 
					import digital.laboratory.platform.sys.entity.SysUser; | 
				
			||||||
 | 
					import digital.laboratory.platform.sys.feign.RemoteOrgService; | 
				
			||||||
 | 
					import digital.laboratory.platform.sys.feign.RemoteUserService; | 
				
			||||||
 | 
					import feign.Response; | 
				
			||||||
 | 
					import lombok.extern.slf4j.Slf4j; | 
				
			||||||
 | 
					import org.apache.commons.io.output.ByteArrayOutputStream; | 
				
			||||||
 | 
					import org.springframework.mock.web.MockMultipartFile; | 
				
			||||||
 | 
					import org.springframework.stereotype.Service; | 
				
			||||||
 | 
					import org.springframework.web.multipart.MultipartFile; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import javax.annotation.Resource; | 
				
			||||||
 | 
					import javax.servlet.http.HttpServletResponse; | 
				
			||||||
 | 
					import java.io.ByteArrayInputStream; | 
				
			||||||
 | 
					import java.util.Base64; | 
				
			||||||
 | 
					import java.util.HashMap; | 
				
			||||||
 | 
					import java.util.List; | 
				
			||||||
 | 
					import java.util.Map; | 
				
			||||||
 | 
					import java.util.stream.Collectors; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** | 
				
			||||||
 | 
					 * 通用的feign请求封装接口服务层接口 实现类 | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					@Slf4j | 
				
			||||||
 | 
					@Service | 
				
			||||||
 | 
					public class CommonFeignServiceImpl implements CommonFeignService { | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Resource | 
				
			||||||
 | 
					    private RemoteOrgService remoteOrgService; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Resource | 
				
			||||||
 | 
					    private RemoteUserService remoteUserService; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Resource | 
				
			||||||
 | 
					    private RemoteWord2PDFService remoteWord2PDFService; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Resource | 
				
			||||||
 | 
					    private OssFile ossFile; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 根据用户获取远程系统机构信息 | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param orgId 用户信息 | 
				
			||||||
 | 
					     * @return 对应的远程系统机构信息 | 
				
			||||||
 | 
					     * @throws RuntimeException 如果未找到对应的机构信息,则抛出运行时异常 | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    @Override | 
				
			||||||
 | 
					    public SysOrg remoteGetSysOrg(String orgId) { | 
				
			||||||
 | 
					        SysOrg sysOrg = null; | 
				
			||||||
 | 
					        R<SysOrg> r = remoteOrgService.getByIdWithoutToken(orgId); | 
				
			||||||
 | 
					        if (r != null && r.getCode() == CommonConstants.SUCCESS) { | 
				
			||||||
 | 
					            sysOrg = r.getData(); | 
				
			||||||
 | 
					        } else { | 
				
			||||||
 | 
					            throw new RuntimeException(String.format("没有找到 orgId 为 %s 的机构, 请确认用户所属机构的正确性!", orgId)); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					        return sysOrg; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 取指定环节可用的用户列表 | 
				
			||||||
 | 
					     * 当流程进行到某个环节的时候, 需要某个用户对这个环节进行处理(通过或不通过)。 | 
				
			||||||
 | 
					     * 这里取可用的用户列表 | 
				
			||||||
 | 
					     * <p> | 
				
			||||||
 | 
					     * 涉及到的环境有以下几个: | 
				
			||||||
 | 
					     * 1、创建委托及提交 | 
				
			||||||
 | 
					     * 委托的提交者就是委托的创建者。不存在不通过的可能。 | 
				
			||||||
 | 
					     * 2、审核 | 
				
			||||||
 | 
					     * 审核者有几个条件: (1)必须是鉴定中心的工作人员 (2)必须拥有委托审核权限 | 
				
			||||||
 | 
					     * 3、审批 | 
				
			||||||
 | 
					     * 审核者有几个条件: (1)必须是鉴定中心的工作人员 (2)必须拥有委托审批权限 | 
				
			||||||
 | 
					     * 4、送检确认 | 
				
			||||||
 | 
					     * 送检确认者有几个条件: (1)必须与委托的创建者是同一个机构, 或上级机构的人 (2)必须拥有委托送检确认权限 | 
				
			||||||
 | 
					     * 5、受理 | 
				
			||||||
 | 
					     * 受理者有几个条件: (1)必须是鉴定中心的工作人员 (2)必须拥有委托受理权限 | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @return | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 根据权限和机构远程获取用户列表 | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param orgId    组织ID | 
				
			||||||
 | 
					     * @param permission 权限列表 | 
				
			||||||
 | 
					     * @return 用户列表 | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    @Override | 
				
			||||||
 | 
					    public List<SysUser> remoteGetUsersByPermission(String orgId, List<String> permission) { | 
				
			||||||
 | 
					        R<List<SysUser>> r = remoteUserService.innerGetUsersByPermission(orgId, permission); | 
				
			||||||
 | 
					        if (r != null && r.getCode() == CommonConstants.SUCCESS) { | 
				
			||||||
 | 
					            return r.getData(); | 
				
			||||||
 | 
					        } else { | 
				
			||||||
 | 
					            throw new RuntimeException(String.format("根据权限 [%s] 和 机构id [%s] 远程获取用户列表!", permission, orgId)); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 通过机构ID远程获取机构所在省市信息 | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param orgId 机构ID | 
				
			||||||
 | 
					     * @return 包含机构所在省市信息的Area对象列表 | 
				
			||||||
 | 
					     * @throws RuntimeException 当根据机构ID获取机构所在省市信息失败时抛出 | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    @Override | 
				
			||||||
 | 
					    public List<Area> remoteGetProvinceCityInfo(String orgId) { | 
				
			||||||
 | 
					        List<Area> result = null; | 
				
			||||||
 | 
					        R<List<Area>> r = remoteOrgService.fetchProvinceCityInfoByOrgId(orgId); | 
				
			||||||
 | 
					        if (r != null && r.getCode() == CommonConstants.SUCCESS) { | 
				
			||||||
 | 
					            result = r.getData(); | 
				
			||||||
 | 
					        } else { | 
				
			||||||
 | 
					            throw new RuntimeException("根据机构id获取机构所在省市信息失败!"); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					        return result; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 远程调用或者用户信息 | 
				
			||||||
 | 
					     * @param username | 
				
			||||||
 | 
					     * @return | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    @Override | 
				
			||||||
 | 
					    public SysUser remoteGetUserByUsername(String username){ | 
				
			||||||
 | 
					        R<UserInfo> info = remoteUserService.innerGetUserInfoByUsername(username); | 
				
			||||||
 | 
					        if (info != null && info.getCode() == CommonConstants.FAIL) { | 
				
			||||||
 | 
					            throw new RuntimeException(String.format("获取用户名为 %s 的用户信息失败!", username)); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					        return info.getData().getSysUser(); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 远程调用获取用户信息 | 
				
			||||||
 | 
					     * @param userId | 
				
			||||||
 | 
					     * @return | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    @Override | 
				
			||||||
 | 
					    public SysUser remoteGetUserById(String userId){ | 
				
			||||||
 | 
					        R<SysUser> info = remoteUserService.innerGetById(userId); | 
				
			||||||
 | 
					        if (info != null && info.getCode() == CommonConstants.FAIL) { | 
				
			||||||
 | 
					            throw new RuntimeException(String.format("获取用户名id为 %s 的用户信息失败!", userId)); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					        return info.getData(); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override | 
				
			||||||
 | 
					    public boolean remoteGenerateWord2PDF(XWPFTemplate template, String originalFilename, String savePath) throws Exception{ | 
				
			||||||
 | 
					        ByteArrayOutputStream fosWord = new ByteArrayOutputStream(); | 
				
			||||||
 | 
					        template.write(fosWord); | 
				
			||||||
 | 
					        template.close(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //------------
 | 
				
			||||||
 | 
					        ByteArrayInputStream fisWord = new ByteArrayInputStream(fosWord.toByteArray()); | 
				
			||||||
 | 
					        fosWord.close(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MockMultipartFile mockMultipartFile = new MockMultipartFile("file", originalFilename + ".docx", "image/jpg", fisWord); | 
				
			||||||
 | 
					        Response response = remoteWord2PDFService.word2pdf(mockMultipartFile); | 
				
			||||||
 | 
					        fisWord.close(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ByteArrayOutputStream outPDF = new ByteArrayOutputStream(); | 
				
			||||||
 | 
					        IoUtil.copy(response.body().asInputStream(), outPDF, IoUtil.DEFAULT_MIDDLE_BUFFER_SIZE); | 
				
			||||||
 | 
					        ByteArrayInputStream isPDF = new ByteArrayInputStream(outPDF.toByteArray()); | 
				
			||||||
 | 
					        outPDF.close(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        boolean b = ossFile.fileSave(savePath, isPDF); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        isPDF.close(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        log.info("转换为 PDF 结束"); | 
				
			||||||
 | 
					        return b; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 远程调用根据文件路径获取文件 | 
				
			||||||
 | 
					     * @param filePath minio上的文件路径 | 
				
			||||||
 | 
					     * @return | 
				
			||||||
 | 
					     * @throws Exception | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    @Override | 
				
			||||||
 | 
					    public ByteArrayInputStream remoteGetFile(String filePath) throws Exception { | 
				
			||||||
 | 
					        ByteArrayOutputStream bos = new ByteArrayOutputStream(); | 
				
			||||||
 | 
					        ossFile.fileGet(filePath, bos); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        byte[] templateArray = bos.toByteArray(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ByteArrayInputStream bis = new ByteArrayInputStream(templateArray); | 
				
			||||||
 | 
					        bos.close(); | 
				
			||||||
 | 
					        return bis; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 远程调用根据文件路径获取文件 | 
				
			||||||
 | 
					     * @param filePath minio上的文件路径 | 
				
			||||||
 | 
					     * @param fileName 名称 | 
				
			||||||
 | 
					     * @param httpServletResponse | 
				
			||||||
 | 
					     * @throws Exception | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    @Override | 
				
			||||||
 | 
					    public void remoteGetFile(String filePath, String fileName, HttpServletResponse httpServletResponse) throws Exception { | 
				
			||||||
 | 
					        ossFile.fileGet(filePath, httpServletResponse.getOutputStream()); | 
				
			||||||
 | 
					        if (StrUtil.isNotBlank(fileName)) { | 
				
			||||||
 | 
					            httpServletResponse.setContentType(fileName); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 远程调用根据文件路径获取文件列表 | 
				
			||||||
 | 
					     * @param filePath minio上的文件路径 | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    @Override | 
				
			||||||
 | 
					    public List<String> remoteGetFileList(String filePath) { | 
				
			||||||
 | 
					        if (StrUtil.isNotBlank(filePath)) { | 
				
			||||||
 | 
					            List<String> fileNameList = ossFile.fileList(filePath); | 
				
			||||||
 | 
					            List<String> fileList = fileNameList.stream().map(fileName -> { | 
				
			||||||
 | 
					                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); | 
				
			||||||
 | 
					                try { | 
				
			||||||
 | 
					                    ossFile.fileGet(filePath + "/" + fileName, byteArrayOutputStream); | 
				
			||||||
 | 
					                    return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray()); | 
				
			||||||
 | 
					                } catch (Exception e) { | 
				
			||||||
 | 
					                    e.printStackTrace(); | 
				
			||||||
 | 
					                    log.error("文件获取失败, 文件路径为: {}", filePath + "/" + fileName); | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					                return null; | 
				
			||||||
 | 
					            }).collect(Collectors.toList()); | 
				
			||||||
 | 
					            return fileList; | 
				
			||||||
 | 
					        } else { | 
				
			||||||
 | 
					            throw new RuntimeException("文件路径不能为空!"); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 远程调用-上传文件 | 
				
			||||||
 | 
					     * @param file 上传的文件对象 | 
				
			||||||
 | 
					     * @param path 上传到minio的位置 | 
				
			||||||
 | 
					     * @return | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    @Override | 
				
			||||||
 | 
					    public Map<String, String> remoteUploadFile(MultipartFile file, String path) { | 
				
			||||||
 | 
					        boolean r = ossFile.fileUpload(file, path); | 
				
			||||||
 | 
					        if (r) { | 
				
			||||||
 | 
					            HashMap<String, String> resultData = new HashMap<>(); | 
				
			||||||
 | 
					            resultData.put("fileName", FileNameUtil.getName(file.getOriginalFilename())); | 
				
			||||||
 | 
					            resultData.put("path", path); | 
				
			||||||
 | 
					            log.info("文件上传成功!"); | 
				
			||||||
 | 
					            return resultData; | 
				
			||||||
 | 
					        } else { | 
				
			||||||
 | 
					            String failMsg = String.format("文件名为 %s 的文件上传失败!", file.getOriginalFilename()); | 
				
			||||||
 | 
					            log.error(failMsg); | 
				
			||||||
 | 
					            throw new RuntimeException(failMsg); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,356 @@ | 
				
			|||||||
 | 
					package digital.laboratory.platform.inspection.service.impl; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import cn.hutool.core.collection.CollUtil; | 
				
			||||||
 | 
					import cn.hutool.core.util.StrUtil; | 
				
			||||||
 | 
					import cn.hutool.json.JSONObject; | 
				
			||||||
 | 
					import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; | 
				
			||||||
 | 
					import com.baomidou.mybatisplus.core.toolkit.Wrappers; | 
				
			||||||
 | 
					import digital.laboratory.platform.inspection.config.ApiPathProperties; | 
				
			||||||
 | 
					import digital.laboratory.platform.inspection.constant.TestRecordSampleDataConstant; | 
				
			||||||
 | 
					import digital.laboratory.platform.inspection.convert.TestRecordSampleDataConverter; | 
				
			||||||
 | 
					import digital.laboratory.platform.inspection.entity.TestRecordInstrument; | 
				
			||||||
 | 
					import digital.laboratory.platform.inspection.entity.TestRecordMethod; | 
				
			||||||
 | 
					import digital.laboratory.platform.inspection.entity.TestRecordReagent; | 
				
			||||||
 | 
					import digital.laboratory.platform.inspection.entity.TestRecordSampleData; | 
				
			||||||
 | 
					import digital.laboratory.platform.inspection.mapper.TestRecordSampleDataMapper; | 
				
			||||||
 | 
					import digital.laboratory.platform.inspection.service.*; | 
				
			||||||
 | 
					import digital.laboratory.platform.inspection.vo.TestRecordReagentVO; | 
				
			||||||
 | 
					import digital.laboratory.platform.inspection.vo.TestRecordSampleDataExpandVO; | 
				
			||||||
 | 
					import digital.laboratory.platform.inspection.vo.TestRecordSampleDataVO; | 
				
			||||||
 | 
					import digital.laboratory.platform.inspetion.api.entity.EntrustInfo; | 
				
			||||||
 | 
					import digital.laboratory.platform.inspetion.api.entity.SampleInfo; | 
				
			||||||
 | 
					import digital.laboratory.platform.inspetion.api.entity.TestRecord; | 
				
			||||||
 | 
					import digital.laboratory.platform.othersys.utils.HttpsUtils; | 
				
			||||||
 | 
					import digital.laboratory.platform.sys.entity.Drug; | 
				
			||||||
 | 
					import digital.laboratory.platform.sys.entity.SysUser; | 
				
			||||||
 | 
					import lombok.extern.slf4j.Slf4j; | 
				
			||||||
 | 
					import org.springframework.http.*; | 
				
			||||||
 | 
					import org.springframework.scheduling.annotation.Scheduled; | 
				
			||||||
 | 
					import org.springframework.stereotype.Service; | 
				
			||||||
 | 
					import org.springframework.web.client.RestClientException; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import javax.annotation.Resource; | 
				
			||||||
 | 
					import java.time.format.DateTimeFormatter; | 
				
			||||||
 | 
					import java.util.*; | 
				
			||||||
 | 
					import java.util.function.Function; | 
				
			||||||
 | 
					import java.util.stream.Collectors; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** | 
				
			||||||
 | 
					 * @author ChenJiangBao | 
				
			||||||
 | 
					 * @version 1.0 | 
				
			||||||
 | 
					 * @description: 推送数据到labscare平台接口 | 
				
			||||||
 | 
					 * @date 2025/3/31 9:31 | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					@Slf4j | 
				
			||||||
 | 
					@Service | 
				
			||||||
 | 
					public class PushDataToLabsCareServiceImpl implements PushDataToLabsCareService { | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Resource | 
				
			||||||
 | 
					    private TestRecordService testRecordService; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Resource | 
				
			||||||
 | 
					    private EntrustInfoService entrustInfoService; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Resource | 
				
			||||||
 | 
					    private SampleInfoService sampleInfoService; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Resource | 
				
			||||||
 | 
					    private TestRecordReagentService testRecordReagentService; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Resource | 
				
			||||||
 | 
					    private TestRecordSampleDataMapper testRecordSampleDataMapper; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Resource | 
				
			||||||
 | 
					    private TestRecordInstrumentService testRecordInstrumentService; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Resource | 
				
			||||||
 | 
					    private TestRecordMethodService testRecordMethodService; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Resource | 
				
			||||||
 | 
					    private InspectRecordService inspectRecordService; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Resource | 
				
			||||||
 | 
					    private ProcessInspectDataService processInspectDataService; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Resource | 
				
			||||||
 | 
					    private CommonFeignService commonFeignService; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Resource | 
				
			||||||
 | 
					    private ApiPathProperties apiPathProperties; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日"); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // "#NULL#" 作为空数据的占位符, 对于系统中没有的信息以占位符代替
 | 
				
			||||||
 | 
					    public final static String NULL_PLACEHOLDER = "#NULL#"; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final static String BIOLOGY_QUALITATIVE_RECORD = "BiologyQualitativeRecord"; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 做一个定时推送,对于推送失败的委托进行重新推送, 每天凌晨1点推送 | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					//    @Scheduled(cron = "30 * * * * ?") // 测试
 | 
				
			||||||
 | 
					    @Scheduled(cron = "0 0 2 * * ?")  // 每天凌晨 2 点执行
 | 
				
			||||||
 | 
					    public void timingPushDataToLabsCare() { | 
				
			||||||
 | 
					        // 查询检验鉴定推送失败的委托
 | 
				
			||||||
 | 
					        List<EntrustInfo> entrustList = entrustInfoService.list( | 
				
			||||||
 | 
					                Wrappers.<EntrustInfo>lambdaQuery() | 
				
			||||||
 | 
					                        .and(wrapper -> wrapper | 
				
			||||||
 | 
					                                .eq(EntrustInfo::getPushFlag, "") | 
				
			||||||
 | 
					                                .or() | 
				
			||||||
 | 
					                                .isNull(EntrustInfo::getPushFlag) | 
				
			||||||
 | 
					                                .or() | 
				
			||||||
 | 
					                                .like(EntrustInfo::getPushFlag, "false") | 
				
			||||||
 | 
					                        ) | 
				
			||||||
 | 
					        ); | 
				
			||||||
 | 
					        if (CollUtil.isEmpty(entrustList)) { | 
				
			||||||
 | 
					            return; | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					        Map<String, EntrustInfo> entrustInfoMap = entrustList.stream().collect(Collectors.toMap(EntrustInfo::getId, Function.identity())); | 
				
			||||||
 | 
					        // 获取所有完成实验的数据
 | 
				
			||||||
 | 
					        List<TestRecord> testRecordList = testRecordService.list(Wrappers.<TestRecord>lambdaQuery() | 
				
			||||||
 | 
					                .eq(TestRecord::getStatus, 5).in(TestRecord::getBusinessId, entrustList.stream() | 
				
			||||||
 | 
					                        .map(EntrustInfo::getId).collect(Collectors.toList())) | 
				
			||||||
 | 
					        ); | 
				
			||||||
 | 
					        if (CollUtil.isEmpty(testRecordList)) { | 
				
			||||||
 | 
					            return; | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (TestRecord testRecord : testRecordList) { | 
				
			||||||
 | 
					            EntrustInfo entrustInfo = entrustInfoMap.get(testRecord.getBusinessId()); | 
				
			||||||
 | 
					            String pushFlag = entrustInfo.getPushFlag(); | 
				
			||||||
 | 
					            if (StrUtil.isBlank(pushFlag)) { | 
				
			||||||
 | 
					                pushBiologyQualitativeRecordData(testRecord.getId()); | 
				
			||||||
 | 
					            } else { | 
				
			||||||
 | 
					                List<String> flagList = StrUtil.split(pushFlag, StrUtil.COMMA).stream().filter(str -> str.contains("false")).collect(Collectors.toList()); | 
				
			||||||
 | 
					                for (String flag : flagList) { | 
				
			||||||
 | 
					                    if (flag.contains(BIOLOGY_QUALITATIVE_RECORD)) { | 
				
			||||||
 | 
					                        pushBiologyQualitativeRecordData(testRecord.getId()); | 
				
			||||||
 | 
					                    } else { | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    } | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 推送生物检材定性记录 | 
				
			||||||
 | 
					     * @param testId 实验id | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    @Override | 
				
			||||||
 | 
					    public void pushBiologyQualitativeRecordData(String testId) { | 
				
			||||||
 | 
					        TestRecord testRecord = testRecordService.getById(testId); | 
				
			||||||
 | 
					        if (testRecord == null) { | 
				
			||||||
 | 
					            log.error("实验id为 [{}] 的实验信息查询为空!", testId); | 
				
			||||||
 | 
					            return; | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					        // 获取委托信息
 | 
				
			||||||
 | 
					        EntrustInfo entrustInfo = entrustInfoService.getById(testRecord.getBusinessId()); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        JSONObject recordJsonPayload = buildBiologyQualitativeRecordJsonPayload(entrustInfo, testRecord); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        String successFlag = BIOLOGY_QUALITATIVE_RECORD + ":true"; | 
				
			||||||
 | 
					        String failureFlag = BIOLOGY_QUALITATIVE_RECORD + ":false"; | 
				
			||||||
 | 
					        try { | 
				
			||||||
 | 
					            ResponseEntity<String> response = exchangeRemoteApi(recordJsonPayload, apiPathProperties.getBiologyQualitativeRecord()); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (response.getStatusCode().is2xxSuccessful() && response.getBody().contains("\"status\":200")) { | 
				
			||||||
 | 
					                updatePushFlag(entrustInfo, BIOLOGY_QUALITATIVE_RECORD, successFlag); | 
				
			||||||
 | 
					                log.info("推送生物定性检验记录数据成功, 受理编号: {}, 响应: {}", entrustInfo.getAcceptNo(), response.getBody()); | 
				
			||||||
 | 
					            } else { | 
				
			||||||
 | 
					                updatePushFlag(entrustInfo, BIOLOGY_QUALITATIVE_RECORD, failureFlag); | 
				
			||||||
 | 
					                log.warn("推送生物定性检验记录数据失败, 受理编号: {}, 状态码: {}, 响应: {}", | 
				
			||||||
 | 
					                        entrustInfo.getAcceptNo(), response.getStatusCode(), response.getBody()); | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        } catch (RestClientException e) { | 
				
			||||||
 | 
					            log.error("推送生物定性检验记录数据到 LabsCare 失败, 受理编号: {}", entrustInfo.getAcceptNo(), e); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private JSONObject buildBiologyQualitativeRecordJsonPayload(EntrustInfo entrustInfo, TestRecord testRecord) { | 
				
			||||||
 | 
					        // 查询封装需要用到的数据
 | 
				
			||||||
 | 
					        List<SampleInfo> sampleInfoList = sampleInfoService | 
				
			||||||
 | 
					                .lambdaQuery() | 
				
			||||||
 | 
					                .in(SampleInfo::getId, testRecord.getSampleTestList()) | 
				
			||||||
 | 
					                .list(); | 
				
			||||||
 | 
					        // 检材转map
 | 
				
			||||||
 | 
					        Map<String, SampleInfo> sampleInfoMap = sampleInfoList.stream().collect(Collectors.toMap(SampleInfo::getAcceptNo, Function.identity())); | 
				
			||||||
 | 
					        // 对受理编号进行分隔
 | 
				
			||||||
 | 
					        List<String> splitAcceptNo = StrUtil.split(entrustInfo.getAcceptNo(), "-"); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 设置使用的标准物质和试剂
 | 
				
			||||||
 | 
					        List<TestRecordReagentVO> references = testRecordReagentService.voListByWrapper(Wrappers.<TestRecordReagent>lambdaQuery() | 
				
			||||||
 | 
					                .in(TestRecordReagent::getId, testRecord.getReagentConsumablesList()) | 
				
			||||||
 | 
					                .eq(TestRecordReagent::getCategory, "标准物质")); | 
				
			||||||
 | 
					        // 本次实验使用的标准品
 | 
				
			||||||
 | 
					        String stdStr = references.stream().map(TestRecordReagentVO::getReagentConsumableName).collect(Collectors.joining("、")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 获取标准物质对应的毒品成分
 | 
				
			||||||
 | 
					        Map<String, Drug> drugMap = references.stream().map(TestRecordReagentVO::getDrug).collect(Collectors.toMap(Drug::getEnglishName, Function.identity())); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 获取检测人用户信息
 | 
				
			||||||
 | 
					        SysUser testUser = commonFeignService.remoteGetUserById(testRecord.getTestUserId()); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 检测数据信息
 | 
				
			||||||
 | 
					        Map<String, List<TestRecordSampleDataVO>> testDataGroupBySampleNO = testRecordSampleDataMapper | 
				
			||||||
 | 
					                .queryTestRecordSampleDataVOList(Wrappers.<TestRecordSampleData>lambdaQuery() | 
				
			||||||
 | 
					                        .eq(TestRecordSampleData::getTestId, testRecord.getId()) | 
				
			||||||
 | 
					                        .eq(TestRecordSampleData::getSampleType, TestRecordSampleDataConstant.SAMPLE_TYPE_ANALYTE) | 
				
			||||||
 | 
					                ) // 数据库查询, 这个是非标准物质的检测数据
 | 
				
			||||||
 | 
					                .stream().collect(Collectors.groupingBy(TestRecordSampleDataVO::getSampleNo)); // 根据查询出来的结果进行分组
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Map<String, TestRecordSampleDataVO> stdTestDataMap = testRecordSampleDataMapper | 
				
			||||||
 | 
					                .queryTestRecordSampleDataVOList(Wrappers.<TestRecordSampleData>lambdaQuery() | 
				
			||||||
 | 
					                        .eq(TestRecordSampleData::getTestId, testRecord.getId()) | 
				
			||||||
 | 
					                        .eq(TestRecordSampleData::getSampleType, TestRecordSampleDataConstant.SAMPLE_TYPE_STD) | 
				
			||||||
 | 
					                ) // 数据库查询, 这个是非标准物质的检测数据
 | 
				
			||||||
 | 
					                .stream().collect(Collectors.toMap(TestRecordSampleDataVO::getCompoundName, Function.identity())); // 根据查询出来的结果进行分组
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 查询使用的仪器
 | 
				
			||||||
 | 
					        List<String> deviceIdList = testRecord.getDeviceIdList(); | 
				
			||||||
 | 
					        List<TestRecordInstrument> instruments = CollectionUtils.isEmpty(deviceIdList) | 
				
			||||||
 | 
					                ? Collections.emptyList() | 
				
			||||||
 | 
					                : testRecordInstrumentService.list(Wrappers.<TestRecordInstrument>lambdaQuery() | 
				
			||||||
 | 
					                .in(TestRecordInstrument::getId, deviceIdList)); | 
				
			||||||
 | 
					        String useInstrumentNameStr = CollectionUtils.isEmpty(instruments) | 
				
			||||||
 | 
					                ? NULL_PLACEHOLDER | 
				
			||||||
 | 
					                : instruments.stream() | 
				
			||||||
 | 
					                .map(TestRecordInstrument::getInstrumentName) | 
				
			||||||
 | 
					                .collect(Collectors.joining(",")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 使用的方法
 | 
				
			||||||
 | 
					        List<String> testMethodList = testRecord.getTestMethodList(); | 
				
			||||||
 | 
					        List<TestRecordMethod> testRecordMethodList = CollectionUtils.isEmpty(testMethodList) | 
				
			||||||
 | 
					                ? Collections.emptyList() | 
				
			||||||
 | 
					                : testRecordMethodService.list(Wrappers.<TestRecordMethod>lambdaQuery() | 
				
			||||||
 | 
					                .in(TestRecordMethod::getId, testMethodList)); | 
				
			||||||
 | 
					        String useMethodNameStr = CollectionUtils.isEmpty(instruments) | 
				
			||||||
 | 
					                ? NULL_PLACEHOLDER | 
				
			||||||
 | 
					                : testRecordMethodList.stream() | 
				
			||||||
 | 
					                .map(TestRecordMethod::getMethodName) | 
				
			||||||
 | 
					                .collect(Collectors.joining(",")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 开始封装数据参数
 | 
				
			||||||
 | 
					        JSONObject jsonObject = new JSONObject(); | 
				
			||||||
 | 
					        jsonObject.set("jcxz", inspectRecordService.buildMaterialCharacterDesc(sampleInfoList)); // 检材性状描述
 | 
				
			||||||
 | 
					        jsonObject.set("jdwsh", String.format("[%s]%s号", splitAcceptNo.get(0), splitAcceptNo.get(1))); // 鉴定文书号
 | 
				
			||||||
 | 
					        jsonObject.set("jysj", testRecord.getTestStartDate().format(formatter)); // 检验时间
 | 
				
			||||||
 | 
					        jsonObject.set("jyy1qm", testUser.getName()); // 检测人1
 | 
				
			||||||
 | 
					        jsonObject.set( | 
				
			||||||
 | 
					                "jyyq", | 
				
			||||||
 | 
					                String.format("对所送检材中是否含有 %s 成分进行定性检测", stdStr) | 
				
			||||||
 | 
					        ); // 检验要求,根据模板上的值来生成
 | 
				
			||||||
 | 
					        jsonObject.set("sjr1", entrustInfo.getDeliver1Name()); // 送检人1
 | 
				
			||||||
 | 
					        jsonObject.set("sjr2", entrustInfo.getDeliver2Name()); // 送检人2
 | 
				
			||||||
 | 
					        jsonObject.set("slrq", entrustInfo.getAcceptDate().format(formatter)); // 受理日期
 | 
				
			||||||
 | 
					        jsonObject.set("sprqm", testUser.getName()); // 检测人2
 | 
				
			||||||
 | 
					        jsonObject.set("swmbw", stdStr); // 生物检测目标物
 | 
				
			||||||
 | 
					        jsonObject.set("tpfj", NULL_PLACEHOLDER); // 图谱
 | 
				
			||||||
 | 
					        jsonObject.set("wtdw", entrustInfo.getEntrustDepartment()); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 生物检材定性记录样品信息
 | 
				
			||||||
 | 
					        List<JSONObject> sampleJsonList = new ArrayList<>(); | 
				
			||||||
 | 
					        testDataGroupBySampleNO.forEach((sampleNo,  testDataList) -> { | 
				
			||||||
 | 
					            JSONObject sampleJson = new JSONObject(); | 
				
			||||||
 | 
					            sampleJson.set("caseName", sampleNo); // 样品编号
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 该样品编号对应的检测数据信息
 | 
				
			||||||
 | 
					            List<JSONObject> gauging = testDataList.stream().map(data -> { | 
				
			||||||
 | 
					                JSONObject testDataJson = new JSONObject(); | 
				
			||||||
 | 
					                testDataJson.set("blsjxdwc", data.getRtTimeError()); // 保留时间相对误差(%)
 | 
				
			||||||
 | 
					                TestRecordSampleDataVO stdData = stdTestDataMap.get(data.getCompoundName()); | 
				
			||||||
 | 
					                testDataJson.set("bzwzblsj", stdData.getTargetRtTime()); // 标准物质保留时间(min)
 | 
				
			||||||
 | 
					                testDataJson.set("bzwzfdb", stdData.getIonAbundanceRatio()); // 标准物质离子对相对丰度比
 | 
				
			||||||
 | 
					                testDataJson.set("bzwzwb", stdData.getCompoundName()); // 标准物质名称
 | 
				
			||||||
 | 
					                testDataJson.set("dcypfdb", data.getIonAbundanceRatio()); // 待测样品离子对相对丰度比
 | 
				
			||||||
 | 
					                testDataJson.set("dxlzd", data.getExpandList().stream().map(TestRecordSampleDataExpandVO::getQualitativeIonPair).collect(Collectors.joining(","))); // 定性离子对
 | 
				
			||||||
 | 
					                testDataJson.set("dxyq", useInstrumentNameStr); // 使用仪器
 | 
				
			||||||
 | 
					                testDataJson.set("fdbwc", data.getIonAbundanceRatioError()); // 丰度比相对偏差
 | 
				
			||||||
 | 
					                testDataJson.set("ffStr", useMethodNameStr); // 所用的方法
 | 
				
			||||||
 | 
					                testDataJson.set("gradientA", NULL_PLACEHOLDER); // 梯度洗脱参考条件-A(%)
 | 
				
			||||||
 | 
					                testDataJson.set("gradientB", NULL_PLACEHOLDER); // 梯度洗脱参考条件-B(%)
 | 
				
			||||||
 | 
					                testDataJson.set("gradientTime", NULL_PLACEHOLDER); // 梯度洗脱参考条件-时间(min)
 | 
				
			||||||
 | 
					                testDataJson.set("jcxz", sampleInfoMap.get(data.getSampleNo()).getForm()); // 检材性状描述
 | 
				
			||||||
 | 
					                testDataJson.set("jdwsh", String.format("[%s]%s号", splitAcceptNo.get(0), splitAcceptNo.get(1))); // 鉴定文书号
 | 
				
			||||||
 | 
					                testDataJson.set("jdyj", inspectRecordService.buildInspectOpinion(Collections.singletonList(TestRecordSampleDataConverter.voToEntity(data)))); | 
				
			||||||
 | 
					                testDataJson.set("jgpd", data.getWhetherCheckOut()); // 结果判定
 | 
				
			||||||
 | 
					                testDataJson.set("jysj", testRecord.getTestStartDate().format(formatter)); // 检验时间
 | 
				
			||||||
 | 
					                testDataJson.set("jyyq", String.format("对所送检材中是否含有 %s 成分进行定性检测", stdData.getCompoundCnName())); // 检验要求
 | 
				
			||||||
 | 
					                testDataJson.set("mbwblsj", data.getTargetRtTime()); // 目标物保留时间(min)
 | 
				
			||||||
 | 
					                testDataJson.set("projectName", "定性检测"); // 化合物名称, 对方公司并未标明该字段信息
 | 
				
			||||||
 | 
					                // 获取对应的化合物信息
 | 
				
			||||||
 | 
					                Drug drug = drugMap.get(data.getCompoundName()); | 
				
			||||||
 | 
					                testDataJson.set("pzdy", StrUtil.join(",", drug.getMainCollisionEnergy(), drug.getMinorCollisionEnergy())); // 碰撞电压
 | 
				
			||||||
 | 
					                testDataJson.set("rjwb", NULL_PLACEHOLDER); // 溶剂
 | 
				
			||||||
 | 
					                testDataJson.set("sjr1", entrustInfo.getDeliver1Name()); // 送检人1
 | 
				
			||||||
 | 
					                testDataJson.set("sjr2", entrustInfo.getDeliver2Name()); // 送检人2
 | 
				
			||||||
 | 
					                testDataJson.set("slrq", entrustInfo.getAcceptDate().format(formatter)); // 受理日期
 | 
				
			||||||
 | 
					                testDataJson.set("swbzgzyzb", NULL_PLACEHOLDER); // 标准工作溶液制备
 | 
				
			||||||
 | 
					                testDataJson.set("swdcypzb", NULL_PLACEHOLDER);  // 待测样品制备
 | 
				
			||||||
 | 
					                testDataJson.set("swmbw", drug.getName()); // 目标物
 | 
				
			||||||
 | 
					                testDataJson.set("swsj", NULL_PLACEHOLDER); // 上机
 | 
				
			||||||
 | 
					                testDataJson.set("swyxtj", NULL_PLACEHOLDER); // 	液相条件
 | 
				
			||||||
 | 
					                testDataJson.set("swzptj", NULL_PLACEHOLDER); // 质谱条件
 | 
				
			||||||
 | 
					                testDataJson.set("wjcjgpd", "±" + processInspectDataService.getHairCaseIonAbundanceRatioWithinErrorRange(stdData.getIonAbundanceRatio().doubleValue())); //相对离子丰度比的最大允许相对误差(%)
 | 
				
			||||||
 | 
					                testDataJson.set("wtdw", entrustInfo.getEntrustDepartment()); | 
				
			||||||
 | 
					                testDataJson.set("wzdy", StrUtil.join(",", drug.getMainDeclusteringPotential(), drug.getMinorDeclusteringPotential())); | 
				
			||||||
 | 
					                return testDataJson; | 
				
			||||||
 | 
					            }).collect(Collectors.toList()); | 
				
			||||||
 | 
					            sampleJson.set("gauging", gauging); // 生物检材定性记录检测信息
 | 
				
			||||||
 | 
					            sampleJsonList.add(sampleJson); | 
				
			||||||
 | 
					        }); | 
				
			||||||
 | 
					        jsonObject.set("sample", sampleJsonList); | 
				
			||||||
 | 
					        return jsonObject; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 通过POST请求与远程API进行交互 | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param jsonObject 包含请求数据的JSONObject对象 | 
				
			||||||
 | 
					     * @param url        远程API的URL | 
				
			||||||
 | 
					     * @return ResponseEntity<String> 包含远程API响应数据的ResponseEntity对象 | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    private ResponseEntity<String> exchangeRemoteApi(JSONObject jsonObject, String url) { | 
				
			||||||
 | 
					        HttpHeaders headers = new HttpHeaders(); | 
				
			||||||
 | 
					        headers.setContentType(MediaType.APPLICATION_JSON);  // 设置为 application/json
 | 
				
			||||||
 | 
					        HttpEntity<JSONObject> requestEntity = new HttpEntity<>(jsonObject, headers); | 
				
			||||||
 | 
					        ResponseEntity<String> response = HttpsUtils | 
				
			||||||
 | 
					                .genRestTemplate() | 
				
			||||||
 | 
					                .exchange( | 
				
			||||||
 | 
					                        url, | 
				
			||||||
 | 
					                        HttpMethod.POST, | 
				
			||||||
 | 
					                        requestEntity, | 
				
			||||||
 | 
					                        String.class | 
				
			||||||
 | 
					                ); | 
				
			||||||
 | 
					        return response; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * 更新推送标志 | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param entrust   委托对象 | 
				
			||||||
 | 
					     * @param pushType    推送类型 EntrustLetter 委托书 | ItemConfirmLetter 事项确认书 | 
				
			||||||
 | 
					     * @param newFlag     新的推送标志 | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    private void updatePushFlag(EntrustInfo entrust, String pushType, String newFlag) { | 
				
			||||||
 | 
					        List<String> flagList = Optional.ofNullable(StrUtil.split(entrust.getPushFlag(), StrUtil.COMMA)) | 
				
			||||||
 | 
					                .orElse(new ArrayList<>()); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 判断是否已有 指定的推送类型,如果有就替换,否则添加
 | 
				
			||||||
 | 
					        boolean exists = flagList.stream().anyMatch(flag -> flag.contains(pushType)); | 
				
			||||||
 | 
					        if (exists) { | 
				
			||||||
 | 
					            flagList.replaceAll(flag -> flag.contains(pushType) ? newFlag : flag); | 
				
			||||||
 | 
					        } else { | 
				
			||||||
 | 
					            flagList.add(newFlag); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					        // 更新标识, 防止定时任务那里连续推送数据时推送标识更新出错
 | 
				
			||||||
 | 
					        entrust.setPushFlag(String.join(",", flagList)); | 
				
			||||||
 | 
					        // 更新数据库
 | 
				
			||||||
 | 
					        entrustInfoService.update(Wrappers.<EntrustInfo>lambdaUpdate() | 
				
			||||||
 | 
					                .eq(EntrustInfo::getId, entrust.getId()) | 
				
			||||||
 | 
					                .set(EntrustInfo::getPushFlag, String.join(",", flagList))); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,52 @@ | 
				
			|||||||
 | 
					package digital.laboratory.platform.inspection.threadpool; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.extern.slf4j.Slf4j; | 
				
			||||||
 | 
					import org.springframework.stereotype.Component; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import javax.annotation.PreDestroy; | 
				
			||||||
 | 
					import java.util.concurrent.*; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** | 
				
			||||||
 | 
					 * 定义全局线程池 | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					@Slf4j | 
				
			||||||
 | 
					@Component | 
				
			||||||
 | 
					public class GlobalThreadPool { | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 定义全局线程池,使用单列模式
 | 
				
			||||||
 | 
					    private static final ExecutorService THREAD_POOL = new ThreadPoolExecutor( | 
				
			||||||
 | 
					            10, // 核心线程数
 | 
				
			||||||
 | 
					            50, // 最大线程数
 | 
				
			||||||
 | 
					            60L, TimeUnit.SECONDS, // 空闲线程存活时间
 | 
				
			||||||
 | 
					            new LinkedBlockingQueue<Runnable>(100), // 任务队列
 | 
				
			||||||
 | 
					            Executors.defaultThreadFactory(), // 线程工厂
 | 
				
			||||||
 | 
					            new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
 | 
				
			||||||
 | 
					    ); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 私有化构造方法,防止外部实例化
 | 
				
			||||||
 | 
					    private GlobalThreadPool() {} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 获取全局线程池实列
 | 
				
			||||||
 | 
					    public static ExecutorService getInstance() { | 
				
			||||||
 | 
					        return THREAD_POOL; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 关闭线程池
 | 
				
			||||||
 | 
					    public static void shutdown() { | 
				
			||||||
 | 
					        THREAD_POOL.shutdown(); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 在应用关闭前执行
 | 
				
			||||||
 | 
					    @PreDestroy | 
				
			||||||
 | 
					    public void destroy() { | 
				
			||||||
 | 
					        log.info("Spring 应用关闭,正在关闭线程池..."); | 
				
			||||||
 | 
					        shutdown(); | 
				
			||||||
 | 
					        try { | 
				
			||||||
 | 
					            if (!THREAD_POOL.awaitTermination(60, TimeUnit.SECONDS)) { | 
				
			||||||
 | 
					                THREAD_POOL.shutdownNow(); | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        } catch (InterruptedException e) { | 
				
			||||||
 | 
					            THREAD_POOL.shutdownNow(); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue