黑马程序员__移动用户资费统计Demo

------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------

模拟实现简易的移动用户资费统计系统逻辑,具体需求如下:

1.移动运营商A设置两种类型的用户:普通用户及VIP用户,现该运营商已有5VIP用户和15个普通用户,共计20个用户。

 

2.普通用户资费标准如下(不考虑漫游和长途):

【基准资费】

  无月租费用。

通话费:0.6元 分钟(仅拨打收费,接听免费)

短信费:0.1元 

数据费:5/ M

 

          【优惠套餐】

 话费套餐 :月功能费 20元,最多可拨打60分钟电话,超出时间按照                   

                               0.5元 分钟计费。

            短信套餐 :月功能费10元,最多可发送200条短信,超出条数按照

                               0.1元 条计费。

            数据套餐 :月功能费20元,最多可获50M的流量,超出流量按照

                               3元 / M 计费。

 

注:用户可以选择多种套餐,各功能(通话、短信、数据)计费时,如已选择对应套餐,则按套餐标准计费;如未选择对应套餐,则按对应的基准资费计费。

 

VIP用户资费标准如下(不考虑漫游和长途):

 

【基准资费】

  月租费用:按天收取,2元 

通话费:0.4元 分钟(仅拨打收费,接听免费)

短信费:0.1元 

数据费:3/ M

 

          【优惠套餐】

 套餐:月基本费用 100元(无月租费用),提供如下服务:

                   ① 最多可拨打750分钟电话,超出部分按照0.3元 分钟计费。

                   ② 最多可发送200条短信,超出条数按照0.1元 条计费。

                   ③ 最多可获得100M数据流量,超出流量按照1元 / M计费。

 

            套餐:月基本费用 200元(无月租费用),提供如下服务:

                   ① 最多可拨打2000分钟电话,超出部分按照0.2元 分钟计费。

                   ② 最多可发送500条短信,超出条数按照0.1元 条计费。

                   ③ 最多可获得300M数据流量,超出流量按照0.5元 / M计费。

 

注:用户最多只能选择一种套餐,如未选择任何套餐,则按照基准资费计费。

 

各类型用户只能选择提供给本类型用户的套餐。

 

新用户入网。

 

            ① 对于新入网的普通用户,入网当月赠送如下服务:免费拨打60分钟

                电话,免费发送200条短信,免费获得50M流量。超出赠送的部分

                按照普通用户基准资费进行计费。

            ② 对于新入网的VIP用户,入网当月赠送如下服务:免费拨打200

                钟电话,免费发送200条短信,免费获得100M数据流量。超出赠送

                的部分按照VIP用户基准资费进行计费(注意:需按入网天数计算月

                租费用)。

  每月为用户计算一次账单,用户订制的套餐信息和账单信息采用文件方式进行存储(提示:可使用java中的Properties API进行文件操作)。

 用户可自由订制或退订所属用户类型的套餐,并从下月起生效。

 

异步随机生成客户操作如下:

 

① 拨打电话,每次拨打时长为110分钟不等(随机决定,以分钟为

    单位)。

② 发送短信,每次发送条数为110条不等(随机决定)。

③ 上网获取数据,每次获取数据流量可为50K100K200K

    500K1M(随机决定)。

④ 订制或退订相应套餐。

⑤ 新用户入网(随机决定用户类型)。

 

注:随机生成客户操作时间间隔自定,可设置。

 

不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。

运用配置文件存储各项数据:

 

在配置文件中存储各项数据

 

采用一种便于程序代码读取的格式:
要存储的数据项有:功能单价费用、功能套餐免费数量、功能套餐月费用、新入网免费数量、整体月基本费或月租费。
一些数据还要随以下类型进行区分:用户类型、套餐类型、功能类型。
在配置文件中通过用点(.)对数据项名称进行分级的方式来区分各个数据项所属的类别和功能,如下所示:
 common.normal.phone.price 表示普通用户/非套餐/电话/单价
 common.pack1.phone.price 表示普通用户/套餐/电话/单价
 common.pack1.phone.free 表示普通用户/套餐/电话/免费数量
 common.pack1.phone.rent 表示普通用户/套餐/电话/套餐月功能费用

 

 vip.normal.phone.price 表示VIP用户/非套餐/电话/单价
 vip.pack1.phone.price 表示VIP用户/套餐1/电话/单价
 vip.pack2.phone.price 表示VIP用户/套餐2/电话/单价

 

 common.new.phone.free 表示普通用户/新开户/电话/免费数量
 vip.new.phone.free 表示VIP用户/新开户/电话/免费数量
对于值为0的数据项,不用在配置文件中存储,这样,当程序代码从配置文件中没有读取到该数据项时,即认为该值为0。
对于vip用户的整体月基本费或月租费,由于计费单位不一样,采用配置文件方式存储将增加程序的复杂度,所以,决定直接在程序代码中硬编码。

编码读取配置文件中的各项数据:

ConfigManager类中的方法:
公有:getPrice、getFree、getRent、getNewCustomerFree。
私有:makePrefix、getNumber
makePrefix方法:
 private static String getPrefix(int customerType,int packType,int entryType){
  String packTitles = {"normal","pack1","pack2“,”new”};
  String entryTitles ={"phone","message","data"};
  
  String customerTitle = customerType==0?"common":"vip";
  String packTitle = packTitles[packType];
  String entryTitle = entryTitles[entryType];
  return customerTitle + "." + packTitle + "." + entryTitle;
 }
getNumber方法:
 private static int getNumber(String key){
  String value = config.getProperty(key);
  try{
   return Integer.parseInt(value);
  }catch(Exception e){
   return 0;
  }  
 }

代表移动公司的类:

 1 public class MobileCorporation {
 2     private ArrayList<Customer> customers = new ArrayList<Customer>();
 3     public MobileCorporation(){
 4         for(int i=1;i<=15;i++){
 5             customers.add(new CommonCustomer(i+"号普通客户",new Date(108,10,1)));}
 6         for(int i=1;i<=5;i++){
 7             customers.add(new VIPCustomer(i+"号VIP客户",new Date(108,10,1)));}
 8         System.out.println("程序创建了运营商已有的5个VIP用户和15个普通用户,并设置他们的入网日期为2008年10月1日.");
 9     }
10     //模拟某个月的业务活动
11     public void simulateBusiness(Date month){
12         for(Customer customer : customers){
13             customer.monthBegin();}
14         System.out.println("--------being simulating " + DateUtil.formatDateToMonth(month) + "--------------");    
15         for(int i=0;i<500/*30*/;i++){
16             randDoOneThing(month);}
17         System.out.println(DateUtil.formatDateToMonth(month)+"的计费汇总清单:");
18         //汇总所有人的账单
19         for(int i=0;i<customers.size();i++){
20             customers.get(i).countMonthMoney(month);}
21     }

移动公司的randDoOneThing方法

 1 private void randDoOneThing(Date month){
 2         Calendar calendar = Calendar.getInstance();
 3         calendar.setTime(month);
 4         calendar.add(Calendar.MONTH, 1);
 5         Date monthOfOrderPack = calendar.getTime();            
 6         
 7         int rand = new Random().nextInt(30);
 8         switch(rand){
 9         case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:case 8:
10             callPhone();break;
11         case 9:    case 10:case 11:case 12:case 13:case 14:case 15:case 16:case 17:            
12             sendMessage();break;
13         case 18:case 19:case 20:case 21:case 22:case 23:case 24:case 25:case 26:            
14             transferData();break;
15         /*让orderPack、cancelPack、joinNewCustomer的出现概率是其他操作的1/9。*/    
16         case 27:        
17             orderPack(monthOfOrderPack);break;
18         case 28:                    
19             cancelPack(monthOfOrderPack);break;
20         case 29:
21             joinNewCustomer(month);break;            
22         }
23     }

移动公司类的joinNewCustomer方法:

 1 private void joinNewCustomer(Date month){
 2         Calendar calendar = Calendar.getInstance();
 3         calendar.setTime(month);
 4         int maxDay = calendar.getMaximum(Calendar.DAY_OF_MONTH);
 5         int randDay = new Random().nextInt(maxDay) + 1;
 6         /*下面的复制过程很重要,不能直接修改date,当然最好是对Calendar直接操作
 7          * 这里是为了演示要注意clone而保留的。
 8          */ 
 9         Date joinTime = (Date)month.clone();
10         joinTime.setDate(randDay);
11         int randType = (new Random().nextInt(10))%2;
12         Customer customer = null;
13         if(randType == 0){
14             int commonId = IdGenerator.getInstance().nextCommonId();
15             customer = new CommonCustomer(commonId+"号普通客户",joinTime);
16             customers.add(customer);
17         }else{
18             int vipId = IdGenerator.getInstance().nextVipId();    
19             customer = new VIPCustomer(vipId+"号VIP客户",joinTime);
20             customers.add(customer);
21         }
22         System.out.println(DateUtil.formatDateToDay(joinTime) + "新注册了" + customer);
23     }

为新用户生成Id编号的方法:

 1 public class IdGenerator {
 2     private IdGenerator(){}
 3     private static IdGenerator instance = new IdGenerator();
 4     public static IdGenerator getInstance(){
 5         return instance;}
 6     
 7     private int lastCommonId = 15;
 8     private int lastVipId = 5;
 9     
10     public synchronized int nextCommonId(){
11         return ++lastCommonId;}
12     public synchronized int nextVipId(){
13         return ++lastVipId;}
14 }

主运行类:

 1 public static void main(String[] args) {
 2         MobileCorporation corp = new MobileCorporation();
 3         //设置要模拟的起始月份
 4         Date month = new Date(109,0,1);    
 5         System.out.println("程序开始模拟从2009年1月1日开始,连续15个月的运行情况.");        
 6         //总共模拟15个连续的月份
 7         for(int i=0;i<15/*3*/;i++){
 8             corp.simulateBusiness(month);
 9             Calendar calendar = Calendar.getInstance();
10             calendar.setTime(month);
11             calendar.add(Calendar.MONTH, 1);
12             month = calendar.getTime();
13         }
14     }

普通用户与VIP用户的抽象父类:

 1 public abstract class Customer {
 2     protected String name; private int customerType; private Date joinTime;//用户入网的时间
 3     //下面集合中积累的结果只表示当月的记录,不代表所有历史记录
 4     protected List<ActionRecord> actionRecords = new ArrayList<ActionRecord>();private ArrayList phoneRecords = new ArrayList();
 5     private ArrayList messageRecords = new ArrayList();private ArrayList dataRecords = new ArrayList();
 6     protected PackStrategy packStrategy;
 7     public void monthBegin(){
 8         phoneRecords.clear();messageRecords.clear();dataRecords.clear();actionRecords.clear();
 9     }
10     public Customer(String name,int customerType,Date joinTime){
11         this.name = name;this.customerType = customerType;this.joinTime = joinTime;
12     }
13     public String toString(){  return name;
14     }
15     public void callPhone(int times){
16         phoneRecords.add(times); actionRecords.add(new ActionRecord("打电话",times + "分钟"));
17     }
18     public void sendMessage(int numbers){
19         messageRecords.add(numbers);  actionRecords.add(new ActionRecord("发短信",numbers + "条"));        
20     }
21     public  void transferData(int size){
22         dataRecords.add(size);  actionRecords.add(new ActionRecord("传数据",size + "k"));            
23     }
24     private int gatherRecords(ArrayList records){
25         int sum = 0;
26         for(int i=0;i<records.size();i++){
27             sum +=  (Integer)(records.get(i));
28         }
29         return sum;
30     }
31     public abstract void randomCancelPack(Date month);    
32     public abstract void randomOrderPack(Date month);    

用户类中计算某月费用的方法:

 1 public int countMonthMoney(Date currentMonth){
 2         boolean newcome = !joinTime.before(currentMonth);//joinTime.after(currentMonth);
 3         int totalPhone = gatherRecords(phoneRecords);int totalMessage = gatherRecords(messageRecords);int totalData = gatherRecords(dataRecords);
 4         int freePhone = 0;int freeMessage = 0;int freeData = 0;
 5         if(newcome){
 6             freePhone = ConfigManager.getNewUserFree(customerType,0);  freeMessage = ConfigManager.getNewUserFree(customerType,1);
 7             freeData = ConfigManager.getNewUserFree(customerType,2);    
 8         }
 9         int chargePhone = totalPhone>freePhone?totalPhone-freePhone:0;int chargeMessage = totalMessage>freeMessage?totalMessage-freeMessage:0;
10         int chargeData = totalData>freeData?totalData-freeData:0;                
11         //汇总打印:包括姓名,入网日期,统计月份,通话清单,费用清单,总费用。
12         System.out.println(name + "," + DateUtil.formatDateToDay(joinTime) + "入网.");
13         System.out.println(" 操作清单如下-----");    
14             for(int i=0;i<actionRecords.size();i++){ System.out.println("  " + actionRecords.get(i)); }
15         System.out.println(" 统计清单如下-----");
16         System.out.println("  通话:" + phoneRecords + "分钟:短信" + messageRecords + "条:数据" + dataRecords + "k");
17         System.out.println("  通话累计:" + totalPhone + "分钟,减除新开户" + freePhone + "分钟,实际收费" + chargePhone + "分钟");
18         System.out.println("  短信累计:" + totalMessage + "条,减除新开户" + freeMessage + "条,实际收费" + chargeMessage + "条");
19         System.out.println("  数据累计:" + totalData + "k,减除新开户" + freeData + "k,实际收费" + chargeData + "k");
20         ComputeStrategy phoneStrategy = packStrategy.getValidComputeStrategy(currentMonth,0);
21         ComputeStrategy messageStrategy = packStrategy.getValidMessageComputeStrategy(currentMonth,1);
22         ComputeStrategy dataStrategy = packStrategy.getValidDataComputeStrategy(currentMonth,2);
23         int sum = 0; Rent rent = packStrategy.getValidRent(currentMonth); //VIP用户才有月租金或基本费
24         if(rent != null){
25             int rentMoney = rent.coputeRent(joinTime,currentMonth);sum +=  rentMoney;
26             System.out.println("  月租费或基本费:" + rentMoney + "分钱");            
27         }
28         sum += phoneStrategy.computeMoney(chargePhone)+messageStrategy.computeMoney(chargeMessage)+ dataStrategy.computeMoney(chargeData);
29         System.out.println("  总计:" + sum + "分钱");
30         return sum;
31     }    

普通用户类的 randomOrderPack方法:

public void randomOrderPack(Date month){
		int rand = new Random().nextInt(3);
		switch(rand){
		case 0:
			if(packStrategy.getOrderedPack(0)==null || packStrategy.getOrderedPack(0).getPackType() == 0){
				packStrategy.orderPack(month,0);
				System.out.println(name + "订购了" + "电话套餐" + "(从" + DateUtil.formatDateToMonth(month) + "开始)");		
				actionRecords.add(new ActionRecord("定电话套餐",""));	
			}	break;
		case 1:
			if(packStrategy.getOrderedPack(1) == null || packStrategy.getOrderedPack(1).getPackType() == 0){			
				packStrategy.orderPack(month,1);	
				System.out.println(name + "订购了" + "短信套餐" + "(从" + DateUtil.formatDateToMonth(month) + "开始)");		
				actionRecords.add(new ActionRecord("定短信套餐",""));		
			}		break;
		case 2:
			if(packStrategy.getOrderedPack(2) == null || packStrategy.getOrderedPack(2).getPackType() == 0){			
				packStrategy.orderPack(month,2);	
				System.out.println(name + "订购了" + "数据套餐" + "(从" + DateUtil.formatDateToMonth(month) + "开始)");	
				actionRecords.add(new ActionRecord("定数据套餐",""));	
			}	break;
		}
	}

 普通用户类的 randomCancelPack方法

public void randomCancelPack(Date orderedMonth){
		int rand = new Random().nextInt(3);
		switch(rand){
		case 0:
			if(packStrategy.getOrderedPack(0) == null || packStrategy.getOrderedPack(0).getPackType() == 0){
				System.out.println(name + "试图退订根本就没有订过的电话套餐");		return;
			}				packStrategy.cancelPack(orderedMonth,0);
			System.out.println(name + "退订了" + "电话套餐" + "(从" + DateUtil.formatDateToMonth(orderedMonth) + "开始)");	
			actionRecords.add(new ActionRecord("退订电话套餐",""));	break;
		case 1:
			if(packStrategy.getOrderedPack(1) ==null || packStrategy.getOrderedPack(1).getPackType() == 0){
				System.out.println(name + "试图退订根本就没有订过的短信套餐");		return;
			}				packStrategy.cancelPack(orderedMonth,1);	
			System.out.println(name + "退订了" + "短信套餐" + "(从" + DateUtil.formatDateToMonth(orderedMonth) + "开始)");	
			actionRecords.add(new ActionRecord("退订短信套餐",""));          break;
		case 2:
			if(packStrategy.getOrderedPack(2)==null || packStrategy.getOrderedPack(2).getPackType() == 0){
				System.out.println(name + "试图退订根本就没有订过的数据套餐");		return;
			}				packStrategy.cancelPack(orderedMonth,2);	
			System.out.println(name + "退订了" + "数据套餐" + "(从" + DateUtil.formatDateToMonth(orderedMonth) + "开始)");	
			actionRecords.add(new ActionRecord("退订数据套餐",""));		break;
		}
	}

 Vip用户的方法与以上方法类似。

计算某项功能费用的策略类:

 1 public class ComputeStrategy {
 2          //此处省略成员变量的定义….
 3     public ComputeStrategy(int customerType, int packType,int entryType) {
 4         this.customerType = customerType;
 5         this.packType = packType;
 6         this.entryType = entryType;
 7         switch(entryType){
 8             case 0:entryName = "电话";break;
 9             case 1:entryName = "短信";break;
10             case 2:entryName = "数据";break;                                
11         }
12     }
13     
14     public int computeMoney(int quantity){
15         int unitPrice = ConfigManager.getUnitPrice(customerType, packType,entryType);
16         int freeQuantity = ConfigManager.getPackFree(customerType, packType,entryType);
17         int packCharge = ConfigManager.getPackCharge(customerType, packType,entryType);
18         int charQuantity = quantity - freeQuantity;
19         if(chargeQuantity < 0){
20             chargeQuantity = 0;
21         }
22         int money = packCharge + price * chargeQuantity;
23         System.out.println("  " + entryName + "月功能费为:" + packCharge + "分钱,");            
24         System.out.println("        收费数量:" + quantity + "-" + freeQuantity + "=" + chargeQuantity + ","); 
25         System.out.println("        使用收费:"    + chargeQuantity + "*" + price + "=" + price * chargeQuantity +"分钱,");
26         System.out.println("        小计收费:"    + packCharge + "+" + price * chargeQuantity + "分钱");
27         return money;
28     }
29 }

计算VIP用户月租费或月基本费的类

public class Rent {
    //此处省略成员变量和构造方法的定义
    public int coputeRent(Date startTime,Date currentMonth){
        //首先应该想到去找开源的日期运算类        
        if(unit == RentUnit.DAY){
            Calendar start = Calendar.getInstance();
            start.setTime(startTime);
            Calendar end = Calendar.getInstance();
            end.setTime(currentMonth);
            //将日期设置为当月的最后一天
            end.set(Calendar.DAY_OF_MONTH, 1);
            end.add(Calendar.MONTH, 1);
            end.add(Calendar.DAY_OF_MONTH, -1);
            int days = end.get(Calendar.DAY_OF_MONTH) ;
            if(end.get(Calendar.MONTH) == start.get(Calendar.MONTH)){
                days -= start.get(Calendar.DAY_OF_MONTH) + 1;                
            }
            return  price*days;
        }
        else{
            return price;
        }
    }    
}

存储用户的各项业务套餐策略的类

 1 public class PackStrategy {
 2     private int customerType;
 3     
 4     private Rent rent;
 5     private ComputeStrategy computeStrategy[] = new  ComputeStrategy[3];    
 6 
 7     private OrderedStrategyHolder<Rent> orderedRent = new OrderedStrategyHolder<Rent>();
 8     private OrderedStrategyHolder<ComputeStrategy>[] orderedStrategyHolder = new OrderedStrategyHolder<ComputeStrategy>[3];
 9     
10     public PackStrategy(int customerType,int packType,Rent rent){
11         this.customerType = customerType;
12         this.rent = rent;
13         
14         for(int i=0;i<3;i++){
15             computeStrategy[i] = new ComputeStrategy(customerType,packType,i);
16             orderedStrategyHolder[i] = new OrderedStrategyHolder<ComputeStrategy>();
17         }    
18     public Rent getValidRent(Date month){
19         Rent holderRent = orderedRent.getValidStrategy(month); 
20         return holderRent==null?rent:holderRent;        
21     }    
22     
23     public void orderRent(Date orderedMonth,Rent rent){
24         Rent oldRent = orderedRent.order(orderedMonth,rent);
25         if(oldRent != null){
26             this.rent = oldRent;
27         }        
28     }
29 
30     public void cancelRent(Date orderedMonth){
31         orderRent(orderedMonth,null);
32     }
33 }
34 public ComputeStrategy getValidComputeStrategy(Date month,int entryType){
35         ComputeStrategy orderedStrategy = orderedStrategyHolder[entryType].getValidStrategy(month); 
36         return orderedStrategy==null?computeStrategy[entryType]:orderedStrategy;
37     }
38 
39     public void orderPack(Date orderedMonth,int packType,entryType){
40         ComputeStrategy orderedStrategy = orderedStrategyHolder[entryType].order(
41                 orderedMonth,new ComputeStrategy(customerType,packType,entryType));
42         if(orderedStrategy != null){
43             computeStrategy[entryType] = orderedStrategy;
44         }
45     }
46 
47     public void cancelPack(Date orderedMonth,int entryType){
48         orderPack(orderedMonth, 0,entryType);
49     }

套餐订购记录类 (存储一项业务套餐订购信息)

 1 public class OrderedStrategyHolder<T> {
 2     private Date orderedMonth;
 3     private T strategy;
 4     
 5     public T order(Date orderingMonth,T strategy){
 6         T oldStrategy = null;
 7         if((this.orderedMonth!=null) && this.orderedMonth.before(orderingMonth)){
 8             oldStrategy = this.strategy;
 9         }
10         
11         this.orderedMonth = orderingMonth;
12         this.strategy = strategy;
13         return oldStrategy;
14     }
15     
16     public T getValidStrategy(Date month){
17         if(orderedMonth!=null && (!orderedMonth.after(month))){
18             return strategy;
19         }
20         return null;
21     }
22 }

 

转载于:https://www.cnblogs.com/xtfgq/archive/2013/04/11/3014244.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值