|
|
|
@ -29,10 +29,10 @@ import digital.laboratory.platform.entrustment.mapper.EntrustMaterialCheckoutRes |
|
|
|
|
import digital.laboratory.platform.entrustment.mapper.EntrustmentIdentificationMaterialMapper; |
|
|
|
|
import digital.laboratory.platform.entrustment.query.EntrustMaterialCheckoutResultQuery; |
|
|
|
|
import digital.laboratory.platform.entrustment.service.*; |
|
|
|
|
import digital.laboratory.platform.entrustment.utils.ExcelUtils; |
|
|
|
|
import digital.laboratory.platform.entrustment.vo.DetectionRateVO; |
|
|
|
|
import digital.laboratory.platform.entrustment.vo.EntrustMaterialCheckoutResultVO; |
|
|
|
|
import digital.laboratory.platform.entrustment.vo.EntrustmentIdentificationMaterialVO; |
|
|
|
|
import digital.laboratory.platform.entrustment.vo.SuspectDetectionVO; |
|
|
|
|
import digital.laboratory.platform.othersys.utils.HttpsUtils; |
|
|
|
|
import digital.laboratory.platform.sys.entity.Area; |
|
|
|
|
import digital.laboratory.platform.sys.entity.DrugLite; |
|
|
|
@ -44,6 +44,7 @@ import org.springframework.beans.BeanUtils; |
|
|
|
|
import org.springframework.http.*; |
|
|
|
|
import org.springframework.stereotype.Service; |
|
|
|
|
import org.springframework.web.client.RestClientException; |
|
|
|
|
import org.springframework.web.multipart.MultipartFile; |
|
|
|
|
|
|
|
|
|
import javax.annotation.Resource; |
|
|
|
|
import javax.servlet.http.HttpServletResponse; |
|
|
|
@ -54,13 +55,11 @@ import java.math.BigDecimal; |
|
|
|
|
import java.math.RoundingMode; |
|
|
|
|
import java.net.URLEncoder; |
|
|
|
|
import java.sql.*; |
|
|
|
|
import java.time.LocalDate; |
|
|
|
|
import java.time.LocalDateTime; |
|
|
|
|
import java.time.LocalTime; |
|
|
|
|
import java.time.Month; |
|
|
|
|
import java.time.*; |
|
|
|
|
import java.time.format.DateTimeFormatter; |
|
|
|
|
import java.time.temporal.TemporalAdjusters; |
|
|
|
|
import java.util.*; |
|
|
|
|
import java.util.concurrent.ThreadLocalRandom; |
|
|
|
|
import java.util.function.Function; |
|
|
|
|
import java.util.stream.Collectors; |
|
|
|
|
|
|
|
|
@ -95,6 +94,18 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus |
|
|
|
|
@Resource |
|
|
|
|
private OssFile ossFile; |
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
地址:83.103.24.236 |
|
|
|
|
端口:43306 |
|
|
|
|
数据库:znyp |
|
|
|
|
数据库账号:wsjc |
|
|
|
|
密码:wsjc9985 |
|
|
|
|
*/ |
|
|
|
|
// 情报平台数据库连接信息
|
|
|
|
|
String intDBUrl = "jdbc:mysql://83.103.24.236:43306/znyp?characterEncoding=utf8&serverTimezone=Asia/Shanghai"; |
|
|
|
|
String intDBUsername = "wsjc"; |
|
|
|
|
String intDBPassword = "wsjc9985"; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 构建委托材料检验结果字符串 |
|
|
|
|
* |
|
|
|
@ -1088,6 +1099,10 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus |
|
|
|
|
if (entrustment == null) { |
|
|
|
|
throw new IllegalArgumentException("未找到委托信息:" + entrustmentId); |
|
|
|
|
} |
|
|
|
|
boolean push = false; |
|
|
|
|
|
|
|
|
|
// 推送数据至省厅系统
|
|
|
|
|
push = this.pushQualitativeReportData(entrustment, user); |
|
|
|
|
|
|
|
|
|
// 获取案件信息
|
|
|
|
|
CaseEvent caseEvent = caseEventService.getById(entrustment.getCaseId()); |
|
|
|
@ -1111,6 +1126,9 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus |
|
|
|
|
.list(); |
|
|
|
|
|
|
|
|
|
if (CollectionUtils.isEmpty(suspects)) { |
|
|
|
|
if (push) { |
|
|
|
|
throw new RuntimeException("推送数据至省厅系统成功,但未找到嫌疑人信息,请检查案件信息!"); |
|
|
|
|
} else |
|
|
|
|
throw new IllegalArgumentException("未找到嫌疑人信息:" + entrustmentId + ",请先添加嫌疑人信息!"); |
|
|
|
|
} |
|
|
|
|
// 构造物证材料映射
|
|
|
|
@ -1126,10 +1144,9 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus |
|
|
|
|
.map(r -> buildResultVO(r, materialMap)) |
|
|
|
|
.sorted(Comparator.comparingInt(EntrustMaterialCheckoutResultVO::getOrderNo)) |
|
|
|
|
.collect(Collectors.toList()); |
|
|
|
|
boolean push = pushToDatabase(suspects, resultVOS, entrustment, caseEvent, user); |
|
|
|
|
if (entrustment.getEntrustmentType() == 0) { |
|
|
|
|
push = this.pushQualitativeReportData(entrustment, user); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
push = pushToDatabase(suspects, resultVOS, entrustment, caseEvent, user); |
|
|
|
|
|
|
|
|
|
if (push) { |
|
|
|
|
entrustment.setPlatformFlag(true); |
|
|
|
|
entrustmentService.updateById(entrustment); |
|
|
|
@ -1138,6 +1155,55 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus |
|
|
|
|
return push; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public R batchPushHistoryDataToProvince(DLPUser user, LocalDateTime startTime, LocalDateTime endTime, Integer entrustType) { |
|
|
|
|
Integer successCount = 0; |
|
|
|
|
Integer failCount = 0; |
|
|
|
|
// 获取检验结果
|
|
|
|
|
List<EntrustMaterialCheckoutResult> results = this.lambdaQuery() |
|
|
|
|
.isNotNull(EntrustMaterialCheckoutResult::getQualitativeResult) |
|
|
|
|
.list(); |
|
|
|
|
|
|
|
|
|
Set<String> entrustId = results.stream() |
|
|
|
|
.map(r -> { |
|
|
|
|
return r.getEntrustId(); |
|
|
|
|
}).collect(Collectors.toSet()); |
|
|
|
|
|
|
|
|
|
List<Entrustment> entrusts = entrustmentService.lambdaQuery() |
|
|
|
|
.in(Entrustment::getId, entrustId) |
|
|
|
|
.between(startTime != null && endTime != null, Entrustment::getAcceptTime, startTime, endTime) |
|
|
|
|
.eq(entrustType != null, Entrustment::getEntrustmentType, entrustType) |
|
|
|
|
.list(); |
|
|
|
|
String flag = "AppraisalDocument" + ":true"; |
|
|
|
|
for (Entrustment entrust : entrusts) { |
|
|
|
|
if (this.pushQualitativeReportData(entrust, user)) { |
|
|
|
|
// 处理 PushFlag,防止 null,并确保 flag 至少存在一次
|
|
|
|
|
List<String> flagList = Optional.ofNullable(StrUtil.split(entrust.getPushFlag(), StrUtil.COMMA)) |
|
|
|
|
.orElse(new ArrayList<>()); |
|
|
|
|
|
|
|
|
|
// 避免重复添加相同的 flag(可选)
|
|
|
|
|
if (!flagList.contains(flag)) { |
|
|
|
|
flagList.add(flag); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 确保至少有一个元素
|
|
|
|
|
if (flagList.isEmpty()) { |
|
|
|
|
flagList.add(flag); // 如果原字符串为空或解析后为空列表,则手动添加 flag
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 将结果重新设置回去
|
|
|
|
|
entrust.setPushFlag(String.join(StrUtil.COMMA, flagList)); |
|
|
|
|
successCount++; |
|
|
|
|
entrustmentService.updateById(entrust); |
|
|
|
|
} else { |
|
|
|
|
failCount++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return R.ok(successCount, "成功推送" + successCount + "条数据,失败" + failCount + "条数据"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
; |
|
|
|
|
|
|
|
|
|
private EntrustMaterialCheckoutResultVO buildResultVO(EntrustMaterialCheckoutResult result, Map<String, EntrustmentIdentificationMaterial> materialMap) { |
|
|
|
|
EntrustMaterialCheckoutResultVO vo = new EntrustMaterialCheckoutResultVO(); |
|
|
|
|
BeanUtils.copyProperties(result, vo); |
|
|
|
@ -1163,32 +1229,9 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private SuspectDetectionVO buildSuspectVO(Suspect suspect, CaseEvent caseEvent, Entrustment entrustment, List<EntrustMaterialCheckoutResultVO> resultVOS, DLPUser user) { |
|
|
|
|
SuspectDetectionVO vo = new SuspectDetectionVO(); |
|
|
|
|
BeanUtils.copyProperties(suspect, vo); |
|
|
|
|
vo.setCaseName(caseEvent.getCaseName()); |
|
|
|
|
vo.setCaseBrief(caseEvent.getCaseBrief()); |
|
|
|
|
vo.setHappenTime(caseEvent.getHappenTime()); |
|
|
|
|
vo.setPushTime(LocalDateTime.now()); |
|
|
|
|
vo.setAcceptNo(entrustment.getAcceptNo()); |
|
|
|
|
vo.setResults(resultVOS); |
|
|
|
|
return vo; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
地址:83.103.24.236 |
|
|
|
|
端口:43306 |
|
|
|
|
数据库:znyp |
|
|
|
|
数据库账号:wsjc |
|
|
|
|
密码:wsjc9985 |
|
|
|
|
*/ |
|
|
|
|
private boolean pushToDatabase(List<Suspect> suspects, List<EntrustMaterialCheckoutResultVO> resultVOS, Entrustment entrustment, CaseEvent caseEvent, DLPUser user) { |
|
|
|
|
String url = "jdbc:mysql://83.103.24.236:43306/znyp?characterEncoding=utf8&serverTimezone=Asia/Shanghai"; |
|
|
|
|
String username = "wsjc"; |
|
|
|
|
String password = "wsjc9985"; |
|
|
|
|
|
|
|
|
|
try (Connection connection = DriverManager.getConnection(url, username, password)) { |
|
|
|
|
try (Connection connection = DriverManager.getConnection(intDBUrl, intDBUsername, intDBPassword)) { |
|
|
|
|
|
|
|
|
|
// 推送嫌疑人信息
|
|
|
|
|
String sql1 = "INSERT INTO b_suspect (id, name, id_number, phone_number, sex, age, accept_no, order_no, case_name, case_brief, happen_time, push_time, push_user, accept_time,client_org_name) " + |
|
|
|
@ -1257,19 +1300,20 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus |
|
|
|
|
|
|
|
|
|
public boolean pushQualitativeReportData(Entrustment entrust, DLPUser dlpUser) { |
|
|
|
|
JSONObject jsonObject = this.buildQualitativeReportJsonPayload(entrust, dlpUser); |
|
|
|
|
log.info("推送定性报告数据, 受理编号: {}, 请求数据: {}", entrust.getAcceptNo(), jsonObject); |
|
|
|
|
String type = entrust.getEntrustmentType() == 0 ? "常规毒品" : "生物样本"; |
|
|
|
|
log.info("推送" + type + "定性报告数据, 受理编号: {}, 请求数据: {}", entrust.getAcceptNo(), jsonObject); |
|
|
|
|
try { |
|
|
|
|
ResponseEntity<String> response = exchangeRemoteApi(jsonObject, "http://83.3.9.45/thirdparty/report-generate/dingXingReport/v1"); |
|
|
|
|
if (response.getStatusCode().is2xxSuccessful() && response.getBody().contains("\"status\":200")) { |
|
|
|
|
log.info("推送定性报告数据成功, 受理编号: {}, 响应: {}", entrust.getAcceptNo(), response.getBody()); |
|
|
|
|
log.info("推送" + type + "定性报告数据成功, 受理编号: {}, 响应: {}", entrust.getAcceptNo(), response.getBody()); |
|
|
|
|
return true; |
|
|
|
|
} else { |
|
|
|
|
log.warn("推送定性报告数据失败, 受理编号: {}, 状态码: {}, 响应: {}", |
|
|
|
|
log.warn("推送" + type + "定性报告数据失败, 受理编号: {}, 状态码: {}, 响应: {}", |
|
|
|
|
entrust.getAcceptNo(), response.getStatusCode(), response.getBody()); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} catch (RestClientException e) { |
|
|
|
|
log.error("推送定性报告数据到 LabsCare 失败, 受理编号: {}", entrust.getAcceptNo(), e); |
|
|
|
|
log.error("推送" + type + "定性报告数据到 LabsCare 失败, 受理编号: {}", entrust.getAcceptNo(), e); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -1304,19 +1348,15 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus |
|
|
|
|
*/ |
|
|
|
|
private JSONObject buildQualitativeReportJsonPayload(Entrustment entrust, DLPUser dlpUser) { |
|
|
|
|
|
|
|
|
|
List<EntrustmentIdentificationMaterial> materialList = entrustmentIdentificationMaterialService.lambdaQuery() |
|
|
|
|
.eq(EntrustmentIdentificationMaterial::getEntrustmentId, entrust.getId()) |
|
|
|
|
.list(); |
|
|
|
|
|
|
|
|
|
CaseEvent caseEvent = caseEventService.getById(entrust.getCaseId()); |
|
|
|
|
|
|
|
|
|
boolean ret = false; |
|
|
|
|
if (!StringUtils.isNotBlank(entrust.getId())) { |
|
|
|
|
ret = true; |
|
|
|
|
} |
|
|
|
|
String[] splitAcceptNo = entrust.getAcceptNo().split("-"); |
|
|
|
|
// 封装推送定性报告数据
|
|
|
|
|
JSONObject jsonObject = new JSONObject(); |
|
|
|
|
jsonObject.set("jcxz", buildMaterialCharacterDesc(materialList)); // 检材性状描述
|
|
|
|
|
|
|
|
|
|
jsonObject.set("jiandingwenshuhao", String.format("[%s]%s号", splitAcceptNo[0], splitAcceptNo[1])); // 鉴定文书号
|
|
|
|
|
jsonObject.set("jdyq", entrust.getEntrustRequirement()); // 鉴定要求
|
|
|
|
|
jsonObject.set("jyaq", caseEvent.getCaseBrief()); // 简要案情
|
|
|
|
|
jsonObject.set("jyy1qm", dlpUser.getName()); // 检测人
|
|
|
|
|
jsonObject.set("jyyr2qm", dlpUser.getName()); // 检测人
|
|
|
|
|
jsonObject.set("jyy1zc", "#NULL#"); // 检验人1专业技术资格或职称
|
|
|
|
@ -1328,12 +1368,27 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus |
|
|
|
|
jsonObject.set("wtdw", entrust.getClientOrgName()); // 委托单位
|
|
|
|
|
jsonObject.set("zrqm", "#NULL#"); // 授权签字人
|
|
|
|
|
jsonObject.set("zrzc", "#NULL#"); // 授权签字人专业技术资格或职称
|
|
|
|
|
|
|
|
|
|
// 定性检验报告样品 检测信息
|
|
|
|
|
JSONObject inspectDataInfo = new JSONObject(); |
|
|
|
|
if (ret) { |
|
|
|
|
jsonObject.set("jcxz", "#NULL#"); // 检材性状描述
|
|
|
|
|
jsonObject.set("jyaq", "#NULL#"); // 简要案情
|
|
|
|
|
jsonObject.set("jdyq", "#NULL#"); // 鉴定要求
|
|
|
|
|
inspectDataInfo.set("outStr", entrust.getPushFlag()); |
|
|
|
|
inspectDataInfo.set("dxff", "#NULL#"); |
|
|
|
|
} else { |
|
|
|
|
List<EntrustmentIdentificationMaterial> materialList = entrustmentIdentificationMaterialService.lambdaQuery() |
|
|
|
|
.eq(EntrustmentIdentificationMaterial::getEntrustmentId, entrust.getId()) |
|
|
|
|
.list(); |
|
|
|
|
CaseEvent caseEvent = caseEventService.getById(entrust.getCaseId()); |
|
|
|
|
jsonObject.set("jcxz", buildMaterialCharacterDesc(materialList)); // 检材性状描述
|
|
|
|
|
jsonObject.set("jyaq", caseEvent.getCaseBrief()); // 简要案情
|
|
|
|
|
jsonObject.set("jdyq", entrust.getEntrustRequirement()); // 鉴定要求
|
|
|
|
|
inspectDataInfo.set("outStr", this.buildResultStr(entrust.getId())); |
|
|
|
|
inspectDataInfo.set("dxff", "《疑似毒品中 2-氟苯丙胺等 388种麻醉药品和精神药品的定性检验 气相色谱-质谱法》(JD/Y JY01.07-2024)"); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
// 定性检验报告样品 检测信息
|
|
|
|
|
inspectDataInfo.set("jysj", entrust.getAcceptTime().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日"))); |
|
|
|
|
inspectDataInfo.set("outStr", this.buildResultStr(entrust.getId())); |
|
|
|
|
cn.hutool.json.JSONArray gauging = new cn.hutool.json.JSONArray(); |
|
|
|
|
gauging.add(inspectDataInfo); |
|
|
|
|
// 定性检验报告样品信息
|
|
|
|
@ -2064,6 +2119,97 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public String pushDataByExcel(MultipartFile file, DLPUser dlpUser, Integer year) throws Exception { |
|
|
|
|
int headIndex = 0; |
|
|
|
|
List<Map<String, String>> data = ExcelUtils.readExcel(file, headIndex); |
|
|
|
|
int total = data.size(); |
|
|
|
|
|
|
|
|
|
// 生成严格递增的虚构受理时间列表
|
|
|
|
|
List<LocalDateTime> acceptTimes = generateAcceptTimes(total, year); |
|
|
|
|
System.out.println(data); |
|
|
|
|
int successCount = 0; |
|
|
|
|
int failCount = 0; |
|
|
|
|
for (int i = 0; i < total; i++) { |
|
|
|
|
Map<String, String> datum = data.get(i); |
|
|
|
|
System.out.println(datum); |
|
|
|
|
Entrustment entrust = new Entrustment(); |
|
|
|
|
entrust.setEntrustRequirement("#NULL#"); |
|
|
|
|
entrust.setDeliverer1Name("专管员1"); |
|
|
|
|
entrust.setDeliverer2Name("专管员2"); |
|
|
|
|
entrust.setAcceptTime(acceptTimes.get(i)); |
|
|
|
|
entrust.setAcceptNo(datum.get("受理编号")); |
|
|
|
|
entrust.setPushFlag(datum.get("检出结果")); |
|
|
|
|
entrust.setClientOrgName(datum.get("委托单位")); |
|
|
|
|
int type = datum.get("类型").equals("常规毒品") ? 0 : 1; |
|
|
|
|
entrust.setEntrustmentType(type); |
|
|
|
|
if (this.pushQualitativeReportData(entrust, dlpUser)) { |
|
|
|
|
successCount++; |
|
|
|
|
} else { |
|
|
|
|
failCount++; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
log.info("成功推送:{},失败:{}", successCount, failCount); |
|
|
|
|
return "成功推送:" + successCount + "条,失败:" + failCount + "条"; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private List<LocalDateTime> generateAcceptTimes(int count, int year) { |
|
|
|
|
List<LocalDate> workDays = getAllWorkDays(year); |
|
|
|
|
List<LocalDateTime> result = new ArrayList<>(); |
|
|
|
|
|
|
|
|
|
int entriesPerDay = 16; // 每天最多安排16条(每30分钟一条)
|
|
|
|
|
int currentIndex = 0; |
|
|
|
|
LocalDateTime lastTime = null; |
|
|
|
|
|
|
|
|
|
outer: |
|
|
|
|
for (LocalDate day : workDays) { |
|
|
|
|
for (int i = 0; i < entriesPerDay; i++) { |
|
|
|
|
// 基础时间:9:00 + i * 30min
|
|
|
|
|
LocalTime baseTime = LocalTime.of(9, 0).plusMinutes(i * 30); |
|
|
|
|
if (baseTime.isAfter(LocalTime.of(17, 0))) continue; |
|
|
|
|
|
|
|
|
|
// 加入 ±5分钟随机偏移
|
|
|
|
|
int randomOffset = ThreadLocalRandom.current().nextInt(-5, 6); |
|
|
|
|
LocalTime randomTime = baseTime.plusMinutes(randomOffset); |
|
|
|
|
|
|
|
|
|
// 限制在工作时间范围
|
|
|
|
|
if (randomTime.isBefore(LocalTime.of(9, 0))) randomTime = LocalTime.of(9, 0); |
|
|
|
|
if (randomTime.isAfter(LocalTime.of(17, 0))) randomTime = LocalTime.of(17, 0); |
|
|
|
|
|
|
|
|
|
LocalDateTime acceptTime = LocalDateTime.of(day, randomTime); |
|
|
|
|
|
|
|
|
|
// 确保递增
|
|
|
|
|
if (lastTime != null && !acceptTime.isAfter(lastTime)) { |
|
|
|
|
acceptTime = lastTime.plusMinutes(1); |
|
|
|
|
} |
|
|
|
|
lastTime = acceptTime; |
|
|
|
|
|
|
|
|
|
result.add(acceptTime); |
|
|
|
|
currentIndex++; |
|
|
|
|
|
|
|
|
|
if (currentIndex >= count) break outer; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private List<LocalDate> getAllWorkDays(int year) { |
|
|
|
|
List<LocalDate> workDays = new ArrayList<>(); |
|
|
|
|
LocalDate date = LocalDate.of(year, 1, 1); |
|
|
|
|
LocalDate end = LocalDate.of(year, 12, 31); |
|
|
|
|
|
|
|
|
|
while (!date.isAfter(end)) { |
|
|
|
|
DayOfWeek dow = date.getDayOfWeek(); |
|
|
|
|
if (dow != DayOfWeek.SATURDAY && dow != DayOfWeek.SUNDAY) { |
|
|
|
|
workDays.add(date); |
|
|
|
|
} |
|
|
|
|
date = date.plusDays(1); |
|
|
|
|
} |
|
|
|
|
return workDays; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|