1
zj
2025-09-25 6287f5366df7a0f13fef767ce7d6505979a13b22
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
162
163
164
165
166
167
168
169
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.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;
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<String, ReentrantLock> accountLocks = new ConcurrentHashMap<>();
 
    @Autowired
    UserAccountService userAccountService;
 
    @Override
    public Result getInsuranceBenefit(String account) {
        synchronized (account) {
            // 获取账户本地锁
            ReentrantLock accountLock = accountLocks.computeIfAbsent(account, k -> new ReentrantLock());
            accountLock.lock();
            UserAccount userAccount = userAccountService.getOne(new LambdaQueryWrapper<UserAccount>()
                    .eq(UserAccount::getAccount,account));
            try {
                MedicalInsuranceAccount medicalInsuranceAccount = medicalInsuranceAccountMapper.selectOne(new LambdaQueryWrapper<MedicalInsuranceAccount>()
                        .eq(MedicalInsuranceAccount::getUserId, userAccount.getId())
                        .eq(MedicalInsuranceAccount::getAccountStatus, MedicalInsuranceAccount.AccountStatus.ACTIVE)
                );
                if (medicalInsuranceAccount == null) {
                    return Result.error("医保账户不存在");
                }
 
                // 检查账户状态
                if (!"ACTIVE".equals(medicalInsuranceAccount.getAccountStatus().name())) {
                    return Result.error("保单已失效");
                }
 
                // 检查账户有效期
                LocalDate now = LocalDate.now();
                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, userAccount.getId())
                        .eq(MedicalInsuranceDailyClaim::getAccountId, medicalInsuranceAccount.getId())
                        .eq(MedicalInsuranceDailyClaim::getClaimDate, today)
                );
                if (todayCount > 0) {
                    return Result.error("今日已领取过金额");
                }
 
                if (medicalInsuranceAccount.getTotalQuota().compareTo(medicalInsuranceAccount.getAlreadyReceived()) == 0) {
                    return Result.error("你的保险额度已领取完!");
                }
 
                // 计算剩余天数
                long remainingDays = ChronoUnit.DAYS.between(now, medicalInsuranceAccount.getExpiryDate()) + 1;
 
                // 计算每日金额
                BigDecimal dailyAmount = calculateDailyAmount(
                        medicalInsuranceAccount.getTotalQuota(), (int) remainingDays);
 
                // 如果账户余额不足,则领取剩余全部金额
                if (medicalInsuranceAccount.getAmountClaimed().compareTo(dailyAmount) < 0) {
                    dailyAmount = medicalInsuranceAccount.getAmountClaimed();
                }
 
                // 更新待领金额
                medicalInsuranceAccount.setAmountClaimed(medicalInsuranceAccount.getAmountClaimed().subtract(dailyAmount));
 
                // 更新已领取金额
                BigDecimal currentAmountReceived = medicalInsuranceAccount.getAlreadyReceived() != null ?
                        medicalInsuranceAccount.getAlreadyReceived(): BigDecimal.ZERO;
                medicalInsuranceAccount.setAlreadyReceived(currentAmountReceived.add(dailyAmount));
 
                medicalInsuranceAccountMapper.updateById(medicalInsuranceAccount);
 
                // 创建领取记录
                MedicalInsuranceDailyClaim claim = new MedicalInsuranceDailyClaim();
                claim.setAccountId(medicalInsuranceAccount.getId());
                claim.setUserId(userAccount.getId());
                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(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 (medicalInsuranceAccount == null) {
            return Result.success(false);
        }
        // 检查账户状态
        if (!"ACTIVE".equals(medicalInsuranceAccount.getAccountStatus().name())) {
            return Result.success(false);
        }
        // 检查账户有效期
        LocalDate now = LocalDate.now();
        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, userAccount.getId())
                .eq(MedicalInsuranceDailyClaim::getAccountId, medicalInsuranceAccount.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);
    }
}