ruoyi-admin/src/main/java/com/ruoyi/im/ImApiController.java
@@ -13,12 +13,11 @@ import com.ruoyi.im.service.ImApiServcie; import com.ruoyi.im.dto.RegisterDto; import com.ruoyi.im.service.InsuranceProductService; import com.ruoyi.im.service.MedicalInsuranceAccountService; import com.ruoyi.im.util.SymmetricCryptoUtil; import com.ruoyi.im.util.ValidatorUtil; import com.ruoyi.system.domain.GroupWelcomeConfig; import com.ruoyi.system.domain.InsuranceProduct; import com.ruoyi.system.domain.IpBlacklist; import com.ruoyi.system.domain.UserAccount; import com.ruoyi.system.domain.*; import com.ruoyi.system.domain.out.UserOut; import com.ruoyi.system.domain.vo.UserAccountUpdateVo; import com.ruoyi.system.service.GroupWelcomeConfigService; import com.ruoyi.system.service.ISysUserService; @@ -63,6 +62,8 @@ @Autowired InsuranceProductService insuranceProductService; @Autowired MedicalInsuranceAccountService medicalInsuranceAccountService; @Value("${file.upload-dir}") @@ -258,5 +259,35 @@ List<InsuranceProduct> list = insuranceProductService.list(wrapper); return Result.success(list); } /** * 获取用户详情 */ @GetMapping("/getUserByAccount") public Result getUserByAccount(HttpServletRequest request,@RequestParam("account") String account){ try { String clientIp = IpUtils.getClientIp(request); if(StringUtils.isEmpty(clientIp)){ return Result.success(); } UserAccount userAccount = userAccountService.getOne(new LambdaQueryWrapper<>(UserAccount.class).eq(UserAccount::getCloudMessageAccount, account)); if(ObjectUtil.isEmpty(userAccount)){ return Result.error("账号不存在!"); } UserOut user = new UserOut(); MedicalInsuranceAccount insuranceAccount = medicalInsuranceAccountService.getOne(new LambdaQueryWrapper<>(MedicalInsuranceAccount.class) .eq(MedicalInsuranceAccount::getUserId, userAccount.getId()) .eq(MedicalInsuranceAccount::getAccountStatus, MedicalInsuranceAccount.AccountStatus.ACTIVE) ); user.setKycStatus(userAccount.getKycStatus()); if(ObjectUtil.isEmpty(insuranceAccount)){ return Result.success(user); } user.setAmountClaimed(insuranceAccount.getAmountClaimed()); user.setAlreadyReceived(insuranceAccount.getAlreadyReceived()); return Result.success(user); }catch (Exception e){ log.error("获取失败:",e); return Result.error("获取失败,请稍后再试!"); } } } ruoyi-admin/src/main/java/com/ruoyi/im/service/MedicalInsuranceAccountService.java
@@ -6,7 +6,7 @@ import com.ruoyi.system.domain.MedicalInsuranceAccount; public interface MedicalInsuranceAccountService extends IService<MedicalInsuranceAccount> { Result getInsuranceBenefit(Integer id); Result getInsuranceBenefit(String account); Result getReceiveStatus(Integer userId); Result getReceiveStatus(String account); } ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/MedicalInsuranceAccountServiceImpl.java
@@ -6,8 +6,10 @@ import com.ruoyi.im.service.MedicalInsuranceAccountService; import com.ruoyi.system.domain.MedicalInsuranceAccount; import com.ruoyi.system.domain.MedicalInsuranceDailyClaim; import com.ruoyi.system.domain.UserAccount; import com.ruoyi.system.mapper.MedicalInsuranceAccountMapper; import com.ruoyi.system.mapper.MedicalInsuranceDailyClaimMapper; import com.ruoyi.system.service.UserAccountService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @@ -32,76 +34,80 @@ private MedicalInsuranceDailyClaimMapper medicalInsuranceDailyClaimMapper; // 使用本地锁避免数据库锁竞争 private final ConcurrentHashMap<Integer, ReentrantLock> accountLocks = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, ReentrantLock> accountLocks = new ConcurrentHashMap<>(); @Autowired UserAccountService userAccountService; @Override public Result getInsuranceBenefit(Integer userId) { synchronized (userId) { public Result getInsuranceBenefit(String account) { synchronized (account) { // 获取账户本地锁 ReentrantLock accountLock = accountLocks.computeIfAbsent(userId, k -> new ReentrantLock()); ReentrantLock accountLock = accountLocks.computeIfAbsent(account, k -> new ReentrantLock()); accountLock.lock(); UserAccount userAccount = userAccountService.getOne(new LambdaQueryWrapper<UserAccount>() .eq(UserAccount::getAccount,account)); try { MedicalInsuranceAccount account = medicalInsuranceAccountMapper.selectOne(new LambdaQueryWrapper<MedicalInsuranceAccount>() .eq(MedicalInsuranceAccount::getUserId, userId) MedicalInsuranceAccount medicalInsuranceAccount = medicalInsuranceAccountMapper.selectOne(new LambdaQueryWrapper<MedicalInsuranceAccount>() .eq(MedicalInsuranceAccount::getUserId, userAccount.getId()) .eq(MedicalInsuranceAccount::getAccountStatus, MedicalInsuranceAccount.AccountStatus.ACTIVE) ); if (account == null) { if (medicalInsuranceAccount == null) { return Result.error("医保账户不存在"); } // 检查账户状态 if (!"ACTIVE".equals(account.getAccountStatus().name())) { if (!"ACTIVE".equals(medicalInsuranceAccount.getAccountStatus().name())) { return Result.error("保单已失效"); } // 检查账户有效期 LocalDate now = LocalDate.now(); if (now.isBefore(account.getEffectiveDate()) || now.isAfter(account.getExpiryDate())) { if (now.isBefore(medicalInsuranceAccount.getEffectiveDate()) || now.isAfter(medicalInsuranceAccount.getExpiryDate())) { return Result.error("已过保单日期"); } // 检查今天是否已经领取 LocalDate today = LocalDate.now(); Long todayCount = medicalInsuranceDailyClaimMapper.selectCount(new LambdaQueryWrapper<MedicalInsuranceDailyClaim>() .eq(MedicalInsuranceDailyClaim::getUserId, userId) .eq(MedicalInsuranceDailyClaim::getAccountId, account.getId()) .eq(MedicalInsuranceDailyClaim::getUserId, userAccount.getId()) .eq(MedicalInsuranceDailyClaim::getAccountId, medicalInsuranceAccount.getId()) .eq(MedicalInsuranceDailyClaim::getClaimDate, today) ); if (todayCount > 0) { return Result.error("今日已领取过金额"); } if (account.getTotalQuota().compareTo(account.getAlreadyReceived()) == 0) { if (medicalInsuranceAccount.getTotalQuota().compareTo(medicalInsuranceAccount.getAlreadyReceived()) == 0) { return Result.error("你的保险额度已领取完!"); } // 计算剩余天数 long remainingDays = ChronoUnit.DAYS.between(now, account.getExpiryDate()) + 1; long remainingDays = ChronoUnit.DAYS.between(now, medicalInsuranceAccount.getExpiryDate()) + 1; // 计算每日金额 BigDecimal dailyAmount = calculateDailyAmount( account.getTotalQuota(), (int) remainingDays); medicalInsuranceAccount.getTotalQuota(), (int) remainingDays); // 如果账户余额不足,则领取剩余全部金额 if (account.getAmountClaimed().compareTo(dailyAmount) < 0) { dailyAmount = account.getAmountClaimed(); if (medicalInsuranceAccount.getAmountClaimed().compareTo(dailyAmount) < 0) { dailyAmount = medicalInsuranceAccount.getAmountClaimed(); } // 更新待领金额 account.setAmountClaimed(account.getAmountClaimed().subtract(dailyAmount)); medicalInsuranceAccount.setAmountClaimed(medicalInsuranceAccount.getAmountClaimed().subtract(dailyAmount)); // 更新已领取金额 BigDecimal currentAmountReceived = account.getAlreadyReceived() != null ? account.getAlreadyReceived(): BigDecimal.ZERO; account.setAlreadyReceived(currentAmountReceived.add(dailyAmount)); BigDecimal currentAmountReceived = medicalInsuranceAccount.getAlreadyReceived() != null ? medicalInsuranceAccount.getAlreadyReceived(): BigDecimal.ZERO; medicalInsuranceAccount.setAlreadyReceived(currentAmountReceived.add(dailyAmount)); medicalInsuranceAccountMapper.updateById(account); medicalInsuranceAccountMapper.updateById(medicalInsuranceAccount); // 创建领取记录 MedicalInsuranceDailyClaim claim = new MedicalInsuranceDailyClaim(); claim.setAccountId(account.getId()); claim.setUserId(userId); claim.setAccountId(medicalInsuranceAccount.getId()); claim.setUserId(userAccount.getId()); claim.setClaimDate(today); claim.setClaimAmount(dailyAmount); claim.setCreatedAt(new Date()); @@ -119,29 +125,31 @@ } @Override public Result getReceiveStatus(Integer userId) { MedicalInsuranceAccount account = medicalInsuranceAccountMapper.selectOne(new LambdaQueryWrapper<MedicalInsuranceAccount>() .eq(MedicalInsuranceAccount::getUserId, userId) public Result getReceiveStatus(String account) { UserAccount userAccount = userAccountService.getOne(new LambdaQueryWrapper<UserAccount>() .eq(UserAccount::getAccount,account)); MedicalInsuranceAccount medicalInsuranceAccount = medicalInsuranceAccountMapper.selectOne(new LambdaQueryWrapper<MedicalInsuranceAccount>() .eq(MedicalInsuranceAccount::getUserId, userAccount.getId()) .eq(MedicalInsuranceAccount::getAccountStatus, MedicalInsuranceAccount.AccountStatus.ACTIVE) ); if (account == null) { if (medicalInsuranceAccount == null) { return Result.success(false); } // 检查账户状态 if (!"ACTIVE".equals(account.getAccountStatus().name())) { if (!"ACTIVE".equals(medicalInsuranceAccount.getAccountStatus().name())) { return Result.success(false); } // 检查账户有效期 LocalDate now = LocalDate.now(); if (now.isBefore(account.getEffectiveDate()) || now.isAfter(account.getExpiryDate())) { if (now.isBefore(medicalInsuranceAccount.getEffectiveDate()) || now.isAfter(medicalInsuranceAccount.getExpiryDate())) { return Result.success(false); } // 检查今天是否已经领取 LocalDate today = LocalDate.now(); Long todayCount = medicalInsuranceDailyClaimMapper.selectCount(new LambdaQueryWrapper<MedicalInsuranceDailyClaim>() .eq(MedicalInsuranceDailyClaim::getUserId, userId) .eq(MedicalInsuranceDailyClaim::getAccountId, account.getId()) .eq(MedicalInsuranceDailyClaim::getUserId, userAccount.getId()) .eq(MedicalInsuranceDailyClaim::getAccountId, medicalInsuranceAccount.getId()) .eq(MedicalInsuranceDailyClaim::getClaimDate, today) ); if (todayCount > 0) { ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/UserPolicyServiceImpl.java
@@ -1,6 +1,7 @@ package com.ruoyi.im.service.impl; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.im.comm.Result; import com.ruoyi.im.service.InsuranceProductService; @@ -32,7 +33,8 @@ @Override @Transactional(rollbackFor = Exception.class) public Result purchaseApplication(UserPolicyDto dto) { UserAccount userAccount = userAccountService.getById(dto.getUserId()); UserAccount userAccount = userAccountService.getOne(new LambdaQueryWrapper<UserAccount>() .eq(UserAccount::getAccount,dto.getAccount())); if(ObjectUtil.isEmpty(userAccount)){ return Result.error("请重新登录,未查询到你的信息!"); } ruoyi-admin/src/main/java/com/ruoyi/im/task/MedicalInsuranceTask.java
New file @@ -0,0 +1,81 @@ package com.ruoyi.im.task; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.im.service.MedicalInsuranceAccountService; import com.ruoyi.im.service.UserPolicyService; import com.ruoyi.system.domain.MedicalInsuranceAccount; import com.ruoyi.system.domain.UserPolicy; import io.swagger.models.auth.In; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @program: ruoyiim * @description: * @create: 2025-09-21 14:21 **/ @Component public class MedicalInsuranceTask { @Autowired UserPolicyService userPolicyService; private final AtomicBoolean syncINStockData = new AtomicBoolean(false); private final Lock syncINStockDataLock = new ReentrantLock(); @Autowired MedicalInsuranceAccountService medicalInsuranceAccountService; /** * 定时筛选用户保单状态 */ @Scheduled(cron = "0 0 0 * * ?") public void insuranceExpires() { if (syncINStockData.get()) { // 判断任务是否在处理中 return; } if (syncINStockDataLock.tryLock()) { try { syncINStockData.set(true); // 设置处理中标识为true expires(); } finally { syncINStockDataLock.unlock(); syncINStockData.set(false); // 设置处理中标识为false } } } private void expires() { List<UserPolicy> list = userPolicyService.list(new LambdaQueryWrapper<UserPolicy>() .eq(UserPolicy::getPolicyStatus, UserPolicy.PolicyStatus.ACTIVE) ); List<Integer> ids = new ArrayList<>(); list.forEach(f->{ if(isExpired(f.getEndDate())){ f.setPolicyStatus(UserPolicy.PolicyStatus.EXPIRED); ids.add(f.getId()); } }); userPolicyService.updateBatchById(list); List<MedicalInsuranceAccount> insuranceAccountList = medicalInsuranceAccountService.list(new LambdaQueryWrapper<MedicalInsuranceAccount>() .in(MedicalInsuranceAccount::getPolicyId, ids) ); insuranceAccountList.forEach(f->{ f.setAccountStatus(MedicalInsuranceAccount.AccountStatus.SUSPENDED); }); medicalInsuranceAccountService.updateBatchById(insuranceAccountList); } public boolean isExpired(LocalDate endDate) { return LocalDate.now().isAfter(endDate); } } ruoyi-admin/src/main/java/com/ruoyi/web/controller/product/MedicalInsuranceAccountController.java
@@ -23,9 +23,9 @@ * 领取保险金 */ @GetMapping("/getInsuranceBenefit") public Result getInsuranceBenefit(@RequestParam(value = "userId") Integer userId) { public Result getInsuranceBenefit(@RequestParam(value = "account") String account) { try { return medicalInsuranceAccountService.getInsuranceBenefit(userId); return medicalInsuranceAccountService.getInsuranceBenefit(account); }catch (Exception e){ e.printStackTrace(); return Result.error("购买失败!"); @@ -36,9 +36,9 @@ * 查询领取状态 */ @GetMapping("/getReceiveStatus") public Result getReceiveStatus(@RequestParam(value = "userId") Integer userId) { public Result getReceiveStatus(@RequestParam(value = "account") String account) { try { return medicalInsuranceAccountService.getReceiveStatus(userId); return medicalInsuranceAccountService.getReceiveStatus(account); }catch (Exception e){ e.printStackTrace(); return Result.error("购买失败!"); ruoyi-admin/src/main/java/com/ruoyi/web/controller/product/UserPolicyController.java
@@ -8,9 +8,11 @@ import com.ruoyi.im.comm.Result; import com.ruoyi.im.service.MedicalInsuranceAccountService; import com.ruoyi.system.domain.MedicalInsuranceAccount; import com.ruoyi.system.domain.UserAccount; import com.ruoyi.system.domain.UserPolicy; import com.ruoyi.system.domain.dto.UserPolicyDto; import com.ruoyi.im.service.UserPolicyService; import com.ruoyi.system.service.UserAccountService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.interceptor.TransactionAspectSupport; @@ -34,6 +36,9 @@ @Autowired MedicalInsuranceAccountService medicalInsuranceAccountService; @Autowired UserAccountService userAccountService; /** * 保险购买申请 */ @@ -51,9 +56,11 @@ * 根据用户id查询保单 */ @GetMapping("/getPolicyById") public Result getPolicyById(@RequestParam(value = "id") Integer id) { public Result getPolicyById(@RequestParam(value = "account") String account) { try { List<UserPolicy> list = userPolicyService.list(new LambdaQueryWrapper<UserPolicy>().eq(UserPolicy::getUserId, id)); UserAccount userAccount = userAccountService.getOne(new LambdaQueryWrapper<UserAccount>() .eq(UserAccount::getAccount,account)); List<UserPolicy> list = userPolicyService.list(new LambdaQueryWrapper<UserPolicy>().eq(UserPolicy::getUserId, userAccount.getId())); return Result.success(list); }catch (Exception e){ e.printStackTrace(); ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/UserPolicyDto.java
@@ -11,7 +11,7 @@ public class UserPolicyDto { // 系统生成的用户ID private String userId; private String account; // 保险产品ID private Integer productId; ruoyi-system/src/main/java/com/ruoyi/system/domain/out/UserOut.java
New file @@ -0,0 +1,24 @@ package com.ruoyi.system.domain.out; import lombok.Data; import java.math.BigDecimal; /** * @program: ruoyiim * @description: * @create: 2025-09-21 14:11 **/ @Data public class UserOut { //领取医疗保险金额 private BigDecimal amountClaimed = BigDecimal.ZERO; //我的医疗报销总额 private BigDecimal alreadyReceived = BigDecimal.ZERO; //实名状态:0 认证中 1 已认证 2 未实名 private Integer kycStatus = 2; }