瀏覽代碼

feat:公司充值同步到缓存、定时同步公司缓存余额到数据表

caoliqin 1 周之前
父節點
當前提交
59ad5ef19d

+ 11 - 1
fs-admin/src/main/java/com/fs/company/controller/CompanyRechargeController.java

@@ -24,6 +24,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.*;
 
+import java.math.BigDecimal;
 import java.util.Date;
 import java.util.List;
 
@@ -121,7 +122,16 @@ public class CompanyRechargeController extends BaseController
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         if(companyRecharge.getIsAudit()==1){
             Company company=companyService.selectCompanyById(companyRecharge.getCompanyId());
-            company.setMoney(company.getMoney().add(companyRecharge.getMoney()));
+
+            // 同步redis缓存
+            // 注意:在进行充值审核之前,需要先执行一下定时任务同步缓存数据到数据库,再进行后续操作,否则金额不正确
+            R r = companyRechargeService.syncUpdateRedisCompanyRecharge(company, companyRecharge.getMoney());
+            if(!"200".equals(r.get("code").toString())){
+                return r;
+            }
+            // 充值后,需要同步更新余额到数据库,否则余额与缓存中的不一致
+            String newMoney = r.get("newMoney").toString();
+            company.setMoney(new BigDecimal(newMoney));
             companyService.updateCompany(company);
             CompanyMoneyLogs log=new CompanyMoneyLogs();
             log.setCompanyId(companyRecharge.getCompanyId());

+ 54 - 2
fs-admin/src/main/java/com/fs/task/FsCompanyTask.java

@@ -1,21 +1,34 @@
 package com.fs.task;
 
+import com.fs.common.constant.FsConstants;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.company.domain.Company;
 import com.fs.company.service.ICompanyService;
 import com.fs.company.vo.RedPacketMoneyVO;
 import com.fs.course.mapper.FsCourseRedPacketLogMapper;
 import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;
 
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
-import java.util.List;
+import java.util.*;
+import java.util.stream.Collectors;
 
 @AllArgsConstructor
 @Component("companyTask")
+@Slf4j
 public class FsCompanyTask {
 
+    private final RedisCache redisCache;
     private FsCourseRedPacketLogMapper fsCourseRedPacketLogMapper;
     private ICompanyService companyService;
 
+    @Autowired
+    private RedisTemplate<String, Object> redisTemplate;
+
     public void refreshCompanyMoney() {
         LocalDateTime now = LocalDateTime.now();
         // 获取上一个小时的开始时间
@@ -31,4 +44,43 @@ public class FsCompanyTask {
             companyService.subtractCompanyMoneyHourse(redPacketMoneyVO.getMoney(), redPacketMoneyVO.getCompanyId(), startTime.toLocalTime(), endTime.toLocalTime());
         }
     }
-}
+
+    /**
+     * 同步公司缓存余额到公司数据表
+     */
+    public void syncRedisCompanyMoneyToDB(){
+            // 获取所有的公司余额key
+            String companyMoneyKeyAll = FsConstants.COMPANY_MONEY_KEY + "*";
+            Collection<String> keys = redisCache.keys(companyMoneyKeyAll);
+
+            if (keys != null && !keys.isEmpty()) {
+                log.info("同步缓存余额到公司表,keys:{}", keys);
+                List<Object> values = redisTemplate.opsForValue().multiGet(keys);
+                Iterator<String> keyIterator = keys.iterator();
+                if(values != null && !values.isEmpty()){
+                    Iterator<Object> valueIterator = values.iterator();
+                    List<Company> companies = companyService.selectCompanyList(new Company());
+
+                    Map<Long, BigDecimal> moneyMap = new HashMap<>();
+                    while (keyIterator.hasNext() && valueIterator.hasNext()) {
+                        String next = keyIterator.next();
+                        String[] keySplit = next.split(":");
+                        Long companyId = Long.parseLong(keySplit[2]);
+                        String value = valueIterator.next().toString();
+                        moneyMap.put(companyId, new BigDecimal(value));
+                    }
+
+                    // 使用Stream进行匹配赋值
+                    List<Company> collect = companies.stream()
+                            .filter(company -> moneyMap.containsKey(company.getCompanyId()))
+                            .peek(company -> company.setMoney(moneyMap.get(company.getCompanyId()))).collect(Collectors.toList());
+
+                    // 保存公司余额
+                    if(!collect.isEmpty()){
+                        companyService.batchUpdateCompany(collect);
+                    }
+                }
+            }
+
+    }
+}

+ 6 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyMapper.java

@@ -205,4 +205,10 @@ public interface CompanyMapper
     List<OptionsVO> getCompanyListByCorpId(@Param("corpId") String corpId);
 
     List<Company> selectCompanyMoneyAllList();
+
+    /**
+     * 批量修改公司余额
+     * @param list
+     */
+    void batchUpdateCompany(@Param("list") List<Company> list);
 }

+ 8 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyRechargeService.java

@@ -4,6 +4,7 @@ import java.math.BigDecimal;
 import java.util.List;
 
 import com.fs.common.core.domain.R;
+import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyRecharge;
 import com.fs.company.domain.CompanyRechargeOrder;
 import com.fs.company.dto.RechargeDTO;
@@ -50,6 +51,13 @@ public interface ICompanyRechargeService
      */
     public int updateCompanyRecharge(CompanyRecharge companyRecharge);
 
+    /**
+     * 同步到缓存余额
+     * @param company 公司
+     * @param rechargeMoney 充值金额
+     * @return 是否成功
+     */
+    R syncUpdateRedisCompanyRecharge(Company company, BigDecimal rechargeMoney);
 
     List<CompanyRechargeVO> selectCompanyRechargeListVO(CompanyRechargeVO companyRecharge);
     /**

+ 6 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyService.java

@@ -169,4 +169,10 @@ public interface ICompanyService
     void subtractCompanyMoneyHourse(BigDecimal money, Long companyId, LocalTime start, LocalTime end);
 
     void syncCompanyBalance();
+
+    /**
+     * 批量更新公司余额
+     * @param list 公司列表
+     */
+    void batchUpdateCompany(List<Company> list);
 }

+ 62 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyRechargeServiceImpl.java

@@ -1,12 +1,17 @@
 package com.fs.company.service.impl;
 
 import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.util.Date;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 import cn.hutool.core.util.ObjectUtil;
+import com.fs.common.constant.FsConstants;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.StringUtils;
 import com.fs.company.constant.PaymentStatus;
 import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyMoneyLogs;
@@ -19,7 +24,10 @@ import com.fs.company.service.CompanyRechargeOrderService;
 import com.fs.company.vo.CompanyRechargeExportVO;
 import com.fs.company.vo.CompanyRechargeVO;
 import lombok.extern.slf4j.Slf4j;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Service;
 import com.fs.company.mapper.CompanyRechargeMapper;
 import com.fs.company.domain.CompanyRecharge;
@@ -45,6 +53,12 @@ public class CompanyRechargeServiceImpl implements ICompanyRechargeService
     private CompanyMapper companyMapper;
     @Autowired
     private CompanyRechargeOrderService companyRechargeOrderService;
+    @Qualifier("redissonClient")
+    @Autowired
+    private RedissonClient redissonClient;
+    @Autowired
+    private RedisCache redisCache;
+
     /**
      * 查询充值
      *
@@ -93,6 +107,54 @@ public class CompanyRechargeServiceImpl implements ICompanyRechargeService
     {
         return companyRechargeMapper.updateCompanyRecharge(companyRecharge);
     }
+
+    @Override
+    public R syncUpdateRedisCompanyRecharge(Company company, BigDecimal rechargeMoney) {
+        if(company.getCompanyId() == null){
+            log.error("公司充值-审核-同步更新到缓存,参数错误,公司id:{}, 充值余额:{}", company.getCompanyId(), rechargeMoney);
+            return R.error("公司id为空");
+        }
+
+        // 公司红包真实余额key
+        String companyMoneyKey = FsConstants.COMPANY_MONEY_KEY + company.getCompanyId();
+
+        // 加锁,与看课发放红包的加锁保持一致
+        RLock lock = redissonClient.getLock(FsConstants.COMPANY_MONEY_LOCK + company.getCompanyId());
+        boolean lockAcquired = false;
+        try {
+            BigDecimal newMoney = company.getMoney();
+
+            // 尝试加锁
+            lockAcquired = lock.tryLock(3, 10, TimeUnit.SECONDS);
+            if (lockAcquired) {
+                BigDecimal redisMoney;
+                // 获取当前余额
+                String moneyStr = redisCache.getCacheObject(companyMoneyKey);
+                if (StringUtils.isNotEmpty(moneyStr)) {
+                    redisMoney = new BigDecimal(moneyStr);
+                } else {
+                    redisMoney = company.getMoney();
+                }
+
+                newMoney = redisMoney.add(rechargeMoney);
+
+                redisCache.setCacheObject(companyMoneyKey, newMoney.toString());
+            }
+            return R.ok().put("newMoney", newMoney);
+        } catch (Exception e) {
+            log.error("公司充值-审核-同步更新到缓存,参数错误,请求异常,异常信息:{}", e.getMessage(), e);
+            return R.error("审核失败,请重试");
+        } finally {
+            if (lockAcquired && lock.isHeldByCurrentThread()) {
+                try {
+                    lock.unlock();
+                } catch (IllegalMonitorStateException e) {
+                    log.warn("尝试释放非当前线程持有的锁: companyId:{}", company.getCompanyId());
+                }
+            }
+        }
+    }
+
     @Override
     public List<CompanyRechargeVO> selectCompanyRechargeListVO(CompanyRechargeVO companyRecharge) {
         return companyRechargeMapper.selectCompanyRechargeListVO(companyRecharge);

+ 5 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java

@@ -1354,4 +1354,9 @@ public class CompanyServiceImpl implements ICompanyService
                 }));
     }
 
+    @Override
+    public void batchUpdateCompany(List<Company> list) {
+        companyMapper.batchUpdateCompany(list);
+    }
+
 }

+ 7 - 0
fs-service/src/main/resources/mapper/company/CompanyMapper.xml

@@ -266,4 +266,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <select id="selectCompanyMoneyAllList" resultType="com.fs.company.domain.Company">
         select company_id,company_name,money from company where is_del= 0
     </select>
+
+    <update id="batchUpdateCompany" parameterType="java.util.List">
+        <foreach collection="list" item="company" separator=";">
+            update company set money = #{company.money}
+            WHERE company_id = #{company.companyId}
+        </foreach>
+    </update>
 </mapper>