网吧计费管理系统开发历程与源码分享(武汉理工大学程序设计综合实验课程设计)

那么,就让我们来谈一谈这个计费管理系统吧!一开始,我就充满了激情和热情,准备写出一份完美无缺的代码。不过,事情往往不是那么容易的,我很快就发现了一些困难。

首先,添加卡的功能是比较简单的,只需要在数据库中新增一条记录即可。不过,查询卡的功能就比较复杂了,因为用户可能会输入精准的卡号,也可能只输入一部分。为了解决这个问题,我决定采用模糊查询的方式,让用户输入的关键字与数据库中所有的卡号进行匹配,然后返回所有匹配到的结果。这样一来,就可以同时满足用户的需求了。

另外,激活、注销、挂失、上机、下机等功能也都比较好实现。其中,上机和下机的时候需要统计费用,还要随机打折优惠,这个我觉得可以用一个算法来实现。不过,如果用户欠费了,就需要处理异常情况了。这个时候,我决定将欠费的用户记录到黑名单中,以便以后做出相应的处理。

退费和转账这两个功能也比较简单,只需要在数据库中做出相应的修改即可。展示卡信息、删除卡、查询统计营业额这些功能也不难实现。最后,管理员设计和聊天室设计,我觉得可以考虑使用一些现有的开源库来实现。

不过,编写这个计费管理系统还是有一些困难的。比如,我在处理异常情况的时候,遇到了一些棘手的问题。我一开始是想将欠费的用户直接从数据库中删除,但是后来发现这样会对数据的完整性造成一定的影响。所以,我决定将欠费的用户记录到黑名单中,并对黑名单进行特殊处理,这样可以保证数据的完整性。

还有,当用户输入模糊关键字的时候,需要使用模糊匹配算法来查询数据库,这个算法也不是那么好实现。我调研了一下,发现可以使用正则表达式来实现模糊匹配。不过,正则表达式的语法比较复杂,需要花费一些时间来学习和理解。

在编写过程中,我也学到了很多东西,比如如何优化算法,如何处理异常情况,如何提高代码的可读性和可维护性等等。我觉得,编写这个计费管理系统的关键是要有耐心和毅力,不怕遇到问题,敢于尝试各种不同的方法。同时,也要善于利用现有的资源,比如开源库、论坛、博客等等,以便更好地解决问题。

编程语言的选择

当初为什么选择用C++而不是C语言开发这个系统?毕竟那老掉牙的经典视频教程可是用纯C写的呀!

哈哈,这个问题好玩!当然,我选择用C++而不是C语言开发这个系统,主要是因为C++比C更加强大和灵活。

首先,C++可以支持面向对象的编程方式,这使得代码的组织和维护更加方便。与C语言相比,C++中的类和对象等概念可以更好地抽象现实世界的问题,使得代码更加易于理解和扩展。

其次,C++中还包含了许多有用的功能和库,比如STL和模板等。这些功能和库可以帮助我们更加高效地开发代码,减少出错的概率。

此外,C++也是一门被广泛使用的语言,拥有大量的开发人员和社区支持。这意味着在开发过程中,我们可以轻松地获取到各种教程、文档、工具等等,以便更好地完成开发任务。

当然,如果我只是要写一些简单的程序,也可以选择使用C语言。不过对于这个计费管理系统来说,我认为使用C++是更好的选择。毕竟,我们不仅要编写代码,还要关注系统的性能、可扩展性等等因素,这些都需要C++的强大能力来支持。使用C++而不是C语言开发这个计费管理系统,不仅是因为C++更加强大和灵活,也是因为它能够更好地满足我们的开发需求。

系统功能模块化

为什么将系统功能封装成不同的类来调用实现呢?

首先,我选择将系统功能封装成不同的类来调用实现,主要是为了实现代码的模块化和可维护性。通过将功能进行分离和封装,我们可以更好地管理代码,减少出错的概率,并且便于维护和升级。

其次,面向对象编程的一个重要思想就是“单一职责原则”。这意味着每个类应该只负责一项职责,以便代码更加清晰易懂。如果将所有的功能都放在一个类中实现,那么会导致代码臃肿,可读性差,不利于后续的开发和维护。

另外,使用类来封装功能,还可以使得代码更加灵活。如果我们需要添加新的功能或者修改某个功能的实现,只需要修改对应的类即可,不需要对其他代码进行大规模修改。这也使得整个开发过程更加高效和安全。

当然,作为一名懒惰又勤奋的程序员,我最大的愿望就是写尽可能少的代码。通过将功能封装成不同的类,我们可以避免重复编写代码,并且提高代码的复用率。这样一来,我们可以写更少的代码,但实现更多的功能,岂不美哉!

综上所述,我选择将系统功能封装成不同的类来调用实现,主要是为了实现代码的模块化和可维护性,以及使得代码更加灵活和高效。当然,还有一点点懒惰的想法,我相信这也是大多数程序员的共同心声吧!

模块调用的大致思路

首先,我想把整个系统分成三个主要的模块:计费管理模块、卡管理模块和聊天室模块。其中,计费管理模块负责统计每张卡的使用情况,卡管理模块负责管理每张卡的信息和状态,聊天室模块负责实现聊天功能。

接下来,我发现计费管理模块和卡管理模块是高度相关的,因为每张卡的使用情况都需要记录在卡的信息中,所以我决定将这两个模块合并成一个模块,并且将其实现为一个名为“BillingSystem”的类。

而“Card”类则是用来表示每张卡的信息和状态的,例如卡号、姓名、余额等等。在“BillingSystem”类中,我通过维护一个“Card”类的vector来实现卡的增删查改等功能。

为了使代码更加清晰易懂,我还创建了一个名为“Card_Service”的类,用于封装一些与卡相关的服务功能,例如卡的充值、退款、上机、下机等等。

最后,我实现了一个名为“ChattingRoom”的类,用于实现聊天室的功能。这个类通过调用函数接口,实现了多个用户之间的聊天功能,让用户可以更好地交流和互动。

我的分模块思路具体就是先将整个系统分成三个主要模块,然后根据模块之间的关系将计费管理模块和卡管理模块合并为一个模块,并通过创建“Card”和“Card_Service”类来实现相关功能,最后实现聊天室模块。我相信,通过这样的模块化设计,我们可以更好地管理代码,提高代码的可维护性和复用性,并且使得开发过程更加愉快和高效!

文件的读写操作

首先,文件的存取可以帮助我们在程序结束之后将数据保存到本地,从而保证数据不会因为程序的关闭而丢失。例如,在我们的计费管理系统中,我们需要记录每张卡的使用情况、余额等信息,如果不进行文件存取,这些信息只能在程序运行期间保存在内存中,一旦程序结束,这些信息就会丢失。

其次,文件的存取也可以帮助我们在程序启动时从文件中读取之前保存的数据,从而恢复程序上次关闭时的状态。这对于用户来说是非常方便的,因为他们不需要每次都重新输入卡的信息或者从头开始聊天,可以直接接着上次的操作进行。

如果我们不实现文件的存取,那么程序每次启动时都需要用户重新输入之前的数据,这不仅浪费用户的时间,而且也可能会因为输入错误等原因导致数据丢失,影响用户体验。

文件的存取非常重要,可以帮助我们保护数据,恢复程序状态,提高用户体验。如果不进行文件存取,我们就可能会面临数据丢失、用户体验不佳等问题,这是我们不想看到的。

主函数的设计

啊,这个main函数啊,是这个系统的大脑啊。它调用了BillingSystem、ChattingRoom、UserMenu、Card_Service四个类来实现各种功能。哎呀,感觉我就像是一个神奇的调度员啊,让各个模块一起协作,才能让这个系统运转起来。

要说难点和头疼的地方嘛,那就是文件的保存和读取啊。这个系统的各种信息都需要保存在文件中,可是一不小心就会出错啊。你说我要是给你看这个main函数一堆读取和保存文件的代码,你会不会吓得跑路啊?所以啊,我就想到了把这个麻烦的事情交给BillingSystem这个类去做。这样就可以让代码更加简洁明了,让我这个调度员更加从容自信了。

至于这个系统的其他模块嘛,其实也都是有很明确的功能的。BillingSystem就是负责卡片的管理,ChattingRoom就是实现了聊天室的功能,UserMenu就是显示菜单和接收用户输入,Card_Service则是提供了一些卡片的服务。把这些功能都分开来,不仅可以让代码更加清晰,也更加方便维护和修改。

说到开发难点,我倒觉得是实现卡片之间的转账功能啊。这个功能看起来简单,但其实牵涉到了卡片余额的修改,还要涉及到密码验证、账户检查等等。所以,我就设计了一个transferBalance的函数,让它能够自动处理这些问题。这样,用户就可以轻松愉快地完成卡片之间的转账啦!

Main.cpp

#include <iostream> // 这个是用来输入输出的头文件,cin和cout就在里面
using namespace std; // 哦,不用自己敲std::cout了,自动给你加上std::

#include "BillingSystem.h" // 自己写的类
#include "ChattingRoom.h" // 还有一个
#include "UserMenu.h" // 我们还要写一个菜单,因为我们的用户太懒了,不愿意记住数字

#include "Card_Service.h" // 这个又是干嘛的呢?谁知道呢。。。因为我懒得看了

int main() {
    BillingSystem billingSystem; // 实例化一个计费系统,这个系统可以让你随心所欲的花钱

    double costPerMinute = 0.1; // 一分钟多少钱?这个可以自己随便定,反正不是你的钱

    int choice; // 一个整数,记录用户的选择,很重要,要好好珍惜

    //测试段
    BillingSystem bs; // 另外一个计费系统
    bs.addCard("123456", "John Doe", (string &) "password"); // 给这个系统加上一些卡号和密码,不知道这些密码是不是"password"
    bs.addCard("789012", "Jane Due", (string &) "password");
    bs.addCard("345678", "John Dmith", (string &) "password");
    bs.addCard("367548", "John Smith", (string &) "password");

    vector<Card*> mCards = bs.findCardsByIdSubstring("D"); // 这个是在查找名字里有"D"的卡号
    for (Card*& card : mCards) {
        cout << card->getId() << " " << card->getName() << "\n"; // 输出卡号和名字,嗯,很好,看起来有很多朋友啊
    }

    while (true) { // 循环一下,因为我们想不断的让用户花钱啊

        printMenu(); // 调用这个函数,输出一个菜单,让用户选择他想干嘛

        cin >> choice; // 让用户输入他的选择,我们会把这个记录下来,等等会用到

        string id, name, password; // 一些字符串变量,用来存储一些信息
        double amount; // 还有一个浮点数,存储钱的数量
        Card* card; // 这个是个指针,指向一张卡

        vector<Card> cards; // 一个卡的集合,是不是很有趣?

        switch (choice) { // 根据用户的选择,我们来执行不同的操作

            case 1:
                cout << "Enter card ID: ";
                cin >> id;
                cout << "Enter card holder's name: ";
                cin.ignore();
                getline(cin, name);
                cout << "Enter card holder's password: ";
                getline(cin, password);

                // 添加卡片,这里其实是暗示你:好像是我自己的钱似的,反正是我要管这些卡
                billingSystem.addCard(id, name, password);
                cout << "Card added successfully!\n";
                break;

           case 2: {
                cout << "1. Precise Query 2. Blurry Query" << endl;
                cout << "Which One? ";
                int queryChoice;
                cin >> queryChoice;
                cout << "Enter card ID: ";
                cin >> id;

                switch (queryChoice) {
                    case 1:
                    // 查找一张卡片(ID精确匹配),这里其实是暗示你:找出谁欠我钱了
                        card = billingSystem.findCard(id);
                        if (card) {
                            cout << "Card found!\n";
                            printCard(card);
                        } else {
                            cout << "Card not found!\n";
                        }
                    break;
                case 2:
                   // 查找卡片(ID模糊匹配),这里其实是暗示你:谁还欠我钱呢?
                        vector<Card *> matchingCards = billingSystem.findCardsByIdSubstring(id);
                    if (!matchingCards.empty()) {
                        cout << "Cards found!\n";
                        printAllCards(matchingCards);
                    } else {
                        cout << "Cards not found!\n";
                    break;
                    }
            break;

        case 3:
            cout << "Enter card ID: ";
            cin >> id;
            // 激活卡片,这里其实是暗示你:收钱收到手软了,我要给这些钱找个家
            if (billingSystem.activateCard(id)) {
                 cout << "Card activated successfully!\n";
            } else {
                cout << "Card activation failed!\n";
            }
            break;

        case 4:
            cout << "Enter card ID: ";
            cin >> id;
            cout << "Enter card Password: ";
            cin>> password;
            card = billingSystem.findCard(id);
            if (card && billingSystem.checkPassword(card,password) &&             billingSystem.rechargeCard(card)) {
            // 给卡片充值,这里其实是暗示你:收钱收到手软了,我要给这些钱找个家
            cout << "Card recharged successfully!\n";
            } else {
            // 充值失败,这里其实是暗示你:他欠我钱,结果连还钱的本事都没有
            cout << "Recharge failed!\n";
            }
            break;
        case 5:
            cout << "Enter card ID: ";
            cin >> id;
            cout << "Enter card Password: ";
            cin >> password;
            cout << "Enter refund amount: ";
            cin >> amount;
            // 退款嘛,有钱就是任性,没钱就是调皮。先输入卡号,再输密码,最后输入退款金额。注意:一定要输正整数,不能输小数,否则可能被误认为刻意设置的Bug。
            card = billingSystem.findCard(id);
            // 先判断卡片是否存在,再判断密码是否正确,最后进行退款操作
            if (card && billingSystem.checkPassword(card, password) && billingSystem.refund(card, amount)) {
// 这里有个小技巧,先用findCard函数查找卡片是否存在,再用checkPassword函数检查密码是否正确,最后进行退款操作。没必要重复发明轮子。
            cout << "Refund successful!\n";// 如果退款成功,就输出“Refund successful!”,反之输出“Refund failed!”。切记,千万别把成功和失败的输出颠倒了。
            } else {
            cout << "Refund failed!\n";
            }
            break;

       case 6:
            cout << "Enter card ID: ";
            cin >> id;
            cout << "Enter card password: ";
            cin >> password;

            //首先,我们需要找到目标卡。如果我们找到了它,并且会话成功开始、密码校验通过以及验证码也正确,那么我们就可以说“Session started successfully(会话成功开始)!”。
            if (billingSystem.findCard(id)&& billingSystem.startCardSession(card)&&
            billingSystem.checkPassword(card,password)&&
    billingSystem.checkVerificationCode()) {
            // 若卡被找到,并且会话开始、密码校验通过且验证码也正确
        cout << "Session started successfully!\n";
    } else {
           // 哦,如果找不到目标卡,或者会话没有成功开始,或者密码校验不通过,或者验证码不正确,我们会输出“Failed to start session(会话开始失败)!”。
            cout << "Failed to start session!\n";
    }
            break;

        case 7:
            cout << "Enter card ID: ";
            cin >> id;
             //我们处理的是结束卡片的会话。这个操作的难点在于如何将会话记录存储在系统中以供后续使用,而且我们还需要计算出本次会话的费用。
            card = billingSystem.findCard(id);
            //调用了BillingSystem的endCardSession()方法来结束会话,并传递了记录、每分钟的通话费用等参数。
            if (card && billingSystem.endCardSession(card, billingSystem.getRecords(), 0.1)) {

            cout << "Session ended successfully!\n";
            } else {
            cout << "Failed to end session!\n";
            }
            break;

        case 8:
            cout << "Enter card ID: ";
            cin >> id;
            card = billingSystem.findCard(id);
            if (card) {
                card->cancel();
                cout << "Card canceled successfully!\n";
                } else {
                cout << "Failed to cancel card!\n";
                }
            break;


        case 9:
            cout << "Enter card ID: ";
            cin >> id;
            cout << "Enter card Password: ";
            cin >> password;
            card = billingSystem.findCard(id);
            if (card && billingSystem.checkPassword(card, password)) {
                if (billingSystem.isInBlacklist(card->getName())) {
                    cout << "Cannot operate with this card as its owner is in the blacklist. Contact the manager!\n" << endl;
                break;
            }
            card->lost();
            cout << "Card reported as lost successfully!\n";
            } else {
            cout << "Failed to report card as lost!\n";
            }
          break;
        case 10:
            cout << "Enter card ID: ";
            cin >> id;
            cout << "Enter card Password: ";
            cin>> password;
            card = billingSystem.findCard(id);
            if (card&&billingSystem.checkPassword(card,password)) {
                if(billingSystem.isInBlacklist(card->getName())){
                    cout<<"Cannot operate with this card as its owner is in the blacklist.Contact the manager!\n"<<endl;
                    break;
                }
                card->lost();
                cout << "Card reported as lost successfully!\n";
            } else {
                cout << "Failed to report card as lost!\n";
            }
            break;

        // “Case 10, 如果你的卡丢了,可以在这里挂失。如果你不是你,但你试图挂失,那么你就像是一只企图跳过一只蜜蜂的兔子,最终会被抓住的。”

         case 11:
            deleteAllCards();
            break;

        // "Case 11, 通过删除所有卡片来清空所有记录。这就像是打开电视机,但是你没开机,而是直接把电视砸了,这样你就解决了你的问题,但是你也不再有电视了。"

         case 12:
             printAllCards();
             break;

        // “Case 12, 打印所有的卡片信息。这就像是在拿着万能胶水尝试修复踏板的电梯,但实际上你只是将踏板粘在了地上,让所有的乘客都能踩在它上面。”

         case 13:
            billingSystem.queryRecords();
            break;

        // “Case 13, 查询所有的通话记录,就像是探索无人区,但是你不知道在那里会发生什么,或者是否能够幸存下来。”

        case 14:
            billingSystem.loginAdmin();
            break;

        // “Case 14, 管理员登陆界面。这就像是一个非常棒的毕业典礼,你感觉你会获得一个大奖,但实际上你只是收到了一份领取毕业证书的指南。”

        case 15:
            billingSystem.enterChattingRoom();
            break;

        // “Case 15, 进入聊天室。这就像是去了一个酒吧,你遇到了新的人,你喝了一些饮料,但是你最终离开时,你忘记了他们的名字,因为你醉得一塌糊涂。”

        case 16: {
            // 开始转账操作,需要输入转出账户、密码、转入账户、转账金额
            string fromAccount, toAccount, password;
            double amount;
            cout << "Enter the account to transfer from: ";
            cin >> fromAccount;
            cout << "Enter the password: ";
            cin >> password;
            cout << "Enter the account to transfer to: ";
            cin >> toAccount;
            cout << "Enter the amount to transfer: ";
            cin >> amount;

            // 根据转出账户查找对应的卡片信息
            Card* fromCard = billingSystem.findCard(fromAccount);

            // 如果转账金额大于0,且卡片信息存在,且密码正确,则进行转账操作
            if (amount > 0 && fromCard && billingSystem.checkPassword(fromCard, password)) {
                if (billingSystem.transferBalance(fromCard, toAccount, amount)) {
            cout << "Thanks for using our transfer system.\n";
            return true;
                }
            }
            break;
        }

        case 17:
            // 打印系统版本号
            cout << "Current system version: " << SYSTEM_VERSION << endl;
            break;

        case 18:
            // 退出系统
            exit(0);
            break;

        default:
            // 对于非法的选项进行提醒
            cout << "Invalid choice. Please try again.\n";
            break;

}

用户菜单设计

首先,你得有一个好看的菜单,不能太花里胡哨,也不能太简陋,就像我家养的那只猫一样,看起来不起眼,但是非常可爱!

然后,你需要实现一些功能,比如添加卡片、充值、退款、开始会话、结束会话等等,这就像你要养猫一样,要给它喂食、清理猫砂、给它洗澡等等,不能忘记任何一个步骤哦!

有时候,你也需要删除所有卡片,这就像你要把猫送回宠物店一样,有点可惜,但有时候也是必要的!

如果你想看看所有的卡片信息,你可以让程序帮你打印出来,这就像你想看看别人的猫一样,有点好奇,但又不敢问!

最后,如果你有问题,你可以打开聊天框,就像你和猫进行心灵交流一样,虽然猫不一定能听懂你的话,但它一定能感受到你的爱!

好了,这就是我为大家讲解如何设计UserMenu,希望大家都能养出一只可爱的猫咪,或者管理好自己的账户,哈哈哈!

UserMenu.h

// 保险起见,加个#pragma once,避免头文件被重复引用导致编译出现不可描述的错误
#pragma once

#ifndef USERMENU_H
#define USERMENU_H

#include "Card.h"

// 打印主菜单,千万不要加辣椒,不然口渴难忍
void printMenu();

// 删除所有的卡片,小心不要删错了,删了系统崩了就不好玩了
void deleteAllCards();

// 打印出所有的卡片,看起来比直接一个一个慢慢翻要方便多了,但要是卡片太多就别打印了,容易眼花缭乱
void printAllCards();

#endif //BILLINGSYSTEM_USERMENU_H

UserMenu.cpp

#include "UserMenu.h"
#include "BillingSystem.h"

// 打印主菜单,其实也就是一个死循环
void printMenu() {
    cout << "=====================================\n";
    cout << "Billing Management System - Main Menu\n";
    cout << "=====================================\n";
    cout << "1. Add a card\n"; // 添加一张卡
    cout << "2. Find a card\n"; // 寻找一张卡
    cout << "3. Activate a card\n"; // 激活一张卡,不是你想的那种激活
    cout << "4. Recharge a card\n"; // 给卡充值
    cout << "5. Refund a card\n"; // 退卡
    cout << "6. Start a session\n"; // 开始会话,别想歪了
    cout << "7. End a session\n"; // 结束会话,真的别想歪
    cout << "8. Cancel a card\n"; // 取消卡
    cout << "9. Report a lost card\n"; // 报失卡,也不是你想的那种失
    cout << "10. Show card information\n"; // 显示卡信息
    cout << "11. Delete all cards\n"; // 删除所有卡,千万别误删
    cout << "12. Show all cards\n"; // 显示所有卡
    cout << "13. Show total revenue\n"; // 显示总收入,也不是收什么的,别想歪了
    cout << "14. Admin Manager\n"; // 管理员管理
    cout << "15. Open ChatBox\n"; // 打开聊天室,千万别误入
    cout << "16. Transfer balance to another user\n"; // 转移余额给别人
    cout << "17. An Easter egg\n"; // 彩蛋,但是我不告诉你在哪里
    cout << "18. Exit\n"; // 退出,可以放心离开了
    cout << "=====================================\n";
    cout << "Enter your choice: ";
}

// 删除所有卡,小心别误删了
void deleteAllCards() {
    char confirm;
    cout << "Are you sure you want to delete all cards? (Y/N): ";
    cin >> confirm;
    if (confirm == 'Y' || confirm == 'y') {
        BillingSystem billingSystem;
        billingSystem.deleteAllCards();
        cout << "All cards deleted successfully!\n";
    } else {
        cout << "Operation canceled.\n";
    }
}

void printAllCards() {
    BillingSystem billingSystem; // 假装我有无限钱财
    vector<Card*> cards = billingSystem.getAllCards(); // 抓取所有卡片信息
    if (cards.empty()) { // 如果没有卡片
        cout << "Sorry, it looks like our card kingdom is empty...no discounts today!\n"; // 对不起,我们的卡片王国好像是空的...今天没有折扣!
    } else { // 如果有卡片
        cout << "All hail the mighty cards in our kingdom!\n"; // 所有卡片的信息请在下方膜拜
        for (Card* card : cards) { // 遍历每张卡片
            cout << "ID: " << card->getId() << ", Name: " << card->getName() // 输出卡片ID和名称
                 << ", Balance: " << card->getBalance() <<", ExpirationTime: " << asctime(localtime(&card->getExpirationTime())) << ", Status: "; // 输出余额,到期时间和状态
            switch (card->getStatus()) { // 根据卡片状态
               case Card::INACTIVE: // 如果是未激活状态
                    cout << "Inactive"; // 输出未激活
                    break;
                case Card::ACTIVE: // 如果是激活状态
                    cout << "Active"; // 输出激活
                    break;
                case Card::CANCELED: // 如果是取消状态
                    cout << "Canceled"; // 输出取消
                    break;
                case Card::LOST: // 如果是丢失状态
                    cout << "Lost"; // 输出丢失
                    break;
            }
            cout << endl; 
        }
    }
}

用户卡类设计

你问为什么我要把系统功能封装成不同的类来调用实现呢?这不是显而易见的吗?因为如果我把所有的功能都塞进一个文件里,那这个文件就会变得越来越臃肿,最后就像我一样,胖到走路都费劲了。所以我才想到将它们分门别类地封装成不同的类,这样就像是把我身上的脂肪分成了不同的部位,显得更加健康有条理。当然,还有一个更加实际的原因,就是为了让程序更加易于维护和扩展,这样我就像是一位少女一样,保养得娇嫩有弹性,让人永远不会觉得我老态龙钟。

再来说说我怎么构思用户卡Card类吧。我知道我看起来可能有些单薄,但是作为一个“优秀”的程序员,我可是很有韧性的。我设计Card类的时候,首先想到的是这个类需要有哪些基本的属性和行为,比如卡号、姓名、余额、状态等等。然后我就开始把这些属性和行为封装到类里面,像是把猪肉包进馒头里一样,紧密地结合在一起。最后,我再对这些属性和行为进行了一番优化和细化,让这个类看起来更加美好动人,就像是一只粉嫩的小猪一样可爱。

Card.h

这里来一波详细的注释,看得懂就给个赞呗~

#pragma once  // 防止重复引用,等价于 #ifndef CARD_H #define CARD_H
#ifndef CARD_H // 如果没有定义 CARD_H,即进行定义操作
#define CARD_H

#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <ctime>
#include <iomanip>  // 用于设置输出时间的格式
#include "Record.h" // 引入Record.h文件

using namespace std;

class Card {
public:
    enum CardStatus { INACTIVE, ACTIVE, CANCELED, LOST };  // 定义卡片状态枚举类型,包括未激活、激活、注销、挂失
    enum BlackListStatus { NOT_IN_BLACKLIST, IN_BLACKLIST }; // 定义黑名单状态枚举类型,包括未加入黑名单、已加入黑名单

   // 构造函数,用于创建Card对象时初始化对象的成员变量
Card(const string& id, const string& name, string& password,time_t expirationTime):
        id_(id), name_(name), password_(password),balance_(0.0), status_(INACTIVE),
        startTime_(0),endTime_(0),totalTime_(0), expirationTime_(expirationTime),
        blstatus_(NOT_IN_BLACKLIST) {
    //将过期时间转换为本地时间
    struct tm* expirationTimeLocal = localtime(&expirationTime_);
    expirationTimeLocal->tm_isdst = -1; // 是否为夏令时
    expirationTime_ = mktime(expirationTimeLocal);
}

// 下面的代码是用来获取Card对象的成员变量的值,因此都是const类型的成员函数
const string& getId() const;
const string& getName() const;
const string& getPassword() const;
double getBalance() const;
CardStatus getStatus() const;
time_t& getStartTime();
time_t& getEndTime();
int getTotalTime() const;
time_t& getExpirationTime();
BlackListStatus getBlackListStatus() const;

// 下面的代码是用来设置Card对象的成员变量的值
void setId(const string& id);
void setName(const string& name);
void setPassword(string& password);
void setBalance(double balance);
void setStatus(CardStatus status);
void setStartTime(time_t startTime);
void setEndTime(time_t endTime);
void setTotalTime(int totalTime);
void setExpirationTime(time_t expirationTime);
void setBlackListStatus(BlackListStatus status);

// 下面的代码是用来修改Card对象的状态的函数
void activate();
void cancel();
void lost();

// 下面的代码是用来检查Card对象的状态是否为某个状态
bool isStatus(CardStatus status) const;

// 下面的代码是用来对Card对象进行充值和退款的函数
bool recharge(double amount);
bool refund(double amount);

// 下面的代码是用来处理Card对象的上机和下机过程的函数
bool startSession();
bool endSession(double costPerMinute,vector<Record>& record);

// 下面的代码是用来重新发卡的函数
bool reissue();

private:
// Card对象的私有成员变量
string id_; // 卡号
string name_; // 姓名
string password_; // 密码
double balance_; // 余额
CardStatus status_; // 状态
time_t startTime_; // 上机时间
time_t endTime_; // 下机时间
int totalTime_; // 上机时间总计
time_t expirationTime_; // 过期时间
BlackListStatus blstatus_; // 黑名单状态
};

#endif //BILLINGSYSTEM_CARD_H

哎呀呀,这可不是一个简单的问题啊!设计一个好的类接口可不是一件容易的事情,特别是当你还需要考虑到诸如时间戳转换等细节问题的时候!

首先,我们需要确保Card类的接口尽可能地简洁和易于理解,这样其他模块在调用Card类的时候才会更加方便。同时,我们还需要保证接口的兼容性和可扩展性,以便后期能够轻松地对系统进行更新和升级。

对于时间戳转换的问题,我不得不说这真的是一个令人头疼的难题。首先,我们需要了解一下时间戳是如何工作的,以及它们在不同系统之间的差异性。我们还需要考虑到夏令时和时区等问题,以确保我们的时间戳转换是准确无误的。

不过幸运的是,C++标准库已经为我们提供了许多方便的时间处理工具,例如tm结构体和localtime函数等等。因此,我们可以利用这些工具来实现我们的时间戳转换。当然,为了避免时间戳转换的困难和可能带来的错误,我们还需要对时间戳进行封装,确保它们只在Card类内部使用。

虽然遇到了一些困难,但最终还是成功地设计出了一个优秀的Card类接口,同时实现了时间戳转换等功能。现在,让我们坐下来,放松一下,一起大笑三声!哈哈哈!

Card.cpp

#include "Card.h"

// 获取卡号
const string& Card::getId() const { return id_; }

// 获取持卡人姓名
const string& Card::getName() const { return name_; }

// 获取密码
const string& Card::getPassword() const {return password_;}

// 获取余额
double Card::getBalance() const { return balance_; }

// 获取卡片状态
Card::CardStatus Card::getStatus() const { return status_; }

// 获取会话开始时间
time_t& Card::getStartTime() { return startTime_; }

// 获取会话结束时间
time_t& Card::getEndTime() { return endTime_; };

// 获取总使用时间
int Card::getTotalTime() const { return totalTime_; }

// 获取过期时间
time_t& Card::getExpirationTime()  {return expirationTime_;}

// 获取卡片黑名单状态
Card::BlackListStatus Card::getBlackListStatus() const {return blstatus_;}


// 设置卡号
void Card::setId(const string& id){ id_ = id; };

// 设置持卡人姓名
void Card::setName(const string& name){ name_ = name; };

// 设置密码
void Card::setPassword(string& password){password_ = password;};

// 设置余额
void Card::setBalance(double balance){ balance_ = balance; };

// 设置卡片状态
void Card::setStatus(CardStatus status){ status_ = status; };

// 设置会话开始时间
void Card::setStartTime(time_t startTime) { startTime_ = startTime; }

// 设置会话结束时间
void Card::setEndTime(time_t endTime) { endTime_ = endTime; }

// 设置总使用时间
void Card::setTotalTime(int totalTime) { totalTime_ = totalTime; }

// 设置过期时间
void Card::setExpirationTime(time_t expirationTime) {expirationTime_ = expirationTime;}

// 设置卡片黑名单状态
void Card::setBlackListStatus(BlackListStatus status) {blstatus_ = status;}


// 激活卡片
void Card::activate() { status_ = ACTIVE; }

// 取消卡片
void Card::cancel() { status_ = CANCELED; }

// 报告卡片丢失
void Card::lost() { status_ = LOST; }

// 判断卡片状态是否为给定状态
bool Card::isStatus(Card::CardStatus status) const { return status_ == status; }

/*充值函数,就像妈妈在给你的手机卡里打钱一样,需要注意卡的状态和余额。如果你的卡被加入黑名单了,那就联系客服吧,不要试图打钱,否则会出事情的。如果卡状态正常,那就把钱存进去,这时候卡的余额就会增加。如果你的卡被标记为丢失了,你可以选择申请一个新的卡,只要你有足够的余额支付 10 元的申请费。如果你的余额不足,那就算了,以后再说吧。如果你的卡被注销了,那就无法充值了,也别想了,省省吧。*/
bool Card::recharge(double amount) {
    if (getBlackListStatus() == IN_BLACKLIST) {
        cout << "This card is blacklisted. Please contact customer service for assistance." << endl;
        return false;
    } else if (isStatus(Card::ACTIVE)) {
        balance_ += amount;
        return true;
    } else if (isStatus(Card::LOST)) {
        cout << "This card is reported as lost. Do you want to apply for a new card? (Y/N)" << endl;
        char choice;
        cin >> choice;
        if (choice == 'Y' || choice == 'y') {
            balance_ += amount;
            if (balance_ >= 10.0) {
                balance_ -= 10.0;
                setStatus(Card::ACTIVE);
                cout << "Your Card has been activated and recharged " << endl;
            } else {
                cout << "Insufficient balance to apply for a new card, try again later!" << endl;
                balance_ -= amount;
                return false;
            }
        } else {
            return false;
        }
    } else if (isStatus(Card::CANCELED)) {
        cout << "Fail to recharge as this card is cancelled!" << "\n";
    } else {
        return false;
    }
}

bool Card::refund(double amount) {
    if (isStatus(ACTIVE) && balance_ >= amount) { // 判断卡片是否激活并且卡余额足够
        balance_ -= amount; // 扣除退款金额
        return true; // 退款成功
    }
    cout<<"Insufficient balance in card.Recharge needed."<<endl; // 卡余额不足,退款失败
    return false;
}
bool Card::startSession() {
    if (isStatus(ACTIVE) && balance_ > 0) { // 判断卡片是否激活并且卡余额大于0
        startTime_ = time(nullptr); // 开始计时
        return true; // 开始计时成功
    }
    cout<<"Card not active or that your account is currently in arrears "<<endl; // 卡片未激活或卡余额不足,开始计时失败
    return false;
}

/*这个函数的作用是结束用户的计费会话。在这个函数中,我们会根据用户的会话时间和费率来计算出应该收取的费用,然后尝试从用户的卡上扣除这笔费用。如果卡上的余额不足以支付费用,我们会提示用户进行充值。如果用户在充值时失败了,我们会将卡片注销,并将其加入黑名单。

话说充值失败这种事情还真不少见,就像我们在游戏中,打怪升级需要各种道具,但是充了钱却没充出来这个道具,这种感觉真是让人抓狂啊!所以在这里,我们充分考虑到了这个情况,设置了最多充值次数,如果尝试了多次充值仍然失败,我们会直接给用户卡片注销并加入黑名单,同时记录下来以备日后查证。*/
bool Card::endSession(double costPerMinute, vector<Record>& record) {
    const double RECHARGE_MINIMUM = 0.01; // 最小充值金额
    const int CANCELLED_BALANCE = 0; // 卡片被注销后的余额
    const int MAX_RECHARGE_ATTEMPTS = 3; // 最多尝试充值的次数

    if (!isStatus(ACTIVE) || startTime_ == 0) {
        return false; // 非法状态,不处理
    }

    int sessionTime = static_cast<int>(difftime(time(nullptr), startTime_));
    double cost;
    if (sessionTime > 60) {
        srand(time(nullptr));//随机数种子
        double discountRate = (double) (rand() % 50 + 50) / 100.0; // 随机折扣率,范围为 0.5 到 1.0
        cost = sessionTime * costPerMinute;
        double discountedCost = sessionTime * costPerMinute * discountRate;//加速时间,假设是按秒来计费
        cout << "Session ended. You have been charged " << discountedCost << " yuan (original cost: " << cost
             << " yuan)." << endl;
        cost = discountedCost;
    } else {
        cost = sessionTime * costPerMinute;
    }

    time_t endTime_ = time(nullptr);
    cout << "Session started at: " << asctime(localtime(&startTime_));
    cout << "Session ended at: " << asctime(localtime(&endTime_));

    // 尝试扣款
    if (balance_ >= cost) {
        balance_ -= cost;
        totalTime_ += sessionTime;
        startTime_ = 0;
        cout << "Session ended successfully" << endl;
        // 添加记录到容器中
        record.emplace_back(id_, startTime_, endTime_, cost);
        return true;
    }

    // 充值
    double amount;
    int rechargeAttempts = 0;
    bool rechargeSuccess = false;

    while (rechargeAttempts < 3 && !rechargeSuccess) {
        do {
            cout << "Enter recharge amount (minimum " << RECHARGE_MINIMUM << "): ";
            cin >> amount;
        } while (amount < RECHARGE_MINIMUM);

        if (recharge(amount)) {
            rechargeSuccess = true;
            balance_ -= cost;
            if (balance_ >= 0) {
                cout << "Session ended successfully" << endl;
                // 添加记录到容器中
                record.emplace_back(id_, startTime_, endTime_, cost);
                return true;
            }
        }

        rechargeAttempts++;
    }

    if (!rechargeSuccess) {
        setStatus(CANCELED);
        setBlackListStatus(IN_BLACKLIST);
        balance_ = CANCELLED_BALANCE;
        cout << "Card has been cancelled and added to blacklist" << endl;
        // 添加记录到容器中
        record.emplace_back(id_, startTime_, endTime_, CANCELLED_BALANCE);
        return false;
    }
}


/*这个函数的作用是重新发卡。当用户的卡片丢失时,如果用户的余额足够,我们会扣除 10 元钱,然后重新激活卡片,让用户可以继续使用。这个过程就像是给卡片重新洗了一遍脸,让它焕发出新的生命力一样。当然,如果用户的余额不足以支付这笔费用,那么我们就无法重新激活卡片,用户只能再去充值了。不过,如果用户的卡片状态不是丢失,那么这个函数也就没有什么作用了。*/
bool Card::reissue() {
    if (isStatus(Card::LOST)) {
        if (balance_ >= 10) {
            balance_ -= 10;
            setStatus(Card::ACTIVE);
            return true;
        }
    }
    return false;
}

卡片服务类设计

嗨呀,这段代码里面可是躲着问题的啊!

首先,卡片状态被定义成了枚举类型,但是在打印卡片信息的时候,只有四种状态能够被输出,而这里却有一个不应该出现的黑名单状态。这是不是很尴尬?这样不光打印出来的卡片信息不准确,连黑名单这个重要的状态也被忽略了,那要是卡片进了黑名单怎么办呢?难道还得去修改代码?这不是开发的禁忌之一吗?

另外一个比较麻烦的问题就是这个打印卡片信息的函数,因为这个函数在循环遍历所有卡片时,对每张卡片都要输出一大堆信息,这可能会导致控制台输出超级多的内容。如果系统里有一堆卡片的话,这个函数的输出可能会占用很多屏幕空间,可不就得不一遍又一遍地往上翻。那就真的是“看得我脖子都酸了”啊!

好了好了,不要太担心这些问题了。相信只要认真思考,细心分析,相信一定可以找到解决的办法的。但是要记得,要时刻保持开心,放松心情,抱着一颗“天塌下来有我这座山”的信念去面对问题!

Card_Service.h

#pragma once
// 注意这里用了 #pragma once 而不是传统的 ifndef/define 宏定义来避免重复包含文件
// 这么做的原因是,你不可能记得住自己的 ifndef/define 宏定义到底写的是啥
// 就算记得住,也难免会打错或者搞错顺序
// 而 #pragma once 就像懒人福音一样,让我们轻松避免了这些问题

#include "BillingSystem.h"  // BillingSystem 已经被打了无数次注释了

// 这里定义了两个函数,一个是打印单个卡片信息的函数 printCard,另一个是打印所有卡片信息的函数 printAllCards
// 看到这里你可能会问,为什么要分成两个函数呢?不是很浪费吗?
// 嗯,其实这个设计是很有讲究的,因为单个卡片信息和所有卡片信息的输出方式可能不一样
// 比如单个卡片信息可以使用表格方式打印,而所有卡片信息可能需要使用列表方式打印
// 由于两个函数的实现很简单,我们就不用多说了,直接上代码
// 话说,你们老师要求写注释,不是要求写作文吧?
// 看到这里我就想起了一句话:开车不需要车技,只需要多撞几次就好了
// 呃,其实我说错了,注释也很重要啦
// 毕竟,好的注释不仅可以让别人更好地理解代码,也能让自己更好地记住代码的细节
// 当然,最重要的是,注释可以让我们少写一些 bug!

void printCard(Card* card);

void printAllCards(vector<Card*> cards);

#endif //CARD_SERVICE_H

Card_service.cpp

#include "Card_Service.h"

void printCard(Card* card) {
    cout << "=====================================\n";
    cout << "Cards Information\n";
    cout << "=====================================\n";
    cout << "Card ID: " << card->getId() << "\n";
    cout << "Card holder's name: " << card->getName() << "\n";
    cout << "Card balance: " << card->getBalance() << "\n";
    cout << "Card's expiration time: "<< asctime(localtime(&card->getExpirationTime())) << "\n";

    switch (card->getStatus()) {
        case Card::INACTIVE:
            cout << "Status: Inactive";
            break;
        case Card::ACTIVE:
            cout << "Status: Active";
            break;
        case Card::CANCELED:
            cout << "Status: Canceled";
            if(card->IN_BLACKLIST == true){
                cout << "(blacklsited)\n";
            }

            break;
        case Card::LOST:
            cout << "Status: Lost";
            break;
    }

    // 这张卡片是不是被列入黑名单了?
    if (card->getBlackListStatus() == Card::IN_BLACKLIST) {
        cout << " (Uh oh, Blacklisted!)";
    }
    cout << "\n";
}

void printAllCards(vector<Card*> cards) {
    cout << "=====================================\n";
    cout << "Cards Information\n";
    cout << "=====================================\n";

    // 遍历所有卡片
    for (Card*& card : cards) {
        cout << "Card ID: " << card->getId() << "\n";
        cout << "Card holder's name: " << card->getName() << "\n";
        cout << "Card balance: " << card->getBalance() << "\n";
        cout << "Card status: ";

        // 输出卡片状态
        switch (card->getStatus()) {
            case Card::INACTIVE:
                cout << "Inactive";
                break;
            case Card::ACTIVE:
                cout << "Active";
                break;
            case Card::CANCELED:
                cout << "Canceled";
                break;
            case Card::LOST:
                cout << "Lost";
                break;
        }

        // 如果卡片在黑名单中,加上一个警告
        if (card->getBlackListStatus() == Card::IN_BLACKLIST) {
            cout << " (Be careful, Blacklisted!)";
        }
        cout << "\n";
    }
}

记录类模块设计

嘿嘿嘿,听说你想知道我是怎么构思上下机记录类的呀?其实我是受到了一个灵感,就是看到了一坨大便。对的没错,就是大便!

你不信?那我给你说说,看看这个Record类,它记录了每一位用户上下机的时间和花费,那么每一个记录就像是一坨大便,留下了用户的痕迹。上机时间,下机时间,花费,这些信息就像是大便的形状,颜色和气味,记录着用户的活动轨迹,方便管理员对用户进行监督和管理。

而这个Record类,就像是一个大便桶,它把所有的记录都收集起来,方便管理员随时查看。当然,如果我们不记录这些信息,就像是没收集大便的桶一样,用户的活动就无从监督和管理了。

那么,我们如何设计这个Record类呢?首先,我们需要记录每一个用户的姓名,这就像是给大便贴上用户的标签。然后,我们需要记录每一个用户上机的时间和下机的时间,这就像是记录大便的形状和颜色。最后,我们还需要记录用户所花费的费用,这就像是记录大便的气味。

通过这些记录,管理员就可以随时查看用户的上下机记录和花费情况,从而更好地管理和监督用户。而且,这个类的名字也非常贴切,叫做Record,记录用户的上下机情况,方便管理员查看和管理。

Record.h

/* 这是一个Record类的头文件,用来记录用户上下机信息 */

#ifndef RECORD_H // 防止重复定义
#define RECORD_H

#include <iostream>
using namespace std;

class Record {
public:
Record(const string& name, time_t startTime, time_t endTime, double cost); // 构造函数
const string& getName() const; // 获取用户名
time_t& getStartTime(); // 获取上机时间
time_t& getEndTime(); // 获取下机时间
double getCost() const; // 获取上机花费

void setName(const string& name); // 设置用户名
void setStartTime(time_t startTime); // 设置上机时间
void setEndTime(time_t endTime); // 设置下机时间
void setCost(double cost); // 设置上机花费

void printInfo() const; // 打印用户上下机信息

private:
string name_; // 用户名
time_t startTime_; // 上机时间
time_t endTime_; // 下机时间
double cost_; // 上机花费
};

#endif //RECORD_H

Record.cpp

#include "Record.h"
// 上面的Record.h是个好孩子,它声明了一个名字叫Record的类,有着开始时间、结束时间、花费和名字等属性。

Record::Record(const string &name, time_t startTime, time_t endTime, double cost)
: name_(name), startTime_(startTime), endTime_(endTime), cost_(cost) {}
// 构造函数,就像搭积木一样把属性拼在一起。

const string& Record::getName() const { return name_; }
// 获取名字,谁都知道这是啥。

time_t& Record::getStartTime() { return startTime_; }
// 获取上机时间,也是要知道的。

time_t& Record::getEndTime() { return endTime_; }
// 获取下机时间,谁不知道?

double Record::getCost() const { return cost_; }
// 获取花费,这个肯定知道,我们总要知道花了多少钱。

void Record::setName(const string& name) { name_ = name; }
// 设置名字,难道只能叫一次?

void Record::setStartTime(time_t startTime) { startTime_ = startTime; }
// 设置上机时间,不然咋知道啥时候上机?

void Record::setEndTime(time_t endTime) { endTime_ = endTime; }
// 设置下机时间,不然咋知道啥时候下机?

void Record::setCost(double cost) { cost_ = cost; }
// 设置花费,如果不知道花了多少钱怎么结账啊?

void Record::printInfo() const {
cout << "Name: " << name_ << endl;
cout << "Start time: " << asctime(localtime(&startTime_));
cout << "End time: " << asctime(localtime(&endTime_));
cout << "Cost: " << cost_ << " yuan" << endl;
}
// 输出记录的详细信息,包括名字、开始时间、结束时间、花费等等,好处多多,方便用户查看、管理。

聊天室(扩展)模块设计

分享一下我构思聊天室类的过程吧!不过在此之前,我想先对这段代码进行点评。看到这个printCard和printAllCards函数,我不禁想到了一个有趣的想法:既然我们已经实现了查卡功能,为什么不在这个函数里面加入一个“表白功能”呢?这样用户就可以在查询卡信息的同时,直接向TA表白了,实现了双重功效,省时省力,何乐而不为呢?(最后因为太复杂Give Up了~)

好了,言归正传,接下来,我来谈一谈我是如何构思聊天室类的。

首先,我们需要明确聊天室的基本功能,然后,我们可以将这些功能封装成不同的函数,并且在聊天室类中进行实现。同时,为了提高代码的可读性和可维护性,我们可以将这些函数进行分类,比如说将群聊和私聊的函数分别放在不同的区域。

其次,我们需要考虑聊天室的数据结构,比如说如何存储用户的信息、聊天记录等。一般来说,我们可以使用队列或者栈等数据结构来存储聊天记录。对于用户信息,我们可以使用一个结构体或者类来存储,这个类可以包含用户名、密码、聊天记录等信息。(代码不得已简化了,因为众所周知的DDL)

最后,我们需要考虑聊天室的安全性和可靠性。为了确保用户的隐私和安全,我们需要对聊天记录进行加密和存储。此外,我们还需要对用户进行身份验证,以确保只有授权用户才能访问聊天室。为了确保聊天室的可靠性,我们还需要考虑如何应对用户断线、网络故障等情况,并且及时恢复聊天记录。(同理)

嘿嘿,我知道你可能已经感到有点累了,我也很累了~,不过我希望我的构思过程能够带给你一些快乐和启发。在编写聊天室类的过程中,需要考虑的因素非常多,但只要努力思考和尝试,相信总会有一种神奇的力量能够帮助我们克服困难~

ChattingRoom.h

#ifndef CHATTINGROOM_H
#define CHATTINGROOM_H
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <ctime>
using namespace std;
class ChattingRoom {
public:
    ChattingRoom();
    ~ChattingRoom();

    void addMessage(const string& sender, const string& message);
    void displayMessages();

    bool loadChatsFromFile();
    bool saveChatsToFile();
private:
    vector<pair<string, pair<string, time_t>>> messages_;
    const string chatFileName_ = "chats_data.txt";//记录数据文件名;
};

#endif //BILLINGSYSTEM_CHATTINGROOM_H

ChattingRoom.cpp

#include "ChattingRoom.h"

ChattingRoom::ChattingRoom() {
    loadChatsFromFile();
}

ChattingRoom::~ChattingRoom() {
    saveChatsToFile();
}

bool ChattingRoom::saveChatsToFile() {
    ofstream fout(chatFileName_, ios::trunc);
    if (!fout) {
        cerr << "Failed to open chat records data file: " << chatFileName_ << "\n";
        return false;
    }

    // 保存聊天记录
    for (vector<pair<string, pair<string, time_t>>>::iterator it = messages_.begin(); it != messages_.end(); ++it) {
        fout << it->first << " " << it->second.first << " " << it->second.second << "\n";
    }

    fout.close();
    return true;
}

bool ChattingRoom::loadChatsFromFile() {
    ifstream fin(chatFileName_);
    if (!fin) {
        cerr << "Fail to open chat records data file: " << chatFileName_ << "\n";
        return false;
    }
    messages_.clear(); // 清空聊天记录
    string sender, message;
    time_t sendTime;
    while (fin >> sender >> message >> sendTime) {
        messages_.push_back(make_pair(sender, make_pair(message, sendTime))); // 将聊天记录加入聊天记录集合
    }

    fin.close();
    return true;
}
void ChattingRoom::displayMessages() {
    cout << "Chatting Records:" << endl;
    for (vector<pair<string, pair<string, time_t>>>::iterator it = messages_.begin(); it != messages_.end(); ++it) {
        cout << asctime(localtime(&it->second.second)) << " " << it->first << ": " << it->second.first << endl;
    }
}


void ChattingRoom::addMessage(const string& sender, const string& message) {
    time_t sendTime = time(nullptr);
    messages_.push_back(make_pair(sender, make_pair(message, sendTime)));
}

计费系统逻辑层调用功能模块设计

计费系统的大头,BillingSystem,它来辣它来辣,就像一道滚烫的麻辣火锅,让人痛并快乐着。在开发过程中,我遇到了诸多的问题,有的让我痛不欲生,有的让我欲哭无泪。

首先,被接口逻辑搞崩到哭的经历,这就像吃了一碗辣椒面,一口气憋不住,突然哇哇大哭。在开发BillingSystem的过程中,我一开始就被复杂的接口逻辑搞得晕头转向,仿佛置身于一个迷宫之中。但我没有放弃,我拿起了拼图一样的代码,一块一块地拼凑起来,终于成功实现了所有接口。那时的我,就像吃完辣椒面的人一样,虽然嘴巴还在发热,但内心却是充满了自豪感。

接下来,我又花费好久实现了黑名单功能,但就在我自以为终于度过难关的时候,又遇到了新的问题——文件读写。被文件读写疯狂报错搞疯的经历,就像在火锅里不停地放入冷水,让火锅越来越凉。每次修改完代码,我都会在文件中保存一下数据,以便下次启动程序时能够读取。可是,每次运行程序都会遇到各种奇怪的文件读写错误,有时候还会出现乱码。我甚至怀疑这个计费系统是不是在捉弄我,恶意修改我保存的数据。然鹅,我最终还是战胜了这个问题。我通过多次排查代码,逐一分析每个函数,最终发现了文件读写的问题所在,并找到了解决方法。虽然经历了一番波折,但是我对计费系统的理解更加深入,对代码的掌握也更加娴熟。

还有,被时间显示一直乱码弄到秃头,无力吐槽,就像在火锅里放了一些奇怪的调料,让整个火锅味道变得怪异。我总是看到乱码,看到一堆数字,甚至看到了二进制!后来才发现,原来是我没有注意到取地址符必须后面跟着引用类型,而我却直接传递了一个整型,怪不得会乱码呢!另外,我发现,asctime函数返回的是英文日期格式,这就导致了一系列的问题,不过好在,我很快就找到了解决方案——使用tm结构体来保存日期和时间,然后使用strftime函数来将日期和时间格式化为中文日期格式。终于,我可以看到清晰的日期和时间了,再也不用看到乱码了!或者——直接英文格式输出!哈哈,这开发过程中遇到的难题还真是让我又爱又恨啊!

不得不说,计费系统的开发过程中真的是有太多太多的坑和难点,但是,每一次解决问题的过程都让我感到无比充实。最后,我在这里想对所有在被计费系统折磨的同学们说:坚持不懈去解决问题,你们定能够创造出无所不能之物!

BillingSystem.h

#pragma once

#ifndef BILLINGSYSTEM_H
#define BILLINGSYSTEM_H

#include "Card.h"
#include "Record.h"
#include "ChattingRoom.h"
#include <vector>
#include <string>
#include <fstream>

class BillingSystem {
public:
BillingSystem(); // 计费系统的大头,它来辣它来辣,构造函数酣畅淋漓,一切从这里开始

~BillingSystem(); // 析构函数,做完事情就要结束,别忘了销毁

void addCard(const string& id, const string& name, string& password); // 加入新卡片,然后你的账单就充实了

Card* findCard(const string& id); // 通过ID找卡片,它就像一个超级侦探,让你的卡片一个都不会跑

Card* findCardByName(const string& name); // 通过名字找卡片,名字一致,卡片难逃

vector<Card*> findCardsByIdSubstring(const string& idSubstring); // 通过ID子串查找卡片,你的ID就像谷歌搜索,只需要输入一部分就可以了

bool activateCard(const string& id); // 激活卡片,好像在给你的卡片喝红牛,充满了力量

bool rechargeCard(Card* card); // 给卡片充值,充电宝,不解释

bool refund(Card* card, int amount); // 退款,有时候事情会出错,所以退款也是必要的

bool checkPassword(Card* card,const string Pwd); // 检查卡片密码,保护你的财产

bool checkVerificationCode(); // 检查验证码,防止恶意攻击

bool startCardSession(Card* card); // 开始卡片会话,就像打电话,你要开始计费了

bool endCardSession(Card* card,vector<Record>& record, double costPerMinute); // 结束卡片会话,停止计费,然后记录所有细节

vector<Card*> getAllCards(); // 获取所有卡片,就像一个快递员,把所有的包裹都送到你手上

void deleteAllCards(); // 删除所有卡片,非常慎重,一定要三思而后行

bool addToBlacklist(Card* card); // 把卡片加入黑名单,像监狱里的犯人一样

bool isInBlacklist(const string& name); // 检查卡片是否在黑名单中,这是一个非常重要的安全检查

string& addVerificationCode(); // 添加验证码,防止别人恶意使用你的卡片

void queryRecords(); // 查询记录,可以看到你所有的账单和交易记录

char* shiftStartTime(time_t startTime); // 把起始时间转换成字符串,这个操作有些神秘

char* shiftExpirationTime(time_t expirationTime); // 把过期时间转换成字符串,同样神秘

void addRecord(const string& cardId, const string& cardHolderName, time_t startTime, time_t endTime, double cost);
// 加入记录函数,一条条记录详细记录下每一次的消费,注意消费开始和结束时间。

vector<Record>& getRecords();
// 获取所有记录的函数,从这里可以看到用户的消费历史,是不是觉得很心动?

bool loginAdmin();
// 管理员登录函数,只有管理员才有权对系统进行一些比较高级的操作。

bool adminMenu();
// 管理员菜单函数,需要管理员身份才能进入,注意不要让用户进来删库跑路。

bool removeCard();
// 删除用户卡片函数,这里的删除是真正的删除,注意不要误删,否则后果自负。

bool addToBlacklist(string name);
// 加入黑名单函数,如果某个用户恶意刷卡或者作弊,就可以将其加入黑名单,警示其它用户不要学习。

bool removeBlacklist(string name);
// 从黑名单中移除用户函数,这里要注意,移除后该用户不再受限制,所以要慎重。

void viewBlacklist();
// 查看黑名单函数,可以看到哪些用户被列入黑名单,不要忘了点赞。

void enterChattingRoom();
// 进入聊天室函数,这里可以和其它用户一起闲聊,放心大胆地聊吧。

bool transferBalance(Card* fromCard, string toUser, int amount);
// 转账函数,可以将自己卡片余额中的一部分转到另一个用户的卡片中,注意金额不要输错哦。

private:
vector<Card> cards_;
// 存放所有卡片的向量。

set<string> blacklist_;
// 存放黑名单用户的集合。

vector<Record> records_;
// 存放所有消费记录的向量。

ChattingRoom chattingRoom_;
// 聊天室实例。

string verificationCode_;
// 验证码,这个东西可以用来验证用户身份。

const string cardsFileName_ = "cards_data.txt";//网卡数据文件名
const string blacklistsFileName_ = "blacklists.txt"; // 黑名单数据文件名;
const string recordsFileName_ = "records_data.txt";//记录数据文件名;
void loadCardsFromFile();
void saveCardsToFile();
bool saveBlacklistsToFile() ;
bool loadBlacklistsFromFile();
void loadRecordsFromFile();
void saveRecordsToFile();
// 一系列文件读写相关函数,不要让程序找不到卡片数据、黑名单数据或消费记录数据哦,否则就会崩溃了。

BillingSystem.cpp

#include <sstream>
#include "BillingSystem.h"

BillingSystem::BillingSystem() {
    loadCardsFromFile(); // 从文件中读取卡片信息
    loadBlacklistsFromFile(); // 从文件中读取黑名单信息
    loadRecordsFromFile(); // 从文件中读取记录信息
    chattingRoom_.loadChatsFromFile(); // 从文件中读取聊天室信息

}

BillingSystem::~BillingSystem() {
    saveCardsToFile(); // 将卡片信息保存到文件
    saveBlacklistsToFile(); // 将黑名单信息保存到文件
    saveRecordsToFile(); // 将记录信息保存到文件
    chattingRoom_.saveChatsToFile(); // 将聊天室信息保存到文件
}

void BillingSystem::addCard(const string& id, const string& name, string& password) {
    if (isInBlacklist(name)) { // 检查是否在黑名单中
        cout << "Sorry, you are not allowed to use our system. Please contact customer service for more information." << endl;
        return;
    }

    // 检查卡片列表中是否已经存在相同ID的卡片
    for (auto& card : cards_) {
        if (card.getId() == id) {
            cout << "A card with the same ID already exists." << endl;
            return;
        }
    }

    // 创建新卡片,设置有效期为一年
    time_t expirationTime = time(nullptr) + 365 * 24 * 60 * 60; // 当前时间加上一年的秒数

    // 添加到卡片列表中
    cards_.push_back(Card(id, name, password, expirationTime));

    // 保存卡片信息到文件
    saveCardsToFile(); // 将卡片信息保存到文件
}

// 转换时间格式
char* BillingSystem::shiftStartTime(time_t startTime) {
    // 将time_t类型的时间转换为日期格式字符串
    struct tm* localTime = localtime(&startTime); // 这个函数把time_t类型的时间转换为了struct tm*类型的时间
    char buffer[80]; // 建立一个缓冲区
    strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", localTime); // 把时间转换为指定格式的字符串
    return buffer; // 返回转换好的字符串
}

char* BillingSystem::shiftExpirationTime(time_t expirationTime) {
    // 将time_t类型的时间转换为日期格式字符串
    struct tm* localTime = localtime(&expirationTime); // 这个函数把time_t类型的时间转换为了struct tm*类型的时间
    char buffer[80]; // 建立一个缓冲区
    strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", localTime); // 把时间转换为指定格式的字符串
    return buffer; // 返回转换好的字符串
}


/*哇,又看到了大名鼎鼎的BillingSystem!它来辣,它来辣!这里是转换时间格式的接口,我们要将time_t类型的时间转换为人能看懂的日期格式字符串。不过呢,之前有一些大佬把程序日期格式改成中文的了,而这里我们用的是asctime函数,它只能支持英文的日期格式,搞得我们要用中文日期格式的话只好再加个转换,痛苦啊QAQ。还有,要注意asctime函数返回的是一个静态字符串,每次调用都会被覆盖,所以要再开一个buffer来存储,这样才不会乱码哦。
*/

/*找卡找卡,一直找卡,找到了名字一模样的卡,找到了就走人,没找到就返回空指针

找卡找卡,找 ID 的子串,找到了就装进容器里,没找到就返回空容器

找卡找卡,找到了对应 ID 的卡,找到了就返回卡片指针,没找到就返回空指针

激活卡激活卡,激活啦,激活卡状态变成了 active,嘿嘿,激活成功,保存到文件里面去,返回激活结果

充值充值,充啊充啊,充你的费用,,先输入充值金额,再调用卡片的 recharge 函数,如果充值失败并且卡片被加入黑名单,就提示用户去客服部门寻求帮助,否则充值成功,保存到文件里面去

退费退费,退啊退啊,退你的费用,调用卡片的 refund 函数,如果退费成功就打印成功信息并保存到文件里面去,否则就打印失败信息*/

Card* BillingSystem::findCardByName(const string& name) {
    for (Card& card : cards_) {
        if (card.getName() == name) {
            return &card;
        }
    }
    return nullptr;
}

// 找到名字匹配的卡片
// 看到卡片的名字,就能顺着名字找到它,就像顺着儿童绘本上的字母找到图画一样
// 希望这个函数不会出现“画猪圈圈引出大佬”的尴尬局面

vector<Card*> BillingSystem::findCardsByIdSubstring(const string& idSubstring) {
    vector<Card*> matchingCards;
    for (Card& card : cards_) {
        if (card.getId().find(idSubstring) != string::npos) {
            matchingCards.push_back(&card);
        }
    }
    return matchingCards;
}

// 找到 ID 含有指定子串的卡片
// 在一堆卡片中找到 ID 含有指定子串的卡片就像在大海中找到一枚落水的针一样难
// 希望调用这个函数时,不会搞错指定子串的位置

Card* BillingSystem::findCard(const string& id) {
    for (Card& card : cards_) {
        if (card.getId() == id) {
            return &card;
        }
    }
    return nullptr;
}

// 找到指定 ID 的卡片
// 找到指定 ID 的卡片就像在一大群人中找到一只钻石鸟,不过比起那只鸟,卡片还是好找一些

bool BillingSystem::activateCard(const string& id) {
    Card* card = findCard(id);
    if (card && card->isStatus(Card::INACTIVE)) {
        card->activate();
        saveCardsToFile();
        return true;
    }
    return false;
}

// 激活卡片
// 想让一张卡片活起来,就得给它打针,就像医生给病人注射一样
// 不过,这个注射器比起医生用的那个要小得多,需要动用一些黑科技才行

//充值
bool BillingSystem::rechargeCard(Card* card) {

    double amount;
    cout << "Enter recharge amount: ";
    cin >> amount;
    bool flag = card->recharge(amount);
    if(!flag && card->getBlackListStatus() == Card::IN_BLACKLIST){
        cout << "Recharge failed!This card is blacklisted. Please contact customer service for assistance.\n";
        return false;
    }

    cout << "Recharge successful!\n";

    // 保存更改到文件中
    saveCardsToFile();
    return true;
}

// 充值卡片
// 给一张卡片充值,就像给手机充电一样
// 不过,手机没充满电就会有电量不足的提示,而卡片没充够钱
bool BillingSystem::refund(Card* card, int amount) {
    if (card->refund(amount)) {
        cout<<"Refund successful!"<<endl;
        saveCardsToFile();
        return true;
    } else {
        cout << "Oops, something went wrong with the refund." << endl;
        return false;
    }
}

// 以下这个函数用来判断密码是否正确
// 你确定你的卡主不是个大佬?
// 如果密码正确,返回真;如果密码错误,返回假。
bool BillingSystem::checkPassword(Card* card,const string Pwd){
    return card->getPassword() == Pwd;
}

// 这个函数的作用是生成验证码
// 这个验证码非常厉害,绝对比你用生日作为密码安全多了
// 如果你觉得验证码不够安全,那就去买个令人头皮发麻的密保吧
bool BillingSystem::checkVerificationCode() {
    addVerificationCode();
    cout << "Enter verification code for safety:"<<verificationCode_<<endl;
    string input;
    cout<<"Input your answer here:";
    cin >> input;
    return verificationCode_ == input;
}

// 这个函数用来生成一个长度为 5 的验证码
// 答案在 [a-zA-Z0-9] 里面选,概率比你中彩票还小
// 如果你觉得这个函数写的不够好,你可以去重写一份
// 只不过你得付出写代码的时间,还要面对一个选择:要不要加班?
string& BillingSystem::addVerificationCode(){
    srand(time(nullptr));
    const int length = 5;
    const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    string code = "";

    for (int i = 0; i < length; i++) {
        int randomIndex = rand() % chars.size();
        code += chars[randomIndex];
    }

    verificationCode_ = code;
    return verificationCode_;
}

// 这个函数用来让卡主开始使用卡片
// 如果可以成功开始,返回真;否则返回假。
// 失败的原因可能是卡片未激活或余额不足,不要轻易放弃哦
bool BillingSystem::startCardSession(Card* card) {
        if(card->startSession()){
            saveCardsToFile();
            saveRecordsToFile();
            return true;
        }
        else {
            cout << "Oops, something went wrong with starting the session. Are you sure the card is active and has enough balance?" << endl;
            return false;
        }
}

// 这个函数用来让卡主结束使用卡片
// 如果可以成功结束,返回真;否则返回假。
// 千万不要忘记付费哦,否则你的卡片就有可能会被拉入黑名单。

// 下机
bool BillingSystem::endCardSession(Card* card,vector<Record>& record, double costPerMinute) {
    // 如果卡片的结束会话成功,那么保存卡片和记录数据,然后返回成功
    if(card->endSession(costPerMinute,record)){
    saveCardsToFile();
    saveRecordsToFile();
    return true;
}
// 如果卡片的结束会话失败,并且卡片被加入黑名单,那么抱歉地通知卡主,然后将其加入黑名单
    if(!card->endSession(costPerMinute,record)&&        (card->getBlackListStatus()==Card::IN_BLACKLIST)){
    cout<<"I'm sorry but you've been blacklisted"<<endl;
    blacklist_.insert(card->getName()); // 将卡主加入黑名单
    saveCardsToFile(); // 保存卡片信息和黑名单信息
    saveRecordsToFile();
    saveBlacklistsToFile();
    return false;
    }
    // 其他情况返回失败
    return false;
    }

void BillingSystem::loadCardsFromFile() {
    // 打开卡片信息数据文件
    ifstream inFile(cardsFileName_);
    // 如果文件无法打开,就输出错误信息,然后撤退!
    if (!inFile) {
    cerr << "Failed to open cards data file: " << cardsFileName_ << "\n";
    return;
    }
    // 声明一些需要读取的数据
    string id, name, password;
    double balance;
    int status, totalTime, blacklistedstatus;
    time_t startTime, endTime, expirationTime;

    // 循环读取文件中的所有信息
    while (inFile >> id >> name >> password >> balance >> status >> startTime >>     endTime >> totalTime >> expirationTime
              >> blacklistedstatus) {
    // 创建一个新卡片对象
    Card card(id, name, password, expirationTime);
    // 设置余额
    card.setBalance(balance);
    // 设置状态
    card.setStatus(static_cast<Card::CardStatus>(status));
    // 设置起始时间
    card.setStartTime(startTime);
    // 设置结束时间
    card.setEndTime(endTime);
    // 设置使用时间
    card.setTotalTime(totalTime);
    // 设置过期时间
    card.setExpirationTime(expirationTime);
    // 设置黑名单状态
    card.setBlackListStatus(static_cast<Card::BlackListStatus>(blacklistedstatus));

    // 添加到卡片容器中
    cards_.push_back(card);
}

// 关闭文件
inFile.close();

}
// 这个函数的功能是从数据文件中读取卡片信息,并将其保存在 cards_ 容器中。如果打开文件失败,那么我们就输出一个错误信息,然后直接退缩!
// 读取文件中的每一行数据,然后根据读取到的数据创建一个新的卡片对象,最后将其添加到卡片容器中。
// 需要注意的是,我们要将读取到的整型状态值强制转换为 CardStatus 和 BlackListStatus 枚举类型。这个转换不容易,所以建议你一定要理解这个过程,不要抄代码!

void BillingSystem::saveCardsToFile() {
    // 打开卡片信息数据文件
    ofstream outFile(cardsFileName_);
    // 如果文件无法打开,就输出错误信息,然后撤退!
    if (!outFile) {
        cerr << "Failed to open cards data file: " << cardsFileName_ << "\n";
        return;
    }
    // 循环写入卡片信息到文件中
    for (Card& card : cards_) {
        outFile << card.getId() << " " << card.getName() << " "<<card.getPassword()<<" "<< card.getBalance() << " "
                << static_cast<int>(card.getStatus()) << " " << card.getStartTime() << " " << card.getEndTime() << " " <<
                card.getTotalTime() << " " << card.getExpirationTime() << " "
                << static_cast<int>(card.getBlackListStatus())<<"\n";
    }

    // 关闭文件
    outFile.close();
}

void BillingSystem::deleteAllCards() {
    // 清空卡片容器
    cards_.clear();
    // 把新的卡片信息写入文件中
    saveCardsToFile();
    // 删除存储卡片信息的文件
    if (remove(cardsFileName_.c_str()) != 0) {
        cerr << "Error deleting file: " << cardsFileName_ << endl;
    }
    else {
        // 成功删除文件,大声欢呼!
        cout << "File successfully deleted: " << cardsFileName_ << endl;
    }
}

vector<Card*> BillingSystem::getAllCards() {
    vector<Card*> allCards;
    for (Card& card : cards_) {
        allCards.push_back(&card);
    }
    return allCards;
}
// 原汁原味的卡片集合,不要试图修改里面的任何东西,否则会被卡住

bool BillingSystem::addToBlacklist(Card* card) {
    if (card) {
        if (card->getStatus() != Card::CANCELED) {
            card->setStatus(Card::CANCELED);
            card->setBlackListStatus(Card::IN_BLACKLIST);
            blacklist_.insert(card->getName());
            return true;
        }
    }
    return false;
}
// 你被拉黑了!现在你无法在我们的系统中活动了!就好像在高速公路上被拦下来一样。

bool BillingSystem::saveBlacklistsToFile() {
    ofstream fout(blacklistsFileName_);
    if (!fout.is_open()) {
        cerr << "Failed to open blacklists data file: " << blacklistsFileName_ << "\n";
        return false;
    }

    // 保存黑名单信息
    for (auto cardNumber : blacklist_) {
        fout << cardNumber << "\n";
    }

    fout.close();
    return true;
}
// 把黑名单存进文件里面去,这样黑名单里的人就不会出来害人了

bool BillingSystem::loadBlacklistsFromFile() {
    ifstream fin(blacklistsFileName_);
    if (!fin.is_open()) {
        cerr << "Failed to open blacklists data file: " << blacklistsFileName_ << "\n";
        return false;
    }

    blacklist_.clear(); // 清空黑名单集合

    string cardNumber;
    while (getline(fin, cardNumber)) {
        blacklist_.insert(cardNumber); // 将卡号加入黑名单
    }

    fin.close();
    return true;
}
// 从文件中读取黑名单,这样我们就可以轻松地控制黑名单里的人了

bool BillingSystem::isInBlacklist(const string& name){
    if (blacklist_.count(name) > 0) {
        //加入黑名单
        cout << "Cannot operate with this card as its owner is in the blacklist.Contact the manager!\n"<<endl;
        return true;
    }
    return false;
}
// 如果你是一个不良分子,就别想在我们的系统里混了!

// 加载已存储的记录数据
// 如果数据文件打不开,那就抛出一顿错误
// (我咋知道你文件名输错了呢?)
void BillingSystem::loadRecordsFromFile() {
    ifstream inFile(recordsFileName_);
    if (!inFile) {
        cerr << "Failed to open records data file: " << recordsFileName_ << "\n";
        return;
    }
    string id;
    time_t startTime, endTime;
    double cost;

    while (inFile >> id >> startTime >> endTime >> cost) {
        Record record(id, startTime, endTime, cost);
        records_.push_back(record);
    }

    inFile.close();

}

// 保存记录到文件中
// 如果无法打开文件,就抛出错误
// (咋样,又输错了文件名?!)

void BillingSystem::saveRecordsToFile() {
    ofstream outFile(recordsFileName_);
    if (!outFile) {
        cerr << "Failed to open records data file: " << recordsFileName_ << "\n";
        return;
    }

    for ( Record& record : records_) {
        outFile << record.getName() << " " << record.getStartTime() << " "
        << record.getEndTime() << " " << record.getCost() << "\n";
    }

    outFile.close();

}

//把所有的记录存进文件里,供以后的人们翻阅,做数据分析。


vector<Record>& BillingSystem::getRecords(){
    return records_;
}


void BillingSystem::queryRecords(){
    double totalSales = 0.0; // 网吧营业总额

    cout << "Enter the card holder's name you want to query (or enter \"ALL\" to query all cards): ";
    string name;
    cin >> name;

    if (name == "ALL") {
        cout << "All Cards Records:\n";
        for (Record& record : records_) {
            cout << "Card holder's name: " << record.getName() << "\n";
            cout << "StartSession: " << asctime(localtime(&record.getStartTime()));
            cout << "EndSession: " << asctime(localtime(&record.getEndTime()));
            cout << "Cost: " << record.getCost() << " yuan\n";
            totalSales += record.getCost();
        }
        cout << "Total sales: " << totalSales << " yuan\n\n";

    } else {
        bool cardFound = false;
        for ( Record& record : records_) {
            if(record.getName() == name){
                cout << "Card holder's name: " << record.getName() << "\n";
                cout << "StartSession: " << asctime(localtime(&record.getStartTime()));
                cout << "EndSession: " << asctime(localtime(&record.getEndTime()));
                cout << "Cost: " << record.getCost() << " yuan\n";
                cardFound = true;
            }
        }

        if (!cardFound) {
            cout << "Card with the holder " << name << " not found!\n\n";
        }
    }
}
//查一查记录,看看谁又花光了父母的钱,然后想想自己的前途。

bool BillingSystem::loginAdmin() {
    const string ADMIN_USERNAME = "admin";
    const string ADMIN_PASSWORD = "admin123";

    string username, password;

    cout << "Enter admin username: ";
    cin >> username;

    cout << "Enter admin password: ";
    cin >> password;

    if (username == ADMIN_USERNAME && password == ADMIN_PASSWORD) {
        cout << "Admin logged in successfully!" << endl;
        if(adminMenu()){
            return true;
        }

    }
    else {
        cout << "Invalid username or password." << endl;
        return false;
    }
}

// 一天到晚管理网吧,管理员的工作就是写程序和喝茶,啊对了,还有登陆。

bool BillingSystem::adminMenu() {
    const int ADD_CARD = 1;
    const int REMOVE_CARD = 2;
    const int ADD_BLACKLIST = 3;
    const int REMOVE_BLACKLIST = 4;
    const int VIEW_BLACKLIST = 5;
    const int EXIT = 6;

    int choice;

    do {
        // 管理员菜单
        cout << "Admin Menu" << endl;
        cout << "1. Add card" << endl;
        cout << "2. Remove card" << endl;
        cout << "3. Add to blacklist" << endl;
        cout << "4. Remove from blacklist" << endl;
        cout << "5. View blacklist" << endl;
        cout << "6. Exit" << endl;
        cout << "Enter your choice: ";
        cin >> choice;

        switch (choice) {
            case ADD_CARD:{
                // 添加卡片
                string id, name, password;
                cout << "Enter card ID: ";
                cin >> id;

                if (findCard(id)) {
                    cout << "Card already exists." << endl;
                    return false;
                }

                cout << "Enter card holder's name: ";
                cin >> name;
                cout << "Enter card password: ";
                cin >> password;
                addCard(id, name, password);
                saveCardsToFile();
                break;
            }

            case REMOVE_CARD:{
                // 删除卡片
                removeCard();
                saveCardsToFile();
                break;
            }
            case ADD_BLACKLIST:{
                // 添加黑名单
                cout << "Input the name needed to add to blacklist\n";
                string input_name;
                cin >> input_name;
                if(isInBlacklist(input_name) > 0){
                    cout << "Operation duplicated \n ";
                }
                addToBlacklist(input_name);
                saveBlacklistsToFile();
                break;
            }
            case REMOVE_BLACKLIST:
            {
                // 移除黑名单
                cout << "Input the name needed to remove from blacklist\n";
                string input_Name;
                cin >> input_Name;
                removeBlacklist(input_Name);
                saveBlacklistsToFile();
                break;
            }
            case VIEW_BLACKLIST:{
                // 查看黑名单
                viewBlacklist();
            }
            case EXIT:
                break;
            default:
                cout << "Invalid choice." << endl;
                break;
        }
    } while (choice != EXIT);
    return true;
}


bool BillingSystem::removeCard() {
// 不能用pop_back(),只能用erase(iterator),反正我也不知道为什么
string id;
cout << "Enter the ID of the card to remove: ";
cin >> id;

for (vector<Card>::iterator it = cards_.begin(); it != cards_.end(); ++it) {
    if (it->getId() == id) {
        cards_.erase(it);
        cout << "Card removed successfully. You just made a card disappear, like a magician! Poof!" << endl;
        return true;
    }
}
cout << "Card not found. Are you sure it exists? Did you enter the correct ID? Please check and try again." << endl;
return false;

}

bool BillingSystem::addToBlacklist(string name) {// 重载函数,因为我们要加入名字而不是卡号
if (blacklist_.count(name)) {
cout << "This person has already been added to the blacklist. You can't blacklist someone twice, that's not fair!" << endl;
return true;
} else {
blacklist_.insert(name);
cout << "This person has been added to the blacklist. Don't mess with us, we don't tolerate rule-breakers!" << endl;
return false;
    }
}

bool BillingSystem::removeBlacklist(string name) {
    for (set<string>::iterator it = blacklist_.begin(); it != blacklist_.end(); ++it) {
        if (*it == name) {
            blacklist_.erase(it);
            cout << "Card removed from blacklist. It's always nice to give someone a second chance, right?" << endl;
        return true;
        }
    }
    cout << "Card not found in blacklist. You tried to remove someone who wasn't there, that's okay, we won't judge you." << endl;
    return false;
}

// 查看黑名单列表
void BillingSystem::viewBlacklist(){
cout << "Blacklist:" << endl;
    if (blacklist_.empty()) {
    cout << "The blacklist is currently empty. That's good news, right?" << endl;
    } 
    else {
    for (set<string>::iterator it = blacklist_.begin(); it != blacklist_.end(); ++it) {
    cout << *it << endl;
    }
    cout << "These are the people who broke our rules and got blacklisted. Shame on them!" << endl;
    }
}

void BillingSystem::enterChattingRoom() {
// 在聊天室输入exit离开时,发现房间内气氛不对,整个聊天室都变得安静
// 经过仔细观察发现,原来这个聊天室是个鬼屋
// 当输入exit时,灵魂也被绑定在聊天室中,永远无法离开
// 所以,聊天室并不是一个好地方,建议大家少去为妙
string username;
cout << "Enter your name: ";
getline(cin, username);
cin.ignore(); // 清空输入缓冲区中的回车符

c

cout << "Welcome to our Chatting Room!" << endl;
chattingRoom_.displayMessages();

while (true) {
    cout << username << ": ";
    string message;
    getline(cin, message);
    if (message.empty()) {
        continue;
    }

    if (message == "exit") {
        cout << "You have left the Chatting Room." << endl;
        break;
    }

    // 将带空格的句子转换为完整字符串,其实就是为了去除用户多输入的空格
    stringstream ss(message);
    string msg, tmp, split = " ";
    while (ss >> tmp) {
        msg += (tmp + split);
    }

    chattingRoom_.addMessage(username, msg);
    chattingRoom_.saveChatsToFile();
    chattingRoom_.displayMessages();
    }

}


bool BillingSystem::transferBalance(Card* fromCard, string toUser, int amount) {
    // 检查转账目标账户是否存在
    // 如果目标账户不存在,我们就无法转账,还不如睡觉
    Card* toCard = findCard(toUser);
    if (!toCard) {
    cout << "Error: the target account does not exist.\n";
    return false;
    }

    // 检查目标账户是否充值激活卡
    // 如果目标账户没有充值激活卡,那么它就像个废物,没用啊
    if (toCard->getStatus() == Card::ACTIVE) {
    cout << "Error: the target account has not activated.\n";
    return false;
    }

    // 检查目标账户是否被注销或拉黑
    // 如果目标账户被注销或拉黑,那我们就别浪费时间了
    if (toCard->getStatus() == Card::CANCELED || toCard->getBlackListStatus() ==             Card::IN_BLACKLIST) {
    cout << "Error: the current account has been canceled or blacklisted.\n";
    return false;
    }

    //检查当前用户检查目标账户是否充值激活卡/是否被注销或拉黑
    // 如果当前用户没有充值激活卡,那我们还是去泡温泉吧
    if(fromCard->getStatus() == Card::ACTIVE){
    cout << "Error: the current account has not been activated.\n";
    return false;
    }

    // 检查当前用户的余额是否足够
    // 如果当前用户余额不足,那就别想着转账了,不然债务缠身
    if (fromCard->getBalance() < amount) {
    cout << "Error: balance of the current account is insufficient.\n";
    return false;
    }

    // 执行转账操作
    // 转账成功啦!但是我们还是要保存一下转账结果,以免下次查询时找不到
    fromCard->setBalance(fromCard->getBalance() - amount);
    toCard->setBalance(toCard->getBalance() + amount);
    cout << "Successfully transferred " << amount << " yuan.\n";
    saveCardsToFile();
}

总结

首先,这门课虽然年代久远,但是实践性不容小觑。之前我以为编程就是枯燥无味的打几十行代码,但是通过这门课,我发现编程也可以更有趣!

其次,这个计费管理系统不错哟!通过这个系统,我们可以实现添加卡,查询卡,激活卡,注销卡,挂失卡,上机,下机,退费,转账,展示卡信息,删除卡,查询统计营业额等功能!

不过,这个系统设计还是遇到了很多困难。比如,最开始我就经常搞混了各个类之间的关系,导致程序运行出现各种奇怪的错误。还有就是在实现某些功能的时候,我想了很久也不知道该怎么写,只能靠不断尝试和调试来解决问题。

但是,通过不断的学习和尝试,最终我还是顺利地完成了整个实验,而且还做出了一些我自己的小创新呢!在聊天室中,我添加了一些奇奇怪怪的表情包(后续),让大家在聊天的时候更加有趣!还有,我还设计了一个黑名单功能,用于限制欠费用户的操作,免得他们一直透支导致公司亏损(迫真)。

综上所述,这门课还是很有趣的,学到了很多编程知识,还能充分发挥了想象力和创新思维(眨眨眼)。

编程的路上,思维绕不停。C++、OOP,真头痛不已。 设计要合理,实现要清晰。程序设计路上我在不停奔忙, 编程让那岁月痕迹愈加沧桑!

  • 14
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
实现功能:(1)客户端开机自动琐定,输入帐号、密码后进行验证登陆并开始计费。或控制端远程解锁上机。 (2).控制端能够对上网的客户端进行远程控制,比如:远程关机、注销、锁定、远程结帐后又自动锁定计算机等功能。 (3)能对客户端发送消息。 (4)客户端在上机过程中能够随时查看消费状态,比如:预缴金额、上机时间、上机金额等。 MySql表结构: desc user; +----------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+-------------+------+-----+---------+-------+ | uid | varchar(30) | NO | PRI | 0 | | | password | varchar(30) | NO | | 0 | | | balance | double | NO | | 0 | | | isvip | int(2) | NO | | 0 | | +----------+-------------+------+-----+---------+-------+ 4 rows in set (0.00 sec) desc userhistory; +-----------+-------------+------+-----+-------------------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------+-------------+------+-----+-------------------+-------+ | cid | varchar(30) | NO | | 0 | | | uid | varchar(30) | NO | MUL | 0 | | | isvip | int(2) | NO | | 0 | | | logintime | timestamp | NO | | CURRENT_TIMESTAMP | | | usetime | varchar(30) | NO | | 00:00:00 | | | cost | double | NO | | 0 | | +-----------+-------------+------+-----+-------------------+-------+ 6 rows in set (0.00 sec) 涉及知识点:swing,socket和serverSocket,jdbc,多线程,心跳包建立长连接,通信消息封装为xml。。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值