中国联通关于一证十卡的后端核心代码
我司对用户开卡数量进行限制(如最多 10 张)是防止资源滥用的重要措施。以下是实现这一业务规则的详细技术:(已脱敏,关键词 / 变量 / 命名等已替换,适合面对有限公布)
一、数据库设计与查询实现
1. 用户-卡号关联表设计
// 用户信息表
@Entity
@Table(name = "user_info")
public class UserInfo {
@Id
private String idNumber; // 身份证号作为主键
private String name;
// 其他用户字段...
}
// 手机卡信息表
@Entity
@Table(name = "sim_cards")
public class SimCard {
@Id
private String iccid; // 卡唯一标识
private String phoneNumber;
@ManyToOne
@JoinColumn(name = "id_number")
private UserInfo user; // 关联用户
private Date activateDate;
private int status; // 卡状态:0未激活 1已激活 2已注销
// 其他卡字段...
}这种设计通过外键关联用户与卡号,便于统计用户名下卡数量。
2. 卡数量查询服务
@Repository
public class SimCardRepository {
@PersistenceContext
private EntityManager em;
public int countActiveCardsByIdNumber(String idNumber) {
String jpql = "SELECT COUNT(c) FROM SimCard c WHERE c.user.idNumber = :idNum AND c.status = 1";
Query query = em.createQuery(jpql)
.setParameter("idNum", idNumber);
return ((Number)query.getSingleResult()).intValue();
}
}该查询使用 JPQL 统计指定用户已激活的卡数量。
二、业务规则校验实现
1. 开卡前置校验服务
@Service
@Transactional
public class CardApplicationService {
private static final int MAX_CARDS_PER_USER = 10;
@Autowired
private SimCardRepository simCardRepo;
public void validateCardApplication(UserInfo user) throws CardLimitExceededException {
int cardCount = simCardRepo.countActiveCardsByIdNumber(user.getIdNumber());
if (cardCount >= MAX_CARDS_PER_USER) {
throw new CardLimitExceededException(
"每个用户最多只能办理" + MAX_CARDS_PER_USER + "张卡,当前已有" + cardCount + "张");
}
}
}该服务在开卡流程前进行校验,超过限制抛出业务异常。
2. 异常类定义
public class CardLimitExceededException extends BusinessException {
public CardLimitExceededException(String message) {
super(ErrorCode.CARD_LIMIT_EXCEEDED, message);
}
}
// 统一错误码定义
public interface ErrorCode {
String CARD_LIMIT_EXCEEDED = "CARD-001";
// 其他错误码...
}通过自定义异常实现业务规则的统一处理。
三、分布式环境下的实现方案
1. Redis分布式计数
@Service
public class CardCountCacheService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String CARD_COUNT_KEY_PREFIX = "user:cards:";
public int getUserCardCount(String idNumber) {
String key = CARD_COUNT_KEY_PREFIX + idNumber;
String count = redisTemplate.opsForValue().get(key);
if (count == null) {
// 缓存未命中,从数据库加载
int dbCount = simCardRepo.countActiveCardsByIdNumber(idNumber);
redisTemplate.opsForValue().set(key, String.valueOf(dbCount), 1, TimeUnit.HOURS);
return dbCount;
}
return Integer.parseInt(count);
}
@Transactional
public void incrementCardCount(String idNumber) {
String key = CARD_COUNT_KEY_PREFIX + idNumber;
redisTemplate.opsForValue().increment(key);
}
}使用 Redis 缓存用户卡数量,提高性能并保证分布式一致性。
2. 分布式锁防并发
public void applyNewCard(UserInfo user) {
String lockKey = "lock:card:apply:" + user.getIdNumber();
try {
// 获取分布式锁
boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 30, TimeUnit.SECONDS);
if (!locked) {
throw new ConcurrentOperationException("操作过于频繁,请稍后再试");
}
// 校验卡数量
cardAppService.validateCardApplication(user);
// 创建新卡
SimCard newCard = createNewCard(user);
simCardRepo.save(newCard);
// 更新缓存计数
cardCountCache.incrementCardCount(user.getIdNumber());
} finally {
// 释放锁
redisTemplate.delete(lockKey);
}
}通过分布式锁防止同一用户并发开卡导致数量超限。
四、联通OCS系统集成方案
中国联通在线计费系统 (OCS) 中相关实现:
1. 用户管理模块集成
@Component
public class OcsUserManager {
@Autowired
private AccountService accountService;
public void checkUserCardLimit(String userId) {
AccountInfo account = accountService.getAccountByUserId(userId);
if (account.getSimCards().size() >= 10) {
throw new OcsException("用户卡数量已达上限");
}
}
}OCS 系统通过用户管理模块进行卡数量校验。
2. 费率管理模块联动
@Configuration
public class CardLimitConfig {
@Bean
public RatePlanFilter cardLimitFilter() {
return (userId, plan) -> {
int cardCount = cardCountService.getUserCardCount(userId);
return cardCount < 10 || plan.isSpecialPlan();
};
}
}费率管理模块可根据用户卡数量限制可选套餐。
五、性能优化方案
1. 数据库优化
-- 添加用户-卡号的复合索引
CREATE INDEX idx_simcard_user ON sim_cards(id_number, status);
-- 定期归档历史数据
CREATE TABLE sim_cards_archive AS
SELECT * FROM sim_cards WHERE status = 2 AND activate_date < NOW() - INTERVAL '2 years';优化查询性能并控制数据量增长。
2. 缓存策略
@Cacheable(value = "userCardCount", key = "#idNumber")
public int getCachedCardCount(String idNumber) {
return simCardRepo.countActiveCardsByIdNumber(idNumber);
}
@CacheEvict(value = "userCardCount", key = "#user.idNumber")
public void createNewCard(UserInfo user) {
// 创建新卡逻辑
}使用 Spring Cache 抽象实现缓存自动管理。
六、测试方案
1. 单元测试
@SpringBootTest
public class CardLimitTest {
@Autowired
private CardApplicationService service;
@Test
void testCardLimit() {
UserInfo user = new UserInfo("123456789012345678");
// 模拟已有10张卡
when(simCardRepo.countActiveCardsByIdNumber(any())).thenReturn(10);
assertThrows(CardLimitExceededException.class,
() -> service.validateCardApplication(user));
}
}2. 性能测试
@Benchmark
@Threads(32)
public void testConcurrentCardApplication() {
String idNumber = "test_" + ThreadLocalRandom.current().nextInt(1000);
try {
cardService.applyNewCard(new UserInfo(idNumber));
} catch (CardLimitExceededException ignored) {}
}使用 JMH 进行并发性能测试。
七、安全与合规考虑
实名认证:开卡前需通过身份证联网核查
日志审计:记录所有开卡操作日志
@Aspect @Component public class CardOperationLogger { @AfterReturning("execution(* com.unicom..*CardService.*(..)) && args(user,..)") public void logOperation(JoinPoint jp, UserInfo user) { auditLog.info("开卡操作:用户{},操作{}", user.getIdNumber(), jp.getSignature().getName()); } }数据加密:敏感信息如身份证号存储时加密
以上技术方案,可以有效实施 "每个用户最多 10 张卡" 的业务规则,同时保证系统性能、并发安全和服务可靠性。
注:该方案为最终定档方案,以后以此执行。
中国联通关于一证十卡的后端核心代码
https://uniomo.com/archives/zhong-guo-lian-tong-guan-yu-yi-zheng-shi-qia-de-hou-duan-he-xin-dai-ma