This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
package jnpf.certificate.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "config.food-safety-ocr")
|
||||
public class FoodSafetyOcrConfig {
|
||||
private Set<String> titleNames = Set.of("标题");
|
||||
private Set<String> titleValues = Set.of("食品经营许可证");
|
||||
private Set<String> issueDateNames = Set.of("日期");
|
||||
private Set<String> expireDateNames = Set.of("有效期至");
|
||||
private Set<String> businessItemsNames = Set.of("经营项目");
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
package jnpf.certificate.consumer;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import io.seata.spring.annotation.GlobalTransactional;
|
||||
import jnpf.base.ActionResult;
|
||||
import jnpf.certificate.service.CertificateManageApiService;
|
||||
import jnpf.config.ConfigValueUtil;
|
||||
import jnpf.database.util.TenantDataSourceUtil;
|
||||
import jnpf.message.enums.permission.v2.OperationTypeMessageEnums;
|
||||
import jnpf.message.enums.permission.v2.OrganizeCategoryMessageEnums;
|
||||
import jnpf.message.model.permission.v2.OrganizeUpdateMessageDTO;
|
||||
import jnpf.model.certificate.vo.CertificateOrganizeBusinessLicenseVO;
|
||||
import jnpf.permission.V2OrganizeApi;
|
||||
import jnpf.permission.eum.v2.OrganizeCategoryEnums;
|
||||
import jnpf.permission.vo.v2.organzie.OrganizeGeneralDetailVO;
|
||||
import jnpf.util.DateUtil;
|
||||
import jnpf.util.ServiceException;
|
||||
import jnpf.util.StringUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.annotation.StreamListener;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.integration.IntegrationMessageHeaderAccessor;
|
||||
import org.springframework.integration.acks.AcknowledgmentCallback;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 证照关联处理组织架构消费者。
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@EnableBinding(CertificateConsumerSource.class)
|
||||
public class CertificateConsumer {
|
||||
|
||||
private static final String IDEMPOTENT_KEY_PREFIX = "certificate:consume";
|
||||
private static final String IDEMPOTENT_VALUE = "1";
|
||||
private static final long IDEMPOTENT_EXPIRE_DAYS = 1L;
|
||||
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
@Resource
|
||||
@Lazy
|
||||
private ConfigValueUtil configValueUtil;
|
||||
|
||||
@Resource
|
||||
private CertificateManageApiService certificateManageApiService;
|
||||
|
||||
/**
|
||||
* 监听组织架构变更消息并同步证照数据。
|
||||
*/
|
||||
@StreamListener(target = CertificateConsumerSource.INPUT, condition = "headers['ROCKET_TAGS'] == 'TAG_ORGANIZE'")
|
||||
public void receiveStoreFranchiseeChange(Message<String> message) {
|
||||
AcknowledgmentCallback acknowledgmentCallback = null;
|
||||
String idempotentKey = null;
|
||||
try {
|
||||
MessageHeaders headers = message.getHeaders();
|
||||
acknowledgmentCallback = headers.get(IntegrationMessageHeaderAccessor.ACKNOWLEDGMENT_CALLBACK, AcknowledgmentCallback.class);
|
||||
if (acknowledgmentCallback != null) {
|
||||
acknowledgmentCallback.noAutoAck();
|
||||
}
|
||||
|
||||
String payload = message.getPayload();
|
||||
if (StrUtil.isBlank(payload)) {
|
||||
acknowledgeAccept(acknowledgmentCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
String rocketKeys = headers.get("ROCKET_KEYS", String.class);
|
||||
if (StrUtil.isBlank(rocketKeys)) {
|
||||
log.warn("证照组织变更消息缺少ROCKET_KEYS,跳过处理。payload={}", payload);
|
||||
acknowledgeAccept(acknowledgmentCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
idempotentKey = IDEMPOTENT_KEY_PREFIX + ":" + rocketKeys;
|
||||
Boolean firstConsume = stringRedisTemplate.opsForValue()
|
||||
.setIfAbsent(idempotentKey, IDEMPOTENT_VALUE, IDEMPOTENT_EXPIRE_DAYS, TimeUnit.DAYS);
|
||||
if (!Boolean.TRUE.equals(firstConsume)) {
|
||||
acknowledgeAccept(acknowledgmentCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
List<OrganizeUpdateMessageDTO> messageList;
|
||||
try {
|
||||
messageList = JSONUtil.toList(JSONUtil.parseArray(payload), OrganizeUpdateMessageDTO.class);
|
||||
} catch (Exception e) {
|
||||
log.error("证照组织变更消息解析失败,payload={}", payload, e);
|
||||
throw e;
|
||||
}
|
||||
if (CollUtil.isEmpty(messageList)) {
|
||||
acknowledgeAccept(acknowledgmentCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
for (OrganizeUpdateMessageDTO item : messageList) {
|
||||
processOrganizeChange(item);
|
||||
}
|
||||
acknowledgeAccept(acknowledgmentCallback);
|
||||
} catch (Exception e) {
|
||||
//todo 异常处理
|
||||
String msg = e.getMessage();
|
||||
if(msg.equals("组织不存在,无法同步营业执照") || msg.equals("切换租户失败")){
|
||||
acknowledgeRequeue(acknowledgmentCallback);
|
||||
}
|
||||
if (StrUtil.isNotBlank(idempotentKey)) {
|
||||
stringRedisTemplate.delete(idempotentKey);
|
||||
}
|
||||
log.error("处理证照组织变更消息失败,message={}", JSONUtil.toJsonStr(message), e);
|
||||
throw e;
|
||||
} finally {
|
||||
// TenantDataSourceUtil.clearSwitchDataSource();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分发处理组织变更。
|
||||
*/
|
||||
private void processOrganizeChange(OrganizeUpdateMessageDTO item) {
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
OperationTypeMessageEnums operationType = item.getOperationTypeEnum();
|
||||
OrganizeCategoryMessageEnums category = item.getOrganizeCategoryEnum();
|
||||
if (operationType == null || category == null) {
|
||||
return;
|
||||
}
|
||||
if (!OrganizeCategoryMessageEnums.STORE.equals(category)
|
||||
&& !OrganizeCategoryMessageEnums.COMPANY.equals(category)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String tenantId = StrUtil.trim(item.getTenantId());
|
||||
if (StrUtil.isBlank(tenantId)) {
|
||||
return;
|
||||
}
|
||||
switchTenant(tenantId);
|
||||
|
||||
if (OperationTypeMessageEnums.ADD.equals(operationType)) {
|
||||
handleAdd(item);
|
||||
return;
|
||||
}
|
||||
if (OperationTypeMessageEnums.UPDATE.equals(operationType)) {
|
||||
handleUpdate(item);
|
||||
return;
|
||||
}
|
||||
if (OperationTypeMessageEnums.DELETE.equals(operationType)) {
|
||||
handleDelete(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理新增。
|
||||
* 门店:新增营业执照+食品经营许可证两条缺失证照。
|
||||
* 公司:同步新增营业执照。
|
||||
*/
|
||||
@GlobalTransactional
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
protected void handleAdd(OrganizeUpdateMessageDTO item) {
|
||||
if (OrganizeCategoryMessageEnums.STORE.equals(item.getOrganizeCategoryEnum())) {
|
||||
handleStoreAdd(item.getId());
|
||||
return;
|
||||
}
|
||||
if (OrganizeCategoryMessageEnums.COMPANY.equals(item.getOrganizeCategoryEnum())) {
|
||||
CertificateOrganizeBusinessLicenseVO req = buildCompanyLicense(item.getId(), item.getJsonEntity());
|
||||
certificateManageApiService.saveBusinessLicense(req);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理编辑。
|
||||
* 门店:无需处理。
|
||||
* 公司:同步更新营业执照。
|
||||
*/
|
||||
@GlobalTransactional
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
protected void handleUpdate(OrganizeUpdateMessageDTO item) {
|
||||
if (OrganizeCategoryMessageEnums.STORE.equals(item.getOrganizeCategoryEnum())) {
|
||||
return;
|
||||
}
|
||||
if (OrganizeCategoryMessageEnums.COMPANY.equals(item.getOrganizeCategoryEnum())) {
|
||||
CertificateOrganizeBusinessLicenseVO req = buildCompanyLicense(item.getId(), item.getJsonEntity());
|
||||
certificateManageApiService.saveBusinessLicense(req);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理删除。
|
||||
* 按组织ID删除该组织主体下全部证照。
|
||||
*/
|
||||
@GlobalTransactional
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
protected void handleDelete(OrganizeUpdateMessageDTO item) {
|
||||
String organizeId = StrUtil.trim(item.getId());
|
||||
if (StrUtil.isBlank(organizeId)) {
|
||||
return;
|
||||
}
|
||||
certificateManageApiService.deleteBusinessLicense(organizeId, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 门店新增时补齐默认缺失证照。
|
||||
*/
|
||||
private void handleStoreAdd(String storeId) {
|
||||
String targetStoreId = StrUtil.trim(storeId);
|
||||
if (StrUtil.isBlank(targetStoreId)) {
|
||||
return;
|
||||
}
|
||||
certificateManageApiService.initStoreDefaultCertificates(targetStoreId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建公司营业执照同步参数,逻辑与 V2OrganizeServiceImpl 的公司新增/编辑保持一致。
|
||||
*/
|
||||
private CertificateOrganizeBusinessLicenseVO buildCompanyLicense(String organizeId, String jsonEntity) {
|
||||
String targetOrganizeId = StrUtil.trim(organizeId);
|
||||
ServiceException.isTrue(StrUtil.isNotBlank(targetOrganizeId), "组织ID不能为空");
|
||||
|
||||
CertificateOrganizeBusinessLicenseVO licenseVO = new CertificateOrganizeBusinessLicenseVO();
|
||||
licenseVO.setOrganizeId(targetOrganizeId);
|
||||
|
||||
JSONObject jsonObject = parseJSONEntity(jsonEntity);
|
||||
|
||||
licenseVO.setCertificateImage(jsonObject.getStr("licenseImg"));
|
||||
licenseVO.setCompanyName(jsonObject.getStr("fullName"));
|
||||
|
||||
JSONObject propertyJson = parsePropertyJson(jsonObject);
|
||||
|
||||
//前端如果没值传的是""空串,直接去获取JsonArry会报错,这里判断一下
|
||||
String businessTermText = propertyJson.getStr("businessTerm");
|
||||
if(StringUtil.isNotBlank(businessTermText)){
|
||||
licenseVO.setBusinessTerm(businessTermText);
|
||||
|
||||
JSONArray businessTerm = propertyJson.getJSONArray("businessTerm");
|
||||
if(businessTerm.size() == 2){
|
||||
licenseVO.setIssueDate(businessTerm.getStr(0));
|
||||
licenseVO.setExpireDate(businessTerm.getStr(1));
|
||||
}
|
||||
}
|
||||
|
||||
String managerName = propertyJson.getStr("managerName");
|
||||
if (managerName != null) {
|
||||
licenseVO.setLegalRepresentative(managerName);
|
||||
}
|
||||
String address = propertyJson.getStr("address");
|
||||
if (address!= null) {
|
||||
licenseVO.setCompanyAddress(address);
|
||||
}
|
||||
Integer businessTermLong = propertyJson.getInt("businessTermLong");
|
||||
if (businessTermLong!= null) {
|
||||
licenseVO.setIsLongTerm(businessTermLong);
|
||||
}
|
||||
|
||||
Date establishDate = propertyJson.getDate("foundedTime");
|
||||
if(Objects.nonNull(establishDate)){
|
||||
licenseVO.setEstablishDate(DateUtil.daFormat(establishDate));
|
||||
}
|
||||
return licenseVO;
|
||||
}
|
||||
|
||||
private JSONObject parseJSONEntity(String jsonEntity) {
|
||||
if (StrUtil.isBlank(jsonEntity) || !JSONUtil.isTypeJSONObject(jsonEntity)) {
|
||||
return new JSONObject();
|
||||
}
|
||||
return JSONUtil.parseObj(jsonEntity);
|
||||
}
|
||||
/**
|
||||
* 从组织消息 jsonEntity 中解析 propertyJson。
|
||||
*/
|
||||
private JSONObject parsePropertyJson(JSONObject jsonEntity) {
|
||||
if (Objects.isNull(jsonEntity)) {
|
||||
return new JSONObject();
|
||||
}
|
||||
JSONObject propertyObj = jsonEntity.getJSONObject("propertyJson");
|
||||
if(Objects.isNull(propertyObj)){
|
||||
return new JSONObject();
|
||||
}
|
||||
return propertyObj;
|
||||
}
|
||||
|
||||
private Integer toInteger(Object value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(String.valueOf(value));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换租户数据源。
|
||||
*/
|
||||
private void switchTenant(String tenantId) {
|
||||
if (!configValueUtil.isMultiTenancy()) {
|
||||
log.info("证照组织变更消息:配置为非多租户,跳过切换租户。tenantId={}", tenantId);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
TenantDataSourceUtil.switchTenant(tenantId);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("切换租户失败tenantId:"+tenantId, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动确认消费成功。
|
||||
*/
|
||||
private void acknowledgeAccept(AcknowledgmentCallback acknowledgmentCallback) {
|
||||
if (acknowledgmentCallback == null || acknowledgmentCallback.isAcknowledged()) {
|
||||
return;
|
||||
}
|
||||
acknowledgmentCallback.acknowledge(AcknowledgmentCallback.Status.ACCEPT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动回执重试。
|
||||
*/
|
||||
private void acknowledgeRequeue(AcknowledgmentCallback acknowledgmentCallback) {
|
||||
if (acknowledgmentCallback == null || acknowledgmentCallback.isAcknowledged()) {
|
||||
return;
|
||||
}
|
||||
acknowledgmentCallback.acknowledge(AcknowledgmentCallback.Status.REQUEUE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package jnpf.certificate.consumer;
|
||||
|
||||
import org.springframework.cloud.stream.annotation.Input;
|
||||
import org.springframework.messaging.SubscribableChannel;
|
||||
|
||||
/**
|
||||
* 证照消息通道定义
|
||||
*/
|
||||
public interface CertificateConsumerSource {
|
||||
/**
|
||||
* 消费通道
|
||||
*/
|
||||
String INPUT = "permission-certificate-input";
|
||||
|
||||
@Input(INPUT)
|
||||
SubscribableChannel input();
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package jnpf.certificate.controller;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import jnpf.base.ActionResult;
|
||||
import jnpf.base.vo.PageListVO;
|
||||
import jnpf.certificate.service.CertificateManageService;
|
||||
import jnpf.certificate.service.CertificateInstanceService;
|
||||
import jnpf.model.certificate.req.CertificateHealthManageQueryReq;
|
||||
import jnpf.model.certificate.req.CertificateInstanceQueryReq;
|
||||
import jnpf.model.certificate.req.CertificateStoreManageQueryReq;
|
||||
import jnpf.model.certificate.req.CertificateStoreDashboardReq;
|
||||
import jnpf.model.certificate.req.CertificateSyncHealthReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppBusinessLicenseUpdateReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppHealthCertificateUpdateReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppHygieneLicenseUpdateReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppStoreCustomUpdateReq;
|
||||
import jnpf.model.certificate.vo.CertificateHealthManageVO;
|
||||
import jnpf.model.certificate.vo.CertificateInstanceVO;
|
||||
import jnpf.model.certificate.vo.CertificateStoreManageVO;
|
||||
import jnpf.model.certificate.vo.CertificateStoreDashboardVO;
|
||||
import jnpf.model.certificate.vo.CertificateStoreCustomStatusTableVO;
|
||||
import jnpf.model.certificate.vo.CertificateTypeOptionVO;
|
||||
import jnpf.model.certificate.vo.app.HealthCertificateDetailVO;
|
||||
import jnpf.util.FtbUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* web证照实例控制器。
|
||||
*/
|
||||
@RestController
|
||||
@Validated
|
||||
@RequestMapping("/web/certificate-instance")
|
||||
public class CertificateInstanceController {
|
||||
|
||||
@Autowired
|
||||
private CertificateInstanceService certificateInstanceService;
|
||||
@Autowired
|
||||
private CertificateManageService certificateManageService;
|
||||
|
||||
/**
|
||||
* 更新健康证。
|
||||
*
|
||||
* @param req 更新参数
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PutMapping("/update-health")
|
||||
public ActionResult<Void> updateHealth(@Validated @RequestBody CertificateAppHealthCertificateUpdateReq req) {
|
||||
certificateManageService.updateHealthCertificate(req);
|
||||
return ActionResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新营业执照。
|
||||
*
|
||||
* @param req 更新参数
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PutMapping("/update-business-license")
|
||||
public ActionResult<Void> updateBusinessLicense(@Validated @RequestBody CertificateAppBusinessLicenseUpdateReq req) {
|
||||
certificateManageService.updateBusinessLicense(req);
|
||||
return ActionResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新食品经营许可证。
|
||||
*
|
||||
* @param req 更新参数
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PutMapping("/update-hygiene-license")
|
||||
public ActionResult<Void> updateHygieneLicense(@Validated @RequestBody CertificateAppHygieneLicenseUpdateReq req) {
|
||||
certificateManageService.updateHygieneLicense(req);
|
||||
return ActionResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新门店自定义证照。
|
||||
*
|
||||
* @param req 更新参数
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PutMapping("/update-store-custom")
|
||||
public ActionResult<Void> updateStoreCustom(@Valid @RequestBody CertificateAppStoreCustomUpdateReq req) {
|
||||
certificateManageService.updateStoreCustomCertificate(req);
|
||||
return ActionResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 按ID查询详情。
|
||||
*
|
||||
* @param id 实例ID
|
||||
* @return 详情
|
||||
*/
|
||||
@GetMapping("/query-info/{id}")
|
||||
public ActionResult<CertificateInstanceVO> queryInfo(@PathVariable("id") @NotBlank(message = "实例ID不能为空") String id) {
|
||||
return ActionResult.success(certificateInstanceService.queryInfo(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询列表。
|
||||
*
|
||||
* @param req 查询参数
|
||||
* @return 分页结果
|
||||
*/
|
||||
@GetMapping("/query-page")
|
||||
@Deprecated
|
||||
public ActionResult<PageListVO<CertificateInstanceVO>> queryPage(@Valid CertificateInstanceQueryReq req) {
|
||||
PageInfo<CertificateInstanceVO> pageInfo = certificateInstanceService.queryPage(req);
|
||||
return ActionResult.page(pageInfo.getList(), FtbUtil.getPagination(pageInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 健康证管理分页查询。
|
||||
*
|
||||
* @param req 查询参数
|
||||
* @return 分页结果
|
||||
*/
|
||||
@PostMapping("/query-health-page")
|
||||
public ActionResult<PageListVO<CertificateHealthManageVO>> queryHealthPage(@RequestBody @Valid CertificateHealthManageQueryReq req) {
|
||||
PageInfo<CertificateHealthManageVO> pageInfo = certificateInstanceService.queryHealthPage(req);
|
||||
return ActionResult.page(pageInfo.getList(), FtbUtil.getPagination(pageInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 门店证照分页查询。
|
||||
*
|
||||
* @param req 查询参数
|
||||
* @return 分页结果
|
||||
*/
|
||||
@PostMapping("/query-store-page")
|
||||
public ActionResult<PageListVO<CertificateStoreManageVO>> queryStorePage(@RequestBody @Valid CertificateStoreManageQueryReq req) {
|
||||
PageInfo<CertificateStoreManageVO> pageInfo = certificateInstanceService.queryStorePage(req);
|
||||
return ActionResult.page(pageInfo.getList(), FtbUtil.getPagination(pageInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 门店证照看板统计。
|
||||
*
|
||||
* @param req 查询参数
|
||||
* @return 看板统计结果
|
||||
*/
|
||||
@PostMapping("/store-dashboard")
|
||||
public ActionResult<CertificateStoreDashboardVO> storeDashboard(@Validated @RequestBody CertificateStoreDashboardReq req) {
|
||||
return ActionResult.success(certificateInstanceService.storeCertificateDashboard(req));
|
||||
}
|
||||
|
||||
/**
|
||||
* 门店证照看板表格分页查询。
|
||||
*
|
||||
* @param req 查询参数
|
||||
* @return 分页结果
|
||||
*/
|
||||
@PostMapping("/store-dashboard-table-page")
|
||||
public ActionResult<PageListVO<CertificateStoreCustomStatusTableVO>> storeDashboardTablePage(@Validated @RequestBody CertificateStoreDashboardReq req) {
|
||||
PageInfo<CertificateStoreCustomStatusTableVO> pageInfo = certificateInstanceService.storeCertificateDashboardTablePage(req);
|
||||
return ActionResult.page(pageInfo.getList(), FtbUtil.getPagination(pageInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询证照类型选项。
|
||||
*
|
||||
* @return 证照类型选项
|
||||
*/
|
||||
@GetMapping("/query-certificate-type-list")
|
||||
public ActionResult<List<CertificateTypeOptionVO>> queryCertificateTypeList() {
|
||||
return ActionResult.success(certificateInstanceService.queryCertificateTypeList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package jnpf.certificate.controller;
|
||||
|
||||
import jnpf.base.ActionResult;
|
||||
import jnpf.certificate.CertificateManageApi;
|
||||
import jnpf.certificate.service.CertificateManageApiService;
|
||||
import jnpf.model.certificate.vo.CertificateOrganizeBusinessLicenseVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 组织营业执照管理接口。
|
||||
*/
|
||||
@Validated
|
||||
@RestController
|
||||
@RequestMapping("/web/certificate-manage-api")
|
||||
public class CertificateManageApiController implements CertificateManageApi {
|
||||
|
||||
@Autowired
|
||||
private CertificateManageApiService certificateManageApiService;
|
||||
|
||||
@Override
|
||||
@GetMapping("/query-business-license")
|
||||
public ActionResult<CertificateOrganizeBusinessLicenseVO> queryBusinessLicense(@RequestParam("organizeId") String organizeId) {
|
||||
return ActionResult.success(certificateManageApiService.queryBusinessLicense(organizeId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostMapping("/query-business-license-batch")
|
||||
public ActionResult<List<CertificateOrganizeBusinessLicenseVO>> queryBusinessLicenseBatch(@RequestBody Collection<String> organizeIds) {
|
||||
return ActionResult.success(certificateManageApiService.queryBusinessLicenseBatch(organizeIds));
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostMapping("/save-business-license")
|
||||
public ActionResult<Void> saveBusinessLicense(@Valid @RequestBody CertificateOrganizeBusinessLicenseVO req) {
|
||||
certificateManageApiService.saveBusinessLicense(req);
|
||||
return ActionResult.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
@DeleteMapping("/delete-business-license")
|
||||
public ActionResult<Void> deleteBusinessLicense(@RequestParam("organizeId") String organizeId,
|
||||
@RequestParam(value = "loginUserId", required = false) String loginUserId) {
|
||||
certificateManageApiService.deleteBusinessLicense(organizeId, loginUserId);
|
||||
return ActionResult.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
package jnpf.certificate.controller;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import com.tencentcloudapi.common.Credential;
|
||||
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
|
||||
import com.tencentcloudapi.common.profile.ClientProfile;
|
||||
import com.tencentcloudapi.common.profile.HttpProfile;
|
||||
import com.tencentcloudapi.ocr.v20181119.OcrClient;
|
||||
import com.tencentcloudapi.ocr.v20181119.models.*;
|
||||
import jnpf.base.ActionResult;
|
||||
import jnpf.certificate.config.FoodSafetyOcrConfig;
|
||||
import jnpf.model.certificate.req.CertificateFoodSafetyOcrReq;
|
||||
import jnpf.model.certificate.vo.CertificateFoodSafetyOcrVO;
|
||||
import jnpf.permission.vo.LicenseVo;
|
||||
import jnpf.personnels.config.TengxunLicenseConfig;
|
||||
import jnpf.util.JsonUtil;
|
||||
import jnpf.util.RedisUtil;
|
||||
import jnpf.util.StringUtil;
|
||||
import jnpf.util.UserProvider;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 证照 OCR 控制器。
|
||||
*/
|
||||
@RestController
|
||||
@Validated
|
||||
@RequestMapping("/web/certificate-ocr")
|
||||
@Slf4j
|
||||
public class CertificateOcrController {
|
||||
|
||||
private static final String FOOD_SAFETY_OCR_CACHE_KEY_PREFIX = "ftb:certificate:ocr:food:safety:";
|
||||
private static final long FOOD_SAFETY_OCR_CACHE_SECONDS = 24 * 60 * 60L;
|
||||
|
||||
@Autowired
|
||||
private TengxunLicenseConfig licenseConfig;
|
||||
@Autowired
|
||||
private FoodSafetyOcrConfig foodSafetyOcrConfig;
|
||||
@Autowired
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
|
||||
/**
|
||||
* 食品安全许可证识别(空实现)。
|
||||
*
|
||||
* @param req 图片地址
|
||||
* @return 识别结果
|
||||
*/
|
||||
@PostMapping("/recognize-food-safety-license")
|
||||
public ActionResult<CertificateFoodSafetyOcrVO> recognizeFoodSafetyLicense(@Validated @RequestBody CertificateFoodSafetyOcrReq req) {
|
||||
return ActionResult.success(recognizeFoodSafetyLicense(req.getImageUrl())
|
||||
.orElse(new CertificateFoodSafetyOcrVO(false)));
|
||||
}
|
||||
|
||||
private Optional<CertificateFoodSafetyOcrVO> recognizeFoodSafetyLicense(String imageUrl) {
|
||||
String tenantId = UserProvider.getUser().getTenantId();
|
||||
String cacheKey = buildFoodSafetyOcrCacheKey(tenantId,imageUrl);
|
||||
try {
|
||||
Object cached = redisUtil.getString(cacheKey);
|
||||
if (cached != null && StringUtil.isNotBlank(cached.toString())) {
|
||||
CertificateFoodSafetyOcrVO cacheVo = JsonUtil.getJsonToBean(cached.toString(), CertificateFoodSafetyOcrVO.class);
|
||||
if (cacheVo != null) {
|
||||
return Optional.of(cacheVo);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("read food safety ocr cache error, imageUrl={}", imageUrl, e);
|
||||
}
|
||||
|
||||
try{
|
||||
// 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密
|
||||
// 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305
|
||||
// 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
|
||||
Credential cred = new Credential(licenseConfig.getSecretId(), licenseConfig.getSecretKey());
|
||||
// 实例化一个http选项,可选的,没有特殊需求可以跳过
|
||||
HttpProfile httpProfile = new HttpProfile();
|
||||
httpProfile.setEndpoint(licenseConfig.getDomain());
|
||||
// 实例化一个client选项,可选的,没有特殊需求可以跳过
|
||||
ClientProfile clientProfile = new ClientProfile();
|
||||
clientProfile.setHttpProfile(httpProfile);
|
||||
// 实例化要请求产品的client对象,clientProfile是可选的
|
||||
OcrClient client = new OcrClient(cred, "ap-guangzhou", clientProfile);
|
||||
// 实例化一个请求对象,每个接口都会对应一个request对象
|
||||
|
||||
SmartStructuralOCRRequest smartStructuralOCRRequest = new SmartStructuralOCRRequest();
|
||||
smartStructuralOCRRequest.setImageUrl(imageUrl);
|
||||
SmartStructuralOCRResponse resp = client.SmartStructuralOCR(smartStructuralOCRRequest);
|
||||
if(Objects.isNull(resp)){
|
||||
return Optional.empty();
|
||||
}
|
||||
StructuralItem [] structuralItems = resp.getStructuralItems();
|
||||
if(structuralItems == null || structuralItems.length == 0){
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Set<String> titleNames = foodSafetyOcrConfig.getTitleNames();
|
||||
Set<String> issueDateNames = foodSafetyOcrConfig.getIssueDateNames();
|
||||
Set<String> expireDateNames = foodSafetyOcrConfig.getExpireDateNames();
|
||||
Set<String> businessItemsNames = foodSafetyOcrConfig.getBusinessItemsNames();
|
||||
String title = null;
|
||||
String issueDate = null;
|
||||
String expireDate = null;
|
||||
String businessItems = null;
|
||||
for (StructuralItem structuralItem : structuralItems){
|
||||
String name = structuralItem.getName();
|
||||
String value = structuralItem.getValue();
|
||||
if(titleNames.contains(name)){
|
||||
title = value;
|
||||
}else if(issueDateNames.contains(name)){
|
||||
issueDate = value;
|
||||
} else if (expireDateNames.contains(name)) {
|
||||
expireDate = value;
|
||||
} else if (businessItemsNames.contains(name)) {
|
||||
businessItems = value;
|
||||
}
|
||||
}
|
||||
if(Objects.isNull(title) || Objects.isNull(issueDate) || Objects.isNull(expireDate) || Objects.isNull(businessItems)){
|
||||
log.error("ocr fail title,issueDate,expireDate,businessItems is null.structuralItems:{}",JsonUtil.getObjectToString(structuralItems));
|
||||
return Optional.empty();
|
||||
}
|
||||
CertificateFoodSafetyOcrVO result = new CertificateFoodSafetyOcrVO(issueDate, expireDate, businessItems,true);
|
||||
try {
|
||||
redisUtil.insert(cacheKey, JsonUtil.getObjectToString(result), FOOD_SAFETY_OCR_CACHE_SECONDS);
|
||||
} catch (Exception e) {
|
||||
log.warn("write food safety ocr cache error, imageUrl={}", imageUrl, e);
|
||||
}
|
||||
return Optional.of(result);
|
||||
} catch (TencentCloudSDKException e) {
|
||||
log.error("Tencent ocr error ",e);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private String buildFoodSafetyOcrCacheKey(String tenantId,String imageUrl) {
|
||||
return FOOD_SAFETY_OCR_CACHE_KEY_PREFIX +tenantId+":"+ DigestUtil.md5Hex(StrUtil.nullToEmpty(imageUrl));
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
String imgUrl = "https://img.cdn1.vip/i/69dda78d48f89_1776134029.webp";
|
||||
try{
|
||||
// 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密
|
||||
// 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305
|
||||
// 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
|
||||
Credential cred = new Credential("AKIDJTbdT7ayRuIAC848D7mKm2Ji5XHua7es", "HOI1iFakDZidu461qaEweHtNfjBY5Rfp");
|
||||
// 实例化一个http选项,可选的,没有特殊需求可以跳过
|
||||
HttpProfile httpProfile = new HttpProfile();
|
||||
httpProfile.setEndpoint("ocr.tencentcloudapi.com");
|
||||
// 实例化一个client选项,可选的,没有特殊需求可以跳过
|
||||
ClientProfile clientProfile = new ClientProfile();
|
||||
clientProfile.setHttpProfile(httpProfile);
|
||||
// 实例化要请求产品的client对象,clientProfile是可选的
|
||||
OcrClient client = new OcrClient(cred, "ap-guangzhou", clientProfile);
|
||||
// 实例化一个请求对象,每个接口都会对应一个request对象
|
||||
|
||||
EnterpriseLicenseOCRRequest req = new EnterpriseLicenseOCRRequest();
|
||||
req.setImageUrl(imgUrl);
|
||||
// 返回的resp是一个BizLicenseOCRResponse的实例,与请求对象对应
|
||||
EnterpriseLicenseOCRResponse resp = client.EnterpriseLicenseOCR(req);
|
||||
|
||||
SmartStructuralOCRV2Request smartStructuralOCRV2Request = new SmartStructuralOCRV2Request();
|
||||
smartStructuralOCRV2Request.setImageUrl(imgUrl);
|
||||
SmartStructuralOCRV2Response smartStructuralOCRV2Response = client.SmartStructuralOCRV2(smartStructuralOCRV2Request);
|
||||
|
||||
SmartStructuralOCRRequest smartStructuralOCRRequest = new SmartStructuralOCRRequest();
|
||||
smartStructuralOCRRequest.setImageUrl(imgUrl);
|
||||
SmartStructuralOCRResponse smartStructuralOCRResponse = client.SmartStructuralOCR(smartStructuralOCRRequest);
|
||||
System.out.println(resp);
|
||||
System.out.println(JsonUtil.getObjectToString(smartStructuralOCRResponse.getStructuralItems()));
|
||||
// 输出json格式的字符串回包
|
||||
} catch (TencentCloudSDKException e) {
|
||||
log.error("Tencent ocr error ",e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package jnpf.certificate.controller;
|
||||
|
||||
import io.seata.spring.annotation.GlobalTransactional;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import jnpf.base.ActionResult;
|
||||
import jnpf.certificate.service.CertificateStoreService;
|
||||
import jnpf.exception.HandleException;
|
||||
import jnpf.model.certificate.req.CertificateStoreSaveReq;
|
||||
import jnpf.model.certificate.vo.CertificateStoreAndCertificatesVO;
|
||||
import jnpf.model.certificate.vo.CertificateStoreTabVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* web门店证照控制器。
|
||||
*/
|
||||
@RestController
|
||||
@Validated
|
||||
@RequestMapping("/web/certificate-store")
|
||||
public class CertificateStoreController {
|
||||
|
||||
/**
|
||||
* 门店证照服务。
|
||||
*/
|
||||
@Autowired
|
||||
private CertificateStoreService certificateStoreService;
|
||||
|
||||
/**
|
||||
* 保存门店及证照数据。
|
||||
* 核心参数包含门店信息、健康证、营业执照、食品经营许可证和门店自定义证照集合。
|
||||
*
|
||||
* @param req 保存参数
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PostMapping("/save-store-and-certificates")
|
||||
@GlobalTransactional
|
||||
public ActionResult<String> saveStoreAndCertificates(@Validated @RequestBody CertificateStoreSaveReq req) {
|
||||
return ActionResult.success("success",certificateStoreService.saveStoreAndCertificates(req));
|
||||
}
|
||||
|
||||
@Operation(summary = "[删除]删除门店和证照")
|
||||
@DeleteMapping(value = "/{id}")
|
||||
@GlobalTransactional
|
||||
public ActionResult<Boolean> deleteStore(@PathVariable("id") String id) throws HandleException {
|
||||
return ActionResult.success(certificateStoreService.deleteStore(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据门店ID查询门店证照详情。营业执照、食品许可证、门店自定义证照集合。
|
||||
*
|
||||
* @param storeId 门店ID
|
||||
* @return 门店证照详情
|
||||
*/
|
||||
@GetMapping("/get-store-certificates")
|
||||
public ActionResult<CertificateStoreAndCertificatesVO> getStoreCertificates(@RequestParam("storeId")
|
||||
@NotBlank(message = "门店ID不能为空")
|
||||
String storeId) {
|
||||
return ActionResult.success(certificateStoreService.getStoreAndCertificates(storeId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询门店证照Tab列表。
|
||||
*
|
||||
* @return 门店证照Tab列表
|
||||
*/
|
||||
@GetMapping("/query-store-certificate-tab-list")
|
||||
public ActionResult<List<CertificateStoreTabVO>> queryStoreCertificateTabList() {
|
||||
return ActionResult.success(certificateStoreService.queryStoreCertificateTabList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,932 @@
|
||||
package jnpf.certificate.controller;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import jnpf.authority.utils.PermissionsUtils;
|
||||
import jnpf.base.ActionResult;
|
||||
import jnpf.certificate.CertificateWarningApi;
|
||||
import jnpf.certificate.helper.NoticeHelper;
|
||||
import jnpf.certificate.helper.OrganizationHelper;
|
||||
import jnpf.certificate.mapper.CertificateInstanceMapper;
|
||||
import jnpf.model.certificate.po.CertificateInstanceEntity;
|
||||
import jnpf.model.storecertificatephoto.po.StoreCertificatePhotoEntity;
|
||||
import jnpf.model.warningnotice.enums.CertificateTypeEnum;
|
||||
import jnpf.model.warningnotice.vo.WarningNoticeTargetVO;
|
||||
import jnpf.model.warningnotice.vo.WarningNoticeUserConfigVO;
|
||||
import jnpf.model.warningnotice.vo.WarningNoticeVO;
|
||||
import jnpf.permission.UserApi;
|
||||
import jnpf.permission.V2UserApi;
|
||||
import jnpf.permission.dto.v2.user.QueryUserBatchDTO;
|
||||
import jnpf.permission.entity.UserEntity;
|
||||
import jnpf.permission.vo.v2.organzie.OrganizeBaseInfoVO;
|
||||
import jnpf.permission.vo.v2.user.UserBaseInfoVO;
|
||||
import jnpf.permission.vo.v2.user.UserBoundVO;
|
||||
import jnpf.storecertificatephoto.mapper.StoreCertificatePhotoMapper;
|
||||
import jnpf.storecertificatephoto.service.WarningNoticeService;
|
||||
import jnpf.util.CustomTenantUtil;
|
||||
import jnpf.util.NoDataSourceBind;
|
||||
import jnpf.util.StringUtil;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static jnpf.certificate.helper.NoticeHelper.*;
|
||||
|
||||
/**
|
||||
* 证照预警接口实现。
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/web/certificate-warning-api")
|
||||
public class CertificateWarningController implements CertificateWarningApi {
|
||||
|
||||
private static final int STATUS_MISSING = 1;
|
||||
private static final int STATUS_EXPIRED = 2;
|
||||
private static final int STATUS_NEAR_EXPIRE = 3;
|
||||
private static final int STATUS_NORMAL = 4;
|
||||
|
||||
private static final int SUBJECT_TYPE_EMPLOYEE = 1;
|
||||
private static final int DEFAULT_NEAR_EXPIRE_DAYS = 30;
|
||||
private static final int DEFAULT_NOTICE_FREQUENCY_DAYS = 1;
|
||||
private static final int STORE_CUSTOM_TEMPLATE_DISABLED_STATUS = 0;
|
||||
private static final int USER_ADMINISTRATOR_FLAG = 1;
|
||||
|
||||
private static final String NOTICE_USER_TYPE_POSITION = "position";
|
||||
private static final String NOTICE_USER_TYPE_PERSONNEL = "personnel";
|
||||
private static final String NOTICE_USER_TYPE_PERSON = "person";
|
||||
|
||||
@Value("${config.certificate.module.id:813679492035805253}")
|
||||
private String HEALTH_CERTIFICATE_PERMISSION_MODULE_ID;
|
||||
|
||||
@Autowired
|
||||
private CertificateInstanceMapper certificateInstanceMapper;
|
||||
@Autowired
|
||||
private WarningNoticeService warningNoticeService;
|
||||
@Autowired
|
||||
private StoreCertificatePhotoMapper storeCertificatePhotoMapper;
|
||||
@Autowired
|
||||
private NoticeHelper noticeHelper;
|
||||
@Autowired
|
||||
private V2UserApi v2UserApi;
|
||||
@Autowired
|
||||
private UserApi userApi;
|
||||
@Autowired
|
||||
private PermissionsUtils permissionsUtils;
|
||||
@Autowired
|
||||
private CustomTenantUtil customTenantUtil;
|
||||
@Autowired
|
||||
private OrganizationHelper organizationHelper;
|
||||
|
||||
/**
|
||||
* 检查证照状态并发送临期通知:
|
||||
* 1. 将正常/临期状态按规则纠正为临期或过期。
|
||||
* 2. 向临期对象发送预警消息(健康证通知本人,组织/门店证照通知组织负责人)。
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @return 处理结果
|
||||
*/
|
||||
@Override
|
||||
@NoDataSourceBind
|
||||
@PostMapping("/checkAndSendCertificateWarning")
|
||||
public ActionResult<Boolean> checkAndSendCertificateWarning(@RequestParam("tenantId") String tenantId) {
|
||||
try {
|
||||
log.error("checkAndSendCertificateWarning tenantId:{}",tenantId);
|
||||
customTenantUtil.checkOutTenant(tenantId);
|
||||
doCheckAndSend(StrUtil.trim(tenantId));
|
||||
return ActionResult.success(Boolean.TRUE);
|
||||
} catch (Exception e) {
|
||||
log.error("检查并发送证照预警失败,tenantId={}", tenantId, e);
|
||||
return ActionResult.success(Boolean.FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行检查与发送。
|
||||
*/
|
||||
private void doCheckAndSend(String tenantId) {
|
||||
List<CertificateInstanceEntity> candidateList = queryCandidateCertificateList();
|
||||
log.error("doCheckAndSend candidateList size:{}", CollUtil.isEmpty(candidateList) ? 0 : candidateList.size());
|
||||
if (CollUtil.isEmpty(candidateList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, WarningNoticeVO> warningConfigMap = queryWarningConfigMap();
|
||||
Map<String, StoreCertificatePhotoEntity> templateMap = queryTemplateMap(candidateList);
|
||||
Map<String, Integer> templateNearExpireDaysMap = buildTemplateNearExpireDaysMap(templateMap);
|
||||
|
||||
List<CertificateInstanceEntity> needUpdateList = new ArrayList<>();
|
||||
List<NearExpireNoticeTask> noticeTaskList = new ArrayList<>();
|
||||
for (CertificateInstanceEntity entity : candidateList) {
|
||||
if (entity == null || StrUtil.isBlank(entity.getId()) || StrUtil.isBlank(entity.getCertificateType())) {
|
||||
continue;
|
||||
}
|
||||
WarningNoticeVO warningNoticeVO = buildWarningNoticeVO(entity, warningConfigMap);
|
||||
boolean skipByExpiryReminderDays = shouldSkipWarningNoticeByExpiryReminderDays(warningNoticeVO);
|
||||
boolean skipWarningNotice = isStoreCustomTemplateWarningDisabled(entity, templateMap);
|
||||
int nearExpireDays = resolveNearExpireDays(entity, warningConfigMap, templateNearExpireDaysMap);
|
||||
int targetStatus = calculateStatus(entity, nearExpireDays,templateMap);
|
||||
|
||||
if ((targetStatus == STATUS_NEAR_EXPIRE || targetStatus == STATUS_EXPIRED || targetStatus == STATUS_NORMAL)
|
||||
&& !Objects.equals(entity.getStatus(), targetStatus)) {
|
||||
entity.setStatus(targetStatus);
|
||||
needUpdateList.add(entity);
|
||||
}
|
||||
|
||||
int effectiveStatus = (targetStatus == STATUS_NEAR_EXPIRE || targetStatus == STATUS_EXPIRED)
|
||||
? targetStatus : (entity.getStatus() == null ? STATUS_MISSING : entity.getStatus());
|
||||
if (effectiveStatus != STATUS_NEAR_EXPIRE || skipWarningNotice || skipByExpiryReminderDays) {
|
||||
continue;
|
||||
}
|
||||
Integer daysToExpire = calculateDaysToExpire(entity.getExpireDate());
|
||||
if (daysToExpire == null || daysToExpire < 0) {
|
||||
continue;
|
||||
}
|
||||
int noticeFrequencyDays = resolveNoticeFrequencyDays(warningNoticeVO);
|
||||
if (!shouldSendByFrequency(daysToExpire, noticeFrequencyDays)) {
|
||||
continue;
|
||||
}
|
||||
String templateName = null;
|
||||
if(isStoreCustomCertificate(entity)){
|
||||
StoreCertificatePhotoEntity storeCertificatePhotoEntity = templateMap.get(entity.getTemplateId());
|
||||
if(Objects.nonNull(storeCertificatePhotoEntity)){
|
||||
templateName = storeCertificatePhotoEntity.getCertificateName();
|
||||
}
|
||||
}
|
||||
noticeTaskList.add(new NearExpireNoticeTask(entity, daysToExpire, warningNoticeVO,templateName));
|
||||
}
|
||||
|
||||
updateCertificateStatusBatch(needUpdateList);
|
||||
sendNearExpireNoticeBatch(noticeTaskList, tenantId);
|
||||
}
|
||||
|
||||
private WarningNoticeVO buildWarningNoticeVO(CertificateInstanceEntity entity, Map<String, WarningNoticeVO> warningConfigMap) {
|
||||
String certificateType = entity.getCertificateType();
|
||||
if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType())) {
|
||||
return warningConfigMap.get(entity.getTemplateId());
|
||||
}
|
||||
return warningConfigMap.get(certificateType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询待检查的证照数据(仅正常、临期)。
|
||||
*/
|
||||
private List<CertificateInstanceEntity> queryCandidateCertificateList() {
|
||||
LambdaQueryWrapper<CertificateInstanceEntity> queryWrapper = Wrappers.lambdaQuery();
|
||||
queryWrapper.eq(CertificateInstanceEntity::getEnabledMark, 0);
|
||||
queryWrapper.eq(CertificateInstanceEntity::getTemplateStatus,1);
|
||||
queryWrapper.in(CertificateInstanceEntity::getStatus, Arrays.asList(STATUS_NORMAL, STATUS_NEAR_EXPIRE));
|
||||
queryWrapper.in(CertificateInstanceEntity::getCertificateType, Arrays.asList(
|
||||
CertificateTypeEnum.HEALTH_CERTIFICATE.getType(),
|
||||
CertificateTypeEnum.BUSINESS_LICENSE.getType(),
|
||||
CertificateTypeEnum.HYGIENE_LICENSE.getType(),
|
||||
CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType()
|
||||
));
|
||||
queryWrapper.orderByAsc(CertificateInstanceEntity::getCertificateType);
|
||||
queryWrapper.orderByAsc(CertificateInstanceEntity::getSubjectType);
|
||||
queryWrapper.orderByAsc(CertificateInstanceEntity::getSubjectId);
|
||||
return certificateInstanceMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询预警设置(健康证、营业执照、食品经营许可证、门店自定义证照)。
|
||||
*/
|
||||
private Map<String, WarningNoticeVO> queryWarningConfigMap() {
|
||||
Map<String, WarningNoticeVO> result = new HashMap<>(4);
|
||||
List<WarningNoticeVO> warningNoticeVOList = warningNoticeService.queryAll();
|
||||
for (WarningNoticeVO warningNoticeVO:warningNoticeVOList){
|
||||
result.put(warningNoticeVO.getTypeOrTemplateId(), warningNoticeVO);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询门店自定义证照模板临期天数配置。
|
||||
*/
|
||||
private Map<String, StoreCertificatePhotoEntity> queryTemplateMap(List<CertificateInstanceEntity> instanceList) {
|
||||
List<String> templateIds = instanceList.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(entity -> StrUtil.equalsIgnoreCase(entity.getCertificateType(), CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType()))
|
||||
.map(CertificateInstanceEntity::getTemplateId)
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.map(StrUtil::trim)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
if (CollUtil.isEmpty(templateIds)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<StoreCertificatePhotoEntity> queryWrapper = Wrappers.lambdaQuery();
|
||||
queryWrapper.in(StoreCertificatePhotoEntity::getId, templateIds);
|
||||
queryWrapper.eq(StoreCertificatePhotoEntity::getEnabledMark, 0);
|
||||
List<StoreCertificatePhotoEntity> templateList = storeCertificatePhotoMapper.selectList(queryWrapper);
|
||||
if (CollUtil.isEmpty(templateList)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
Map<String, StoreCertificatePhotoEntity> result = new HashMap<>(templateList.size());
|
||||
for (StoreCertificatePhotoEntity template : templateList) {
|
||||
if (template == null || StrUtil.isBlank(template.getId())) {
|
||||
continue;
|
||||
}
|
||||
result.put(StrUtil.trim(template.getId()), template);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建门店自定义证照模板临期天数映射。
|
||||
*/
|
||||
private Map<String, Integer> buildTemplateNearExpireDaysMap(Map<String, StoreCertificatePhotoEntity> templateMap) {
|
||||
if (CollUtil.isEmpty(templateMap)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, Integer> result = new HashMap<>(templateMap.size());
|
||||
for (Map.Entry<String, StoreCertificatePhotoEntity> entry : templateMap.entrySet()) {
|
||||
String templateId = StrUtil.trim(entry.getKey());
|
||||
if (StrUtil.isBlank(templateId)) {
|
||||
continue;
|
||||
}
|
||||
StoreCertificatePhotoEntity template = entry.getValue();
|
||||
Integer reminderDays = template == null ? null : template.getExpiryReminderDays();
|
||||
result.put(templateId, reminderDays == null || reminderDays < 0 ? DEFAULT_NEAR_EXPIRE_DAYS : reminderDays);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按证照类型解析临期阈值天数。
|
||||
*/
|
||||
private int resolveNearExpireDays(CertificateInstanceEntity entity,
|
||||
Map<String, WarningNoticeVO> warningConfigMap,
|
||||
Map<String, Integer> templateNearExpireDaysMap) {
|
||||
if (entity == null || StrUtil.isBlank(entity.getCertificateType())) {
|
||||
return DEFAULT_NEAR_EXPIRE_DAYS;
|
||||
}
|
||||
String certificateType = StrUtil.trim(entity.getCertificateType());
|
||||
if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType())) {
|
||||
String templateId = StrUtil.trim(entity.getTemplateId());
|
||||
Integer templateDays = templateNearExpireDaysMap.get(templateId);
|
||||
return templateDays == null || templateDays < 0 ? DEFAULT_NEAR_EXPIRE_DAYS : templateDays;
|
||||
}
|
||||
WarningNoticeVO warningNoticeVO = warningConfigMap.get(certificateType);
|
||||
Integer reminderDays = warningNoticeVO == null ? null : warningNoticeVO.getExpiryReminderDays();
|
||||
return reminderDays == null || reminderDays < 0 ? DEFAULT_NEAR_EXPIRE_DAYS : reminderDays;
|
||||
}
|
||||
|
||||
/**
|
||||
* 若是门店自定义证照,且模板状态为0,则不发送预警通知。
|
||||
*/
|
||||
private boolean isStoreCustomTemplateWarningDisabled(CertificateInstanceEntity entity,
|
||||
Map<String, StoreCertificatePhotoEntity> templateMap) {
|
||||
if (entity == null || !StrUtil.equalsIgnoreCase(entity.getCertificateType(), CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType())) {
|
||||
return false;
|
||||
}
|
||||
String templateId = StrUtil.trim(entity.getTemplateId());
|
||||
if (StrUtil.isBlank(templateId)) {
|
||||
return false;
|
||||
}
|
||||
StoreCertificatePhotoEntity template = templateMap.get(templateId);
|
||||
return template != null && Integer.valueOf(STORE_CUSTOM_TEMPLATE_DISABLED_STATUS).equals(template.getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* 按证照类型解析通知频率天数。
|
||||
*/
|
||||
private int resolveNoticeFrequencyDays(WarningNoticeVO warningNoticeVO) {
|
||||
Integer frequencyDays = warningNoticeVO == null ? null : warningNoticeVO.getNoticeFrequencyDays();
|
||||
return frequencyDays == null || frequencyDays <= 0 ? DEFAULT_NOTICE_FREQUENCY_DAYS : frequencyDays;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当预警配置的临期提醒天数小于等于0时,跳过预警通知发送。
|
||||
*/
|
||||
private boolean shouldSkipWarningNoticeByExpiryReminderDays(WarningNoticeVO warningNoticeVO) {
|
||||
Integer expiryReminderDays = warningNoticeVO == null ? null : warningNoticeVO.getExpiryReminderDays();
|
||||
return expiryReminderDays == null || expiryReminderDays <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新证照状态。
|
||||
*/
|
||||
private void updateCertificateStatusBatch(List<CertificateInstanceEntity> needUpdateList) {
|
||||
if (CollUtil.isEmpty(needUpdateList)) {
|
||||
return;
|
||||
}
|
||||
for (CertificateInstanceEntity entity : needUpdateList) {
|
||||
if (entity == null || StrUtil.isBlank(entity.getId()) || entity.getStatus() == null) {
|
||||
continue;
|
||||
}
|
||||
CertificateInstanceEntity updateEntity = new CertificateInstanceEntity();
|
||||
updateEntity.setId(entity.getId());
|
||||
updateEntity.setStatus(entity.getStatus());
|
||||
certificateInstanceMapper.updateById(updateEntity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量发送临期通知。
|
||||
*/
|
||||
private void sendNearExpireNoticeBatch(List<NearExpireNoticeTask> noticeTaskList, String tenantId) {
|
||||
if (CollUtil.isEmpty(noticeTaskList) || StrUtil.isBlank(tenantId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Pair<Map<String, OrganizeBaseInfoVO>,Map<String,UserBaseInfoVO>> organizeMapAndUserOrgMap = queryOrganizeInfoMap(noticeTaskList,tenantId);
|
||||
Map<String, OrganizeBaseInfoVO> organizeMap = organizeMapAndUserOrgMap.getLeft();
|
||||
Map<String,UserBaseInfoVO> userBaseInfoByUserId = organizeMapAndUserOrgMap.getRight();
|
||||
for (NearExpireNoticeTask task : noticeTaskList) {
|
||||
if (task == null || task.getEntity() == null) {
|
||||
continue;
|
||||
}
|
||||
CertificateInstanceEntity entity = task.getEntity();
|
||||
OrganizeBaseInfoVO organizeBaseInfoVO = organizeMap.get(entity.getSubjectId());
|
||||
//组织被禁用,则不推送
|
||||
if(!isHealthEmployeeCertificate(entity) && Objects.nonNull(organizeBaseInfoVO) && organizeBaseInfoVO.isDisabled()){
|
||||
continue;
|
||||
}
|
||||
|
||||
//如果不是健康证
|
||||
if(!isHealthEmployeeCertificate(entity)){
|
||||
List<String> receiverUserIds = resolveReceiverUserIds(entity, organizeMap, task.getWarningNoticeVO(), tenantId);
|
||||
if(CollUtil.isEmpty(receiverUserIds)){
|
||||
continue;
|
||||
}
|
||||
NoticeMessage noticeMessage = buildNearExpireMessage(entity, task, organizeMap,false,null);
|
||||
noticeHelper.sendMessage(receiverUserIds, tenantId, noticeMessage.getTitle(), noticeMessage.getContent(), null, null, null);
|
||||
continue;
|
||||
}
|
||||
|
||||
//如果是健康证的通知
|
||||
//获取健康证本人
|
||||
List<String> receiverUserIds = resolveReceiverUserIds(entity, organizeMap, task.getWarningNoticeVO(), tenantId);
|
||||
if (CollUtil.isNotEmpty(receiverUserIds)){
|
||||
NoticeMessage noticeMessage = buildNearExpireMessage(entity, task, organizeMap,true,null);
|
||||
noticeHelper.sendMessage(receiverUserIds, tenantId, noticeMessage.getTitle(), noticeMessage.getContent(), BUTTON_NAME_HANDLER, buildJumpHealthCertificateUrl(entity), MP_ID_CERTIFICATE);
|
||||
}
|
||||
//获取健康证所属组织的负责人
|
||||
OrganizeBaseInfoVO healthEmployeeOrgLeaderReceiver = resolveHealthEmployeeOrgLeaderReceiver(entity, organizeMap, userBaseInfoByUserId);
|
||||
List<String> otherReceiverUserIds = new ArrayList<>();
|
||||
if(Objects.nonNull(healthEmployeeOrgLeaderReceiver)){
|
||||
otherReceiverUserIds.add(healthEmployeeOrgLeaderReceiver.getLeaderId());
|
||||
}
|
||||
//获取配置的通知者
|
||||
List<String> configUserIds = resolveWarningConfigUserIds(entity, task.getWarningNoticeVO(), tenantId);
|
||||
if(CollUtil.isNotEmpty(configUserIds)){
|
||||
otherReceiverUserIds.addAll(configUserIds);
|
||||
}
|
||||
|
||||
if (CollUtil.isEmpty(otherReceiverUserIds)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//健康证风险,发给该健康证的组织负责人以及配置的人员
|
||||
NoticeMessage healthEmployLeaderNoticeMessage = buildNearExpireMessage(entity, task, organizeMap,false,userBaseInfoByUserId);
|
||||
if(Objects.isNull(healthEmployLeaderNoticeMessage)){
|
||||
continue;
|
||||
}
|
||||
noticeHelper.sendMessage(otherReceiverUserIds, tenantId, healthEmployLeaderNoticeMessage.getTitle(), healthEmployLeaderNoticeMessage.getContent(), BUTTON_NAME_HANDLER, buildJumpHealthCertificateUrl(entity), MP_ID_CERTIFICATE);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildJumpHealthCertificateUrl(CertificateInstanceEntity entity) {
|
||||
if(Objects.isNull(entity)){
|
||||
return null;
|
||||
}
|
||||
String subjectId = entity.getSubjectId();
|
||||
if(StringUtil.isBlank(subjectId)){
|
||||
return null;
|
||||
}
|
||||
return String.format(URL_CERTIFICATE,entity.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 左侧 查询组织信息映射(用于拿负责人和组织名称)。 key为组织id,value为基础组织信息。
|
||||
* 右侧 如果是健康证还需要查询员工所属组织信息。key为userId,value为该userId的基础信息
|
||||
* 包含非员工主体的组织信息,以及健康证员工所属组织的信息。
|
||||
*/
|
||||
private Pair<Map<String, OrganizeBaseInfoVO>,Map<String,UserBaseInfoVO>> queryOrganizeInfoMap(List<NearExpireNoticeTask> noticeTaskList,String tenantId) {
|
||||
Set<String> organizeIdSet = new LinkedHashSet<>();
|
||||
// 收集健康证员工的 subjectId,用于查询其所属组织
|
||||
Set<String> healthEmployeeUserIds = new LinkedHashSet<>();
|
||||
for (NearExpireNoticeTask task : noticeTaskList) {
|
||||
if (task == null || task.getEntity() == null) {
|
||||
continue;
|
||||
}
|
||||
CertificateInstanceEntity entity = task.getEntity();
|
||||
if (Integer.valueOf(SUBJECT_TYPE_EMPLOYEE).equals(entity.getSubjectType())) {
|
||||
if (isHealthEmployeeCertificate(entity) && StrUtil.isNotBlank(entity.getSubjectId())) {
|
||||
healthEmployeeUserIds.add(StrUtil.trim(entity.getSubjectId()));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
String organizeId = StrUtil.trim(entity.getSubjectId());
|
||||
if (StrUtil.isNotBlank(organizeId)) {
|
||||
organizeIdSet.add(organizeId);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, UserBaseInfoVO> userBaseInfoByUserId = Collections.emptyMap();
|
||||
// 查询健康证员工所属的组织ID(批量SQL查询)
|
||||
if (CollUtil.isNotEmpty(healthEmployeeUserIds)) {
|
||||
userBaseInfoByUserId = organizationHelper.buildUserBaseInfoByUserIds(healthEmployeeUserIds,tenantId);
|
||||
organizeIdSet.addAll(userBaseInfoByUserId.values()
|
||||
.stream()
|
||||
.map(UserBaseInfoVO::getOrganizeId)
|
||||
.collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
if (CollUtil.isEmpty(organizeIdSet)) {
|
||||
return Pair.of(Collections.emptyMap(),userBaseInfoByUserId);
|
||||
}
|
||||
|
||||
return Pair.of(organizationHelper.buildBaseOrganizeVO(organizeIdSet,tenantId),userBaseInfoByUserId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析接收人:
|
||||
* 1. 健康证通知当前人员
|
||||
* 2. 组织/门店证照通知组织负责人。
|
||||
*/
|
||||
private List<String> resolveReceiverUserIds(CertificateInstanceEntity entity,
|
||||
Map<String, OrganizeBaseInfoVO> organizeMap,
|
||||
WarningNoticeVO warningNoticeVO,
|
||||
String tenantId) {
|
||||
if (entity == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
String subjectId = StrUtil.trim(entity.getSubjectId());
|
||||
if (StrUtil.isBlank(subjectId)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<String> receiverUserIds = new ArrayList<>();
|
||||
if (isHealthEmployeeCertificate(entity)) {
|
||||
receiverUserIds.add(subjectId);
|
||||
} else {
|
||||
addOrganizationLeaderReceiver(entity, organizeMap, receiverUserIds);
|
||||
}
|
||||
|
||||
if (warningNoticeVO != null && !isHealthEmployeeCertificate(entity)) {
|
||||
List<String> configUserIds = resolveWarningConfigUserIds(entity, warningNoticeVO, tenantId);
|
||||
if (CollUtil.isNotEmpty(configUserIds)) {
|
||||
receiverUserIds.addAll(configUserIds);
|
||||
}
|
||||
}
|
||||
return normalizeIdList(receiverUserIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 非健康证默认通知组织负责人。
|
||||
*/
|
||||
private void addOrganizationLeaderReceiver(CertificateInstanceEntity entity,
|
||||
Map<String, OrganizeBaseInfoVO> organizeMap,
|
||||
List<String> receiverUserIds) {
|
||||
if (entity == null || CollUtil.isEmpty(organizeMap) || receiverUserIds == null) {
|
||||
return;
|
||||
}
|
||||
String subjectId = StrUtil.trim(entity.getSubjectId());
|
||||
if (StrUtil.isBlank(subjectId)) {
|
||||
return;
|
||||
}
|
||||
OrganizeBaseInfoVO organize = organizeMap.get(subjectId);
|
||||
if (organize == null || StrUtil.isBlank(organize.getLeaderId())) {
|
||||
return;
|
||||
}
|
||||
receiverUserIds.add(StrUtil.trim(organize.getLeaderId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 健康证通知该员工所属组织的负责人。
|
||||
*/
|
||||
private OrganizeBaseInfoVO resolveHealthEmployeeOrgLeaderReceiver(CertificateInstanceEntity entity,
|
||||
Map<String, OrganizeBaseInfoVO> organizeMap,
|
||||
Map<String,UserBaseInfoVO> userBaseInfoByUserId) {
|
||||
if (Objects.isNull(entity) || CollUtil.isEmpty(organizeMap)) {
|
||||
return null;
|
||||
}
|
||||
if(!isHealthEmployeeCertificate(entity)){
|
||||
return null;
|
||||
}
|
||||
String subjectId = StrUtil.trim(entity.getSubjectId());
|
||||
UserBaseInfoVO userBaseInfoVO = userBaseInfoByUserId.get(subjectId);
|
||||
if(Objects.isNull(userBaseInfoVO)){
|
||||
log.error("resolveHealthEmployeeOrgLeaderReceiver 用户的基础为空!.subjectId:{}",subjectId);
|
||||
return null;
|
||||
}
|
||||
String orgId = userBaseInfoVO.getOrganizeId();
|
||||
if(StrUtil.isBlank(orgId)){
|
||||
log.error("resolveHealthEmployeeOrgLeaderReceiver 用户的组织id为空!.subjectId:{}",subjectId);
|
||||
return null;
|
||||
}
|
||||
OrganizeBaseInfoVO organize = organizeMap.get(orgId);
|
||||
if (organize != null && StrUtil.isNotBlank(organize.getLeaderId())) {
|
||||
return organize;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析预警配置接收人。
|
||||
*/
|
||||
private List<String> resolveWarningConfigUserIds(CertificateInstanceEntity entity,
|
||||
WarningNoticeVO warningNoticeVO,
|
||||
String tenantId) {
|
||||
if (entity == null || warningNoticeVO == null || StrUtil.isBlank(tenantId)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<WarningNoticeUserConfigVO> noticeConfigList = warningNoticeVO.getNoticeConfigList();
|
||||
if (CollUtil.isEmpty(noticeConfigList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
boolean healthCertificate = isHealthEmployeeCertificate(entity);
|
||||
List<String> receiverUserIds = new ArrayList<>();
|
||||
for (WarningNoticeUserConfigVO config : noticeConfigList) {
|
||||
if (config == null || StrUtil.isBlank(config.getNoticeUserType()) || CollUtil.isEmpty(config.getNoticeUserList())) {
|
||||
continue;
|
||||
}
|
||||
String noticeUserType = StrUtil.trim(config.getNoticeUserType());
|
||||
if (StrUtil.equalsAnyIgnoreCase(noticeUserType, NOTICE_USER_TYPE_POSITION)) {
|
||||
receiverUserIds.addAll(resolvePositionUserIds(config.getNoticeUserList(), entity, tenantId));
|
||||
continue;
|
||||
}
|
||||
if (StrUtil.equalsAnyIgnoreCase(noticeUserType, NOTICE_USER_TYPE_PERSONNEL, NOTICE_USER_TYPE_PERSON)) {
|
||||
if (healthCertificate) {
|
||||
receiverUserIds.addAll(resolveHealthPersonnelConfigUserIds(config.getNoticeUserList(), entity, tenantId));
|
||||
} else {
|
||||
receiverUserIds.addAll(extractTargetIds(config.getNoticeUserList()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return normalizeIdList(receiverUserIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按岗位解析通知人。健康证场景下需按数据权限过滤。
|
||||
*/
|
||||
private List<String> resolvePositionUserIds(List<WarningNoticeTargetVO> noticeUserList,
|
||||
CertificateInstanceEntity entity,
|
||||
String tenantId) {
|
||||
List<String> positionIds = extractTargetIds(noticeUserList);
|
||||
if (CollUtil.isEmpty(positionIds) || StrUtil.isBlank(tenantId)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
QueryUserBatchDTO dto = new QueryUserBatchDTO();
|
||||
dto.setPositionIds(positionIds);
|
||||
dto.setTenantId(tenantId);
|
||||
ActionResult<List<UserBoundVO>> userInfoBatch = v2UserApi.getUserInfoBatch(dto);
|
||||
if (userInfoBatch == null || !Integer.valueOf(200).equals(userInfoBatch.getCode()) || CollUtil.isEmpty(userInfoBatch.getData())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> positionUserIds = normalizeIdList(userInfoBatch.getData().stream().map(UserBoundVO::getId).collect(Collectors.toList()));
|
||||
|
||||
// 健康证场景:按数据权限过滤岗位人员
|
||||
if (isHealthEmployeeCertificate(entity) && CollUtil.isNotEmpty(positionUserIds)) {
|
||||
return filterByDataPermission(positionUserIds, entity, tenantId);
|
||||
}
|
||||
return positionUserIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按数据权限过滤通知人员(健康证场景)。
|
||||
*/
|
||||
private List<String> filterByDataPermission(List<String> candidateUserIds,
|
||||
CertificateInstanceEntity entity,
|
||||
String tenantId) {
|
||||
String subjectId = entity == null ? null : StrUtil.trim(entity.getSubjectId());
|
||||
if (StrUtil.isBlank(subjectId) || StrUtil.isBlank(tenantId) || CollUtil.isEmpty(candidateUserIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<UserEntity> userEntityList = userApi.getUserListNoData(candidateUserIds, tenantId);
|
||||
if (CollUtil.isEmpty(userEntityList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
for (UserEntity userEntity : userEntityList) {
|
||||
if (userEntity == null || StrUtil.isBlank(userEntity.getId())) {
|
||||
continue;
|
||||
}
|
||||
String userId = StrUtil.trim(userEntity.getId());
|
||||
if (Integer.valueOf(USER_ADMINISTRATOR_FLAG).equals(userEntity.getIsAdministrator())) {
|
||||
result.add(userId);
|
||||
continue;
|
||||
}
|
||||
List<String> dataPermissionUserIds;
|
||||
try {
|
||||
dataPermissionUserIds = permissionsUtils.obtainPersonnelUserIdDataPermissions(userId, HEALTH_CERTIFICATE_PERMISSION_MODULE_ID);
|
||||
} catch (Exception ex) {
|
||||
log.warn("query health certificate position user permission failed,userId={},module={}", userId, HEALTH_CERTIFICATE_PERMISSION_MODULE_ID, ex);
|
||||
continue;
|
||||
}
|
||||
if (dataPermissionUserIds == null) {
|
||||
result.add(userId);
|
||||
continue;
|
||||
}
|
||||
if (CollUtil.isNotEmpty(dataPermissionUserIds) && dataPermissionUserIds.contains(subjectId)) {
|
||||
result.add(userId);
|
||||
}
|
||||
}
|
||||
return normalizeIdList(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 健康证-按人员配置时,按数据权限过滤可通知人员。
|
||||
*/
|
||||
private List<String> resolveHealthPersonnelConfigUserIds(List<WarningNoticeTargetVO> noticeUserList,
|
||||
CertificateInstanceEntity entity,
|
||||
String tenantId) {
|
||||
String subjectId = entity == null ? null : StrUtil.trim(entity.getSubjectId());
|
||||
if (StrUtil.isBlank(subjectId) || StrUtil.isBlank(tenantId)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> targetUserIds = extractTargetIds(noticeUserList);
|
||||
if (CollUtil.isEmpty(targetUserIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<UserEntity> userEntityList = userApi.getUserListNoData(targetUserIds, tenantId);
|
||||
if (CollUtil.isEmpty(userEntityList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
for (UserEntity userEntity : userEntityList) {
|
||||
if (userEntity == null || StrUtil.isBlank(userEntity.getId())) {
|
||||
continue;
|
||||
}
|
||||
String userId = StrUtil.trim(userEntity.getId());
|
||||
if (Integer.valueOf(USER_ADMINISTRATOR_FLAG).equals(userEntity.getIsAdministrator())) {
|
||||
result.add(userId);
|
||||
continue;
|
||||
}
|
||||
List<String> dataPermissionUserIds;
|
||||
try {
|
||||
dataPermissionUserIds = permissionsUtils.obtainPersonnelUserIdDataPermissions(userId, HEALTH_CERTIFICATE_PERMISSION_MODULE_ID);
|
||||
} catch (Exception ex) {
|
||||
log.warn("query health certificate user permission failed,userId={},module={}", userId, HEALTH_CERTIFICATE_PERMISSION_MODULE_ID, ex);
|
||||
continue;
|
||||
}
|
||||
if (dataPermissionUserIds == null || CollUtil.isEmpty(dataPermissionUserIds)) {
|
||||
if (dataPermissionUserIds == null) {
|
||||
result.add(userId);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (dataPermissionUserIds.contains(subjectId)) {
|
||||
result.add(userId);
|
||||
}
|
||||
}
|
||||
return normalizeIdList(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取配置对象ID。
|
||||
*/
|
||||
private List<String> extractTargetIds(List<WarningNoticeTargetVO> noticeUserList) {
|
||||
if (CollUtil.isEmpty(noticeUserList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return normalizeIdList(noticeUserList.stream().map(WarningNoticeTargetVO::getId).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 标准化ID列表。
|
||||
*/
|
||||
private List<String> normalizeIdList(Collection<String> idList) {
|
||||
if (CollUtil.isEmpty(idList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return idList.stream()
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.map(StrUtil::trim)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否健康证+人员主体。
|
||||
*/
|
||||
private boolean isHealthEmployeeCertificate(CertificateInstanceEntity entity) {
|
||||
return entity != null
|
||||
&& StrUtil.equalsIgnoreCase(entity.getCertificateType(), CertificateTypeEnum.HEALTH_CERTIFICATE.getType())
|
||||
&& Integer.valueOf(SUBJECT_TYPE_EMPLOYEE).equals(entity.getSubjectType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否自定义证照
|
||||
*/
|
||||
private boolean isStoreCustomCertificate(CertificateInstanceEntity entity) {
|
||||
return entity != null
|
||||
&& StrUtil.equalsIgnoreCase(entity.getCertificateType(), CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算状态。
|
||||
*/
|
||||
private int calculateStatus(CertificateInstanceEntity entity, Integer nearExpireDays,Map<String, StoreCertificatePhotoEntity> templateMap) {
|
||||
Integer isLongTerm = entity.getIsLongTerm();
|
||||
Date expireDate = entity.getExpireDate();
|
||||
String certificateType = entity.getCertificateType();
|
||||
String templateId = entity.getTemplateId();
|
||||
StoreCertificatePhotoEntity storeCertificatePhotoEntity = templateMap.get(templateId);
|
||||
if(CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType().equals(certificateType) &&//自定义证照,如果临期提醒设置为0,则计算为正常
|
||||
Objects.nonNull(storeCertificatePhotoEntity) &&
|
||||
Integer.valueOf(0).equals(nearExpireDays)){
|
||||
return STATUS_NORMAL;
|
||||
}
|
||||
if (Integer.valueOf(1).equals(isLongTerm)) {
|
||||
return STATUS_NORMAL;
|
||||
}
|
||||
if (expireDate == null) {
|
||||
return STATUS_MISSING;
|
||||
}
|
||||
Date today = DateUtil.beginOfDay(DateUtil.date());
|
||||
Date target = DateUtil.beginOfDay(expireDate);
|
||||
if (target.before(today)) {
|
||||
return STATUS_EXPIRED;
|
||||
}
|
||||
int threshold = nearExpireDays == null || nearExpireDays < 0 ? 0 : nearExpireDays;
|
||||
long daysDiff = DateUtil.betweenDay(today, target, false);
|
||||
return daysDiff <= threshold ? STATUS_NEAR_EXPIRE : STATUS_NORMAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算距离到期天数。
|
||||
*/
|
||||
private Integer calculateDaysToExpire(Date expireDate) {
|
||||
if (expireDate == null) {
|
||||
return null;
|
||||
}
|
||||
Date today = DateUtil.beginOfDay(DateUtil.date());
|
||||
Date target = DateUtil.beginOfDay(expireDate);
|
||||
return (int) DateUtil.betweenDay(today, target, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按通知频率判断是否发送。
|
||||
*/
|
||||
private boolean shouldSendByFrequency(Integer daysToExpire, Integer noticeFrequencyDays) {
|
||||
if (daysToExpire == null || daysToExpire < 0) {
|
||||
return false;
|
||||
}
|
||||
int frequency = noticeFrequencyDays == null || noticeFrequencyDays <= 0 ? DEFAULT_NOTICE_FREQUENCY_DAYS : noticeFrequencyDays;
|
||||
return frequency <= 1 || daysToExpire % frequency == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建临期通知文案。
|
||||
*/
|
||||
private NoticeMessage buildNearExpireMessage(CertificateInstanceEntity entity,
|
||||
NearExpireNoticeTask task,
|
||||
Map<String, OrganizeBaseInfoVO> organizeMap,
|
||||
boolean healthCertificateReceiverBySelf,
|
||||
Map<String,UserBaseInfoVO> userBaseInfoByUserId) {
|
||||
Integer daysToExpire = task.getDaysToExpire();
|
||||
String certificateName = resolveCertificateName(entity == null ? null : entity.getCertificateType(),task.getCertificateTemplateName());
|
||||
int remainDays = daysToExpire == null ? 0 : Math.max(daysToExpire, 0);
|
||||
|
||||
if(isHealthEmployeeCertificate(entity)){
|
||||
return buildHealthCertificateNearExpireMessage(entity,healthCertificateReceiverBySelf,userBaseInfoByUserId,remainDays);
|
||||
}
|
||||
|
||||
String subjectName = "-";
|
||||
if (entity != null && StrUtil.isNotBlank(entity.getSubjectId())) {
|
||||
OrganizeBaseInfoVO organize = organizeMap.get(StrUtil.trim(entity.getSubjectId()));
|
||||
if (organize != null && StrUtil.isNotBlank(organize.getName())) {
|
||||
subjectName = StrUtil.trim(organize.getName());
|
||||
}
|
||||
}
|
||||
String title = subjectName + certificateName + "即将到期,请及时处理";
|
||||
String content;
|
||||
if(remainDays <= 0){
|
||||
content = String.format("%s%s今天到期,请及时更新。", subjectName, certificateName);
|
||||
}else{
|
||||
content = String.format("%s%s离到期时间还有%s天,请及时更新。", subjectName, certificateName, remainDays);
|
||||
}
|
||||
return new NoticeMessage(title, content);
|
||||
}
|
||||
|
||||
private NoticeMessage buildHealthCertificateNearExpireMessage(CertificateInstanceEntity entity,
|
||||
boolean receiverBySelf,
|
||||
Map<String,UserBaseInfoVO> userBaseInfoByUserId,
|
||||
int remainDays) {
|
||||
if(receiverBySelf){
|
||||
return new NoticeMessage("您的健康证存在风险,请及时处理", buildHealthCertificateNearExpireContent(remainDays));
|
||||
}
|
||||
UserBaseInfoVO baseInfoVO = userBaseInfoByUserId.get(entity.getSubjectId());
|
||||
if(Objects.isNull(baseInfoVO)){
|
||||
log.error("buildNearExpireMessage 通知健康证所属负责人,该健康证的用户名称获取为空。entity:{}",entity);
|
||||
return null;
|
||||
}
|
||||
return new NoticeMessage("您管理组织的健康证存在风险,请及时处理", buildNearExpireContentByNoSelf(baseInfoVO.getUserName(),remainDays));
|
||||
}
|
||||
|
||||
private String buildHealthCertificateNearExpireContent(int remainDays) {
|
||||
if(remainDays <= 0){
|
||||
return "您的健康证今天过期,请及时更新健康证。";
|
||||
}
|
||||
return String.format("您的健康证离到期时间还有%s天,请及时更新健康证。", remainDays);
|
||||
}
|
||||
|
||||
private String buildNearExpireContentByNoSelf(String username, int remainDays) {
|
||||
if(remainDays <= 0){
|
||||
return String.format("%s的健康证今天过期,请及时更新健康证。",username);
|
||||
}
|
||||
return String.format("%s的健康证离到期时间还有%s天,请及时更新健康证。",username,remainDays);
|
||||
}
|
||||
|
||||
/**
|
||||
* 证照类型转中文名称。
|
||||
*/
|
||||
private String resolveCertificateName(String certificateType,String certificateTemplateName) {
|
||||
if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.HEALTH_CERTIFICATE.getType())) {
|
||||
return "健康证";
|
||||
}
|
||||
if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.BUSINESS_LICENSE.getType())) {
|
||||
return "营业执照";
|
||||
}
|
||||
if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.HYGIENE_LICENSE.getType())) {
|
||||
return "食品经营许可证";
|
||||
}
|
||||
if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType())) {
|
||||
return certificateTemplateName;
|
||||
}
|
||||
return "证照";
|
||||
}
|
||||
|
||||
/**
|
||||
* 临期通知任务。
|
||||
*/
|
||||
@Data
|
||||
private static class NearExpireNoticeTask {
|
||||
private final CertificateInstanceEntity entity;
|
||||
private final Integer daysToExpire;
|
||||
private final WarningNoticeVO warningNoticeVO;
|
||||
private final String certificateTemplateName;
|
||||
|
||||
private NearExpireNoticeTask(CertificateInstanceEntity entity, Integer daysToExpire, WarningNoticeVO warningNoticeVO,String certificateTemplateName) {
|
||||
this.entity = entity;
|
||||
this.daysToExpire = daysToExpire;
|
||||
this.warningNoticeVO = warningNoticeVO;
|
||||
this.certificateTemplateName = certificateTemplateName;
|
||||
}
|
||||
|
||||
private CertificateInstanceEntity getEntity() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
private Integer getDaysToExpire() {
|
||||
return daysToExpire;
|
||||
}
|
||||
|
||||
private WarningNoticeVO getWarningNoticeVO() {
|
||||
return warningNoticeVO;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知文案。
|
||||
*/
|
||||
private static class NoticeMessage {
|
||||
private final String title;
|
||||
private final String content;
|
||||
|
||||
private NoticeMessage(String title, String content) {
|
||||
this.title = title;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
private String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
private String getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package jnpf.certificate.controller.app;
|
||||
|
||||
import jnpf.base.ActionResult;
|
||||
import jnpf.certificate.service.CertificateInstanceService;
|
||||
import jnpf.certificate.service.CertificateManageService;
|
||||
import jnpf.model.certificate.req.app.CertificateAppBusinessLicenseUpdateReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppHealthCertificateUpdateReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppHygieneLicenseUpdateReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppStoreCustomUpdateReq;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppCertificateDetailVO;
|
||||
import jnpf.model.certificate.vo.app.HealthCertificateDetailVO;
|
||||
import jnpf.model.certificate.vo.app.HealthCertificateVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* App端证照管理控制器。
|
||||
*/
|
||||
@RestController
|
||||
@Validated
|
||||
@RequestMapping("/app/certificate-manage")
|
||||
public class AppCertificateManageController {
|
||||
|
||||
/**
|
||||
* App端证照管理服务。
|
||||
*/
|
||||
@Autowired
|
||||
private CertificateManageService certificateManageService;
|
||||
@Autowired
|
||||
private CertificateInstanceService certificateInstanceService;
|
||||
|
||||
/**
|
||||
* 根据证照实例ID查询证照详情。
|
||||
* 返回结果中包含certificateType,并按类型返回不同明细对象。
|
||||
*
|
||||
* @param certificateInstanceId 证照实例ID
|
||||
* @return 证照详情
|
||||
*/
|
||||
@GetMapping("/query-info")
|
||||
public ActionResult<CertificateAppCertificateDetailVO> queryInfo(@RequestParam("certificateInstanceId")
|
||||
@NotBlank(message = "证照实例ID不能为空")
|
||||
String certificateInstanceId) {
|
||||
return ActionResult.success(certificateManageService.queryInfo(certificateInstanceId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新健康证。
|
||||
*
|
||||
* @param req 更新参数
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PutMapping("/update-health")
|
||||
public ActionResult<Void> updateHealth(@Validated @RequestBody CertificateAppHealthCertificateUpdateReq req) {
|
||||
certificateManageService.updateHealthCertificate(req);
|
||||
return ActionResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询某个人的健康证信息
|
||||
*
|
||||
* @return 健康证信息
|
||||
*/
|
||||
@GetMapping("/query-health-certificate/{userId}")
|
||||
public ActionResult<HealthCertificateVO> getHealthCertificateDetail(@PathVariable("userId") String userId) {
|
||||
Optional<HealthCertificateDetailVO> healthCertificateDetailVOOptional = certificateInstanceService.getHealthCertificateDetail(userId);
|
||||
return healthCertificateDetailVOOptional.map(h->ActionResult.success(HealthCertificateVO.of(h)))
|
||||
.orElseGet(() -> ActionResult.fail(404, "未查询到健康证信息"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新营业执照。
|
||||
*
|
||||
* @param req 更新参数
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PutMapping("/update-business-license")
|
||||
public ActionResult<Void> updateBusinessLicense(@Validated @RequestBody CertificateAppBusinessLicenseUpdateReq req) {
|
||||
certificateManageService.updateBusinessLicense(req);
|
||||
return ActionResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新食品经营许可证。
|
||||
*
|
||||
* @param req 更新参数
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PutMapping("/update-hygiene-license")
|
||||
public ActionResult<Void> updateHygieneLicense(@Validated @RequestBody CertificateAppHygieneLicenseUpdateReq req) {
|
||||
certificateManageService.updateHygieneLicense(req);
|
||||
return ActionResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新门店自定义证照。
|
||||
*
|
||||
* @param req 更新参数
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PutMapping("/update-store-custom")
|
||||
public ActionResult<Void> updateStoreCustom(@Valid @RequestBody CertificateAppStoreCustomUpdateReq req) {
|
||||
certificateManageService.updateStoreCustomCertificate(req);
|
||||
return ActionResult.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package jnpf.certificate.controller.app;
|
||||
|
||||
import jnpf.base.ActionResult;
|
||||
import jnpf.certificate.service.CertificateAppReminderService;
|
||||
import jnpf.model.certificate.req.app.CertificateAppBatchRemindReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppSingleRemindReq;
|
||||
import jnpf.util.UserProvider;
|
||||
import jnpf.util.context.ThreadContext;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.redisson.Redisson;
|
||||
import org.redisson.api.RLock;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* App端证照提醒控制器。
|
||||
*/
|
||||
@RestController
|
||||
@Validated
|
||||
@RequestMapping("/app/certificate-reminder")
|
||||
public class AppCertificateReminderController {
|
||||
|
||||
/**
|
||||
* 证照提醒服务。
|
||||
*/
|
||||
@Autowired
|
||||
private CertificateAppReminderService certificateAppReminderService;
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor commonExecutor;
|
||||
@Autowired
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
private static final String REMIND_LOCK_KEY_PREFIX = "certificate-reminder:";
|
||||
|
||||
/**
|
||||
* 一键提醒。
|
||||
*
|
||||
* @param req 提醒参数(主体类型)
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PostMapping("/batch-remind")
|
||||
public ActionResult<Void> batchRemind(@Validated @RequestBody CertificateAppBatchRemindReq req) {
|
||||
Boolean succ = stringRedisTemplate.opsForValue().setIfAbsent(buildRemindLockKey(),"1", 10, TimeUnit.SECONDS);
|
||||
if(Objects.isNull(succ) || !succ){
|
||||
return ActionResult.fail("操作太快了,请稍后在操作!");
|
||||
}
|
||||
//可能数据过多,但由于内部过滤数据,无法通过单独的线程执行,所以先暂时这样。
|
||||
certificateAppReminderService.batchRemind(req);
|
||||
return ActionResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个提醒。
|
||||
*
|
||||
* @param req 提醒参数(证照实例ID)
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PostMapping("/single-remind")
|
||||
public ActionResult<Void> singleRemind(@Validated @RequestBody CertificateAppSingleRemindReq req) {
|
||||
Boolean succ = stringRedisTemplate.opsForValue().setIfAbsent(buildRemindLockKey(),"1", 10, TimeUnit.SECONDS);
|
||||
if(Objects.isNull(succ) || !succ){
|
||||
return ActionResult.fail("操作太快了,请稍后在操作!");
|
||||
}
|
||||
certificateAppReminderService.singleRemind(req);
|
||||
return ActionResult.success();
|
||||
}
|
||||
|
||||
private String buildRemindLockKey() {
|
||||
String userId = UserProvider.getLoginUserId();
|
||||
return REMIND_LOCK_KEY_PREFIX + userId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package jnpf.certificate.controller.app;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import jnpf.base.ActionResult;
|
||||
import jnpf.base.vo.PageListVO;
|
||||
import jnpf.certificate.service.CertificateAppRiskService;
|
||||
import jnpf.model.certificate.req.app.CertificateAppEmployeeRiskQueryReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppRiskChartReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppStoreRiskQueryReq;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppEmployeeRiskVO;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppRiskChartVO;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppRiskReminderCountVO;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppStoreRiskVO;
|
||||
import jnpf.util.FtbUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* App端证照风险管理控制器。
|
||||
*/
|
||||
@RestController
|
||||
@Validated
|
||||
@RequestMapping("/app/certificate-risk")
|
||||
public class AppCertificateRiskController {
|
||||
|
||||
/**
|
||||
* 证照风险服务。
|
||||
*/
|
||||
@Autowired
|
||||
private CertificateAppRiskService certificateAppRiskService;
|
||||
|
||||
/**
|
||||
* 查询风险图表统计。
|
||||
*
|
||||
* @param req 查询参数(组织门店、证照类型)
|
||||
* @return 风险图表统计数据
|
||||
*/
|
||||
@GetMapping("/query-chart")
|
||||
public ActionResult<CertificateAppRiskChartVO> queryChart(@Valid CertificateAppRiskChartReq req) {
|
||||
return ActionResult.success(certificateAppRiskService.queryChart(req));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询员工证照风险列表。
|
||||
*
|
||||
* @param req 查询参数(组织门店、分页)
|
||||
* @return 员工证照风险分页结果(含分页信息与列表)
|
||||
*/
|
||||
@GetMapping("/query-employee-page")
|
||||
public ActionResult<PageListVO<CertificateAppEmployeeRiskVO>> queryEmployeePage(@Valid CertificateAppEmployeeRiskQueryReq req) {
|
||||
PageInfo<CertificateAppEmployeeRiskVO> pageInfo = certificateAppRiskService.queryEmployeePage(req);
|
||||
// 返回员工证照风险分页数据。
|
||||
return ActionResult.page(pageInfo.getList(), FtbUtil.getPagination(pageInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询门店证照风险列表。
|
||||
* 默认仅查询缺失、过期、临期状态(1、2、3)。
|
||||
*
|
||||
* @param req 查询参数(组织门店、证照类型、分页)
|
||||
* @return 门店证照风险分页结果
|
||||
*/
|
||||
@GetMapping("/query-store-page")
|
||||
public ActionResult<PageListVO<CertificateAppStoreRiskVO>> queryStorePage(@Valid CertificateAppStoreRiskQueryReq req) {
|
||||
PageInfo<CertificateAppStoreRiskVO> pageInfo = certificateAppRiskService.queryStorePage(req);
|
||||
return ActionResult.page(pageInfo.getList(), FtbUtil.getPagination(pageInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询风险提醒总数量。
|
||||
* 无入参,统计缺失、临期、过期的员工风险数量和组织风险数量。
|
||||
*
|
||||
* @return 风险提醒数量统计
|
||||
*/
|
||||
@GetMapping("/query-reminder-count")
|
||||
public ActionResult<CertificateAppRiskReminderCountVO> queryReminderCount(@RequestParam(value = "orgId",required = false)String orgId) {
|
||||
return ActionResult.success(certificateAppRiskService.queryRiskReminderCount(orgId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package jnpf.certificate.helper;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.handler.codec.http.HttpUtil;
|
||||
import jnpf.ImRobotApi;
|
||||
import jnpf.base.ActionResult;
|
||||
import jnpf.from.*;
|
||||
import jnpf.util.JsonUtil;
|
||||
import jnpf.util.StringUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 证照提醒消息发送工具。
|
||||
* 参考健康证定时任务的消息结构,实现按用户集合发送提醒。
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class NoticeHelper {
|
||||
|
||||
private static final String APP_NAME = "证照管理";
|
||||
public static final String MP_ID_CERTIFICATE = "__UNI__C5F3D72";
|
||||
public static final String URL_CERTIFICATE = "/pages/license/detail?id=%s&label=健康证";
|
||||
public static final String BUTTON_NAME_HANDLER = "去处理";
|
||||
private static final String LOGO_URL = "https://jnpf-resource-1304460613.cos.ap-chengdu.myqcloud.com/jnpf-resources/UserAvatar/665ecb69e4b0ae5df114c87a.png";
|
||||
private static final int SEND_BATCH_SIZE = 450;
|
||||
|
||||
@Resource
|
||||
private ImRobotApi imRobotApi;
|
||||
|
||||
/**
|
||||
* 发送机器人提醒消息。
|
||||
*
|
||||
* @param userIds 接收用户ID集合
|
||||
* @param tenantId 租户ID
|
||||
* @param title 消息标题
|
||||
* @param content 消息内容
|
||||
* @param buttonName 跳转按钮名称(可为空)
|
||||
* @param url 跳转地址(可为空)
|
||||
* @param mpId 小程序ID(可为空)
|
||||
*/
|
||||
public void sendMessage(Collection<String> userIds, String tenantId, String title, String content,
|
||||
String buttonName, String url, String mpId) {
|
||||
List<String> targetUserIds = normalizeUserIds(userIds);
|
||||
if (CollUtil.isEmpty(targetUserIds) || StrUtil.isBlank(tenantId) || StrUtil.isBlank(title) || StrUtil.isBlank(content)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SingleSendRobotNoticeForm form = new SingleSendRobotNoticeForm();
|
||||
form.setRobotTypeEnum(ImRobotTypeEnum.FTB_XZS);
|
||||
form.setTenantId(tenantId);
|
||||
if (StrUtil.isNotBlank(mpId)) {
|
||||
form.setMessageAttributionRobotMpId(mpId);
|
||||
}
|
||||
form.setRobotNoticeDataForm(buildNoticeData(title, content, buttonName, url, mpId));
|
||||
|
||||
for (int start = 0; start < targetUserIds.size(); start += SEND_BATCH_SIZE) {
|
||||
int end = Math.min(start + SEND_BATCH_SIZE, targetUserIds.size());
|
||||
form.setToUserIds(targetUserIds.subList(start, end));
|
||||
ActionResult<?> actionResult = imRobotApi.sendSingleRobotNotice(form);
|
||||
if (actionResult == null || !Integer.valueOf(200).equals(actionResult.getCode())) {
|
||||
log.error("证照提醒消息发送失败,req={},result={}", JSONUtil.toJsonStr(form), JSONUtil.toJsonStr(actionResult));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建消息体。
|
||||
*/
|
||||
private SendRobotNoticeDataForm buildNoticeData(String title, String content, String buttonName, String url, String mpId) {
|
||||
SendRobotNoticeDataForm noticeData = new SendRobotNoticeDataForm();
|
||||
noticeData.setLogo(LOGO_URL);
|
||||
noticeData.setAppName(APP_NAME);
|
||||
noticeData.setTitle(title);
|
||||
noticeData.setContent(content);
|
||||
|
||||
if (StrUtil.isNotBlank(buttonName) && StrUtil.isNotBlank(url) && StrUtil.isNotBlank(mpId)) {
|
||||
JumpUrlListModel jumpUrl = new JumpUrlListModel();
|
||||
jumpUrl.setDisplayMethodEnum(JumpUrlListModel.DisplayMethodEnum.TRUE);
|
||||
jumpUrl.setButtonName(buttonName);
|
||||
jumpUrl.setReqMethod(JumpUrlListModel.ReqMethodEnum.GET);
|
||||
jumpUrl.setType(1);
|
||||
jumpUrl.setUrl(url);
|
||||
jumpUrl.setMpId(mpId);
|
||||
//fixme 有空改为传参,不这样处理
|
||||
if(url.contains("?")){
|
||||
jumpUrl.setMiniAppUrl(buildMiniAppUrl(mpId,url));
|
||||
}
|
||||
LinkedList<JumpUrlListModel> jumpUrlList = new LinkedList<>();
|
||||
jumpUrlList.add(jumpUrl);
|
||||
noticeData.setJumpUrlList(jumpUrlList);
|
||||
}
|
||||
return noticeData;
|
||||
}
|
||||
|
||||
private String buildUrl(String url) {
|
||||
if(!url.contains("?")){
|
||||
return url;
|
||||
}
|
||||
return url.substring(0,url.indexOf("?"));
|
||||
}
|
||||
|
||||
private MiniAppUrl buildMiniAppUrl(String mpId,String url) {
|
||||
String params = url.substring(url.indexOf("?")+1);
|
||||
if(StringUtil.isBlank(params)){
|
||||
return null;
|
||||
}
|
||||
JSONObject mpParam = new JSONObject();
|
||||
for (String kv:params.split("&")){
|
||||
String [] kvs = kv.split("=");
|
||||
if(kvs.length != 2){
|
||||
return null;
|
||||
}
|
||||
mpParam.set(kvs[0],kvs[1]);
|
||||
}
|
||||
return MiniAppUrl.builder()
|
||||
.mpId(mpId)
|
||||
.mpPage(buildUrl(url))
|
||||
.mpParam(mpParam.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 规整接收用户集合:去空、去重、去首尾空格。
|
||||
*/
|
||||
private List<String> normalizeUserIds(Collection<String> userIds) {
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
Set<String> userIdSet = new LinkedHashSet<>();
|
||||
for (String userId : userIds) {
|
||||
String trimUserId = StrUtil.trim(userId);
|
||||
if (StrUtil.isNotBlank(trimUserId)) {
|
||||
userIdSet.add(trimUserId);
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(userIdSet);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
package jnpf.certificate.helper;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import jnpf.base.ActionResult;
|
||||
import jnpf.permission.StoreApi;
|
||||
import jnpf.permission.V2OrganizeApi;
|
||||
import jnpf.permission.V2PositionApi;
|
||||
import jnpf.permission.V2UserApi;
|
||||
import jnpf.permission.dto.v2.organzie.BaseOrganizeQueryDTO;
|
||||
import jnpf.permission.dto.v2.user.QueryUserOrgRelationDTO;
|
||||
import jnpf.permission.dto.v2.user.QueryUserBaseRelationDTO;
|
||||
import jnpf.permission.eum.v2.UserWorkStatusEnums;
|
||||
import jnpf.permission.vo.store.StoreInfoDetailVO;
|
||||
import jnpf.permission.vo.v2.organzie.OrganizeBaseInfoVO;
|
||||
import jnpf.permission.vo.v2.organzie.OrganizeGeneralDetailVO;
|
||||
import jnpf.permission.vo.v2.position.PositionBaseInfoVO;
|
||||
import jnpf.permission.vo.v2.user.UserBaseInfoVO;
|
||||
import jnpf.permission.vo.v2.user.UserBoundVO;
|
||||
import jnpf.permission.vo.v2.user.UserBaseRelationSimpleVO;
|
||||
import jnpf.util.StringUtil;
|
||||
import jnpf.util.UserProvider;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class OrganizationHelper {
|
||||
@Autowired
|
||||
private V2OrganizeApi v2OrganizeApi;
|
||||
@Autowired
|
||||
private V2PositionApi v2PositionApi;
|
||||
@Autowired
|
||||
private V2UserApi v2UserApi;
|
||||
@Autowired
|
||||
private StoreApi storeApi;
|
||||
|
||||
public List<String> getUserIdsByOrgId(String orgId){
|
||||
if(StringUtil.isBlank(orgId)){
|
||||
return Collections.emptyList();
|
||||
}
|
||||
ActionResult<List<String>> actionResult = v2UserApi.listUserIdsByOrganizeId(orgId, UserProvider.getUser().getTenantId());
|
||||
return getDataByResult(actionResult,"v2UserApi.listUserIdsByOrganizeId",orgId)
|
||||
.orElse(Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据多个userId查询多个用户信息
|
||||
* @return
|
||||
*/
|
||||
public Map<String, UserBaseRelationSimpleVO> getUserBaseRelationMapByOrgId(String orgId){
|
||||
if(StringUtil.isBlank(orgId)){
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
QueryUserBaseRelationDTO dto = new QueryUserBaseRelationDTO();
|
||||
dto.setTenantId(UserProvider.getUser().getTenantId());
|
||||
dto.setOrgIds(Collections.singletonList(orgId));
|
||||
return getUserBaseRelationMapByUserIds(dto);
|
||||
}
|
||||
|
||||
public Map<String, UserBaseRelationSimpleVO> getUserBaseRelationMapByUserIds(Collection<String> userIds){
|
||||
if(CollectionUtil.isEmpty(userIds)){
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
QueryUserBaseRelationDTO dto = new QueryUserBaseRelationDTO();
|
||||
dto.setTenantId(UserProvider.getUser().getTenantId());
|
||||
dto.setUserIds(userIds);
|
||||
return getUserBaseRelationMapByUserIds(dto);
|
||||
}
|
||||
|
||||
public Map<String, UserBaseRelationSimpleVO> getUserBaseRelationMapByUserIds(QueryUserBaseRelationDTO dto){
|
||||
if(Objects.isNull(dto)){
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
dto.setTenantId(UserProvider.getUser().getTenantId());
|
||||
ActionResult<List<UserBaseRelationSimpleVO>> actionResult = v2UserApi.queryUserBaseRelationList(dto);
|
||||
Optional<List<UserBaseRelationSimpleVO>> listOptional = getDataByResult(actionResult,"v2UserApi.queryUserBaseRelationList",dto);
|
||||
return listOptional.map(l->l.stream()
|
||||
.filter(item -> StringUtil.isNotBlank(item.getUserId()))
|
||||
.collect(Collectors.toMap(UserBaseRelationSimpleVO::getUserId, item -> item, (v1, v2) -> v1)))
|
||||
.orElse(Collections.emptyMap());
|
||||
}
|
||||
|
||||
public Map<String, UserBoundVO> getUserPrimaryBoundBatch(Collection<String> userIds) {
|
||||
if(CollectionUtil.isEmpty(userIds)){
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
ActionResult<List<UserBoundVO>> actionResult = v2UserApi.getUserPrimaryBoundBatch(new ArrayList<>(userIds),UserProvider.getUser().getTenantId());
|
||||
Optional<List<UserBoundVO>> listOptional = getDataByResult(actionResult,"v2UserApi.getUserPrimaryBoundBatch",userIds);
|
||||
return listOptional
|
||||
.map(l->l.stream()
|
||||
.collect(Collectors.toMap(UserBoundVO::getId, item -> item, (v1, v2) -> v1)))
|
||||
.orElse(Collections.emptyMap());
|
||||
}
|
||||
|
||||
public Map<String,UserBoundVO> getAllUserInfoBatch(Collection<String> userIds){
|
||||
if(CollectionUtil.isEmpty(userIds)){
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
ActionResult<List<UserBoundVO>> actionResult = v2UserApi.getAllUserInfoBatch(new ArrayList<>(userIds),UserProvider.getUser().getTenantId());
|
||||
Optional<List<UserBoundVO>> listOptional = getDataByResult(actionResult,"v2UserApi.getAllUserInfoBatch",userIds);
|
||||
return listOptional.map(l->l.stream()
|
||||
.collect(Collectors.toMap(UserBoundVO::getId,u->u)))
|
||||
.orElse(Collections.emptyMap());
|
||||
}
|
||||
|
||||
public Map<String,List<UserBoundVO>> buildUserBoundVOs(Collection<String> orgIds,
|
||||
boolean hasChild,
|
||||
List<UserWorkStatusEnums> notUserWorkStatusEnumsList,
|
||||
String tenantId) {
|
||||
if(CollectionUtil.isEmpty(orgIds)){
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
ActionResult<List<UserBoundVO>> actionResult = v2UserApi.listTargetOrganizesOrHaveChild(new ArrayList<>(orgIds),hasChild,notUserWorkStatusEnumsList,tenantId);
|
||||
|
||||
Optional<List<UserBoundVO>> listOptional = getDataByResult(actionResult,"v2UserApi.listTargetOrganizesOrHaveChild",orgIds,hasChild,notUserWorkStatusEnumsList,tenantId);
|
||||
return listOptional.map(l->l.stream()
|
||||
.collect(Collectors.groupingBy(UserBoundVO::getOrganizeId)))
|
||||
.orElse(Collections.emptyMap());
|
||||
}
|
||||
|
||||
public Map<String, PositionBaseInfoVO> buildPositionBaseInfos(Collection<String> positionIds) {
|
||||
if(CollectionUtil.isEmpty(positionIds)){
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
String tenantId = UserProvider.getUser().getTenantId();
|
||||
ActionResult<List<PositionBaseInfoVO>> actionResult = v2PositionApi.listPositionBaseInfoByIds(new ArrayList<>(positionIds), tenantId);
|
||||
|
||||
Optional<List<PositionBaseInfoVO>> positionBaseInfoVOS = getDataByResult(actionResult,"v2PositionApi.listPositionBaseInfoByIds",positionIds);
|
||||
return positionBaseInfoVOS.map(l->l.stream()
|
||||
.collect(Collectors.toMap(PositionBaseInfoVO::getId, item -> item, (v1, v2) -> v1)))
|
||||
.orElse(Collections.emptyMap());
|
||||
}
|
||||
|
||||
public Map<String, OrganizeGeneralDetailVO> buildOrganizeGenerals(Collection<String> orgIds) {
|
||||
if(CollectionUtil.isEmpty(orgIds)){
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
ActionResult<List<OrganizeGeneralDetailVO>> listActionResult = v2OrganizeApi.organizesByOrganizeIds(new ArrayList<>(orgIds));
|
||||
|
||||
Optional<List<OrganizeGeneralDetailVO>> listOptional = getDataByResult(listActionResult,"v2OrganizeApi.organizesByOrganizeIds",orgIds);
|
||||
return listOptional.map(l->l.stream()
|
||||
.collect(Collectors.toMap(OrganizeGeneralDetailVO::getId, item -> item, (v1, v2) -> v1)))
|
||||
.orElse(Collections.emptyMap());
|
||||
}
|
||||
|
||||
public Map<String, List<StoreInfoDetailVO>> buildStoreInfoDetailsByFranchiseeId(String ...franchiseeId) {
|
||||
ActionResult<List<StoreInfoDetailVO>> listActionResult = storeApi.listInfoDetailsByFranchiseeIds(List.of(franchiseeId));
|
||||
|
||||
Optional<List<StoreInfoDetailVO>> listOptional = getDataByResult(listActionResult,"storeApi.listInfoDetailsByFranchiseeIds", (Object) franchiseeId);
|
||||
return listOptional.map(storeInfoDetailVOS -> storeInfoDetailVOS.stream()
|
||||
.collect(Collectors.groupingBy(StoreInfoDetailVO::getFranchiseeId)))
|
||||
.orElse(Collections.emptyMap());
|
||||
}
|
||||
|
||||
public Map<String,List<String>> buildUserIdsByOrgIds(Collection<String> orgIds,boolean orgIdsEmptyQueryAll,String storeTag){
|
||||
if(CollectionUtil.isEmpty(orgIds) && !orgIdsEmptyQueryAll){
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
QueryUserOrgRelationDTO queryUserOrgRelationDTO = new QueryUserOrgRelationDTO(orgIdsEmptyQueryAll,orgIds,UserProvider.getUser().getTenantId(),storeTag);
|
||||
ActionResult<Map<String, List<String>>> actionResult = v2UserApi.listUserIdsByQueryDTO(queryUserOrgRelationDTO);
|
||||
return getDataByResult(actionResult,"v2UserApi.listUserIdsByQueryDTO",queryUserOrgRelationDTO)
|
||||
.orElse(Collections.emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户id获取组织id。key为userId,value为组织id
|
||||
* @param userIds
|
||||
* @return
|
||||
*/
|
||||
public Map<String,String> buildOrgIdsByUserIds(Collection<String> userIds,String tenantId){
|
||||
if(CollectionUtil.isEmpty(userIds)){
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
ActionResult<Map<String, String>> actionResult = v2UserApi.listOrganizeIdsByUserIds(userIds,tenantId);
|
||||
return getDataByResult(actionResult,"v2UserApi.listOrganizeIdsByUserIds",userIds)
|
||||
.orElse(Collections.emptyMap());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据用户id获取用户基础信息,用户id,名称,组织id
|
||||
* @param userIds
|
||||
* @return
|
||||
*/
|
||||
public Map<String, UserBaseInfoVO> buildUserBaseInfoByUserIds(Collection<String> userIds, String tenantId){
|
||||
if(CollectionUtil.isEmpty(userIds)){
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
ActionResult<List<UserBaseInfoVO>> actionResult = v2UserApi.listUserBaseInfoByUserIds(userIds,tenantId);
|
||||
return getDataByResult(actionResult,"v2UserApi.listUserBaseInfoByUserIds",userIds,tenantId)
|
||||
.orElse(Collections.emptyList())
|
||||
.stream()
|
||||
.collect(Collectors.toMap(UserBaseInfoVO::getUserId, item -> item));
|
||||
}
|
||||
|
||||
public Map<String, OrganizeBaseInfoVO> buildBaseOrganizeVO(Collection<String> orgIds,String tenantId) {
|
||||
if(CollectionUtil.isEmpty(orgIds)){
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
ActionResult<List<OrganizeBaseInfoVO>> listActionResult = v2OrganizeApi.baseOrganizesByOrganizeIds(orgIds,tenantId);
|
||||
|
||||
Optional<List<OrganizeBaseInfoVO>> listOptional = getDataByResult(listActionResult,"v2OrganizeApi.baseOrganizesByOrganizeIds",orgIds);
|
||||
return listOptional.map(l->l.stream()
|
||||
.collect(Collectors.toMap(OrganizeBaseInfoVO::getId, item -> item)))
|
||||
.orElse(Collections.emptyMap());
|
||||
}
|
||||
|
||||
|
||||
public Map<String, OrganizeBaseInfoVO> buildBaseOrganizeVO(Collection<String> orgIds,String storeTag,String tenantId) {
|
||||
if(CollectionUtil.isEmpty(orgIds) && StringUtil.isBlank(storeTag)){
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
BaseOrganizeQueryDTO dto = new BaseOrganizeQueryDTO();
|
||||
dto.setOrganizeIds(orgIds);
|
||||
dto.setStoreTag(storeTag);
|
||||
dto.setTenantId(tenantId);
|
||||
dto.setQueryParentName(true);
|
||||
ActionResult<List<OrganizeBaseInfoVO>> listActionResult = v2OrganizeApi.baseOrganizesByQueryDTO(dto);
|
||||
|
||||
Optional<List<OrganizeBaseInfoVO>> listOptional = getDataByResult(listActionResult,"v2OrganizeApi.baseOrganizesByOrganizeIds",orgIds);
|
||||
return listOptional.map(l->l.stream()
|
||||
.collect(Collectors.toMap(OrganizeBaseInfoVO::getId, item -> item)))
|
||||
.orElse(Collections.emptyMap());
|
||||
}
|
||||
|
||||
private <T>Optional<T> getDataByResult(ActionResult<T> actionResult,String url,Object...params) {
|
||||
if(Objects.isNull(actionResult)){
|
||||
log.error("{} return null.params:{}",url,params);
|
||||
return Optional.empty();
|
||||
}
|
||||
if(actionResult.getCode() != 200){
|
||||
log.error("{} return code is not 200.actionResult:{},params:{}",url,actionResult,params);
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(actionResult.getData());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package jnpf.certificate.mapper;
|
||||
|
||||
import jnpf.certificate.model.CertificateAppReminderRecord;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* App端证照提醒Mapper。
|
||||
*/
|
||||
public interface CertificateAppReminderMapper {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package jnpf.certificate.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.fantaibao.permission.annotation.DataScope;
|
||||
import com.fantaibao.permission.enums.FilterWhereTypeEnum;
|
||||
import jnpf.model.certificate.req.app.CertificateAppEmployeeRiskQueryReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppRiskChartReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppStoreRiskQueryReq;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppEmployeeRiskVO;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppStatusCountVO;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppStoreRiskItemVO;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppStoreRiskVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public interface CertificateAppRiskMapper {
|
||||
|
||||
@DataScope(tableField = "F_SubjectId",tableAlias = "ci")
|
||||
List<CertificateAppStatusCountVO> queryHealthRiskStatusCount(@Param("req") CertificateAppRiskChartReq req);
|
||||
@DataScope(tableFieldOrg = "F_SubjectId",tableAlias = "ci",type = FilterWhereTypeEnum.ORGANIZATION_FILTER)
|
||||
List<CertificateAppStatusCountVO> queryStoreRiskStatusCount(@Param("req") CertificateAppRiskChartReq req);
|
||||
|
||||
@DataScope(tableField = "F_SubjectId",tableAlias = "ci")
|
||||
Page<CertificateAppEmployeeRiskVO> queryEmployeeRiskPageByUserIds(@Param("page") Page<CertificateAppEmployeeRiskVO> page,
|
||||
@Param("userIds") Collection<String> userIds);
|
||||
@DataScope(tableFieldOrg = "F_SubjectId",tableAlias = "ci",type = FilterWhereTypeEnum.ORGANIZATION_FILTER)
|
||||
Page<CertificateAppStoreRiskVO> queryStoreRiskPage(@Param("page") Page<CertificateAppStoreRiskVO> page,
|
||||
@Param("req") CertificateAppStoreRiskQueryReq req);
|
||||
|
||||
List<CertificateAppStoreRiskItemVO> queryStoreRiskDetailList(@Param("subjectIds") Collection<String> subjectIds);
|
||||
|
||||
@DataScope(tableField = "F_SubjectId",tableAlias = "ci")
|
||||
Long queryEmployeeRiskTotalCount(@Param("userIds") Collection<String> userIds);
|
||||
|
||||
@DataScope(tableFieldOrg = "F_SubjectId",tableAlias = "ci",type = FilterWhereTypeEnum.ORGANIZATION_FILTER)
|
||||
Long queryOrganizationRiskTotalCount(@Param("orgIds") Collection<String> orgIds);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package jnpf.certificate.mapper;
|
||||
|
||||
import jnpf.base.mapper.SuperMapper;
|
||||
import jnpf.model.certificate.po.CertificateBusinessLicenseExtEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 营业执照扩展Mapper。
|
||||
*/
|
||||
public interface CertificateBusinessLicenseExtMapper extends SuperMapper<CertificateBusinessLicenseExtEntity> {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package jnpf.certificate.mapper;
|
||||
|
||||
import jnpf.base.mapper.SuperMapper;
|
||||
import jnpf.model.certificate.po.CertificateHygieneLicenseExtEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 食品许可证扩展Mapper。
|
||||
*/
|
||||
public interface CertificateHygieneLicenseExtMapper extends SuperMapper<CertificateHygieneLicenseExtEntity> {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package jnpf.certificate.mapper;
|
||||
|
||||
import jnpf.base.mapper.SuperMapper;
|
||||
import jnpf.model.certificate.po.CertificateInstanceItemEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 证照实例明细Mapper。
|
||||
*/
|
||||
public interface CertificateInstanceItemMapper extends SuperMapper<CertificateInstanceItemEntity> {
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package jnpf.certificate.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.fantaibao.permission.annotation.DataScope;
|
||||
import com.fantaibao.permission.enums.FilterWhereTypeEnum;
|
||||
import jnpf.base.mapper.SuperMapper;
|
||||
import jnpf.model.certificate.dto.EmployeeOrganizeDTO;
|
||||
import jnpf.model.certificate.dto.HealthCertificateStatusDTO;
|
||||
import jnpf.model.certificate.po.CertificateInstanceEntity;
|
||||
import jnpf.model.certificate.req.CertificateHealthManageQueryReq;
|
||||
import jnpf.model.certificate.req.CertificateStoreManageQueryReq;
|
||||
import jnpf.model.certificate.vo.CertificateHealthDashboardStatusStatVO;
|
||||
import jnpf.model.certificate.vo.CertificateHealthManageVO;
|
||||
import jnpf.model.certificate.vo.CertificateStoreCustomStatusTableVO;
|
||||
import jnpf.model.certificate.vo.CertificateStoreManageVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 证照实例Mapper。
|
||||
*/
|
||||
public interface CertificateInstanceMapper extends SuperMapper<CertificateInstanceEntity> {
|
||||
|
||||
/**
|
||||
* 健康证管理分页查询。
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param req 查询参数
|
||||
* @param nearExpireDays 临期天数阈值
|
||||
* @return 分页结果
|
||||
*/
|
||||
@DataScope(tableAlias = "fci",tableField = "F_SubjectId")
|
||||
Page<CertificateHealthManageVO> queryHealthManagePage(@Param("page") Page<CertificateHealthManageVO> page,
|
||||
@Param("req") CertificateHealthManageQueryReq req,
|
||||
@Param("nearExpireDays") Integer nearExpireDays);
|
||||
|
||||
@DataScope(tableAlias = "fci",tableField = "F_SubjectId")
|
||||
List<HealthCertificateStatusDTO> queryHealthCertificateStatus(@Param("userIds") Collection<String> userIds);
|
||||
|
||||
/**
|
||||
* 非健康证看板按状态直接聚合统计(不按组织分组)
|
||||
*
|
||||
* @param certificateType 证照类型
|
||||
* @param templateId 模板ID(仅门店自定义证照场景)
|
||||
* @param storeIds 门店IDs筛选
|
||||
* @param storeType 门店类型筛选
|
||||
* @return 状态聚合结果
|
||||
*/
|
||||
@DataScope(tableAlias = "fci", type = FilterWhereTypeEnum.STORE_FILTER, tableFieldOrg = "F_SubjectId")
|
||||
List<CertificateHealthDashboardStatusStatVO> queryStoreDashboardStatusStats(@Param("certificateType") String certificateType,
|
||||
@Param("templateId") String templateId,
|
||||
@Param("storeIds") List<String> storeIds,
|
||||
@Param("storeType") String storeType);
|
||||
|
||||
/**
|
||||
* 非健康证看板表格分页查询(按门店聚合)。
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param certificateType 证照类型
|
||||
* @param templateId 模板ID(仅门店自定义证照场景)
|
||||
* @param storeIds 门店IDs筛选
|
||||
* @param storeType 门店类型筛选
|
||||
* @return 门店表格分页数据
|
||||
*/
|
||||
@DataScope(tableAlias = "fci", type = FilterWhereTypeEnum.ORGANIZATION_FILTER, tableFieldOrg = "F_SubjectId")
|
||||
Page<CertificateStoreCustomStatusTableVO> queryStoreDashboardTablePage(@Param("page") Page<CertificateStoreCustomStatusTableVO> page,
|
||||
@Param("certificateType") String certificateType,
|
||||
@Param("templateId") String templateId,
|
||||
@Param("storeIds") List<String> storeIds,
|
||||
@Param("storeType") String storeType);
|
||||
|
||||
/**
|
||||
* 批量初始化员工健康证实例(仅插入不存在的数据)。
|
||||
*
|
||||
* @param entities 员工用户ID集合
|
||||
* @return 插入条数
|
||||
*/
|
||||
int batchInitHealthCertificate(@Param("entities") Collection<CertificateInstanceEntity> entities);
|
||||
|
||||
/**
|
||||
* 根据id查询证照实例
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
CertificateInstanceEntity selectInstanceById(@Param("id") String id);
|
||||
|
||||
/**
|
||||
* 根据多个subjectType和多个status查询证照实例
|
||||
* @param subjectTypes
|
||||
* @param statuses
|
||||
* @return
|
||||
*/
|
||||
@DataScope(tableAlias = "fci",tableField = "F_SubjectId",type = FilterWhereTypeEnum.USER_FILTER)
|
||||
List<CertificateInstanceEntity> selectBySubjectTypesAndStatusesFilterUser(@Param("subjectTypes") Collection<Integer> subjectTypes,
|
||||
@Param("statuses") Collection<Integer> statuses,
|
||||
@Param("userIds") Collection<String> userIds);
|
||||
|
||||
/**
|
||||
* 根据多个subjectType和多个status查询证照实例
|
||||
* @param subjectTypes
|
||||
* @param statuses
|
||||
* @return
|
||||
*/
|
||||
@DataScope(tableAlias = "fci",tableFieldOrg = "F_SubjectId",type = FilterWhereTypeEnum.ORGANIZATION_FILTER)
|
||||
List<CertificateInstanceEntity> selectBySubjectTypesAndStatusesFilterOrganization(@Param("subjectTypes") Collection<Integer> subjectTypes,
|
||||
@Param("statuses") Collection<Integer> statuses,
|
||||
@Param("orgId") String orgId);
|
||||
|
||||
@DataScope(tableAlias = "fci",tableFieldOrg = "F_SubjectId",type = FilterWhereTypeEnum.ORGANIZATION_FILTER)
|
||||
Page<CertificateStoreManageVO> selectCertificateStoreManageVOPage(@Param("page") Page<CertificateStoreManageVO> page,
|
||||
@Param("queryReq")CertificateStoreManageQueryReq queryReq);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package jnpf.certificate.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* App证照提醒查询结果模型。
|
||||
*/
|
||||
@Data
|
||||
public class CertificateAppReminderRecord {
|
||||
|
||||
/**
|
||||
* 证照实例ID。
|
||||
*/
|
||||
private String certificateInstanceId;
|
||||
|
||||
/**
|
||||
* 证照类型。
|
||||
*/
|
||||
private String certificateType;
|
||||
|
||||
/**
|
||||
* 主体类型:1-员工,2-组织,3-门店。
|
||||
*/
|
||||
private Integer subjectType;
|
||||
|
||||
/**
|
||||
* 主体ID。
|
||||
*/
|
||||
private String subjectId;
|
||||
|
||||
/**
|
||||
* 证照状态:1-缺失,2-过期,3-临期,4-正常。
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 到期日期。
|
||||
*/
|
||||
private Date expireDate;
|
||||
|
||||
/**
|
||||
* 接收提醒的用户ID。
|
||||
*/
|
||||
private String receiverUserId;
|
||||
|
||||
/**
|
||||
* 主体名称(组织/门店名称)。
|
||||
*/
|
||||
private String subjectName;
|
||||
|
||||
/**
|
||||
* 组织权限匹配ID。
|
||||
*/
|
||||
private String orgPermissionId;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package jnpf.certificate.service;
|
||||
|
||||
import jnpf.model.certificate.req.app.CertificateAppBatchRemindReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppSingleRemindReq;
|
||||
|
||||
/**
|
||||
* App端证照提醒服务。
|
||||
*/
|
||||
public interface CertificateAppReminderService {
|
||||
|
||||
/**
|
||||
* 一键提醒。
|
||||
*
|
||||
* @param req 提醒参数
|
||||
*/
|
||||
void batchRemind(CertificateAppBatchRemindReq req);
|
||||
|
||||
/**
|
||||
* 单个提醒。
|
||||
*
|
||||
* @param req 提醒参数
|
||||
*/
|
||||
void singleRemind(CertificateAppSingleRemindReq req);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package jnpf.certificate.service;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import jnpf.model.certificate.req.app.CertificateAppEmployeeRiskQueryReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppRiskChartReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppStoreRiskQueryReq;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppEmployeeRiskVO;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppRiskChartVO;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppRiskReminderCountVO;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppStoreRiskVO;
|
||||
|
||||
/**
|
||||
* App 端证照风险服务。
|
||||
*/
|
||||
public interface CertificateAppRiskService {
|
||||
|
||||
/**
|
||||
* 查询证照风险图表统计。
|
||||
*
|
||||
* @param req 查询参数(组织门店、证照类型)
|
||||
* @return 图表统计结果
|
||||
*/
|
||||
CertificateAppRiskChartVO queryChart(CertificateAppRiskChartReq req);
|
||||
|
||||
/**
|
||||
* 分页查询员工证照风险列表(缺失、临期、过期)。
|
||||
*
|
||||
* @param req 查询参数(组织门店、分页)
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageInfo<CertificateAppEmployeeRiskVO> queryEmployeePage(CertificateAppEmployeeRiskQueryReq req);
|
||||
|
||||
/**
|
||||
* 分页查询门店证照风险列表(缺失、临期、过期)。
|
||||
*
|
||||
* @param req 查询参数(组织门店、证照类型、状态、分页)
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageInfo<CertificateAppStoreRiskVO> queryStorePage(CertificateAppStoreRiskQueryReq req);
|
||||
|
||||
/**
|
||||
* 查询风险提醒总数量(员工+组织)。
|
||||
*
|
||||
* @return 风险提醒数量统计
|
||||
*/
|
||||
CertificateAppRiskReminderCountVO queryRiskReminderCount(String orgId);
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package jnpf.certificate.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import jnpf.model.certificate.po.CertificateInstanceEntity;
|
||||
import jnpf.model.certificate.req.CertificateInstanceAddReq;
|
||||
import jnpf.model.certificate.req.CertificateHealthManageQueryReq;
|
||||
import jnpf.model.certificate.req.CertificateInstanceQueryReq;
|
||||
import jnpf.model.certificate.req.CertificateStoreManageQueryReq;
|
||||
import jnpf.model.certificate.req.CertificateStoreDashboardReq;
|
||||
import jnpf.model.certificate.req.CertificateSyncHealthReq;
|
||||
import jnpf.model.certificate.req.CertificateInstanceUpdateReq;
|
||||
import jnpf.model.certificate.vo.CertificateHealthManageVO;
|
||||
import jnpf.model.certificate.vo.CertificateInstanceVO;
|
||||
import jnpf.model.certificate.vo.CertificateTypeOptionVO;
|
||||
import jnpf.model.certificate.vo.CertificateStoreManageVO;
|
||||
import jnpf.model.certificate.vo.CertificateStoreDashboardVO;
|
||||
import jnpf.model.certificate.vo.CertificateStoreCustomStatusTableVO;
|
||||
import jnpf.model.certificate.vo.app.HealthCertificateDetailVO;
|
||||
import jnpf.model.warningnotice.enums.CertificateTypeEnum;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 证照实例服务。
|
||||
*/
|
||||
public interface CertificateInstanceService extends IService<CertificateInstanceEntity> {
|
||||
|
||||
/**
|
||||
* 新增证照实例。
|
||||
*
|
||||
* @param req 请求参数
|
||||
*/
|
||||
void add(CertificateInstanceAddReq req);
|
||||
|
||||
/**
|
||||
* 更新证照实例。
|
||||
*
|
||||
* @param req 请求参数
|
||||
*/
|
||||
void update(CertificateInstanceUpdateReq req);
|
||||
|
||||
/**
|
||||
* 按ID查询详情。
|
||||
*
|
||||
* @param id 实例ID
|
||||
* @return 详情
|
||||
*/
|
||||
CertificateInstanceVO queryInfo(String id);
|
||||
|
||||
/**
|
||||
* 分页查询列表。
|
||||
*
|
||||
* @param req 查询参数
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageInfo<CertificateInstanceVO> queryPage(CertificateInstanceQueryReq req);
|
||||
|
||||
/**
|
||||
* 健康证管理分页查询。
|
||||
*
|
||||
* @param req 查询参数
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageInfo<CertificateHealthManageVO> queryHealthPage(CertificateHealthManageQueryReq req);
|
||||
|
||||
/**
|
||||
* 门店证照分页查询。
|
||||
*
|
||||
* @param req 查询参数
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageInfo<CertificateStoreManageVO> queryStorePage(CertificateStoreManageQueryReq req);
|
||||
|
||||
/**
|
||||
* 逻辑删除。
|
||||
*
|
||||
* @param id 实例ID
|
||||
*/
|
||||
void delete(String id);
|
||||
|
||||
/**
|
||||
* 保存用户的健康证信息
|
||||
* @param userId
|
||||
* @param healthCertificate
|
||||
* @param startHealthDate
|
||||
* @param endHealthDate
|
||||
*/
|
||||
void saveHealthCertificate(String userId, String healthCertificate, String startHealthDate, String endHealthDate);
|
||||
|
||||
/**
|
||||
* 删除员工健康证
|
||||
* @param userId
|
||||
*/
|
||||
void deleteHealthCertificate(String userId);
|
||||
|
||||
/**
|
||||
* 根据用户id查询健康证
|
||||
* @param userId
|
||||
* @return
|
||||
*/
|
||||
Optional<HealthCertificateDetailVO> getHealthCertificateDetail(String userId);
|
||||
|
||||
/**
|
||||
* 根据用户id查询健康证
|
||||
* @param userIds
|
||||
* @return
|
||||
*/
|
||||
List<HealthCertificateDetailVO> getHealthCertificateDetails(Collection<String> userIds);
|
||||
|
||||
/**
|
||||
* 根据用户id查询健康证
|
||||
* @param userIds
|
||||
* @return
|
||||
*/
|
||||
default Map<String, HealthCertificateDetailVO> getHealthCertificateDetailMap(Collection<String> userIds){
|
||||
return getHealthCertificateDetails(userIds)
|
||||
.stream()
|
||||
.collect(Collectors.toMap(HealthCertificateDetailVO::getUserId, v -> v));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据状态,证照类型 查询用户id列表
|
||||
* @param status
|
||||
* @return
|
||||
*/
|
||||
List<String> getUserIdsByStatus(Integer status,String certificateType);
|
||||
|
||||
/**
|
||||
* 查询健康证用户id列表
|
||||
* @param status
|
||||
* @return
|
||||
*/
|
||||
default List<String> getHealthCertificateUserIdsByStatus(Integer status){
|
||||
return getUserIdsByStatus(status, CertificateTypeEnum.HEALTH_CERTIFICATE.getType());
|
||||
}
|
||||
/**
|
||||
* 门店自定义证照看板统计。
|
||||
*
|
||||
* @param req 查询参数
|
||||
* @return 看板统计结果
|
||||
*/
|
||||
CertificateStoreDashboardVO storeCertificateDashboard(CertificateStoreDashboardReq req);
|
||||
|
||||
/**
|
||||
* 门店证照看板表格分页查询。
|
||||
*
|
||||
* @param req 查询参数
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageInfo<CertificateStoreCustomStatusTableVO> storeCertificateDashboardTablePage(CertificateStoreDashboardReq req);
|
||||
|
||||
/**
|
||||
* 查询证照类型下拉选项。
|
||||
*
|
||||
* @return 证照类型选项
|
||||
*/
|
||||
List<CertificateTypeOptionVO> queryCertificateTypeList();
|
||||
|
||||
/**
|
||||
* 批量初始化员工健康证实例(缺失状态)。
|
||||
*
|
||||
* @param userIds 员工用户ID集合
|
||||
* @return 初始化条数
|
||||
*/
|
||||
int batchInitHealthCertificate(Collection<String> userIds);
|
||||
|
||||
/**
|
||||
* 初始化员工健康证实例(缺失状态)。
|
||||
*
|
||||
* @param userId 员工用户ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
default boolean initHealthCertificate(String userId){
|
||||
return batchInitHealthCertificate(List.of(userId)) > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package jnpf.certificate.service;
|
||||
|
||||
import jnpf.model.certificate.vo.CertificateOrganizeBusinessLicenseVO;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 组织营业执照开放接口服务。
|
||||
*/
|
||||
public interface CertificateManageApiService {
|
||||
|
||||
/**
|
||||
* 根据组织ID查询营业执照信息。
|
||||
*
|
||||
* @param organizeId 组织ID
|
||||
* @return 营业执照信息
|
||||
*/
|
||||
CertificateOrganizeBusinessLicenseVO queryBusinessLicense(String organizeId);
|
||||
|
||||
/**
|
||||
* 根据组织ID列表批量查询营业执照信息。
|
||||
*
|
||||
* @param organizeIds 组织ID列表
|
||||
* @return 营业执照信息列表
|
||||
*/
|
||||
List<CertificateOrganizeBusinessLicenseVO> queryBusinessLicenseBatch(Collection<String> organizeIds);
|
||||
|
||||
/**
|
||||
* 保存营业执照信息。
|
||||
*
|
||||
* @param req 营业执照参数
|
||||
*/
|
||||
void saveBusinessLicense(CertificateOrganizeBusinessLicenseVO req);
|
||||
|
||||
/**
|
||||
* 初始化门店默认缺失证照(营业执照、食品经营许可证)。
|
||||
*
|
||||
* @param storeId 门店ID
|
||||
*/
|
||||
void initStoreDefaultCertificates(String storeId);
|
||||
|
||||
/**
|
||||
* 根据组织ID删除该主体下全部证照信息。
|
||||
*
|
||||
* @param organizeId 组织ID
|
||||
* @param loginUserId 当前登录用户ID
|
||||
*/
|
||||
void deleteBusinessLicense(String organizeId, String loginUserId);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package jnpf.certificate.service;
|
||||
|
||||
import jnpf.model.certificate.req.app.CertificateAppBusinessLicenseUpdateReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppHealthCertificateUpdateReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppHygieneLicenseUpdateReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppStoreCustomUpdateReq;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppCertificateDetailVO;
|
||||
|
||||
/**
|
||||
* 端证照管理服务。
|
||||
*/
|
||||
public interface CertificateManageService {
|
||||
|
||||
/**
|
||||
* 根据证照实例ID查询详情。
|
||||
*
|
||||
* @param certificateInstanceId 证照实例ID
|
||||
* @return 证照详情
|
||||
*/
|
||||
CertificateAppCertificateDetailVO queryInfo(String certificateInstanceId);
|
||||
|
||||
/**
|
||||
* 更新健康证。
|
||||
*
|
||||
* @param req 更新参数
|
||||
*/
|
||||
void updateHealthCertificate(CertificateAppHealthCertificateUpdateReq req);
|
||||
|
||||
/**
|
||||
* 更新营业执照。
|
||||
*
|
||||
* @param req 更新参数
|
||||
*/
|
||||
void updateBusinessLicense(CertificateAppBusinessLicenseUpdateReq req);
|
||||
|
||||
/**
|
||||
* 更新食品经营许可证。
|
||||
*
|
||||
* @param req 更新参数
|
||||
*/
|
||||
void updateHygieneLicense(CertificateAppHygieneLicenseUpdateReq req);
|
||||
|
||||
/**
|
||||
* 更新门店自定义证照。
|
||||
*
|
||||
* @param req 更新参数
|
||||
*/
|
||||
void updateStoreCustomCertificate(CertificateAppStoreCustomUpdateReq req);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package jnpf.certificate.service;
|
||||
|
||||
import jnpf.base.ActionResult;
|
||||
import jnpf.exception.HandleException;
|
||||
import jnpf.model.certificate.req.CertificateStoreSaveReq;
|
||||
import jnpf.model.certificate.vo.CertificateStoreAndCertificatesVO;
|
||||
import jnpf.model.certificate.vo.CertificateStoreTabVO;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 门店证照服务。
|
||||
*/
|
||||
public interface CertificateStoreService {
|
||||
|
||||
/**
|
||||
* 保存门店及证照数据。
|
||||
*
|
||||
* @param req 保存参数
|
||||
*/
|
||||
String saveStoreAndCertificates(CertificateStoreSaveReq req);
|
||||
|
||||
/**
|
||||
* 删除门店。
|
||||
*
|
||||
* @param id 门店ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean deleteStore(@PathVariable("id") String id) throws HandleException;
|
||||
|
||||
/**
|
||||
* 根据门店ID查询门店及门店证照详情。
|
||||
*
|
||||
* @param storeId 门店ID
|
||||
* @return 门店及门店证照详情
|
||||
*/
|
||||
CertificateStoreAndCertificatesVO getStoreAndCertificates(String storeId);
|
||||
|
||||
/**
|
||||
* 查询门店证照Tab列表。
|
||||
*
|
||||
* @return 门店证照Tab列表
|
||||
*/
|
||||
List<CertificateStoreTabVO> queryStoreCertificateTabList();
|
||||
}
|
||||
@@ -0,0 +1,628 @@
|
||||
package jnpf.certificate.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import jnpf.authority.utils.PermissionsApplicableEnums;
|
||||
import jnpf.authority.utils.PermissionsApplicableObject;
|
||||
import jnpf.authority.utils.PermissionsUtils;
|
||||
import jnpf.base.ActionResult;
|
||||
import jnpf.certificate.helper.NoticeHelper;
|
||||
import jnpf.certificate.helper.OrganizationHelper;
|
||||
import jnpf.certificate.mapper.CertificateInstanceMapper;
|
||||
import jnpf.certificate.model.CertificateAppReminderRecord;
|
||||
import jnpf.certificate.service.CertificateAppReminderService;
|
||||
import jnpf.certificate.util.SubjectNameUtils;
|
||||
import jnpf.model.certificate.po.CertificateInstanceEntity;
|
||||
import jnpf.model.certificate.req.app.CertificateAppBatchRemindReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppSingleRemindReq;
|
||||
import jnpf.model.warningnotice.enums.CertificateTypeEnum;
|
||||
import jnpf.permission.V2UserApi;
|
||||
import jnpf.permission.vo.v2.organzie.OrganizeBaseInfoVO;
|
||||
import jnpf.permission.vo.v2.organzie.OrganizeGeneralDetailVO;
|
||||
import jnpf.permission.vo.v2.user.UserBoundInfoVO;
|
||||
import jnpf.storecertificatephoto.helper.StoreCertificatePhotoHelper;
|
||||
import jnpf.util.ServiceException;
|
||||
import jnpf.util.StringUtil;
|
||||
import jnpf.util.UserProvider;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.tuple.MutableTriple;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static jnpf.certificate.helper.NoticeHelper.URL_CERTIFICATE;
|
||||
|
||||
/**
|
||||
* App端证照提醒服务实现。
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class CertificateAppReminderServiceImpl implements CertificateAppReminderService {
|
||||
|
||||
private static final int STATUS_MISSING = 1;
|
||||
private static final int STATUS_EXPIRED = 2;
|
||||
private static final int STATUS_NEAR_EXPIRE = 3;
|
||||
private static final int STATUS_NORMAL = 4;
|
||||
|
||||
private static final int SUBJECT_TYPE_EMPLOYEE = 1;
|
||||
private static final int SUBJECT_TYPE_ORGANIZATION = 2;
|
||||
private static final int SUBJECT_TYPE_STORE = 3;
|
||||
|
||||
@Autowired
|
||||
private CertificateInstanceMapper certificateInstanceMapper;
|
||||
@Autowired
|
||||
private V2UserApi v2UserApi;
|
||||
@Autowired
|
||||
private OrganizationHelper organizationHelper;
|
||||
@Autowired
|
||||
private PermissionsUtils permissionsUtils;
|
||||
@Autowired
|
||||
private NoticeHelper noticeHelper;
|
||||
@Autowired
|
||||
private StoreCertificatePhotoHelper storeCertificatePhotoHelper;
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor commonExecutor;
|
||||
|
||||
/**
|
||||
* 一键提醒。
|
||||
* 健康证提醒员工本人,非健康证提醒组织负责人。
|
||||
*/
|
||||
@Override
|
||||
public void batchRemind(CertificateAppBatchRemindReq req) {
|
||||
log.error("batchRemind req:{}",req);
|
||||
ServiceException.notNull(req, "请求参数不能为空");
|
||||
Integer subjectType = req.getSubjectType();
|
||||
ServiceException.isTrue(subjectType != null && subjectType >= SUBJECT_TYPE_EMPLOYEE && subjectType <= SUBJECT_TYPE_STORE,
|
||||
"主体类型不合法");
|
||||
|
||||
// 构建查询的subjectType列表,subjectType=2时同时查3
|
||||
List<Integer> subjectTypes = new ArrayList<>();
|
||||
subjectTypes.add(subjectType);
|
||||
if (Integer.valueOf(SUBJECT_TYPE_ORGANIZATION).equals(subjectType)) {
|
||||
subjectTypes.add(SUBJECT_TYPE_STORE);
|
||||
}
|
||||
// 查询非正常状态的证照实例
|
||||
List<Integer> statuses = Arrays.asList(STATUS_MISSING, STATUS_EXPIRED, STATUS_NEAR_EXPIRE);
|
||||
|
||||
List<CertificateInstanceEntity> records= null;
|
||||
List<String> userIds = buildQueryUserIds(req.getOrgId());
|
||||
if(Objects.nonNull(userIds) && userIds.isEmpty()){//没查到数据,返回
|
||||
return;
|
||||
}
|
||||
if(req.getSubjectType() == SUBJECT_TYPE_EMPLOYEE){
|
||||
records = certificateInstanceMapper.selectBySubjectTypesAndStatusesFilterUser(subjectTypes, statuses,userIds);
|
||||
}else{
|
||||
records = certificateInstanceMapper.selectBySubjectTypesAndStatusesFilterOrganization(subjectTypes, statuses,req.getOrgId());
|
||||
}
|
||||
|
||||
if (CollUtil.isEmpty(records)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String tenantId = UserProvider.getUser().getTenantId();
|
||||
String remindUserName = StrUtil.blankToDefault(StrUtil.trim(UserProvider.getUser().getUserName()), "系统");
|
||||
|
||||
// 按是否健康证分组,批量解析接收人
|
||||
Map<Boolean, List<CertificateInstanceEntity>> grouped = records.stream()
|
||||
.collect(Collectors.partitioningBy(r -> isHealthCertificate(r.getCertificateType())));
|
||||
|
||||
// 健康证:接收人就是员工本人(subjectId)
|
||||
Map<String, String> healthReceiverMap = resolveHealthReceivers(grouped.getOrDefault(true, Collections.emptyList()));
|
||||
// 非健康证:接收人是组织负责人
|
||||
MutableTriple<Map<String,String>,Map<String,String>,Map<String,String>>
|
||||
orgReceiversAndOrgNamesAndTemplateNames = buildOrgReceiversAndOrgNamesAndTemplateNames(grouped.getOrDefault(false, Collections.emptyList()));
|
||||
|
||||
Map<String,String> orgReceivers = orgReceiversAndOrgNamesAndTemplateNames.getLeft();
|
||||
Map<String,String> orgNames = orgReceiversAndOrgNamesAndTemplateNames.getMiddle();
|
||||
Map<String,String> templateNames = orgReceiversAndOrgNamesAndTemplateNames.getRight();
|
||||
|
||||
List<CertificateInstanceEntity> finalRecords = records;
|
||||
commonExecutor.execute(() -> sendNotices(finalRecords, healthReceiverMap, orgReceivers, orgNames, templateNames, tenantId, remindUserName));
|
||||
}
|
||||
|
||||
private List<String> buildQueryUserIds(String orgId) {
|
||||
if(StringUtil.isBlank(orgId)){
|
||||
return null;
|
||||
}
|
||||
Map<String,List<String>> userIdsByOrgIds = organizationHelper.buildUserIdsByOrgIds(List.of(orgId),false,null);
|
||||
if(CollUtil.isEmpty(userIdsByOrgIds)){
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> userIds = userIdsByOrgIds.get(orgId);
|
||||
if(CollectionUtil.isEmpty(userIds)){
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return userIds;
|
||||
}
|
||||
|
||||
private void sendNotices(List<CertificateInstanceEntity> records,
|
||||
Map<String, String> healthReceiverMap,
|
||||
Map<String,String> orgReceivers,
|
||||
Map<String,String> orgNames,
|
||||
Map<String,String> templateNames,
|
||||
String tenantId,
|
||||
String remindUserName){
|
||||
for (CertificateInstanceEntity record : records) {
|
||||
String receiverUserId = isHealthCertificate(record.getCertificateType())
|
||||
? healthReceiverMap.get(StrUtil.trim(record.getSubjectId()))
|
||||
: orgReceivers.get(StrUtil.trim(record.getSubjectId()));
|
||||
if (StrUtil.isBlank(receiverUserId)) {
|
||||
continue;
|
||||
}
|
||||
String subjectId = record.getSubjectId();
|
||||
String orgName = orgNames.get(subjectId);
|
||||
String templateName = null;
|
||||
|
||||
String templateId = record.getTemplateId();
|
||||
if(StringUtil.isNotBlank(templateId)){
|
||||
templateName = templateNames.get(templateId);
|
||||
}
|
||||
sendNotice(record, receiverUserId, tenantId, remindUserName,templateName,orgName);
|
||||
}
|
||||
}
|
||||
|
||||
// 批量解析健康证接收人:subjectId -> userId
|
||||
private Map<String, String> resolveHealthReceivers(List<CertificateInstanceEntity> records) {
|
||||
if (CollUtil.isEmpty(records)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Set<String> subjectIds = records.stream()
|
||||
.map(r -> StrUtil.trim(r.getSubjectId()))
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.collect(Collectors.toSet());
|
||||
if (CollUtil.isEmpty(subjectIds)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
// 健康证subjectId就是userId,直接映射
|
||||
Map<String, String> result = new HashMap<>(subjectIds.size());
|
||||
subjectIds.forEach(id -> result.put(id, id));
|
||||
return result;
|
||||
}
|
||||
|
||||
// 批量解析非健康证接收人:subjectId(组织/门店) -> leaderId
|
||||
private Map<String, String> resolveOrgReceivers(List<CertificateInstanceEntity> records) {
|
||||
if (CollUtil.isEmpty(records)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Set<String> subjectIds = records.stream()
|
||||
.map(r -> StrUtil.trim(r.getSubjectId()))
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.collect(Collectors.toSet());
|
||||
if (CollUtil.isEmpty(subjectIds)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, OrganizeGeneralDetailVO> organizeMap = organizationHelper.buildOrganizeGenerals(subjectIds);
|
||||
Map<String, String> result = new HashMap<>(subjectIds.size());
|
||||
for (String subjectId : subjectIds) {
|
||||
OrganizeGeneralDetailVO org = organizeMap.get(subjectId);
|
||||
if (org != null && StrUtil.isNotBlank(org.getLeaderId())) {
|
||||
result.put(subjectId, StrUtil.trim(org.getLeaderId()));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 左边是 组织接收人。key为subjectId,value为组织负责人id
|
||||
* 中间是 组织名称,key为组织id,value为组织名称
|
||||
* 右边是 模板名称,key为模板id,value为模板名称
|
||||
* @param records
|
||||
* @return
|
||||
*/
|
||||
private MutableTriple<Map<String,String>,Map<String,String>,Map<String,String>>
|
||||
buildOrgReceiversAndOrgNamesAndTemplateNames(List<CertificateInstanceEntity> records) {
|
||||
if (CollUtil.isEmpty(records)) {
|
||||
return MutableTriple.of(Collections.emptyMap(),Collections.emptyMap(),Collections.emptyMap());
|
||||
}
|
||||
|
||||
Set<String> templateIds = new HashSet<>();
|
||||
Set<String> orgIds = new HashSet<>();
|
||||
for (CertificateInstanceEntity record:records){
|
||||
String templateId = record.getTemplateId();
|
||||
if(StringUtil.isNotBlank(templateId)){
|
||||
templateIds.add(templateId);
|
||||
}
|
||||
orgIds.add(record.getSubjectId());
|
||||
}
|
||||
|
||||
Map<String,String> orgReceivers = new HashMap<>();
|
||||
Map<String,String> orgNames = new HashMap<>();
|
||||
|
||||
Map<String, OrganizeGeneralDetailVO> organizeMap = organizationHelper.buildOrganizeGenerals(orgIds);
|
||||
Map<String,String> templateNames = storeCertificatePhotoHelper.buildStoreCertificateIdAndNames(templateIds,null);
|
||||
for (String orgId : orgIds) {
|
||||
OrganizeGeneralDetailVO org = organizeMap.get(orgId);
|
||||
if(Objects.isNull(org)){
|
||||
continue;
|
||||
}
|
||||
if (StrUtil.isNotBlank(org.getLeaderId())) {
|
||||
orgReceivers.put(orgId, StrUtil.trim(org.getLeaderId()));
|
||||
}
|
||||
orgNames.put(orgId,org.getName());
|
||||
}
|
||||
return MutableTriple.of(orgReceivers,orgNames,templateNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个提醒。
|
||||
* 根据证照实例ID查询后,按证照类型解析接收人并发送提醒。
|
||||
*/
|
||||
@Override
|
||||
public void singleRemind(CertificateAppSingleRemindReq req) {
|
||||
ServiceException.notNull(req, "请求参数不能为空");
|
||||
List<String> certificateInstanceIds = req.getCertificateInstanceIds();
|
||||
ServiceException.isTrue(CollUtil.isNotEmpty(certificateInstanceIds), "证照实例ID不能为空");
|
||||
|
||||
// 查询证照实例
|
||||
List<CertificateInstanceEntity> certificateInstanceEntities = certificateInstanceMapper.selectBatchIds(certificateInstanceIds);
|
||||
if(CollUtil.isEmpty(certificateInstanceEntities)){
|
||||
log.warn("证照实例不存在,certificateInstanceId={}", certificateInstanceIds);
|
||||
return;
|
||||
}
|
||||
|
||||
Set<String> templateIds = new HashSet<>();
|
||||
Set<String> orgIds = new HashSet<>();
|
||||
for (CertificateInstanceEntity record:certificateInstanceEntities) {
|
||||
if (record.getStatus() == null || Integer.valueOf(STATUS_NORMAL).equals(record.getStatus())) {
|
||||
log.warn("证照状态无需提醒,certificateInstanceId={}, status={}", record.getId(), record.getStatus());
|
||||
continue;
|
||||
}
|
||||
String templateId = record.getTemplateId();
|
||||
Integer subjectType = record.getSubjectType();
|
||||
String subjectId = record.getSubjectId();
|
||||
if(StringUtil.isNotBlank(templateId)){
|
||||
templateIds.add(templateId);
|
||||
}
|
||||
if(Objects.nonNull(subjectType) && subjectType != SUBJECT_TYPE_EMPLOYEE){
|
||||
orgIds.add(subjectId);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String,String> templateIdAndNames = storeCertificatePhotoHelper.buildStoreCertificateIdAndNames(templateIds,null);
|
||||
Map<String, OrganizeBaseInfoVO> organizeBaseInfoById = organizationHelper.buildBaseOrganizeVO(orgIds,UserProvider.getUser().getTenantId());
|
||||
|
||||
for (CertificateInstanceEntity record:certificateInstanceEntities){
|
||||
if (record.getStatus() == null || Integer.valueOf(STATUS_NORMAL).equals(record.getStatus())) {
|
||||
log.warn("证照状态无需提醒,certificateInstanceId={}, status={}", record.getId(), record.getStatus());
|
||||
continue;
|
||||
}
|
||||
|
||||
// 解析接收人
|
||||
String receiverUserId = resolveSingleReceiverUserId(record,organizeBaseInfoById);
|
||||
if (StrUtil.isBlank(receiverUserId)) {
|
||||
log.warn("未找到提醒接收人,certificateInstanceId={}, subjectType={}, subjectId={}",
|
||||
record.getId(), record.getSubjectType(), record.getSubjectId());
|
||||
continue;
|
||||
}
|
||||
|
||||
String tenantId = UserProvider.getUser().getTenantId();
|
||||
String remindUserName = StrUtil.blankToDefault(StrUtil.trim(UserProvider.getUser().getUserName()), "系统");
|
||||
|
||||
String templateName = templateIdAndNames.get(record.getTemplateId());
|
||||
String orgName = null;
|
||||
OrganizeBaseInfoVO organizeBaseInfoVO = organizeBaseInfoById.get(record.getSubjectId());
|
||||
if(Objects.nonNull(organizeBaseInfoVO)){
|
||||
orgName = organizeBaseInfoVO.getName();
|
||||
}
|
||||
|
||||
sendNotice(record, receiverUserId, tenantId, remindUserName,templateName,orgName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析单条提醒的接收人用户ID。
|
||||
* 健康证:通过V2UserApi#getUsersBound查询员工本人信息。
|
||||
* 非健康证:通过OrganizationHelper#buildOrganizeGenerals查询组织负责人。
|
||||
*/
|
||||
private String resolveSingleReceiverUserId(CertificateInstanceEntity record,Map<String, OrganizeBaseInfoVO> organizeBaseInfoById) {
|
||||
String subjectId = StrUtil.trim(record.getSubjectId());
|
||||
if (StrUtil.isBlank(subjectId)) {
|
||||
return null;
|
||||
}
|
||||
if (isHealthCertificate(record.getCertificateType())) {
|
||||
return subjectId;
|
||||
}
|
||||
OrganizeBaseInfoVO organize = organizeBaseInfoById.get(subjectId);
|
||||
if (organize == null) {
|
||||
log.error("组织信息不存在,subjectId={}", subjectId);
|
||||
return null;
|
||||
}
|
||||
return StrUtil.trim(organize.getLeaderId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送单条提醒消息。
|
||||
*/
|
||||
private void sendNotice(CertificateInstanceEntity record,
|
||||
String receiverUserId,
|
||||
String tenantId,
|
||||
String remindUserName,
|
||||
String templateName,
|
||||
String orgName) {
|
||||
if (record == null) {
|
||||
return;
|
||||
}
|
||||
if (StrUtil.isBlank(receiverUserId)) {
|
||||
return;
|
||||
}
|
||||
NoticeMessage noticeMessage = buildNoticeMessage(record, remindUserName,templateName,orgName);
|
||||
|
||||
if(isHealthCertificate(record.getCertificateType())){
|
||||
noticeHelper.sendMessage(
|
||||
Collections.singletonList(receiverUserId),
|
||||
tenantId,
|
||||
noticeMessage.getTitle(),
|
||||
noticeMessage.getContent(),
|
||||
NoticeHelper.BUTTON_NAME_HANDLER,
|
||||
buildJumpHealthCertificateUrl(record),
|
||||
NoticeHelper.MP_ID_CERTIFICATE
|
||||
);
|
||||
return;
|
||||
}
|
||||
noticeHelper.sendMessage(
|
||||
Collections.singletonList(receiverUserId),
|
||||
tenantId,
|
||||
noticeMessage.getTitle(),
|
||||
noticeMessage.getContent(),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
private String buildJumpHealthCertificateUrl(CertificateInstanceEntity record) {
|
||||
if(Objects.isNull(record)){
|
||||
return null;
|
||||
}
|
||||
String subjectId = record.getSubjectId();
|
||||
if(StringUtil.isBlank(subjectId)){
|
||||
return null;
|
||||
}
|
||||
return String.format(URL_CERTIFICATE,record.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建提醒文案。
|
||||
* 文案依据原型:标题“存在风险,请及时处理”,正文按缺失/临期/过期区分。
|
||||
*/
|
||||
private NoticeMessage buildNoticeMessage(CertificateInstanceEntity record,
|
||||
String remindUserName,
|
||||
String templateName,
|
||||
String orgName) {
|
||||
String certificateType = StrUtil.trim(record.getCertificateType());
|
||||
Integer status = record.getStatus();
|
||||
String certificateName = resolveCertificateTypeName(certificateType,templateName);
|
||||
int remainDays = calculateRemainDays(record.getExpireDate());
|
||||
String subjectName = StrUtil.blankToDefault(orgName,StrUtil.blankToDefault(SubjectNameUtils.getSubjectName(record.getSubjectType()), "该主体"));
|
||||
|
||||
if (isHealthCertificate(certificateType)) {
|
||||
String title = "您的健康证存在风险,请及时处理!";
|
||||
if (Integer.valueOf(STATUS_NEAR_EXPIRE).equals(status)) {
|
||||
return new NoticeMessage(title, buildHealthCertificateNearExpireContent(remainDays,remindUserName));
|
||||
}
|
||||
if (Integer.valueOf(STATUS_EXPIRED).equals(status)) {
|
||||
return new NoticeMessage(title, String.format("您的健康证已过期,%s提醒您及时更新健康证。", remindUserName));
|
||||
}
|
||||
return new NoticeMessage(title, String.format("您的健康证缺失,%s提醒您及时更新健康证。", remindUserName));
|
||||
}
|
||||
|
||||
String title = subjectName + certificateName + "存在风险,请及时处理!";
|
||||
if (Integer.valueOf(STATUS_NEAR_EXPIRE).equals(status)) {
|
||||
return new NoticeMessage(title, buildCertificateNearExpireContent(subjectName,certificateName,remainDays));
|
||||
}
|
||||
if (Integer.valueOf(STATUS_EXPIRED).equals(status)) {
|
||||
return new NoticeMessage(title, String.format("%s%s已过期,请及时更新%s。", subjectName, certificateName, certificateName));
|
||||
}
|
||||
return new NoticeMessage(title, String.format("%s%s缺失,请及时上传%s。", subjectName, certificateName, certificateName));
|
||||
}
|
||||
|
||||
private String buildCertificateNearExpireContent(String subjectName, String certificateName, int remainDays) {
|
||||
if(remainDays <= 0){
|
||||
return String.format("%s%s今天过期,请及时更新%s。", subjectName, certificateName, certificateName);
|
||||
}
|
||||
return String.format("%s%s离到期时间还有%s天,请及时更新%s。", subjectName, certificateName, remainDays, certificateName);
|
||||
}
|
||||
|
||||
private String buildHealthCertificateNearExpireContent(int remainDays,String remindUserName) {
|
||||
if(remainDays <= 0){
|
||||
return String.format("您的健康证今天过期,%s提醒您请及时更新健康证。", remindUserName);
|
||||
}
|
||||
return String.format("您的健康证离到期时间还有%s天,%s提醒您请及时更新健康证。", remainDays,remindUserName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 单条提醒权限校验。
|
||||
*/
|
||||
private void validateSinglePermission(CertificateAppReminderRecord record, String userId, String moduleId) {
|
||||
Integer subjectType = record.getSubjectType();
|
||||
if (Integer.valueOf(SUBJECT_TYPE_EMPLOYEE).equals(subjectType)) {
|
||||
PermissionUserScope userScope = resolvePermissionUserScope(userId, moduleId);
|
||||
if (Boolean.TRUE.equals(userScope.getAllDataPermission())) {
|
||||
return;
|
||||
}
|
||||
ServiceException.isTrue(CollUtil.isNotEmpty(userScope.getUserIds()), "暂无数据权限");
|
||||
ServiceException.isTrue(userScope.getUserIds().contains(StrUtil.trim(record.getReceiverUserId())), "暂无当前证照数据权限");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Integer.valueOf(SUBJECT_TYPE_ORGANIZATION).equals(subjectType) || Integer.valueOf(SUBJECT_TYPE_STORE).equals(subjectType)) {
|
||||
PermissionOrgScope orgScope = resolvePermissionOrgScope(userId, moduleId,UserProvider.getUser().getTenantId());
|
||||
if (Boolean.TRUE.equals(orgScope.getAllDataPermission())) {
|
||||
return;
|
||||
}
|
||||
String orgPermissionId = StrUtil.trim(record.getOrgPermissionId());
|
||||
ServiceException.isTrue(StrUtil.isNotBlank(orgPermissionId), "组织数据不存在,无法提醒");
|
||||
ServiceException.isTrue(CollUtil.isNotEmpty(orgScope.getOrgIds()), "暂无数据权限");
|
||||
ServiceException.isTrue(orgScope.getOrgIds().contains(orgPermissionId), "暂无当前证照数据权限");
|
||||
return;
|
||||
}
|
||||
throw new ServiceException("主体类型不支持提醒");
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析员工维度权限范围。
|
||||
* allDataPermission=true 代表全量权限,false 时使用 userIds 过滤。
|
||||
*/
|
||||
private PermissionUserScope resolvePermissionUserScope(String userId, String moduleId) {
|
||||
List<String> permissionUserIds = permissionsUtils.obtainPersonnelUserIdDataPermissions(userId, moduleId);
|
||||
if (permissionUserIds == null) {
|
||||
return new PermissionUserScope(true, Collections.emptyList());
|
||||
}
|
||||
return new PermissionUserScope(false, normalizeIdList(permissionUserIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析组织维度权限范围。
|
||||
* allDataPermission=true 代表全量权限,false 时使用 orgIds 过滤。
|
||||
*/
|
||||
private PermissionOrgScope resolvePermissionOrgScope(String userId, String moduleId,String tenantId) {
|
||||
if (Boolean.TRUE.equals(UserProvider.getUser().getIsAdministrator())) {
|
||||
return new PermissionOrgScope(true, Collections.emptyList());
|
||||
}
|
||||
|
||||
PermissionsApplicableObject applicableObject = permissionsUtils.obtainTheScopeOfUserPermissionsEnums(userId, moduleId,tenantId);
|
||||
PermissionsApplicableEnums applicableEnums = applicableObject == null ? null : applicableObject.getPermissionsApplicableEnums();
|
||||
if (PermissionsApplicableEnums.ALL.equals(applicableEnums)) {
|
||||
return new PermissionOrgScope(true, Collections.emptyList());
|
||||
}
|
||||
|
||||
List<String> orgIds = normalizeIdList(applicableObject == null ? Collections.emptyList() : applicableObject.getOrgIds());
|
||||
if (CollUtil.isEmpty(orgIds)) {
|
||||
try {
|
||||
List<String> fallbackOrgIds = permissionsUtils.obtainPersonnelOrganizationIdDataPermissions(userId);
|
||||
if (fallbackOrgIds == null) {
|
||||
return new PermissionOrgScope(true, Collections.emptyList());
|
||||
}
|
||||
orgIds = normalizeIdList(fallbackOrgIds);
|
||||
} catch (Exception e) {
|
||||
return new PermissionOrgScope(false, Collections.emptyList());
|
||||
}
|
||||
}
|
||||
return new PermissionOrgScope(false, orgIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 规整ID集合:去空、去重、去首尾空格。
|
||||
*/
|
||||
private List<String> normalizeIdList(Collection<String> idList) {
|
||||
if (CollUtil.isEmpty(idList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return idList.stream()
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.map(StrUtil::trim)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否健康证类型。
|
||||
*/
|
||||
private boolean isHealthCertificate(String certificateType) {
|
||||
return StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.HEALTH_CERTIFICATE.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 证照类型转中文名称。
|
||||
*/
|
||||
private String resolveCertificateTypeName(String certificateType,String templateName) {
|
||||
if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.HEALTH_CERTIFICATE.getType())) {
|
||||
return "健康证";
|
||||
}
|
||||
if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.BUSINESS_LICENSE.getType())) {
|
||||
return "营业执照";
|
||||
}
|
||||
if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.HYGIENE_LICENSE.getType())) {
|
||||
return "食品经营许可证";
|
||||
}
|
||||
if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType())) {
|
||||
return templateName;
|
||||
}
|
||||
return "证照";
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算距离到期的剩余天数。
|
||||
*/
|
||||
private int calculateRemainDays(Date expireDate) {
|
||||
if (expireDate == null) {
|
||||
return 0;
|
||||
}
|
||||
Date today = DateUtil.beginOfDay(DateUtil.date());
|
||||
Date target = DateUtil.beginOfDay(expireDate);
|
||||
long days = DateUtil.betweenDay(today, target, false);
|
||||
return (int) Math.max(days, 0L);
|
||||
}
|
||||
|
||||
/**
|
||||
* 员工权限范围。
|
||||
*/
|
||||
private static class PermissionUserScope {
|
||||
private final Boolean allDataPermission;
|
||||
private final List<String> userIds;
|
||||
|
||||
private PermissionUserScope(Boolean allDataPermission, List<String> userIds) {
|
||||
this.allDataPermission = allDataPermission;
|
||||
this.userIds = userIds;
|
||||
}
|
||||
|
||||
public Boolean getAllDataPermission() {
|
||||
return allDataPermission;
|
||||
}
|
||||
|
||||
public List<String> getUserIds() {
|
||||
return userIds;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 组织权限范围。
|
||||
*/
|
||||
private static class PermissionOrgScope {
|
||||
private final Boolean allDataPermission;
|
||||
private final List<String> orgIds;
|
||||
|
||||
private PermissionOrgScope(Boolean allDataPermission, List<String> orgIds) {
|
||||
this.allDataPermission = allDataPermission;
|
||||
this.orgIds = orgIds;
|
||||
}
|
||||
|
||||
public Boolean getAllDataPermission() {
|
||||
return allDataPermission;
|
||||
}
|
||||
|
||||
public List<String> getOrgIds() {
|
||||
return orgIds;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提醒文案对象。
|
||||
*/
|
||||
private static class NoticeMessage {
|
||||
private final String title;
|
||||
private final String content;
|
||||
|
||||
private NoticeMessage(String title, String content) {
|
||||
this.title = title;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,514 @@
|
||||
package jnpf.certificate.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import jnpf.authority.utils.PermissionsApplicableEnums;
|
||||
import jnpf.authority.utils.PermissionsApplicableObject;
|
||||
import jnpf.authority.utils.PermissionsUtils;
|
||||
import jnpf.certificate.helper.OrganizationHelper;
|
||||
import jnpf.certificate.mapper.CertificateAppRiskMapper;
|
||||
import jnpf.certificate.service.CertificateAppRiskService;
|
||||
import jnpf.model.certificate.req.app.CertificateAppEmployeeRiskQueryReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppRiskChartReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppStoreRiskQueryReq;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppEmployeeRiskVO;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppRiskChartVO;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppRiskReminderCountVO;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppStatusCountVO;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppStoreRiskItemVO;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppStoreRiskVO;
|
||||
import jnpf.model.warningnotice.enums.CertificateTypeEnum;
|
||||
import jnpf.permission.eum.v2.FtbStoreTagEnum;
|
||||
import jnpf.permission.vo.v2.organzie.OrganizeBaseInfoVO;
|
||||
import jnpf.permission.vo.v2.organzie.OrganizeGeneralDetailVO;
|
||||
import jnpf.permission.vo.v2.position.PositionBaseInfoVO;
|
||||
import jnpf.permission.vo.v2.user.UserBaseRelationSimpleVO;
|
||||
import jnpf.permission.vo.v2.user.UserBoundVO;
|
||||
import jnpf.storecertificatephoto.helper.StoreCertificatePhotoHelper;
|
||||
import jnpf.util.StringUtil;
|
||||
import jnpf.util.UserProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* App 端证照风险服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class CertificateAppRiskServiceImpl implements CertificateAppRiskService {
|
||||
|
||||
private static final int STATUS_MISSING = 1;
|
||||
private static final int STATUS_EXPIRED = 2;
|
||||
private static final int STATUS_NEAR_EXPIRE = 3;
|
||||
private static final int STATUS_NORMAL = 4;
|
||||
|
||||
/**
|
||||
* 证照风险查询 Mapper。
|
||||
*/
|
||||
@Autowired
|
||||
private CertificateAppRiskMapper certificateAppRiskMapper;
|
||||
|
||||
/**
|
||||
* 数据权限工具。
|
||||
*/
|
||||
@Autowired
|
||||
private PermissionsUtils permissionsUtils;
|
||||
@Autowired
|
||||
private OrganizationHelper organizationHelper;
|
||||
@Autowired
|
||||
private StoreCertificatePhotoHelper storeCertificatePhotoHelper;
|
||||
|
||||
|
||||
/**
|
||||
* 查询证照风险图表统计。
|
||||
* 健康证按用户数据权限统计,非健康证按组织数据权限统计。
|
||||
*/
|
||||
@Override
|
||||
public CertificateAppRiskChartVO queryChart(CertificateAppRiskChartReq req) {
|
||||
CertificateAppRiskChartReq queryReq = req == null ? new CertificateAppRiskChartReq() : req;
|
||||
queryReq.setOrgId(StrUtil.trim(queryReq.getOrgId()));
|
||||
queryReq.setCertificateType(StrUtil.blankToDefault(
|
||||
StrUtil.trim(queryReq.getCertificateType()),
|
||||
CertificateTypeEnum.HEALTH_CERTIFICATE.getType()
|
||||
));
|
||||
|
||||
String orgId = queryReq.getOrgId();
|
||||
boolean healthCertificateQuery = isHealthCertificate(queryReq.getCertificateType());
|
||||
buildQueryUserIds(queryReq,healthCertificateQuery);
|
||||
//如果查询组织id,但是没有查询到该组织的用户id列表,则返回空
|
||||
if(healthCertificateQuery && StringUtil.isNotBlank(orgId) && CollectionUtil.isEmpty(queryReq.getUserIds())){
|
||||
return buildChart(null);
|
||||
}
|
||||
List<CertificateAppStatusCountVO> statusCountList;
|
||||
if (healthCertificateQuery) {
|
||||
statusCountList = certificateAppRiskMapper.queryHealthRiskStatusCount(queryReq);
|
||||
} else {
|
||||
statusCountList = certificateAppRiskMapper.queryStoreRiskStatusCount(queryReq);
|
||||
}
|
||||
return buildChart(statusCountList);
|
||||
}
|
||||
|
||||
private void buildQueryUserIds(CertificateAppRiskChartReq queryReq,boolean healthCertificateQuery) {
|
||||
if(!healthCertificateQuery){
|
||||
return;
|
||||
}
|
||||
String queryReqOrgId = queryReq.getOrgId();
|
||||
if(StringUtil.isBlank(queryReqOrgId)){
|
||||
return;
|
||||
}
|
||||
List<String> userIdsByOrgId = organizationHelper.getUserIdsByOrgId(queryReqOrgId);
|
||||
queryReq.setUserIds(userIdsByOrgId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询员工证照风险数据。
|
||||
*/
|
||||
@Override
|
||||
public PageInfo<CertificateAppEmployeeRiskVO> queryEmployeePage(CertificateAppEmployeeRiskQueryReq req) {
|
||||
CertificateAppEmployeeRiskQueryReq queryReq = req == null ? new CertificateAppEmployeeRiskQueryReq() : req;
|
||||
queryReq.setOrgId(StrUtil.trim(queryReq.getOrgId()));
|
||||
long current = queryReq.getCurrentPage() == null || queryReq.getCurrentPage() <= 0 ? 1L : queryReq.getCurrentPage();
|
||||
long pageSize = queryReq.getPageSize() == null || queryReq.getPageSize() <= 0 ? 10L : queryReq.getPageSize();
|
||||
String orgId = queryReq.getOrgId();
|
||||
Collection<String> queryUserIds;
|
||||
Map<String, UserBaseRelationSimpleVO> relationMap;
|
||||
if (StringUtil.isNotBlank(orgId)) {
|
||||
relationMap = organizationHelper.getUserBaseRelationMapByOrgId(orgId);
|
||||
queryUserIds = relationMap.keySet();
|
||||
} else {
|
||||
relationMap = Collections.emptyMap();
|
||||
queryUserIds = null;
|
||||
}
|
||||
if (StringUtil.isNotBlank(orgId) && CollectionUtil.isEmpty(queryUserIds)) {
|
||||
return buildEmptyEmployeePage(current, pageSize);
|
||||
}
|
||||
|
||||
Page<CertificateAppEmployeeRiskVO> page = new Page<>(current, pageSize);
|
||||
Page<CertificateAppEmployeeRiskVO> queryPage = certificateAppRiskMapper.queryEmployeeRiskPageByUserIds(page, queryUserIds);
|
||||
List<CertificateAppEmployeeRiskVO> records = queryPage.getRecords() == null ? new ArrayList<>() : queryPage.getRecords();
|
||||
if (CollectionUtil.isEmpty(records)) {
|
||||
return buildEmptyEmployeePage(current, pageSize);
|
||||
}
|
||||
|
||||
Set<String> pageUserIds = records.stream()
|
||||
.map(CertificateAppEmployeeRiskVO::getUserId)
|
||||
.filter(StringUtil::isNotBlank)
|
||||
.collect(Collectors.toSet());
|
||||
if (CollectionUtil.isEmpty(pageUserIds)) {
|
||||
return buildEmptyEmployeePage(current, pageSize);
|
||||
}
|
||||
if (StringUtil.isBlank(orgId)) {
|
||||
relationMap = organizationHelper.getUserBaseRelationMapByUserIds(pageUserIds);
|
||||
}
|
||||
|
||||
for (CertificateAppEmployeeRiskVO item : records){
|
||||
UserBaseRelationSimpleVO relation = relationMap.get(item.getUserId());
|
||||
if(Objects.isNull(relation)){
|
||||
continue;
|
||||
}
|
||||
item.setWorkerName(StrUtil.blankToDefault(StrUtil.trim(relation.getUserName()), "-"));
|
||||
item.setPhone(StrUtil.blankToDefault(StrUtil.trim(relation.getMobilePhone()), "-"));
|
||||
item.setOrgId(relation.getOrganizeId());
|
||||
item.setPositionId(relation.getPositionId());
|
||||
item.setOrgName(relation.getOrganizeName());
|
||||
item.setPositionName(relation.getPositionName());
|
||||
item.setPhone(relation.getMobilePhone());
|
||||
}
|
||||
|
||||
PageInfo<CertificateAppEmployeeRiskVO> pageInfo = new PageInfo<>();
|
||||
pageInfo.setList(records);
|
||||
pageInfo.setPageNum((int) queryPage.getCurrent());
|
||||
pageInfo.setPageSize((int) queryPage.getSize());
|
||||
pageInfo.setTotal(queryPage.getTotal());
|
||||
return pageInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询门店证照风险数据。
|
||||
*/
|
||||
@Override
|
||||
public PageInfo<CertificateAppStoreRiskVO> queryStorePage(CertificateAppStoreRiskQueryReq req) {
|
||||
CertificateAppStoreRiskQueryReq queryReq = req == null ? new CertificateAppStoreRiskQueryReq() : req;
|
||||
queryReq.setOrgId(StrUtil.trim(queryReq.getOrgId()));
|
||||
queryReq.setCertificateType(StrUtil.trim(queryReq.getCertificateType()));
|
||||
long current = queryReq.getCurrentPage() == null || queryReq.getCurrentPage() <= 0 ? 1L : queryReq.getCurrentPage();
|
||||
long pageSize = queryReq.getPageSize() == null || queryReq.getPageSize() <= 0 ? 10L : queryReq.getPageSize();
|
||||
|
||||
PageInfo<CertificateAppStoreRiskVO> emptyPage = buildEmptyStorePage(current, pageSize);
|
||||
|
||||
Page<CertificateAppStoreRiskVO> page = new Page<>(current, pageSize);
|
||||
Page<CertificateAppStoreRiskVO> queryPage = certificateAppRiskMapper.queryStoreRiskPage(page, queryReq);
|
||||
|
||||
List<CertificateAppStoreRiskVO> records = queryPage.getRecords() == null ? Collections.emptyList() : queryPage.getRecords();
|
||||
if (CollectionUtil.isEmpty(records)) {
|
||||
return emptyPage;
|
||||
}
|
||||
|
||||
Set<String> orgIds = new HashSet<>();
|
||||
records.forEach(vo -> {
|
||||
orgIds.add(vo.getSubjectId());
|
||||
});
|
||||
List<CertificateAppStoreRiskItemVO> detailList = certificateAppRiskMapper.queryStoreRiskDetailList(orgIds);
|
||||
|
||||
Set<String> templateIds = detailList.stream()
|
||||
.map(CertificateAppStoreRiskItemVO::getTemplateId)
|
||||
.filter(StringUtil::isNotBlank)
|
||||
.collect(Collectors.toSet());
|
||||
Map<String,String> templateIdAndNames = null;
|
||||
if(CollUtil.isNotEmpty(templateIds)){
|
||||
templateIdAndNames = storeCertificatePhotoHelper.buildStoreCertificateIdAndNames(templateIds,1);
|
||||
}else {
|
||||
templateIdAndNames = new HashMap<>();
|
||||
}
|
||||
Map<String, String> finalTemplateIdAndNames = templateIdAndNames;
|
||||
Map<String, List<CertificateAppStoreRiskItemVO>> detailMap = CollectionUtil.emptyIfNull(detailList).stream()
|
||||
.peek(item -> item.setCertificateTypeName(resolveCertificateTypeName(item.getCertificateType(),item.getTemplateId(), finalTemplateIdAndNames)))
|
||||
.filter(item -> StringUtil.isNotBlank(item.getCertificateTypeName()))//过滤掉被禁用的证照
|
||||
.collect(Collectors.groupingBy(CertificateAppStoreRiskItemVO::getSubjectId, LinkedHashMap::new, Collectors.toList()));
|
||||
for (CertificateAppStoreRiskVO record : records) {
|
||||
List<CertificateAppStoreRiskItemVO> certificateList = detailMap.getOrDefault(record.getSubjectId(), Collections.emptyList());
|
||||
record.setCertificateList(certificateList);
|
||||
}
|
||||
Map<String, OrganizeBaseInfoVO> organizeBaseInfoVOMap = organizationHelper.buildBaseOrganizeVO(orgIds,UserProvider.getUser().getTenantId());
|
||||
|
||||
fillStoreRiskItem(records,organizeBaseInfoVOMap);
|
||||
|
||||
PageInfo<CertificateAppStoreRiskVO> pageInfo = new PageInfo<>();
|
||||
pageInfo.setList(records);
|
||||
pageInfo.setPageNum((int) queryPage.getCurrent());
|
||||
pageInfo.setPageSize((int) queryPage.getSize());
|
||||
pageInfo.setTotal(queryPage.getTotal());
|
||||
return pageInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询有风险的员工、组织、总数量
|
||||
*/
|
||||
@Override
|
||||
public CertificateAppRiskReminderCountVO queryRiskReminderCount(String orgId) {
|
||||
|
||||
List<String> orgIds = new ArrayList<>(1);
|
||||
Map<String,List<String>> userIdsByOrgId = null;
|
||||
if(StringUtil.isNotBlank(orgId)){
|
||||
userIdsByOrgId = organizationHelper.buildUserIdsByOrgIds(List.of(orgId),true,"");
|
||||
orgIds.add(orgId);
|
||||
}else{
|
||||
userIdsByOrgId = organizationHelper.buildUserIdsByOrgIds(null,true,"");
|
||||
}
|
||||
|
||||
Set<String> userIds = userIdsByOrgId.values()
|
||||
.stream()
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toSet());
|
||||
long employeeCount = 0L;
|
||||
if(!CollUtil.isEmpty(userIds)){
|
||||
employeeCount = certificateAppRiskMapper.queryEmployeeRiskTotalCount(userIds);
|
||||
}
|
||||
|
||||
Long organizationCount = certificateAppRiskMapper.queryOrganizationRiskTotalCount(orgIds);
|
||||
if(Objects.isNull(organizationCount)){
|
||||
organizationCount = 0L;
|
||||
}
|
||||
|
||||
CertificateAppRiskReminderCountVO vo = new CertificateAppRiskReminderCountVO();
|
||||
vo.setEmployeeCount(employeeCount);
|
||||
vo.setOrganizationCount(organizationCount);
|
||||
vo.setTotalCount(employeeCount + organizationCount);
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析员工数据权限范围。
|
||||
* 通过 allDataPermission 标识是否全量权限,userIds 为受限时可访问用户。
|
||||
*/
|
||||
private PermissionUserScope resolvePermissionUserScope(String userId, String moduleId) {
|
||||
List<String> permissionUserIds = permissionsUtils.obtainPersonnelUserIdDataPermissions(userId, moduleId);
|
||||
if (permissionUserIds == null) {
|
||||
return new PermissionUserScope(true, Collections.emptyList());
|
||||
}
|
||||
return new PermissionUserScope(false, normalizeIdList(permissionUserIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析组织数据权限范围。
|
||||
* 通过 allDataPermission 标识是否全量权限,orgIds 为受限时可访问组织。
|
||||
*/
|
||||
private PermissionOrgScope resolvePermissionOrgScope(String userId, String moduleId,String tenantId) {
|
||||
if (Boolean.TRUE.equals(UserProvider.getUser().getIsAdministrator())) {
|
||||
return new PermissionOrgScope(true, Collections.emptyList());
|
||||
}
|
||||
|
||||
PermissionsApplicableObject applicableObject = permissionsUtils.obtainTheScopeOfUserPermissionsEnums(userId, moduleId,tenantId);
|
||||
PermissionsApplicableEnums applicableEnums = applicableObject == null ? null : applicableObject.getPermissionsApplicableEnums();
|
||||
if (PermissionsApplicableEnums.ALL.equals(applicableEnums)) {
|
||||
return new PermissionOrgScope(true, Collections.emptyList());
|
||||
}
|
||||
|
||||
List<String> orgIds = normalizeIdList(applicableObject == null ? Collections.emptyList() : applicableObject.getOrgIds());
|
||||
if (CollUtil.isEmpty(orgIds)) {
|
||||
try {
|
||||
List<String> fallbackOrgIds = permissionsUtils.obtainPersonnelOrganizationIdDataPermissions(userId);
|
||||
if (fallbackOrgIds == null) {
|
||||
return new PermissionOrgScope(true, Collections.emptyList());
|
||||
}
|
||||
orgIds = normalizeIdList(fallbackOrgIds);
|
||||
} catch (Exception e) {
|
||||
return new PermissionOrgScope(false, Collections.emptyList());
|
||||
}
|
||||
}
|
||||
return new PermissionOrgScope(false, orgIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 规整 ID 列表:去空、去重、去前后空格。
|
||||
*/
|
||||
private List<String> normalizeIdList(List<String> idList) {
|
||||
if (CollUtil.isEmpty(idList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return idList.stream()
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.map(StrUtil::trim)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为健康证类型。
|
||||
*/
|
||||
private boolean isHealthCertificate(String certificateType) {
|
||||
return StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.HEALTH_CERTIFICATE.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建图表统计结果对象。
|
||||
*/
|
||||
private CertificateAppRiskChartVO buildChart(List<CertificateAppStatusCountVO> statusCountList) {
|
||||
Map<Integer, Integer> countMap = CollUtil.emptyIfNull(statusCountList)
|
||||
.stream()
|
||||
.filter(item -> item != null && item.getStatus() != null)
|
||||
.collect(Collectors.toMap(
|
||||
CertificateAppStatusCountVO::getStatus,
|
||||
item -> item.getCount() == null ? 0 : item.getCount(),
|
||||
Integer::sum
|
||||
));
|
||||
|
||||
int missingCount = countMap.getOrDefault(STATUS_MISSING, 0);
|
||||
int nearExpireCount = countMap.getOrDefault(STATUS_NEAR_EXPIRE, 0);
|
||||
int expiredCount = countMap.getOrDefault(STATUS_EXPIRED, 0);
|
||||
int normalCount = countMap.getOrDefault(STATUS_NORMAL, 0);
|
||||
int totalCount = missingCount + nearExpireCount + expiredCount + normalCount;
|
||||
|
||||
CertificateAppRiskChartVO chartVO = new CertificateAppRiskChartVO();
|
||||
chartVO.setMissingCount(missingCount);
|
||||
chartVO.setNearExpireCount(nearExpireCount);
|
||||
chartVO.setExpiredCount(expiredCount);
|
||||
chartVO.setNormalCount(normalCount);
|
||||
chartVO.setTotalCount(totalCount);
|
||||
|
||||
chartVO.setStatusList(Arrays.asList(
|
||||
buildStatusCount(STATUS_MISSING, missingCount),
|
||||
buildStatusCount(STATUS_NEAR_EXPIRE, nearExpireCount),
|
||||
buildStatusCount(STATUS_EXPIRED, expiredCount),
|
||||
buildStatusCount(STATUS_NORMAL, normalCount)
|
||||
));
|
||||
return chartVO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建单个状态统计项。
|
||||
*/
|
||||
private CertificateAppStatusCountVO buildStatusCount(Integer status, Integer count) {
|
||||
CertificateAppStatusCountVO item = new CertificateAppStatusCountVO();
|
||||
item.setStatus(status);
|
||||
item.setStatusName(resolveStatusName(status));
|
||||
item.setCount(count == null ? 0 : count);
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建空图表结果。
|
||||
*/
|
||||
private CertificateAppRiskChartVO buildEmptyChart() {
|
||||
return buildChart(Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建员工风险空分页。
|
||||
*/
|
||||
private PageInfo<CertificateAppEmployeeRiskVO> buildEmptyEmployeePage(long current, long pageSize) {
|
||||
PageInfo<CertificateAppEmployeeRiskVO> pageInfo = new PageInfo<>();
|
||||
pageInfo.setList(Collections.emptyList());
|
||||
pageInfo.setPageNum((int) current);
|
||||
pageInfo.setPageSize((int) pageSize);
|
||||
pageInfo.setTotal(0);
|
||||
return pageInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建门店风险空分页。
|
||||
*/
|
||||
private PageInfo<CertificateAppStoreRiskVO> buildEmptyStorePage(long current, long pageSize) {
|
||||
PageInfo<CertificateAppStoreRiskVO> pageInfo = new PageInfo<>();
|
||||
pageInfo.setList(Collections.emptyList());
|
||||
pageInfo.setPageNum((int) current);
|
||||
pageInfo.setPageSize((int) pageSize);
|
||||
pageInfo.setTotal(0);
|
||||
return pageInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充门店风险返回项的展示字段。
|
||||
*/
|
||||
private void fillStoreRiskItem(List<CertificateAppStoreRiskVO> records,Map<String, OrganizeBaseInfoVO> organizeBaseInfoVOMap) {
|
||||
if (CollectionUtil.isEmpty(records)) {
|
||||
return;
|
||||
}
|
||||
for (CertificateAppStoreRiskVO item : records){
|
||||
OrganizeBaseInfoVO organizeBaseInfoVO = organizeBaseInfoVOMap.get(item.getSubjectId());
|
||||
if(Objects.isNull(organizeBaseInfoVO)){
|
||||
continue;
|
||||
}
|
||||
item.setOrganizeId(organizeBaseInfoVO.getId());
|
||||
item.setOrganizeName(organizeBaseInfoVO.getName());
|
||||
String storeTag = organizeBaseInfoVO.getStoreTag();
|
||||
item.setStoreTag(storeTag);
|
||||
item.setOrganizeLeaderName(organizeBaseInfoVO.getLeaderName());
|
||||
item.setOrganizeLeaderId(organizeBaseInfoVO.getLeaderId());
|
||||
FtbStoreTagEnum storeTagEnum = FtbStoreTagEnum.fromName(storeTag);
|
||||
if(Objects.nonNull(storeTagEnum)){
|
||||
item.setStoreTagName(storeTagEnum.getLabel());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态编码转中文名称。
|
||||
*/
|
||||
private String resolveStatusName(Integer status) {
|
||||
if (Integer.valueOf(STATUS_MISSING).equals(status)) {
|
||||
return "缺失";
|
||||
}
|
||||
if (Integer.valueOf(STATUS_EXPIRED).equals(status)) {
|
||||
return "过期";
|
||||
}
|
||||
if (Integer.valueOf(STATUS_NEAR_EXPIRE).equals(status)) {
|
||||
return "临期";
|
||||
}
|
||||
if (Integer.valueOf(STATUS_NORMAL).equals(status)) {
|
||||
return "正常";
|
||||
}
|
||||
return "-";
|
||||
}
|
||||
|
||||
/**
|
||||
* 证照类型编码转中文名称。
|
||||
*/
|
||||
private String resolveCertificateTypeName(String certificateType,String templateId,Map<String,String> templateIdAndNames) {
|
||||
if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.HEALTH_CERTIFICATE.getType())) {
|
||||
return "健康证";
|
||||
}
|
||||
if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.BUSINESS_LICENSE.getType())) {
|
||||
return "营业执照";
|
||||
}
|
||||
if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.HYGIENE_LICENSE.getType())) {
|
||||
return "食品经营许可证";
|
||||
}
|
||||
if (StrUtil.isBlank(certificateType)) {
|
||||
return "证照";
|
||||
}
|
||||
String templateName = templateIdAndNames.get(templateId);
|
||||
if(StringUtil.isNotBlank(templateName)){
|
||||
return templateName;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户维度权限范围。
|
||||
*/
|
||||
private static class PermissionUserScope {
|
||||
private final Boolean allDataPermission;
|
||||
private final List<String> userIds;
|
||||
|
||||
private PermissionUserScope(Boolean allDataPermission, List<String> userIds) {
|
||||
this.allDataPermission = allDataPermission;
|
||||
this.userIds = userIds;
|
||||
}
|
||||
|
||||
public Boolean getAllDataPermission() {
|
||||
return allDataPermission;
|
||||
}
|
||||
|
||||
public List<String> getUserIds() {
|
||||
return userIds;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 组织维度权限范围。
|
||||
*/
|
||||
private static class PermissionOrgScope {
|
||||
private final Boolean allDataPermission;
|
||||
private final List<String> orgIds;
|
||||
|
||||
private PermissionOrgScope(Boolean allDataPermission, List<String> orgIds) {
|
||||
this.allDataPermission = allDataPermission;
|
||||
this.orgIds = orgIds;
|
||||
}
|
||||
|
||||
public Boolean getAllDataPermission() {
|
||||
return allDataPermission;
|
||||
}
|
||||
|
||||
public List<String> getOrgIds() {
|
||||
return orgIds;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,574 @@
|
||||
package jnpf.certificate.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import io.seata.spring.annotation.GlobalTransactional;
|
||||
import jnpf.certificate.mapper.CertificateBusinessLicenseExtMapper;
|
||||
import jnpf.certificate.mapper.CertificateHygieneLicenseExtMapper;
|
||||
import jnpf.certificate.mapper.CertificateInstanceItemMapper;
|
||||
import jnpf.certificate.mapper.CertificateInstanceMapper;
|
||||
import jnpf.certificate.service.CertificateManageApiService;
|
||||
import jnpf.model.certificate.po.CertificateBusinessLicenseExtEntity;
|
||||
import jnpf.model.certificate.po.CertificateHygieneLicenseExtEntity;
|
||||
import jnpf.model.certificate.po.CertificateInstanceEntity;
|
||||
import jnpf.model.certificate.po.CertificateInstanceItemEntity;
|
||||
import jnpf.model.certificate.vo.CertificateOrganizeBusinessLicenseVO;
|
||||
import jnpf.model.warningnotice.enums.CertificateTypeEnum;
|
||||
import jnpf.util.FtbUtil;
|
||||
import jnpf.util.ServiceException;
|
||||
import jnpf.util.StringUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 组织营业执照开放接口服务实现。
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class CertificateManageApiServiceImpl implements CertificateManageApiService {
|
||||
|
||||
private static final int STATUS_MISSING = 1;
|
||||
private static final int STATUS_EXPIRED = 2;
|
||||
private static final int STATUS_NEAR_EXPIRE = 3;
|
||||
private static final int STATUS_NORMAL = 4;
|
||||
private static final int SUBJECT_TYPE_ORGANIZE = 2;
|
||||
private static final int SUBJECT_TYPE_STORE = 3;
|
||||
private static final int IS_LONG_TERM_NO = 0;
|
||||
private static final int NEAR_EXPIRE_DAYS = 30;
|
||||
|
||||
@Autowired
|
||||
private CertificateInstanceMapper certificateInstanceMapper;
|
||||
@Autowired
|
||||
private CertificateBusinessLicenseExtMapper certificateBusinessLicenseExtMapper;
|
||||
@Autowired
|
||||
private CertificateHygieneLicenseExtMapper certificateHygieneLicenseExtMapper;
|
||||
@Autowired
|
||||
private CertificateInstanceItemMapper certificateInstanceItemMapper;
|
||||
|
||||
/**
|
||||
* 根据组织ID查询营业执照信息。
|
||||
*/
|
||||
@Override
|
||||
public CertificateOrganizeBusinessLicenseVO queryBusinessLicense(String organizeId) {
|
||||
ServiceException.isTrue(StrUtil.isNotBlank(organizeId), "组织ID不能为空");
|
||||
return buildOrganizeBusinessLicenseVO(StrUtil.trim(organizeId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据组织ID列表批量查询营业执照信息。
|
||||
*/
|
||||
@Override
|
||||
public List<CertificateOrganizeBusinessLicenseVO> queryBusinessLicenseBatch(Collection<String> organizeIds) {
|
||||
if (CollUtil.isEmpty(organizeIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> targetIds = organizeIds.stream()
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.map(StrUtil::trim)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
if (CollUtil.isEmpty(targetIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Map<String, CertificateInstanceEntity> instanceMap = queryBusinessInstanceMap(targetIds);
|
||||
Map<String, CertificateBusinessLicenseExtEntity> extMap = queryBusinessExtMap(instanceMap.values());
|
||||
|
||||
List<CertificateOrganizeBusinessLicenseVO> result = new ArrayList<>(targetIds.size());
|
||||
for (String organizeId : targetIds) {
|
||||
CertificateInstanceEntity instance = instanceMap.get(organizeId);
|
||||
CertificateBusinessLicenseExtEntity ext = instance == null ? null : extMap.get(instance.getId());
|
||||
result.add(convertToVO(organizeId, instance, ext));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存组织营业执照信息。
|
||||
*/
|
||||
@Override
|
||||
@GlobalTransactional
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveBusinessLicense(CertificateOrganizeBusinessLicenseVO req) {
|
||||
ServiceException.notNull(req, "请求参数不能为空");
|
||||
String organizeId = StrUtil.trim(req.getOrganizeId());
|
||||
ServiceException.isTrue(StrUtil.isNotBlank(organizeId), "组织ID不能为空");
|
||||
|
||||
Date establishDate = parseDate(req.getEstablishDate(), "成立时间");
|
||||
Date issueDate = parseDate(req.getIssueDate(), "发证日期");
|
||||
Date expireDate = parseDate(req.getExpireDate(), "到期日期");
|
||||
Integer isLongTerm = req.getIsLongTerm() == null ? 0 : req.getIsLongTerm();
|
||||
if (Integer.valueOf(1).equals(isLongTerm)) {
|
||||
expireDate = null;
|
||||
}
|
||||
|
||||
String certificateType = CertificateTypeEnum.BUSINESS_LICENSE.getType();
|
||||
String certificateImage = trimToNull(req.getCertificateImage());
|
||||
int status = calculateStatus(isLongTerm, expireDate);
|
||||
|
||||
String instanceId = upsertBusinessInstance(
|
||||
organizeId,
|
||||
certificateType,
|
||||
certificateImage,
|
||||
issueDate,
|
||||
expireDate,
|
||||
isLongTerm,
|
||||
status
|
||||
);
|
||||
if(Objects.isNull(instanceId)){
|
||||
return;
|
||||
}
|
||||
upsertBusinessExt(instanceId, req, establishDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化门店默认缺失证照(营业执照、食品经营许可证)。
|
||||
*/
|
||||
@Override
|
||||
@GlobalTransactional
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void initStoreDefaultCertificates(String storeId) {
|
||||
String targetStoreId = StrUtil.trim(storeId);
|
||||
ServiceException.isTrue(StrUtil.isNotBlank(targetStoreId), "门店ID不能为空");
|
||||
|
||||
List<String> defaultTypes = Arrays.asList(
|
||||
CertificateTypeEnum.BUSINESS_LICENSE.getType(),
|
||||
CertificateTypeEnum.HYGIENE_LICENSE.getType()
|
||||
);
|
||||
for (String certificateType : defaultTypes) {
|
||||
initStoreDefaultCertificate(targetStoreId, certificateType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据组织ID删除该主体下全部证照信息。
|
||||
*/
|
||||
@Override
|
||||
@GlobalTransactional
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteBusinessLicense(String organizeId, String loginUserId) {
|
||||
String targetOrganizeId = StrUtil.trim(organizeId);
|
||||
ServiceException.isTrue(StrUtil.isNotBlank(targetOrganizeId), "组织ID不能为空");
|
||||
|
||||
Date now = new Date();
|
||||
LambdaUpdateWrapper<CertificateInstanceEntity> instanceUpdateWrapper = Wrappers.lambdaUpdate();
|
||||
instanceUpdateWrapper.eq(CertificateInstanceEntity::getEnabledMark, 0);
|
||||
instanceUpdateWrapper.eq(CertificateInstanceEntity::getSubjectId, targetOrganizeId);
|
||||
instanceUpdateWrapper.set(CertificateInstanceEntity::getEnabledMark, 1);
|
||||
instanceUpdateWrapper.set(CertificateInstanceEntity::getLastModifyTime, now);
|
||||
if(StringUtil.isNotBlank(loginUserId)){
|
||||
instanceUpdateWrapper.set(CertificateInstanceEntity::getLastModifyUserId, loginUserId);
|
||||
}
|
||||
certificateInstanceMapper.update(null, instanceUpdateWrapper);
|
||||
|
||||
LambdaQueryWrapper<CertificateInstanceEntity> instanceQueryWrapper = Wrappers.lambdaQuery();
|
||||
instanceQueryWrapper.eq(CertificateInstanceEntity::getSubjectType, SUBJECT_TYPE_ORGANIZE);
|
||||
instanceQueryWrapper.eq(CertificateInstanceEntity::getSubjectId, targetOrganizeId);
|
||||
List<CertificateInstanceEntity> allBusinessInstances = certificateInstanceMapper.selectList(instanceQueryWrapper);
|
||||
if (CollUtil.isEmpty(allBusinessInstances)) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> instanceIds = allBusinessInstances.stream()
|
||||
.map(CertificateInstanceEntity::getId)
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.map(StrUtil::trim)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
if (CollUtil.isEmpty(instanceIds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LambdaUpdateWrapper<CertificateBusinessLicenseExtEntity> extUpdateWrapper = Wrappers.lambdaUpdate();
|
||||
extUpdateWrapper.eq(CertificateBusinessLicenseExtEntity::getEnabledMark, 0);
|
||||
extUpdateWrapper.in(CertificateBusinessLicenseExtEntity::getInstanceId, instanceIds);
|
||||
extUpdateWrapper.set(CertificateBusinessLicenseExtEntity::getEnabledMark, 1);
|
||||
extUpdateWrapper.set(CertificateBusinessLicenseExtEntity::getLastModifyTime, now);
|
||||
extUpdateWrapper.set(CertificateBusinessLicenseExtEntity::getLastModifyUserId, loginUserId);
|
||||
certificateBusinessLicenseExtMapper.update(null, extUpdateWrapper);
|
||||
|
||||
LambdaUpdateWrapper<CertificateHygieneLicenseExtEntity> hygieneExtUpdateWrapper = Wrappers.lambdaUpdate();
|
||||
hygieneExtUpdateWrapper.eq(CertificateHygieneLicenseExtEntity::getEnabledMark, 0);
|
||||
hygieneExtUpdateWrapper.in(CertificateHygieneLicenseExtEntity::getInstanceId, instanceIds);
|
||||
hygieneExtUpdateWrapper.set(CertificateHygieneLicenseExtEntity::getEnabledMark, 1);
|
||||
hygieneExtUpdateWrapper.set(CertificateHygieneLicenseExtEntity::getLastModifyTime, now);
|
||||
hygieneExtUpdateWrapper.set(CertificateHygieneLicenseExtEntity::getLastModifyUserId, loginUserId);
|
||||
certificateHygieneLicenseExtMapper.update(null, hygieneExtUpdateWrapper);
|
||||
|
||||
LambdaUpdateWrapper<CertificateInstanceItemEntity> itemUpdateWrapper = Wrappers.lambdaUpdate();
|
||||
itemUpdateWrapper.eq(CertificateInstanceItemEntity::getEnabledMark, 0);
|
||||
itemUpdateWrapper.in(CertificateInstanceItemEntity::getInstanceId, instanceIds);
|
||||
itemUpdateWrapper.set(CertificateInstanceItemEntity::getEnabledMark, 1);
|
||||
itemUpdateWrapper.set(CertificateInstanceItemEntity::getLastModifyTime, now);
|
||||
itemUpdateWrapper.set(CertificateInstanceItemEntity::getLastModifyUserId, loginUserId);
|
||||
certificateInstanceItemMapper.update(null, itemUpdateWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询并组装组织营业执照返回对象。
|
||||
*/
|
||||
private CertificateOrganizeBusinessLicenseVO buildOrganizeBusinessLicenseVO(String organizeId) {
|
||||
CertificateInstanceEntity instance = queryLatestBusinessInstance(organizeId);
|
||||
CertificateBusinessLicenseExtEntity ext = instance == null ? null : queryLatestBusinessExt(instance.getId());
|
||||
return convertToVO(organizeId, instance, ext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量查询营业执照主表数据映射。
|
||||
*/
|
||||
private Map<String, CertificateInstanceEntity> queryBusinessInstanceMap(List<String> organizeIds) {
|
||||
LambdaQueryWrapper<CertificateInstanceEntity> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(CertificateInstanceEntity::getEnabledMark, 0);
|
||||
wrapper.eq(CertificateInstanceEntity::getSubjectType, SUBJECT_TYPE_ORGANIZE);
|
||||
wrapper.eq(CertificateInstanceEntity::getCertificateType, CertificateTypeEnum.BUSINESS_LICENSE.getType());
|
||||
wrapper.in(CertificateInstanceEntity::getSubjectId, organizeIds);
|
||||
wrapper.orderByDesc(CertificateInstanceEntity::getLastModifyTime);
|
||||
wrapper.orderByDesc(CertificateInstanceEntity::getCreatorTime);
|
||||
List<CertificateInstanceEntity> instanceList = certificateInstanceMapper.selectList(wrapper);
|
||||
if (CollUtil.isEmpty(instanceList)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, CertificateInstanceEntity> result = new LinkedHashMap<>();
|
||||
for (CertificateInstanceEntity entity : instanceList) {
|
||||
if (entity == null || StrUtil.isBlank(entity.getSubjectId())) {
|
||||
continue;
|
||||
}
|
||||
String subjectId = StrUtil.trim(entity.getSubjectId());
|
||||
result.putIfAbsent(subjectId, entity);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量查询营业执照扩展表数据映射。
|
||||
*/
|
||||
private Map<String, CertificateBusinessLicenseExtEntity> queryBusinessExtMap(Iterable<CertificateInstanceEntity> instances) {
|
||||
List<String> instanceIds = new ArrayList<>();
|
||||
for (CertificateInstanceEntity entity : instances) {
|
||||
if (entity != null && StrUtil.isNotBlank(entity.getId())) {
|
||||
instanceIds.add(StrUtil.trim(entity.getId()));
|
||||
}
|
||||
}
|
||||
if (CollUtil.isEmpty(instanceIds)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
LambdaQueryWrapper<CertificateBusinessLicenseExtEntity> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(CertificateBusinessLicenseExtEntity::getEnabledMark, 0);
|
||||
wrapper.in(CertificateBusinessLicenseExtEntity::getInstanceId, instanceIds);
|
||||
wrapper.orderByDesc(CertificateBusinessLicenseExtEntity::getLastModifyTime);
|
||||
wrapper.orderByDesc(CertificateBusinessLicenseExtEntity::getCreatorTime);
|
||||
List<CertificateBusinessLicenseExtEntity> extList = certificateBusinessLicenseExtMapper.selectList(wrapper);
|
||||
if (CollUtil.isEmpty(extList)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, CertificateBusinessLicenseExtEntity> result = new LinkedHashMap<>();
|
||||
for (CertificateBusinessLicenseExtEntity ext : extList) {
|
||||
if (ext == null || StrUtil.isBlank(ext.getInstanceId())) {
|
||||
continue;
|
||||
}
|
||||
result.putIfAbsent(StrUtil.trim(ext.getInstanceId()), ext);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询组织最新营业执照主表记录。
|
||||
*/
|
||||
private CertificateInstanceEntity queryLatestBusinessInstance(String organizeId) {
|
||||
LambdaQueryWrapper<CertificateInstanceEntity> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(CertificateInstanceEntity::getEnabledMark, 0);
|
||||
wrapper.eq(CertificateInstanceEntity::getSubjectType, SUBJECT_TYPE_ORGANIZE);
|
||||
wrapper.eq(CertificateInstanceEntity::getSubjectId, organizeId);
|
||||
wrapper.eq(CertificateInstanceEntity::getCertificateType, CertificateTypeEnum.BUSINESS_LICENSE.getType());
|
||||
wrapper.orderByDesc(CertificateInstanceEntity::getLastModifyTime);
|
||||
wrapper.orderByDesc(CertificateInstanceEntity::getCreatorTime);
|
||||
List<CertificateInstanceEntity> list = certificateInstanceMapper.selectList(wrapper);
|
||||
return CollUtil.isEmpty(list) ? null : list.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询实例最新营业执照扩展记录。
|
||||
*/
|
||||
private CertificateBusinessLicenseExtEntity queryLatestBusinessExt(String instanceId) {
|
||||
LambdaQueryWrapper<CertificateBusinessLicenseExtEntity> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(CertificateBusinessLicenseExtEntity::getEnabledMark, 0);
|
||||
wrapper.eq(CertificateBusinessLicenseExtEntity::getInstanceId, instanceId);
|
||||
wrapper.orderByDesc(CertificateBusinessLicenseExtEntity::getLastModifyTime);
|
||||
wrapper.orderByDesc(CertificateBusinessLicenseExtEntity::getCreatorTime);
|
||||
List<CertificateBusinessLicenseExtEntity> list = certificateBusinessLicenseExtMapper.selectList(wrapper);
|
||||
return CollUtil.isEmpty(list) ? null : list.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存或更新营业执照主表信息。
|
||||
*/
|
||||
private String upsertBusinessInstance(String organizeId,
|
||||
String certificateType,
|
||||
String certificateImage,
|
||||
Date issueDate,
|
||||
Date expireDate,
|
||||
Integer isLongTerm,
|
||||
Integer status) {
|
||||
LambdaUpdateWrapper<CertificateInstanceEntity> updateWrapper = buildBusinessInstanceUpdateWrapper(
|
||||
organizeId, certificateType, certificateImage, issueDate, expireDate, isLongTerm, status
|
||||
);
|
||||
int updateRows = certificateInstanceMapper.update(null, updateWrapper);
|
||||
if (updateRows > 0) {
|
||||
CertificateInstanceEntity latest = queryLatestBusinessInstance(organizeId);
|
||||
ServiceException.notNull(latest, "营业执照实例更新成功但未查询到数据");
|
||||
return latest.getId();
|
||||
}
|
||||
|
||||
CertificateInstanceEntity insertEntity = new CertificateInstanceEntity();
|
||||
insertEntity.setId(FtbUtil.getId());
|
||||
insertEntity.setCertificateType(certificateType);
|
||||
insertEntity.setSubjectType(SUBJECT_TYPE_ORGANIZE);
|
||||
insertEntity.setSubjectId(organizeId);
|
||||
insertEntity.setCertificateImage(certificateImage);
|
||||
insertEntity.setIssueDate(issueDate);
|
||||
insertEntity.setExpireDate(expireDate);
|
||||
if(Objects.nonNull(expireDate)){
|
||||
insertEntity.setExpireTimestamp(expireDate.getTime());
|
||||
}
|
||||
insertEntity.setIsLongTerm(isLongTerm);
|
||||
insertEntity.setStatus(status);
|
||||
insertEntity.setEnabledMark(0);
|
||||
try {
|
||||
certificateInstanceMapper.insert(insertEntity);
|
||||
return insertEntity.getId();
|
||||
} catch (DuplicateKeyException e) {
|
||||
log.warn("营业执照实例插入出现重复键,执行重试更新。organizeId={}, certificateType={}", organizeId, certificateType, e);
|
||||
LambdaUpdateWrapper<CertificateInstanceEntity> retryWrapper = buildBusinessInstanceUpdateWrapper(
|
||||
organizeId, certificateType, certificateImage, issueDate, expireDate, isLongTerm, status
|
||||
);
|
||||
int retryRows = certificateInstanceMapper.update(null, retryWrapper);
|
||||
if(retryRows <= 0){
|
||||
log.error("upsertBusinessInstance fail.insertEntity:{}",insertEntity);
|
||||
return null;
|
||||
}
|
||||
CertificateInstanceEntity latest = queryLatestBusinessInstance(organizeId);
|
||||
ServiceException.notNull(latest, "营业执照实例重试更新成功但未查询到数据");
|
||||
return latest.getId();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化单个门店默认证照记录。
|
||||
*/
|
||||
private void initStoreDefaultCertificate(String storeId, String certificateType) {
|
||||
LambdaQueryWrapper<CertificateInstanceEntity> queryWrapper = Wrappers.lambdaQuery();
|
||||
queryWrapper.eq(CertificateInstanceEntity::getEnabledMark, 0);
|
||||
queryWrapper.eq(CertificateInstanceEntity::getSubjectType, SUBJECT_TYPE_STORE);
|
||||
queryWrapper.eq(CertificateInstanceEntity::getSubjectId, storeId);
|
||||
queryWrapper.eq(CertificateInstanceEntity::getCertificateType, certificateType);
|
||||
queryWrapper.last("limit 1");
|
||||
CertificateInstanceEntity existed = certificateInstanceMapper.selectOne(queryWrapper);
|
||||
if (existed != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
CertificateInstanceEntity insertEntity = new CertificateInstanceEntity();
|
||||
insertEntity.setId(FtbUtil.getId());
|
||||
insertEntity.setCertificateType(certificateType);
|
||||
insertEntity.setSubjectType(SUBJECT_TYPE_STORE);
|
||||
insertEntity.setSubjectId(storeId);
|
||||
insertEntity.setIsLongTerm(IS_LONG_TERM_NO);
|
||||
insertEntity.setStatus(STATUS_MISSING);
|
||||
insertEntity.setEnabledMark(0);
|
||||
try {
|
||||
certificateInstanceMapper.insert(insertEntity);
|
||||
} catch (DuplicateKeyException e) {
|
||||
log.warn("初始化门店默认证照出现重复键,忽略。storeId={}, certificateType={}", storeId, certificateType, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存或更新营业执照扩展表信息。
|
||||
*/
|
||||
private void upsertBusinessExt(String instanceId, CertificateOrganizeBusinessLicenseVO req, Date establishDate) {
|
||||
String companyName = trimToNull(req.getCompanyName());
|
||||
String companyAddress = trimToNull(req.getCompanyAddress());
|
||||
String legalRepresentative = trimToNull(req.getLegalRepresentative());
|
||||
|
||||
LambdaUpdateWrapper<CertificateBusinessLicenseExtEntity> updateWrapper = buildBusinessExtUpdateWrapper(
|
||||
instanceId, companyName, companyAddress, legalRepresentative, establishDate
|
||||
);
|
||||
int updateRows = certificateBusinessLicenseExtMapper.update(null, updateWrapper);
|
||||
if (updateRows > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
CertificateBusinessLicenseExtEntity insertEntity = new CertificateBusinessLicenseExtEntity();
|
||||
insertEntity.setId(FtbUtil.getId());
|
||||
insertEntity.setInstanceId(instanceId);
|
||||
insertEntity.setCompanyName(companyName);
|
||||
insertEntity.setCompanyAddress(companyAddress);
|
||||
insertEntity.setLegalRepresentative(legalRepresentative);
|
||||
insertEntity.setEstablishDate(establishDate);
|
||||
insertEntity.setEnabledMark(0);
|
||||
try {
|
||||
certificateBusinessLicenseExtMapper.insert(insertEntity);
|
||||
} catch (DuplicateKeyException e) {
|
||||
log.warn("营业执照扩展插入出现重复键,执行重试更新。instanceId={}", instanceId, e);
|
||||
LambdaUpdateWrapper<CertificateBusinessLicenseExtEntity> retryWrapper = buildBusinessExtUpdateWrapper(
|
||||
instanceId, companyName, companyAddress, legalRepresentative, establishDate
|
||||
);
|
||||
int retryRows = certificateBusinessLicenseExtMapper.update(null, retryWrapper);
|
||||
ServiceException.isTrue(retryRows > 0, "营业执照扩展保存失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装营业执照返回对象。
|
||||
*/
|
||||
private CertificateOrganizeBusinessLicenseVO convertToVO(String organizeId,
|
||||
CertificateInstanceEntity instance,
|
||||
CertificateBusinessLicenseExtEntity ext) {
|
||||
CertificateOrganizeBusinessLicenseVO vo = new CertificateOrganizeBusinessLicenseVO();
|
||||
vo.setOrganizeId(organizeId);
|
||||
vo.setIsLongTerm(instance == null || instance.getIsLongTerm() == null ? 0 : instance.getIsLongTerm());
|
||||
if (instance != null) {
|
||||
vo.setCertificateImage(instance.getCertificateImage());
|
||||
vo.setIssueDate(formatDate(instance.getIssueDate()));
|
||||
vo.setExpireDate(formatDate(instance.getExpireDate()));
|
||||
vo.setBusinessTerm(buildBusinessTerm(instance.getIssueDate(), instance.getExpireDate(), vo.getIsLongTerm()));
|
||||
} else {
|
||||
vo.setBusinessTerm("[]");
|
||||
}
|
||||
if (ext != null) {
|
||||
vo.setCompanyName(ext.getCompanyName());
|
||||
vo.setLegalRepresentative(ext.getLegalRepresentative());
|
||||
vo.setCompanyAddress(ext.getCompanyAddress());
|
||||
vo.setEstablishDate(formatDate(ext.getEstablishDate()));
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算证照状态。
|
||||
*/
|
||||
private Integer calculateStatus(Integer isLongTerm, Date expireDate) {
|
||||
if (Integer.valueOf(1).equals(isLongTerm)) {
|
||||
return STATUS_NORMAL;
|
||||
}
|
||||
if (expireDate == null) {
|
||||
return STATUS_MISSING;
|
||||
}
|
||||
Date today = DateUtil.beginOfDay(DateUtil.date());
|
||||
Date target = DateUtil.beginOfDay(expireDate);
|
||||
if (target.before(today)) {
|
||||
return STATUS_EXPIRED;
|
||||
}
|
||||
long daysDiff = DateUtil.betweenDay(today, target, false);
|
||||
if (daysDiff <= NEAR_EXPIRE_DAYS) {
|
||||
return STATUS_NEAR_EXPIRE;
|
||||
}
|
||||
return STATUS_NORMAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析日期字符串。
|
||||
*/
|
||||
private Date parseDate(String dateText, String fieldName) {
|
||||
if (StrUtil.isBlank(dateText)) {
|
||||
return null;
|
||||
}
|
||||
int strLen = dateText.length();
|
||||
if(StringUtil.isNumeric(dateText) && (strLen == 10 || strLen == 13)){
|
||||
long date = Long.parseLong(dateText);
|
||||
if(dateText.length() == 10){
|
||||
date = date * 1000;
|
||||
}
|
||||
return new Date(date);
|
||||
}
|
||||
try {
|
||||
return DateUtil.parseDate(StrUtil.trim(dateText));
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException(fieldName + "格式不正确,正确格式为yyyy-MM-dd");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化日期。
|
||||
*/
|
||||
private String formatDate(Date date) {
|
||||
return date == null ? null : DateUtil.formatDate(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装营业期限展示文案。
|
||||
*/
|
||||
private String buildBusinessTerm(Date issueDate, Date expireDate, Integer isLongTerm) {
|
||||
if (Integer.valueOf(1).equals(isLongTerm)) {
|
||||
return "[]";
|
||||
}
|
||||
if (expireDate == null) {
|
||||
return "[]";
|
||||
}
|
||||
String issueText = issueDate == null ? "" : DateUtil.formatDate(issueDate);
|
||||
return "[\""+issueText + "\",\"" + DateUtil.formatDate(expireDate)+"\"]";
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建营业执照主表更新条件。
|
||||
*/
|
||||
private LambdaUpdateWrapper<CertificateInstanceEntity> buildBusinessInstanceUpdateWrapper(String organizeId,
|
||||
String certificateType,
|
||||
String certificateImage,
|
||||
Date issueDate,
|
||||
Date expireDate,
|
||||
Integer isLongTerm,
|
||||
Integer status) {
|
||||
LambdaUpdateWrapper<CertificateInstanceEntity> updateWrapper = Wrappers.lambdaUpdate();
|
||||
updateWrapper.eq(CertificateInstanceEntity::getSubjectId, organizeId);
|
||||
updateWrapper.eq(CertificateInstanceEntity::getCertificateType, certificateType);
|
||||
updateWrapper.set(CertificateInstanceEntity::getSubjectType, SUBJECT_TYPE_ORGANIZE);
|
||||
updateWrapper.set(CertificateInstanceEntity::getEnabledMark, 0);
|
||||
updateWrapper.set(CertificateInstanceEntity::getCertificateImage, certificateImage);
|
||||
updateWrapper.set(CertificateInstanceEntity::getIssueDate, issueDate);
|
||||
updateWrapper.set(CertificateInstanceEntity::getExpireDate, expireDate);
|
||||
if(Objects.nonNull(expireDate)){
|
||||
updateWrapper.set(CertificateInstanceEntity::getExpireTimestamp, expireDate.getTime());
|
||||
}
|
||||
updateWrapper.set(CertificateInstanceEntity::getIsLongTerm, isLongTerm);
|
||||
updateWrapper.set(CertificateInstanceEntity::getStatus, status);
|
||||
return updateWrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建营业执照扩展表更新条件。
|
||||
*/
|
||||
private LambdaUpdateWrapper<CertificateBusinessLicenseExtEntity> buildBusinessExtUpdateWrapper(String instanceId,
|
||||
String companyName,
|
||||
String companyAddress,
|
||||
String legalRepresentative,
|
||||
Date establishDate) {
|
||||
LambdaUpdateWrapper<CertificateBusinessLicenseExtEntity> updateWrapper = Wrappers.lambdaUpdate();
|
||||
updateWrapper.eq(CertificateBusinessLicenseExtEntity::getEnabledMark, 0);
|
||||
updateWrapper.eq(CertificateBusinessLicenseExtEntity::getInstanceId, instanceId);
|
||||
updateWrapper.set(CertificateBusinessLicenseExtEntity::getCompanyName, companyName);
|
||||
updateWrapper.set(CertificateBusinessLicenseExtEntity::getCompanyAddress, companyAddress);
|
||||
updateWrapper.set(CertificateBusinessLicenseExtEntity::getLegalRepresentative, legalRepresentative);
|
||||
updateWrapper.set(CertificateBusinessLicenseExtEntity::getEstablishDate, establishDate);
|
||||
return updateWrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 去首尾空格,空串返回 null。
|
||||
*/
|
||||
private String trimToNull(String text) {
|
||||
String value = StrUtil.trim(text);
|
||||
return StrUtil.isBlank(value) ? null : value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,481 @@
|
||||
package jnpf.certificate.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Assert;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import jnpf.certificate.mapper.CertificateBusinessLicenseExtMapper;
|
||||
import jnpf.certificate.mapper.CertificateHygieneLicenseExtMapper;
|
||||
import jnpf.certificate.mapper.CertificateInstanceItemMapper;
|
||||
import jnpf.certificate.mapper.CertificateInstanceMapper;
|
||||
import jnpf.certificate.service.CertificateManageService;
|
||||
import jnpf.model.certificate.po.CertificateBusinessLicenseExtEntity;
|
||||
import jnpf.model.certificate.po.CertificateHygieneLicenseExtEntity;
|
||||
import jnpf.model.certificate.po.CertificateInstanceEntity;
|
||||
import jnpf.model.certificate.po.CertificateInstanceItemEntity;
|
||||
import jnpf.model.certificate.req.CertificateInstanceItemReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppBusinessLicenseUpdateReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppHealthCertificateUpdateReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppHygieneLicenseUpdateReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppStoreCustomUpdateReq;
|
||||
import jnpf.model.certificate.vo.CertificateInstanceItemVO;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppBusinessLicenseDetailVO;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppCertificateDetailVO;
|
||||
import jnpf.model.certificate.vo.app.HealthCertificateDetailVO;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppHygieneLicenseDetailVO;
|
||||
import jnpf.model.certificate.vo.app.CertificateAppStoreCustomDetailVO;
|
||||
import jnpf.model.warningnotice.enums.CertificateTypeEnum;
|
||||
import jnpf.util.FtbUtil;
|
||||
import jnpf.util.ServiceException;
|
||||
import jnpf.util.UserProvider;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* App端证照管理服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class CertificateManageServiceImpl implements CertificateManageService {
|
||||
|
||||
private static final int STATUS_MISSING = 1;
|
||||
private static final int STATUS_EXPIRED = 2;
|
||||
private static final int STATUS_NEAR_EXPIRE = 3;
|
||||
private static final int STATUS_NORMAL = 4;
|
||||
private static final int NEAR_EXPIRE_DAYS = 30;
|
||||
|
||||
@Autowired
|
||||
private CertificateInstanceMapper certificateInstanceMapper;
|
||||
@Autowired
|
||||
private CertificateBusinessLicenseExtMapper certificateBusinessLicenseExtMapper;
|
||||
@Autowired
|
||||
private CertificateHygieneLicenseExtMapper certificateHygieneLicenseExtMapper;
|
||||
@Autowired
|
||||
private CertificateInstanceItemMapper certificateInstanceItemMapper;
|
||||
|
||||
/**
|
||||
* 根据证照实例ID查询详情,按证照类型返回不同明细对象。
|
||||
*/
|
||||
@Override
|
||||
public CertificateAppCertificateDetailVO queryInfo(String certificateInstanceId) {
|
||||
ServiceException.isTrue(StrUtil.isNotBlank(certificateInstanceId), "证照实例ID不能为空");
|
||||
CertificateInstanceEntity entity = queryActiveEntity(StrUtil.trim(certificateInstanceId));
|
||||
Optional<CertificateTypeEnum> certificateTypeEnumOptional = CertificateTypeEnum.getByType(entity.getCertificateType());
|
||||
ServiceException.isTrue(certificateTypeEnumOptional.isPresent(), "不支持的证照类型");
|
||||
CertificateTypeEnum certificateTypeEnum = certificateTypeEnumOptional.get();
|
||||
|
||||
Integer status = calculateStatus(entity.getIsLongTerm(), entity.getExpireDate());
|
||||
CertificateAppCertificateDetailVO result = new CertificateAppCertificateDetailVO();
|
||||
result.setCertificateInstanceId(entity.getId());
|
||||
result.setCertificateType(certificateTypeEnum.getType());
|
||||
result.setStatus(status);
|
||||
result.setStatusName(resolveStatusName(status));
|
||||
|
||||
if (CertificateTypeEnum.HEALTH_CERTIFICATE.equals(certificateTypeEnum)) {
|
||||
result.setDetail(buildHealthDetail(entity));
|
||||
return result;
|
||||
}
|
||||
if (CertificateTypeEnum.BUSINESS_LICENSE.equals(certificateTypeEnum)) {
|
||||
result.setDetail(buildBusinessLicenseDetail(entity));
|
||||
return result;
|
||||
}
|
||||
if (CertificateTypeEnum.HYGIENE_LICENSE.equals(certificateTypeEnum)) {
|
||||
result.setDetail(buildHygieneLicenseDetail(entity));
|
||||
return result;
|
||||
}
|
||||
result.setDetail(buildStoreCustomDetail(entity));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新健康证数据。
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateHealthCertificate(CertificateAppHealthCertificateUpdateReq req) {
|
||||
ServiceException.notNull(req, "请求参数不能为空");
|
||||
CertificateInstanceEntity entity = queryAndAssertType(req.getCertificateInstanceId(), CertificateTypeEnum.HEALTH_CERTIFICATE, "健康证");
|
||||
|
||||
Date issueDate = parseDate(req.getIssueDate(), "发证日期");
|
||||
Date expireDate = parseDate(req.getExpireDate(), "到期日期");
|
||||
|
||||
//原生app增量更新,可能不传值,则使用数据库值
|
||||
if(Objects.isNull(issueDate)){
|
||||
issueDate = entity.getIssueDate();
|
||||
}
|
||||
if(Objects.isNull(expireDate)){
|
||||
expireDate = entity.getExpireDate();
|
||||
}
|
||||
if(Objects.nonNull(issueDate) && Objects.nonNull(expireDate) && issueDate.after(expireDate)){
|
||||
throw new ServiceException("发证日期不能大于到期日期");
|
||||
}
|
||||
|
||||
entity.setCertificateImage(trimToNull(req.getCertificateImage()));
|
||||
entity.setIssueDate(issueDate);
|
||||
entity.setExpireDate(expireDate);
|
||||
entity.setIsLongTerm(0);
|
||||
entity.setStatus(calculateStatus(entity.getIsLongTerm(), entity.getExpireDate()));
|
||||
int r = certificateInstanceMapper.updateById(entity);
|
||||
if(r <= 0){
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新营业执照数据,并同步营业执照扩展表。
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateBusinessLicense(CertificateAppBusinessLicenseUpdateReq req) {
|
||||
ServiceException.notNull(req, "请求参数不能为空");
|
||||
CertificateInstanceEntity entity = queryAndAssertType(req.getCertificateInstanceId(), CertificateTypeEnum.BUSINESS_LICENSE, "营业执照");
|
||||
|
||||
Date establishDate = parseDate(req.getEstablishDate(), "成立时间");
|
||||
Date issueDate = parseDate(req.getIssueDate(), "发证日期");
|
||||
Date expireDate = parseDate(req.getExpireDate(), "到期日期");
|
||||
Integer isLongTerm = req.getIsLongTerm() == null ? 0 : req.getIsLongTerm();
|
||||
if (Integer.valueOf(1).equals(isLongTerm)) {
|
||||
expireDate = null;
|
||||
}
|
||||
|
||||
entity.setCertificateImage(trimToNull(req.getCertificateImage()));
|
||||
entity.setCertificateNo(trimToNull(req.getCertificateNo()));
|
||||
entity.setIssueDate(issueDate);
|
||||
entity.setExpireDate(expireDate);
|
||||
entity.setIsLongTerm(isLongTerm);
|
||||
entity.setStatus(calculateStatus(entity.getIsLongTerm(), entity.getExpireDate()));
|
||||
certificateInstanceMapper.updateById(entity);
|
||||
|
||||
saveBusinessLicenseExt(entity.getId(), req, establishDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新食品经营许可证数据,并同步食品许可扩展表。
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateHygieneLicense(CertificateAppHygieneLicenseUpdateReq req) {
|
||||
ServiceException.notNull(req, "请求参数不能为空");
|
||||
CertificateInstanceEntity entity = queryAndAssertType(req.getCertificateInstanceId(), CertificateTypeEnum.HYGIENE_LICENSE, "食品经营许可证");
|
||||
|
||||
Date issueDate = parseDate(req.getIssueDate(), "发证日期");
|
||||
Date expireDate = parseDate(req.getExpireDate(), "到期日期");
|
||||
|
||||
entity.setCertificateImage(trimToNull(req.getCertificateImage()));
|
||||
entity.setIssueDate(issueDate);
|
||||
entity.setExpireDate(expireDate);
|
||||
entity.setIsLongTerm(0);
|
||||
entity.setStatus(calculateStatus(entity.getIsLongTerm(), entity.getExpireDate()));
|
||||
certificateInstanceMapper.updateById(entity);
|
||||
|
||||
saveHygieneLicenseExt(entity.getId(), req);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新门店自定义证照数据,并重写自定义字段明细。
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateStoreCustomCertificate(CertificateAppStoreCustomUpdateReq req) {
|
||||
ServiceException.notNull(req, "请求参数不能为空");
|
||||
CertificateInstanceEntity entity = queryAndAssertType(req.getCertificateInstanceId(), CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE, "门店自定义证照");
|
||||
|
||||
Date expireDate = parseDate(req.getExpireDate(), "到期日期");
|
||||
entity.setCertificateImage("");
|
||||
entity.setIssueDate(null);
|
||||
entity.setExpireDate(expireDate);
|
||||
entity.setIsLongTerm(0);
|
||||
entity.setStatus(calculateStatus(entity.getIsLongTerm(), entity.getExpireDate()));
|
||||
certificateInstanceMapper.updateById(entity);
|
||||
|
||||
deleteAndSaveStoreCustomItems(entity.getId(), req.getItemList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建健康证明细返回对象。
|
||||
*/
|
||||
private HealthCertificateDetailVO buildHealthDetail(CertificateInstanceEntity entity) {
|
||||
HealthCertificateDetailVO detailVO = new HealthCertificateDetailVO();
|
||||
detailVO.setCertificateImage(entity.getCertificateImage());
|
||||
detailVO.setIssueDate(entity.getIssueDate());
|
||||
detailVO.setExpireDate(entity.getExpireDate());
|
||||
detailVO.setStatus(entity.getStatus());
|
||||
return detailVO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建营业执照明细返回对象。
|
||||
*/
|
||||
private CertificateAppBusinessLicenseDetailVO buildBusinessLicenseDetail(CertificateInstanceEntity entity) {
|
||||
CertificateBusinessLicenseExtEntity extEntity = queryBusinessLicenseExt(entity.getId());
|
||||
CertificateAppBusinessLicenseDetailVO detailVO = new CertificateAppBusinessLicenseDetailVO();
|
||||
detailVO.setCertificateImage(entity.getCertificateImage());
|
||||
detailVO.setCertificateNo(entity.getCertificateNo());
|
||||
detailVO.setIssueDate(entity.getIssueDate());
|
||||
detailVO.setExpireDate(entity.getExpireDate());
|
||||
detailVO.setIsLongTerm(entity.getIsLongTerm());
|
||||
detailVO.setStatus(entity.getStatus());
|
||||
if (extEntity != null) {
|
||||
detailVO.setCompanyName(extEntity.getCompanyName());
|
||||
detailVO.setLegalRepresentative(extEntity.getLegalRepresentative());
|
||||
detailVO.setEstablishDate(extEntity.getEstablishDate());
|
||||
detailVO.setBusinessScope(extEntity.getBusinessScope());
|
||||
detailVO.setAddress(extEntity.getCompanyAddress());
|
||||
}
|
||||
return detailVO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建食品经营许可证明细返回对象。
|
||||
*/
|
||||
private CertificateAppHygieneLicenseDetailVO buildHygieneLicenseDetail(CertificateInstanceEntity entity) {
|
||||
CertificateHygieneLicenseExtEntity extEntity = queryHygieneLicenseExt(entity.getId());
|
||||
CertificateAppHygieneLicenseDetailVO detailVO = new CertificateAppHygieneLicenseDetailVO();
|
||||
detailVO.setCertificateImage(entity.getCertificateImage());
|
||||
detailVO.setIssueDate(entity.getIssueDate());
|
||||
detailVO.setExpireDate(entity.getExpireDate());
|
||||
detailVO.setStatus(entity.getStatus());
|
||||
if (extEntity != null) {
|
||||
detailVO.setBusinessProject(extEntity.getBusinessProject());
|
||||
}
|
||||
return detailVO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建门店自定义证照明细返回对象。
|
||||
*/
|
||||
private CertificateAppStoreCustomDetailVO buildStoreCustomDetail(CertificateInstanceEntity entity) {
|
||||
CertificateAppStoreCustomDetailVO detailVO = new CertificateAppStoreCustomDetailVO();
|
||||
detailVO.setCertificateImage(entity.getCertificateImage());
|
||||
detailVO.setExpireDate(entity.getExpireDate());
|
||||
|
||||
LambdaQueryWrapper<CertificateInstanceItemEntity> itemWrapper = Wrappers.lambdaQuery();
|
||||
itemWrapper.eq(CertificateInstanceItemEntity::getInstanceId, entity.getId());
|
||||
itemWrapper.eq(CertificateInstanceItemEntity::getEnabledMark, 0);
|
||||
itemWrapper.orderByAsc(CertificateInstanceItemEntity::getSorts);
|
||||
List<CertificateInstanceItemEntity> itemList = certificateInstanceItemMapper.selectList(itemWrapper);
|
||||
if (CollUtil.isNotEmpty(itemList)) {
|
||||
detailVO.setItemList(itemList.stream().map(CertificateInstanceItemVO::convert).collect(Collectors.toList()));
|
||||
}
|
||||
return detailVO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存营业执照扩展信息,不存在则新增,存在则更新。
|
||||
*/
|
||||
private void saveBusinessLicenseExt(String instanceId, CertificateAppBusinessLicenseUpdateReq req, Date establishDate) {
|
||||
CertificateBusinessLicenseExtEntity extEntity = queryBusinessLicenseExt(instanceId);
|
||||
if (extEntity == null) {
|
||||
extEntity = new CertificateBusinessLicenseExtEntity();
|
||||
extEntity.setId(FtbUtil.getId());
|
||||
extEntity.setInstanceId(instanceId);
|
||||
extEntity.setEnabledMark(0);
|
||||
fillBusinessLicenseExt(extEntity, req, establishDate);
|
||||
certificateBusinessLicenseExtMapper.insert(extEntity);
|
||||
return;
|
||||
}
|
||||
fillBusinessLicenseExt(extEntity, req, establishDate);
|
||||
certificateBusinessLicenseExtMapper.update(null,new LambdaUpdateWrapper<CertificateBusinessLicenseExtEntity>()
|
||||
.eq(CertificateBusinessLicenseExtEntity::getId, extEntity.getId())
|
||||
.set(CertificateBusinessLicenseExtEntity::getCompanyName,extEntity.getCompanyName())
|
||||
.set(CertificateBusinessLicenseExtEntity::getCompanyAddress,extEntity.getCompanyAddress())
|
||||
.set(CertificateBusinessLicenseExtEntity::getBusinessScope,extEntity.getBusinessScope())
|
||||
.set(CertificateBusinessLicenseExtEntity::getEstablishDate,extEntity.getEstablishDate())
|
||||
.set(CertificateBusinessLicenseExtEntity::getLegalRepresentative,extEntity.getLegalRepresentative())
|
||||
.set(CertificateBusinessLicenseExtEntity::getLastModifyUserId, UserProvider.getLoginUserId())
|
||||
.set(CertificateBusinessLicenseExtEntity::getLastModifyTime,new Date()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充营业执照扩展字段。
|
||||
*/
|
||||
private void fillBusinessLicenseExt(CertificateBusinessLicenseExtEntity extEntity, CertificateAppBusinessLicenseUpdateReq req, Date establishDate) {
|
||||
extEntity.setCompanyName(trimToNull(req.getCompanyName()));
|
||||
extEntity.setLegalRepresentative(trimToNull(req.getLegalRepresentative()));
|
||||
extEntity.setEstablishDate(establishDate);
|
||||
extEntity.setBusinessScope(trimToNull(req.getBusinessScope()));
|
||||
extEntity.setCompanyAddress(req.getAddress());
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存食品经营许可扩展信息,不存在则新增,存在则更新。
|
||||
*/
|
||||
private void saveHygieneLicenseExt(String instanceId, CertificateAppHygieneLicenseUpdateReq req) {
|
||||
CertificateHygieneLicenseExtEntity extEntity = queryHygieneLicenseExt(instanceId);
|
||||
if (extEntity == null) {
|
||||
extEntity = new CertificateHygieneLicenseExtEntity();
|
||||
extEntity.setId(FtbUtil.getId());
|
||||
extEntity.setInstanceId(instanceId);
|
||||
extEntity.setEnabledMark(0);
|
||||
extEntity.setBusinessProject(trimToNull(req.getBusinessProject()));
|
||||
certificateHygieneLicenseExtMapper.insert(extEntity);
|
||||
return;
|
||||
}
|
||||
extEntity.setBusinessProject(trimToNull(req.getBusinessProject()));
|
||||
certificateHygieneLicenseExtMapper.update(null,new LambdaUpdateWrapper<CertificateHygieneLicenseExtEntity>()
|
||||
.eq(CertificateHygieneLicenseExtEntity::getId, extEntity.getId())
|
||||
.set(CertificateHygieneLicenseExtEntity::getBusinessProject,extEntity.getBusinessProject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除旧的门店自定义明细并保存新的明细列表。
|
||||
*/
|
||||
private void deleteAndSaveStoreCustomItems(String instanceId, List<CertificateInstanceItemReq> itemList) {
|
||||
LambdaQueryWrapper<CertificateInstanceItemEntity> deleteWrapper = Wrappers.lambdaQuery();
|
||||
deleteWrapper.eq(CertificateInstanceItemEntity::getInstanceId, instanceId);
|
||||
certificateInstanceItemMapper.delete(deleteWrapper);
|
||||
|
||||
List<CertificateInstanceItemReq> safeList = itemList == null ? Collections.emptyList() : itemList;
|
||||
long defaultSort = 1L;
|
||||
for (CertificateInstanceItemReq itemReq : safeList) {
|
||||
if (itemReq == null || StrUtil.isBlank(itemReq.getItemName())) {
|
||||
continue;
|
||||
}
|
||||
CertificateInstanceItemEntity itemEntity = new CertificateInstanceItemEntity();
|
||||
itemEntity.setId(FtbUtil.getId());
|
||||
itemEntity.setInstanceId(instanceId);
|
||||
itemEntity.setTemplateItemId(trimToNull(itemReq.getTemplateItemId()));
|
||||
itemEntity.setItemName(trimToNull(itemReq.getItemName()));
|
||||
itemEntity.setItemType(itemReq.getItemType());
|
||||
itemEntity.setItemValue(trimToNull(itemReq.getItemValue()));
|
||||
itemEntity.setSorts(itemReq.getSorts() == null ? defaultSort : itemReq.getSorts());
|
||||
itemEntity.setEnabledMark(0);
|
||||
certificateInstanceItemMapper.insert(itemEntity);
|
||||
defaultSort++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据实例ID查询营业执照扩展信息。
|
||||
*/
|
||||
private CertificateBusinessLicenseExtEntity queryBusinessLicenseExt(String instanceId) {
|
||||
LambdaQueryWrapper<CertificateBusinessLicenseExtEntity> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(CertificateBusinessLicenseExtEntity::getInstanceId, instanceId);
|
||||
wrapper.eq(CertificateBusinessLicenseExtEntity::getEnabledMark, 0);
|
||||
wrapper.orderByDesc(CertificateBusinessLicenseExtEntity::getLastModifyTime);
|
||||
wrapper.orderByDesc(CertificateBusinessLicenseExtEntity::getCreatorTime);
|
||||
List<CertificateBusinessLicenseExtEntity> extList = certificateBusinessLicenseExtMapper.selectList(wrapper);
|
||||
return CollUtil.isEmpty(extList) ? null : extList.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据实例ID查询食品许可扩展信息。
|
||||
*/
|
||||
private CertificateHygieneLicenseExtEntity queryHygieneLicenseExt(String instanceId) {
|
||||
LambdaQueryWrapper<CertificateHygieneLicenseExtEntity> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(CertificateHygieneLicenseExtEntity::getInstanceId, instanceId);
|
||||
wrapper.eq(CertificateHygieneLicenseExtEntity::getEnabledMark, 0);
|
||||
wrapper.orderByDesc(CertificateHygieneLicenseExtEntity::getLastModifyTime);
|
||||
wrapper.orderByDesc(CertificateHygieneLicenseExtEntity::getCreatorTime);
|
||||
List<CertificateHygieneLicenseExtEntity> extList = certificateHygieneLicenseExtMapper.selectList(wrapper);
|
||||
return CollUtil.isEmpty(extList) ? null : extList.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询有效证照实例。
|
||||
*/
|
||||
private CertificateInstanceEntity queryActiveEntity(String id) {
|
||||
CertificateInstanceEntity entity = certificateInstanceMapper.selectById(id);
|
||||
ServiceException.notNull(entity, "证照数据不存在");
|
||||
ServiceException.isTrue(Integer.valueOf(0).equals(entity.getEnabledMark()), "证照数据不存在");
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询实例并校验证照类型。
|
||||
*/
|
||||
private CertificateInstanceEntity queryAndAssertType(String certificateInstanceId, CertificateTypeEnum expectType, String typeName) {
|
||||
ServiceException.isTrue(StrUtil.isNotBlank(certificateInstanceId), "证照实例ID不能为空");
|
||||
CertificateInstanceEntity entity = queryActiveEntity(StrUtil.trim(certificateInstanceId));
|
||||
ServiceException.isTrue(StrUtil.equalsIgnoreCase(StrUtil.trim(entity.getCertificateType()), expectType.getType()),
|
||||
"当前证照不是" + typeName);
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析日期字符串。
|
||||
*/
|
||||
private Date parseDate(String dateText, String fieldName) {
|
||||
if (StrUtil.isBlank(dateText)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return DateUtil.parseDate(StrUtil.trim(dateText));
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException(fieldName + "格式不正确,正确格式为yyyy-MM-dd");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析日期时间戳
|
||||
*/
|
||||
private Date parseDate(Long dateTimestamp, String fieldName) {
|
||||
if (Objects.isNull(dateTimestamp) || dateTimestamp <= 0L) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return new Date(dateTimestamp);
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException(fieldName + "格式不正确,正确格式为yyyy-MM-dd");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算证照状态。
|
||||
*/
|
||||
private Integer calculateStatus(Integer isLongTerm, Date expireDate) {
|
||||
if (Integer.valueOf(1).equals(isLongTerm)) {
|
||||
return STATUS_NORMAL;
|
||||
}
|
||||
if (expireDate == null) {
|
||||
return STATUS_MISSING;
|
||||
}
|
||||
Date today = DateUtil.beginOfDay(DateUtil.date());
|
||||
Date target = DateUtil.beginOfDay(expireDate);
|
||||
if (target.before(today)) {
|
||||
return STATUS_EXPIRED;
|
||||
}
|
||||
long daysDiff = DateUtil.betweenDay(today, target, false);
|
||||
if (daysDiff <= NEAR_EXPIRE_DAYS) {
|
||||
return STATUS_NEAR_EXPIRE;
|
||||
}
|
||||
return STATUS_NORMAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态转中文名称。
|
||||
*/
|
||||
private String resolveStatusName(Integer status) {
|
||||
if (Integer.valueOf(STATUS_MISSING).equals(status)) {
|
||||
return "缺失";
|
||||
}
|
||||
if (Integer.valueOf(STATUS_EXPIRED).equals(status)) {
|
||||
return "过期";
|
||||
}
|
||||
if (Integer.valueOf(STATUS_NEAR_EXPIRE).equals(status)) {
|
||||
return "临期";
|
||||
}
|
||||
if (Integer.valueOf(STATUS_NORMAL).equals(status)) {
|
||||
return "正常";
|
||||
}
|
||||
return "-";
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串去空格,空串按null处理。
|
||||
*/
|
||||
private String trimToNull(String value) {
|
||||
String trimValue = StrUtil.trim(value);
|
||||
return StrUtil.isBlank(trimValue) ? null : trimValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,906 @@
|
||||
package jnpf.certificate.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import io.seata.spring.annotation.GlobalTransactional;
|
||||
import jnpf.base.ActionResult;
|
||||
import jnpf.certificate.mapper.CertificateBusinessLicenseExtMapper;
|
||||
import jnpf.certificate.mapper.CertificateHygieneLicenseExtMapper;
|
||||
import jnpf.certificate.mapper.CertificateInstanceItemMapper;
|
||||
import jnpf.certificate.mapper.CertificateInstanceMapper;
|
||||
import jnpf.certificate.service.CertificateStoreService;
|
||||
import jnpf.exception.HandleException;
|
||||
import jnpf.model.certificate.po.CertificateBusinessLicenseExtEntity;
|
||||
import jnpf.model.certificate.po.CertificateHygieneLicenseExtEntity;
|
||||
import jnpf.model.certificate.po.CertificateInstanceEntity;
|
||||
import jnpf.model.certificate.po.CertificateInstanceItemEntity;
|
||||
import jnpf.model.certificate.req.CertificateInstanceItemReq;
|
||||
import jnpf.model.certificate.req.CertificateStoreSaveReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppBusinessLicenseUpdateReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppHygieneLicenseUpdateReq;
|
||||
import jnpf.model.certificate.req.app.CertificateAppStoreCustomUpdateReq;
|
||||
import jnpf.model.certificate.vo.CertificateStoreAndCertificatesVO;
|
||||
import jnpf.model.certificate.vo.CertificateStoreTabVO;
|
||||
import jnpf.model.storecertificatephoto.po.StoreCertificatePhotoEntity;
|
||||
import jnpf.model.storecertificatephoto.po.StoreCertificatePhotoItemEntity;
|
||||
import jnpf.model.storecertificatephoto.vo.StoreCertificatePhotoItemVO;
|
||||
import jnpf.model.storecertificatephoto.vo.StoreCertificatePhotoVO;
|
||||
import jnpf.model.warningnotice.enums.CertificateTypeEnum;
|
||||
import jnpf.permission.OrganizeApi;
|
||||
import jnpf.permission.dto.v2.organzie.SaveStoreDTO;
|
||||
import jnpf.storecertificatephoto.helper.StoreCertificatePhotoHelper;
|
||||
import jnpf.storecertificatephoto.mapper.StoreCertificatePhotoItemMapper;
|
||||
import jnpf.storecertificatephoto.mapper.StoreCertificatePhotoMapper;
|
||||
import jnpf.util.FtbUtil;
|
||||
import jnpf.util.ServiceException;
|
||||
import jnpf.util.UserProvider;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.sql.SQLIntegrityConstraintViolationException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 门店证照服务实现。
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class CertificateStoreServiceImpl implements CertificateStoreService {
|
||||
|
||||
private static final int STATUS_MISSING = 1;
|
||||
private static final int STATUS_EXPIRED = 2;
|
||||
private static final int STATUS_NEAR_EXPIRE = 3;
|
||||
private static final int STATUS_NORMAL = 4;
|
||||
private static final int SUBJECT_TYPE_STORE = 3;
|
||||
private static final int NEAR_EXPIRE_DAYS = 30;
|
||||
|
||||
@Autowired
|
||||
private OrganizeApi organizeApi;
|
||||
@Autowired
|
||||
private CertificateInstanceMapper certificateInstanceMapper;
|
||||
@Autowired
|
||||
private CertificateBusinessLicenseExtMapper certificateBusinessLicenseExtMapper;
|
||||
@Autowired
|
||||
private CertificateHygieneLicenseExtMapper certificateHygieneLicenseExtMapper;
|
||||
@Autowired
|
||||
private CertificateInstanceItemMapper certificateInstanceItemMapper;
|
||||
@Autowired
|
||||
private StoreCertificatePhotoMapper storeCertificatePhotoMapper;
|
||||
@Autowired
|
||||
private StoreCertificatePhotoItemMapper storeCertificatePhotoItemMapper;
|
||||
@Autowired
|
||||
private StoreCertificatePhotoHelper storeCertificatePhotoHelper;
|
||||
|
||||
/**
|
||||
* 保存门店及门店证照。
|
||||
* 先调用组织服务保存门店,再保存证照实例及扩展表。
|
||||
*/
|
||||
@Override
|
||||
public String saveStoreAndCertificates(CertificateStoreSaveReq req) {
|
||||
ServiceException.notNull(req, "请求参数不能为空");
|
||||
SaveStoreDTO store = req.getStore();
|
||||
ServiceException.notNull(store, "门店信息不能为空");
|
||||
validCustomerCertificates(req.getStoreCustomCertificates());
|
||||
|
||||
String storeId = saveStore(store);
|
||||
saveBusinessLicense(storeId,store.getDisabled(), req.getBusinessLicense());
|
||||
saveHygieneLicense(storeId,store.getDisabled(), req.getHygieneLicense());
|
||||
saveStoreCustomCertificates(storeId, store.getDisabled(),req.getStoreCustomCertificates());
|
||||
return storeId;
|
||||
}
|
||||
|
||||
private void validCustomerCertificates(Collection<CertificateAppStoreCustomUpdateReq> storeCustomCertificates) {
|
||||
if(CollectionUtil.isEmpty(storeCustomCertificates)){
|
||||
return;
|
||||
}
|
||||
for (CertificateAppStoreCustomUpdateReq req : storeCustomCertificates){
|
||||
ServiceException.notNull(req.getTemplateId(), "门店自定义证照模板id不能为空");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteStore(String id) throws HandleException {
|
||||
ActionResult<Boolean> actionResult = organizeApi.deleteStore(id);
|
||||
if(Objects.isNull(actionResult) || actionResult.getCode() != 200){
|
||||
log.error("deleteStore fail.id:{},actionResult:{}",id,actionResult);
|
||||
throw new HandleException("组织服务异常");
|
||||
}
|
||||
if(!actionResult.getData()){
|
||||
log.error("deleteStore organize fail.id:{}",id);
|
||||
throw new HandleException("删除失败!");
|
||||
}
|
||||
certificateInstanceMapper.update(new CertificateInstanceEntity(),new LambdaUpdateWrapper<CertificateInstanceEntity>()
|
||||
.eq(CertificateInstanceEntity::getSubjectId, id)
|
||||
.set(CertificateInstanceEntity::getEnabledMark,1));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据门店ID查询门店及门店证照详情。
|
||||
*/
|
||||
@Override
|
||||
public CertificateStoreAndCertificatesVO getStoreAndCertificates(String storeId) {
|
||||
ServiceException.isTrue(StrUtil.isNotBlank(storeId), "门店ID不能为空");
|
||||
String targetStoreId = StrUtil.trim(storeId);
|
||||
|
||||
CertificateStoreAndCertificatesVO result = new CertificateStoreAndCertificatesVO();
|
||||
result.setBusinessLicense(queryBusinessLicense(targetStoreId));
|
||||
result.setHygieneLicense(queryHygieneLicense(targetStoreId));
|
||||
result.setStoreCustomCertificates(queryStoreCustomCertificates(targetStoreId));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CertificateStoreTabVO> queryStoreCertificateTabList() {
|
||||
List<CertificateStoreTabVO> result = new ArrayList<>();
|
||||
List<CertificateTypeEnum> defaultTypeList = Arrays.asList(
|
||||
CertificateTypeEnum.BUSINESS_LICENSE,
|
||||
CertificateTypeEnum.HYGIENE_LICENSE
|
||||
);
|
||||
for (CertificateTypeEnum certificateType : defaultTypeList) {
|
||||
CertificateStoreTabVO tabVO = new CertificateStoreTabVO();
|
||||
tabVO.setLabel(certificateType.getLabel());
|
||||
tabVO.setKey(certificateType.getType());
|
||||
tabVO.setType(certificateType.getType());
|
||||
result.add(tabVO);
|
||||
}
|
||||
|
||||
List<StoreCertificatePhotoVO> templateList = queryStoreCustomTemplateList(1);
|
||||
if (CollUtil.isEmpty(templateList)) {
|
||||
return result;
|
||||
}
|
||||
for (StoreCertificatePhotoVO template : templateList) {
|
||||
if (template == null || StrUtil.isBlank(template.getId()) || StrUtil.isBlank(template.getCertificateName())) {
|
||||
continue;
|
||||
}
|
||||
CertificateStoreTabVO tabVO = new CertificateStoreTabVO();
|
||||
tabVO.setLabel(StrUtil.trim(template.getCertificateName()));
|
||||
tabVO.setKey(StrUtil.trim(template.getId()));
|
||||
tabVO.setType(CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType());
|
||||
tabVO.setStoreCustomConfig(template);
|
||||
result.add(tabVO);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用组织服务保存门店并返回门店ID。
|
||||
*/
|
||||
private String saveStore(SaveStoreDTO storeDTO) {
|
||||
ActionResult<String> actionResult;
|
||||
try {
|
||||
actionResult = organizeApi.saveStore(storeDTO);
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("保存门店失败: " + e.getMessage());
|
||||
}
|
||||
ServiceException.notNull(actionResult, "保存门店失败");
|
||||
ServiceException.isTrue(ActionResult.success().getCode().equals(actionResult.getCode()),
|
||||
StrUtil.blankToDefault(actionResult.getMsg(), "保存门店失败"));
|
||||
String storeId = StrUtil.trim(actionResult.getData());
|
||||
if (StrUtil.isBlank(storeId)) {
|
||||
storeId = StrUtil.trim(storeDTO.getId());
|
||||
}
|
||||
ServiceException.isTrue(StrUtil.isNotBlank(storeId), "保存门店成功但未返回门店ID");
|
||||
return storeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量查询启用中的门店自定义证照模板及明细。
|
||||
*/
|
||||
private List<StoreCertificatePhotoVO> queryStoreCustomTemplateList(Integer status) {
|
||||
LambdaQueryWrapper<StoreCertificatePhotoEntity> templateWrapper = Wrappers.lambdaQuery();
|
||||
templateWrapper.eq(StoreCertificatePhotoEntity::getEnabledMark, 0);
|
||||
if(Objects.nonNull(status)){
|
||||
templateWrapper.eq(StoreCertificatePhotoEntity::getStatus, status);
|
||||
}
|
||||
templateWrapper.orderByDesc(StoreCertificatePhotoEntity::getCreatorTime);
|
||||
List<StoreCertificatePhotoEntity> templateEntities = storeCertificatePhotoMapper.selectList(templateWrapper);
|
||||
if (CollUtil.isEmpty(templateEntities)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<String> templateIds = templateEntities.stream()
|
||||
.map(StoreCertificatePhotoEntity::getId)
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.map(StrUtil::trim)
|
||||
.collect(Collectors.toList());
|
||||
Map<String, List<StoreCertificatePhotoItemEntity>> itemMap = queryStoreCustomTemplateItemMap(templateIds);
|
||||
return templateEntities.stream()
|
||||
.map(entity -> buildStoreCustomTemplateVO(entity, itemMap))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 按模板ID列表查询模板明细并按模板分组。
|
||||
*/
|
||||
private Map<String, List<StoreCertificatePhotoItemEntity>> queryStoreCustomTemplateItemMap(List<String> templateIds) {
|
||||
if (CollUtil.isEmpty(templateIds)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
LambdaQueryWrapper<StoreCertificatePhotoItemEntity> itemWrapper = Wrappers.lambdaQuery();
|
||||
itemWrapper.in(StoreCertificatePhotoItemEntity::getPhotoId, templateIds);
|
||||
itemWrapper.eq(StoreCertificatePhotoItemEntity::getEnabledMark, 0);
|
||||
itemWrapper.orderByAsc(StoreCertificatePhotoItemEntity::getPhotoId)
|
||||
.orderByAsc(StoreCertificatePhotoItemEntity::getItemType)
|
||||
.orderByAsc(StoreCertificatePhotoItemEntity::getSorts);
|
||||
List<StoreCertificatePhotoItemEntity> itemEntities = storeCertificatePhotoItemMapper.selectList(itemWrapper);
|
||||
if (CollUtil.isEmpty(itemEntities)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return itemEntities.stream().collect(Collectors.groupingBy(StoreCertificatePhotoItemEntity::getPhotoId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建门店自定义证照模板返回对象。
|
||||
*/
|
||||
private StoreCertificatePhotoVO buildStoreCustomTemplateVO(StoreCertificatePhotoEntity entity,
|
||||
Map<String, List<StoreCertificatePhotoItemEntity>> itemMap) {
|
||||
if (entity == null || StrUtil.isBlank(entity.getId())) {
|
||||
return null;
|
||||
}
|
||||
StoreCertificatePhotoVO vo = StoreCertificatePhotoVO.convert(entity);
|
||||
List<StoreCertificatePhotoItemEntity> itemEntities = itemMap.getOrDefault(entity.getId(), Collections.emptyList());
|
||||
List<StoreCertificatePhotoItemVO> imageItemList = new ArrayList<>();
|
||||
List<StoreCertificatePhotoItemVO> textItemList = new ArrayList<>();
|
||||
for (StoreCertificatePhotoItemEntity itemEntity : itemEntities) {
|
||||
StoreCertificatePhotoItemVO itemVO = StoreCertificatePhotoItemVO.convert(itemEntity);
|
||||
if (itemVO == null) {
|
||||
continue;
|
||||
}
|
||||
if (Integer.valueOf(1).equals(itemEntity.getItemType())) {
|
||||
imageItemList.add(itemVO);
|
||||
continue;
|
||||
}
|
||||
if (Integer.valueOf(2).equals(itemEntity.getItemType())) {
|
||||
textItemList.add(itemVO);
|
||||
}
|
||||
}
|
||||
imageItemList.sort(Comparator.comparing(item -> item.getSorts() == null ? Long.MAX_VALUE : item.getSorts()));
|
||||
textItemList.sort(Comparator.comparing(item -> item.getSorts() == null ? Long.MAX_VALUE : item.getSorts()));
|
||||
vo.setImageItemList(imageItemList);
|
||||
vo.setTextItemList(textItemList);
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存营业执照实例和扩展数据。
|
||||
*/
|
||||
private void saveBusinessLicense(String storeId,Integer disable, CertificateAppBusinessLicenseUpdateReq req) {
|
||||
if (req == null) {
|
||||
req = new CertificateAppBusinessLicenseUpdateReq();
|
||||
}
|
||||
CertificateInstanceEntity instance = findOrCreateSingleInstance(
|
||||
storeId,
|
||||
req.getCertificateInstanceId(),
|
||||
CertificateTypeEnum.BUSINESS_LICENSE.getType()
|
||||
);
|
||||
Date issueDate = parseDate(req.getIssueDate(), "营业执照发证日期");
|
||||
Date expireDate = parseDate(req.getExpireDate(), "营业执照到期日期");
|
||||
Date establishDate = parseDate(req.getEstablishDate(), "营业执照成立时间");
|
||||
Integer isLongTerm = req.getIsLongTerm() == null ? 0 : req.getIsLongTerm();
|
||||
if (Integer.valueOf(1).equals(isLongTerm)) {
|
||||
expireDate = null;
|
||||
}
|
||||
|
||||
instance.setIssueDate(issueDate);
|
||||
instance.setExpireDate(expireDate);
|
||||
if(Objects.nonNull(expireDate)){
|
||||
instance.setExpireTimestamp(expireDate.getTime());
|
||||
}else{
|
||||
instance.setExpireTimestamp(0);
|
||||
}
|
||||
instance.setIsLongTerm(isLongTerm);
|
||||
instance.setCertificateNo(trimToNull(req.getCertificateNo()));
|
||||
instance.setCertificateImage(trimToNull(req.getCertificateImage()));
|
||||
instance.setStatus(calculateStatus(instance.getIsLongTerm(), instance.getExpireDate()));
|
||||
instance.setStoreDisable(buildStoreDisable(disable));
|
||||
saveOrUpdateInstance(instance);
|
||||
|
||||
upsertBusinessLicenseExt(instance.getId(), req, establishDate);
|
||||
}
|
||||
|
||||
private Integer buildStoreDisable(Integer disable) {
|
||||
if(Objects.isNull(disable)){
|
||||
return 0;
|
||||
}
|
||||
return disable == 1?1:0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询门店营业执照详情。
|
||||
*/
|
||||
private CertificateAppBusinessLicenseUpdateReq queryBusinessLicense(String storeId) {
|
||||
CertificateInstanceEntity instance = findLatestByStoreAndType(storeId, CertificateTypeEnum.BUSINESS_LICENSE.getType(),null);
|
||||
if (instance == null) {
|
||||
return null;
|
||||
}
|
||||
CertificateBusinessLicenseExtEntity extEntity = findBusinessExt(instance.getId());
|
||||
CertificateAppBusinessLicenseUpdateReq result = new CertificateAppBusinessLicenseUpdateReq();
|
||||
result.setCertificateInstanceId(instance.getId());
|
||||
result.setCertificateImage(instance.getCertificateImage());
|
||||
result.setCertificateNo(instance.getCertificateNo());
|
||||
result.setIssueDate(formatDate(instance.getIssueDate()));
|
||||
result.setExpireDate(formatDate(instance.getExpireDate()));
|
||||
result.setIsLongTerm(instance.getIsLongTerm() == null ? 0 : instance.getIsLongTerm());
|
||||
if (extEntity != null) {
|
||||
result.setCompanyName(extEntity.getCompanyName());
|
||||
result.setLegalRepresentative(extEntity.getLegalRepresentative());
|
||||
result.setEstablishDate(formatDate(extEntity.getEstablishDate()));
|
||||
result.setBusinessScope(extEntity.getBusinessScope());
|
||||
result.setAddress(extEntity.getCompanyAddress());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存食品经营许可证实例和扩展数据。
|
||||
*/
|
||||
private void saveHygieneLicense(String storeId, Integer storeDisable,CertificateAppHygieneLicenseUpdateReq req) {
|
||||
if (req == null) {
|
||||
req = new CertificateAppHygieneLicenseUpdateReq();
|
||||
}
|
||||
CertificateInstanceEntity instance = findOrCreateSingleInstance(
|
||||
storeId,
|
||||
req.getCertificateInstanceId(),
|
||||
CertificateTypeEnum.HYGIENE_LICENSE.getType()
|
||||
);
|
||||
Date issueDate = parseDate(req.getIssueDate(), "食品经营许可证发证日期");
|
||||
Date expireDate = parseDate(req.getExpireDate(), "食品经营许可证到期日期");
|
||||
|
||||
instance.setIssueDate(issueDate);
|
||||
instance.setExpireDate(expireDate);
|
||||
if(Objects.nonNull(expireDate)){
|
||||
instance.setExpireTimestamp(expireDate.getTime());
|
||||
}
|
||||
instance.setIsLongTerm(0);
|
||||
instance.setCertificateImage(trimToNull(req.getCertificateImage()));
|
||||
instance.setStatus(calculateStatus(instance.getIsLongTerm(), instance.getExpireDate()));
|
||||
instance.setStoreDisable(buildStoreDisable(storeDisable));
|
||||
saveOrUpdateInstance(instance);
|
||||
|
||||
upsertHygieneLicenseExt(instance.getId(), req);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询门店食品经营许可证详情。
|
||||
*/
|
||||
private CertificateAppHygieneLicenseUpdateReq queryHygieneLicense(String storeId) {
|
||||
CertificateInstanceEntity instance = findLatestByStoreAndType(storeId, CertificateTypeEnum.HYGIENE_LICENSE.getType(),null);
|
||||
if (instance == null) {
|
||||
return null;
|
||||
}
|
||||
CertificateHygieneLicenseExtEntity extEntity = findHygieneExt(instance.getId());
|
||||
CertificateAppHygieneLicenseUpdateReq result = new CertificateAppHygieneLicenseUpdateReq();
|
||||
result.setCertificateInstanceId(instance.getId());
|
||||
result.setCertificateImage(instance.getCertificateImage());
|
||||
result.setIssueDate(formatDate(instance.getIssueDate()));
|
||||
result.setExpireDate(formatDate(instance.getExpireDate()));
|
||||
if (extEntity != null) {
|
||||
result.setBusinessProject(extEntity.getBusinessProject());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存门店自定义证照实例和明细数据。
|
||||
*/
|
||||
private void saveStoreCustomCertificates(String storeId,Integer disabled, Collection<CertificateAppStoreCustomUpdateReq> reqList) {
|
||||
if (CollUtil.isEmpty(reqList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String,String> templateNameById = storeCertificatePhotoHelper.buildStoreCertificateIdAndNames(reqList.stream()
|
||||
.map(CertificateAppStoreCustomUpdateReq::getTemplateId)
|
||||
.collect(Collectors.toSet()),1);
|
||||
for (CertificateAppStoreCustomUpdateReq req : reqList) {
|
||||
if (req == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//如果是禁用状态,则跳过
|
||||
String templateId = req.getTemplateId();
|
||||
int templateStatus = 1;
|
||||
if(!templateNameById.containsKey(templateId)){
|
||||
templateStatus = 0;
|
||||
continue;
|
||||
}
|
||||
CertificateInstanceEntity instance = findOrCreateCustomInstance(storeId, req.getCertificateInstanceId(), templateId);
|
||||
|
||||
Date expireDate = parseDate(req.getExpireDate(), "门店自定义证照到期日期");
|
||||
|
||||
instance.setCertificateImage(buildCustomCertificateImage(req.getItemList()));
|
||||
instance.setIssueDate(null);
|
||||
instance.setExpireDate(expireDate);
|
||||
if(Objects.nonNull(expireDate)){
|
||||
instance.setExpireTimestamp(expireDate.getTime());
|
||||
}
|
||||
instance.setIsLongTerm(0);
|
||||
instance.setStatus(calculateStatus(instance.getIsLongTerm(), instance.getExpireDate()));
|
||||
|
||||
instance.setTemplateStatus(templateStatus);
|
||||
instance.setStoreDisable(buildStoreDisable(disabled));
|
||||
saveOrUpdateInstance(instance);
|
||||
|
||||
rewriteCustomItems(instance.getId(), req.getItemList());
|
||||
}
|
||||
}
|
||||
|
||||
private String buildCustomCertificateImage(List<CertificateInstanceItemReq> itemList) {
|
||||
if(CollectionUtil.isEmpty(itemList)){
|
||||
return "";
|
||||
}
|
||||
for (CertificateInstanceItemReq item : itemList) {
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
if (item.getItemType() == 1) {
|
||||
return item.getItemValue();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询门店自定义证照详情集合。
|
||||
*/
|
||||
private List<CertificateAppStoreCustomUpdateReq> queryStoreCustomCertificates(String storeId) {
|
||||
LambdaQueryWrapper<CertificateInstanceEntity> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(CertificateInstanceEntity::getEnabledMark, 0);
|
||||
wrapper.eq(CertificateInstanceEntity::getSubjectType, SUBJECT_TYPE_STORE);
|
||||
wrapper.eq(CertificateInstanceEntity::getSubjectId, storeId);
|
||||
wrapper.eq(CertificateInstanceEntity::getCertificateType, CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType());
|
||||
wrapper.orderByDesc(CertificateInstanceEntity::getLastModifyTime);
|
||||
wrapper.orderByDesc(CertificateInstanceEntity::getCreatorTime);
|
||||
List<CertificateInstanceEntity> instanceList = certificateInstanceMapper.selectList(wrapper);
|
||||
if (CollUtil.isEmpty(instanceList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Set<String> enabledTemplateIds = storeCertificatePhotoMapper.selectList(new LambdaQueryWrapper<StoreCertificatePhotoEntity>()
|
||||
.select(StoreCertificatePhotoEntity::getId)
|
||||
.in(StoreCertificatePhotoEntity::getId,instanceList.stream()
|
||||
.map(CertificateInstanceEntity::getTemplateId)
|
||||
.collect(Collectors.toSet()))
|
||||
.eq(StoreCertificatePhotoEntity::getEnabledMark,0)
|
||||
.eq(StoreCertificatePhotoEntity::getStatus,1))
|
||||
.stream()
|
||||
.map(StoreCertificatePhotoEntity::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Map<String, List<CertificateInstanceItemEntity>> itemMap = queryStoreCustomItemMap(instanceList.stream()
|
||||
.filter(instance->{
|
||||
String templateId = instance.getTemplateId();
|
||||
if(StringUtils.isBlank(templateId)){
|
||||
return true;
|
||||
}
|
||||
return enabledTemplateIds.contains(templateId);
|
||||
})
|
||||
.map(CertificateInstanceEntity::getId)
|
||||
.collect(Collectors.toSet()));
|
||||
return instanceList.stream()
|
||||
.filter(instance->{
|
||||
String templateId = instance.getTemplateId();
|
||||
if(StringUtils.isBlank(templateId)){
|
||||
return true;
|
||||
}
|
||||
return enabledTemplateIds.contains(templateId);
|
||||
})
|
||||
.map(instance -> convertStoreCustomCertificate(instance, itemMap))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 按证照实例ID批量查询自定义证照明细并分组。
|
||||
*/
|
||||
private Map<String, List<CertificateInstanceItemEntity>> queryStoreCustomItemMap(Collection<String> instanceIds) {
|
||||
if (CollUtil.isEmpty(instanceIds)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
LambdaQueryWrapper<CertificateInstanceItemEntity> itemWrapper = Wrappers.lambdaQuery();
|
||||
itemWrapper.in(CertificateInstanceItemEntity::getInstanceId, instanceIds);
|
||||
itemWrapper.eq(CertificateInstanceItemEntity::getEnabledMark, 0);
|
||||
itemWrapper.orderByAsc(CertificateInstanceItemEntity::getInstanceId);
|
||||
itemWrapper.orderByAsc(CertificateInstanceItemEntity::getSorts);
|
||||
List<CertificateInstanceItemEntity> itemEntities = certificateInstanceItemMapper.selectList(itemWrapper);
|
||||
if (CollUtil.isEmpty(itemEntities)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
return itemEntities.stream()
|
||||
.collect(Collectors.groupingBy(CertificateInstanceItemEntity::getInstanceId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据证照实例ID或门店+证照类型查询单条实例,不存在则创建。
|
||||
*/
|
||||
private CertificateInstanceEntity findOrCreateSingleInstance(String storeId, String certificateInstanceId, String certificateType) {
|
||||
CertificateInstanceEntity instance = findByInstanceId(certificateInstanceId, certificateType);
|
||||
if (instance == null) {
|
||||
instance = findLatestByStoreAndType(storeId, certificateType,null);
|
||||
}
|
||||
if (instance == null) {
|
||||
instance = createNewInstance(storeId, certificateType);
|
||||
}
|
||||
normalizeStoreInstance(instance, storeId, certificateType);
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据证照实例ID查询自定义证照实例,不存在则创建新实例。
|
||||
*/
|
||||
private CertificateInstanceEntity findOrCreateCustomInstance(String storeId, String certificateInstanceId, String templateId) {
|
||||
String certificateType = CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType();
|
||||
CertificateInstanceEntity instance = findByInstanceId(certificateInstanceId, certificateType);
|
||||
if (instance == null) {
|
||||
instance = createNewInstance(storeId, certificateType);
|
||||
instance.setTemplateId(templateId);
|
||||
}
|
||||
normalizeStoreInstance(instance, storeId, certificateType);
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按证照实例ID查询。
|
||||
*/
|
||||
private CertificateInstanceEntity findByInstanceId(String certificateInstanceId, String certificateType) {
|
||||
if (StrUtil.isBlank(certificateInstanceId)) {
|
||||
return null;
|
||||
}
|
||||
String instanceId = StrUtil.trim(certificateInstanceId);
|
||||
CertificateInstanceEntity instance = certificateInstanceMapper.selectById(instanceId);
|
||||
ServiceException.isTrue(instance != null && Integer.valueOf(0).equals(instance.getEnabledMark()), "证照实例不存在");
|
||||
ServiceException.isTrue(StrUtil.equalsIgnoreCase(StrUtil.trim(instance.getCertificateType()), certificateType), "证照类型不匹配");
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按门店和证照类型查询最新实例。
|
||||
*/
|
||||
private CertificateInstanceEntity findLatestByStoreAndType(String storeId, String certificateType,String templateId) {
|
||||
LambdaQueryWrapper<CertificateInstanceEntity> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(CertificateInstanceEntity::getEnabledMark, 0);
|
||||
wrapper.eq(CertificateInstanceEntity::getSubjectType, SUBJECT_TYPE_STORE);
|
||||
wrapper.eq(CertificateInstanceEntity::getSubjectId, storeId);
|
||||
wrapper.eq(CertificateInstanceEntity::getCertificateType, certificateType);
|
||||
if(StringUtils.isNotBlank(templateId)){
|
||||
wrapper.eq(CertificateInstanceEntity::getTemplateId, templateId);
|
||||
}
|
||||
wrapper.orderByDesc(CertificateInstanceEntity::getLastModifyTime);
|
||||
wrapper.orderByDesc(CertificateInstanceEntity::getCreatorTime);
|
||||
List<CertificateInstanceEntity> list = certificateInstanceMapper.selectList(wrapper);
|
||||
return CollUtil.isEmpty(list) ? null : list.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建新证照实例。
|
||||
*/
|
||||
private CertificateInstanceEntity createNewInstance(String storeId, String certificateType) {
|
||||
CertificateInstanceEntity instance = new CertificateInstanceEntity();
|
||||
instance.setSubjectType(SUBJECT_TYPE_STORE);
|
||||
instance.setSubjectId(storeId);
|
||||
instance.setCertificateType(certificateType);
|
||||
instance.setEnabledMark(0);
|
||||
instance.setTemplateId("");
|
||||
instance.setCreatorUserId(UserProvider.getLoginUserId());
|
||||
instance.setCreatorTime(new Date());
|
||||
instance.setLastModifyUserId(UserProvider.getLoginUserId());
|
||||
instance.setLastModifyTime(new Date());
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 规范化门店证照实例字段。
|
||||
*/
|
||||
private void normalizeStoreInstance(CertificateInstanceEntity instance, String storeId, String certificateType) {
|
||||
instance.setSubjectType(SUBJECT_TYPE_STORE);
|
||||
instance.setSubjectId(storeId);
|
||||
instance.setCertificateType(certificateType);
|
||||
if (instance.getEnabledMark() == null) {
|
||||
instance.setEnabledMark(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存或更新证照实例。
|
||||
*/
|
||||
private void saveOrUpdateInstance(CertificateInstanceEntity instance) {
|
||||
if(StringUtils.isNotBlank(instance.getId())){
|
||||
updateInstanceByWrapper(instance);
|
||||
return;
|
||||
}
|
||||
instance.setId(FtbUtil.getId());
|
||||
try {
|
||||
certificateInstanceMapper.insert(instance);
|
||||
} catch (DuplicateKeyException e) {
|
||||
updateInstance(instance);
|
||||
} catch (DataIntegrityViolationException e) {
|
||||
if (isDuplicateKeyException(e)) {
|
||||
updateInstance(instance);
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
if (isDuplicateKeyException(e)) {
|
||||
updateInstance(instance);
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
// catch (SQLIntegrityConstraintViolationException e){
|
||||
// String msg = e.getMessage();
|
||||
// if (msg.contains("Duplicate entry")){
|
||||
// updateInstance(instance);
|
||||
// return;
|
||||
// }
|
||||
// throw e;
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断异常链中是否包含唯一键冲突。
|
||||
* 兼容 Seata/MyBatis 对 JDBC 异常的包装场景。
|
||||
*/
|
||||
private boolean isDuplicateKeyException(Throwable throwable) {
|
||||
Throwable current = throwable;
|
||||
while (current != null) {
|
||||
if (current instanceof DuplicateKeyException || current instanceof SQLIntegrityConstraintViolationException) {
|
||||
return true;
|
||||
}
|
||||
String message = current.getMessage();
|
||||
if (StringUtils.isNotBlank(message)
|
||||
&& StringUtils.containsIgnoreCase(message, "Duplicate entry")
|
||||
&& StringUtils.containsIgnoreCase(message, "for key")) {
|
||||
return true;
|
||||
}
|
||||
current = current.getCause();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void updateInstance(CertificateInstanceEntity instance) {
|
||||
if(Objects.isNull(instance)){
|
||||
return;
|
||||
}
|
||||
CertificateInstanceEntity dbInstance = findLatestByStoreAndType(instance.getSubjectId(), instance.getCertificateType(),instance.getTemplateId());
|
||||
if(Objects.isNull(dbInstance)){
|
||||
log.error("updateInstance fail.findLatestByStoreAndType null.instance:{}",instance);
|
||||
return;
|
||||
}
|
||||
instance.setId(dbInstance.getId());
|
||||
updateInstanceByWrapper(instance);
|
||||
}
|
||||
|
||||
private void updateInstanceByWrapper(CertificateInstanceEntity instance) {
|
||||
LambdaUpdateWrapper<CertificateInstanceEntity> updateWrapper = new LambdaUpdateWrapper<CertificateInstanceEntity>().eq(CertificateInstanceEntity::getId,instance.getId())
|
||||
.set(CertificateInstanceEntity::getCertificateImage, instance.getCertificateImage())
|
||||
.set(CertificateInstanceEntity::getIssueDate, instance.getIssueDate())
|
||||
.set(CertificateInstanceEntity::getExpireDate, instance.getExpireDate())
|
||||
.set(CertificateInstanceEntity::getExpireTimestamp, instance.getExpireTimestamp())
|
||||
.set(CertificateInstanceEntity::getStatus, instance.getStatus())
|
||||
.set(CertificateInstanceEntity::getLastModifyTime, instance.getLastModifyTime())
|
||||
.set(CertificateInstanceEntity::getLastModifyUserId, instance.getLastModifyUserId())
|
||||
.set(CertificateInstanceEntity::getEnabledMark, instance.getEnabledMark())
|
||||
.set(CertificateInstanceEntity::getCreatorTime, instance.getCreatorTime())
|
||||
.set(CertificateInstanceEntity::getCreatorUserId, instance.getCreatorUserId())
|
||||
.set(CertificateInstanceEntity::getStoreDisable,instance.getStoreDisable());
|
||||
Integer templateStatus = instance.getTemplateStatus();
|
||||
if(Objects.nonNull(templateStatus)){
|
||||
updateWrapper.set(CertificateInstanceEntity::getTemplateStatus,templateStatus);
|
||||
}
|
||||
int r = certificateInstanceMapper.update(null,updateWrapper);
|
||||
if(r<=0){
|
||||
log.error("saveOrUpdateInstance insert fail.update also fail.instance:{}",instance);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存营业执照扩展表。
|
||||
*/
|
||||
private void upsertBusinessLicenseExt(String instanceId, CertificateAppBusinessLicenseUpdateReq req, Date establishDate) {
|
||||
CertificateBusinessLicenseExtEntity extEntity = new CertificateBusinessLicenseExtEntity();
|
||||
extEntity.setId(FtbUtil.getId());
|
||||
extEntity.setInstanceId(instanceId);
|
||||
extEntity.setEnabledMark(0);
|
||||
fillBusinessExt(extEntity, req, establishDate);
|
||||
try {
|
||||
certificateBusinessLicenseExtMapper.insert(extEntity);
|
||||
}catch (DuplicateKeyException e){
|
||||
int r = certificateBusinessLicenseExtMapper.update(null,new LambdaUpdateWrapper<CertificateBusinessLicenseExtEntity>()
|
||||
.eq(CertificateBusinessLicenseExtEntity::getInstanceId,instanceId)
|
||||
.eq(CertificateBusinessLicenseExtEntity::getEnabledMark,0)
|
||||
.set(CertificateBusinessLicenseExtEntity::getCompanyName,extEntity.getCompanyName())
|
||||
.set(CertificateBusinessLicenseExtEntity::getCompanyAddress,extEntity.getCompanyAddress())
|
||||
.set(CertificateBusinessLicenseExtEntity::getLegalRepresentative,extEntity.getLegalRepresentative())
|
||||
.set(CertificateBusinessLicenseExtEntity::getEstablishDate,extEntity.getEstablishDate())
|
||||
.set(CertificateBusinessLicenseExtEntity::getBusinessScope,extEntity.getBusinessScope())
|
||||
.set(CertificateBusinessLicenseExtEntity::getCreatorTime,extEntity.getCreatorTime())
|
||||
.set(CertificateBusinessLicenseExtEntity::getCreatorUserId,extEntity.getCreatorUserId())
|
||||
.set(CertificateBusinessLicenseExtEntity::getLastModifyTime,extEntity.getLastModifyTime())
|
||||
.set(CertificateBusinessLicenseExtEntity::getLastModifyUserId,extEntity.getLastModifyUserId())
|
||||
);
|
||||
if(r <= 0){
|
||||
log.error("upsertBusinessLicenseExt insert fail.update row count is:{}.instanceId:{},extEntity:{}",r,instanceId,extEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存食品经营许可证扩展表。
|
||||
*/
|
||||
private void upsertHygieneLicenseExt(String instanceId, CertificateAppHygieneLicenseUpdateReq req) {
|
||||
CertificateHygieneLicenseExtEntity extEntity = findHygieneExt(instanceId);
|
||||
if (extEntity == null) {
|
||||
extEntity = new CertificateHygieneLicenseExtEntity();
|
||||
extEntity.setId(FtbUtil.getId());
|
||||
extEntity.setInstanceId(instanceId);
|
||||
extEntity.setEnabledMark(0);
|
||||
extEntity.setBusinessProject(trimToNull(req.getBusinessProject()));
|
||||
certificateHygieneLicenseExtMapper.insert(extEntity);
|
||||
return;
|
||||
}
|
||||
extEntity.setBusinessProject(trimToNull(req.getBusinessProject()));
|
||||
certificateHygieneLicenseExtMapper.updateById(extEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写门店自定义证照明细。
|
||||
*/
|
||||
private void rewriteCustomItems(String instanceId, List<CertificateInstanceItemReq> itemList) {
|
||||
LambdaQueryWrapper<CertificateInstanceItemEntity> deleteWrapper = Wrappers.lambdaQuery();
|
||||
deleteWrapper.eq(CertificateInstanceItemEntity::getInstanceId, instanceId);
|
||||
certificateInstanceItemMapper.delete(deleteWrapper);
|
||||
|
||||
List<CertificateInstanceItemReq> safeItemList = itemList == null ? Collections.emptyList() : itemList;
|
||||
long sort = 1L;
|
||||
for (CertificateInstanceItemReq itemReq : safeItemList) {
|
||||
if (itemReq == null || StrUtil.isBlank(itemReq.getItemName())) {
|
||||
continue;
|
||||
}
|
||||
CertificateInstanceItemEntity itemEntity = new CertificateInstanceItemEntity();
|
||||
itemEntity.setId(FtbUtil.getId());
|
||||
itemEntity.setInstanceId(instanceId);
|
||||
itemEntity.setTemplateItemId(trimToNull(itemReq.getTemplateItemId()));
|
||||
itemEntity.setItemName(trimToNull(itemReq.getItemName()));
|
||||
itemEntity.setItemType(itemReq.getItemType());
|
||||
itemEntity.setItemValue(trimToNull(itemReq.getItemValue()));
|
||||
itemEntity.setSorts(itemReq.getSorts() == null ? sort : itemReq.getSorts());
|
||||
itemEntity.setEnabledMark(0);
|
||||
certificateInstanceItemMapper.insert(itemEntity);
|
||||
sort++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询营业执照扩展数据。
|
||||
*/
|
||||
private CertificateBusinessLicenseExtEntity findBusinessExt(String instanceId) {
|
||||
LambdaQueryWrapper<CertificateBusinessLicenseExtEntity> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(CertificateBusinessLicenseExtEntity::getInstanceId, instanceId);
|
||||
wrapper.eq(CertificateBusinessLicenseExtEntity::getEnabledMark, 0);
|
||||
wrapper.orderByDesc(CertificateBusinessLicenseExtEntity::getLastModifyTime);
|
||||
wrapper.orderByDesc(CertificateBusinessLicenseExtEntity::getCreatorTime);
|
||||
List<CertificateBusinessLicenseExtEntity> extList = certificateBusinessLicenseExtMapper.selectList(wrapper);
|
||||
return CollUtil.isEmpty(extList) ? null : extList.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询食品经营许可证扩展数据。
|
||||
*/
|
||||
private CertificateHygieneLicenseExtEntity findHygieneExt(String instanceId) {
|
||||
LambdaQueryWrapper<CertificateHygieneLicenseExtEntity> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(CertificateHygieneLicenseExtEntity::getInstanceId, instanceId);
|
||||
wrapper.eq(CertificateHygieneLicenseExtEntity::getEnabledMark, 0);
|
||||
wrapper.orderByDesc(CertificateHygieneLicenseExtEntity::getLastModifyTime);
|
||||
wrapper.orderByDesc(CertificateHygieneLicenseExtEntity::getCreatorTime);
|
||||
List<CertificateHygieneLicenseExtEntity> extList = certificateHygieneLicenseExtMapper.selectList(wrapper);
|
||||
return CollUtil.isEmpty(extList) ? null : extList.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将自定义证照实例转换为查询返回对象。
|
||||
*/
|
||||
private CertificateAppStoreCustomUpdateReq convertStoreCustomCertificate(
|
||||
CertificateInstanceEntity instance,
|
||||
Map<String, List<CertificateInstanceItemEntity>> itemMap) {
|
||||
CertificateAppStoreCustomUpdateReq result = new CertificateAppStoreCustomUpdateReq();
|
||||
result.setCertificateInstanceId(instance.getId());
|
||||
result.setExpireDate(formatDate(instance.getExpireDate()));
|
||||
result.setTemplateId(instance.getTemplateId());
|
||||
|
||||
List<CertificateInstanceItemEntity> itemEntities = itemMap.getOrDefault(instance.getId(), Collections.emptyList());
|
||||
if (CollUtil.isEmpty(itemEntities)) {
|
||||
return result;
|
||||
}
|
||||
result.setItemList(itemEntities.stream().map(entity -> {
|
||||
CertificateInstanceItemReq itemReq = new CertificateInstanceItemReq();
|
||||
itemReq.setTemplateItemId(entity.getTemplateItemId());
|
||||
itemReq.setItemName(entity.getItemName());
|
||||
itemReq.setItemType(entity.getItemType());
|
||||
itemReq.setItemValue(entity.getItemValue());
|
||||
itemReq.setSorts(entity.getSorts());
|
||||
return itemReq;
|
||||
}).filter(Objects::nonNull).collect(Collectors.toList()));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充营业执照扩展字段。
|
||||
*/
|
||||
private void fillBusinessExt(CertificateBusinessLicenseExtEntity extEntity, CertificateAppBusinessLicenseUpdateReq req, Date establishDate) {
|
||||
extEntity.setCompanyName(trimToNull(req.getCompanyName()));
|
||||
extEntity.setLegalRepresentative(trimToNull(req.getLegalRepresentative()));
|
||||
extEntity.setEstablishDate(establishDate);
|
||||
extEntity.setBusinessScope(trimToNull(req.getBusinessScope()));
|
||||
extEntity.setCompanyAddress(req.getAddress());
|
||||
extEntity.setCreatorTime(new Date());
|
||||
extEntity.setCreatorUserId(UserProvider.getLoginUserId());
|
||||
extEntity.setLastModifyTime(new Date());
|
||||
extEntity.setLastModifyUserId(UserProvider.getLoginUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析日期文本,格式为 yyyy-MM-dd。
|
||||
*/
|
||||
private Date parseDate(String dateText, String fieldName) {
|
||||
if (StrUtil.isBlank(dateText)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return DateUtil.parseDate(StrUtil.trim(dateText));
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException(fieldName + "格式不正确,正确格式为yyyy-MM-dd");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期格式化为 yyyy-MM-dd 字符串。
|
||||
*/
|
||||
private String formatDate(Date date) {
|
||||
if (date == null) {
|
||||
return null;
|
||||
}
|
||||
return DateUtil.formatDate(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据结束时间计算证照状态。
|
||||
*/
|
||||
private Integer calculateStatus(Integer isLongTerm, Date expireDate) {
|
||||
if (Integer.valueOf(1).equals(isLongTerm)) {
|
||||
return STATUS_NORMAL;
|
||||
}
|
||||
if (expireDate == null) {
|
||||
return STATUS_MISSING;
|
||||
}
|
||||
Date today = DateUtil.beginOfDay(DateUtil.date());
|
||||
Date target = DateUtil.beginOfDay(expireDate);
|
||||
if (target.before(today)) {
|
||||
return STATUS_EXPIRED;
|
||||
}
|
||||
long daysDiff = DateUtil.betweenDay(today, target, false);
|
||||
if (daysDiff <= NEAR_EXPIRE_DAYS) {
|
||||
return STATUS_NEAR_EXPIRE;
|
||||
}
|
||||
return STATUS_NORMAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串去首尾空格,空串转为null。
|
||||
*/
|
||||
private String trimToNull(String value) {
|
||||
String trimValue = StrUtil.trim(value);
|
||||
return StrUtil.isBlank(trimValue) ? null : trimValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package jnpf.certificate.util;
|
||||
|
||||
public class CertificateStatusUtils {
|
||||
public static String buildStatusName(Integer status){
|
||||
switch (status){
|
||||
case 1:
|
||||
return "缺失";
|
||||
case 2:
|
||||
return "过期";
|
||||
case 3:
|
||||
return "临期";
|
||||
case 4:
|
||||
return "正常";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package jnpf.certificate.util;
|
||||
|
||||
import jnpf.permission.eum.v2.FtbStoreTagEnum;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class StoreTypeUtils {
|
||||
public static String buildStoreType(String storeType) {
|
||||
if(Objects.isNull(storeType)){
|
||||
return "";
|
||||
}
|
||||
FtbStoreTagEnum storeTypeEnum = FtbStoreTagEnum.fromName(storeType);
|
||||
if(Objects.isNull(storeTypeEnum)){
|
||||
return "";
|
||||
}
|
||||
return storeTypeEnum.getLabel();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package jnpf.certificate.util;
|
||||
|
||||
public class SubjectNameUtils {
|
||||
//主体类型,1为员工、2为公司、3为门店
|
||||
public static String getSubjectName(Integer subjectType) {
|
||||
switch (subjectType){
|
||||
case 1:
|
||||
return "员工";
|
||||
case 2:
|
||||
return "公司";
|
||||
case 3:
|
||||
return "门店";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package jnpf.certificate.util;
|
||||
|
||||
import jnpf.model.enums.StaffWorkerStatus;
|
||||
import jnpf.util.StringUtil;
|
||||
|
||||
public class WorkerStatusUtils {
|
||||
//301、预入职 302、试用 303、正式 304、待离职 305 离职
|
||||
public static String buildStatusName(String workerStatus) {
|
||||
if(StringUtil.isBlank(workerStatus)){
|
||||
return "";
|
||||
}
|
||||
return StaffWorkerStatus.getWorkerStatusNameByValue(workerStatus);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user