1
zj
2025-09-20 02d6a517f7d4dac2d5271cefe421a628d838414b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package com.ruoyi.im.service.impl;
 
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.MedicalInsuranceAccountService;
import com.ruoyi.system.domain.MedicalInsuranceAccount;
import com.ruoyi.system.domain.MedicalInsuranceDailyClaim;
import com.ruoyi.system.mapper.MedicalInsuranceAccountMapper;
import com.ruoyi.system.mapper.MedicalInsuranceDailyClaimMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
 
import javax.xml.crypto.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
 
@Service
public class MedicalInsuranceAccountServiceImpl extends ServiceImpl<MedicalInsuranceAccountMapper, MedicalInsuranceAccount> implements MedicalInsuranceAccountService {
 
    @Autowired
    private MedicalInsuranceAccountMapper medicalInsuranceAccountMapper;
 
    @Autowired
    private MedicalInsuranceDailyClaimMapper medicalInsuranceDailyClaimMapper;
 
    // 使用本地锁避免数据库锁竞争
    private final ConcurrentHashMap<Integer, ReentrantLock> accountLocks = new ConcurrentHashMap<>();
 
    @Override
    public Result getInsuranceBenefit(Integer userId) {
        synchronized (userId) {
            // 获取账户本地锁
            ReentrantLock accountLock = accountLocks.computeIfAbsent(userId, k -> new ReentrantLock());
            accountLock.lock();
 
            try {
                MedicalInsuranceAccount account = medicalInsuranceAccountMapper.selectOne(new LambdaQueryWrapper<MedicalInsuranceAccount>()
                        .eq(MedicalInsuranceAccount::getUserId, userId)
                        .eq(MedicalInsuranceAccount::getAccountStatus, MedicalInsuranceAccount.AccountStatus.ACTIVE)
                );
                if (account == null) {
                    return Result.error("医保账户不存在");
                }
 
                // 检查账户状态
                if (!"ACTIVE".equals(account.getAccountStatus().name())) {
                    return Result.error("保单已失效");
                }
 
                // 检查账户有效期
                LocalDate now = LocalDate.now();
                if (now.isBefore(account.getEffectiveDate()) ||
                        now.isAfter(account.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::getClaimDate, today)
                );
                if (todayCount > 0) {
                    return Result.error("今日已领取过金额");
                }
 
                if (account.getTotalQuota().compareTo(account.getAlreadyReceived()) == 0) {
                    return Result.error("你的保险额度已领取完!");
                }
 
                // 计算剩余天数
                long remainingDays = ChronoUnit.DAYS.between(now, account.getExpiryDate()) + 1;
 
                // 计算每日金额
                BigDecimal dailyAmount = calculateDailyAmount(
                        account.getTotalQuota(), (int) remainingDays);
 
                // 如果账户余额不足,则领取剩余全部金额
                if (account.getAmountClaimed().compareTo(dailyAmount) < 0) {
                    dailyAmount = account.getAmountClaimed();
                }
 
                // 更新待领金额
                account.setAmountClaimed(account.getAmountClaimed().subtract(dailyAmount));
 
                // 更新已领取金额
                BigDecimal currentAmountReceived = account.getAlreadyReceived() != null ?
                        account.getAlreadyReceived(): BigDecimal.ZERO;
                account.setAlreadyReceived(currentAmountReceived.add(dailyAmount));
 
                medicalInsuranceAccountMapper.updateById(account);
 
                // 创建领取记录
                MedicalInsuranceDailyClaim claim = new MedicalInsuranceDailyClaim();
                claim.setAccountId(account.getId());
                claim.setUserId(userId);
                claim.setClaimDate(today);
                claim.setClaimAmount(dailyAmount);
                claim.setCreatedAt(new Date());
                medicalInsuranceDailyClaimMapper.insert(claim);
                return Result.success("领取成功");
            }catch (Exception e){
                e.printStackTrace();
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                log.error("领取保险金出错:"+e.getMessage());
            }finally {
                accountLock.unlock();
            }
        }
        return Result.error("领取失败!");
    }
 
    @Override
    public Result getReceiveStatus(Integer userId) {
        MedicalInsuranceAccount account = medicalInsuranceAccountMapper.selectOne(new LambdaQueryWrapper<MedicalInsuranceAccount>()
                .eq(MedicalInsuranceAccount::getUserId, userId)
                .eq(MedicalInsuranceAccount::getAccountStatus, MedicalInsuranceAccount.AccountStatus.ACTIVE)
        );
        if (account == null) {
            return Result.success(false);
        }
        // 检查账户状态
        if (!"ACTIVE".equals(account.getAccountStatus().name())) {
            return Result.success(false);
        }
        // 检查账户有效期
        LocalDate now = LocalDate.now();
        if (now.isBefore(account.getEffectiveDate()) ||
                now.isAfter(account.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::getClaimDate, today)
        );
        if (todayCount > 0) {
            return Result.success(false);
        }
        return Result.success(true);
    }
 
 
    private BigDecimal calculateDailyAmount(BigDecimal totalAmount, int totalDays) {
        if (totalDays <= 0) {
            return BigDecimal.ZERO;
        }
        // 使用四舍五入到分位
        return totalAmount.divide(new BigDecimal(totalDays), 2, BigDecimal.ROUND_DOWN);
    }
}