中国联通关于一证十卡的后端核心代码

我司对用户开卡数量进行限制(如最多 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 进行并发性能测试。

七、安全与合规考虑

  1. 实名认证:开卡前需通过身份证联网核查

  2. 日志审计:记录所有开卡操作日志

    @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());
        }
    }
  3. 数据加密:敏感信息如身份证号存储时加密

以上技术方案,可以有效实施 "每个用户最多 10 张卡" 的业务规则,同时保证系统性能、并发安全和服务可靠性。

注:该方案为最终定档方案,以后以此执行。


中国联通关于一证十卡的后端核心代码
https://uniomo.com/archives/zhong-guo-lian-tong-guan-yu-yi-zheng-shi-qia-de-hou-duan-he-xin-dai-ma
作者
雨落秋垣
发布于
2025年06月10日
许可协议