策略模式:根据国家代码优化身份证信息提取
在处理不同国家的用户身份信息时,我们经常需要从身份证件的文本中提取关键数据,例如姓名和证件号码。以哈萨克斯坦和乌兹别克斯坦为例,它们的身份证格式和信息提取规则各不相同。本方案利用策略模式来优雅地处理这种多态行为。
核心接口定义
首先,我们定义一个抽象策略接口,它规定了所有具体策略必须实现的方法。每个策略负责识别特定国家的用户信息,并从文本列表中提取所需数据。
package com.example.ocr.strategy;
import com.example.ocr.model.Customer;
import com.example.ocr.enums.CountryEnum;
import java.util.List;
/**
* OCR 识别策略接口
*/
public interface OcrExtractionStrategy {
/**
* 根据国家代码识别客户信息
*
* @param textLines 文本行列表
* @param customer 待填充的客户对象
* @return 填充后的客户对象
*/
Customer extractInfo(List<String> textLines, Customer customer);
/**
* 获取该策略对应的国家代码
*
* @return 国家代码枚举
*/
CountryEnum getCountry();
}
具体策略实现
接下来,为每个国家的用户身份证设计具体的识别策略。
哈萨克斯坦 (KZ) 策略
此实现负责解析哈萨克斯坦身份证中的个人识别号(ИИН)和姓名信息。
package com.example.ocr.strategy.impl;
import com.example.ocr.model.Customer;
import com.example.ocr.strategy.OcrExtractionStrategy;
import com.example.ocr.enums.CountryEnum;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Component
public class KazakhstanOcrStrategy implements OcrExtractionStrategy {
private static final String IDENTITY_PATTERN = "ЖОНЛИИН\\s+(\\d+)";
private static final String NAME_PATTERN = "ТЕП / ФАМИЛИЯ\\R+(.*?)\\R+.*?АТЫ/ИМЯ\\R+(.*?)(\\R|$)";
@Override
public Customer extractInfo(List<String> textLines, Customer customer) {
if (textLines == null || textLines.isEmpty()) {
return customer;
}
String frontPageText = textLines.get(0);
// 提取身份证号 (ИИН)
Pattern idPattern = Pattern.compile(IDENTITY_PATTERN);
Matcher idMatcher = idPattern.matcher(frontPageText);
if (idMatcher.find()) {
String idNumber = idMatcher.group(1);
customer.setIdentityNumber(idNumber);
customer.setTaxId(idNumber); // 假设税务ID与身份证号相同
}
// 提取姓名
Pattern namePattern = Pattern.compile(NAME_PATTERN, Pattern.DOTALL);
Matcher nameMatcher = namePattern.matcher(frontPageText);
if (nameMatcher.find()) {
String surname = nameMatcher.group(1).trim();
String givenName = nameMatcher.group(2).trim();
String fullName = givenName + " " + surname;
customer.setFullName(fullName);
}
return customer;
}
@Override
public CountryEnum getCountry() {
return CountryEnum.KZ;
}
}
乌兹别克斯坦 (UZ) 策略
此实现用于解析乌兹别克斯坦身份证的姓名和个人识别号码。
package com.example.ocr.strategy.impl;
import com.example.ocr.model.Customer;
import com.example.ocr.strategy.OcrExtractionStrategy;
import com.example.ocr.enums.CountryEnum;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Component
public class UzbekistanOcrStrategy implements OcrExtractionStrategy {
private static final String NAME_PATTERN = "Familiyasi/Surname\\R+(\\w+)\\R+.*?Ismi/Given name\\(s\\)\\R+(\\w+)";
private static final String IDENTITY_PATTERN = "Shaxsiy raqami Personal number\\s+(\\d+)";
@Override
public Customer extractInfo(List<String> textLines, Customer customer) {
if (textLines == null || textLines.size() < 2) {
return customer;
}
String frontPageText = textLines.get(0);
String backPageText = textLines.get(1);
// 提取姓名
Pattern namePattern = Pattern.compile(NAME_PATTERN, Pattern.DOTALL);
Matcher nameMatcher = namePattern.matcher(frontPageText);
if (nameMatcher.find()) {
String surname = nameMatcher.group(1);
String givenName = nameMatcher.group(2);
String fullName = givenName + " " + surname;
customer.setFullName(fullName);
}
// 提取身份证号 (Personal number)
Pattern idPattern = Pattern.compile(IDENTITY_PATTERN);
Matcher idMatcher = idPattern.matcher(backPageText);
if (idMatcher.find()) {
String idNumber = idMatcher.group(1);
customer.setIdentityNumber(idNumber);
customer.setTaxId(idNumber); // 假设税务ID与身份证号相同
}
return customer;
}
@Override
public CountryEnum getCountry() {
return CountryEnum.UZ;
}
}
上下文类(环境类)
上下文类负责管理和查找适合特定国家代码的策略。它利用 Spring 的 `ApplicationContext` 自动发现并注册所有实现了 `OcrExtractionStrategy` 接口的 Bean。
package com.example.ocr.context;
import com.example.ocr.strategy.OcrExtractionStrategy;
import com.example.ocr.enums.CountryEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON; // 假设使用 Fastjson 序列化日志
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
@Slf4j
public class OcrExtractionContext implements ApplicationContextAware {
// 使用 ConcurrentHashMap 存储策略,线程安全
private static final ConcurrentHashMap strategyMap = new ConcurrentHashMap<>();
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// 从 Spring 上下文中获取所有 OcrExtractionStrategy 类型的 Bean
Map beansOfType = applicationContext.getBeansOfType(OcrExtractionStrategy.class);
beansOfType.forEach((key, strategy) -> {
// 将策略按国家代码存入 map
strategyMap.put(strategy.getCountry(), strategy);
log.info("Registered OCR extraction strategy for country {}: {}", strategy.getCountry(), strategy.getClass().getName());
});
log.info("OCR extraction strategy context initialized. Total strategies: {}", strategyMap.size());
log.debug("Initialized strategies: {}", JSON.toJSONString(strategyMap.keySet()));
}
/**
* 根据国家代码获取对应的 OCR 识别策略
*
* @param countryCode 国家代码枚举
* @return 对应的 OCR 识别策略,如果不存在则返回 null
*/
public static OcrExtractionStrategy getStrategy(CountryEnum countryCode) {
return strategyMap.get(countryCode);
}
}
国家代码枚举
定义一个简单的枚举类来表示不同的国家代码,例如 Kazakhstan (KZ) 和 Uzbekistan (UZ)。
package com.example.ocr.enums;
/**
* 国家代码枚举
*/
public enum CountryEnum {
KZ, // 哈萨克斯坦
UZ // 乌兹别克斯坦
// 可以根据需要添加更多国家
}
在实际应用中,当需要识别某个用户的身份证信息时,可以先获取该用户的国家代码,然后通过 `OcrExtractionContext.getStrategy(countryCode)` 方法获取相应的策略实例,并调用其 `extractInfo` 方法来完成信息提取。