20250401 更新
1.推送生物检材定性检验记录数据到labscare平台
This commit is contained in:
@@ -99,6 +99,12 @@
|
||||
<artifactId>dlp-admin-api</artifactId>
|
||||
<version>${dlp.version}</version>
|
||||
</dependency>
|
||||
<!-- 第三方系统api工具-->
|
||||
<dependency>
|
||||
<groupId>digital.laboratory.platform</groupId>
|
||||
<artifactId>dlp-othersys-api</artifactId>
|
||||
<version>${dlp.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>digital.laboratory.platform</groupId>
|
||||
<artifactId>dlp-drugtesting-api</artifactId>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,14 @@ public class TestRecordReagentConverter {
|
||||
return testRecordReagentVO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体分页对象转 VO 分页对象
|
||||
*/
|
||||
public static List<TestRecordReagentVO> entityToVOList(List<TestRecordReagent> entityList) {
|
||||
// 将实体列表转换为 VO 列表
|
||||
return entityList.stream().map(TestRecordReagentConverter::entityToVO).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体分页对象转 VO 分页对象
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package digital.laboratory.platform.inspection.convert;
|
||||
|
||||
import digital.laboratory.platform.inspection.constant.TestRecordSampleDataConstant;
|
||||
import digital.laboratory.platform.inspection.dto.SampleInspectDataDTO;
|
||||
import digital.laboratory.platform.inspection.entity.TestRecordSampleData;
|
||||
import digital.laboratory.platform.inspection.vo.TestRecordSampleDataVO;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -15,6 +17,30 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
public class TestRecordSampleDataConverter {
|
||||
|
||||
public static TestRecordSampleData voToEntity(TestRecordSampleDataVO vo) {
|
||||
if (vo == null) return null;
|
||||
TestRecordSampleData testRecordSampleData = new TestRecordSampleData();
|
||||
testRecordSampleData.setId(vo.getId());
|
||||
testRecordSampleData.setName(vo.getName());
|
||||
testRecordSampleData.setSampleNo(vo.getSampleNo());
|
||||
testRecordSampleData.setTestId(vo.getTestId());
|
||||
testRecordSampleData.setStdConcentration(vo.getStdConcentration());
|
||||
testRecordSampleData.setSampleConcentration(vo.getSampleConcentration());
|
||||
testRecordSampleData.setCompoundName(vo.getCompoundName());
|
||||
testRecordSampleData.setRtTimeWithinError(vo.getRtTimeWithinError());
|
||||
testRecordSampleData.setRtTimeError(vo.getRtTimeError());
|
||||
testRecordSampleData.setTargetRtTime(vo.getTargetRtTime());
|
||||
testRecordSampleData.setStdRtTime(vo.getStdRtTime());
|
||||
testRecordSampleData.setIsDetected(vo.getIsDetected().equals(TestRecordSampleDataConstant.IS) ? 1 : 0);
|
||||
testRecordSampleData.setSampleType(vo.getSampleType());
|
||||
testRecordSampleData.setDataJson(vo.getDataJson());
|
||||
testRecordSampleData.setDataResultJson(vo.getDataResultJson());
|
||||
testRecordSampleData.setStatus(vo.getStatus());
|
||||
testRecordSampleData.setCompoundCnName(vo.getCompoundCnName());
|
||||
testRecordSampleData.setWhetherCheckOut(vo.getWhetherCheckOut());
|
||||
return testRecordSampleData;
|
||||
}
|
||||
|
||||
/**
|
||||
* dot 转 entity
|
||||
* @param dto
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package digital.laboratory.platform.inspection.service;
|
||||
|
||||
import digital.laboratory.platform.common.core.util.R;
|
||||
import digital.laboratory.platform.inspection.entity.TestRecordSampleData;
|
||||
import digital.laboratory.platform.inspetion.api.entity.SampleInfo;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author ChenJiangBao
|
||||
@@ -21,4 +24,29 @@ public interface InspectRecordService {
|
||||
R buildInspectionRecord(String businessId, String materialType) throws Exception;
|
||||
|
||||
void previewInspectionRecord(String businessId, String materialType, HttpServletResponse servletResponse) throws Exception;
|
||||
|
||||
/**
|
||||
* 构建检材性状描述(使用检材编号尾号,并合并连续编号)
|
||||
* <p>
|
||||
* 规则:
|
||||
* 1. **提取编号尾号**(如 `2025007001` → `1`)
|
||||
* 2. **按尾号排序**
|
||||
* 3. **连续编号 & 物质性状相同时合并**
|
||||
* 4. **只有一个检材时,省略编号,直接描述**
|
||||
* 5. **输出格式示例:**
|
||||
* - `"1号至4号检材为粉状"`
|
||||
* - `"检材为粉状"`(仅有一个检材时)
|
||||
*
|
||||
* @param sampleInfoList 样本信息列表
|
||||
* @return 物料特性描述(如果列表为空,则返回空字符串)
|
||||
*/
|
||||
String buildMaterialCharacterDesc(List<SampleInfo> sampleInfoList);
|
||||
|
||||
/**
|
||||
* 根据传入的TestRecordSampleData列表生成鉴定意见
|
||||
*
|
||||
* @param dataList 包含检验数据的列表
|
||||
* @return 检测意见字符串,以“;”分隔
|
||||
*/
|
||||
String buildInspectOpinion(List<TestRecordSampleData> dataList);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,14 @@ public interface ProcessInspectDataService {
|
||||
*/
|
||||
String calculateHairCaseIonAbundanceRatioWithinError(double ionAbundanceRatioWithinError, double stdIonAbundanceRatio);
|
||||
|
||||
/**
|
||||
* 根据给定的标准离子丰度比获取生物样本案件离子丰度比相对误差范围内的最大允许相对误差
|
||||
*
|
||||
* @param stdIonAbundanceRatio 标准离子丰度比
|
||||
* @return 发案离子丰度比允许的最大误差值
|
||||
*/
|
||||
Double getHairCaseIonAbundanceRatioWithinErrorRange(double stdIonAbundanceRatio);
|
||||
|
||||
/**
|
||||
* 计算离子丰度比偏差
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package digital.laboratory.platform.inspection.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
@@ -29,6 +30,13 @@ public interface TestRecordReagentService extends IService<TestRecordReagent> {
|
||||
*/
|
||||
TestRecordReagentVO getVOById(String id);
|
||||
|
||||
/**
|
||||
* 根据wrapper获取vo类
|
||||
* @param queryWrapper
|
||||
* @return
|
||||
*/
|
||||
List<TestRecordReagentVO> voListByWrapper(Wrapper<TestRecordReagent> queryWrapper);
|
||||
|
||||
TestRecordReagent addTestRecordReagent(TestRecordReagent testRecordReagent);//添加实验使用方法
|
||||
|
||||
TestRecordReagent updateTestRecordReagent(TestRecordReagent testRecordReagent);//修改实验使用方法
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,6 @@ import digital.laboratory.platform.inspection.vo.IonPairAndCEVO;
|
||||
import digital.laboratory.platform.inspection.vo.TestRecordReagentVO;
|
||||
import digital.laboratory.platform.inspetion.api.entity.EntrustInfo;
|
||||
import digital.laboratory.platform.inspetion.api.entity.SampleInfo;
|
||||
import digital.laboratory.platform.inspetion.api.entity.TargetObject;
|
||||
import digital.laboratory.platform.inspetion.api.entity.TestRecord;
|
||||
import digital.laboratory.platform.sys.entity.Drug;
|
||||
import digital.laboratory.platform.sys.enums.entrust.EntrustBiologyType;
|
||||
@@ -846,7 +845,8 @@ public class InspectRecordServiceImpl implements InspectRecordService {
|
||||
* @param sampleInfoList 样本信息列表
|
||||
* @return 物料特性描述(如果列表为空,则返回空字符串)
|
||||
*/
|
||||
private String buildMaterialCharacterDesc(List<SampleInfo> sampleInfoList) {
|
||||
@Override
|
||||
public String buildMaterialCharacterDesc(List<SampleInfo> sampleInfoList) {
|
||||
if (CollUtil.isEmpty(sampleInfoList)) {
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
@@ -930,6 +930,13 @@ public class InspectRecordServiceImpl implements InspectRecordService {
|
||||
return String.join("、", sampleNumbers);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据传入的TestRecordSampleData列表生成鉴定意见
|
||||
*
|
||||
* @param dataList 包含检验数据的列表
|
||||
* @return 检测意见字符串,以“;”分隔
|
||||
*/
|
||||
@Override
|
||||
public String buildInspectOpinion(List<TestRecordSampleData> dataList) {
|
||||
if (dataList == null || dataList.isEmpty()) {
|
||||
return "";
|
||||
|
||||
@@ -47,34 +47,35 @@ public class ProcessInspectDataServiceImpl implements ProcessInspectDataService
|
||||
@Override
|
||||
public String calculateHairCaseIonAbundanceRatioWithinError(double ionAbundanceRatioWithinError, double stdIonAbundanceRatio) {
|
||||
|
||||
// 判断是否在离子丰度比允许的最大偏差范围
|
||||
return setHairCaseWhetherCheckOut(
|
||||
Math.abs(ionAbundanceRatioWithinError),
|
||||
getHairCaseIonAbundanceRatioWithinErrorRange(stdIonAbundanceRatio));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据给定的标准离子丰度比获取生物样本案件离子丰度比相对误差范围内的最大允许相对误差
|
||||
*
|
||||
* @param stdIonAbundanceRatio 标准离子丰度比
|
||||
* @return 发案离子丰度比允许的最大误差值
|
||||
*/
|
||||
@Override
|
||||
public Double getHairCaseIonAbundanceRatioWithinErrorRange(double stdIonAbundanceRatio) {
|
||||
|
||||
// 判断是否在离子丰度比允许的最大偏差范围
|
||||
if (stdIonAbundanceRatio > TestRecordSampleDataConstant.HAIR_CASE_ION_ABUNDANCE_RATIO_1) {
|
||||
|
||||
return setHairCaseWhetherCheckOut(
|
||||
ionAbundanceRatioWithinError,
|
||||
TestRecordSampleDataConstant.HAIR_CASE_POSITIVE_MAX_ALLOW_ERROR_1,
|
||||
TestRecordSampleDataConstant.HAIR_CASE_NEGATIVE_MAX_ALLOW_ERROR_1);
|
||||
|
||||
return TestRecordSampleDataConstant.HAIR_CASE_POSITIVE_MAX_ALLOW_ERROR_1;
|
||||
} else if (stdIonAbundanceRatio > TestRecordSampleDataConstant.HAIR_CASE_ION_ABUNDANCE_RATIO_2
|
||||
&& stdIonAbundanceRatio <= TestRecordSampleDataConstant.HAIR_CASE_ION_ABUNDANCE_RATIO_1) {
|
||||
|
||||
return setHairCaseWhetherCheckOut(
|
||||
ionAbundanceRatioWithinError,
|
||||
TestRecordSampleDataConstant.HAIR_CASE_POSITIVE_MAX_ALLOW_ERROR_2,
|
||||
TestRecordSampleDataConstant.HAIR_CASE_NEGATIVE_MAX_ALLOW_ERROR_2);
|
||||
|
||||
return TestRecordSampleDataConstant.HAIR_CASE_POSITIVE_MAX_ALLOW_ERROR_2;
|
||||
} else if (stdIonAbundanceRatio > TestRecordSampleDataConstant.HAIR_CASE_ION_ABUNDANCE_RATIO_3
|
||||
&& stdIonAbundanceRatio <= TestRecordSampleDataConstant.HAIR_CASE_ION_ABUNDANCE_RATIO_2) {
|
||||
|
||||
return setHairCaseWhetherCheckOut(
|
||||
ionAbundanceRatioWithinError,
|
||||
TestRecordSampleDataConstant.HAIR_CASE_POSITIVE_MAX_ALLOW_ERROR_3,
|
||||
TestRecordSampleDataConstant.HAIR_CASE_NEGATIVE_MAX_ALLOW_ERROR_3);
|
||||
return TestRecordSampleDataConstant.HAIR_CASE_POSITIVE_MAX_ALLOW_ERROR_3;
|
||||
} else {
|
||||
return setHairCaseWhetherCheckOut(
|
||||
ionAbundanceRatioWithinError,
|
||||
TestRecordSampleDataConstant.HAIR_CASE_POSITIVE_MAX_ALLOW_ERROR_4,
|
||||
TestRecordSampleDataConstant.HAIR_CASE_NEGATIVE_MAX_ALLOW_ERROR_4);
|
||||
return TestRecordSampleDataConstant.HAIR_CASE_POSITIVE_MAX_ALLOW_ERROR_4;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,11 +167,9 @@ public class ProcessInspectDataServiceImpl implements ProcessInspectDataService
|
||||
*
|
||||
* @param ionAbundanceRatioWithinError
|
||||
* @param positive
|
||||
* @param negative
|
||||
*/
|
||||
private String setHairCaseWhetherCheckOut(double ionAbundanceRatioWithinError, double positive, double negative) {
|
||||
if (ionAbundanceRatioWithinError < positive
|
||||
&& ionAbundanceRatioWithinError > negative) {
|
||||
private String setHairCaseWhetherCheckOut(double ionAbundanceRatioWithinError, double positive) {
|
||||
if (ionAbundanceRatioWithinError < positive) {
|
||||
return TestRecordSampleDataConstant.IS;
|
||||
} else {
|
||||
return TestRecordSampleDataConstant.NO;
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package digital.laboratory.platform.inspection.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
@@ -66,6 +67,17 @@ public class TestRecordReagentServiceImpl extends ServiceImpl<TestRecordReagentM
|
||||
return TestRecordReagentConverter.entityToVO(super.getById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据wrapper获取vo类
|
||||
* @param queryWrapper
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@DlpResultProc
|
||||
public List<TestRecordReagentVO> voListByWrapper(Wrapper<TestRecordReagent> queryWrapper) {
|
||||
return TestRecordReagentConverter.entityToVOList(super.list(queryWrapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestRecordReagent addTestRecordReagent(TestRecordReagent testRecordReagent) {
|
||||
if (StringUtils.isBlank(testRecordReagent.getId())) {
|
||||
|
||||
@@ -28,6 +28,7 @@ import digital.laboratory.platform.inspection.enums.StdSolutionNum;
|
||||
import digital.laboratory.platform.inspection.enums.TaskTestDataStatus;
|
||||
import digital.laboratory.platform.inspection.event.AuditDataExecutionEvent;
|
||||
import digital.laboratory.platform.inspection.event.FinishTestExecutionEvent;
|
||||
import digital.laboratory.platform.inspection.event.PushDataToLabsCareEvent;
|
||||
import digital.laboratory.platform.inspection.mapper.SampleInjectorMapper;
|
||||
import digital.laboratory.platform.inspection.mapper.TestRecordSampleDataMapper;
|
||||
import digital.laboratory.platform.inspection.query.AnalysisTestResultPageQuery;
|
||||
@@ -250,20 +251,6 @@ public class TestRecordSampleDataServiceImpl extends ServiceImpl<TestRecordSampl
|
||||
List<TestRecordSampleDataVO> list = baseMapper.queryTestRecordSampleDataVOList(Wrappers.<TestRecordSampleData>lambdaQuery()
|
||||
.eq(TestRecordSampleData::getTestId, testId)
|
||||
.last("ORDER BY compound_name, SUBSTRING_INDEX(name, '-', -1) + 0"));
|
||||
// List<Object> retList = new ArrayList<>();
|
||||
// switch (BusinessType.getBusinessTypeByType(type)) {
|
||||
// case SCREENING_EVENT:
|
||||
// case NPS_CASE:
|
||||
// // NPS的数据分析
|
||||
// extractedSampleTestData(list, retList, NPSCaseTestDataDto.class);
|
||||
// break;
|
||||
// case BOINT_CASE:
|
||||
// extractedSampleTestData(list, retList, HairSewageDataDto.class);
|
||||
// break;
|
||||
// }
|
||||
|
||||
// z最后转换类型
|
||||
// TypeCasting(retList);
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -279,19 +266,6 @@ public class TestRecordSampleDataServiceImpl extends ServiceImpl<TestRecordSampl
|
||||
.eq(TestRecordSampleData::getTestId, pageDTO.getTestId())
|
||||
.orderByDesc(TestRecordSampleData::getCompoundName)
|
||||
.orderByDesc(TestRecordSampleData::getSampleNo));
|
||||
// List<Object> retList = new ArrayList<>();
|
||||
// switch (BusinessType.getBusinessTypeByType(pageDTO.getType())) {
|
||||
// case NPS_CASE:
|
||||
// // NPS的数据分析
|
||||
// extractedSampleTestData(page.getRecords(), retList, NPSCaseTestDataDto.class);
|
||||
// case BOINT_CASE:
|
||||
// extractedSampleTestData(page.getRecords(), retList, HairSewageDataDto.class);
|
||||
// }
|
||||
// Page<Object> resultPage = new Page<>();
|
||||
// BeanUtils.copyProperties(page, resultPage, "records");
|
||||
// // z最后转换类型
|
||||
//// TypeCasting(retList);
|
||||
// resultPage.setRecords(retList);
|
||||
return page;
|
||||
}
|
||||
|
||||
@@ -733,6 +707,7 @@ public class TestRecordSampleDataServiceImpl extends ServiceImpl<TestRecordSampl
|
||||
}
|
||||
// 发布事件
|
||||
applicationContext.publishEvent(new FinishTestExecutionEvent(this));
|
||||
applicationContext.publishEvent(new PushDataToLabsCareEvent(this, ));
|
||||
return R.ok("完成实验成功!");
|
||||
}
|
||||
|
||||
@@ -1364,26 +1339,6 @@ public class TestRecordSampleDataServiceImpl extends ServiceImpl<TestRecordSampl
|
||||
return JSONObject.toJavaObject(jsonObject, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检验数据类型转换
|
||||
*
|
||||
* @param retList
|
||||
* @return
|
||||
*/
|
||||
private List<?> typeCasting(List<Object> retList) {
|
||||
List<?> collect = retList.stream().map(item -> {
|
||||
if (item instanceof HairSewageDataDto) {
|
||||
return (HairSewageDataDto) item;
|
||||
} else if (item instanceof NPSCaseTestDataDto) {
|
||||
return (NPSCaseTestDataDto) item;
|
||||
} else {
|
||||
// todo 待处理的任务dto
|
||||
return item;
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
return collect;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成审核任务列表的头部名称json数组
|
||||
*
|
||||
|
||||
@@ -283,6 +283,8 @@ public class TestRecordSampledataExpandServiceImpl extends ServiceImpl<TestRecor
|
||||
} else {
|
||||
sampleDataExpand.setIonAbundanceRatioWithinError(processInspectDataService.calculateHairCaseIonAbundanceRatioWithinError(ionAbundanceRatioError.doubleValue(), stdDataExpand.getIonAbundanceRatio().doubleValue()));
|
||||
}
|
||||
} else {
|
||||
log.warn("没有找到匹配的标准物质检验数据!");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,3 +54,11 @@ oss:
|
||||
accessKey: dlp
|
||||
secretKey: 87990016
|
||||
bucket-name: dlpfiles
|
||||
# 贵阳禁毒情报平台推送数据配置
|
||||
gyjd:
|
||||
labscare:
|
||||
api:
|
||||
host: http://lc2203.cdn.labscare.com
|
||||
entrust-letter: /thirdparty/report-generate/jianDingWeiTuoShuReport/v1
|
||||
confirm-letter: /thirdparty/report-generate/jianDingShiXiangQueRenShuReport/v1
|
||||
biology-qualitative-record: /thirdparty/report-generate/shenWuJianCaiDingXingJiLuReport/v1
|
||||
|
||||
Reference in New Issue
Block a user