From 34981c30a78e8bbd7791131059a9210f9928b62c Mon Sep 17 00:00:00 2001
From: 刘光辉 <347230014@qq.com>
Date: 星期四, 17 九月 2026 09:24:09 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master' into master

---
 jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/impl/LimsMessageServiceImpl.java |  204 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 204 insertions(+), 0 deletions(-)

diff --git a/jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/impl/LimsMessageServiceImpl.java b/jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/impl/LimsMessageServiceImpl.java
new file mode 100644
index 0000000..c00d48c
--- /dev/null
+++ b/jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/impl/LimsMessageServiceImpl.java
@@ -0,0 +1,204 @@
+package jnpf.limsService.impl;
+
+import jnpf.base.UserInfo;
+import jnpf.limsEntity.LimsMessageDeliveryEntity;
+import jnpf.limsEntity.LimsMessageDeliveryStatus;
+import jnpf.limsEntity.LimsMessageEventEntity;
+import jnpf.limsEntity.LimsMessageEventStatus;
+import jnpf.limsEntity.LimsMessageLedger;
+import jnpf.limsEntity.LimsMessageRequest;
+import jnpf.limsEntity.LimsMessageSendResult;
+import jnpf.limsMapper.LimsMessageDeliveryMapper;
+import jnpf.limsMapper.LimsMessageEventMapper;
+import jnpf.limsService.LimsMessageService;
+import jnpf.limsService.LimsRecipientResolver;
+import jnpf.limsService.client.LimsMessageClient;
+import jnpf.message.model.SentMessageForm;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class LimsMessageServiceImpl implements LimsMessageService {
+    private static final int MAX_ERROR_LENGTH = 1000;
+
+    private final LimsMessageEventMapper eventMapper;
+    private final LimsMessageDeliveryMapper deliveryMapper;
+    private final LimsRecipientResolver recipientResolver;
+    private final LimsMessageClient messageClient;
+    private final LimsMessageLedgerService ledgerService;
+    private final LimsMessageConfigValidator configValidator;
+
+    @Override
+    public LimsMessageSendResult send(LimsMessageRequest request) {
+        validate(request);
+        request.setTenantId(normalizeTenant(request.getTenantId()));
+        List<String> recipients = recipientResolver.resolve(request.getTenantId(), request.getRecipients());
+        LimsMessageConfigValidator.ValidationResult config = validateConfig(request);
+        if (request.isDryRun()) {
+            log.info("[lims-message][dry] event={}, bizId={}, occurrence={}, configValid={}, configError={}, " +
+                            "recipients={}, parameters={}",
+                    request.getEvent().getCode(), request.getBizId(), request.getOccurrenceKey(),
+                    config.isValid(), config.getError(), recipients, request.getParameters());
+            int sendable = config.isValid() ? recipients.size() : 0;
+            return new LimsMessageSendResult(true, false, recipients.size(), sendable, 0);
+        }
+
+        boolean ready = config.isValid() && !recipients.isEmpty();
+        LimsMessageLedger ledger = ledgerService.initialize(request,
+                ready ? recipients : Collections.emptyList());
+        if (!ledger.isAcquired()) {
+            return LimsMessageSendResult.duplicate();
+        }
+        LimsMessageEventEntity event = ledger.getEvent();
+        if (!config.isValid()) {
+            event.setRecipientCount(recipients.size());
+            failEvent(event, config.getError());
+            log.error("[lims-message] configuration rejected event={}, bizId={}, occurrence={}, reason={}",
+                    request.getEvent().getCode(), request.getBizId(), request.getOccurrenceKey(), config.getError());
+            return new LimsMessageSendResult(true, false, recipients.size(), 0, 0);
+        }
+        if (recipients.isEmpty()) {
+            failEvent(event, "RECIPIENT_NOT_FOUND: no active user resolved");
+            log.error("[lims-message] no recipient event={}, bizId={}, occurrence={}, refs={}, tenant={}",
+                    request.getEvent().getCode(), request.getBizId(), request.getOccurrenceKey(),
+                    request.getRecipients(), request.getTenantId());
+            return new LimsMessageSendResult(true, false, 0, 0, 0);
+        }
+
+        List<LimsMessageDeliveryEntity> deliveries = ledger.getDeliveries();
+        event.setStatus(LimsMessageEventStatus.PROCESSING);
+        event.setLastModifyTime(new Date());
+        eventMapper.updateById(event);
+
+        int sent = 0;
+        int failed = 0;
+        for (LimsMessageDeliveryEntity delivery : deliveries) {
+            if (deliveryMapper.claim(delivery.getId()) == 0) {
+                failed++;
+                log.warn("[lims-message] delivery claim lost, event={}, delivery={}",
+                        request.getEvent().getCode(), delivery.getId());
+                continue;
+            }
+            try {
+                List<String> errors = messageClient.sendScheduleMessage(
+                        buildMessageForm(request, delivery));
+                if (errors != null && !errors.isEmpty()) {
+                    markFailed(delivery, String.join("; ", errors));
+                    failed++;
+                } else {
+                    markSent(delivery);
+                    sent++;
+                }
+            } catch (Exception e) {
+                markFailed(delivery, e.getMessage());
+                failed++;
+                log.error("[lims-message] send failed event={}, bizId={}, recipient={}",
+                        request.getEvent().getCode(), request.getBizId(), delivery.getRecipientUserId(), e);
+            }
+        }
+        aggregate(event, recipients.size(), sent, failed);
+        return new LimsMessageSendResult(true, false, recipients.size(), sent, failed);
+    }
+
+    private SentMessageForm buildMessageForm(LimsMessageRequest request, LimsMessageDeliveryEntity delivery) {
+        UserInfo userInfo = new UserInfo();
+        userInfo.setUserId("system");
+        userInfo.setUserName("LIMS");
+        userInfo.setTenantId(normalizeTenant(request.getTenantId()));
+
+        Map<String, Object> parameters = request.getParameters() == null
+                ? new HashMap<>() : new HashMap<>(request.getParameters());
+        parameters.put("deliveryKey", delivery.getDeliveryKey());
+
+        SentMessageForm form = new SentMessageForm();
+        form.setToUserIds(Collections.singletonList(delivery.getRecipientUserId()));
+        form.setTemplateId(request.getEvent().getCode());
+        form.setParameterMap(parameters);
+        form.setContentMsg(new HashMap<>());
+        form.setUserInfo(userInfo);
+        return form;
+    }
+
+    private void markSent(LimsMessageDeliveryEntity delivery) {
+        Date now = new Date();
+        delivery.setStatus(LimsMessageDeliveryStatus.SENT);
+        delivery.setSentTime(now);
+        delivery.setLastModifyTime(now);
+        delivery.setErrorCode(null);
+        delivery.setErrorMessage(null);
+        deliveryMapper.updateById(delivery);
+    }
+
+    private void markFailed(LimsMessageDeliveryEntity delivery, String error) {
+        delivery.setStatus(LimsMessageDeliveryStatus.FAILED);
+        delivery.setRetryCount(delivery.getRetryCount() == null ? 1 : delivery.getRetryCount() + 1);
+        delivery.setErrorCode("SEND_FAILED");
+        delivery.setErrorMessage(limit(error));
+        delivery.setNextRetryTime(new Date(System.currentTimeMillis() + 2 * 60_000L));
+        delivery.setLastModifyTime(new Date());
+        deliveryMapper.updateById(delivery);
+    }
+
+    private void aggregate(LimsMessageEventEntity event, int recipients, int sent, int failed) {
+        Date now = new Date();
+        event.setRecipientCount(recipients);
+        event.setSentCount(sent);
+        event.setFailedCount(failed);
+        event.setStatus(failed == 0 ? LimsMessageEventStatus.COMPLETED
+                : sent == 0 ? LimsMessageEventStatus.FAILED : LimsMessageEventStatus.PARTIAL_FAILED);
+        event.setLastModifyTime(now);
+        if (failed == 0) {
+            event.setCompletedTime(now);
+        }
+        eventMapper.updateById(event);
+    }
+
+    private void failEvent(LimsMessageEventEntity event, String error) {
+        event.setStatus(LimsMessageEventStatus.FAILED);
+        event.setErrorMessage(limit(error));
+        event.setLastModifyTime(new Date());
+        eventMapper.updateById(event);
+    }
+
+    private void validate(LimsMessageRequest request) {
+        if (request == null || request.getEvent() == null) {
+            throw new IllegalArgumentException("message event is required");
+        }
+        if (!StringUtils.hasText(request.getBizId()) || !StringUtils.hasText(request.getOccurrenceKey())) {
+            throw new IllegalArgumentException("bizId and occurrenceKey are required");
+        }
+    }
+
+    private LimsMessageConfigValidator.ValidationResult validateConfig(LimsMessageRequest request) {
+        try {
+            return configValidator.validate(request.getEvent().getCode());
+        } catch (Exception e) {
+            log.error("[lims-message] configuration check failed event={}, bizId={}",
+                    request.getEvent().getCode(), request.getBizId(), e);
+            return new LimsMessageConfigValidator.ValidationResult(false,
+                    "CONFIG_INVALID: configuration check failed: " + limit(e.getMessage()));
+        }
+    }
+
+    private String normalizeTenant(String tenantId) {
+        return StringUtils.hasText(tenantId) ? tenantId : "0";
+    }
+
+    private String limit(String value) {
+        if (!StringUtils.hasText(value)) {
+            return "Unknown send error";
+        }
+        return value.length() <= MAX_ERROR_LENGTH ? value : value.substring(0, MAX_ERROR_LENGTH);
+    }
+
+}

--
Gitblit v1.8.0