Java小项目---嗖嗖移动大厅

1.项目需求

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

品牌套餐话痨套餐网虫套餐超人套餐
通话时长(分钟)1000100600
上网流量104020
短信条数(条)10010050
费用(元/月)586878

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

  • 超出的通话: 0.2元/分
  • 超出的短信:0.1元/条
  • 超出的上网流量:0.1元/MB

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

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

2.项目使用的技术

  • 面向对象的思想
  • 封装,继承,多态,接口的使用
  • 异常处理的合理使用
  • 集合框架的使用
  • I/O 操作实现对文件的写
  • MySQL数据
  • JDBC操作数据库

3.项目需求分析

3.1 实体类接口

  • Card(电话号码类)

    • cardNumber 卡号
    • status 状态
  • MoboleCard(嗖嗖移动卡类)

    • cardNumber 卡号
    • username 用户名
    • password 密码
    • serPackage 所属套餐
    • money 账户余额
    • status 状态
  • monthlyConsumptionRecords(月消费记录类)

    • cardNumber 卡号
    • consumAmount 当月消费金额
    • realTalkTime 当月实际通话时长
    • realSMSCount 当月实际发送短信条数
    • realFlow 当月实际上网流量
    • consumeDate 消费日期
  • 套餐类 SerPackage

    • talkTime 通话时长
    • smsCount 短信条数
    • price 套餐月资费
    • flow 上网流量
    • type 套餐类型
  • 套餐类型类 SerPackageType

    • name 套餐名称
  • ConsumInfo(消费信息类)

    • cardNumber 卡号
    • type 消费类型
    • consumData 消费数据
    • consumeDate 消费日期
  • Scene(使用场景类)

    • type 场景类型
    • data 场景消费数据
    • description description
  • RechargeRecord(充值记录类)

    • amount 充值金额
    • rechargeDate 充值日期
    • cardNumber 卡号

4.程序流程图

在这里插入图片描述

5.功能模块分析

5.1 用户登录

主要思路:首先用户输入手机号码和密码,将手机号码传给服务层,如何再传到Dao层,Dao层传出用户信息到表示层,表示层判断号码是否被冻结,是否欠费,如果冻结就不准登录同时提示充钱激活,如果欠费就提醒欠费。

代码演示:

private void login() {
        System.out.print("请输入手机号码:");
        card_number = scanner.next();
        System.out.print("请输入密码:");
        password = scanner.next();
        //传号码,得数据,mobole存储用户数据
        moboleCard = moboleCardService.freeze(card_number);
        if (moboleCard != null) {
            //判断是否冻结,status:0.正常 1.冻结
            if (moboleCard.getStatus() == 0) {
                String reply = moboleCardService.login(card_number, password);
                System.out.println(reply);
                if ("登录成功".equals(reply)) {
                    if (moboleCard.getMoney() < 0) {
                        System.err.println("您已欠费,请及时充值,如果欠费达到50元我们将对你的手机号进行冻结!");
                    }
                    secondMenu();
                }
            } else {
                System.out.println("对不起,您的手机号欠费" + -moboleCard.getMoney() + "已经被冻结,无法登录!\n请及时联系管理员或将欠费还清以解除冻结!");
                System.out.print("是否充值(1.不充值 其他键即为充值):");
                if (scanner.nextInt() != 1) {
                    rechargeSoso(card_number);
                }
            }
        } else {
            System.out.println("请输入正确的手机号!");
        }
        firstMenu();
    }

5.2 用户注册

主要思路:首先输入注册信息,在选择电话号码时我弄了个上一页和下一页,让用户有更多的选择空间。选择完套餐就是输入充值金额,如果金额不足以支付套餐金额,就重新输入金额,然后就存入数据库,并记录扣费信息

主代码演示:

private void enroll() {
        MonthlyConsumptionRecord monthly = new MonthlyConsumptionRecord();
        List<Card> cards = cardService.chooseCard();
        
        //获取现在的时间对于的天,后面用
        LocalDate now = LocalDate.now();
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("d");
        String nowDay = dtf.format(now);
        
        System.out.println("********************************************************");
        if (!cards.isEmpty()) {
            int page = 1;
            int num = 0;
            //选择手机号码,选择后存入全局变量card_number
            chooseCard(cards, page, num);
            System.out.print("请输入你的姓名:");
            String username = scanner.next();
            System.out.print("请输入你的密码:");
            password = scanner.next();
            //展示套餐
            showPackage();
            int ser_package;
            //如果输入的数据不合法就重新输入
            do {
                System.out.print("请选择你想要的套餐:");
                ser_package = scanner.nextInt();
            } while (ser_package < 0 || ser_package > 4);
            
            outer:
            do {
                System.out.print("请输入你的充值金额:");
                Double money = scanner.nextDouble();
                for (SerPackage serPackage : serPackages) {
                    //判断输入的金额是否满足套餐所需金额
                    if (serPackage.getId().equals(ser_package)) {
                        if (serPackage.getPrice() > money) {
                            System.out.println("你充值的金额不足!");
                        } else {

                            //每月一号收取月租,防止在一号注册时扣两次月租
                            if (!nowDay.equals("1")) {
                                money -= serPackage.getPrice();
                            }

                            String reply = moboleCardService.enrollSoso(new MoboleCard(card_number, 0, username, password, ser_package, money));
                            System.out.println(reply);

                            //将消费数据传入消费数据表
                            monthly.setCard_number(card_number);
                            monthly.setConsum_amount(money);
                            monthly.setReal_talk_time(0);
                            monthly.setReal_SMS_count(0);
                            monthly.setReal_flow(0);
                            monthlyConsumptionRecordService.update(monthly);

                            if ("注册失败".equals(reply)) {
                                updateCard(0);
                            }
                            break outer;
                        }
                    }
                }
            } while (true);
        } else {
            System.out.println("没有多余的手机号码可以注册!");
        }
        firstMenu();
    }

展示手机号码代码:

private void chooseCard(List<Card> cards, int page, int num) {
        System.out.println("\n***************************************");
        System.out.println("请从下面的手机号码中挑选:");
        boolean bo = true;
        do {
            System.out.print((num % 10 + 1) + "." + cards.get(num).getCard_number() + "\t");
            num++;
            //每一行五个,换行
            if (num % 5 == 0 || num == cards.size()) {
                System.out.println();
            }
            //一页输出十个
            if (num % 10 == 0 || num == cards.size()) {

                //计算页数
                int maxPage;
                if (cards.size() % 10 == 0) {
                    maxPage = cards.size() / 10;
                } else {
                    maxPage = cards.size() / 10 + 1;
                }

                System.out.println();
                System.out.println("当前为第" + page + "页");
                System.out.println("1.上一页\t2.下一页\t3.选择手机号码");
                System.out.print("请选择:(选择其他键返回第一页)");
                switch (scanner.nextInt()) {
                    case 1:
                        if (page != 1) {
                            page--;
                            num = (page - 1) * 10;
                        } else {
                            num = 0;
                            System.out.println("这是第一页,没有上一页了!");
                        }
                        chooseCard(cards, page, num);
                        break;
                    case 2:
                        if (page != maxPage) {
                            page++;
                        } else {
                            //如果是最后一页,将num加的值退回,重新输出这一页                            
                            if (num % 10 == 0) {
                                num -= 10;
                            } else {
                                num -= num % 10;
                            }
                            System.out.println("这是最后一页了!");
                        }
                        chooseCard(cards, page, num);
                        break;
                    case 3:
                        int maxIndex = 10;
                        int choose;
                        if (page == maxPage) {
                            maxIndex = cards.size() % 10;
                        }
                        do {
                            System.out.print("请输入你想要的手机号码的编号:");
                            choose = scanner.nextInt();
                            if (choose > maxIndex) {
                                System.out.println("请重新输入!");
                            }
                        } while (choose > maxIndex);
                        //精准定位选中的手机号码
                        card_number = cards.get(choose + (page - 1) * 10 - 1).getCard_number();
                        updateCard(1);
                        break;
                    default:
                        page = 1;
                        num = 0;
                        chooseCard(cards, page, num);
                }
                bo = false;
            }
        } while (bo);
    }
}

5.3 话费充值

主要思路:根据电话号码,输入要充值的金额数,然后修改数据库的数据,如果手机号码被冻结,如果账户余额大于0就解冻。

代码演示:

private void rechargeSoso(String card_number) {
        RechargeRecord record = new RechargeRecord();
        System.out.print("请选择你的充值金额:");
        Double amount = scanner.nextDouble();
        //充值,修改数据库中的数据
        moboleCardService.rechargeSoso(card_number, amount);
        //读取数据库中的数据,获取用户数据
        moboleCard = moboleCardService.freeze(card_number);
        if (moboleCard != null) {
            //如果手机号码是冻结状态,且余额大于0
            if (moboleCard.getStatus() == 1 && (moboleCard.getMoney() + amount) > 0) {
                moboleCard.setStatus(0);
                //如果修改数据成功
                if (moboleCardService.update(moboleCard) == 1) {
                    System.out.println("手机号码:" + card_number + "已成功解冻!");
                }
            }
        }
        //添加充值数据
        record.setCard_number(card_number);
        record.setAmount(amount);
        if (rechargeRecordService.insert(record) == 1) {
            System.out.println("充值成功!");
        }
        System.out.println();
    }

5.4 套餐说明

主要代码:

private void showPackage() {
        System.out.println("************************套餐说明*****************************");
        serPackages = serPackageService.getPackageType();
        //循环输出读到的数据
        for (SerPackage serPackage : serPackages) {
            System.out.println("套餐" + serPackage.getId() + "---" + serPackage);
        }
    }

5.5 使用嗖嗖

主要思路:用Random获取一个随机数,随机进行一个场景,并输出。然后根据这个场景的类别分别进行数据的修改。算消费金额时有三种情况,第一种是消费的属性的量没有超过套餐给的量,就不计算金额,仅计算消费的属性的量;第二种是在消费之前,套餐还有余额,消费后套餐余额不足还超出消费,就要计算超出量的金额;第三种是套餐本来就没有余额,直接计算消费的金额。为区分这三种情况,我是先计算使用后的总消费量,如果总消费量没超出套餐提供的量,就是第一种情况,超出套餐提供的量,再判断使用前套餐剩余量是否大于0,大于0就是第二种情况,小于零就是第三种情况,然后再分别计算消费金额。最后再判断一下卡是否欠费,是否被冻结(欠费超50,自动冻结)。

场景展示:
在这里插入图片描述

主要代码(以通话为例,流量和短信与之类似):

System.out.println("***********欢迎使用嗦嗦*************");
        List<Scene> scenes = sceneService.queryAll();
        Random random = new Random();
        int choose = random.nextInt(scenes.size());
        //获取随机场景并输出
        String type = scenes.get(choose).getType();
        System.out.println(scenes.get(choose).getDescription());
        double money = 0;
        if (type.equals("通话")) {
            int before = monthlyConsumptionRecord.getReal_talk_time();
            //计算原套餐还剩下多少
            int balance = serPackages.get(mcp.getSer_package() - 1).getTalk_time() - before;
            //将总通话时间计算出来传入对象
            monthlyConsumptionRecord.setReal_talk_time(before + scenes.get(choose).getData());
            //如果总使用通话时间超过套餐所提供的通话时间,扣钱
            if (monthlyConsumptionRecord.getReal_talk_time() > serPackages.get(mcp.getSer_package() - 1).getTalk_time()) {
            
                //计算当月消费金额
                if (balance > 0) {
                    money = (monthlyConsumptionRecord.getReal_talk_time() - serPackages.get(mcp.getSer_package() - 1).getTalk_time()) * 0.2;
                } else {
                    money = scenes.get(choose).getData() * 0.2;
                }
                monthlyConsumptionRecord.setConsum_amount(monthlyConsumptionRecord.getConsum_amount() + money);
                //扣钱
                moboleCard.setMoney(moboleCard.getMoney() - money);
            }
        }

5.6 本月账单查询

主要代码:

private void selectBill() {
        System.out.println("*********本月账单**********");
        System.out.println("您的卡号:" + card_number + ",当月账单:");
        //mcp为全局对象,通过多表查询存储本月账单信息
        System.out.println("套餐资费:" + mcp.getPrice());
        System.out.println("合计:" + (mcp.getPrice() + mcp.getConsum_amount()));
        System.out.println("账号余额:" + mcp.getMoney());
        secondMenu();
    }

5.7 套餐余量查询

主要代码:

private void remainder() {
        int callTime;
        int sms;
        int flowRate;
        List<MonthlyConsumptionRecord> monthlyConsumptionRecords = monthlyConsumptionRecordService.queryById(card_number);
        //通过电话号码查询的list数据里只存储了一条数据,运用索引0获取
        MonthlyConsumptionRecord monthlyConsumptionRecord = monthlyConsumptionRecords.get(0);
        System.out.println("*********余量查询**********");
        System.out.println("您的卡号" + card_number + ",套餐内剩余:");
        //计算通话时长剩余量
        if ((callTime = (serPackages.get(mcp.getSer_package() - 1).getTalk_time() - monthlyConsumptionRecord.getReal_talk_time())) < 0) {
            callTime = 0;
        }
        System.out.println("通话时长:" + callTime + "分钟");
        if ((sms = (serPackages.get(mcp.getSer_package() - 1).getSms_count() - monthlyConsumptionRecord.getReal_SMS_count())) < 0) {
            sms = 0;
        }
        System.out.println("短信条数:" + sms + "条");
        if ((flowRate = (serPackages.get(mcp.getSer_package() - 1).getFlow() - monthlyConsumptionRecord.getReal_flow())) < 0) {
            flowRate = 0;
        }
        System.out.println("上网流量:" + flowRate / 1024 + "GB");
        secondMenu();
    }

5.8 消费详情

private void consumeList() {
        BufferedWriter bw = null;
        StringBuffer stringBuffer = new StringBuffer();
        System.out.println("*********消费详情查询**********");
        System.out.print("请输入本年要查询的月份(1-12):");
        int month = scanner.nextInt();
        //获取对应月份的消费数据
        List<ConsumInfo> consumInfos = consuminfoService.selectData(card_number, month);
        if (consumInfos != null && !consumInfos.isEmpty()) {
            System.out.println("序号\t类型\t数据\t日期");
            int i = 1;
            //循环打印数据
            for (ConsumInfo consumInfo : consumInfos) {
                System.out.println(i + "\t" + consumInfo);
                stringBuffer.append(i).append("\t").append(consumInfo).append("\n");
                i++;
            }
            System.out.println("请问需要打印消费清单吗?(1.需要 2.不需要)");
            if (scanner.nextInt() == 1) {
                try {
                    //根据手机号打印到对应的文件中
                    bw = new BufferedWriter(new FileWriter("D:\\飞思---\\项目\\嗦嗦移动大厅\\客户消费记录\\" + card_number + ".txt"));
                    bw.write(String.valueOf(stringBuffer));
                    bw.newLine();
                    bw.flush();
                    System.out.println("打印完成");
                } catch (IOException e) {
                    throw new RuntimeException(e);
                } finally {
                    try {
                        if (bw != null) {
                            bw.close();
                        }
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        } else {
            System.out.println("[友情提示]:对不起,不存在本卡号" + month + "月消费记录");
        }
        secondMenu();
    }

5.9 套餐变更

主要思路:根据用户的选择,如果用户本来就是这个套餐,就不可改变套餐。如果套餐可以改变,就判断用户余额能否满足套餐所需月租,不足就让用户选择是否充值,不充值就退出,充值就修改数据库数据。

主要代码:

private void packageAlter() {
        boolean bo = true;
        System.out.println("*********套餐变更**********");
        showPackage();
        moboleCards = moboleCardService.queryById(card_number);
        moboleCard = moboleCards.get(0);
        System.out.print("请选择(序号):");
        int packageNum = scanner.nextInt();
        if (moboleCard.getSer_package() == packageNum) {
            System.out.println("[友情提示]:您已经是该套餐的用户,无需更换!");
        } else {
            while (moboleCard.getMoney() < serPackages.get(packageNum - 1).getPrice()) {
                System.out.println("[友情提示]:对不起,您的余额不足以支付新套餐本月资费,请充值后办理变更套餐业务!");
                bo = false;
                System.out.print("是否充值(1.不充值 其他键即为充值):");
                if (scanner.nextInt() != 1) {
                    rechargeSoso(card_number);
                    bo = true;
                }
            }
            if (bo) {
                SerPackage serPackage = serPackages.get(packageNum - 1);
                moboleCard.setMoney(moboleCard.getMoney() - serPackage.getPrice());
                moboleCard.setSer_package(packageNum);
                moboleCard.setCard_number(card_number);
                if (moboleCardService.update(moboleCard) == 1) {
                    System.out.println("[友情提示]:更换套餐成功!" + serPackage);
                }
            }
        }
        secondMenu();
    }

5.10 办理退网

主要代码:

private void exit() {
        System.out.println("\n你确认要退网吗:");
        System.out.println("请选择(1.确认 其他键为取消):");
        if (scanner.nextInt() == 1) {
            cardService.updateCard(card_number, 0);
            List<ConsumInfo> consuminfos = consuminfoService.selectDates(card_number);
            List<MonthlyConsumptionRecord> monthlyConsumptionRecords = monthlyConsumptionRecordService.queryById(card_number);
            List<RechargeRecord> rechargeRecords = rechargeRecordService.queryById(card_number);
            int consuminfo = consuminfoService.deleteConsuminfo(card_number);
            int moboleCard = moboleCardService.deleteMoboleCard(card_number);
            int monthlyConsumption = monthlyConsumptionRecordService.deleteMCR(card_number);
            int rechargeRecord = rechargeRecordService.deleteRechargeRecord(card_number);
            if (consuminfo == consuminfos.size() && moboleCard == 1 && monthlyConsumptionRecords.size() == monthlyConsumption && rechargeRecords.size() == rechargeRecord) {
                System.out.println("注销成功");
                firstMenu();
            } else {
                System.out.println("注销失败");
            }
        }
        secondMenu();
    }

5.11 自动收取月租和每月自动生成消费数据

主要思路:用两个循环读取数据,一个是读取用户数据表中的手机号,一个是消费数据表的数据,如果当月本电话号码存在消费数据就不添加,不存在就添加上默认数据。然后通过LocalDate获取当前时间然后获取当前的日,如果是一号,就对每个手机号的进行扣月租的处理。

主要代码:

@Override
public void atuoAddData() {
        //用来添加数据
        MonthlyConsumptionRecord monthly = new MonthlyConsumptionRecord();

        //用来转换日期
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
        LocalDate now = LocalDate.now();
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM");
        String nowMonth = dtf.format(now);

        Mobole_CardDaoImpl moboleCardDao = new Mobole_CardDaoImpl();
        List<Mobole_Card> moboleCards = moboleCardDao.queryAll();

        MonthlyConsumptionRecordsDaoImpl monthlyConsumptionRecordsDao = new MonthlyConsumptionRecordsDaoImpl();
        List<MonthlyConsumptionRecord> monthlyConsumptionRecords = monthlyConsumptionRecordsDao.queryAll();

        //遍历用户表
        for (Mobole_Card mobole_Card : moboleCards) {
            boolean bo = true;
            String card_number = mobole_Card.getCard_number();
            //遍历消费数据表
            for (MonthlyConsumptionRecord monthlyConsumptionRecord : monthlyConsumptionRecords) {
                //判断用户表中的电话号码是否在消费数据表中有当月的记录
                if (mobole_Card.getCard_number().equals(monthlyConsumptionRecord.getCard_number()) && sdf.format(monthlyConsumptionRecord.getConsume_date()).equals(nowMonth)) {
                    bo = false;
                }
            }
            if (bo) {
                //传默认值
                monthly.setCard_number(card_number);
                monthly.setReal_talk_time(0);
                monthly.setReal_SMS_count(0);
                monthly.setReal_flow(0);
                monthlyConsumptionRecordsDao.insert(monthly);
                //收月租
                monthly.setConsum_amount(monthlyRent(mobole_Card.getCard_number()));
                monthlyConsumptionRecordsDao.update(monthly);
            }
        }
    }

收月租的代码:

    @Override
    public Double monthlyRent(String card_number) {
        MoboleCard moboleCard=new MoboleCard();
        MoboleCardDaoImpl moboleCardDao = new MoboleCardDaoImpl();
        List<MoboleCard_Package> moboleCardPackages = moboleCardDao.getMessage(card_number);
        //判断读的数据是否为空
        if (moboleCardPackages != null && !moboleCardPackages.isEmpty()) {
            //存储多表查出来的数据
            MoboleCard_Package moboleCardPackage = moboleCardPackages.get(0);

            //获取现在的时间对应的天
            LocalDate now = LocalDate.now();
            DateTimeFormatter dtf = DateTimeFormatter.ofPattern("d");
            String nowDay = dtf.format(now);
            //如果是一号
            if (nowDay.equals("1")) {
                //将数据传入对象,防止修改表内不该改的数据
                moboleCard.setCard_number(card_number);
                moboleCard.setUsername(moboleCardPackage.getUsername());
                moboleCard.setPassword(moboleCardPackage.getPassword());
                moboleCard.setSer_package(moboleCardPackage.getSer_package());
                //扣钱
                moboleCard.setMoney(moboleCardPackage.getMoney()-moboleCardPackage.getPrice());
                if(moboleCard.getMoney()<-50){
                    moboleCard.setStatus(1);
                }else {
                    moboleCard.setStatus(0);
                }
                //修改数据
                if(moboleCardDao.update(moboleCard)!=1){
                    throw  new RuntimeException("月租没收成");
                }
                return moboleCardPackage.getPrice();
            }
        }
        return 0.0;
    }
}

6.总结

这个小项目,灵活的运用了面向对象的思想,三层架构的思想和JDBC对数据库的操作。
不过还有很多不足的地方,首先对数据库的处理方面,没有进行回滚操作,一个操作存在多个sql语句,如果前几个sql语句正常执行,后面的出了异常,数据就会出问题。然后就是使用嗖嗖模块太死板了,没有真正的场景操作,只是根据自己存在数据库的场景,进行计算收费等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值