Просмотр исходного кода

修改bug:
优化invoicingCheck接口,并增加ins_ply_income_invoice_link的invoice_type字段

jiakai 4 месяцев назад
Родитель
Сommit
fad35af1f9

+ 4 - 0
commons/src/main/java/com/jzg/commons/entity/finance/po/InsPlyIncomeInvoiceLink.java

@@ -17,4 +17,8 @@ public class InsPlyIncomeInvoiceLink{
     @Schema(description = "应收表ID")
     private String incomeId;
 
+    // 开票类型 1:手续费 2:跟单费 3:驾意手续费 4::驾意跟单费
+    @Schema(description = "开票类型")
+    private String invoiceType;
+
 }

+ 84 - 31
tenant/organization/src/main/java/com/jzg/organization/service/impl/ReceivableServiceImpl.java

@@ -454,44 +454,96 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
         return invoiceIds;
     }
 
+    /**
+     * 开票重复校验
+     * 功能:DB+Redis双重校验,防止并发重复开票
+     *
+     * @param invoicingVo 开票参数
+     * @return true=允许开票,false=禁止开票
+     */
     @Override
     public boolean invoicingCheck(InvoicingVo invoicingVo) {
+        // 1. 基础参数校验
+        if (invoicingVo == null || CollUtil.isEmpty(invoicingVo.getIncomeIds()) || StrUtil.isBlank(invoicingVo.getInvoiceType())) {
+            log.error("开票校验参数不能为空");
+            return false;
+        }
+
+        // 2. 数据库校验:是否已存在开票记录
         Integer count = baseMapper.invoicingCheck(invoicingVo);
-        RKeys keys = redissonClient.getKeys();
-        Iterable<String> allKeys = null;
-        String redisKey = null;
-        if (invoicingVo.getInvoiceType().equals("1")) {
-            redisKey = CAR_INVOICING_COMMISSION_LOCK;
-        } else if (invoicingVo.getInvoiceType().equals("2")) {
-            redisKey = CAR_INVOICING_OTHER_LOCK;
-        } else if (invoicingVo.getInvoiceType().equals("3")) {
-            redisKey = JY_INVOICING_COMMISSION_LOCK;
-        } else if (invoicingVo.getInvoiceType().equals("4")) {
-            redisKey = JY_INVOICING_OTHER_LOCK;
+        if (count > 0) {
+            log.info("数据库已存在开票记录,直接禁止");
+            return false;
         }
-        allKeys = keys.getKeysByPattern(redisKey + "*");
-        //提取所有redis的key  然后遍历是否存在重复的id
-        for (String key : allKeys) {
-            // 过滤掉自己的redis key
-            if (!key.equals(key + baseController.getUserName())) {
-                RBucket<Object> bucket = redissonClient.getBucket(key);
-                String incomeIds = bucket.get().toString();
-                incomeIds = incomeIds.substring(1, incomeIds.length() - 1);
-                List<String> incomeIdList = Arrays.stream(incomeIds.split(",")).collect(Collectors.toList());
-                boolean hasDuplicate = invoicingVo.getIncomeIds().stream().anyMatch(incomeIdList::contains);
-                if (hasDuplicate) {
-                    return false;
-                }
+
+        // 3. 根据开票类型获取Redis锁前缀
+        String redisKeyPrefix = switch (invoicingVo.getInvoiceType()) {
+            case "1" -> CAR_INVOICING_COMMISSION_LOCK;
+            case "2" -> CAR_INVOICING_OTHER_LOCK;
+            case "3" -> JY_INVOICING_COMMISSION_LOCK;
+            case "4" -> JY_INVOICING_OTHER_LOCK;
+            default -> throw new SystemException("不支持的开票类型");
+        };
+
+        // 4. 当前用户的Redis Key
+        String currentUserName = baseController.getUserName();
+        String currentRedisKey = redisKeyPrefix + currentUserName;
+
+        // 5. Redis校验:同类型下,是否有其他用户占用了相同incomeId
+        // 把锁key存入同一个set,避免使用keys命令
+        RKeys keys = redissonClient.getKeys();
+        Iterable<String> allLockKeys = keys.getKeysByPattern(redisKeyPrefix + "*");
+
+        for (String lockKey : allLockKeys) {
+            // 正确过滤自己的key
+            if (currentRedisKey.equals(lockKey)) {
+                continue;
+            }
+
+            // 安全获取缓存值
+            RBucket<Object> bucket = redissonClient.getBucket(lockKey);
+            Object cacheObj = bucket.get();
+            if (cacheObj == null) {
+                continue;
             }
-        }
 
-        if (count == 0) {
-            // 设置缓存
-            RBucket<Object> bucket = redissonClient.getBucket(redisKey + baseController.getUserName());
-            bucket.set(invoicingVo.getIncomeIds(), 10, TimeUnit.MINUTES);
+            // 解析缓存中的incomeId集合
+            List<String> cacheIncomeIds = this.parseIncomeIdsFromRedis(cacheObj.toString());
+            // 校验重复
+            boolean duplicate = invoicingVo.getIncomeIds().stream().anyMatch(cacheIncomeIds::contains);
+            if (duplicate) {
+                log.info("Redis校验重复,lockKey:{}", lockKey);
+                return false;
+            }
         }
 
-        return count == 0;
+        // 6. 校验通过,存入Redis锁(10分钟自动过期)
+        RBucket<Object> bucket = redissonClient.getBucket(currentRedisKey);
+        bucket.set(invoicingVo.getIncomeIds(), 10, TimeUnit.MINUTES);
+        log.info("开票校验通过,Redis加锁成功,key:{}", currentRedisKey);
+
+        return true;
+    }
+
+    /**
+     * 从Redis字符串解析收入ID集合(安全解析,防止异常)
+     */
+    private List<String> parseIncomeIdsFromRedis(String incomeIdsStr) {
+        try {
+            if (StrUtil.isBlank(incomeIdsStr) || !incomeIdsStr.startsWith("[") || !incomeIdsStr.endsWith("]")) {
+                return Collections.emptyList();
+            }
+            String content = incomeIdsStr.substring(1, incomeIdsStr.length() - 1).trim();
+            if (StrUtil.isBlank(content)) {
+                return Collections.emptyList();
+            }
+            return Arrays.stream(content.split(","))
+                    .map(String::trim)
+                    .collect(Collectors.toList());
+        } catch (Exception e) {
+            log.error("解析Redis收入ID异常", e);
+            return Collections.emptyList();
+        }
     }
 
     @Override
@@ -553,11 +605,12 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
                 link.setId(IdGenerate.nextId());
                 link.setInvoiceId(invoice.getId());
                 link.setIncomeId(incomeId);
+                link.setInvoiceType(invoicingVo.getInvoiceType());
                 invoicingLinkList.add(link);
             });
             insPlyIncomeInvoiceLinkMapper.insert(invoicingLinkList);
             result.setInvoiceId(invoice.getId());
-            result.setSumPrice(invoicingVo.getReceivablePremium().toString());
+            result.setSumPrice(null != invoicingVo.getReceivablePremium() ? invoicingVo.getReceivablePremium().toString() : null);
             return result;
         } catch (Exception e) {
             log.info("开票异常.invoicingVo=[{}]",JSONUtil.toJsonStr(invoicingVo), e);

+ 15 - 10
tenant/organization/src/main/resources/mapper/ReceivableMapper.xml

@@ -1028,16 +1028,21 @@
             eic.id
     </select>
 
-    <select id="invoicingCheck" resultType="Integer">
-        SELECT
-            count(id) as count
-        FROM
-            ins_ply_income_invoice_link
-        where
-            income_id IN
-            <foreach collection="invoicingVo.incomeIds" item="id" open="(" separator="," close=")">
-                #{id}
-            </foreach>
+    <select id="invoicingCheck" resultType="java.lang.Integer">
+        SELECT count(id) AS count
+        FROM ins_ply_income_invoice_link
+        <where>
+            <if test="invoicingVo.incomeIds != null and invoicingVo.incomeIds.size > 0">
+                AND income_id IN
+                <foreach collection="invoicingVo.incomeIds"
+                         item="id" open="(" separator="," close=")">
+                    #{id}
+                </foreach>
+            </if>
+            <if test="invoicingVo.invoiceType != null and invoicingVo.invoiceType != ''">
+                AND invoice_type = #{invoicingVo.invoiceType}
+            </if>
+        </where>
     </select>
 
     <!-- 查询订单绑定的发票信息 lipf 2026年5月21日11:56:19 -->