模拟手机办卡项目(移动大厅)--结合面向对象、JDBC、MYSQL、dao层模式,使用JAVA控制台实现

news/2024/11/29 8:28:32/

目录

1. 项目需求

2. 项目使用的技术

3.项目需求分析

3.1 实体类和接口

 4.项目结构

5.业务实现

5.1 登录

5.1.1 实现步骤

 5.1.2 原生代码问题

​编辑

 5.1.3 解决方法

1.说明:

2. ResultSetHandler结果集处理

5.1.4 代码

5.1.5 实现后的效果图

 登录成功​编辑

登录失败 

 5.2 注册

5.2.1 实现步骤

 5.2.2 代码

5.2.3 数据库设计

5.2.4 实现后的效果图

 5.3 本月账单查询

5.3.1 实现步骤

5.2.2 代码 

5.2.3 实现后的效果图

 5.4 使用嗖嗖

5.4.1 功能分析

5.4.2 实现步骤

5.4.3 代码

5.4.4 实现后的效果图 

消费成功:

​编辑 消费失败:


1. 项目需求

       中国移动,中国联通,中国电信是国内3大通信运营商,每个运营商都提供了不同的品牌套餐来应对不同的用户群,比如北京移动主要有全球通,神州行,动感地带等3大品牌套餐,每种套餐的内容和费用不同,嗖嗖移动是一个假定的通信运营商,提供了话痨套餐,网虫套餐,超人套餐,各种套餐所包含的服务内容及费用如下表:

品牌套餐

话痨套餐网虫套餐超人套餐
通话时长(分钟)6000300
上网流量02010
短信条数(条)100050
费用(元/月)586878

如实际使用中超出套餐内包含的通话时长,短信条数和上网流量,则按一下规则计费:

  • 超出的通话: 0.2元/分

  • 超出的短信:0.1元/条

  • 超出的上网流量:0.1元/MB

        本任务实现的"嗖嗖移动业务大厅"提供了嗖嗖移动用户的常用功能,包括新用户注册,本月账单查询,套餐余量查询,打印消费详情,套餐变更,办理退网,话费充值,查看消费记录,查看话费说明等功能.另外,还可以模拟用户通话,上网,发送短信的场景进行相应的扣费并记录消费信息.各功能介绍如下表:    

菜单级别功能描述
主菜单用户登录输入正确的手机号码和密码进入二级菜单列表
主菜单用户注册录入信息并开卡,用户输入的信息包括:选择卡号,选择套餐类型,输入用户名和密码,预存话费金额(预存话费金额必须满足以支付所选套餐的一个月的费用)
主菜单使用嗖嗖输入正确的手机号码和密码之后,随机进入本号码所属套餐可以支持的一个场景,消费套餐余量或者话费余额,并记录消费信息.当话费余额不足时,抛出异常提醒用户充值
主菜单话费充值输入正确的用户名和密码之后,可为该卡号充值
主菜单资费说明提供各品牌套餐所包含的通话时长,上网流量,短信条数,月费用等
主菜单退出系统退出本系统
二级菜单本月账单查询可查询该卡号的套餐费用,实际消费金额,账户余额
二级菜单套餐余量查询可查询该卡号的套餐余量
二级菜单打印消费详情输入正确的卡号和密码后,可打印当前卡号用户的消费详单, 使用输出流把用户信息输出到文件
二级菜单套餐变更可变更为其他套餐类型,变更后话费余额需减去变更后的套餐费用,余额不足时需要给出信息提示,套餐变更后重新统计卡中实际消费数据以及当月消费金额
二级菜单办理退网输入正确的卡号和密码后,可以从已注册的号码列表中删除本号码,并退出系统

2. 项目使用的技术

  • 面向对象的思想

  • 封装,继承,多态,接口的使用

  • 异常处理的合理使用

  • 集合框架的使用

  • I/O 操作实现对文件的写

  • MySQL数据

  • JDBC操作数据库

3.项目需求分析

3.1 实体类和接口

  1. Card(电话号码类)

  2. MoboleCard(嗖嗖移动卡类)

  3. monthlyConsumptionRecords(月消费记录类)

  4. 套餐类 SerPackage

  5. 套餐类型类 SerPackageType

  6. ConsumInfo(消费信息类)

  7. Scene(使用场景类)

  8. RechargeRecord(充值记录类)

Card(电话号码类)

private String cardNumber;
private Integer status;

 MoboleCard(嗖嗖移动卡类)

private String cardNumber;
private String username;
private String password;
private Integer serPackage;
private BigDecimal money;
private Integer status;

 monthlyConsumptionRecords(月消费记录类)

private String cardNumber;
private BigDecimal comsumAmount;
private Integer realTalkTime;
private Integer realSmsCount;
private Integer realFlow;
private Date consumeDate;

SerPackage(套餐类 )

private Integer talkTime;
private Integer smsCount;
private BigDecimal price;
private Integer flow;
private Integer type;

 SerPackageType(套餐类型类)

private String name;

 ConsumInfo(消费信息类)

private String cardNumber;
private String type;
private Integer consumData;
private Date consumeDate;

 Scene(使用场景类)

private String type;
private Integer data;
private String description;

 RechargeRecord(充值记录类)

private Integer id;
private BigDecimal amount;
private Date rechargeDate;
private String CardNum;

 4.项目结构

分层模式:

|–entity 实体类所在包

|–dao 数据访问层接口包

|–impl: 数据访问层接口的实现类所在包     每一个方法只操作一条sql

|–service: 业务接口层

|–impl: 业务接口实现类所在包

|–ui : 界面菜单显示

|–util: 通用工具类所在包

5.业务实现

5.1 登录

从前(ui/网页)往后写

ui –> service–> mappe

上层调用下层, 不能下层调用上层

5.1.1 实现步骤

  1. 用户在控制台输入手机卡号和密码,根据手机卡号和密码查询tb_mobile_card表,判断是否存在该用户
  2. 如果用户卡号和密码输入正确,通过用户输入的电话号码查询tb_card表,判断卡号是否被冻结
  3. 编写异常类,通过异常判断该用户是卡号或密码输入错误还是卡号被冻结

 5.1.2 原生代码问题

 5.1.3 解决方法

使用第三方的jar: apache提供的: commons-dbutils

提供一个核心类: QueryRunner

1.说明:

DBUtils是java编程中数据库操作的实用工具,小巧简单实用。第一个操作数据库的框架(jar) DBUtils封装了许多JDBC的代码,简化了JDBC操作,可以少写很多代码 DBUtils三个核心功能的介绍: DBUtils类: 它就是一个工具类,定义了关闭资源和事务处理的方法

QueryRunner类: 提供了对sql语句操作的方法,query():查询,update():修改

ResultSetHandler接口: 用于定义select操作后,如何封装结果集

QueryRunner核心类

  • QueryRunner(DataSource ds):提供数据源(连接池)DataSource,DBUtils底层自动维护和连接Connection数据库 DataSource : 数据源(连接池)

  • QueryRunner(): 创建一个QueryRunner对象

  • query(String sql, ResultSetHandler<T> rsh, Object... params):执行查询语句select

  • update(String sql, Object... params):执行更新语句insert,update,delete,参数就是一个数组,参数个数取决于sql语句中?的个数

2. ResultSetHandler结果集处理
Handler类型说明
BeanHandler将结果集中第一条记录封装到一个指定的javaBean中。
BeanListHandler将结果集中每一条记录封装到指定的javaBean中,将这些javaBean在封装到List集合中
ScalarHandler它是用于单个数据。例如select count(*) from 表操作。
ArrayHandler将结果集中的第一条记录封装到一个Object[]数组中,数组中的每一个元素就是这条记录中的每一个字段的值
ArrayListHandler将结果集中的每一条记录都封装到一个Object[]数组中,将这些数组在封装到List集合中。
ColumnListHandler将结果集中指定的列的字段值,封装到一个List集合中
KeyedHandler将结果集中每一条记录封装到Map<String,Object>,在将这个map集合做为另一个Map的value,另一个Map集合的key是指定的字段的值。
MapHandler将结果集中第一条记录封装到了Map<String,Object>集合中,key就是字段名称,value就是字段值
MapListHandler将结果集中每一条记录封装到了Map<String,Object>集合中,key就是字段名称,value就是字段值,在将这些Map封装到List集合中。

5.1.4 代码

 UI层方法:

private void loginUI() {
    System.out.println("\n---------------------用户登录--------------------");
    System.out.print("请输入您的手机卡号:");
    String cardNum = scanner.next();
    System.out.print("请输入密码:");
    String password = scanner.next();
    //调用业务层的登录方法
    MobileCardService mobileCardService=new MobileCardServiceImpl();
    try{
        cardNumber=mobileCardService.login(cardNum,password);
    }catch (CardNumOrPasswordException e){
        System.out.println("提示!!! "+e.getMessage());
        showFirstMenu();
    }catch (CardFreezenException e){
        System.out.println("提示!!! "+e.getMessage());
        showFirstMenu();
    }
    showSecondMenu();
}

MobileCardDaoImpl电话卡数据访问实现方法:

public MobileCard findByCardNum(String cardNum) {
    QueryRunner queryRunner = new QueryRunner();
    Connection connection = null;
    try {
        connection = getConnection();
        String sql="select id,card_number cardNumber,username,password,ser_package serPackage,money,status from tb_mobile_card where card_number = ?";
        //注意:要求字段名与属性对应  如果属性名与数据库列名不一致 取别名
        MobileCard mobileCard = queryRunner.query(connection, sql, new BeanHandler<>(MobileCard.class), cardNum);//(Connection对象,sql语句,结果的映射.?的值)
        return mobileCard;
    } catch (SQLException e) {
        throw new RuntimeException(e);
    }finally {
        close(connection);
    }
}

用户手机卡业务层实现:

MobileCardService:

/**
* 登录
* @param cardNum 卡号
* @param password  卡密码
* @throws CardNumOrPasswordException  卡号或密码错误异常
* @throws CardFreezenException  卡冻结异常
*/
String login(String cardNum, String password) throws CardNumOrPasswordException, CardFreezenException;

MobileCardServiceImple:

@Override
public String login(String cardNum, String password) throws CardNumOrPasswordException, CardFreezenException {
    MobileCardDao mobileCardDao=new MobileCardDaoImpl();
    MobileCard mobileCard=mobileCardDao.findByCardNum(cardNum);
    if(mobileCard==null){
        throw new CardNumOrPasswordException("卡号或者密码错误");
    }else{
        //比较密码
        if(!mobileCard.getPassword().equals(password)){
            throw new CardNumOrPasswordException("卡号或者密码错误");
        }
        //判断状态是否冻结
        else {
            if(mobileCard.getStatus()==CARD_FREEZEN){
                throw new CardFreezenException("该卡已冻结,请联系客服");
            }
        }
    }
    return mobileCard.getCardNumber();
}

电话卡业务层实现:

CardService:

public interface CardService extends IService<Card>{

    Card queryByNumber(String cardNumber);
}

CardServiceImpl:

public Card queryByNumber(String cardNumber) {
    CardDao cardDao = new CardDaoImpl();
    return cardDao.queryByNumber(cardNumber);
}

电话卡数据访问层实现:

CardDao:

public interface CardDao extends BaseDao<Card>{

    Card queryByNumber(String cardNumber);
}

CardDaoImpl:

public Card queryByNumber(String cardNumber) {
    QueryRunner queryRunner = new QueryRunner();
    Connection connection = null;
    try {
        connection = getConnection();
        String sql="select id,cardNumber,status from tb_card where cardNumber = ?";
        //注意:要求字段名与属性对应  如果属性名与数据库列名不一致 取别名
        Card card = queryRunner.query(connection, sql, new BeanHandler<>(Card.class), cardNumber);//(Connection对象,sql语句,结果的映射.?的值)
        return card;
    } catch (SQLException e) {
        throw new RuntimeException(e);
    }finally {
        close(connection);
    }
}

5.1.5 实现后的效果图

 登录成功
登录失败 

 5.2 注册

5.2.1 实现步骤

  1. 查询tb_card: 查询所有可用号码

  2. 查询所有套餐类型 tb_serpackage_type,并展示

  3. 用户选择对应的套餐类型, 根据套餐类型查询套餐详情 tb_serpackage

  4. 用户输入充值金额, 判断充值金额 >= 套餐月租, 办卡成功, 往tb_moboel_card 插入一条记录, 往充值表tb_recharge_record 插入一条记录, 修改tb_card的记录的状态  

 5.2.2 代码

UI层实现:

private void registerUI() {
    System.out.println("\n---------------------用户注册--------------------");
    System.out.println("\n----------可选择的卡号----------");
    CardService cardService = new CardServiceImpl();
    List<Card> cardList = cardService.queryAll();
    for(int i=0;i<cardList.size();i++){
        Card card=cardList.get(i);
        System.out.print("\t"+(i+1)+". "+card.getCardNumber());
        if((i+1) % 3 == 0){
            System.out.println();
        }
    }
    System.out.print("\n请选择您要注册的手机卡号:");
    int choice = scanner.nextInt();
    Card card = cardList.get(choice-1);
    //列表显示所有的套餐类型
    System.out.println("\n----------可选择的套餐----------");
    SerPackageTypeService serPackageTypeService = new SerPackageTypeServiceImpl();
    List<SerPackageType> serPackageList = serPackageTypeService.queryAll();
    for(int i=0;i<serPackageList.size();i++){
        SerPackageType serPackageType=serPackageList.get(i);
        System.out.println((i+1)+". "+serPackageType.getName());
    }
    System.out.print("\n请选择您要注册的套餐类型:");
    int choice1 = scanner.nextInt();
    SerPackageType chooseserPackageType = serPackageList.get(choice1-1);
    //查询用户选择的套餐类型的套餐详情
    SerPackageService serPackageService = new SerPackageServiceImpl();
    SerPackage serPackage = serPackageService.queryByType(chooseserPackageType.getId());
    //基本信息输入
    System.out.print("\n请输入您的姓名:");
    String name=scanner.next();
    System.out.print("\n请输入您的密码:");
    String password=scanner.next();
    System.out.print("\n请输入预存话费金额:");
    BigDecimal money=scanner.nextBigDecimal();
    //判断输入的金额是否满足套餐
    //月租大于预存金额
    double balance=0;
    while((balance=serPackage.getPrice().subtract(money).doubleValue()) > 0){
        System.out.println("提示!!! 您的预存话费不足以支付本月套餐话费,请重新输入!");
        System.out.println("\n请输入您的预存话费:");
        money=scanner.nextBigDecimal();
    }
    //往用户手机卡中插入一条数据zh-CN
    MobileCardService mobileCardService=new MobileCardServiceImpl();
    MobileCard mobileCard=new MobileCard();
    mobileCard.setCardNumber(card.getCardNumber());
    mobileCard.setUsername(name);
    mobileCard.setPassword(password);
    mobileCard.setMoney(money.subtract(serPackage.getPrice()));
    mobileCard.setSerPackage(serPackage.getId());
    mobileCard.setStatus(CARD_NORMAL);
    mobileCardService.add(mobileCard);
    //往充值记录表中插入一条数据
    RechargeRecord rechargeRecord=new RechargeRecord();
    rechargeRecord.setAmount(money);
    rechargeRecord.setCardNum(card.getCardNumber());
    rechargeRecord.setRechargeDate(new Date());
    RechargeRecordService rechargeRecordService=new RechargeRecordServiceImpl();
    rechargeRecordService.add(rechargeRecord);
    //修改手机卡表的手机号码状态为已激活
    card.setStatus(CARD_FREEZEN);
    cardService.update(card);
    System.out.println("【友情提示】恭喜您,注册成功! 您的手机卡号为:"+card.getCardNumber()+",用户名:"+name+",当前卡余额:"+mobileCard.getMoney());
    System.out.println(chooseserPackageType.getName()+"详细信息如下:");
    System.out.println("套餐名称:"+chooseserPackageType.getName()+"\n套餐价格:"+serPackage.getPrice()+"元\n通话时长:"+serPackage.getTalkTime()+"分钟\n短信条数:"+serPackage.getSmsCount()+"条\n流量:"+serPackage.getFlow()+"MB");
    loginUI();
}

套餐类型业务层实现:

SerPackageTypeServiceImpl:

public List<SerPackageType> queryAll() {
    SerPackageTypeDao serPackageTypeDao = new SerPackageTypeDaoImpl();
    return serPackageTypeDao.queryAll();
}

套餐类型数据访问层实现:

SerPackageTypeDaoImpl:

public List<SerPackageType> queryAll() {
    String sql="select * from tb_serpackage_type";
    Connection con = null;
    try {
        con=getConnection();
        QueryRunner queryRunner=new QueryRunner();
        return queryRunner.query(con,sql,new BeanListHandler<SerPackageType>(SerPackageType.class));
    } catch (SQLException e) {
        throw new RuntimeException(e);
    }finally {
        close(con);
    }
}

用户卡业务层实现:

MobileCardServiceImpl:

public void add(MobileCard mobileCard) {
    MobileCardDao mobileCardDao=new MobileCardDaoImpl();
    mobileCardDao.add(mobileCard);
}

用户卡数据访问层实现:

MobileCardDaoImpl:

public void add(MobileCard mobileCard) {
    Connection connection = null;
    try {
        connection = getConnection();
        String sql="insert into tb_mobile_card(card_number,username,password,ser_package,money,status) values(?,?,?,?,?,?)";
        QueryRunner queryRunner=new QueryRunner();
        queryRunner.update(connection,sql,mobileCard.getCardNumber(),mobileCard.getUsername(),
                                          mobileCard.getPassword(),mobileCard.getSerPackage(),
                                          mobileCard.getMoney(),mobileCard.getStatus());
    } catch (SQLException e) {
        throw new RuntimeException(e);
    }finally {
        close(connection);
    }
}

消费记录业务层实现:

ConsumInfoServiceImpl:

public void add(ConsumInfo consumInfo) {
    ConsumInfoDao consumInfoDao=new ConsumInfoDaoImpl();
    consumInfoDao.add(consumInfo);
}

消费记录数据访问层实现:

ConsumInfoDaoImpl:

public void add(ConsumInfo consumInfo) {
    Connection connection = null;
    try {
        connection = getConnection();
        String sql="insert into tb_consuminfo(card_number,type,consum_data,consume_date) values(?,?,?,?)";
        QueryRunner queryRunner=new QueryRunner();
        queryRunner.update(connection,sql,consumInfo.getCardNumber(),consumInfo.getType(),
                consumInfo.getConsumData(), DateUtil.formatDate(consumInfo.getConsumeDate(),"yy-MM-dd hh:mm:ss"));
    } catch (SQLException e) {
        throw new RuntimeException(e);
    }finally {
        close(connection);
    }
}

电话卡类业务层实现:

CardServiceImpl:

public void update(Card card) {
    CardDao cardDao = new CardDaoImpl();
    cardDao.update(card);
}

电话卡类数据访问层实现:

CardDaoImpl:

public void update(Card card) {
    Connection connection = null;
    try {
        connection = getConnection();
        String sql="update tb_card set status=? where id=?";
        QueryRunner queryRunner=new QueryRunner();
        queryRunner.update(connection,sql,card.getStatus(),card.getId());
    } catch (SQLException e) {
        throw new RuntimeException(e);
    }finally {
        close(connection);
    }
}

5.2.3 数据库设计

5.2.4 实现后的效果图

 5.3 本月账单查询

5.3.1 实现步骤

  1. 根据登录的手机号码查询卡的余额 tb_mobile_card
  2. 根据手机号码绑定的套餐id查询套餐月租
  3. 根据手机号,本月日期:2024-11-01 查询本月消费金额
  4. 如果没有记录,就往表里面插入一条数据
  5. 有就直接查询 tb_monthly_consumption_records

5.2.2 代码 

UI层实现:

private void QueryMonthBill() {
    System.out.println("\n---------------------本月账单查询--------------------");
    //查询手机号码的余额
    MobileCardService mobileCardService = new MobileCardServiceImpl();
    MobileCard mobileCard = mobileCardService.querryByNumber(cardNumber);
    //根据手机号码绑定的套餐id查询套餐月租
    SerPackageService serPackageService = new SerPackageServiceImpl();
    SerPackage serPackage = serPackageService.queryByType(mobileCard.getSerPackage());
    //根据手机号,本月日期:2024-11-01  查询本月消费金额
    MonthlyConsumptionRecordsService mCRService = new MonthlyConsumptionRecordsServiceImpl();
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.DAY_OF_MONTH,1);
    Date date = cal.getTime();
    MonthlyConsumptionRecords consumptionRecords =mCRService.queryByNumberAndDate(cardNumber,date);
    if(Objects.isNull(consumptionRecords)){
        consumptionRecords=new MonthlyConsumptionRecords();
        consumptionRecords.setCardNumber(cardNumber);
        consumptionRecords.setComsumAmount(new BigDecimal(0));
        consumptionRecords.setConsumeDate(date);
        consumptionRecords.setRealFlow(0);
        consumptionRecords.setRealTalkTime(0);
        consumptionRecords.setRealSmsCount(0);
        mCRService.add(consumptionRecords);
    }
    System.out.println("【友情提示】 您的本月账单如下:");
    System.out.println("\n您的卡号:"+cardNumber);
    System.out.println("套餐资费:"+serPackage.getPrice()+"元");
    System.out.println("合计:"+serPackage.getPrice().doubleValue()+consumptionRecords.getComsumAmount().doubleValue()+"元");
    System.out.println("账户余额:"+mobileCard.getMoney().doubleValue()+"元");
    showSecondMenu();
}

月消费记录业务层实现:

MonthlyConsumptionRecordsServiceImpl:

public MonthlyConsumptionRecords queryByNumberAndDate(String cardNumber, Date date) {
    MonthlyConsumptionRecordsDao mcrDao = new MonthlyConsumptionRecordsDaoImpl();
    return mcrDao.queryByNumberAndDate(cardNumber,date);
}

月消费记录数据访问层实现:

MonthlyConsumptionRecordsDaompl:

public MonthlyConsumptionRecords queryByNumberAndDate(String cardNumber, Date date) {
    QueryRunner queryRunner = new QueryRunner();
    Connection connection = null;
    try {
        connection = getConnection();
        String sql="select id,card_number cardNumber,consum_amount comsumAmount,real_talk_time realTalkTime,real_SMS_count realSmsCount,real_flow realFlow,consume_date consumeDate from tb_monthly_consumption_records " +
                "where card_number = ? and consume_date = ?";
        //注意:要求字段名与属性对应  如果属性名与数据库列名不一致 取别名
        MonthlyConsumptionRecords mcr = queryRunner.query(connection, sql, new BeanHandler<>(MonthlyConsumptionRecords.class), cardNumber,formatDate(date,"yy-MM-dd"));
        return mcr;
    } catch (SQLException e) {
        throw new RuntimeException(e);
    }finally {
        close(connection);
    }
}

5.2.3 实现后的效果图

 5.4 使用嗖嗖

5.4.1 功能分析

(1)模拟嗖嗖用户使用卡的过程,选择该功能后,输入当前卡号,通过验证后,可随机进入如下表的6个场景,要求所进入的场景的服务类型是该卡所属套餐支持的(如网虫套餐只能进入服务类型为"上网"的场景)

序号服务类型描述
0通话问候客户,谁知其如此难缠,通话90分钟
1通话询问妈妈身体状况,本地通话30分钟
2短信参与环境保护实施方案问卷调查,发送短信5条
3短信同时朋友本人已换手机号码,发送短信50条
4上网和女朋友微信视频聊天,使用流量1GB
5上网晚上手机在线追剧,一不留神睡着了,使用流量2GB
(2) 模拟消费,进入场景之后,将按场景的描述要求消费套餐余量, 如果套餐余量不足,则需要按套餐外的费用规则扣费,成功消费后,添加一条消费记录

5.4.2 实现步骤

  1. 输入手机号码
  2. 根据手机号码查询用户手机卡,判断是否存在,是否禁用
  3. 查询所有场景,随机一个场景
  4. 根据场景类型(通话,短信,上网),判断是否额外支付,余额是否满足消费
  5. 如果余额不足本次消费,计算最大消费数据
  6. 如果产生费用,修改用户卡中余额,没有产生费用,不需要修改
  7. 修改月账单记录表,真实数据,额外费用
  8. 往消费记录表插入一条数据
  9. 输出结果
  10. 该查询的表:套餐表 用户卡表 月消费记录表 场景表 消费记录表

5.4.3 代码

部分数据库表查询和增删改的方法前面写过,所以要增加的数据分层实现方法比较轻松实现,难得是UI层和计算

UI层实现:

private void useSoSoUI() {
    System.out.println("\n---------------------使用嗖嗖--------------------");
    System.out.print("请输入手机卡号:");
    cardNumber=scanner.next();
    //根据手机号码查询用户手机卡,判断是否存在,是否禁用
    MobileCardService mobileCardService = new MobileCardServiceImpl();
    MobileCard mobileCard = mobileCardService.querryByNumber(cardNumber);
    if(Objects.isNull(mobileCard)){
        System.out.println("【友情提示】 该卡不存在,无法使用");
        return;
    }else if(mobileCard.getStatus().intValue() == CARD_FREEZEN){
        System.out.println("【友情提示】 该卡已被冻结,无法使用");
        return;
    }
    //查询卡的套餐
    SerPackageService serPackageService = new SerPackageServiceImpl();
    SerPackage serPackage = serPackageService.queryByType(mobileCard.getSerPackage());
    //查询所有场景
    SceneService sceneService=new SceneServiceImpl();
    List<Scene> scenes = sceneService.queryAll();
    //随机一个场景
    Random random = new Random();
    int index = random.nextInt(scenes.size());
    Scene scene = scenes.get(index);
    String sceneType=scene.getType();
    //查询余额消费记录
    MonthlyConsumptionRecordsService mcrService = new MonthlyConsumptionRecordsServiceImpl();
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.DAY_OF_MONTH,1);
    Date date = cal.getTime();
    MonthlyConsumptionRecords consumptionRecords =mcrService.queryByNumberAndDate(cardNumber,date);
    if(Objects.isNull(consumptionRecords)){
        consumptionRecords=new MonthlyConsumptionRecords();
        consumptionRecords.setCardNumber(cardNumber);
        consumptionRecords.setComsumAmount(new BigDecimal(0));
        consumptionRecords.setConsumeDate(date);
        consumptionRecords.setRealFlow(0);
        consumptionRecords.setRealTalkTime(0);
        consumptionRecords.setRealSmsCount(0);
        mcrService.add(consumptionRecords);
    }
    ConsumInfo consumInfo=new ConsumInfo();
    ConsumInfoService csrService = new ConsumInfoServiceImpl();
    int margin=0;
    BigDecimal amount = new BigDecimal(0); //需要支付的费用
    int extra = 0; //额外通话时长
    int real = scene.getData();
    switch (sceneType){
        case "通话":
             //判断余量是否满足本次消费
            //余量= 套餐免费通话时长 - 本月已消费时长
            margin=serPackage.getTalkTime()-consumptionRecords.getRealTalkTime(); //余量
            margin = margin < 0 ? 0 : margin;
            if(margin < scene.getData()){  //余量  不足
                extra = scene.getData() - margin;  //额外需要的通话时长
                amount = new BigDecimal(extra).multiply(new BigDecimal(EXTRA_TALK));  //额外需要的话费
                //余额是否满足需要支付的话费
                if(mobileCard.getMoney().compareTo(amount) < 0){  //不满足
                    //计算最大消费数据
                    extra=mobileCard.getMoney().divide(new BigDecimal(EXTRA_TALK),2,BigDecimal.ROUND_HALF_UP).intValue();
                    amount = new BigDecimal(extra).multiply(new BigDecimal(EXTRA_TALK));
                    real=margin+extra;  //余额不足  真实通话时长
                    System.out.println("【友情提示】本次已通话"+real+"分钟,您的余额不足,请充值后使用");
                }
            }
            //如果产生费用,修改用户卡中余额,没有产生费用,不需要修改
            if(amount.compareTo(new BigDecimal(0)) > 0){  //产生额外费用需要支付,修改用户卡中余额
                mobileCard.setMoney(mobileCard.getMoney().subtract(amount));
                mobileCardService.update(mobileCard);
            }
            //修改月账单记录表,真实数据,额外费用
            consumptionRecords.setRealTalkTime(real+consumptionRecords.getRealTalkTime());
            consumptionRecords.setComsumAmount(consumptionRecords.getComsumAmount().add(amount));
            mcrService.update(consumptionRecords);
            //往消费记录表插入一条数据
            consumInfo.setConsumeDate(new Date());
            consumInfo.setCardNumber(cardNumber);
            consumInfo.setType(sceneType);
            consumInfo.setConsumData(real);
            csrService.add(consumInfo);
            System.out.println(scene.getDescription());
            break;
        case "短信":
            //判断余量是否满足本次消费
            //余量= 套餐免费通话时长 - 本月已消费时长
            margin=serPackage.getSmsCount()-consumptionRecords.getRealSmsCount(); //余量
            margin = margin < 0 ? 0 : margin;
            if(margin < scene.getData()){  //余量  不足
                extra = scene.getData() - margin;  //额外需要的通话时长
                amount = new BigDecimal(extra).multiply(new BigDecimal(EXTRA_SMS));  //额外需要的话费
                //余额是否满足需要支付的话费
                if(mobileCard.getMoney().compareTo(amount) < 0){  //不满足
                    //计算最大消费数据
                    extra=mobileCard.getMoney().divide(new BigDecimal(EXTRA_SMS),2,BigDecimal.ROUND_HALF_UP).intValue();
                    amount = new BigDecimal(extra).multiply(new BigDecimal(EXTRA_SMS));
                    real=margin+extra;  //余额不足  真实通话时长
                    System.out.println("【友情提示】本次已发送"+real+"条,您的余额不足,请充值后使用");
                }
            }
            //如果产生费用,修改用户卡中余额,没有产生费用,不需要修改
            if(amount.compareTo(new BigDecimal(0)) > 0){  //产生额外费用需要支付,修改用户卡中余额
                mobileCard.setMoney(mobileCard.getMoney().subtract(amount));
                mobileCardService.update(mobileCard);
            }
            //修改月账单记录表,真实数据,额外费用
            consumptionRecords.setRealSmsCount(real+consumptionRecords.getRealSmsCount());
            consumptionRecords.setComsumAmount(consumptionRecords.getComsumAmount().add(amount));
            mcrService.update(consumptionRecords);
            //往消费记录表插入一条数据
            consumInfo.setConsumeDate(new Date());
            consumInfo.setCardNumber(cardNumber);
            consumInfo.setType(sceneType);
            consumInfo.setConsumData(real);
            csrService.add(consumInfo);
            System.out.println(scene.getDescription());
            break;
        case "上网":
            //判断余量是否满足本次消费
            //余量= 套餐免费通话时长 - 本月已消费时长
            margin=serPackage.getFlow()-consumptionRecords.getRealFlow(); //余量
            margin = margin < 0 ? 0 : margin;
            if(margin < scene.getData()){  //余量  不足
                extra = scene.getData() - margin;  //额外需要的通话时长
                amount = new BigDecimal(extra).multiply(new BigDecimal(EXTRA_FLOW));  //额外需要的话费
                //余额是否满足需要支付的话费
                if(mobileCard.getMoney().compareTo(amount) < 0){  //不满足
                    //计算最大消费数据
                    extra=mobileCard.getMoney().divide(new BigDecimal(EXTRA_FLOW),2,BigDecimal.ROUND_HALF_UP).intValue();
                    amount = new BigDecimal(extra).multiply(new BigDecimal(EXTRA_FLOW));
                    real=margin+extra;  //余额不足  真实通话时长
                    System.out.println("【友情提示】本次已使用"+real+"MB的流量,您的余额不足,请充值后使用");
                }
            }
            //如果产生费用,修改用户卡中余额,没有产生费用,不需要修改
            if(amount.compareTo(new BigDecimal(0)) > 0){  //产生额外费用需要支付,修改用户卡中余额
                mobileCard.setMoney(mobileCard.getMoney().subtract(amount));
                mobileCardService.update(mobileCard);
            }
            //修改月账单记录表,真实数据,额外费用
            consumptionRecords.setRealFlow(real+consumptionRecords.getRealFlow());
            consumptionRecords.setComsumAmount(consumptionRecords.getComsumAmount().add(amount));
            mcrService.update(consumptionRecords);
            //往消费记录表插入一条数据
            consumInfo.setConsumeDate(new Date());
            consumInfo.setCardNumber(cardNumber);
            consumInfo.setType(sceneType);
            consumInfo.setConsumData(real);
            csrService.add(consumInfo);
            System.out.println(scene.getDescription());
            break;
    }
    showFirstMenu();
}

消费记录业务层实现:

ConsumInfoServiceImpl:

public void add(ConsumInfo consumInfo) {
    ConsumInfoDao consumInfoDao=new ConsumInfoDaoImpl();
    consumInfoDao.add(consumInfo);
}

消费记录数据分层实现:

ConsumInfoDaoImpl:

public void add(ConsumInfo consumInfo) {
    Connection connection = null;
    try {
        connection = getConnection();
        String sql="insert into tb_consuminfo(card_number,type,consum_data,consume_date) values(?,?,?,?)";
        QueryRunner queryRunner=new QueryRunner();
        queryRunner.update(connection,sql,consumInfo.getCardNumber(),consumInfo.getType(),
                consumInfo.getConsumData(), DateUtil.formatDate(consumInfo.getConsumeDate(),"yy-MM-dd hh:mm:ss"));
    } catch (SQLException e) {
        throw new RuntimeException(e);
    }finally {
        close(connection);
    }
}

月账单记录业务层实现:

public void update(MonthlyConsumptionRecords monthlyConsumptionRecords) {
    MonthlyConsumptionRecordsDao mcrDao = new MonthlyConsumptionRecordsDaoImpl();
    mcrDao.update(monthlyConsumptionRecords);
}

月账单记录数据访问层实现:

public void update(MonthlyConsumptionRecords mcrRecords) {
     Connection connection = null;
    try {
        connection = getConnection();
        String sql="update tb_monthly_consumption_records set consum_amount = ?,real_talk_time = ?,real_SMS_count = ?,real_flow = ? where id = ?";
        QueryRunner queryRunner=new QueryRunner();
        queryRunner.update(connection,sql,mcrRecords.getComsumAmount(),mcrRecords.getRealTalkTime(),
                mcrRecords.getRealSmsCount(),mcrRecords.getRealFlow(),mcrRecords.getId());
    } catch (SQLException e) {
        throw new RuntimeException(e);
    }finally {
        close(connection);
    }
}

5.4.4 实现后的效果图 

消费成功:
 消费失败:

将套餐中的固定数额消费完后,再使用嗖嗖就会显示余额不足


http://www.ppmy.cn/news/1550855.html

相关文章

分布式MQTT代理中使用布隆过滤器管理通配符主题

论文标题&#xff1a;Wildcard Topic Management using Bloom Filter in Distributed MQTT Brokers 中文标题&#xff1a;分布式MQTT代理中使用布隆过滤器管理通配符主题 作者信息&#xff1a; Ryohei Banno&#xff0c;Hitotsubashi University, Graduate School of Social…

QT 实现组织树状图

1.实现效果 在Qt中使用QGraphicsItem和QGraphicsScene实现树状图,你需要创建自定义的QGraphicsItem类来表示树的节点,并管理它们的位置和连接,以下是实现效果图。 2.实现思路 可以看见,上图所示,我们需要自定义连线类和节点类。 每个节点类Node,需要绘制矩形框体文字…

告别繁琐剪辑:【星海智算】FunClip重新定义视频创作

FunClip模型介绍 FunClip是由阿里达摩院精心打造的一款完全开源、本地部署的自动化视频剪辑工具。FunClip让用户能够根据识别结果&#xff0c;轻松选择文本片段或特定说话人&#xff0c;从而快速裁剪出所需视频片段。FunClip的特色功能包括集成高精度的中文ASR模型、支持热词定…

讨论JAVA、JVM与Spring

Q1: 作为一个JAVA开发人员&#xff0c;对于jvm肯定不陌生&#xff0c;但很多人对它不陌生也仅止于概念上&#xff0c;而且对概念也是模糊不清的&#xff0c;但jvm实际是java程序运行在其中的实际存在的环境&#xff0c;对它的理解应该要是具象化的。 我们还是从一项技术产生的…

【数据库】macos官网/brew安装mysql5.7/8/9,修改cnf配置/身份验证插件,sqldump数据库备份与恢复

【数据库】macos官网/brew安装mysql5.7/8/9&#xff0c;修改cnf配置/身份验证插件&#xff0c;sqldump数据库备份与恢复 文章目录 1、mysql安装homebrew 安装 mysql9.0官网安装mysql 5.7社区版-归档版 2、修改cnf配置/身份验证方式起因&#xff1a;修改密码验证方式为mysql_nat…

SqlServer REVERSE字符串值的逆序排序函数

SqlServer中 REVERSE函数返回字符串值的逆序排序 适用于&#xff1a; SQL ServerAzure SQL 数据库Azure SQL 托管实例Azure Synapse Analytics分析平台系统 (PDW)Microsoft Fabric 中的 SQL 分析端点Microsoft Fabric 中的仓库 1、语法 REVERSE ( string_expression )2、参…

(0基础保姆教程)-JavaEE开课啦!--11课程(初识Spring MVC + Vue2.0 + Mybatis)-实验9

一、什么是Spring MVC&#xff1f; Spring MVC 是一个基于 Java 的 Web 框架&#xff0c;遵循 MVC 设计模式&#xff0c;用于构建企业级应用程序。它通过控制器(Controller)处理用户请求&#xff0c;模型(Model)处理业务逻辑&#xff0c;视图(View)展示数据&#xff0c;实现了请…

【机器学习】—逻辑回归

逻辑回归实现详解 介绍 逻辑回归&#xff08;Logistic Regression&#xff09;是一种广泛应用于分类问题的统计模型&#xff0c;尤其适用于二分类问题。本文将通过一个简单的例子&#xff0c;使用Python和PyTorch库实现逻辑回归&#xff0c;并通过可视化展示模型的训练过程和…