软件逆向设计二次开发:书店购书管理系统重构与改进

软件逆向设计二次开发:书店购书管理系统重构与改进

一、项目来源

本次作业选择的项目来源于同学大一C++程序设计期末报告: BuyBook.cpp,该程序实现了一个简易的书店购书管理系统,主要包含以下功能:

  • 会员管理
  • 图书管理
  • 购书交易处理
  • 交易记录查看
  • 数据文件读写(永久)

原始代码采用面向对象方式设计,定义了 BuyerBookPurchaseLibraryManager 等类,初步具备一定的软件工程结构,适合作为本次“阅读分析—找缺陷—重构优化—测试验证”的二次开发对象。

二、运行环境 + 运行结果

1. 运行环境

  • 操作系统:
    image

  • 软件版本:
    Microsoft Visual Studio Community 2022 (64 位) - Current
    版本 17.11.5

2. 原程序运行情况

原程序能够完成基本菜单显示、图书购买、会员管理和交易记录保存,但在复读源码和实际运行中发现以下不足:

  • 大量使用 system("cls")system("pause"),导致该C++项目仅限于 Windows 环境;
  • 输入读取大多使用 cin >>,测试中发现当输入带空格的书名、出版社、地址等文本时无法处理;
  • 交易金额逻辑存在缺陷,累计消费与“本次实付金额”混在一起;
  • 添加图书、添加会员时缺少重复校验与数值合法性校验;
  • 搜索功能只支持完全匹配,实用性不高。

3. 运行结果截图

image

4. 源码

点击查看源码
#include <iostream>
#include <string>
#include <fstream>
#include <iomanip>
#include <vector>
#include <cstdlib>
#include <sstream>
using namespace std;

// 购书者基类
class Buyer {
protected:
    string name;        // 姓名
    int buyerID;        // 购书人编号
    string address;     // 地址
    double totalPay;    // 总购书费用
public:
    Buyer();
    Buyer(string n, int b, string a, double p);
    string getBuyerName() const;    // 取姓名
    string getAddress() const;      // 取地址
    double getTotalPay() const;     // 取总费用
    int getID() const;              // 取购书人编号
    virtual string getType() const = 0; // 获取买家类型
    virtual string getDetail() const = 0; // 获取详细信息
    virtual void display() const = 0;     // 显示对象
    virtual void calculatePay(double price) = 0;    // 计算购书费用
    virtual ~Buyer() {}; // 虚析构函数
};

// 会员类
class Member : public Buyer {
private:
    int memberGrade;      // 会员级别
public:
    Member(string n, int b, int l, string a, double p) : Buyer(n, b, a, p) {
        memberGrade = l;
    }   // 构造函数
    string getType() const override { return "M"; }
    string getDetail() const override { return to_string(memberGrade); }
    void display() const override;         // 显示函数
    void calculatePay(double price) override;  // 计算购书费用
};

// 贵宾类
class HonouredGuest : public Buyer {
private:
    double discountRate;   // 折扣率
public:
    HonouredGuest(string n, int b, double r, string a, double p) : Buyer(n, b, a, p) {
        discountRate = r;
    }   // 构造函数
    string getType() const override { return "H"; }
    string getDetail() const override { return to_string(discountRate); }
    void display() const override;         // 显示对象
    void calculatePay(double price) override;  // 计算购书费用
};

// 普通人类
class Layfolk : public Buyer {
public:
    Layfolk(string n, int b, string a, double p) : Buyer(n, b, a, p) {}
    // 构造函数
    string getType() const override { return "L"; }
    string getDetail() const override { return ""; }
    void display() const override;         // 显示对象
    void calculatePay(double price) override;  // 计算购书费用
};

// 临时顾客类
class TemporaryBuyer : public Buyer {
public:
    TemporaryBuyer(string n, string a, double p) : Buyer(n, 0, a, p) {}
    string getType() const override { return "T"; }
    string getDetail() const override { return ""; }
    void display() const override;
    void calculatePay(double price) override;
};

// 图书类
class Book {
protected:
    string bookID;         // 书号
    string bookName;       // 书名
    string author;         // 作者
    string publishing;     // 出版社
    double price;          // 定价
    int stock;             // 库存
public:
    // 构造函数
    Book();
    Book(string b_id, string b_n, string au, string pu, double pr, int st);
    void display() const;        // 显示图书信息
    string getBookID() const;    // 取书号
    string getBookName() const;  // 取书名
    string getAuthor() const;    // 取作者
    string getPublishing() const; // 取出版社
    double getPrice() const;     // 取定价
    int getStock() const;        // 取库存
    void setStock(int st);       // 设置库存
};

// 购买记录类
class Purchase {
private:
    string buyerName;      // 购书人姓名
    int buyerID;           // 购书人编号
    string date;           // 购买日期
    string address;        // 地址
    double totalAmount;    // 应付金额
    double actualAmount;   // 实付金额
    vector<string> bookIDs; // 购买的图书ID列表
public:
    Purchase(string bn, int bid, string d, string addr, double total, double actual);
    void addBookID(string bookID);
    void display(const vector<Book>& books) const;
    string getDate() const;
    string getBuyerName() const;
    int getBuyerID() const;
    string getAddress() const;
    double getTotalAmount() const;
    double getActualAmount() const;
    const vector<string>& getBookIDs() const;
};

// 购书者基类实现
Buyer::Buyer() {
    name = "";
    buyerID = 0;
    address = "";
    totalPay = 0;
}

Buyer::Buyer(string n, int b, string a, double p) {
    name = n;
    buyerID = b;
    address = a;
    totalPay = p;
}

string Buyer::getBuyerName() const {
    return name;
}

string Buyer::getAddress() const {
    return address;
}

double Buyer::getTotalPay() const {
    return totalPay;
}

int Buyer::getID() const {
    return buyerID;
}

// 会员类实现
void Member::display() const {
    cout << left << setw(10) << name << setw(10) << buyerID
        << setw(15) << "会员(级别:" << memberGrade << ")"
        << setw(15) << address << setw(10) << totalPay << endl;
}

void Member::calculatePay(double price) {
    double discount = 1.0;
    switch (memberGrade) {
    case 1: discount = 0.95; break;
    case 2: discount = 0.90; break;
    case 3: discount = 0.85; break;
    case 4: discount = 0.80; break;
    case 5: discount = 0.70; break;
    default: cout << "会员级别错误!" << endl; return;
    }
    totalPay += price * discount;
}

// 贵宾类实现
void HonouredGuest::display() const {
    cout << left << setw(10) << name << setw(10) << buyerID
        << setw(9) << "贵宾(折扣:" << discountRate * 100 << "%) "
        << setw(15) << address << setw(10) << totalPay << endl;
}

void HonouredGuest::calculatePay(double price) {
    totalPay += price * (1 - discountRate);
}

// 普通人类实现
void Layfolk::display() const {
    cout << left << setw(10) << name << setw(10) << buyerID
        << setw(15) << "普通人"
        << setw(15) << address << setw(10) << totalPay << endl;
}

void Layfolk::calculatePay(double price) {
    totalPay += price;
}

// 临时顾客实现
void TemporaryBuyer::display() const {
    cout << left << setw(10) << name << setw(10) << buyerID
        << setw(15) << "临时顾客"
        << setw(15) << address << setw(10) << totalPay << endl;
}

void TemporaryBuyer::calculatePay(double price) {
    totalPay += price;
}

// 图书类实现
Book::Book() {
    bookID = "";
    bookName = "";
    author = "";
    publishing = "";
    price = 0;
    stock = 0;
}

Book::Book(string b_id, string b_n, string au, string pu, double pr, int st) {
    bookID = b_id;
    bookName = b_n;
    author = au;
    publishing = pu;
    price = pr;
    stock = st;
}

void Book::display() const {
    cout << left << setw(15) << bookID << setw(20) << bookName
        << setw(15) << author << setw(15) << publishing
        << setw(10) << price << setw(10) << stock << endl;
}

string Book::getBookID() const {
    return bookID;
}

string Book::getBookName() const {
    return bookName;
}

string Book::getAuthor() const {
    return author;
}

string Book::getPublishing() const {
    return publishing;
}

double Book::getPrice() const {
    return price;
}

int Book::getStock() const {
    return stock;
}

void Book::setStock(int st) {
    stock = st;
}

// 购买记录类实现
Purchase::Purchase(string bn, int bid, string d, string addr, double total, double actual) {
    buyerName = bn;
    buyerID = bid;
    date = d;
    address = addr;
    totalAmount = total;
    actualAmount = actual;
}

void Purchase::addBookID(string bookID) {
    bookIDs.push_back(bookID);
}

void Purchase::display(const vector<Book>& books) const {
    cout << "\n===================================================================================" << endl;
    cout << "购书人: " << buyerName << " (ID: " << buyerID << ")" << endl;
    cout << "日期: " << date << ", 地址: " << address << endl;
    cout << "应付金额: " << totalAmount << ", 实付金额: " << actualAmount << endl;
    cout << "\n购买的图书:" << endl;
    cout << left << setw(25) << "书号" << setw(20) << "书名"
        << setw(15) << "作者" << setw(15) << "出版社"
        << setw(10) << "定价" << endl;

    for (const auto& bookID : bookIDs) {
        bool found = false;
        for (const auto& book : books) {
            if (book.getBookID() == bookID) {
                cout << left << setw(25) << book.getBookID()
                    << setw(20) << book.getBookName()
                    << setw(15) << book.getAuthor()
                    << setw(15) << book.getPublishing()
                    << setw(10) << book.getPrice() << endl;
                found = true;
                break;
            }
        }
        if (!found) {
            cout << left << setw(25) << bookID << setw(20) << "[未知图书]"
                << setw(15) << "" << setw(15) << ""
                << setw(10) << 0.0 << endl;
        }
    }
    cout << "===================================================================================" << endl;
}

string Purchase::getDate() const {
    return date;
}

string Purchase::getBuyerName() const {
    return buyerName;
}

int Purchase::getBuyerID() const {
    return buyerID;
}

string Purchase::getAddress() const {
    return address;
}

double Purchase::getTotalAmount() const {
    return totalAmount;
}

double Purchase::getActualAmount() const {
    return actualAmount;
}

const vector<string>& Purchase::getBookIDs() const {
    return bookIDs;
}

// 字符串分割函数
vector<string> split(const string& s, char delimiter) {
    vector<string> tokens;
    string token;
    istringstream tokenStream(s);
    while (getline(tokenStream, token, delimiter)) {
        tokens.push_back(token);
    }
    return tokens;
}

// 图书馆管理类
class LibraryManager {
private:
    vector<Buyer*> buyers;
    vector<Book> books;
    vector<Purchase> purchases;
    vector<TemporaryBuyer> tempBuyers;
    string getCurrentDate();
    void createEmptyFilesIfNotExist();
public:
    LibraryManager();
    ~LibraryManager();
    void loadData();
    void saveData();
    void mainMenu();
    void buyerManagementMenu();
    void bookManagementMenu();
    void transactionMenu();
    void displayAllBuyers();
    void displayAllBooks();
    void addBuyer();
    void addBook();
    void makeTransaction();
    void searchBuyerByName();
    void searchBookByName();
    void displayAllPurchases();
};

LibraryManager::LibraryManager() {
    createEmptyFilesIfNotExist();
    loadData();
}

LibraryManager::~LibraryManager() {
    for (auto buyer : buyers) {
        delete buyer;
    }
}

void LibraryManager::createEmptyFilesIfNotExist() {
    // 检查并创建空的数据文件
    ifstream checkFile("LibraryHouse.txt");
    if (!checkFile.good()) {
        ofstream("LibraryHouse.txt") << "书号,书名,作者,出版社,价格,库存\n";
    }
    checkFile.close();

    checkFile.open("LibraryDeal.txt");
    if (!checkFile.good()) {
        ofstream("LibraryDeal.txt") << "购书人姓名,购书人编号,日期,地址,应付金额,实付金额,购买的书号列表\n";
    }
    checkFile.close();

    checkFile.open("LibraryMember.txt");
    if (!checkFile.good()) {
        ofstream("LibraryMember.txt") << "类型,姓名,编号,会员级别/折扣率,地址,总消费金额\n";
    }
    checkFile.close();

    checkFile.open("LibraryTem.txt");
    if (!checkFile.good()) {
        ofstream("LibraryTem.txt") << "姓名,地址,总消费金额\n";
    }
    checkFile.close();
}

string LibraryManager::getCurrentDate() {
    time_t now = time(0);
    tm localTime;

#ifdef _MSC_VER  // Microsoft Visual C++
    errno_t err = localtime_s(&localTime, &now);
    if (err != 0) {
        return "错误日期";
    }
#else  // 其他编译器
    if (localtime_r(&now, &localTime) == nullptr) {
        return "错误日期";
    }
#endif

    char buffer[80];
    strftime(buffer, sizeof(buffer), "%Y-%m-%d", &localTime);
    return string(buffer);
}

void LibraryManager::loadData() {
    // 从文件加载图书库存
    ifstream bookFile("LibraryHouse.txt");
    string line;

    // 跳过标题行
    getline(bookFile, line);

    while (getline(bookFile, line)) {
        vector<string> tokens = split(line, ',');
        if (tokens.size() >= 6) {
            books.push_back(Book(
                tokens[0], tokens[1], tokens[2], tokens[3],
                stod(tokens[4]), stoi(tokens[5])
            ));
        }
    }
    bookFile.close();

    // 从文件加载会员数据
    ifstream memberFile("LibraryMember.txt");

    // 跳过标题行
    getline(memberFile, line);

    while (getline(memberFile, line)) {
        vector<string> tokens = split(line, ',');
        if (tokens.size() >= 6) {
            string type = tokens[0];
            string name = tokens[1];
            int id = stoi(tokens[2]);
            string detail = tokens[3];
            string address = tokens[4];
            double totalPay = stod(tokens[5]);

            if (type == "M") {  // 会员
                buyers.push_back(new Member(name, id, stoi(detail), address, totalPay));
            }
            else if (type == "H") {  // 贵宾
                buyers.push_back(new HonouredGuest(name, id, stod(detail), address, totalPay));
            }
            else if (type == "L") {  // 普通人
                buyers.push_back(new Layfolk(name, id, address, totalPay));
            }
        }
    }
    memberFile.close();

    // 从文件加载临时顾客
    ifstream tempFile("LibraryTem.txt");

    // 跳过标题行
    getline(tempFile, line);

    while (getline(tempFile, line)) {
        vector<string> tokens = split(line, ',');
        if (tokens.size() >= 3) {
            tempBuyers.push_back(TemporaryBuyer(
                tokens[0], tokens[1], stod(tokens[2])
            ));
        }
    }
    tempFile.close();

    // 从文件加载交易记录
    ifstream dealFile("LibraryDeal.txt");

    // 跳过标题行
    getline(dealFile, line);

    while (getline(dealFile, line)) {
        vector<string> tokens = split(line, ',');
        if (tokens.size() >= 7) {
            string buyerName = tokens[0];
            int buyerID = stoi(tokens[1]);
            string date = tokens[2];
            string address = tokens[3];
            double totalAmount = stod(tokens[4]);
            double actualAmount = stod(tokens[5]);

            Purchase purchase(buyerName, buyerID, date, address, totalAmount, actualAmount);

            // 从第7个元素开始是书号
            for (size_t i = 6; i < tokens.size(); i++) {
                purchase.addBookID(tokens[i]);
            }

            purchases.push_back(purchase);
        }
    }
    dealFile.close();

    cout << "数据加载完成!" << endl;
}

void LibraryManager::saveData() {
    // 保存图书库存到文件
    ofstream bookFile("LibraryHouse.txt");
    bookFile << "书号,书名,作者,出版社,价格,库存\n";

    for (const auto& book : books) {
        bookFile << book.getBookID() << ","
            << book.getBookName() << ","
            << book.getAuthor() << ","
            << book.getPublishing() << ","
            << book.getPrice() << ","
            << book.getStock() << "\n";
    }
    bookFile.close();

    // 保存会员数据到文件
    ofstream memberFile("LibraryMember.txt");
    memberFile << "类型,姓名,编号,会员级别/折扣率,地址,总消费金额\n";

    for (const auto& buyer : buyers) {
        memberFile << buyer->getType() << ","
            << buyer->getBuyerName() << ","
            << buyer->getID() << ","
            << buyer->getDetail() << ","
            << buyer->getAddress() << ","
            << buyer->getTotalPay() << "\n";
    }
    memberFile.close();

    // 保存临时顾客到文件
    ofstream tempFile("LibraryTem.txt");
    tempFile << "姓名,地址,总消费金额\n";

    for (const auto& tempBuyer : tempBuyers) {
        tempFile << tempBuyer.getBuyerName() << ","
            << tempBuyer.getAddress() << ","
            << tempBuyer.getTotalPay() << "\n";
    }
    tempFile.close();

    // 保存交易记录到文件
    ofstream dealFile("LibraryDeal.txt");
    dealFile << "购书人姓名,购书人编号,日期,地址,应付金额,实付金额,购买的书号列表\n";

    for (const auto& purchase : purchases) {
        dealFile << purchase.getBuyerName() << ","
            << purchase.getBuyerID() << ","
            << purchase.getDate() << ","
            << purchase.getAddress() << ","
            << purchase.getTotalAmount() << ","
            << purchase.getActualAmount();

        const auto& bookIDs = purchase.getBookIDs();
        for (const auto& bookID : bookIDs) {
            dealFile << "," << bookID;
        }
        dealFile << "\n";
    }
    dealFile.close();

    cout << "数据保存完成!" << endl;
}

void LibraryManager::mainMenu() {
    while (true) {
        system("cls");
        cout << "╒═书店购书管理系统═══════════════╕" << endl;
        cout << "│                                │" << endl;
        cout << "│                                │" << endl;
        cout << "│        1. 会员管理             │" << endl;
        cout << "│        2. 图书管理             │" << endl;
        cout << "│        3. 交易处理             │" << endl;
        cout << "│        4. 查看交易记录         │" << endl;
        cout << "│        5. 更改主题             │" << endl;
        cout << "│        0. 退出系统             │" << endl;
        cout << "│                                │" << endl;
        cout << "╘════════════════════════════════╛" << endl;
        cout << "请输入您的选择: ";

        int choice;
        cin >> choice;

        switch (choice) {
        case 1:
            buyerManagementMenu();
            break;
        case 2:
            bookManagementMenu();
            break;
        case 3:
            transactionMenu();
            break;
        case 4:
            displayAllPurchases();
            system("pause");
            break;
        case 5:
            printf("\033[0m╒════════════════════════════╕\n");
            printf("│ 请选择:                   │\n");
            printf("│                            │\n");
            printf("│      \033[0m默认0                 │\n");
            printf("│      \033[31m主题1\033[0m                 │\n");
            printf("│      \033[32m主题2\033[0m                 │\n");
            printf("│      \033[33m主题3\033[0m                 │\n");
            printf("│      \033[34m主题4\033[0m                 │\n");
            printf("│      \033[35m主题5\033[0m                 │\n");
            printf("│      \033[36m主题6\033[0m                 │\n");
            printf("│                            │\n");
            printf("╘════════════════════════════╛\033[0m\n");
            cout << "请输入所需主题编号:" << endl;
            int themenum;
            cin >> themenum;
            switch (themenum) {
            case 0:
                printf("\033[0m1:\033[1m修改成功!\n");
                break;
            case 1:
                cout << "\033[31m修改成功!" << endl;
                break;
            case 2:
                cout << "\033[32m修改成功!" << endl;
                break;
            case 3:
                cout << "\033[33m修改成功!" << endl;
                break;
            case 4:
                cout << "\033[34m修改成功!" << endl;
                break;
            case 5:
                cout << "\033[35m修改成功!" << endl;
                break;
            case 6:
                cout << "\033[36m修改成功!" << endl;
                break;
            default:
                cout << "无效选择,请重新输入!" << endl;
                system("pause");
            }

            system("pause");
            break;
        case 0:
            saveData();  // 退出前保存数据
            cout << "感谢使用图书馆管理系统,再见!" << endl;
            return;
        default:
            cout << "无效选择,请重新输入!" << endl;
            system("pause");
        }
    }
}

void LibraryManager::buyerManagementMenu() {
    while (true) {
        system("cls");
        cout << "╒═════════════════会员管理═══╕ " << endl;
        cout << "│                            │" << endl;
        cout << "│                            │" << endl;
        cout << "│      1. 显示所有会员       │" << endl;
        cout << "│      2. 按姓名查找会员     │" << endl;
        cout << "│      3. 添加新会员         │" << endl;
        cout << "│      4. 返回主菜单         │" << endl;
        cout << "│                            │" << endl;
        cout << "╘════════════════════════════╛" << endl;
        cout << "请输入您的选择: ";

        int choice;
        cin >> choice;

        switch (choice) {
        case 1:
            displayAllBuyers();
            system("pause");
            break;
        case 2:
            searchBuyerByName();
            system("pause");
            break;
        case 3:
            addBuyer();
            system("pause");
            break;
        case 4:
            return;
        default:
            cout << "无效选择,请重新输入!" << endl;
            system("pause");
        }
    }
}

void LibraryManager::bookManagementMenu() {
    while (true) {
        system("cls");
        cout << "╒═════════════════图书管理═══╕ " << endl;
        cout << "│                            │" << endl;
        cout << "│                            │" << endl;
        cout << "│      1. 显示所有图书       │" << endl;
        cout << "│      2. 按书名查找图书     │" << endl;
        cout << "│      3. 添加新图书         │" << endl;
        cout << "│      4. 返回主菜单         │" << endl;
        cout << "│                            │" << endl;
        cout << "╘════════════════════════════╛" << endl;
        cout << "请输入您的选择: ";

        int choice;
        cin >> choice;

        switch (choice) {
        case 1:
            displayAllBooks();
            system("pause");
            break;
        case 2:
            searchBookByName();
            system("pause");
            break;
        case 3:
            addBook();
            system("pause");
            break;
        case 4:
            return;
        default:
            cout << "无效选择,请重新输入!" << endl;
            system("pause");
        }
    }
}

void LibraryManager::transactionMenu() {
    while (true) {
        system("cls");
        cout << "╒═════════════════交易处理═══╕ " << endl;
        cout << "│                            │" << endl;
        cout << "│                            │" << endl;
        cout << "│      1. 购买图书           │" << endl;
        cout << "│      2. 返回主菜单         │" << endl;
        cout << "│                            │" << endl;
        cout << "╘════════════════════════════╛" << endl;
        cout << "请输入您的选择: ";

        int choice;
        cin >> choice;

        switch (choice) {
        case 1:
            makeTransaction();
            system("pause");
            break;
        case 2:
            return;
        default:
            cout << "无效选择,请重新输入!" << endl;
            system("pause");
        }
    }
}

void LibraryManager::displayAllBuyers() {
    system("cls");
    cout << "======================== 会员信息列表 ========================" << endl;
    cout << left << setw(10) << "姓名" << setw(10) << "编号"
        << setw(15) << "会员类型" << setw(15) << "地址"
        << setw(10) << "消费金额" << endl;
    cout << "--------------------------------------------------------------\n" << endl;

    for (const auto& buyer : buyers) {
        buyer->display();
    }

    cout << "\n======================== 临时顾客列表 ========================" << endl;
    for (const auto& tempBuyer : tempBuyers) {
        tempBuyer.display();
    }
    cout << "--------------------------------------------------------------\n" << endl;
}

void LibraryManager::displayAllBooks() {
    system("cls");
    cout << "======== 图书信息列表 ================================" << endl;
    cout << left << setw(25) << "书号" << setw(20) << "书名"
        << setw(15) << "作者" << setw(15) << "出版社"
        << setw(10) << "定价" << setw(10) << "库存" << endl;
    cout << "--------------------------------------------------------------\n" << endl;

    for (const auto& book : books) {
        book.display();
    }
    cout << "--------------------------------------------------------------\n" << endl;
}

void LibraryManager::addBuyer() {
    system("cls");
    cout << "==================== 添加新会员 ====================" << endl;

    string name, address;
    int id, type, grade;
    double discount, initialPay = 0;

    cout << "请输入会员姓名: ";
    cin >> name;
    cout << "请输入会员编号: ";
    cin >> id;
    cout << "请输入会员地址: ";
    cin >> address;
    cout << "请输入会员类型 (1: 普通会员, 2: 贵宾会员, 3: 普通用户): ";
    cin >> type;

    Buyer* newBuyer = nullptr;
    if (type == 1) {
        cout << "请输入会员级别 (1-5): ";
        cin >> grade;
        newBuyer = new Member(name, id, grade, address, initialPay);
    }
    else if (type == 2) {
        cout << "请输入折扣率 (0-1): ";
        cin >> discount;
        newBuyer = new HonouredGuest(name, id, discount, address, initialPay);
    }
    else if (type == 3) {
        newBuyer = new Layfolk(name, id, address, initialPay);
    }
    else {
        cout << "无效的会员类型!" << endl;
        return;
    }

    buyers.push_back(newBuyer);
    cout << "会员添加成功!" << endl;
}

void LibraryManager::addBook() {
    system("cls");
    cout << "==================== 添加新图书 ====================" << endl;

    string id, name, author, publishing;
    double price;
    int stock;

    cout << "请输入书号: ";
    cin >> id;
    cout << "请输入书名: ";
    cin >> name;
    cout << "请输入作者: ";
    cin >> author;
    cout << "请输入出版社: ";
    cin >> publishing;
    cout << "请输入定价: ";
    cin >> price;
    cout << "请输入库存: ";
    cin >> stock;

    books.push_back(Book(id, name, author, publishing, price, stock));
    cout << "图书添加成功!" << endl;
}

void LibraryManager::searchBuyerByName() {
    system("cls");
    cout << "==================== 按姓名查找会员 ====================" << endl;

    string name;
    cout << "请输入会员姓名: ";
    cin >> name;

    bool found = false;
    cout << left << setw(10) << "姓名" << setw(10) << "编号"
        << setw(15) << "会员类型" << setw(15) << "地址"
        << setw(10) << "消费金额" << endl;
    cout << "--------------------------------------------------------------\n" << endl;

    for (const auto& buyer : buyers) {
        if (buyer->getBuyerName() == name) {
            buyer->display();
            found = true;
        }
    }

    // 查找临时顾客
    for (const auto& tempBuyer : tempBuyers) {
        if (tempBuyer.getBuyerName() == name) {
            tempBuyer.display();
            found = true;
        }
    }

    if (!found) {
        cout << "未找到该会员!" << endl;
    }
}

void LibraryManager::searchBookByName() {
    system("cls");
    cout << "==================== 按书名查找图书 ====================" << endl;

    string name;
    cout << "请输入图书名称: ";
    cin >> name;

    bool found = false;
    cout << left << setw(15) << "书号" << setw(20) << "书名"
        << setw(15) << "作者" << setw(15) << "出版社"
        << setw(10) << "定价" << setw(10) << "库存" << endl;
    cout << "--------------------------------------------------------------\n" << endl;

    for (const auto& book : books) {
        if (book.getBookName() == name) {
            book.display();
            found = true;
        }
    }

    if (!found) {
        cout << "未找到该图书!" << endl;
    }
}

void LibraryManager::makeTransaction() {
    system("cls");
    cout << "==================== 购买图书 ====================" << endl;

    int buyerId;
    cout << "请输入购书人编号 (新顾客请输入0): ";
    cin >> buyerId;

    Buyer* buyer = nullptr;
    TemporaryBuyer* tempBuyer = nullptr;

    // 查找正式会员
    if (buyerId != 0) {
        for (auto b : buyers) {
            if (b->getID() == buyerId) {
                buyer = b;
                break;
            }
        }

        if (!buyer) {
            cout << "未找到该购书人!" << endl;
            return;
        }
    }
    else {
        // 处理临时顾客
        string name, address;
        cout << "您是新顾客,将被记录为临时顾客" << endl;
        cout << "请输入您的姓名: ";
        cin >> name;
        cout << "请输入您的地址: ";
        cin >> address;

        // 检查是否已有同名临时顾客
        for (auto& tb : tempBuyers) {
            if (tb.getBuyerName() == name) {
                tempBuyer = &tb;
                break;
            }
        }

        // 如果没有,创建新的临时顾客
        if (!tempBuyer) {
            tempBuyers.push_back(TemporaryBuyer(name, address, 0));
            tempBuyer = &tempBuyers.back();
        }
    }

    displayAllBooks();

    vector<string> selectedBookIDs;
    double totalPrice = 0;

    cout << "\n请输入要购买的图书数量: ";
    int count;
    cin >> count;

    for (int i = 0; i < count; i++) {
        string bookId;
        cout << "请输入第 " << (i + 1) << " 本图书的书号: ";
        cin >> bookId;

        bool found = false;
        for (auto& book : books) {
            if (book.getBookID() == bookId) {
                if (book.getStock() > 0) {
                    selectedBookIDs.push_back(bookId);
                    totalPrice += book.getPrice();
                    book.setStock(book.getStock() - 1);
                    found = true;
                    break;
                }
                else {
                    cout << "该图书库存不足!" << endl;
                    i--;
                    found = true;
                    break;
                }
            }
        }

        if (!found) {
            cout << "未找到该图书!" << endl;
            i--;
        }
    }

    // 计算实际支付金额
    double actualAmount = totalPrice;
    if (buyer) {
        buyer->calculatePay(totalPrice);
        actualAmount = buyer->getTotalPay();
    }
    else if (tempBuyer) {
        tempBuyer->calculatePay(totalPrice);
        actualAmount = tempBuyer->getTotalPay();
    }

    // 创建交易记录
    string buyerName = buyer ? buyer->getBuyerName() : tempBuyer->getBuyerName();
    int buyerIdRecord = buyer ? buyer->getID() : 0;
    string address = buyer ? buyer->getAddress() : tempBuyer->getAddress();

    Purchase purchase(buyerName, buyerIdRecord, getCurrentDate(), address, totalPrice, actualAmount);
    for (const auto& bookId : selectedBookIDs) {
        purchase.addBookID(bookId);
    }

    purchases.push_back(purchase);

    cout << "\n交易完成!" << endl;
    cout << "应付金额: " << totalPrice << " 元" << endl;
    cout << "实付金额: " << actualAmount << " 元" << endl;
}
void LibraryManager::displayAllPurchases() {
    system("cls");
    cout << "╒═════════ 交易记录列表════════════════════════════════════════════════════════════╕\n " << endl;

    if (purchases.empty()) {
        cout << "当前没有交易记录!" << endl;
        return;
    }

    for (size_t i = 0; i < purchases.size(); i++) {
        cout << "\n ═══ 交易记录 " << (i + 1) << " ═══════════════════════════════════════════════════════════════════" << endl;
        purchases[i].display(books);
        cout << endl;
    }
}
int main() {
    LibraryManager manager;
    manager.mainMenu();
    return 0;
}

三、原程序主要问题列表

问题 1:平台兼容性差

原程序在多个菜单中直接调用:

system("cls");
system("pause");

这使程序强依赖 Windows 命令环境。在 Linux/macOS 下运行会出现:

  • cls: not found
  • pause: not found

改进思路:
封装跨平台清屏与暂停逻辑;至少应避免把平台相关代码散落在多个函数中,提升可维护性。


问题 2:输入处理不健壮

原代码大量使用:

cin >> name;
cin >> address;
cin >> publishing;

这意味着:

  • 地址中不能包含空格
  • 书名中不能包含空格
  • 出版社名称不能包含空格
  • 用户一旦误输入字符到数值字段,程序容易进入异常状态

改进思路:

  • 统一封装输入函数
  • 文本输入使用 getline(cin >> ws, str)
  • 数值输入增加循环校验和错误恢复

问题 3:交易金额设计存在逻辑错误

原程序在购买时的处理如下:

buyer->calculatePay(totalPrice);
actualAmount = buyer->getTotalPay();

这里的 getTotalPay() 表示累计消费总额,却被直接赋给了 actualAmount(本次实付金额)。

也就是说:

  • 第一次买书时看起来正常
  • 第二次买书开始,actualAmount 会混入历史消费,导致交易记录失真

改进思路:
将“本次订单应付金额计算”和“用户累计消费更新”分离:

  • calculateAmount(price):只计算本次订单的实付金额
  • addConsume(amount):将本次实付金额加入累计消费

问题 4:缺少唯一性与合法性校验

原程序添加会员、图书时没有检查:

  • 会员编号是否重复
  • 书号是否重复
  • 价格是否小于 0
  • 库存是否小于 0
  • 会员等级是否超出范围
  • 贵宾折扣率是否越界

改进思路:
在新增逻辑前加入业务校验,避免无效数据写入文件。


问题 5:搜索功能体验较差

原程序按姓名、按书名查询时,只支持:

if (book.getBookName() == name)

这要求输入必须完全一致,不支持模糊查询。

改进思路:
改为子串匹配,提高使用体验:

if (book.getBookName().find(target) != string::npos)

问题 6:显示逻辑和业务逻辑耦合较重

菜单、输入、数据校验、业务计算、文件保存都集中在 LibraryManager 内,尽管功能完整,但扩展性一般。

改进思路:
本次重构先进行“轻量级重构”:

  • 封装输入函数
  • 封装查找函数
  • 封装平台相关函数
  • 明确区分“订单金额”和“累计消费”

四、二次开发与重构方案

1. 重构目标

在保留原系统主要结构和业务流程的前提下,提高:

  • 可维护性
  • 输入健壮性
  • 平台兼容性
  • 数据正确性
  • 用户使用体验

2. 具体改进内容

(1)新增统一输入函数

增加:

  • readLine()
  • readInt()
  • readDouble()

作用:

  • 支持带空格文本输入
  • 支持错误输入恢复
  • 避免反复编写相同输入判断逻辑

(2)重构支付计算逻辑

新增:

  • calculateAmount(double price) const
  • addConsume(double amount)

作用:

  • 订单实付金额单独计算
  • 累计消费单独更新
  • 修复交易记录中的金额失真问题

(3)增加数据校验

加入以下校验:

  • 会员编号不可重复
  • 书号不可重复
  • 会员级别必须在 1~5
  • 折扣率必须在 0~1
  • 价格与库存不能小于 0
  • 购买数量必须大于 0

(4)提升查询能力

  • 会员查询支持模糊匹配
  • 图书查询支持模糊匹配

(5)优化跨平台兼容性

把清屏操作封装为 clearScreen(),避免原代码中大量散落 system("cls")


(6)代码示例

1. 本次订单金额与累计消费分离
virtual double calculateAmount(double price) const = 0;
void addConsume(double amount) { totalPay += amount; }

会员类中:

double calculateAmount(double price) const override {
    double discount = 1.0;
    switch (memberGrade) {
    case 1: discount = 0.95; break;
    case 2: discount = 0.90; break;
    case 3: discount = 0.85; break;
    case 4: discount = 0.80; break;
    case 5: discount = 0.70; break;
    default: discount = 1.0; break;
    }
    return price * discount;
}

交易处理中:

double actualAmount = buyer ? buyer->calculateAmount(totalPrice)
                            : tempBuyer->calculateAmount(totalPrice);
if (buyer) buyer->addConsume(actualAmount);
else tempBuyer->addConsume(actualAmount);
2. 输入函数封装
static string readLine(const string& prompt) {
    cout << prompt;
    string s;
    getline(cin >> ws, s);
    return s;
}
static int readInt(const string& prompt) {
    int value;
    while (true) {
        cout << prompt;
        if (cin >> value) return value;
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
        cout << "输入无效,请输入整数。\n";
    }
}
3. 重复数据校验
bool buyerIdExists(int id) const {
    for (auto buyer : buyers) if (buyer->getID() == id) return true;
    return false;
}
Book* findBookById(const string& id) {
    for (auto& book : books) if (book.getBookID() == id) return &book;
    return nullptr;
}
4. 模糊查询
if (book.getBookName().find(target) != string::npos) {
    book.display();
}

(7)完整新代码

点击查看源码 #include #include #include #include #include #include #include #include #include using namespace std;

namespace UI {
static string g_theme = "\033[0m";

void setTheme(const string& themeCode) {
g_theme = themeCode;
cout << g_theme;
}

void clearScreen() {
cout << "\033[2J\033[H";
}

void pause() {
cout << "\n按回车继续...";
cin.ignore(numeric_limits::max(), '\n');
cin.get();
}

string border(int width, char ch = '=') {
return string(width, ch);
}

string repeatText(const string& text, int count) {
string result;
for (int i = 0; i < count; ++i) result += text;
return result;
}

void printTitle(const string& title, int width = 60) {
cout << g_theme;
cout << "╒" << repeatText("═", width - 2) << "╕\n";
int inner = width - 2;
int pad = max(0, inner - static_cast(title.size()));
int left = pad / 2;
int right = pad - left;
cout << "│" << string(left, ' ') << title << string(right, ' ') << "│\n";
cout << "╘" << repeatText("═", width - 2) << "╛\n";
}

void printThemeMenu() {
cout << "╒════════════════════════════╕\n";
cout << "│ 请选择主题: │\n";
cout << "│ │\n";
cout << "│ 0. 默认 │\n";
cout << "│ 1. 红色 │\n";
cout << "│ 2. 绿色 │\n";
cout << "│ 3. 黄色 │\n";
cout << "│ 4. 蓝色 │\n";
cout << "│ 5. 紫色 │\n";
cout << "│ 6. 青色 │\n";
cout << "│ │\n";
cout << "╘════════════════════════════╛\n";
}
}

class InputHelper {
public:
static string readLine(const string& prompt) {
cout << prompt;
string s;
getline(cin >> ws, s);
return s;
}

static int readInt(const string& prompt, int minValue = numeric_limits::min(), int maxValue = numeric_limits::max()) {
int value;
while (true) {
cout << prompt;
if (cin >> value) {
if (value < minValue || value > maxValue) {
cout << "输入超出范围,请重新输入。\n";
}
else {
return value;
}
}
else {
cout << "输入无效,请输入整数。\n";
cin.clear();
}
cin.ignore(numeric_limits::max(), '\n');
}
}

static double readDouble(const string& prompt, double minValue = -numeric_limits::max(), double maxValue = numeric_limits::max()) {
double value;
while (true) {
cout << prompt;
if (cin >> value) {
if (value < minValue || value > maxValue) {
cout << "输入超出范围,请重新输入。\n";
}
else {
return value;
}
}
else {
cout << "输入无效,请输入数字。\n";
cin.clear();
}
cin.ignore(numeric_limits::max(), '\n');
}
}
};

class Buyer {
protected:
string name;
int buyerID;
string address;
double totalPay;
public:
Buyer() : name(""), buyerID(0), address(""), totalPay(0) {}
Buyer(const string& n, int b, const string& a, double p) : name(n), buyerID(b), address(a), totalPay(p) {}

string getBuyerName() const { return name; }
string getAddress() const { return address; }
double getTotalPay() const { return totalPay; }
int getID() const { return buyerID; }
void addConsume(double amount) { totalPay += amount; }

virtual string getType() const = 0;
virtual string getDetail() const = 0;
virtual void display() const = 0;
virtual double calculateAmount(double price) const = 0;
virtual ~Buyer() {}
};

class Member : public Buyer {
private:
int memberGrade;
public:
Member(const string& n, int b, int l, const string& a, double p)
: Buyer(n, b, a, p), memberGrade(l) {}

string getType() const override { return "M"; }
string getDetail() const override { return to_string(memberGrade); }

double calculateAmount(double price) const override {
double discount = 1.0;
switch (memberGrade) {
case 1: discount = 0.95; break;
case 2: discount = 0.90; break;
case 3: discount = 0.85; break;
case 4: discount = 0.80; break;
case 5: discount = 0.70; break;
default: discount = 1.0; break;
}
return price * discount;
}

void display() const override {
ostringstream typeText;
typeText << "会员(级别:" << memberGrade << ")";
cout << left << setw(12) << name
<< setw(10) << buyerID
<< setw(20) << typeText.str()
<< setw(20) << address
<< fixed << setprecision(2) << totalPay << '\n';
}
};

class HonouredGuest : public Buyer {
private:
double discountRate;
public:
HonouredGuest(const string& n, int b, double r, const string& a, double p)
: Buyer(n, b, a, p), discountRate(r) {}

string getType() const override { return "H"; }
string getDetail() const override {
ostringstream oss;
oss << fixed << setprecision(2) << discountRate;
return oss.str();
}

double calculateAmount(double price) const override {
return price * (1.0 - discountRate);
}

void display() const override {
ostringstream typeText;
typeText << "贵宾(折扣:" << fixed << setprecision(0) << discountRate * 100 << "%)";
cout << left << setw(12) << name
<< setw(10) << buyerID
<< setw(20) << typeText.str()
<< setw(20) << address
<< fixed << setprecision(2) << totalPay << '\n';
}
};

class Layfolk : public Buyer {
public:
Layfolk(const string& n, int b, const string& a, double p)
: Buyer(n, b, a, p) {}

string getType() const override { return "L"; }
string getDetail() const override { return ""; }
double calculateAmount(double price) const override { return price; }

void display() const override {
cout << left << setw(12) << name
<< setw(10) << buyerID
<< setw(20) << "普通用户"
<< setw(20) << address
<< fixed << setprecision(2) << totalPay << '\n';
}
};

class TemporaryBuyer : public Buyer {
public:
TemporaryBuyer(const string& n, const string& a, double p)
: Buyer(n, 0, a, p) {}

string getType() const override { return "T"; }
string getDetail() const override { return ""; }
double calculateAmount(double price) const override { return price; }

void display() const override {
cout << left << setw(12) << name
<< setw(10) << buyerID
<< setw(20) << "临时顾客"
<< setw(20) << address
<< fixed << setprecision(2) << totalPay << '\n';
}
};

class Book {
private:
string bookID;
string bookName;
string author;
string publishing;
double price;
int stock;
public:
Book() : bookID(""), bookName(""), author(""), publishing(""), price(0), stock(0) {}
Book(const string& b_id, const string& b_n, const string& au, const string& pu, double pr, int st)
: bookID(b_id), bookName(b_n), author(au), publishing(pu), price(pr), stock(st) {}

void display() const {
cout << left << setw(15) << bookID
<< setw(24) << bookName
<< setw(18) << author
<< setw(22) << publishing
<< setw(10) << fixed << setprecision(2) << price
<< stock << '\n';
}

string getBookID() const { return bookID; }
string getBookName() const { return bookName; }
string getAuthor() const { return author; }
string getPublishing() const { return publishing; }
double getPrice() const { return price; }
int getStock() const { return stock; }
void setStock(int st) { stock = st; }
};

class Purchase {
private:
string buyerName;
int buyerID;
string date;
string address;
double totalAmount;
double actualAmount;
vector bookIDs;
public:
Purchase(const string& bn, int bid, const string& d, const string& addr, double total, double actual)
: buyerName(bn), buyerID(bid), date(d), address(addr), totalAmount(total), actualAmount(actual) {}

void addBookID(const string& bookID) { bookIDs.push_back(bookID); }

void display(const vector& books) const {
cout << UI::border(90, '=') << '\n';
cout << "购书人: " << buyerName << " (ID: " << buyerID << ")\n";
cout << "日期: " << date << ",地址: " << address << '\n';
cout << fixed << setprecision(2)
<< "应付金额: " << totalAmount << " 元,实付金额: " << actualAmount << " 元\n";
cout << UI::border(90, '-') << '\n';
cout << left << setw(15) << "书号"
<< setw(24) << "书名"
<< setw(18) << "作者"
<< setw(22) << "出版社"
<< "定价\n";
cout << UI::border(90, '-') << '\n';

for (const string& id : bookIDs) {
bool found = false;
for (const Book& book : books) {
if (book.getBookID() == id) {
cout << left << setw(15) << book.getBookID()
<< setw(24) << book.getBookName()
<< setw(18) << book.getAuthor()
<< setw(22) << book.getPublishing()
<< fixed << setprecision(2) << book.getPrice() << '\n';
found = true;
break;
}
}
if (!found) {
cout << left << setw(15) << id
<< setw(24) << "[未知图书]"
<< setw(18) << ""
<< setw(22) << ""
<< "0.00\n";
}
}
cout << UI::border(90, '=') << '\n';
}

string getDate() const { return date; }
string getBuyerName() const { return buyerName; }
int getBuyerID() const { return buyerID; }
string getAddress() const { return address; }
double getTotalAmount() const { return totalAmount; }
double getActualAmount() const { return actualAmount; }
const vector& getBookIDs() const { return bookIDs; }
};

vector split(const string& s, char delimiter) {
vector tokens;
string token;
istringstream tokenStream(s);
while (getline(tokenStream, token, delimiter)) {
tokens.push_back(token);
}
return tokens;
}

class LibraryManager {
private:
vector<Buyer*> buyers;
vector books;
vector purchases;
vector tempBuyers;

string getCurrentDate() const {
time_t now = time(nullptr);
tm localTime{};

ifdef _WIN32

localtime_s(&localTime, &now);

else

localtime_r(&now, &localTime);

endif

char buffer[32];
strftime(buffer, sizeof(buffer), "%Y-%m-%d", &localTime);
return string(buffer);
}

void createEmptyFilesIfNotExist() {
ifstream checkFile("LibraryHouse.txt");
if (!checkFile.good()) {
ofstream("LibraryHouse.txt") << "书号,书名,作者,出版社,价格,库存\n";
}
checkFile.close();

checkFile.open("LibraryDeal.txt");
if (!checkFile.good()) {
ofstream("LibraryDeal.txt") << "购书人姓名,购书人编号,日期,地址,应付金额,实付金额,购买的书号列表\n";
}
checkFile.close();

checkFile.open("LibraryMember.txt");
if (!checkFile.good()) {
ofstream("LibraryMember.txt") << "类型,姓名,编号,会员级别/折扣率,地址,总消费金额\n";
}
checkFile.close();

checkFile.open("LibraryTem.txt");
if (!checkFile.good()) {
ofstream("LibraryTem.txt") << "姓名,地址,总消费金额\n";
}
}

bool buyerIdExists(int id) const {
for (const auto buyer : buyers) {
if (buyer->getID() == id) return true;
}
return false;
}

bool bookIdExists(const string& id) const {
for (const auto& book : books) {
if (book.getBookID() == id) return true;
}
return false;
}

Buyer* findBuyerById(int id) {
for (auto buyer : buyers) {
if (buyer->getID() == id) return buyer;
}
return nullptr;
}

Book* findBookById(const string& id) {
for (auto& book : books) {
if (book.getBookID() == id) return &book;
}
return nullptr;
}

TemporaryBuyer* findTempBuyerByName(const string& name) {
for (auto& tb : tempBuyers) {
if (tb.getBuyerName() == name) return &tb;
}
return nullptr;
}

public:
LibraryManager() {
createEmptyFilesIfNotExist();
loadData();
}

~LibraryManager() {
for (auto buyer : buyers) {
delete buyer;
}
}

void loadData() {
string line;

ifstream bookFile("LibraryHouse.txt");
getline(bookFile, line);
while (getline(bookFile, line)) {
vector tokens = split(line, ',');
if (tokens.size() >= 6) {
books.emplace_back(tokens[0], tokens[1], tokens[2], tokens[3], stod(tokens[4]), stoi(tokens[5]));
}
}
bookFile.close();

ifstream memberFile("LibraryMember.txt");
getline(memberFile, line);
while (getline(memberFile, line)) {
vector tokens = split(line, ',');
if (tokens.size() >= 6) {
string type = tokens[0];
string name = tokens[1];
int id = stoi(tokens[2]);
string detail = tokens[3];
string address = tokens[4];
double totalPay = stod(tokens[5]);
if (type == "M") buyers.push_back(new Member(name, id, stoi(detail), address, totalPay));
else if (type == "H") buyers.push_back(new HonouredGuest(name, id, stod(detail), address, totalPay));
else if (type == "L") buyers.push_back(new Layfolk(name, id, address, totalPay));
}
}
memberFile.close();

ifstream tempFile("LibraryTem.txt");
getline(tempFile, line);
while (getline(tempFile, line)) {
vector tokens = split(line, ',');
if (tokens.size() >= 3) {
tempBuyers.emplace_back(tokens[0], tokens[1], stod(tokens[2]));
}
}
tempFile.close();

ifstream dealFile("LibraryDeal.txt");
getline(dealFile, line);
while (getline(dealFile, line)) {
vector tokens = split(line, ',');
if (tokens.size() >= 7) {
Purchase purchase(tokens[0], stoi(tokens[1]), tokens[2], tokens[3], stod(tokens[4]), stod(tokens[5]));
for (size_t i = 6; i < tokens.size(); ++i) {
purchase.addBookID(tokens[i]);
}
purchases.push_back(purchase);
}
}
dealFile.close();

cout << "数据加载完成!\n";
}

void saveData() {
ofstream bookFile("LibraryHouse.txt");
bookFile << "书号,书名,作者,出版社,价格,库存\n";
for (const auto& book : books) {
bookFile << book.getBookID() << ","
<< book.getBookName() << ","
<< book.getAuthor() << ","
<< book.getPublishing() << ","
<< book.getPrice() << ","
<< book.getStock() << "\n";
}
bookFile.close();

ofstream memberFile("LibraryMember.txt");
memberFile << "类型,姓名,编号,会员级别/折扣率,地址,总消费金额\n";
for (const auto buyer : buyers) {
memberFile << buyer->getType() << ","
<< buyer->getBuyerName() << ","
<< buyer->getID() << ","
<< buyer->getDetail() << ","
<< buyer->getAddress() << ","
<< buyer->getTotalPay() << "\n";
}
memberFile.close();

ofstream tempFile("LibraryTem.txt");
tempFile << "姓名,地址,总消费金额\n";
for (const auto& tempBuyer : tempBuyers) {
tempFile << tempBuyer.getBuyerName() << ","
<< tempBuyer.getAddress() << ","
<< tempBuyer.getTotalPay() << "\n";
}
tempFile.close();

ofstream dealFile("LibraryDeal.txt");
dealFile << "购书人姓名,购书人编号,日期,地址,应付金额,实付金额,购买的书号列表\n";
for (const auto& purchase : purchases) {
dealFile << purchase.getBuyerName() << ","
<< purchase.getBuyerID() << ","
<< purchase.getDate() << ","
<< purchase.getAddress() << ","
<< purchase.getTotalAmount() << ","
<< purchase.getActualAmount();
for (const auto& bookID : purchase.getBookIDs()) {
dealFile << "," << bookID;
}
dealFile << "\n";
}
dealFile.close();

cout << "数据保存完成!\n";
}

void displayAllBuyers() {
UI::clearScreen();
UI::printTitle("会员信息列表", 66);
cout << left << setw(12) << "姓名"
<< setw(10) << "编号"
<< setw(20) << "会员类型"
<< setw(20) << "地址"
<< "消费金额\n";
cout << UI::border(72, '-') << '\n';
for (const auto buyer : buyers) {
buyer->display();
}

cout << "\n";
UI::printTitle("临时顾客列表", 66);
cout << left << setw(12) << "姓名"
<< setw(10) << "编号"
<< setw(20) << "类型"
<< setw(20) << "地址"
<< "消费金额\n";
cout << UI::border(72, '-') << '\n';
for (const auto& tempBuyer : tempBuyers) {
tempBuyer.display();
}
}

void displayAllBooks() {
UI::clearScreen();
UI::printTitle("图书信息列表", 96);
cout << left << setw(15) << "书号"
<< setw(24) << "书名"
<< setw(18) << "作者"
<< setw(22) << "出版社"
<< setw(10) << "定价"
<< "库存\n";
cout << UI::border(96, '-') << '\n';
for (const auto& book : books) {
book.display();
}
}

void addBuyer() {
UI::clearScreen();
UI::printTitle("添加新会员", 54);

string name = InputHelper::readLine("请输入会员姓名: ");
int id = InputHelper::readInt("请输入会员编号: ", 1);
if (buyerIdExists(id)) {
cout << "会员编号已存在,添加失败!\n";
return;
}

string address = InputHelper::readLine("请输入会员地址: ");
int type = InputHelper::readInt("请输入会员类型 (1: 普通会员, 2: 贵宾会员, 3: 普通用户): ", 1, 3);

Buyer* newBuyer = nullptr;
if (type == 1) {
int grade = InputHelper::readInt("请输入会员级别 (1-5): ", 1, 5);
newBuyer = new Member(name, id, grade, address, 0.0);
}
else if (type == 2) {
double discount = InputHelper::readDouble("请输入折扣率 (0-1): ", 0.0, 1.0);
newBuyer = new HonouredGuest(name, id, discount, address, 0.0);
}
else {
newBuyer = new Layfolk(name, id, address, 0.0);
}

buyers.push_back(newBuyer);
cout << "会员添加成功!\n";
}

void addBook() {
UI::clearScreen();
UI::printTitle("添加新图书", 54);

string id = InputHelper::readLine("请输入书号: ");
if (bookIdExists(id)) {
cout << "书号已存在,添加失败!\n";
return;
}

string name = InputHelper::readLine("请输入书名: ");
string author = InputHelper::readLine("请输入作者: ");
string publishing = InputHelper::readLine("请输入出版社: ");
double price = InputHelper::readDouble("请输入定价: ", 0.0);
int stock = InputHelper::readInt("请输入库存: ", 0);

books.emplace_back(id, name, author, publishing, price, stock);
cout << "图书添加成功!\n";
}

void searchBuyerByName() {
UI::clearScreen();
UI::printTitle("按姓名查找会员", 54);

string target = InputHelper::readLine("请输入会员姓名关键字: ");
bool found = false;

cout << left << setw(12) << "姓名"
<< setw(10) << "编号"
<< setw(20) << "会员类型"
<< setw(20) << "地址"
<< "消费金额\n";
cout << UI::border(72, '-') << '\n';

for (const auto buyer : buyers) {
if (buyer->getBuyerName().find(target) != string::npos) {
buyer->display();
found = true;
}
}
for (const auto& tempBuyer : tempBuyers) {
if (tempBuyer.getBuyerName().find(target) != string::npos) {
tempBuyer.display();
found = true;
}
}

if (!found) {
cout << "未找到相关会员!\n";
}
}

void searchBookByName() {
UI::clearScreen();
UI::printTitle("按书名查找图书", 54);

string target = InputHelper::readLine("请输入图书名称关键字: ");
bool found = false;

cout << left << setw(15) << "书号"
<< setw(24) << "书名"
<< setw(18) << "作者"
<< setw(22) << "出版社"
<< setw(10) << "定价"
<< "库存\n";
cout << UI::border(96, '-') << '\n';

for (const auto& book : books) {
if (book.getBookName().find(target) != string::npos) {
book.display();
found = true;
}
}

if (!found) {
cout << "未找到相关图书!\n";
}
}

void makeTransaction() {
UI::clearScreen();
UI::printTitle("购买图书", 54);

int buyerId = InputHelper::readInt("请输入购书人编号 (新顾客请输入0): ", 0);
Buyer* buyer = nullptr;
TemporaryBuyer* tempBuyer = nullptr;

if (buyerId != 0) {
buyer = findBuyerById(buyerId);
if (!buyer) {
cout << "未找到该购书人!\n";
return;
}
}
else {
cout << "您是新顾客,将被记录为临时顾客。\n";
string name = InputHelper::readLine("请输入您的姓名: ");
string address = InputHelper::readLine("请输入您的地址: ");
tempBuyer = findTempBuyerByName(name);
if (!tempBuyer) {
tempBuyers.emplace_back(name, address, 0.0);
tempBuyer = &tempBuyers.back();
}
}

displayAllBooks();
int count = InputHelper::readInt("\n请输入要购买的图书数量: ", 1);

vector selectedBookIDs;
double totalPrice = 0.0;

for (int i = 0; i < count; ++i) {
string bookId = InputHelper::readLine("请输入第 " + to_string(i + 1) + " 本图书的书号: ");
Book* book = findBookById(bookId);
if (!book) {
cout << "未找到该图书,请重新输入。\n";
--i;
continue;
}
if (book->getStock() <= 0) {
cout << "该图书库存不足,请重新输入。\n";
--i;
continue;
}
selectedBookIDs.push_back(bookId);
totalPrice += book->getPrice();
book->setStock(book->getStock() - 1);
}

double actualAmount = buyer ? buyer->calculateAmount(totalPrice)
tempBuyer->calculateAmount(totalPrice);
if (buyer) {
buyer->addConsume(actualAmount);
}
else {
tempBuyer->addConsume(actualAmount);
}

string buyerName = buyer ? buyer->getBuyerName() : tempBuyer->getBuyerName();
int buyerIdRecord = buyer ? buyer->getID() : 0;
string address = buyer ? buyer->getAddress() : tempBuyer->getAddress();

Purchase purchase(buyerName, buyerIdRecord, getCurrentDate(), address, totalPrice, actualAmount);
for (const auto& id : selectedBookIDs) {
purchase.addBookID(id);
}
purchases.push_back(purchase);

cout << "\n交易完成!\n";
cout << fixed << setprecision(2)
<< "应付金额: " << totalPrice << " 元\n"
<< "实付金额: " << actualAmount << " 元\n";
}

void displayAllPurchases() {
UI::clearScreen();
UI::printTitle("交易记录列表", 96);
if (purchases.empty()) {
cout << "当前没有交易记录!\n";
return;
}
for (size_t i = 0; i < purchases.size(); ++i) {
cout << "\n═══ 交易记录 " << (i + 1) << " ═══\n";
purchases[i].display(books);
}
}

void buyerManagementMenu() {
while (true) {
UI::clearScreen();
cout << "╒═════════════════会员管理═══╕\n";
cout << "│ │\n";
cout << "│ 1. 显示所有会员 │\n";
cout << "│ 2. 按姓名查找会员 │\n";
cout << "│ 3. 添加新会员 │\n";
cout << "│ 4. 返回主菜单 │\n";
cout << "│ │\n";
cout << "╘════════════════════════════╛\n";

int choice = InputHelper::readInt("请输入您的选择: ", 1, 4);
switch (choice) {
case 1: displayAllBuyers(); UI::pause(); break;
case 2: searchBuyerByName(); UI::pause(); break;
case 3: addBuyer(); UI::pause(); break;
case 4: return;
}
}
}

void bookManagementMenu() {
while (true) {
UI::clearScreen();
cout << "╒═════════════════图书管理═══╕\n";
cout << "│ │\n";
cout << "│ 1. 显示所有图书 │\n";
cout << "│ 2. 按书名查找图书 │\n";
cout << "│ 3. 添加新图书 │\n";
cout << "│ 4. 返回主菜单 │\n";
cout << "│ │\n";
cout << "╘════════════════════════════╛\n";

int choice = InputHelper::readInt("请输入您的选择: ", 1, 4);
switch (choice) {
case 1: displayAllBooks(); UI::pause(); break;
case 2: searchBookByName(); UI::pause(); break;
case 3: addBook(); UI::pause(); break;
case 4: return;
}
}
}

void transactionMenu() {
while (true) {
UI::clearScreen();
cout << "╒═════════════════交易处理═══╕\n";
cout << "│ │\n";
cout << "│ 1. 购买图书 │\n";
cout << "│ 2. 返回主菜单 │\n";
cout << "│ │\n";
cout << "╘════════════════════════════╛\n";

int choice = InputHelper::readInt("请输入您的选择: ", 1, 2);
switch (choice) {
case 1: makeTransaction(); UI::pause(); break;
case 2: return;
}
}
}

void changeTheme() {
UI::clearScreen();
UI::printThemeMenu();
int themeNum = InputHelper::readInt("请输入所需主题编号: ", 0, 6);
switch (themeNum) {
case 0: UI::setTheme("\033[0m"); break;
case 1: UI::setTheme("\033[31m"); break;
case 2: UI::setTheme("\033[32m"); break;
case 3: UI::setTheme("\033[33m"); break;
case 4: UI::setTheme("\033[34m"); break;
case 5: UI::setTheme("\033[35m"); break;
case 6: UI::setTheme("\033[36m"); break;
}
cout << "主题修改成功!\n";
}

void mainMenu() {
while (true) {
UI::clearScreen();
cout << UI::g_theme;
cout << "╒═书店购书管理系统═══════════════╕\n";
cout << "│ │\n";
cout << "│ 1. 会员管理 │\n";
cout << "│ 2. 图书管理 │\n";
cout << "│ 3. 交易处理 │\n";
cout << "│ 4. 查看交易记录 │\n";
cout << "│ 5. 更改主题 │\n";
cout << "│ 0. 退出系统 │\n";
cout << "│ │\n";
cout << "╘════════════════════════════════╛\n";

int choice = InputHelper::readInt("请输入您的选择: ", 0, 5);
switch (choice) {
case 1: buyerManagementMenu(); break;
case 2: bookManagementMenu(); break;
case 3: transactionMenu(); break;
case 4: displayAllPurchases(); UI::pause(); break;
case 5: changeTheme(); UI::pause(); break;
case 0:
saveData();
cout << "感谢使用书店购书管理系统,再见!\n";
return;
}
}
}
};

int main() {
LibraryManager manager;
manager.mainMenu();
return 0;
}

五、重构的软件的测试截图

image
image
image
image

六、总结

这次二次开发最大的收获,不是简单“把代码改能跑”,而是学会了从软件工程角度审视已有项目。

1. 难点

本次分析和重构中,最有难度的地方主要有两个:

  • 理解原有类之间的关系:在 Buyer 的多态设计,以及不同买家类型对支付逻辑的影响。
  • 定位隐藏的业务逻辑错误:例如“累计消费”与“本次订单金额”混用的问题,此 bug 并未及时发现,在本次作业接近尾声时偶然发现。

2. 花时间较久的部分

  • 阅读原代码并梳理类关系
  • 找出输入、文件存储、业务计算之间的关系

3. 对逆向软件工程的思考

阅读别人的代码,和自己从零写代码是完全不同的体验。

自己写代码时,思维连续;而阅读已有项目时,需要先逆向理解,更耗时耗力:

  • 这个系统的核心功能是什么
  • 各个类分别承担什么职责
  • 经常忘记类的作用及关系
  • 哪些地方是“表面能跑”,但内部设计并不合理

这让我意识到:

  • 养成良好的注释习惯。
  • 软件工程不仅是实现功能,更重要的是保证代码可读、可维护、可扩展。
  • 好的重构不是盲目推翻重写,而是在深度理解原设计的基础上,找到最关键的问题并逐步优化。
  • 测试是二次开发中非常重要的一环,只有通过测试,才能证明改进真正有效。

总体来看,这次作业让我从“写程序”进一步进入到“分析系统、发现问题、改进设计”的层面,对软件工程实践有了更深的理解。


posted on 2026-03-12 09:34  ~喵喵喵~  阅读(20)  评论(0)    收藏  举报