一、系统需求分析与功能定位
电话计费与查询系统需满足两类核心需求:计费规则管理与数据查询服务。计费规则需支持灵活配置,例如按通话时长阶梯计费、分时段差异化费率、长途/本地通话区分等;查询服务需实现高效检索,支持按用户ID、时间范围、通话类型等多维度查询。
从架构设计角度,系统需具备可扩展性(支持新增计费规则)、高可用性(7×24小时查询服务)和数据一致性(计费与查询结果同步)。建议采用分层架构:数据访问层(DAO)、业务逻辑层(Service)、接口层(Controller),结合缓存技术提升查询性能。
二、数据库设计与关键表结构
数据库是系统的核心存储,需设计以下关键表:
- 用户表(User):存储用户基本信息(ID、姓名、电话号码、账户余额)
- 通话记录表(CallRecord):记录每次通话详情(ID、用户ID、主叫号码、被叫号码、开始时间、结束时间、通话类型)
- 计费规则表(BillingRule):定义计费策略(ID、规则名称、费率、生效时间、失效时间、适用通话类型)
示例SQL建表语句:
CREATE TABLE User (user_id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(50) NOT NULL,phone VARCHAR(20) UNIQUE NOT NULL,balance DECIMAL(10,2) DEFAULT 0.00);CREATE TABLE CallRecord (record_id INT PRIMARY KEY AUTO_INCREMENT,user_id INT NOT NULL,caller VARCHAR(20) NOT NULL,callee VARCHAR(20) NOT NULL,start_time DATETIME NOT NULL,end_time DATETIME NOT NULL,call_type VARCHAR(20) NOT NULL, -- 本地/长途/国际FOREIGN KEY (user_id) REFERENCES User(user_id));CREATE TABLE BillingRule (rule_id INT PRIMARY KEY AUTO_INCREMENT,rule_name VARCHAR(50) NOT NULL,rate DECIMAL(5,2) NOT NULL, -- 元/分钟effective_time DATETIME NOT NULL,expiry_time DATETIME NOT NULL,call_type VARCHAR(20) NOT NULL);
三、核心功能实现:计费与查询
1. 计费逻辑实现
计费模块需根据通话记录和计费规则计算费用。关键步骤:
- 规则匹配:根据通话类型和时间段选择适用规则
- 时长计算:
duration = end_time - start_time - 费用计算:
fee = duration * rate
示例代码(Service层):
public class BillingService {@Autowiredprivate BillingRuleRepository ruleRepo;@Autowiredprivate CallRecordRepository recordRepo;public BigDecimal calculateFee(Long recordId) {CallRecord record = recordRepo.findById(recordId).orElseThrow();BillingRule rule = ruleRepo.findByCallTypeAndTime(record.getCallType(),record.getStartTime(),record.getEndTime());long duration = Duration.between(record.getStartTime().toInstant(),record.getEndTime().toInstant()).toMinutes();return BigDecimal.valueOf(duration).multiply(rule.getRate());}}
2. 查询功能实现
查询模块需支持多条件组合查询,例如“查询用户A在2023年10月的长途通话记录”。优化建议:
- 索引优化:在
user_id、start_time、call_type字段建立复合索引 - 分页查询:避免一次性加载大量数据
- 缓存策略:对高频查询结果(如“今日通话记录”)缓存
示例代码(Controller层):
@RestController@RequestMapping("/api/calls")public class CallQueryController {@Autowiredprivate CallRecordRepository recordRepo;@GetMappingpublic Page<CallRecord> queryCalls(@RequestParam Long userId,@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate,@RequestParam(defaultValue = "0") int page,@RequestParam(defaultValue = "10") int size) {Pageable pageable = PageRequest.of(page, size);return recordRepo.findByUserIdAndStartTimeBetween(userId,startDate.atStartOfDay(),endDate.plusDays(1).atStartOfDay(),pageable);}}
四、性能优化与扩展性设计
1. 数据库优化
- 读写分离:主库负责写操作,从库负责读操作
- 分表策略:按用户ID或时间范围分表(如
CallRecord_202310) - 异步处理:计费结果写入队列,避免阻塞主流程
2. 缓存层设计
引入Redis缓存高频查询结果:
- 缓存键设计:
user:{userId}
{startDate}:{endDate} - 过期策略:根据业务需求设置TTL(如1小时)
- 缓存穿透防护:对空结果缓存短期值(如1分钟)
示例缓存代码:
@Cacheable(value = "callRecords", key = "#userId + ':' + #startDate + ':' + #endDate")public List<CallRecord> getCachedCalls(Long userId, LocalDate startDate, LocalDate endDate) {// 实际查询逻辑}
3. 微服务化扩展
当系统规模扩大时,可拆分为:
- 计费服务:独立部署,处理计费逻辑
- 查询服务:提供RESTful API
- 规则管理服务:支持动态更新计费规则
通过消息队列(如Kafka)实现服务间通信,例如计费完成后发布FeeCalculated事件。
五、测试与部署建议
- 单元测试:使用JUnit测试计费逻辑,模拟不同计费规则
- 集成测试:验证数据库与缓存的交互
- 压力测试:模拟高并发查询场景(如1000QPS)
- 部署方案:
- 容器化部署(Docker + Kubernetes)
- 监控告警(Prometheus + Grafana)
- 日志收集(ELK栈)
六、总结与最佳实践
开发电话计费与查询系统时,需重点关注:
- 计费规则的灵活性:通过配置化支持业务变化
- 查询性能的优化:索引、缓存、分页三管齐下
- 系统的可扩展性:从单体到微服务的平滑演进
- 数据的一致性:通过事务和消息队列保证
实际开发中,可参考开源框架(如Spring Boot)快速搭建基础架构,结合业务需求定制核心逻辑。对于高并发场景,建议采用异步处理和分布式缓存提升系统吞吐量。