新增文书送达、文书送达记录、字典查询、文书信息等接口

This commit is contained in:
luolianxin 2025-03-07 17:31:02 +08:00
parent 8383232e5c
commit a7420da684
4 changed files with 1217 additions and 0 deletions

View File

@ -0,0 +1,139 @@
package com.aisino.iles.lawenforcement.service;
import com.aisino.iles.common.util.PageableHelper;
import com.aisino.iles.lawenforcement.model.DeliveryMethod;
import com.aisino.iles.lawenforcement.model.query.DeliveryQuery;
import com.aisino.iles.lawenforcement.repository.DeliveryMethodRepository;
import com.smartlx.sso.client.model.RemoteUserInfo;
import jakarta.persistence.criteria.Predicate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* 送达方式服务类
*/
@Service
@Slf4j
public class DeliveryMethodService {
private final DeliveryMethodRepository deliveryMethodRepo;
public DeliveryMethodService(DeliveryMethodRepository deliveryMethodRepo) {
this.deliveryMethodRepo = deliveryMethodRepo;
}
/**
* 保存送达方式信息
*
* @param deliveryMethod 送达方式信息
* @return 保存后的送达方式信息
*/
@Transactional
public DeliveryMethod saveDeliveryMethod(DeliveryMethod deliveryMethod, RemoteUserInfo user, String type) {
LocalDateTime now = LocalDateTime.now();
if ("add".equals(type)) {
deliveryMethod.setIsDeleted(false);
deliveryMethod.setCreateTime(now);
if (null != user) {
deliveryMethod.setCreatedBy(user.getXm());
deliveryMethod.setCreatedAccountBy(user.getYhwybs());
}
}
deliveryMethod.setUpdateTime(now);
return deliveryMethodRepo.save(deliveryMethod);
}
/**
* 根据ID查询送达方式信息
*
* @param deliveryMethodId 送达方式ID
* @return 送达方式信息
*/
public Optional<DeliveryMethod> findDeliveryMethodById(String deliveryMethodId) {
return deliveryMethodRepo.findById(deliveryMethodId);
}
/**
* 根据查询条件分页查询送达方式信息
*
* @param query 查询条件
* @return 分页送达方式信息
*/
public Page<DeliveryMethod> findDeliveryMethodPage(DeliveryQuery query) {
return deliveryMethodRepo.findAll(build(query),
PageableHelper.buildPageRequest(query.page(), query.pageSize(), "updateTime", "desc"));
}
/**
* 构建查询条件
*
* @param query 查询条件
* @return 规格
*/
private Specification<DeliveryMethod> build(DeliveryQuery query) {
return (root, criteriaQuery, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
Optional.ofNullable(query.getIsDeleted()).ifPresent(o -> predicates.add(criteriaBuilder.equal(root.get("isDeleted"), o)));
Optional.ofNullable(query.getMethodName()).filter(StringUtils::hasText).ifPresent(o -> predicates.add(criteriaBuilder.like(root.get("methodName"), "%" + o + "%")));
Optional.ofNullable(query.getDescription()).filter(StringUtils::hasText).ifPresent(o -> predicates.add(criteriaBuilder.like(root.get("description"), "%" + o + "%")));
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
/**
* 根据ID删除送达方式信息
*
* @param deliveryMethodId 送达方式ID
*/
@Transactional
public void deleteDeliveryMethodById(String deliveryMethodId) {
deliveryMethodRepo.findById(deliveryMethodId).ifPresent(deliveryMethod -> {
deliveryMethod.setIsDeleted(true);
deliveryMethod.setUpdateTime(LocalDateTime.now());
deliveryMethodRepo.save(deliveryMethod);
});
}
/**
* 送达方式是否存在
*
* @param deliveryMethodId 送达方式ID
* @return 是否存在
*/
public boolean existsDeliveryMethodById(String deliveryMethodId) {
return deliveryMethodRepo.existsById(deliveryMethodId);
}
/**
* 批量删除送达方式
*
* @param itemIds 送达方式ID列表
*/
@Transactional
public void deleteDeliveryMethodByIds(List<String> itemIds) {
deliveryMethodRepo.findAllById(itemIds).forEach(deliveryMethod -> {
deliveryMethod.setIsDeleted(true);
deliveryMethod.setUpdateTime(LocalDateTime.now());
deliveryMethodRepo.save(deliveryMethod);
});
}
/**
* 根据查询条件获取送达方式列表
*
* @param query 查询条件
* @return 送达方式集合
*/
public List<DeliveryMethod> getDeliveryMethodList(DeliveryQuery query) {
return deliveryMethodRepo.findAll(build(query));
}
}

View File

@ -0,0 +1,769 @@
package com.aisino.iles.lawenforcement.service;
import com.aisino.iles.common.model.enums.IndustryCategoryForFile;
import com.aisino.iles.common.service.FtpService;
import com.aisino.iles.common.util.BeanUtils;
import com.aisino.iles.common.util.KmsServer;
import com.aisino.iles.common.util.PageableHelper;
import com.aisino.iles.core.exception.BusinessError;
import com.aisino.iles.lawenforcement.model.*;
import com.aisino.iles.lawenforcement.model.dto.DeliveryRecordStatisticsDto;
import com.aisino.iles.lawenforcement.model.dto.DeliveryScenePicDto;
import com.aisino.iles.lawenforcement.model.query.DeliveryQuery;
import com.aisino.iles.lawenforcement.repository.AgencyRepository;
import com.aisino.iles.lawenforcement.repository.DeliveryRecordRepository;
import com.aisino.iles.lawenforcement.repository.MaterialsRepository;
import com.aisino.iles.lawenforcement.repository.SmsSendRecordRepository;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.smartlx.sso.client.model.RemoteUserInfo;
import jakarta.persistence.criteria.Predicate;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import static java.nio.charset.StandardCharsets.US_ASCII;
/**
* 文书送达记录服务类
*/
@Service
@Slf4j
public class DeliveryRecordService {
private final DeliveryRecordRepository deliveryRecordRepo;
private final DeliveryMethodService deliveryMethodService;
private final AgencyRepository agencyRepo;
private final SmsSendRecordRepository smsSendRecordRepo;
private final MaterialsRepository materialsRepo;
private final RestTemplate restTemplate;
private final FtpService ftpService;
private final ObjectMapper objectMapper;
private final String deliveryUsername;
private final String deliveryPassword;
private final String publishSmsCallUrl;
private final String publishVoiceCallUrl;
public DeliveryRecordService(DeliveryRecordRepository deliveryRecordRepo,
DeliveryMethodService deliveryMethodService,
AgencyRepository agencyRepo,
SmsSendRecordRepository smsSendRecordRepo,
MaterialsRepository materialsRepo,
RestTemplate restTemplate,
FtpService ftpService,
ObjectMapper objectMapper,
@Value("${third-party.delivery.username}") String deliveryUsername,
@Value("${third-party.delivery.password}") String deliveryPassword,
@Value("${third-party.delivery.publish-sms-call-url}") String publishSmsCallUrl,
@Value("${third-party.delivery.publish-voice-call-url}") String publishVoiceCallUrl) {
this.deliveryRecordRepo = deliveryRecordRepo;
this.deliveryMethodService = deliveryMethodService;
this.agencyRepo = agencyRepo;
this.smsSendRecordRepo = smsSendRecordRepo;
this.materialsRepo = materialsRepo;
this.restTemplate = restTemplate;
this.ftpService = ftpService;
this.objectMapper = objectMapper;
this.deliveryUsername = deliveryUsername;
this.deliveryPassword = deliveryPassword;
this.publishSmsCallUrl = publishSmsCallUrl;
this.publishVoiceCallUrl = publishVoiceCallUrl;
}
/**
* 保存文书送达记录信息
*
* @param deliveryRecord 文书送达记录信息
* @return 保存后的文书送达记录信息
*/
@Transactional
public DeliveryRecord saveDeliveryRecord(DeliveryRecord deliveryRecord, RemoteUserInfo user, String type) {
LocalDateTime now = LocalDateTime.now();
if ("add".equals(type)) {
deliveryRecord.setCreateTime(now);
deliveryRecord.setDeliveryStatus((short) 0);
if (null != user) {
deliveryRecord.setCreatedBy(user.getXm());
deliveryRecord.setCreatedAccountBy(user.getYhwybs());
}
} else if ("send".equals(type)) {
deliveryRecord.setDeliveryStatus((short) 1);
deliveryRecord.setSendTime(now);
}
deliveryRecord.setUpdateTime(now);
DeliveryRecord save = deliveryRecordRepo.save(deliveryRecord);
if ("add".equals(type) || "up".equals(type)) {
List<Materials> materialsList = deliveryRecord.getMaterials();
if (null != materialsList && !materialsList.isEmpty()) {
List<Materials> list = materialsRepo.findByLinkId(deliveryRecord.getDeliveryId());
List<String> newPics = list.stream().map(Materials::getSavePath).toList();
materialsList.stream().filter(o -> !newPics.contains(o.getSavePath()))
.forEach(materials -> {
materials.setLrsj(now);
String name = materials.getName();
materials.setFileType(StringUtils.hasText(name) ? name.split("\\.")[1] : null);
materials.setIsOriginal("1");
materials.setMaterialsTypeCode("3");
materials.setMaterialsTypeName("文书送达现场图片");
materials.setLinkId(save.getDeliveryId());
materials.setMaterialsId(null);
materialsRepo.save(materials);
});
List<String> paths = materialsList.stream().map(Materials::getSavePath).toList();
list.stream().filter(o -> !paths.contains(o.getSavePath())).forEach(o -> {
if (StringUtils.hasText(o.getSavePath()))
ftpService.deletePathFile(o.getSavePath());
materialsRepo.delete(o);
});
}
}
return save;
}
/**
* 根据ID查询文书送达记录信息
*
* @param deliveryRecordId 文书送达记录ID
* @return 文书送达记录信息
*/
@Transactional(readOnly = true)
public Optional<DeliveryRecord> findDeliveryRecordById(String deliveryRecordId) {
return deliveryRecordRepo.findById(deliveryRecordId).map(this::itemDeliveryRecord).map(deliveryRecord -> {
deliveryRecord.setMaterials(materialsRepo.findByLinkId(deliveryRecord.getDeliveryId()).stream().peek(scene -> {
String url = scene.getSavePath();
if (StringUtils.hasText(url)) {
scene.setUrl(ftpService.getFileUrl(url));
scene.setDownloadUrl(ftpService.getFileDownloadUrl(url));
}
}).collect(Collectors.toList()));
return deliveryRecord;
});
}
@Transactional(readOnly = true)
public List<DeliveryRecord> findDeliveryRecordByIds(List<String> deliveryIds) {
return deliveryRecordRepo.findAllById(deliveryIds).stream().map(this::itemDeliveryRecord).collect(Collectors.toList());
}
/**
* 根据查询条件分页查询文书送达记录信息
*
* @param query 查询条件
* @return 分页文书送达记录信息
*/
@Transactional(readOnly = true)
public Page<DeliveryRecord> findDeliveryRecordPage(DeliveryQuery query) {
return deliveryRecordRepo.findAll(build(query), PageableHelper.buildPageRequest(query.page(), query.pageSize(), "updateTime", "desc"), "delivery_record-all").map(this::itemDeliveryRecord);
}
private DeliveryRecord itemDeliveryRecord(DeliveryRecord deliveryRecord) {
if (null != deliveryRecord.getSendTime() && deliveryRecord.getDeliveryStatus() != 2) {
// 计算两个时间之间的小时差
Duration duration = Duration.between(deliveryRecord.getSendTime(), LocalDateTime.now());
long hours = duration.toHours();
deliveryRecord.setNeedCll(hours > 24);
}
return deliveryRecord;
}
/**
* 构建查询条件
*
* @param query 查询条件
* @return 规格
*/
private Specification<DeliveryRecord> build(DeliveryQuery query) {
return (root, criteriaQuery, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
Optional.ofNullable(query.getAgencyCode()).filter(StringUtils::hasText)
.ifPresent(o -> predicates.add(criteriaBuilder.like(root.get(DeliveryRecord_.caseInfo).get(Case_.enforcementInfo).get(EnforcementInfo_.agency).get(Agency_.agencyCode),
com.aisino.iles.common.util.StringUtils.trimEven0(o) + "%")));
Optional.ofNullable(query.getCaseId()).filter(StringUtils::hasText).ifPresent(o -> predicates.add(criteriaBuilder.equal(root.get(DeliveryRecord_.caseId), o)));
Optional.ofNullable(query.getRecipientId()).filter(StringUtils::hasText).ifPresent(o -> predicates.add(criteriaBuilder.equal(root.get(DeliveryRecord_.recipientId), o)));
Optional.ofNullable(query.getDeliveryMethodId()).filter(StringUtils::hasText).ifPresent(o -> predicates.add(criteriaBuilder.equal(root.get(DeliveryRecord_.deliveryMethodId), o)));
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
/**
* 根据ID删除文书送达记录信息
*
* @param deliveryRecordId 文书送达记录ID
*/
@Transactional
public void deleteDeliveryRecordById(String deliveryRecordId) {
deliveryRecordRepo.deleteById(deliveryRecordId);
}
/**
* 文书送达记录是否存在
*
* @param deliveryRecordId 文书送达记录ID
* @return 是否存在
*/
public Boolean existsDeliveryRecordById(String deliveryRecordId) {
return deliveryRecordRepo.existsById(deliveryRecordId);
}
/**
* 批量删除文书送达记录
*
* @param itemIds 文书送达记录ID列表
*/
@Transactional
public void deleteDeliveryRecordByIds(List<String> itemIds) {
deliveryRecordRepo.deleteAllById(itemIds);
}
/**
* 统计
*
* @param query 查询参数
* @param user 用户
* @return 统计结果
*/
public List<Map<String, Object>> statistics(DeliveryQuery query, RemoteUserInfo user) {
String agencyCode = query.getAgencyCode();
if (!StringUtils.hasText(agencyCode)) agencyCode = user.getGajgjgdm();
Integer agencyLevel = query.getAgencyLevel();
if (null == agencyLevel)
agencyLevel = agencyRepo.findByAgencyCode(agencyCode).map(Agency::getAgencyLevel).orElseThrow(() -> new BusinessError("当前用户没有匹配到有效的机构信息"));
String code = com.aisino.iles.common.util.StringUtils.trimEven0(agencyCode) + "%";
List<Agency> agencyList = agencyRepo.findLikeAgencyCode(code, agencyLevel).stream().filter(o -> !"01610100000000001".equals(o.getAgencyCode())).toList();
List<DeliveryRecordStatisticsDto> list = deliveryRecordRepo.statistics(code);
Map<String, List<DeliveryRecordStatisticsDto>> listMap = list.stream().collect(Collectors.groupingBy(DeliveryRecordStatisticsDto::getAgencyCode));
Map<String, Map<String, Object>> mm = new HashMap<>();
Set<String> map = categories();
listMap.forEach((key, val) -> {
Map<String, Object> resMap = new HashMap<>();
map.forEach(k -> {
val.stream()
.filter(dto -> k.equals(dto.getDeliveryMethodId()))
.findFirst()
.ifPresentOrElse(
dto -> resMap.put(k, dto.getNum()),
() -> resMap.put(k, 0L)
);
});
long sum = resMap.values().stream()
.mapToLong(o -> Long.parseLong(String.valueOf(o)))
.sum();
resMap.put("total", sum);
mm.put(key, resMap);
});
return agencyList.stream().map(o -> {
Map<String, Object> resMap = new HashMap<>();
resMap.put("agencyCode", o.getAgencyCode());
resMap.put("agencyName", o.getAgencyName());
resMap.put("agencyLevel", o.getAgencyLevel());
resMap.put("hasChildren", o.getLeaf() ? 0 : 1);
Set<String> set = mm.keySet().stream().filter(v -> v.length() >= o.getAgencySimpleCode().length())
.filter(v -> Objects.equals(v.substring(0, o.getAgencySimpleCode().length()), o.getAgencySimpleCode()))//过滤掉截取后不相同的数据
.collect(Collectors.toSet());
// 累加相同属性的值
Map<String, Object> mapSum = set.stream()
.flatMap(str -> mm.get(str).entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(v1, v2) -> (Long) v1 + (Long) v2
));
if (mapSum.isEmpty()) {
map.forEach(k -> resMap.put(k, 0L));
resMap.put("total", 0L);
} else {
resMap.putAll(mapSum);
}
return resMap;
}).collect(Collectors.toList());
}
private Set<String> categories() {
DeliveryQuery deliveryQuery = new DeliveryQuery();
deliveryQuery.setIsDeleted(false);
deliveryQuery.setEnabled(true);
return deliveryMethodService.getDeliveryMethodList(deliveryQuery).stream().map(DeliveryMethod::getDeliveryMethodId).collect(Collectors.toSet());
}
/**
* 下发机器人语音叫应
* 请求示例:
* {
* "JYBT": "日常叫应",
* "JYNR": "请立即应答",
* "YPDZ": "https://ip:port/static/audio_1.mp3",
* "TZRY": [ ... ],
* "TZDC": { ... },
* "JZSJ": { ... },
* "LYZH": "test001"
* }
*
* @param deliveryIds 送达记录ID列表
* @param user 当前用户
*/
@Transactional
public void publishSmsCall(List<String> deliveryIds, RemoteUserInfo user) {
String auth = deliveryUsername + ":" + deliveryPassword;
String authHeader = "Basic " + Base64.getEncoder().encodeToString(auth.getBytes(US_ASCII));
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", authHeader);
headers.setContentType(MediaType.APPLICATION_JSON);
List<DeliveryRecord> list = findDeliveryRecordByIds(deliveryIds);
if (list.isEmpty())
throw new BusinessError("送达记录不存在");
list.stream().collect(Collectors.groupingBy(DeliveryRecord::getRecipientId)).forEach((recipientId, l) -> {
l.stream().collect(Collectors.groupingBy(DeliveryRecord::getCaseId)).forEach((caseid, ll) -> {
PublishSmsCallRequest smsRequest = new PublishSmsCallRequest();
Case caseInfo = ll.stream().map(DeliveryRecord::getCaseInfo).findFirst().get();
String jybt = "" + caseInfo.getCaseName() + "";
String jynr = "" + caseInfo.getCaseName() + "》的文书已通过短信送达,具体内容请查看附件.";
smsRequest.setJybt(jybt);
smsRequest.setJynr(jynr);
List<Attachment> attachments = new ArrayList<>();
ll.stream().map(DeliveryRecord::getDocumentInfo).filter(documentInfo -> StringUtils.hasText(documentInfo.getFilePath()))
.forEach(documentInfo -> {
Attachment attachment = new Attachment();
attachment.setWjbh(documentInfo.getDocumentId());
String url = ftpService.getFileUrl(documentInfo.getFilePath(), DateUtils.addDays(new Date(), 2));
attachment.setWjdz(url);
attachment.setWjlx("4");
attachments.add(attachment);
});
if (!attachments.isEmpty()) smsRequest.setFjzl(attachments);
RecipientInfo recipientInfo = l.stream().map(DeliveryRecord::getRecipientInfo).findFirst().get();
NotifyPerson notifyPerson = new NotifyPerson();
notifyPerson.setRybh(recipientInfo.getRecipientId());
notifyPerson.setRyxm(recipientInfo.getName());
notifyPerson.setYddh(recipientInfo.getPhone());
smsRequest.setTzry(List.of(notifyPerson));
NotifySupervision tzdc = new NotifySupervision();//通知督促
tzdc.setSjjg(60);
tzdc.setDccs(1);
smsRequest.setTzdc(tzdc);
smsRequest.setDcfs("1");
Deadline deadline = new Deadline();//截止时间
deadline.setSjjg(24);
deadline.setSjdw("3");
smsRequest.setJzsj(deadline);
smsRequest.setLyzh(deliveryUsername);
HttpEntity<PublishSmsCallRequest> requestEntity = new HttpEntity<>(smsRequest, headers);
String smsUrl = publishSmsCallUrl;
try {
log.info("Publishing SMS to URL: {} with request: {}", smsUrl, objectMapper.writeValueAsString(smsRequest));
ResponseEntity<String> response = restTemplate.exchange(smsUrl, HttpMethod.POST, requestEntity, String.class);
log.info("status{}", response.getStatusCode());
if (response.getStatusCode() == HttpStatus.OK) {
String body = response.getBody();
// String body = "{\"code\":0,\"msg\":\"请求成功\",\"data\":{\"JYBH\":\"460779fbe1d74eea90c9bac61dfe4b63\"}}";
log.info("SMS publish response: {}", body);
PublishVoiceCallResponse callResponse = objectMapper.readValue(body, PublishVoiceCallResponse.class);
if (0 == callResponse.getCode()) {
ll.forEach(deliveryRecord -> {
String jybh = callResponse.getData().getJybh();
saveDeliveryRecord(deliveryRecord, user, "send");
SmsSendRecord smsSendRecord = new SmsSendRecord();
smsSendRecord.setJybt(jybt);
smsSendRecord.setJynr(jynr);
smsSendRecord.setJybh(jybh);
smsSendRecord.setYddh(recipientInfo.getPhone());
smsSendRecord.setDeliveryId(deliveryRecord.getDeliveryId());
smsSendRecord.setOperator(user.getXm());
smsSendRecord.setOperatorAccount(user.getYhwybs());
smsSendRecord.setSendTime(LocalDateTime.now());
// 检验加密签名保存
Optional.ofNullable(smsSendRecord.getYddh()).filter(StringUtils::hasText).ifPresent(s -> smsSendRecord.setSginData(KmsServer.kmsSign(s)));
smsSendRecordRepo.save(smsSendRecord);
});
}
}
log.info("SMS publish completed");
} catch (Exception e) {
log.error("Error publishing SMS: {}", e.getMessage(), e);
}
});
});
}
/**
* 下发机器人语音叫应
* 请求示例:
* {
* "JYBT": "日常叫应",
* "JYNR": "请立即应答",
* "YPDZ": "https://ip:port/static/audio_1.mp3",
* "TZRY": [ ... ],
* "TZDC": { ... },
* "JZSJ": { ... },
* "LYZH": "test001"
* }
*
* @param deliveryRecord 送达记录
* @param user 当前用户
* 外部接口返回的调用结果
*/
public void publishVoiceCall(DeliveryRecord deliveryRecord, RemoteUserInfo user) {
PublishVoiceCallRequest voiceRequest = new PublishVoiceCallRequest();
String auth = deliveryUsername + ":" + deliveryPassword;
String authHeader = "Basic " + Base64.getEncoder().encodeToString(auth.getBytes(US_ASCII));
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", authHeader);
headers.setContentType(MediaType.APPLICATION_JSON);
String jybt = deliveryRecord.getCaseInfo().getCaseName();
RecipientInfo recipientInfo = deliveryRecord.getRecipientInfo();
String jynr = jybt + "的文书已通过短信送达,请尽快处理";
voiceRequest.setJybt(jybt);
voiceRequest.setJynr(jynr);
NotifyPerson notifyPerson = new NotifyPerson();
notifyPerson.setRybh(recipientInfo.getRecipientId());
notifyPerson.setRyxm(recipientInfo.getName());
notifyPerson.setYddh(recipientInfo.getPhone());
voiceRequest.setTzry(List.of(notifyPerson));
voiceRequest.setLyzh(deliveryUsername);
HttpEntity<PublishVoiceCallRequest> requestEntity = new HttpEntity<>(voiceRequest, headers);
try {
log.info("Publishing Voice call to URL: {} with request: {}", publishVoiceCallUrl, objectMapper.writeValueAsString(voiceRequest));
ResponseEntity<String> response = restTemplate.exchange(publishVoiceCallUrl, HttpMethod.POST, requestEntity, String.class);
log.info("Voice call publish response: {}", response.getBody());
if (response.getStatusCode() == HttpStatus.OK) {
String body = response.getBody();
log.info("Voice call response: {}", body);
PublishVoiceCallResponse callResponse = objectMapper.readValue(body, PublishVoiceCallResponse.class);
if (0 == callResponse.getCode()) {
String jybh = callResponse.getData().getJybh();
SmsSendRecord smsSendRecord = new SmsSendRecord();
smsSendRecord.setJybt(jybt);
smsSendRecord.setJynr(jynr);
smsSendRecord.setJybh(jybh);
smsSendRecord.setYddh(recipientInfo.getPhone());
smsSendRecord.setDeliveryId(deliveryRecord.getDeliveryId());
smsSendRecord.setOperator(user.getXm());
smsSendRecord.setOperatorAccount(user.getYhwybs());
smsSendRecord.setSendTime(LocalDateTime.now());
// 检验加密签名保存
Optional.ofNullable(smsSendRecord.getYddh()).filter(StringUtils::hasText).ifPresent(s -> smsSendRecord.setSginData(KmsServer.kmsSign(s)));
smsSendRecordRepo.save(smsSendRecord);
}
}
} catch (Exception e) {
log.error("Error publishing voice call: {}", e.getMessage(), e);
}
}
/**
* 短信状态回调
*
* @param smsSendRecord 短信发送记录
*/
@Transactional
public void smsStatusCallback(SmsSendRecord smsSendRecord) {
smsSendRecordRepo.findByJybh(smsSendRecord.getJybh()).forEach(o -> {
BeanUtils.copyNoNullProperties(smsSendRecord, o);
// 检验加密签名保存
Optional.ofNullable(smsSendRecord.getYddh()).filter(StringUtils::hasText).ifPresent(s -> o.setSginData(KmsServer.kmsSign(s)));
smsSendRecordRepo.save(o);
if ("0401".equals(smsSendRecord.getTzztCode())) {
deliveryRecordRepo.findById(o.getDeliveryId()).ifPresent(deliveryRecord -> {
deliveryRecord.setDeliveryStatus((short) 2);
LocalDateTime now = LocalDateTime.now();
deliveryRecord.setUpdateTime(now);
deliveryRecord.setFinishTime(now);
deliveryRecordRepo.save(deliveryRecord);
});
}
});
}
// DTO用于下发短信叫应
@Data
public static class PublishSmsCallRequest {
/**
* 叫应标题, 必填, String
*/
@JsonProperty("JYBT")
private String jybt;
/**
* 叫应内容, 必填, String
*/
@JsonProperty("JYNR")
private String jynr;
/**
* 应答方式, 可选, 默认为0 (0按键应答, 1视频应答)
*/
@JsonProperty("YDFS")
private String ydfs;
/**
* 叫应地点, 可选, Object 类型
*/
@JsonProperty("JYDD")
private Location jydd;
/**
* 附件资料, 可选, List<Attachment>
*/
@JsonProperty("FJZL")
private List<Attachment> fjzl;
/**
* 通知人员, 必填, List<NotifyPerson>
*/
@JsonProperty("TZRY")
private List<NotifyPerson> tzry;
/**
* 通知督促, 可选, Object 类型
*/
@JsonProperty("TZDC")
private NotifySupervision tzdc;
/**
* 督促方式, 可选, 默认00普通1语音2视频
*/
@JsonProperty("DCFS")
private String dcfs;
/**
* 截止时间, 可选, Object 类型
*/
@JsonProperty("JZSJ")
private Deadline jzsj;
/**
* 超时提醒, 可选, List<TimeoutRemind>
*/
@JsonProperty("CSTX")
private List<TimeoutRemind> cstx;
/**
* 来源帐号, 必填, String
*/
@JsonProperty("LYZH")
private String lyzh;
}
// DTO用于下发机器人语音叫应
@Data
public static class PublishVoiceCallRequest {
/**
* 叫应标题, 必填, String
*/
@JsonProperty("JYBT")
private String jybt;
/**
* 叫应内容, 可选, String
*/
@JsonProperty("JYNR")
private String jynr;
/**
* 音频地址, 可选, String (url 地址)
*/
@JsonProperty("YPDZ")
private String ypdz;
/**
* 通知人员, 必填, List<NotifyPerson>
*/
@JsonProperty("TZRY")
private List<NotifyPerson> tzry;
/**
* 通知督促, 可选, Object 类型
*/
@JsonProperty("TZDC")
private NotifySupervision tzdc;
/**
* 截止时间, 可选, Object 类型
*/
@JsonProperty("JZSJ")
private Deadline jzsj;
/**
* 来源帐号, 必填, String
*/
@JsonProperty("LYZH")
private String lyzh;
}
@Data
public static class PublishVoiceCallResponse {
/**
* 状态码 0 表示成功
*/
private Integer code;
private String msg;
private PublishVoiceCallData data;
}
@Data
public static class PublishVoiceCallData {
@JsonProperty("JYBH")
private String jybh;
}
// 内部实体类定义
@Data
public static class Location {
/**
* 地点名称, 可选, String
*/
@JsonProperty("WZMC")
private String wzmc;
/**
* 经度, 可选, Double
*/
@JsonProperty("WZJD")
private Double wzjd;
/**
* 纬度, 可选, Double
*/
@JsonProperty("WZWD")
private Double wzwd;
}
@Data
public static class Attachment {
/**
* 文件编号, 必填, String
*/
@JsonProperty("WJBH")
private String wjbh;
/**
* 文件地址 (url 链接), 必填, String
*/
@JsonProperty("WJDZ")
private String wjdz;
/**
* 缩略图地址, 可选, String (url 链接)
*/
@JsonProperty("SLTP")
private String sltp;
/**
* 文件类型, 必填, String (1视频2音频3图片4文档9其他)
*/
@JsonProperty("WJLX")
private String wjlx;
}
@Data
public static class NotifyPerson {
/**
* 人员编号, 可选, String
*/
@JsonProperty("RYBH")
private String rybh;
/**
* 人员姓名, 可选, String
*/
@JsonProperty("RYXM")
private String ryxm;
/**
* 移动电话, 必填, String
*/
@JsonProperty("YDDH")
private String yddh;
// 可根据需要增加人员工号(RYGH)固定电话(GDDH)所属单位(SSDW)所属辖区(SSXQ)
}
@Data
public static class NotifySupervision {
/**
* 通知督促的数值, 可选, Integer
*/
@JsonProperty("SJJG")
private Integer sjjg;
/**
* 督促次数, 可选, Integer
*/
@JsonProperty("DCCS")
private Integer dccs;
}
@Data
public static class Deadline {
/**
* 截止时间数值, 可选, Integer
*/
@JsonProperty("SJJG")
private Integer sjjg;
/**
* 截止时间单位, 时间单位12分钟3小时 可选, String
*/
@JsonProperty("SJDW")
private String sjdw;
}
@Data
public static class TimeoutRemind {
/**
* 截止时间数值, 可选, Integer
*/
@JsonProperty("SJJG")
private Integer sjjg;
/**
* 截止时间单位, 时间单位12分钟3小时 可选, String
*/
@JsonProperty("SJDW")
private String sjdw;
}
/**
* 添加场景图片
*
* @param scenePicDto 图片信息
*/
@Transactional
public void addScenePic(DeliveryScenePicDto scenePicDto) {
DeliveryRecord deliveryRecord = deliveryRecordRepo.findById(scenePicDto.getDeliveryRecordId()).orElseThrow(() -> new BusinessError("送达记录不存在"));
Materials materials = new Materials();
materials.setLrsj(LocalDateTime.now());
MultipartFile pic = scenePicDto.getPic();
String name = pic.getName();
materials.setFileType(StringUtils.hasText(name) ? name.split("\\.")[1] : null);
materials.setName(name);
try {
String url = ftpService.uploadTempFile(IndustryCategoryForFile.pub, name, pic.getInputStream());
if (!StringUtils.hasText(url)) {
throw new BusinessError("文书送达现场图片接口返回失败:上传时异常");
}
materials.setSavePath(url);
} catch (Exception e) {
throw new BusinessError("文书送达现场图片接口上传失败:" + e);
}
materials.setIsOriginal("3");
materials.setMaterialsTypeCode("3");
materials.setMaterialsTypeName("文书送达现场图片");
materials.setLinkId(deliveryRecord.getDeliveryId());
materialsRepo.save(materials);
}
}

View File

@ -0,0 +1,132 @@
package com.aisino.iles.lawenforcement.service;
import cn.hutool.http.HttpUtil;
import com.aisino.iles.lawenforcement.model.dto.DictItemDto;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.smartlx.sso.client.model.RemoteUserInfo;
import com.smartlx.sso.client.properties.SsoClientProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Service
public class DictInterfaceService {
private final String dictUrl;
private final ObjectMapper objectMapper;
private final SsoClientProperties ssoClientProperties;
private List<DictItemDto> dictItemDtos = new ArrayList<>();
public DictInterfaceService(@Value("${third-party.dict:}") String dictUrl,
ObjectMapper objectMapper,
SsoClientProperties ssoClientProperties) {
this.dictUrl = dictUrl;
this.objectMapper = objectMapper;
this.ssoClientProperties = ssoClientProperties;
}
public void getDictItemByUser(String dictCode, RemoteUserInfo user) {
String accessToken = user.getAccessToken().getAccess_token();
String tokenType = user.getAccessToken().getToken_type();
getDictItem(dictCode, tokenType, accessToken);
}
public void getDictItem(String dictCode, String tokenType, String accessToken) {
String body = HttpUtil.createPost(dictUrl + dictCode)
.header("Content-Type", "application/json")
.header("Authorization", tokenType + " " + accessToken)
.header("appId", ssoClientProperties.getClientId())
.execute()
.body();
log.info("字典查询结果--------------");
try {
JsonNode jsonNode = objectMapper.readValue(body, JsonNode.class);
if (jsonNode.has("code") && 200 == jsonNode.get("code").asInt() && jsonNode.has("data")) {
JsonNode dataNode = objectMapper.readValue(jsonNode.get("data").toString(), JsonNode.class);
dictItemDtos = objectMapper.readValue(
dataNode.traverse(),
objectMapper.getTypeFactory().constructCollectionType(List.class, DictItemDto.class)
);
}
} catch (Exception e) {
log.error("解析字典结果失败", e);
}
}
public String getFjdm(String xxdm) {
if (!StringUtils.hasText(xxdm))
return null;
DictItemDto item = findItemByXxdm(dictItemDtos, xxdm);
return item != null ? item.getFjdm() : null;
}
/**
* 根据xxdm递归查找DictItemDto对象
*
* @param dtos 字典项列表
* @param xxdm 需要查找的代码
* @return 找到的DictItemDto对象未找到返回null
*/
private DictItemDto findItemByXxdm(List<DictItemDto> dtos, String xxdm) {
if (dtos == null || dtos.isEmpty() || !StringUtils.hasText(xxdm))
return null;
for (DictItemDto dto : dtos) {
// 如果当前节点匹配
if (xxdm.equals(dto.getXxdm()))
return dto;
// 递归查找子节点
if (dto.getChildren() != null && !dto.getChildren().isEmpty()) {
DictItemDto found = findItemByXxdm(dto.getChildren(), xxdm);
if (found != null) {
return found;
}
}
}
return null;
}
public String getTopXxdm(String xxdm) {
// 构建从当前节点到顶级节点的路径
StringBuilder path = new StringBuilder();
buildPath(dictItemDtos, xxdm, path);
return path.toString();
}
private boolean buildPath(List<DictItemDto> dtos, String xxdm, StringBuilder path) {
for (DictItemDto dto : dtos) {
if (dto.getXxdm().equals(xxdm)) {
// 找到当前节点
if (dto.getChildren() == null || dto.getChildren().isEmpty()) {
// 如果是叶子节点则开始构建路径
path.insert(0, dto.getXxdm());
return true;
} else {
// 如果不是叶子节点继续向下查找子节点
for (DictItemDto child : dto.getChildren()) {
if (buildPath(List.of(child), xxdm, path)) {
// 在子树中找到目标节点拼接当前节点的xxdm
path.insert(0, dto.getXxdm() + "/");
return true;
}
}
}
} else {
// 当前节点不匹配检查其子节点
if (dto.getChildren() != null && !dto.getChildren().isEmpty()) {
if (buildPath(dto.getChildren(), xxdm, path)) {
// 子节点中找到了目标节点拼接当前节点的xxdm
path.insert(0, dto.getXxdm() + "/");
return true;
}
}
}
}
return false; // 没有找到目标节点
}
}

View File

@ -0,0 +1,177 @@
package com.aisino.iles.lawenforcement.service;
import com.aisino.iles.common.model.enums.IndustryCategoryForFile;
import com.aisino.iles.common.service.FtpService;
import com.aisino.iles.common.util.PageableHelper;
import com.aisino.iles.core.exception.BusinessError;
import com.aisino.iles.lawenforcement.event.DataChangeAction;
import com.aisino.iles.lawenforcement.event.aop.PublishDataChange;
import com.aisino.iles.lawenforcement.model.Case;
import com.aisino.iles.lawenforcement.model.Case_;
import com.aisino.iles.lawenforcement.model.Document;
import com.aisino.iles.lawenforcement.model.Document_;
import com.aisino.iles.lawenforcement.model.dto.FormDataDto;
import com.aisino.iles.lawenforcement.model.query.DocumentQuery;
import com.aisino.iles.lawenforcement.repository.DocumentRepository;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Predicate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Service
@Slf4j
public class DocumentService {
private final DocumentRepository documentRepository;
private final ObjectMapper objectMapper;
private final FtpService ftpService;
public DocumentService(DocumentRepository documentRepository,
ObjectMapper objectMapper,
FtpService ftpService) {
this.objectMapper = objectMapper;
this.ftpService = ftpService;
this.documentRepository = documentRepository;
}
/**
* 文书信息
*
* @param query@return 分页查询文书信息
*/
@Transactional(readOnly = true)
public Page<Document> findDocumentsPage(DocumentQuery query) {
return documentRepository.findAll(buildSpec(query), PageableHelper.buildPageRequest(query.page(), query.pageSize(), query.sort(), query.dir())).map(this::handleResult);
}
private Document handleResult(Document document) {
Optional.ofNullable(document.getCaseInfo()).ifPresent(caseInfo -> caseInfo.getCaseName());
Optional.ofNullable(document.getFilePath()).ifPresent(filePath -> document.setDownloadUrl(ftpService.getFileUrl(filePath)));
return document;
}
public List<Document> listDocuments(DocumentQuery query) {
Sort sort = Sort.by(Sort.Direction.ASC, "documentNo");
return documentRepository.findAll(buildSpec(query), sort, "").stream().map(document -> {
Optional.ofNullable(document.getFilePath()).ifPresent(filePath -> document.setDownloadUrl(ftpService.getFileUrl(filePath)));
return document;
}).toList();
}
private Specification<Document> buildSpec(DocumentQuery query) {
return Specification.where((root, q, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
Join<Document, Case> caseDocumentRoot = root.join("caseInfo", JoinType.LEFT);
if (StringUtils.hasText(query.getCaseNum()))
predicates.add(criteriaBuilder.equal(caseDocumentRoot.get(Case_.caseNum), query.getCaseNum()));
if (StringUtils.hasText(query.getCaseName()))
predicates.add(criteriaBuilder.like(caseDocumentRoot.get(Case_.caseName), "%"+query.getCaseName()+"%"));
Optional.ofNullable(query.getCaseId()).ifPresent(o -> predicates.add(criteriaBuilder.equal(root.get(Document_.caseInfo).get(Case_.caseId), o)));
Optional.ofNullable(query.getDocumentNo()).ifPresent(o -> predicates.add(criteriaBuilder.equal(root.get(Document_.documentNo), o)));
Optional.ofNullable(query.getDocumentCode()).ifPresent(o -> predicates.add(criteriaBuilder.equal(root.get(Document_.documentCode), o)));
Optional.ofNullable(query.getStatus()).filter("done"::equals).ifPresent(o -> {
predicates.add(criteriaBuilder.equal(root.get(Document_.status), Document.DocumentStatus.done));
});
Optional.ofNullable(query.getDocumentName()).ifPresent(o -> predicates.add(criteriaBuilder.like(root.get(Document_.documentName), "%" + o + "%")));
Optional.ofNullable(query.getDocumentDate()).filter(f -> f.length == 2).map(f -> {
List<Predicate> timePredicates = new ArrayList<>();
Optional.ofNullable(f[0]).map(from -> criteriaBuilder.greaterThanOrEqualTo(root.get(Document_.documentDate), from)).ifPresent(timePredicates::add);
Optional.ofNullable(f[1]).map(to -> criteriaBuilder.lessThanOrEqualTo(root.get(Document_.documentDate), to)).ifPresent(timePredicates::add);
return timePredicates;
}).ifPresent(predicates::addAll);
Optional.ofNullable(query.getConditionlike()).filter(StringUtils::hasText).ifPresent(o -> {
List<Predicate> orPredicates = new ArrayList<>();
Predicate p1 = criteriaBuilder.like(root.get(Document_.documentCode), "%" + o + "%");
orPredicates.add(criteriaBuilder.or(p1));
Predicate p2 = criteriaBuilder.like(root.get(Document_.documentName), "%" + o + "%");
orPredicates.add(criteriaBuilder.or(p2));
Predicate p = criteriaBuilder.or(orPredicates.toArray(new Predicate[0]));
predicates.add(p);
});
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
});
}
/**
* 保存文书
*
* @param document 文书信息
* @return 保存后的文书信息
*/
@PublishDataChange(action = DataChangeAction.SAVE)
@Transactional
public Document saveDocument(Document document) {
document.setDocumentDate(LocalDateTime.now());
return documentRepository.save(document);
}
/**
* 根据ID删除文书信息
*
* @param documentId 文书ID
*/
@Transactional
public void deleteDocumentById(String documentId) {
documentRepository.deleteById(documentId);
}
/**
* 批量删除文书信息
*
* @param documentIds 文书ID列表
*/
@Transactional
public void deleteDocumentsByIds(List<String> documentIds) {
documentRepository.deleteAllById(documentIds);
}
@PublishDataChange(action = DataChangeAction.SAVE)
@Transactional
public Document addDocumentFile(FormDataDto formData) {
MultipartFile file = formData.getFile1();
Document document = new Document();
try {
document = objectMapper.readValue(formData.getFormDataJson(), Document.class);
if (null != file) {
String url;
try {
url = ftpService.uploadTempFile(IndustryCategoryForFile.pub, file.getOriginalFilename(), file.getInputStream());
if (!StringUtils.hasText(url)) {
throw new BusinessError(document.getDocumentName() + "返回失败:上传时异常");
}
document.setFilePath(url);
document.setStatus(Document.DocumentStatus.done);
document.setDocumentDate(LocalDateTime.now());
documentRepository.save(document);
document.setDownloadUrl(ftpService.getFileUrl(url));
} catch (Exception e) {
throw new BusinessError(document.getDocumentName()+ "上传失败:" + e);
}
}
} catch (JsonProcessingException e) {
log.error(e.getMessage(), e);
}
return document;
}
@Transactional(readOnly = true)
public Optional<Document> findByCaseIdAndDocumentName(String caseId,String documentName) {
return documentRepository.findByCaseIdAndDocumentName( caseId, documentName);
}
}