保险业务技术详解
保险业务系统核心技术与架构
一、业务概述
保险业务是汇丰集团的重要组成部分,为个人和企业客户提供人寿保险、财产保险、健康保险等产品。作为开发人员,理解保险业务的技术特点对于开发保险系统至关重要。
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运算
}九、总结
保险业务的技术特点:
- 业务流程复杂:核保、保全、理赔各有复杂的状态机
- 精算要求高:现金价值、责任准备金等需要精算系统支持
- 合规要求严格:监管报表、双录等要求
- 数据长期保存:保单数据需要长期甚至终身保存
开发建议:
- 深入理解保险业务流程
- 掌握状态机设计模式
- 重视合规和审计要求
- 注意保费计算精度
本文档持续更新中,如有疑问欢迎交流讨论。