Java电话计费与查询系统设计:从架构到代码实现

一、系统需求分析与功能定位

电话计费与查询系统需满足两类核心需求:计费规则管理数据查询服务。计费规则需支持灵活配置,例如按通话时长阶梯计费、分时段差异化费率、长途/本地通话区分等;查询服务需实现高效检索,支持按用户ID、时间范围、通话类型等多维度查询。

从架构设计角度,系统需具备可扩展性(支持新增计费规则)、高可用性(7×24小时查询服务)和数据一致性(计费与查询结果同步)。建议采用分层架构:数据访问层(DAO)、业务逻辑层(Service)、接口层(Controller),结合缓存技术提升查询性能。

二、数据库设计与关键表结构

数据库是系统的核心存储,需设计以下关键表:

  1. 用户表(User):存储用户基本信息(ID、姓名、电话号码、账户余额)
  2. 通话记录表(CallRecord):记录每次通话详情(ID、用户ID、主叫号码、被叫号码、开始时间、结束时间、通话类型)
  3. 计费规则表(BillingRule):定义计费策略(ID、规则名称、费率、生效时间、失效时间、适用通话类型)

示例SQL建表语句:

  1. CREATE TABLE User (
  2. user_id INT PRIMARY KEY AUTO_INCREMENT,
  3. name VARCHAR(50) NOT NULL,
  4. phone VARCHAR(20) UNIQUE NOT NULL,
  5. balance DECIMAL(10,2) DEFAULT 0.00
  6. );
  7. CREATE TABLE CallRecord (
  8. record_id INT PRIMARY KEY AUTO_INCREMENT,
  9. user_id INT NOT NULL,
  10. caller VARCHAR(20) NOT NULL,
  11. callee VARCHAR(20) NOT NULL,
  12. start_time DATETIME NOT NULL,
  13. end_time DATETIME NOT NULL,
  14. call_type VARCHAR(20) NOT NULL, -- 本地/长途/国际
  15. FOREIGN KEY (user_id) REFERENCES User(user_id)
  16. );
  17. CREATE TABLE BillingRule (
  18. rule_id INT PRIMARY KEY AUTO_INCREMENT,
  19. rule_name VARCHAR(50) NOT NULL,
  20. rate DECIMAL(5,2) NOT NULL, -- 元/分钟
  21. effective_time DATETIME NOT NULL,
  22. expiry_time DATETIME NOT NULL,
  23. call_type VARCHAR(20) NOT NULL
  24. );

三、核心功能实现:计费与查询

1. 计费逻辑实现

计费模块需根据通话记录和计费规则计算费用。关键步骤:

  • 规则匹配:根据通话类型和时间段选择适用规则
  • 时长计算duration = end_time - start_time
  • 费用计算fee = duration * rate

示例代码(Service层):

  1. public class BillingService {
  2. @Autowired
  3. private BillingRuleRepository ruleRepo;
  4. @Autowired
  5. private CallRecordRepository recordRepo;
  6. public BigDecimal calculateFee(Long recordId) {
  7. CallRecord record = recordRepo.findById(recordId).orElseThrow();
  8. BillingRule rule = ruleRepo.findByCallTypeAndTime(
  9. record.getCallType(),
  10. record.getStartTime(),
  11. record.getEndTime()
  12. );
  13. long duration = Duration.between(
  14. record.getStartTime().toInstant(),
  15. record.getEndTime().toInstant()
  16. ).toMinutes();
  17. return BigDecimal.valueOf(duration).multiply(rule.getRate());
  18. }
  19. }

2. 查询功能实现

查询模块需支持多条件组合查询,例如“查询用户A在2023年10月的长途通话记录”。优化建议:

  • 索引优化:在user_idstart_timecall_type字段建立复合索引
  • 分页查询:避免一次性加载大量数据
  • 缓存策略:对高频查询结果(如“今日通话记录”)缓存

示例代码(Controller层):

  1. @RestController
  2. @RequestMapping("/api/calls")
  3. public class CallQueryController {
  4. @Autowired
  5. private CallRecordRepository recordRepo;
  6. @GetMapping
  7. public Page<CallRecord> queryCalls(
  8. @RequestParam Long userId,
  9. @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
  10. @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate,
  11. @RequestParam(defaultValue = "0") int page,
  12. @RequestParam(defaultValue = "10") int size) {
  13. Pageable pageable = PageRequest.of(page, size);
  14. return recordRepo.findByUserIdAndStartTimeBetween(
  15. userId,
  16. startDate.atStartOfDay(),
  17. endDate.plusDays(1).atStartOfDay(),
  18. pageable
  19. );
  20. }
  21. }

四、性能优化与扩展性设计

1. 数据库优化

  • 读写分离:主库负责写操作,从库负责读操作
  • 分表策略:按用户ID或时间范围分表(如CallRecord_202310
  • 异步处理:计费结果写入队列,避免阻塞主流程

2. 缓存层设计

引入Redis缓存高频查询结果:

  • 缓存键设计user:{userId}:calls:{startDate}:{endDate}
  • 过期策略:根据业务需求设置TTL(如1小时)
  • 缓存穿透防护:对空结果缓存短期值(如1分钟)

示例缓存代码:

  1. @Cacheable(value = "callRecords", key = "#userId + ':' + #startDate + ':' + #endDate")
  2. public List<CallRecord> getCachedCalls(Long userId, LocalDate startDate, LocalDate endDate) {
  3. // 实际查询逻辑
  4. }

3. 微服务化扩展

当系统规模扩大时,可拆分为:

  • 计费服务:独立部署,处理计费逻辑
  • 查询服务:提供RESTful API
  • 规则管理服务:支持动态更新计费规则

通过消息队列(如Kafka)实现服务间通信,例如计费完成后发布FeeCalculated事件。

五、测试与部署建议

  1. 单元测试:使用JUnit测试计费逻辑,模拟不同计费规则
  2. 集成测试:验证数据库与缓存的交互
  3. 压力测试:模拟高并发查询场景(如1000QPS)
  4. 部署方案
    • 容器化部署(Docker + Kubernetes)
    • 监控告警(Prometheus + Grafana)
    • 日志收集(ELK栈)

六、总结与最佳实践

开发电话计费与查询系统时,需重点关注:

  1. 计费规则的灵活性:通过配置化支持业务变化
  2. 查询性能的优化:索引、缓存、分页三管齐下
  3. 系统的可扩展性:从单体到微服务的平滑演进
  4. 数据的一致性:通过事务和消息队列保证

实际开发中,可参考开源框架(如Spring Boot)快速搭建基础架构,结合业务需求定制核心逻辑。对于高并发场景,建议采用异步处理和分布式缓存提升系统吞吐量。