From 66c13062b2ab0afb650513ce5e834c502e6be8bd Mon Sep 17 00:00:00 2001 From: Timi Date: Thu, 10 Sep 2026 12:47:29 +0800 Subject: [PATCH] fix notify fail --- .../api/modules/common/entity/Notify.java | 6 +- .../modules/common/mapper/NotifyMapper.java | 8 +- .../modules/common/service/NotifyService.java | 9 +- .../implement/NotifyServiceImplement.java | 111 +++++++++--------- .../GaoOrderReminderServiceImplement.java | 9 +- .../modules/system/audit/AuditOperation.java | 93 ++++++--------- .../system/audit/OperationAuditAspect.java | 17 +-- .../V76__extend_notify_biz_reminded_id.sql | 25 ++++ .../timi-server/common/NotifyMapper.xml | 25 ++++ 9 files changed, 159 insertions(+), 144 deletions(-) create mode 100644 src/main/resources/db/migration/timiserver/V76__extend_notify_biz_reminded_id.sql diff --git a/src/main/java/com/imyeyu/api/modules/common/entity/Notify.java b/src/main/java/com/imyeyu/api/modules/common/entity/Notify.java index 95b0ce3..72a3178 100644 --- a/src/main/java/com/imyeyu/api/modules/common/entity/Notify.java +++ b/src/main/java/com/imyeyu/api/modules/common/entity/Notify.java @@ -20,11 +20,11 @@ public class Notify extends UUIDEntity { /** 业务类型 */ private String bizType; - /** 提醒业务类型 */ + /** 业务提醒类型 */ private String bizRemindedType; - /** 提醒业务 ID */ - private String bizRemindedId; + /** 业务提醒查重键 */ + private String bizRemindedKey; /** 参数 */ private String args; diff --git a/src/main/java/com/imyeyu/api/modules/common/mapper/NotifyMapper.java b/src/main/java/com/imyeyu/api/modules/common/mapper/NotifyMapper.java index 5636f9f..44df6e9 100644 --- a/src/main/java/com/imyeyu/api/modules/common/mapper/NotifyMapper.java +++ b/src/main/java/com/imyeyu/api/modules/common/mapper/NotifyMapper.java @@ -2,9 +2,8 @@ package com.imyeyu.api.modules.common.mapper; import com.imyeyu.api.modules.common.entity.Notify; import com.imyeyu.api.modules.common.vo.NotifyManagementItem; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Select; import com.imyeyu.spring.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; import java.util.List; @@ -16,9 +15,8 @@ import java.util.List; */ public interface NotifyMapper extends BaseMapper { - /// 查询业务提醒是否已创建 - @Select("SELECT EXISTS (SELECT 1 FROM `notify` WHERE `biz_type` = #{bizType} AND `biz_reminded_type` = #{bizRemindedType} AND `biz_reminded_id` = #{bizRemindedId} AND `deleted_at` IS NULL)") - boolean existsByBizReminded(@Param("bizType") String bizType, @Param("bizRemindedType") String bizRemindedType, @Param("bizRemindedId") String bizRemindedId); + /// 原子创建业务提醒 + int insertIfAbsent(Notify notify); /// 分页查询管理端通知 /// diff --git a/src/main/java/com/imyeyu/api/modules/common/service/NotifyService.java b/src/main/java/com/imyeyu/api/modules/common/service/NotifyService.java index 4b4bd09..1b738c8 100644 --- a/src/main/java/com/imyeyu/api/modules/common/service/NotifyService.java +++ b/src/main/java/com/imyeyu/api/modules/common/service/NotifyService.java @@ -14,13 +14,10 @@ import com.imyeyu.spring.service.BaseService; */ public interface NotifyService extends BaseService { - /// 查询业务提醒是否已创建 + /// 原子创建业务提醒,重复查重键不会创建通知详情 /// - /// @param bizType 业务类型 - /// @param bizRemindedType 被提醒业务类型 - /// @param bizRemindedId 被提醒业务 ID - /// @return true 为已创建 - boolean existsByBizReminded(String bizType, String bizRemindedType, String bizRemindedId); + /// @param notify 通知 + void createIfAbsent(Notify notify); /// 分页查询当前用户创建的通知 /// diff --git a/src/main/java/com/imyeyu/api/modules/common/service/implement/NotifyServiceImplement.java b/src/main/java/com/imyeyu/api/modules/common/service/implement/NotifyServiceImplement.java index 3f5acee..20adf27 100644 --- a/src/main/java/com/imyeyu/api/modules/common/service/implement/NotifyServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/common/service/implement/NotifyServiceImplement.java @@ -7,9 +7,8 @@ import com.imyeyu.api.modules.common.mapper.NotifyMapper; import com.imyeyu.api.modules.common.service.NotifyDetailService; import com.imyeyu.api.modules.common.service.NotifyService; import com.imyeyu.api.modules.common.vo.NotifyManagementItem; -import com.imyeyu.api.modules.system.entity.Logger; -import com.imyeyu.api.modules.system.service.LoggerService; -import com.imyeyu.api.modules.user.service.UserLoginService; +import com.imyeyu.api.modules.system.audit.AuditOperation; +import com.imyeyu.api.modules.system.audit.OperationAudit; import com.imyeyu.java.TimiJava; import com.imyeyu.java.bean.timi.TimiCode; import com.imyeyu.java.bean.timi.TimiException; @@ -17,12 +16,13 @@ import com.imyeyu.spring.bean.Page; import com.imyeyu.spring.bean.PageResult; import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.service.AbstractEntityService; +import com.imyeyu.utils.Time; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.UUID; /** * 通知服务实现 @@ -30,13 +30,10 @@ import java.util.List; * @author 夜雨 * @since 2026-05-13 14:46 */ -@Slf4j @Service @RequiredArgsConstructor public class NotifyServiceImplement extends AbstractEntityService implements NotifyService { - private final LoggerService loggerService; - private final UserLoginService userLoginService; private final NotifyDetailService detailService; private final NotifyMapper mapper; @@ -46,43 +43,53 @@ public class NotifyServiceImplement extends AbstractEntityService detailList = detailService.listByNotifyId(notifyId); - for (NotifyDetail detail : detailList) { - detailService.delete(detail.getId()); - } - super.delete(notifyId); - logger.setLevel(Logger.Level.INFO); - logger.setResult(notifyId); - } catch (TimiException e) { - logger.setLevel(Logger.Level.WARN); - logger.setException(e.getMessage()); - throw e; - } catch (Exception e) { - logger.setLevel(Logger.Level.ERROR); - logger.setException(TimiJava.serializeThrowable(e)); - log.error("删除通知失败", e); - throw new TimiException(TimiCode.ERROR, "删除通知失败", e); - } finally { - loggerService.create(logger); + TimiException.required(notifyId, "未找到通知 ID"); + TimiException.required(get(notifyId), "未找到通知"); + List detailList = detailService.listByNotifyId(notifyId); + for (NotifyDetail detail : detailList) { + detailService.delete(detail.getId()); } + super.delete(notifyId); } /// 取消通知中尚未成功发送的通知详情 diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderReminderServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderReminderServiceImplement.java index 0db3907..760a676 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderReminderServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderReminderServiceImplement.java @@ -103,20 +103,17 @@ public class GaoOrderReminderServiceImplement implements GaoOrderReminderService log.warn("GAO 订单提醒跳过不存在的店长用户: storeId[{}], userId[{}]", storeId, manager.getUserId()); continue; } - String remindedId = "%s:%s:%s:%s".formatted(LocalDate.now(ZoneId.of(ZONE_ID)), storeId, manager.getUserId(), reminderType); - if (notifyService.existsByBizReminded(NOTIFY_BIZ_TYPE, reminderType, remindedId)) { - continue; - } + String remindedKey = "%s:%s:%s:%s".formatted(LocalDate.now(ZoneId.of(ZONE_ID)), storeId, manager.getUserId(), reminderType); long sendAt = Time.now(); Notify notify = new Notify(); notify.setBizType(NOTIFY_BIZ_TYPE); notify.setBizRemindedType(reminderType); - notify.setBizRemindedId(remindedId); + notify.setBizRemindedKey(remindedKey); notify.setDetailList(List.of( createInternalDetail(manager.getUserId(), content, sendAt), createSmsDetail(user.getPhoneNo(), customerNames, action, content, sendAt) )); - notifyService.create(notify); + notifyService.createIfAbsent(notify); } catch (Exception e) { log.error("GAO 订单提醒发送失败: storeId[{}], userId[{}], reminderType[{}]", storeId, manager.getUserId(), reminderType, e); } diff --git a/src/main/java/com/imyeyu/api/modules/system/audit/AuditOperation.java b/src/main/java/com/imyeyu/api/modules/system/audit/AuditOperation.java index 95ce596..df5f76d 100644 --- a/src/main/java/com/imyeyu/api/modules/system/audit/AuditOperation.java +++ b/src/main/java/com/imyeyu/api/modules/system/audit/AuditOperation.java @@ -1,66 +1,51 @@ package com.imyeyu.api.modules.system.audit; import com.imyeyu.api.modules.system.entity.Logger; +import lombok.AllArgsConstructor; +import lombok.Getter; /// 操作审计编码 /// /// @author Codex /// @since 2026-09-09 +@Getter +@AllArgsConstructor public enum AuditOperation { - GAO_CUSTOMER_PACKAGE_PURCHASE(Logger.Module.GAO, "GAO_CUSTOMER_PACKAGE_PURCHASE"), - GAO_CUSTOMER_PACKAGE_CANCEL(Logger.Module.GAO, "GAO_CUSTOMER_PACKAGE_CANCEL"), - GAO_SALES_ORDER_PAYMENT_RECEIVE(Logger.Module.GAO, "GAO_SALES_ORDER_PAYMENT_RECEIVE"), - GAO_SALES_ORDER_PAYMENT_VERIFY(Logger.Module.GAO, "GAO_SALES_ORDER_PAYMENT_VERIFY"), - GAO_SALES_ORDER_REFUND(Logger.Module.GAO, "GAO_SALES_ORDER_REFUND"), - GAO_SALES_ORDER_CONFIRM_NO_REFUND(Logger.Module.GAO, "GAO_SALES_ORDER_CONFIRM_NO_REFUND"), - GAO_SALES_ORDER_FUND_REVOKE(Logger.Module.GAO, "GAO_SALES_ORDER_FUND_REVOKE"), - GAO_ORDER_FULFILLMENT_CREATE(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_CREATE"), - GAO_ORDER_FULFILLMENT_PREPARE_STOCK(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_PREPARE_STOCK"), - GAO_ORDER_FULFILLMENT_VERIFY(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_VERIFY"), - GAO_ORDER_FULFILLMENT_REJECT(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_REJECT"), - GAO_ORDER_FULFILLMENT_CANCEL(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_CANCEL"), - GAO_ORDER_FULFILLMENT_REVOKE(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_REVOKE"), - GAO_PACKAGE_CONFIG_CREATE(Logger.Module.GAO, "GAO_PACKAGE_CONFIG_CREATE"), - GAO_PACKAGE_CONFIG_UPDATE(Logger.Module.GAO, "GAO_PACKAGE_CONFIG_UPDATE"), - GAO_PACKAGE_CONFIG_DELETE(Logger.Module.GAO, "GAO_PACKAGE_CONFIG_DELETE"), - GAO_PRODUCT_CATEGORY_SORT(Logger.Module.GAO, "GAO_PRODUCT_CATEGORY_SORT"), - GAO_PRODUCT_CATEGORY_CREATE(Logger.Module.GAO, "GAO_PRODUCT_CATEGORY_CREATE"), - GAO_PRODUCT_CATEGORY_UPDATE(Logger.Module.GAO, "GAO_PRODUCT_CATEGORY_UPDATE"), - GAO_PRODUCT_CATEGORY_DISABLE(Logger.Module.GAO, "GAO_PRODUCT_CATEGORY_DISABLE"), - GAO_PRODUCT_CREATE(Logger.Module.GAO, "GAO_PRODUCT_CREATE"), - GAO_PRODUCT_UPDATE(Logger.Module.GAO, "GAO_PRODUCT_UPDATE"), - GAO_PRODUCT_DISABLE(Logger.Module.GAO, "GAO_PRODUCT_DISABLE"), - GAO_PRODUCT_STOCK_INITIAL_IN(Logger.Module.GAO, "GAO_PRODUCT_STOCK_INITIAL_IN"), - GAO_PRODUCT_STOCK_MANUAL_IN(Logger.Module.GAO, "GAO_PRODUCT_STOCK_MANUAL_IN"), - GAO_PRODUCT_STOCK_MANUAL_OUT(Logger.Module.GAO, "GAO_PRODUCT_STOCK_MANUAL_OUT"), - GAO_PRODUCT_STOCK_MANUAL_REVOKE(Logger.Module.GAO, "GAO_PRODUCT_STOCK_MANUAL_REVOKE"), - GAO_PRODUCT_STOCK_STOCKTAKE_GAIN(Logger.Module.GAO, "GAO_PRODUCT_STOCK_STOCKTAKE_GAIN"), - GAO_PRODUCT_STOCK_STOCKTAKE_LOSS(Logger.Module.GAO, "GAO_PRODUCT_STOCK_STOCKTAKE_LOSS"), - GAO_SALES_ORDER_DIRECT_PRODUCT_CREATE(Logger.Module.GAO, "GAO_SALES_ORDER_DIRECT_PRODUCT_CREATE"), - GAO_SALES_ORDER_UPDATE(Logger.Module.GAO, "GAO_SALES_ORDER_UPDATE"), - GAO_SALES_ORDER_CANCEL(Logger.Module.GAO, "GAO_SALES_ORDER_CANCEL"); + COMMON_NOTIFY_CREATE(Logger.Module.COMMON), + COMMON_NOTIFY_DELETE(Logger.Module.COMMON), + GAO_CUSTOMER_PACKAGE_PURCHASE(Logger.Module.GAO), + GAO_CUSTOMER_PACKAGE_CANCEL(Logger.Module.GAO), + GAO_SALES_ORDER_PAYMENT_RECEIVE(Logger.Module.GAO), + GAO_SALES_ORDER_PAYMENT_VERIFY(Logger.Module.GAO), + GAO_SALES_ORDER_REFUND(Logger.Module.GAO), + GAO_SALES_ORDER_CONFIRM_NO_REFUND(Logger.Module.GAO), + GAO_SALES_ORDER_FUND_REVOKE(Logger.Module.GAO), + GAO_ORDER_FULFILLMENT_CREATE(Logger.Module.GAO), + GAO_ORDER_FULFILLMENT_PREPARE_STOCK(Logger.Module.GAO), + GAO_ORDER_FULFILLMENT_VERIFY(Logger.Module.GAO), + GAO_ORDER_FULFILLMENT_REJECT(Logger.Module.GAO), + GAO_ORDER_FULFILLMENT_CANCEL(Logger.Module.GAO), + GAO_ORDER_FULFILLMENT_REVOKE(Logger.Module.GAO), + GAO_PACKAGE_CONFIG_CREATE(Logger.Module.GAO), + GAO_PACKAGE_CONFIG_UPDATE(Logger.Module.GAO), + GAO_PACKAGE_CONFIG_DELETE(Logger.Module.GAO), + GAO_PRODUCT_CATEGORY_SORT(Logger.Module.GAO), + GAO_PRODUCT_CATEGORY_CREATE(Logger.Module.GAO), + GAO_PRODUCT_CATEGORY_UPDATE(Logger.Module.GAO), + GAO_PRODUCT_CATEGORY_DISABLE(Logger.Module.GAO), + GAO_PRODUCT_CREATE(Logger.Module.GAO), + GAO_PRODUCT_UPDATE(Logger.Module.GAO), + GAO_PRODUCT_DISABLE(Logger.Module.GAO), + GAO_PRODUCT_STOCK_INITIAL_IN(Logger.Module.GAO), + GAO_PRODUCT_STOCK_MANUAL_IN(Logger.Module.GAO), + GAO_PRODUCT_STOCK_MANUAL_OUT(Logger.Module.GAO), + GAO_PRODUCT_STOCK_MANUAL_REVOKE(Logger.Module.GAO), + GAO_PRODUCT_STOCK_STOCKTAKE_GAIN(Logger.Module.GAO), + GAO_PRODUCT_STOCK_STOCKTAKE_LOSS(Logger.Module.GAO), + GAO_SALES_ORDER_DIRECT_PRODUCT_CREATE(Logger.Module.GAO), + GAO_SALES_ORDER_UPDATE(Logger.Module.GAO), + GAO_SALES_ORDER_CANCEL(Logger.Module.GAO); - private final Logger.Module module; - - private final String code; - - AuditOperation(Logger.Module module, String code) { - this.module = module; - this.code = code; - } - - /// 获取日志模块 - /// - /// @return 日志模块 - public Logger.Module getModule() { - return module; - } - - /// 获取操作编码 - /// - /// @return 操作编码 - public String getCode() { - return code; - } + final Logger.Module module; } diff --git a/src/main/java/com/imyeyu/api/modules/system/audit/OperationAuditAspect.java b/src/main/java/com/imyeyu/api/modules/system/audit/OperationAuditAspect.java index a1e1431..71368ad 100644 --- a/src/main/java/com/imyeyu/api/modules/system/audit/OperationAuditAspect.java +++ b/src/main/java/com/imyeyu/api/modules/system/audit/OperationAuditAspect.java @@ -1,6 +1,7 @@ package com.imyeyu.api.modules.system.audit; import com.imyeyu.api.modules.system.entity.Logger; +import com.imyeyu.java.TimiJava; import com.imyeyu.java.bean.timi.TimiException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -30,7 +31,7 @@ public class OperationAuditAspect { /// 执行并记录操作审计 /// - /// @param joinPoint 连接点 + /// @param joinPoint 连接点 /// @param operationAudit 审计配置 /// @return 业务返回值 /// @throws Throwable 业务异常 @@ -38,7 +39,7 @@ public class OperationAuditAspect { public Object around(ProceedingJoinPoint joinPoint, OperationAudit operationAudit) throws Throwable { AuditOperation operation = operationAudit.value(); Object[] arguments = joinPoint.getArgs(); - Logger logger = new Logger(operation.getModule(), operation.getCode()); + Logger logger = new Logger(operation.getModule(), operation.name()); logger.setContent(payloadSerializer.serializeArguments(arguments)); long startNanos = System.nanoTime(); try { @@ -56,9 +57,9 @@ public class OperationAuditAspect { throw e; } catch (Throwable e) { logger.setLevel(Logger.Level.ERROR); - logger.setException(serializeThrowable(e)); + logger.setException(TimiJava.serializeThrowable(e)); logger.setDuration(duration(startNanos)); - log.error("业务操作失败: {}", operation.getCode(), e); + log.error("业务操作失败: {}", operation.name(), e); record(logger); throw e; } @@ -79,12 +80,4 @@ public class OperationAuditAspect { } return (int) Math.max(0L, millis); } - - private String serializeThrowable(Throwable throwable) { - java.io.StringWriter stringWriter = new java.io.StringWriter(); - try (java.io.PrintWriter printWriter = new java.io.PrintWriter(stringWriter)) { - throwable.printStackTrace(printWriter); - } - return stringWriter.toString(); - } } diff --git a/src/main/resources/db/migration/timiserver/V76__extend_notify_biz_reminded_id.sql b/src/main/resources/db/migration/timiserver/V76__extend_notify_biz_reminded_id.sql new file mode 100644 index 0000000..48013cc --- /dev/null +++ b/src/main/resources/db/migration/timiserver/V76__extend_notify_biz_reminded_id.sql @@ -0,0 +1,25 @@ +ALTER TABLE `notify` + CHANGE COLUMN `biz_reminded_id` `biz_reminded_key` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '业务提醒查重键', + ADD COLUMN `biz_reminded_active_key` TINYINT(1) GENERATED ALWAYS AS (IF(`deleted_at` IS NULL, 1, NULL)) STORED COMMENT '未删除查重键' INVISIBLE AFTER `deleted_at`, + DROP INDEX `idx_biz_reminded_created`, + ADD INDEX `idx_biz_reminded_key_created`(`biz_type` ASC, `biz_reminded_type` ASC, `biz_reminded_key` ASC, `created_at` ASC) USING BTREE; + +UPDATE `notify` target +INNER JOIN ( + SELECT + id, + ROW_NUMBER() OVER ( + PARTITION BY biz_type, biz_reminded_type, biz_reminded_key + ORDER BY created_at DESC, id DESC + ) AS rn + FROM `notify` + WHERE deleted_at IS NULL + AND biz_type IS NOT NULL + AND biz_reminded_type IS NOT NULL + AND biz_reminded_key IS NOT NULL +) duplicate ON duplicate.id = target.id +SET target.deleted_at = UNIX_TIMESTAMP() * 1000 +WHERE duplicate.rn > 1; + +ALTER TABLE `notify` + ADD UNIQUE INDEX `uk_biz_reminded_key_active`(`biz_type` ASC, `biz_reminded_type` ASC, `biz_reminded_key` ASC, `biz_reminded_active_key` ASC) USING BTREE; diff --git a/src/main/resources/mapper/timi-server/common/NotifyMapper.xml b/src/main/resources/mapper/timi-server/common/NotifyMapper.xml index 0e518c9..e2bdba7 100644 --- a/src/main/resources/mapper/timi-server/common/NotifyMapper.xml +++ b/src/main/resources/mapper/timi-server/common/NotifyMapper.xml @@ -1,6 +1,31 @@ + + INSERT INTO `notify` ( + `id`, + `biz_type`, + `biz_reminded_type`, + `biz_reminded_key`, + `args`, + `created_by`, + `created_at`, + `updated_at`, + `deleted_at` + ) VALUES ( + #{id}, + #{bizType}, + #{bizRemindedType}, + #{bizRemindedKey}, + #{args}, + #{createdBy}, + #{createdAt}, + #{updatedAt}, + #{deletedAt} + ) + ON DUPLICATE KEY UPDATE `id` = `id` + +