commit
Some checks failed
API接口参数变更检测 / api-param-check (push) Has been cancelled

This commit is contained in:
2026-06-05 16:18:40 +08:00
parent 1ca34c6bb2
commit 3cba3bb74e
4393 changed files with 450030 additions and 103 deletions

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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为subjectIdvalue为组织负责人id
* 中间是 组织名称key为组织idvalue为组织名称
* 右边是 模板名称key为模板idvalue为模板名称
* @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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}