This commit is contained in:
@@ -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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user