Procházet zdrojové kódy

优化接收平台要求请求的事件逻辑,增加响应邀约表数据的录入,调整事件参与接口,屏蔽掉事件已参与的校验,以及用户参与记录的录入

james před 3 dny
rodič
revize
4082d459b7

+ 13 - 0
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/VppDrUnInvitationIngestService.java

@@ -0,0 +1,13 @@
+package com.usky.vpp.service;
+
+import com.usky.vpp.domain.VppDrEvent;
+
+import java.util.Map;
+
+/**
+ * UN 邀约报文入库(与 {@link VppDrInvitationService} 解耦,避免循环依赖)。
+ */
+public interface VppDrUnInvitationIngestService {
+
+    void syncInvitationsFromUnEvent(VppDrEvent event, Map<String, Object> eventMap);
+}

+ 8 - 0
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/impl/VppDrEventIngestServiceImpl.java

@@ -12,6 +12,7 @@ import com.usky.vpp.mapper.VppDrEventMapper;
 import com.usky.vpp.mapper.VppDrParticipationMapper;
 import com.usky.vpp.service.VppDrEventIngestService;
 import com.usky.vpp.service.VppDrExecutionBootstrapService;
+import com.usky.vpp.service.VppDrUnInvitationIngestService;
 import com.usky.vpp.service.VppUnDrSyncService;
 import com.usky.vpp.util.VppAuditHelper;
 import com.usky.vpp.util.VppUnEventParser;
@@ -49,6 +50,8 @@ public class VppDrEventIngestServiceImpl implements VppDrEventIngestService {
     @Autowired
     private VppDrExecutionBootstrapService executionBootstrapService;
     @Autowired
+    private VppDrUnInvitationIngestService unInvitationIngestService;
+    @Autowired
     private ObjectMapper objectMapper;
 
     @Override
@@ -187,6 +190,9 @@ public class VppDrEventIngestServiceImpl implements VppDrEventIngestService {
         }
 
         applyPhase(event, eventMap, phase, platformStatus);
+        if (phase == VppUnEventPhase.INVITATION) {
+            unInvitationIngestService.syncInvitationsFromUnEvent(event, eventMap);
+        }
         VppAuditHelper.fillUpdate(event);
         eventMapper.updateById(event);
         return event;
@@ -247,7 +253,9 @@ public class VppDrEventIngestServiceImpl implements VppDrEventIngestService {
 
     private void applyBaseFields(VppDrEvent event, Map<String, Object> eventMap, Map<String, Object> descriptor) {
         String comment = VppUnPayloadHelper.getString(descriptor, "comment");
+        LocalDateTime replyDeadline = VppUnPayloadHelper.getDateTime(descriptor, "deadline");
         event.setEventName(StringUtils.hasText(comment) ? comment : event.getEventId());
+        event.setDeclareDeadline(replyDeadline);
         event.setResponseType(VppUnEventParser.mapResponseType(descriptor));
         event.setEventType(VppUnEventParser.mapEventType(descriptor));
 

+ 43 - 9
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/impl/VppDrInvitationServiceImpl.java

@@ -11,11 +11,13 @@ import com.usky.vpp.domain.VppCustomerContact;
 import com.usky.vpp.domain.VppDrEvent;
 import com.usky.vpp.domain.VppDrInvitation;
 import com.usky.vpp.domain.VppDrParticipation;
+import com.usky.vpp.domain.VppResourcePoint;
 import com.usky.vpp.mapper.VppCustomerContactMapper;
 import com.usky.vpp.mapper.VppCustomerMapper;
 import com.usky.vpp.mapper.VppDrEventMapper;
 import com.usky.vpp.mapper.VppDrInvitationMapper;
 import com.usky.vpp.mapper.VppDrParticipationMapper;
+import com.usky.vpp.mapper.VppResourcePointMapper;
 import com.usky.vpp.service.VppAliyunSmsService;
 import com.usky.vpp.service.VppDrInvitationService;
 import com.usky.vpp.service.VppUnIntegrationService;
@@ -79,6 +81,8 @@ public class VppDrInvitationServiceImpl implements VppDrInvitationService {
     @Autowired
     private VppDrParticipationMapper participationMapper;
     @Autowired
+    private VppResourcePointMapper resourcePointMapper;
+    @Autowired
     private VppUnIntegrationService unIntegrationService;
     @Autowired
     private VppAliyunSmsService aliyunSmsService;
@@ -111,7 +115,7 @@ public class VppDrInvitationServiceImpl implements VppDrInvitationService {
 
         VppDrEvent event = requireEvent(request.getDrEventId());
         VppCustomer customer = requireCustomer(request.getCustomerId());
-        ensureInvitationUnique(request.getDrEventId(), request.getCustomerId(), null);
+        ensureInvitationUnique(request, null);
 
         VppDrInvitation invitation = new VppDrInvitation();
         invitation.setInvitationNo(VppAuditHelper.nextInvitationNo());
@@ -368,13 +372,19 @@ public class VppDrInvitationServiceImpl implements VppDrInvitationService {
     }
 
     private VppDrParticipation findOrCreateParticipation(VppDrInvitation invitation, BigDecimal declaredCapacityKw) {
-        VppDrParticipation existing = participationMapper.selectOne(new LambdaQueryWrapper<VppDrParticipation>()
+        Long resourceId = resolveResourceIdBySiteId(invitation.getSiteId());
+        LambdaQueryWrapper<VppDrParticipation> wrapper = new LambdaQueryWrapper<VppDrParticipation>()
                 .eq(VppDrParticipation::getEventId, invitation.getDrEventId())
                 .eq(VppDrParticipation::getCustomerId, invitation.getCustomerId())
-                .isNull(VppDrParticipation::getResourceId)
-                .eq(VppDrParticipation::getDeleteFlag, VppAuditHelper.NOT_DELETED)
-                .last("LIMIT 1"));
+                .eq(VppDrParticipation::getDeleteFlag, VppAuditHelper.NOT_DELETED);
+        if (resourceId != null) {
+            wrapper.eq(VppDrParticipation::getResourceId, resourceId);
+        } else {
+            wrapper.isNull(VppDrParticipation::getResourceId);
+        }
+        VppDrParticipation existing = participationMapper.selectOne(wrapper.last("LIMIT 1"));
         if (existing != null) {
+            existing.setResourceId(resourceId);
             existing.setParticipateStatus(PARTICIPATE_ACCEPT);
             existing.setDeclaredCapacityKw(declaredCapacityKw);
             existing.setDeclaredAt(LocalDateTime.now());
@@ -387,6 +397,7 @@ public class VppDrInvitationServiceImpl implements VppDrInvitationService {
         VppDrParticipation participation = new VppDrParticipation();
         participation.setEventId(invitation.getDrEventId());
         participation.setCustomerId(invitation.getCustomerId());
+        participation.setResourceId(resourceId);
         participation.setParticipateStatus(PARTICIPATE_ACCEPT);
         participation.setDeclaredCapacityKw(declaredCapacityKw);
         participation.setDeclaredAt(LocalDateTime.now());
@@ -399,6 +410,20 @@ public class VppDrInvitationServiceImpl implements VppDrInvitationService {
         return participation;
     }
 
+    private Long resolveResourceIdBySiteId(Long siteId) {
+        if (siteId == null) {
+            throw new BusinessException("邀约未关联站点,无法创建参与记录");
+        }
+        VppResourcePoint resourcePoint = resourcePointMapper.selectOne(new LambdaQueryWrapper<VppResourcePoint>()
+                .eq(VppResourcePoint::getSiteId, siteId)
+                .eq(VppResourcePoint::getDeleteFlag, VppAuditHelper.NOT_DELETED)
+                .last("LIMIT 1"));
+        if (resourcePoint == null) {
+            throw new BusinessException("站点未配置资源点,无法参与");
+        }
+        return resourcePoint.getId();
+    }
+
     private void syncParticipationToUn(Long eventId, boolean participate, List<VppDrParticipation> participations) {
         try {
             unIntegrationService.submitParticipation(eventId, participate, participations);
@@ -461,16 +486,25 @@ public class VppDrInvitationServiceImpl implements VppDrInvitationService {
         return any != null ? any.getId() : null;
     }
 
-    private void ensureInvitationUnique(Long drEventId, Long customerId, Long excludeId) {
+    private void ensureInvitationUnique(DrInvitationRequest request, Long excludeId) {
+        if (request == null || request.getDrEventId() == null) {
+            return;
+        }
         LambdaQueryWrapper<VppDrInvitation> wrapper = new LambdaQueryWrapper<VppDrInvitation>()
-                .eq(VppDrInvitation::getDrEventId, drEventId)
-                .eq(VppDrInvitation::getCustomerId, customerId)
+                .eq(VppDrInvitation::getDrEventId, request.getDrEventId())
                 .eq(VppDrInvitation::getDeleteFlag, VppAuditHelper.NOT_DELETED);
+        if (request.getSiteId() != null) {
+            wrapper.eq(VppDrInvitation::getSiteId, request.getSiteId());
+        } else if (request.getCustomerId() != null) {
+            wrapper.eq(VppDrInvitation::getCustomerId, request.getCustomerId());
+        }
         if (excludeId != null) {
             wrapper.ne(VppDrInvitation::getId, excludeId);
         }
         if (invitationMapper.selectCount(wrapper) > 0) {
-            throw new BusinessException("该事件已存在该企业的邀约记录");
+            throw new BusinessException(request.getSiteId() != null
+                    ? "该事件已存在该站点的邀约记录"
+                    : "该事件已存在该企业的邀约记录");
         }
     }
 

+ 29 - 29
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/impl/VppDrServiceImpl.java

@@ -319,16 +319,16 @@ public class VppDrServiceImpl implements VppDrService {
             throw new BusinessException("参与状态不能为空");
         }
 
-        List<VppDrParticipation> existingParticipations = participationMapper.selectList(
-                new LambdaQueryWrapper<VppDrParticipation>()
-                        .eq(VppDrParticipation::getEventId, eventId)
-                        .eq(VppDrParticipation::getParticipateStatus, PARTICIPATE_STATUS_ACCEPT)
-                        .eq(VppDrParticipation::getDeleteFlag, VppAuditHelper.NOT_DELETED)
-        );
-
-        if (!existingParticipations.isEmpty()) {
-            throw new BusinessException("已参与该事件");
-        }
+//        List<VppDrParticipation> existingParticipations = participationMapper.selectList(
+//                new LambdaQueryWrapper<VppDrParticipation>()
+//                        .eq(VppDrParticipation::getEventId, eventId)
+//                        .eq(VppDrParticipation::getParticipateStatus, PARTICIPATE_STATUS_ACCEPT)
+//                        .eq(VppDrParticipation::getDeleteFlag, VppAuditHelper.NOT_DELETED)
+//        );
+//
+//        if (!existingParticipations.isEmpty()) {
+//            throw new BusinessException("已参与该事件");
+//        }
 
         if (Boolean.TRUE.equals(request.getParticipate())) {
             if (request.getResources() == null || request.getResources().isEmpty()) {
@@ -385,25 +385,25 @@ public class VppDrServiceImpl implements VppDrService {
 
             List<VppDrParticipation> savedParticipations = new ArrayList<>();
 
-            for (DrParticipateRequest.DrResourceDeclare resource : request.getResources()) {
-                VppResourcePoint rp = resourcePointMapper.selectById(resource.getResourceId());
-                VppDrParticipation participation = new VppDrParticipation();
-                participation.setEventId(eventId);
-                participation.setCustomerId(siteResourceHelper.resolveCustomerId(rp, siteMap));
-                participation.setResourceId(resource.getResourceId());
-                participation.setParticipateStatus(PARTICIPATE_STATUS_ACCEPT);
-                participation.setDeclaredCapacityKw(declaredByResourceId.get(resource.getResourceId()));
-                participation.setDeclaredAt(LocalDateTime.now());
-                VppDrParticipationHelper.refreshCompletionRate(participation);
-                VppAuditHelper.fillCreate(participation);
-                if (participation.getTenantId() == null && event.getTenantId() != null) {
-                    participation.setTenantId(event.getTenantId());
-                }
-                participationMapper.insert(participation);
-
-                savedParticipations.add(participation);
-
-            }
+//            for (DrParticipateRequest.DrResourceDeclare resource : request.getResources()) {
+//                VppResourcePoint rp = resourcePointMapper.selectById(resource.getResourceId());
+//                VppDrParticipation participation = new VppDrParticipation();
+//                participation.setEventId(eventId);
+//                participation.setCustomerId(siteResourceHelper.resolveCustomerId(rp, siteMap));
+//                participation.setResourceId(resource.getResourceId());
+//                participation.setParticipateStatus(PARTICIPATE_STATUS_ACCEPT);
+//                participation.setDeclaredCapacityKw(declaredByResourceId.get(resource.getResourceId()));
+//                participation.setDeclaredAt(LocalDateTime.now());
+//                VppDrParticipationHelper.refreshCompletionRate(participation);
+//                VppAuditHelper.fillCreate(participation);
+//                if (participation.getTenantId() == null && event.getTenantId() != null) {
+//                    participation.setTenantId(event.getTenantId());
+//                }
+//                participationMapper.insert(participation);
+//
+//                savedParticipations.add(participation);
+//
+//            }
 
             event.setEventStatus(EVENT_STATUS_DECLARED);
             VppAuditHelper.fillUpdate(event);

+ 187 - 0
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/impl/VppDrUnInvitationIngestServiceImpl.java

@@ -0,0 +1,187 @@
+package com.usky.vpp.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.usky.vpp.constant.VppDrEventStatus;
+import com.usky.vpp.domain.VppCustomerContact;
+import com.usky.vpp.domain.VppDrEvent;
+import com.usky.vpp.domain.VppDrInvitation;
+import com.usky.vpp.domain.VppSite;
+import com.usky.vpp.mapper.VppCustomerContactMapper;
+import com.usky.vpp.mapper.VppDrInvitationMapper;
+import com.usky.vpp.mapper.VppSiteMapper;
+import com.usky.vpp.service.VppDrUnInvitationIngestService;
+import com.usky.vpp.util.VppAuditHelper;
+import com.usky.vpp.util.VppDrParticipationHelper;
+import com.usky.vpp.util.VppUnEventParser;
+import com.usky.vpp.util.VppUnPayloadHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class VppDrUnInvitationIngestServiceImpl implements VppDrUnInvitationIngestService {
+
+    private static final Logger log = LoggerFactory.getLogger(VppDrUnInvitationIngestServiceImpl.class);
+
+    private static final int REPLY_PENDING = 0;
+    private static final int RESPONSE_PENDING = VppDrEventStatus.PENDING;
+    private static final int SMS_NOT_SENT = 0;
+
+    @Autowired
+    private VppDrInvitationMapper invitationMapper;
+    @Autowired
+    private VppSiteMapper siteMapper;
+    @Autowired
+    private VppCustomerContactMapper contactMapper;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void syncInvitationsFromUnEvent(VppDrEvent event, Map<String, Object> eventMap) {
+        if (event == null || event.getId() == null || eventMap == null) {
+            return;
+        }
+        List<String> unResourceIds = VppUnEventParser.getTargetResourceIds(eventMap);
+        if (unResourceIds.isEmpty()) {
+            return;
+        }
+
+        Map<String, Object> descriptor = VppUnEventParser.getDescriptor(eventMap);
+        LocalDateTime[] period = VppUnEventParser.resolveActivePeriod(eventMap);
+        LocalDate executeStart = resolveExecuteDate(period[0], event.getStartTime());
+        LocalDate executeEnd = resolveExecuteDate(period[1], event.getEndTime());
+        if (executeStart == null) {
+            log.warn("UN 邀约缺少执行开始时间 eventId={}", event.getEventId());
+            return;
+        }
+        if (executeEnd == null) {
+            executeEnd = executeStart;
+        }
+        LocalDateTime replyDeadline = VppUnPayloadHelper.getDateTime(descriptor, "deadline");
+        if (replyDeadline == null && event.getStartTime() != null) {
+            replyDeadline = event.getStartTime();
+        }
+        if (replyDeadline == null) {
+            log.warn("UN 邀约缺少回复截止时间 eventId={}", event.getEventId());
+            return;
+        }
+
+        BigDecimal demandCapacityKw = resolveUnDemandCapacityKw(event, eventMap);
+        for (String unResourceId : unResourceIds) {
+            syncSingleUnInvitation(event, unResourceId, executeStart, executeEnd, replyDeadline, demandCapacityKw);
+        }
+    }
+
+    private void syncSingleUnInvitation(VppDrEvent event,
+                                        String resourceId,
+                                        LocalDate executeStart,
+                                        LocalDate executeEnd,
+                                        LocalDateTime replyDeadline,
+                                        BigDecimal demandCapacityKw) {
+        VppSite site = siteMapper.selectOne(new LambdaQueryWrapper<VppSite>()
+                .eq(VppSite::getAccountNo, resourceId.trim())
+                .eq(VppSite::getDeleteFlag, VppAuditHelper.NOT_DELETED)
+                .last("LIMIT 1"));
+        if (site == null) {
+            log.warn("UN resourceID 未匹配站点 unResourceId={} eventId={}", resourceId, event.getEventId());
+            return;
+        }
+        if (site.getCustomerId() == null) {
+            log.warn("站点缺少 customerId siteId={} unResourceId={}", site.getId(), resourceId);
+            return;
+        }
+        if (findExistingInvitation(event.getId(), site.getId(), site.getCustomerId()) != null) {
+            log.debug("邀约已存在,跳过 eventPk={} siteId={}", event.getId(), site.getId());
+            return;
+        }
+
+        VppDrInvitation invitation = new VppDrInvitation();
+        invitation.setInvitationNo(VppAuditHelper.nextInvitationNo());
+        invitation.setDrEventId(event.getId());
+        invitation.setCustomerId(site.getCustomerId());
+        invitation.setSiteId(site.getId());
+        invitation.setExecuteStartDate(executeStart);
+        invitation.setExecuteEndDate(executeEnd);
+        invitation.setReplyDeadline(shiftOneHourEarlier(replyDeadline));
+        invitation.setIssueTime(shiftOneHourEarlier(LocalDateTime.now()));
+        invitation.setTransactionType(event.getEventType());
+        invitation.setResponseType(event.getResponseType());
+        invitation.setDemandCapacityKw(demandCapacityKw);
+        invitation.setDeclaredCapacityKw(BigDecimal.ZERO);
+        invitation.setReplyStatus(REPLY_PENDING);
+        invitation.setResponseStatus(RESPONSE_PENDING);
+        invitation.setSmsNotifyStatus(SMS_NOT_SENT);
+        invitation.setSmsContactId(resolveSmsContactId(site.getCustomerId()));
+        invitation.setEstimatedCompletionRate(VppDrParticipationHelper.calcEstimatedCompletionRate(null, null));
+        invitation.setResponseCompletionRate(VppDrParticipationHelper.calcResponseCompletionRate(null, null));
+        invitation.setRemark("UN邀约 resourceID=" + resourceId);
+        VppAuditHelper.fillCreate(invitation);
+        if (event.getTenantId() != null) {
+            invitation.setTenantId(event.getTenantId());
+        }
+        invitationMapper.insert(invitation);
+        log.info("UN 邀约已入库 eventId={} siteId={} unResourceId={}", event.getEventId(), site.getId(), resourceId);
+    }
+
+    private Long resolveSmsContactId(Long customerId) {
+        if (customerId == null) {
+            return null;
+        }
+        VppCustomerContact primary = contactMapper.selectOne(new LambdaQueryWrapper<VppCustomerContact>()
+                .eq(VppCustomerContact::getCustomerId, customerId)
+                .eq(VppCustomerContact::getIsPrimary, 1)
+                .eq(VppCustomerContact::getDeleteFlag, VppAuditHelper.NOT_DELETED)
+                .last("LIMIT 1"));
+        if (primary != null) {
+            return primary.getId();
+        }
+        VppCustomerContact any = contactMapper.selectOne(new LambdaQueryWrapper<VppCustomerContact>()
+                .eq(VppCustomerContact::getCustomerId, customerId)
+                .eq(VppCustomerContact::getDeleteFlag, VppAuditHelper.NOT_DELETED)
+                .last("LIMIT 1"));
+        return any != null ? any.getId() : null;
+    }
+
+    private LocalDate resolveExecuteDate(LocalDateTime fromPeriod, LocalDateTime fromEvent) {
+        if (fromPeriod != null) {
+            return fromPeriod.toLocalDate();
+        }
+        return fromEvent != null ? fromEvent.toLocalDate() : null;
+    }
+
+    /** UN 报文时间与平台展示/业务规则校正:向前(提前)1 小时。 */
+    private static LocalDateTime shiftOneHourEarlier(LocalDateTime time) {
+        return time != null ? time.minusHours(1) : null;
+    }
+
+    private BigDecimal resolveUnDemandCapacityKw(VppDrEvent event, Map<String, Object> eventMap) {
+        BigDecimal demand = VppUnEventParser.extractDemandChargeKw(eventMap);
+        if (demand == null || demand.compareTo(BigDecimal.ZERO) <= 0) {
+            demand = event.getTargetCapacityKw();
+        }
+        if (demand == null || demand.compareTo(BigDecimal.ZERO) <= 0) {
+            demand = BigDecimal.ONE;
+        }
+        return demand;
+    }
+
+    private VppDrInvitation findExistingInvitation(Long drEventId, Long siteId, Long customerId) {
+        LambdaQueryWrapper<VppDrInvitation> wrapper = new LambdaQueryWrapper<VppDrInvitation>()
+                .eq(VppDrInvitation::getDrEventId, drEventId)
+                .eq(VppDrInvitation::getDeleteFlag, VppAuditHelper.NOT_DELETED)
+                .last("LIMIT 1");
+        if (siteId != null) {
+            wrapper.eq(VppDrInvitation::getSiteId, siteId);
+        } else {
+            wrapper.eq(VppDrInvitation::getCustomerId, customerId);
+        }
+        return invitationMapper.selectOne(wrapper);
+    }
+}

+ 23 - 0
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/util/VppUnEventParser.java

@@ -56,6 +56,29 @@ public final class VppUnEventParser {
         return target instanceof Map ? (Map<String, Object>) target : Collections.emptyMap();
     }
 
+    @SuppressWarnings("unchecked")
+    public static List<String> getTargetResourceIds(Map<String, Object> event) {
+        Map<String, Object> target = getTarget(event);
+        Object resourceIds = target.get("resourceID");
+        if (resourceIds == null) {
+            resourceIds = target.get("resourceId");
+        }
+        if (!(resourceIds instanceof List)) {
+            return Collections.emptyList();
+        }
+        List<String> result = new ArrayList<>();
+        for (Object item : (List<?>) resourceIds) {
+            if (item == null) {
+                continue;
+            }
+            String value = String.valueOf(item).trim();
+            if (StringUtils.hasText(value)) {
+                result.add(value);
+            }
+        }
+        return result;
+    }
+
     @SuppressWarnings("unchecked")
     public static List<Map<String, Object>> getTargetResources(Map<String, Object> event) {
         Map<String, Object> target = getTarget(event);