Skip to content

保险业务技术详解

保险业务系统核心技术与架构


一、业务概述

保险业务是汇丰集团的重要组成部分,为个人和企业客户提供人寿保险、财产保险、健康保险等产品。作为开发人员,理解保险业务的技术特点对于开发保险系统至关重要。

1.1 业务线定位

保险业务主要服务以下客户群体:

  • 个人客户(人寿保险、健康保险)
  • 企业客户(团体保险、财产险)
  • 高净值客户(财富传承保险)

1.2 产品体系

保险业务的核心产品线包括:

产品类别典型产品技术复杂度
人寿保险终身寿险、定期寿险、年金险
健康保险医疗险、重疾险、护理险
财产保险车险、家财险、责任险
意外保险意外险、旅行险

二、核心业务功能

2.1 保单管理

保单是保险业务的核心对象,贯穿整个保险生命周期。

2.1.1 保单数据模型

java
/**
 * 保单
 */
@Entity
@Table(name = "insurance_policy")
public class InsurancePolicy {

    @Id
    @Column(name = "policy_id")
    private String policyId;

    @Column(name = "policy_number")
    private String policyNumber;  // 保单号

    @Column(name = "product_id")
    private String productId;  // 产品ID

    @Column(name = "product_type")
    private ProductType productType;  // 人寿/健康/财产

    @Column(name = "policy_holder_id")
    private String policyHolderId;  // 投保人

    @Column(name = "insured_id")
    private String insuredId;  // 被保人

    @Column(name = "beneficiary_id")
    private String beneficiaryId;  // 受益人

    @Column(name = "premium")
    private BigDecimal premium;  // 保费

    @Column(name = "sum_insured")
    private BigDecimal sumInsured;  // 保额

    @Column(name = "policy_term")
    private Integer policyTerm;  // 保险期间(年)

    @Column(name = "payment_term")
    private Integer paymentTerm;  // 缴费期间(年)

    @Column(name = "payment_frequency")
    private PaymentFrequency paymentFrequency;  // 缴费频率

    @Column(name = "issue_date")
    private LocalDate issueDate;  // 签发日期

    @Column(name = "effective_date")
    private LocalDate effectiveDate;  // 生效日期

    @Column(name = "maturity_date")
    private LocalDate maturityDate;  // 满期日期

    @Column(name = "status")
    private PolicyStatus status;  // 生效/失效/终止

    @Column(name = "cash_value")
    private BigDecimal cashValue;  // 现金价值
}

2.1.2 保单生命周期

┌─────────┐     ┌─────────┐     ┌─────────┐     ┌─────────┐     ┌─────────┐
│  投保   │ ──→ │  核保   │ ──→ │  承保   │ ──→ │  保全   │ ──→ │  理赔   │
└─────────┘     └─────────┘     └─────────┘     └─────────┘     └─────────┘
                    │                                                     │
                    ▼                                                     ▼
              ┌─────────┐                                           ┌─────────┐
              │  拒保   │                                           │  结案   │
              └─────────┘                                           └─────────┘

2.1.3 承保流程

java
/**
 * 承保服务
 */
@Service
public class UnderwritingService {

    /**
     * 提交投保申请
     */
    @Transactional
    public PolicyApplication submitApplication(PolicyApplicationRequest request) {
        // 1. 校验产品
        InsuranceProduct product = productRepository.findById(request.getProductId())
            .orElseThrow(() -> new ProductNotFoundException());

        // 2. 校验被保人信息
        validateInsuredInfo(request.getInsuredInfo());

        // 3. 计算保费
        BigDecimal premium = premiumCalculator.calculate(
            product,
            request.getSumInsured(),
            request.getPolicyTerm(),
            request.getInsuredInfo()
        );

        // 4. 创建投保申请
        PolicyApplication application = PolicyApplication.builder()
            .applicationNumber(generateApplicationNumber())
            .productId(product.getId())
            .policyHolderId(request.getPolicyHolderId())
            .insuredInfo(request.getInsuredInfo())
            .sumInsured(request.getSumInsured())
            .policyTerm(request.getPolicyTerm())
            .premium(premium)
            .status(ApplicationStatus.SUBMITTED)
            .submitTime(LocalDateTime.now())
            .build();

        applicationRepository.save(application);

        // 5. 触发核保流程
        underwritingWorkflow.startUnderwriting(application);

        return application;
    }

    /**
     * 核保
     */
    @Transactional
    public UnderwritingResult underwrite(String applicationId, UnderwritingRequest request) {
        PolicyApplication application = applicationRepository.findById(applicationId)
            .orElseThrow();

        // 1. 获取核保规则
        List<UnderwritingRule> rules = getApplicableRules(application);

        // 2. 逐条规则检查
        List<UnderwritingDecision> decisions = new ArrayList<>();
        for (UnderwritingRule rule : rules) {
            UnderwritingDecision decision = evaluateRule(rule, application);
            decisions.add(decision);

            if (decision.getResult() == DecisionResult.DECLINE) {
                // 直接拒保
                application.setStatus(ApplicationStatus.DECLINED);
                application.setUnderwritingResult("拒保:" + decision.getReason());
                applicationRepository.save(application);
                return UnderwritingResult.declined(decisions);
            }
        }

        // 3. 综合评定
        UnderwritingResult result = comprehensiveAssessment(decisions);

        // 4. 更新申请状态
        application.setStatus(mapToApplicationStatus(result.getOverallResult()));
        application.setUnderwritingResult(result.getOverallResult().name());
        application.setUnderwritingGrade(result.getGrade());
        application.setUnderwritingTime(LocalDateTime.now());
        applicationRepository.save(application);

        // 5. 如果标准承保,直接生成保单
        if (result.getOverallResult() == UnderwritingResultEnum.STANDARD) {
            issuePolicy(application);
        }

        return result;
    }

    /**
     * 签发保单
     */
    @Transactional
    public InsurancePolicy issuePolicy(PolicyApplication application) {
        // 1. 创建保单
        InsurancePolicy policy = InsurancePolicy.builder()
            .policyNumber(generatePolicyNumber())
            .productId(application.getProductId())
            .policyHolderId(application.getPolicyHolderId())
            .insuredId(application.getInsuredInfo().getInsuredId())
            .beneficiaryId(application.getBeneficiaryId())
            .premium(application.getPremium())
            .sumInsured(application.getSumInsured())
            .policyTerm(application.getPolicyTerm())
            .paymentTerm(application.getPaymentTerm())
            .paymentFrequency(application.getPaymentFrequency())
            .issueDate(LocalDate.now())
            .effectiveDate(application.getEffectiveDate())
            .maturityDate(application.getEffectiveDate().plusYears(application.getPolicyTerm()))
            .status(PolicyStatus.IN_FORCE)
            .build();

        policyRepository.save(policy);

        // 2. 发送首期保费扣费指令
        paymentService.schedulePremiumCollection(policy);

        // 3. 更新申请状态
        application.setStatus(ApplicationStatus.ISSUED);
        application.setPolicyId(policy.getPolicyId());
        applicationRepository.save(application);

        // 4. 发送保单
        policyDeliveryService.deliverPolicy(policy);

        return policy;
    }
}

2.2 保全服务

保全是指保险合同生效后,根据投保人要求办理的合同变更业务。

2.2.1 保全业务类型

保全项目说明技术复杂度
缴费方式变更银行转账/现金/第三方
联系方式变更电话/地址/邮箱
受益人变更变更受益人
保额变更加保/减保
保单贷款保单现金价值贷款
退保解除保险合同

2.2.2 保全处理服务

java
/**
 * 保全服务
 */
@Service
public class PolicyServiceService {

    /**
     * 处理保全申请
     */
    @Transactional
    public ServiceResult processServiceRequest(ServiceRequest request) {
        // 1. 校验保单状态
        InsurancePolicy policy = policyRepository.findById(request.getPolicyId())
            .orElseThrow();

        if (policy.getStatus() != PolicyStatus.IN_FORCE) {
            throw new PolicyStatusException("保单状态异常");
        }

        // 2. 根据保全类型处理
        ServiceResult result;
        switch (request.getServiceType()) {
            case BENEFICIARY_CHANGE:
                result = processBeneficiaryChange(policy, request);
                break;

            case SUM_INSURED_CHANGE:
                result = processSumInsuredChange(policy, request);
                break;

            case POLICY_LOAN:
                result = processPolicyLoan(policy, request);
                break;

            case SURRENDER:
                result = processSurrender(policy, request);
                break;

            default:
                throw new UnsupportedServiceTypeException();
        }

        // 3. 记录保全日志
        serviceLogRepository.save(ServiceLog.builder()
            .policyId(policy.getPolicyId())
            .serviceType(request.getServiceType())
            .requestId(request.getRequestId())
            .result(result)
            .build());

        return result;
    }

    /**
     * 保单贷款
     */
    @Transactional
    public PolicyLoanResult processPolicyLoan(
            InsurancePolicy policy,
            ServiceRequest request) {

        // 1. 计算可贷款金额(现金价值的80%-90%)
        BigDecimal maxLoanAmount = policy.getCashValue()
            .multiply(new BigDecimal("0.9"));

        BigDecimal requestAmount = request.getParameters().getAmount();
        if (requestAmount.compareTo(maxLoanAmount) > 0) {
            throw new AmountExceededException("超过可贷款金额");
        }

        // 2. 计算贷款利息
        BigDecimal interestRate = loanRateService.getCurrentRate(LoanType.POLICY_LOAN);
        BigDecimal interest = calculateInterest(
            requestAmount,
            interestRate,
            request.getParameters().getLoanTerm()
        );

        // 3. 创建贷款记录
        PolicyLoan loan = PolicyLoan.builder()
            .loanId(generateLoanId())
            .policyId(policy.getPolicyId())
            .loanAmount(requestAmount)
            .interestRate(interestRate)
            .interest(interest)
            .totalRepayment(requestAmount.add(interest))
            .loanTerm(request.getParameters().getLoanTerm())
            .status(LoanStatus.APPROVED)
            .loanDate(LocalDate.now())
            .build();

        loanRepository.save(loan);

        // 4. 更新保单现金价值(扣除贷款金额)
        policy.setCashValue(policy.getCashValue().subtract(requestAmount));
        policyRepository.save(policy);

        // 5. 发放贷款
        loanDisbursementService.disburse(policy.getPolicyHolderId(), requestAmount);

        return PolicyLoanResult.success(loan);
    }

    /**
     * 退保处理
     */
    @Transactional
    public SurrenderResult processSurrender(
            InsurancePolicy policy,
            ServiceRequest request) {

        // 1. 计算退保金
        BigDecimal surrenderValue = calculateSurrenderValue(policy);

        // 2. 扣除退保费用
        BigDecimal surrenderFee = calculateSurrenderFee(policy);
        BigDecimal netSurrenderValue = surrenderValue.subtract(surrenderFee);

        // 3. 更新保单状态
        policy.setStatus(PolicyStatus.SURRENDERED);
        policy.setSurrenderDate(LocalDate.now());
        policy.setSurrenderValue(netSurrenderValue);
        policyRepository.save(policy);

        // 4. 支付退保金
        paymentService.disburse(policy.getPolicyHolderId(), netSurrenderValue);

        // 5. 生成退保批单
        endorsementService.generateSurrenderEndorsement(policy);

        return SurrenderResult.builder()
            .surrenderValue(surrenderValue)
            .surrenderFee(surrenderFee)
            .netSurrenderValue(netSurrenderValue)
            .surrenderDate(LocalDate.now())
            .build();
    }
}

2.3 理赔服务

理赔是保险最重要的服务环节,直接影响客户体验和公司声誉。

2.3.1 理赔数据模型

java
/**
 * 理赔案件
 */
@Entity
@Table(name = "claim_case")
public class ClaimCase {

    @Id
    @Column(name = "case_id")
    private String caseId;

    @Column(name = "case_number")
    private String caseNumber;  // 案件号

    @Column(name = "policy_id")
    private String policyId;

    @Column(name = "claim_type")
    private ClaimType claimType;  // 身故/医疗/重疾/伤残

    @Column(name = "claim_amount")
    private BigDecimal claimAmount;  // 申请金额

    @Column(name = "approved_amount")
    private BigDecimal approvedAmount;  // 核定金额

    @Column(name = "incident_date")
    private LocalDate incidentDate;  // 出险日期

    @Column(name = "report_date")
    private LocalDate reportDate;  // 报案日期

    @Column(name = "status")
    private ClaimStatus status;  // 报案/调查/核定/支付/结案

    @Column(name = "investigator_id")
    private String investigatorId;  // 调查人员
}

2.3.2 理赔流程

java
/**
 * 理赔服务
 */
@Service
public class ClaimService {

    /**
     * 报案
     */
    @Transactional
    public ClaimCase report(ClaimReportRequest request) {
        // 1. 校验保单
        InsurancePolicy policy = policyRepository.findById(request.getPolicyId())
            .orElseThrow();

        if (policy.getStatus() != PolicyStatus.IN_FORCE) {
            throw new PolicyStatusException("保单不在有效状态");
        }

        // 2. 校验出险日期是否在保障期内
        if (request.getIncidentDate().isBefore(policy.getEffectiveDate()) ||
            request.getIncidentDate().isAfter(policy.getMaturityDate())) {
            throw new IncidentDateException("出险日期不在保障期内");
        }

        // 3. 检查等待期
        if (isWithinWaitingPeriod(policy, request.getIncidentDate())) {
            throw new WaitingPeriodException("等待期内出险");
        }

        // 4. 创建理赔案件
        ClaimCase case = ClaimCase.builder()
            .caseNumber(generateCaseNumber())
            .policyId(policy.getPolicyId())
            .claimType(request.getClaimType())
            .claimAmount(request.getClaimAmount())
            .incidentDate(request.getIncidentDate())
            .reportDate(LocalDate.now())
            .incidentDescription(request.getDescription())
            .status(ClaimStatus.REPORTED)
            .build();

        claimRepository.save(case);

        // 5. 发送报案确认
        notificationService.sendClaimReportConfirmation(case);

        return case;
    }

    /**
     * 理赔核定
     */
    @Transactional
    public ClaimApprovalResult approve(String caseId, ClaimApprovalRequest request) {
        ClaimCase case = claimRepository.findByCaseId(caseId)
            .orElseThrow();

        // 1. 案件调查
        InvestigationResult investigation = investigate(case);

        // 2. 计算理赔金额
        BigDecimal approvedAmount = calculateClaimAmount(
            case.getPolicyId(),
            case.getClaimType(),
            case.getClaimAmount(),
            investigation
        );

        // 3. 更新案件状态
        case.setApprovedAmount(approvedAmount);
        case.setStatus(ClaimStatus.APPROVED);
        case.setApprovalDate(LocalDate.now());
        case.setApprovalRemark(request.getRemark());

        claimRepository.save(case);

        // 4. 支付理赔金
        paymentService.disburseClaim(case);

        return ClaimApprovalResult.builder()
            .caseId(caseId)
            .claimAmount(case.getClaimAmount())
            .approvedAmount(approvedAmount)
            .deductionAmount(case.getClaimAmount().subtract(approvedAmount))
            .deductionReason(request.getDeductionReason())
            .build();
    }

    /**
     * 计算理赔金额
     */
    private BigDecimal calculateClaimAmount(
            String policyId,
            ClaimType claimType,
            BigDecimal claimAmount,
            InvestigationResult investigation) {

        InsurancePolicy policy = policyRepository.findById(policyId).orElseThrow();

        BigDecimal approvedAmount = claimAmount;

        // 1. 检查保额
        if (approvedAmount.compareTo(policy.getSumInsured()) > 0) {
            approvedAmount = policy.getSumInsured();
        }

        // 2. 扣除已赔付金额(多次理赔)
        BigDecimal alreadyPaid = claimRepository.sumApprovedAmountByPolicyId(policyId);
        approvedAmount = approvedAmount.subtract(alreadyPaid);

        // 3. 根据调查结论调整
        if (investigation.hasExclusions()) {
            for (Exclusion exclusion : investigation.getExclusions()) {
                approvedAmount = approvedAmount.subtract(exclusion.getAmount());
            }
        }

        return approvedAmount.max(BigDecimal.ZERO);
    }
}

三、数据流转

3.1 保险业务数据流

┌──────────┐     ┌────────────┐     ┌───────────┐     ┌──────────┐
│  投保    │ ──→ │  核保承保  │ ──→ │  保全变更 │ ──→ │  理赔   │
│          │     │            │     │           │     │          │
└──────────┘     └────────────┘     └───────────┘     └──────────┘
      │                                         │
      ▼                                         ▼
┌──────────────┐                         ┌─────────────┐
│  保费计算   │                         │  现金价值计算│
│  精算系统   │                         │  精算系统   │
└──────────────┘                         └─────────────┘

┌──────────────────────────────────────────────────────┐
│                    财务系统                           │
│  保费收入 │  理赔支出 │  投资收益 │  责任准备金       │
└──────────────────────────────────────────────────────┘

3.2 与精算系统交互

java
/**
 * 精算系统交互服务
 */
@Service
public class ActuarialService {

    /**
     * 请求精算计算
     */
    public ActuarialResult requestCalculation(ActuarialCalculationRequest request) {
        // 1. 构建请求
        ActuarialRequest actuarialRequest = ActuarialRequest.builder()
            .productId(request.getProductId())
            .calculationType(request.getType())  // PREMIUM, CASH_VALUE, RESERVE
            .parameters(request.getParameters())
            .build();

        // 2. 调用精算系统
        String result = actuarialClient.calculate(actuarialRequest);

        // 3. 解析结果
        return actuarialResultParser.parse(result, request.getType());
    }

    /**
     * 计算现金价值
     */
    public BigDecimal calculateCashValue(
            String policyId,
            LocalDate calculateDate) {

        InsurancePolicy policy = policyRepository.findById(policyId)
            .orElseThrow();

        // 调用精算系统计算
        ActuarialResult result = requestCalculation(
            ActuarialCalculationRequest.builder()
                .productId(policy.getProductId())
                .type(CalculationType.CASH_VALUE)
                .parameters(Map.of(
                    "policyDate", calculateDate,
                    "premium", policy.getPremium(),
                    "policyTerm", policy.getPolicyTerm(),
                    "paymentTerm", policy.getPaymentTerm()
                ))
                .build()
        );

        return result.getCashValue();
    }

    /**
     * 计算责任准备金
     */
    public BigDecimal calculateReserve(String policyId, LocalDate calculateDate) {
        InsurancePolicy policy = policyRepository.findById(policyId)
            .orElseThrow();

        ActuarialResult result = requestCalculation(
            ActuarialCalculationRequest.builder()
                .productId(policy.getProductId())
                .type(CalculationType.RESERVE)
                .parameters(Map.of(
                    "calculateDate", calculateDate,
                    "policyYear", calculateYear(policy, calculateDate)
                ))
                .build()
        );

        return result.getReserve();
    }
}

四、技术架构与技术选型

4.1 保险系统技术特点

维度特点技术选型
业务流程复杂的状态机流程引擎
精算计算复杂的数学模型专用精算系统
数据量保单数据量大分库分表
监管要求报表要求严格报表平台

4.2 业务流程引擎

java
/**
 * 保险业务流程服务
 */
@Service
public class InsuranceWorkflowService {

    @Autowired
    private FlowEngine flowEngine;

    /**
     * 启动核保流程
     */
    public void startUnderwriting(PolicyApplication application) {
        // 构建流程变量
        Map<String, Object> variables = new HashMap<>();
        variables.put("applicationId", application.getId());
        variables.put("productType", application.getProductType());
        variables.put("sumInsured", application.getSumInsured());
        variables.put("premium", application.getPremium());

        // 启动流程
        flowEngine.startProcess("underwriting", application.getId(), variables);
    }

    /**
     * 核保任务处理
     */
    public void processUnderwritingTask(String taskId, UnderwritingDecision decision) {
        Map<String, Object> variables = new HashMap<>();
        variables.put("decision", decision.getResult().name());
        variables.put("reason", decision.getReason());
        variables.put("grade", decision.getGrade());

        flowEngine.completeTask(taskId, variables);
    }

    /**
     * 理赔流程处理
     */
    public void startClaimProcess(ClaimCase claimCase) {
        Map<String, Object> variables = new HashMap<>();
        variables.put("caseId", claimCase.getCaseId());
        variables.put("claimType", claimCase.getClaimType());
        variables.put("claimAmount", claimCase.getClaimAmount());

        // 根据理赔类型启动不同流程
        String processKey = "claim_" + claimCase.getClaimType().name().toLowerCase();
        flowEngine.startProcess(processKey, claimCase.getCaseId(), variables);
    }
}

五、典型开发场景

5.1 续期保费扣费

java
/**
 * 续期保费服务
 */
@Service
public class RenewalPremiumService {

    /**
     * 续期保费批扣
     */
    @Scheduled(cron = "0 0 5 * * ?")  // 每天凌晨5点
    @Transactional
    public void processRenewalCollection() {
        // 1. 获取今日应扣保单
        List<PolicyToRenew> policies = getPoliciesDueForRenewal(LocalDate.now());

        int successCount = 0;
        int failCount = 0;

        for (PolicyToRenew policy : policies) {
            try {
                // 2. 执行扣费
                boolean success = premiumCollectionService.collect(
                    policy.getPolicyId(),
                    policy.getRenewalAmount()
                );

                if (success) {
                    // 3. 更新保单状态
                    updatePolicyStatus(policy.getPolicyId());
                    successCount++;
                } else {
                    failCount++;
                    // 发送催收通知
                    sendCollectionNotice(policy);
                }

            } catch (Exception e) {
                log.error("续期扣费失败 policyId={}", policy.getPolicyId(), e);
                failCount++;
            }
        }

        log.info("续期批扣完成 成功={} 失败={}", successCount, failCount);
    }

    /**
     * 获取应续期保单
     */
    private List<PolicyToRenew> getPoliciesDueForRenewal(LocalDate date) {
        return policyRepository.findPoliciesForRenewal(date);
    }
}

5.2 保费计算

java
/**
 * 保费计算服务
 */
@Service
public class PremiumCalculationService {

    /**
     * 计算保费
     */
    public BigDecimal calculatePremium(
            InsuranceProduct product,
            BigDecimal sumInsured,
            Integer term,
            InsuredInfo insuredInfo) {

        // 1. 获取精算定价公式
        ActuarialPricing pricing = product.getActuarialPricing();

        // 2. 计算基础保费
        BigDecimal basePremium = calculateBasePremium(
            pricing,
            sumInsured,
            term,
            insuredInfo
        );

        // 3. 应用风险系数
        BigDecimal riskFactor = calculateRiskFactor(insuredInfo);
        BigDecimal adjustedPremium = basePremium.multiply(riskFactor);

        // 4. 应用折扣
        BigDecimal discount = calculateDiscount(product, insuredInfo);
        BigDecimal finalPremium = adjustedPremium.multiply(discount);

        return finalPremium.setScale(2, RoundingMode.HALF_UP);
    }

    /**
     * 计算风险系数
     */
    private BigDecimal calculateRiskFactor(InsuredInfo info) {
        BigDecimal factor = BigDecimal.ONE;

        // 年龄系数
        factor = factor.multiply(getAgeFactor(info.getAge()));

        // 职业系数
        factor = factor.multiply(getOccupationFactor(info.getOccupationCode()));

        // 健康状况系数
        if (info.hasHealthIssues()) {
            factor = factor.multiply(new BigDecimal("1.5"));
        }

        return factor;
    }
}

六、业务难点与解决方案

6.1 现金价值计算

问题:分红险的现金价值计算复杂

解决方案:封装精算系统调用

java
/**
 * 现金价值计算服务
 */
@Service
public class CashValueService {

    /**
     * 计算保单现金价值
     */
    public BigDecimal calculateCashValue(String policyId, LocalDate calculateDate) {
        InsurancePolicy policy = policyRepository.findById(policyId)
            .orElseThrow();

        // 调用精算系统
        ActuarialResult result = actuarialService.requestCalculation(
            ActuarialCalculationRequest.builder()
                .productId(policy.getProductId())
                .type(CalculationType.CASH_VALUE)
                .parameters(buildParameters(policy, calculateDate))
                .build()
        );

        // 更新保单现金价值
        policy.setCashValue(result.getCashValue());
        policyRepository.save(policy);

        return result.getCashValue();
    }

    /**
     * 计算退保金
     */
    public BigDecimal calculateSurrenderValue(String policyId, LocalDate surrenderDate) {
        BigDecimal cashValue = calculateCashValue(policyId, surrenderDate);

        // 计算退保费用
        InsurancePolicy policy = policyRepository.findById(policyId).orElseThrow();
        int policyYear = calculatePolicyYear(policy, surrenderDate);

        BigDecimal surrenderRate = getSurrenderRate(policy.getProductType(), policyYear);
        BigDecimal surrenderFee = cashValue.multiply(surrenderRate);

        return cashValue.subtract(surrenderFee);
    }
}

6.2 理赔欺诈检测

问题:理赔欺诈是保险业的重要风险

解决方案:规则引擎 + 机器学习

java
/**
 * 理赔欺诈检测服务
 */
@Service
public class ClaimFraudDetectionService {

    /**
     * 检测理赔欺诈
     */
    public FraudDetectionResult detectFraud(ClaimCase claimCase) {
        List<FraudRule> triggeredRules = new ArrayList<>();
        BigDecimal riskScore = BigDecimal.ZERO;

        // 1. 规则检测
        for (FraudRule rule : fraudRules) {
            if (rule.evaluate(claimCase)) {
                triggeredRules.add(rule);
                riskScore = riskScore.add(rule.getScore());
            }
        }

        // 2. 机器学习模型预测
        if (mlModel.isEnabled()) {
            BigDecimal mlScore = mlModel.predict(claimCase);
            riskScore = riskScore.multiply(new BigDecimal("0.7"))
                .add(mlScore.multiply(new BigDecimal("0.3")));
        }

        // 3. 判定结果
        FraudDetectionResult result = FraudDetectionResult.builder()
            .caseId(claimCase.getCaseId())
            .riskLevel(determineRiskLevel(riskScore))
            .riskScore(riskScore)
            .triggeredRules(triggeredRules)
            .recommendation(determineRecommendation(riskScore))
            .build();

        // 4. 高风险案件自动转调查
        if (result.getRiskLevel() == RiskLevel.HIGH) {
            claimCase.setStatus(ClaimStatus.INVESTIGATION_REQUIRED);
            claimRepository.save(claimCase);
        }

        return result;
    }

    /**
     * 欺诈规则
     */
    private boolean evaluateRule(FraudRule rule, ClaimCase claimCase) {
        switch (rule.getRuleType()) {
            case FREQUENT_CLAIM:
                return isFrequentClaim(claimCase);

            case LATE_REPORT:
                return isLateReport(claimCase);

            case SUSPICIOUS_AMOUNT:
                return isSuspiciousAmount(claimCase);

            case MULTIPLE_POLICIES:
                return hasMultiplePolicies(claimCase);

            default:
                return false;
        }
    }
}

七、合规要求

7.1 监管报表

保险公司需要定期向监管机构报送各类报表:

java
/**
 * 监管报表服务
 */
@Service
public class RegulatoryReportService {

    /**
     * 生成偿付能力报告
     */
    public SolvencyReport generateSolvencyReport(LocalDate reportDate) {
        // 1. 计算认可资产
        BigDecimal admittedAssets = calculateAdmittedAssets(reportDate);

        // 2. 计算认可负债
        BigDecimal admittedLiabilities = calculateAdmittedLiabilities(reportDate);

        // 3. 计算实际资本
        BigDecimal actualCapital = admittedAssets.subtract(admittedLiabilities);

        // 4. 计算最低资本
        BigDecimal minimumCapital = calculateMinimumCapital(reportDate);

        // 5. 计算偿付能力充足率
        BigDecimal solvencyRatio = actualCapital.divide(
            minimumCapital, 4, RoundingMode.HALF_UP);

        return SolvencyReport.builder()
            .reportDate(reportDate)
            .admittedAssets(admittedAssets)
            .admittedLiabilities(admittedLiabilities)
            .actualCapital(actualCapital)
            .minimumCapital(minimumCapital)
            .solvencyRatio(solvencyRatio)
            .build();
    }

    /**
     * 生成保费收入报告
     */
    public PremiumIncomeReport generatePremiumIncomeReport(int year, int quarter) {
        List<Policy> policies = policyRepository
            .findByIssueDateBetween(
                quarterStartDate(year, quarter),
                quarterEndDate(year, quarter)
            );

        BigDecimal firstYearPremium = policies.stream()
            .filter(p -> p.getPolicyYear() == 1)
            .map(Policy::getPremium)
            .reduce(BigDecimal.ZERO, BigDecimal::add);

        BigDecimal renewalPremium = policies.stream()
            .filter(p -> p.getPolicyYear() > 1)
            .map(Policy::getPremium)
            .reduce(BigDecimal.ZERO, BigDecimal::add);

        return PremiumIncomeReport.builder()
            .year(year)
            .quarter(quarter)
            .firstYearPremium(firstYearPremium)
            .renewalPremium(renewalPremium)
            .totalPremium(firstYearPremium.add(renewalPremium))
            .build();
    }
}

7.2 双录要求

java
/**
 * 双录(录音录像)服务
 */
@Service
public class RecordingService {

    /**
     * 开始双录
     */
    public RecordingSession startRecording(String applicationId, String channel) {
        // 1. 创建录音录像任务
        RecordingSession session = RecordingSession.builder()
            .sessionId(generateSessionId())
            .applicationId(applicationId)
            .channel(channel)
            .status(RecordingStatus.STARTED)
            .startTime(LocalDateTime.now())
            .build();

        recordingRepository.save(session);

        // 2. 启动录音录像设备
        recordingDevice.start(session.getSessionId());

        return session;
    }

    /**
     * 结束双录
     */
    @Transactional
    public void endRecording(String sessionId) {
        RecordingSession session = recordingRepository.findBySessionId(sessionId)
            .orElseThrow();

        // 1. 停止录音录像
        recordingDevice.stop(sessionId);

        // 2. 获取录制文件
        List<String> files = recordingDevice.getFiles(sessionId);

        // 3. 保存文件信息
        session.setVideoFile(files.stream()
            .filter(f -> f.endsWith(".mp4"))
            .findFirst()
            .orElse(null));

        session.setAudioFile(files.stream()
            .filter(f -> f.endsWith(".mp3"))
            .findFirst()
            .orElse(null));

        session.setStatus(RecordingStatus.COMPLETED);
        session.setEndTime(LocalDateTime.now());
        recordingRepository.save(session);

        // 4. 上传至存储
        storageService.upload(session);
    }
}

八、常见问题与避坑指南

8.1 保单状态管理

:保单状态变更遗漏

java
// ✅ 正确做法:使用状态机
public enum PolicyStatus {
    PENDING,    // 待生效
    IN_FORCE,   // 生效中
    LAPSE,      // 失效
    SURRENDERED,// 退保
    CLAIMED,   // 理赔终止
    EXPIRED     // 满期终止
}

// 状态转换规则
public class PolicyStatusTransition {
    public static final Map<PolicyStatus, Set<PolicyStatus>> ALLOWED =
        Map.of(
            PolicyStatus.PENDING, Set.of(PolicyStatus.IN_FORCE, PolicyStatus.DECLINED),
            PolicyStatus.IN_FORCE, Set.of(PolicyStatus.LAPSE, PolicyStatus.SURRENDERED,
                PolicyStatus.CLAIMED, PolicyStatus.EXPIRED),
            // ...
        );
}

8.2 保费计算精度

:保费计算使用double

java
// ✅ 正确做法:始终使用BigDecimal
public BigDecimal calculatePremium(...) {
    BigDecimal baseRate = new BigDecimal("0.005");  // 使用String构造
    BigDecimal sumInsured = new BigDecimal("100000");
    return baseRate.multiply(sumInsured);  // 使用BigDecimal运算
}

九、总结

保险业务的技术特点:

  1. 业务流程复杂:核保、保全、理赔各有复杂的状态机
  2. 精算要求高:现金价值、责任准备金等需要精算系统支持
  3. 合规要求严格:监管报表、双录等要求
  4. 数据长期保存:保单数据需要长期甚至终身保存

开发建议:

  • 深入理解保险业务流程
  • 掌握状态机设计模式
  • 重视合规和审计要求
  • 注意保费计算精度

本文档持续更新中,如有疑问欢迎交流讨论。

> 学而时习之,不亦说乎?