yeg
2026-09-17 dd22571b369970b35ca6c48f32010e2876b43d25
talent-card-server/src/main/java/com/talentcard/server/business/merchant/MerchantWorkService.java
@@ -215,6 +215,34 @@
      return requested == null || requested.isBlank() ? "merchant" : requested.trim();
   }
   /**
    * 手输 6 位数字核销码之后的确认核销。
    *
    * <p>为什么不复用 {@code /api/merchant/orders/{orderNo}/verify}:手输那条路的扫码答复刻意不返回
    * 「这个码属于哪笔订单」(6 位只有 100 万空间,带回去等于给枚举一张映射表),所以客户端拿不到单号。
    * 这里由服务端从扫码尝试反查订单,再走同一套核销实现——幂等、行锁、资金确认都不重复写第二份。
    */
   @Transactional
   public Map<String, Object> confirmNumericVerification(MerchantPrincipal principal, String verificationId, String idempotencyKey) {
      if (idempotencyKey == null || idempotencyKey.isBlank()) {
         throw new BusinessException(ErrorCode.BAD_REQUEST, "缺少幂等键");
      }
      String attemptId = verificationId == null ? "" : verificationId.trim();
      if (attemptId.isBlank()) throw new BusinessException(ErrorCode.BAD_REQUEST, "缺少核销凭证编号");
      List<Map<String, Object>> orders = query("""
            select o.order_no
            from merchant_verification_attempt a
            join order_dynamic_verification_code c on c.id = a.order_verification_code_id
            join benefit_order o on o.id = c.order_id and o.deleted = 0
            where a.verification_id = ? and a.credential_type = 'ORDER_CODE'
              and a.merchant_id = ? and a.store_id = ? and a.operator_id = ?
            limit 1
            """, attemptId, principal.merchantId(), principal.storeId(), principal.operatorId());
      if (orders.isEmpty()) throw new BusinessException(ErrorCode.NOT_FOUND, "核销凭证不存在或无权操作");
      return verify(String.valueOf(orders.get(0).get("orderNo")).trim(), principal,
            new VerifyRequest("ORDER_CODE", "", attemptId), idempotencyKey);
   }
   /** 扫码尝试:核销成功后要标记完成,并且只有扫码码未过期才能核销。 */
   private record Attempt(Long id, java.time.LocalDateTime expiresAt) { }