C++ 网上购书系统二次开发:缺陷排查与业务逻辑重构实践

(1)来源本次二次开发项目来源于舍友的大一项目,核心实现购书人分层管理(会员 / 贵宾 / 普通用户)、书籍信息存储、订单创建与费用计算等基础功能,是软件工程课程中典型的面向对象设计练习项目。本次作业对该项目进行缺陷排查、代码重构,并完成二次开发优化。
(2)运行环境 + 原始运行结果(附缺陷源代码)
运行环境
操作系统:Windows 11 64 位
开发工具:Visual Studio 2022 Community
编程语言:C++11
字符集配置:多字节字符集(解决中文乱码)
缺陷 1:订单金额叠加
测试同一客户连续下 2 单( 80.91元 + 53.91 元),第二次应付金额显示134.82 元,原因是 setpay 函数为累加逻辑,calculateTotal 函数未清空客户历史费用,导致多次下单金额叠加。
image
image
重构方案
数据读取层:放弃 >> 读取地址,改用 getline() 读取整行地址(需处理文件读取的换行 / 分隔符问题);
数据存储层:地址字段(address)保留完整字符串(包含空格),不做拆分;
展示层:直接输出完整的 address 字段,确保空格不影响展示。

缺陷 2:地址读取截断
查看客户信息时,带空格的地址(如 “北京市海淀区”)仅显示 “北京市”,原因是用 >> 读取地址,该运算符按空格分隔,无法读取含空格的完整字符串。
image
重构方案
数据分离:明确区分「订单当前总价」和「用户累计消费」两个概念;
订单计算:订单总价仅计算当前订单的折扣后金额(如 89.9*0.9=80.91),不关联累计消费;
累计消费:用户累计消费(pay)单独累加当前订单总价,仅用于用户信息展示,不影响订单总价计算。

缺陷 3:库存未扣减
下单后查看书籍库存,数量未发生变化,原因是 addItem 函数仅将书籍加入订单,未调用 decreaseStock 函数扣减库存,导致库存数据与实际销售不符。
image

image
重构方案
业务联动:在 addItem() 方法中,添加商品到订单的同时,立即调用书籍的 decreaseStock(quantity) 方法;
校验前置:扣减库存前先校验库存是否充足(stock >= quantity),避免超卖;
数据一致性:确保订单创建和库存扣减是原子操作

完整原缺陷代码****

点击展开完整缺陷代码

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

class buyer
{
protected:
string name;
int buyerID;
string address;
double pay;

public:
buyer();
buyer(string n, int b, string a, double p);
string getbuyname();
string getaddress();
double getpay();
int getid();
virtual void display() = 0;
virtual void setpay(double = 0) = 0;
virtual string getType() = 0;
};

class member : public buyer
{
private:
int leaguer_grade;

public:
member(string n, int b, int l, string a, double p) : buyer(n, b, a, p)
{
leaguer_grade = l;
}
void display();
void setpay(double p);
string getType() { return "会员"; }
};

class honoured_guest : public buyer
{
private:
double discount_rate;

public:
honoured_guest(string n, int b, double r, string a, double p) : buyer(n, b, a, p)
{
discount_rate = r;
}
void display();
void setpay(double p);
string getType() { return "贵宾"; }
};

class layfolk : public buyer
{
public:
layfolk(string n, int b, string a, double p) : buyer(n, b, a, p) {}
void display();
void setpay(double p);
string getType() { return "普通用户"; }
};

class book
{
protected:
string book_ID;
string book_name;
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 = 0);
void display();
string getbook_ID();
string getbook_name();
string getauthor();
string getpublishing();
double getprice();
int getStock();
void decreaseStock(int amount);
};

class order
{
private:
string orderID;
buyer* customer;
vector<pair<book*, int>> items;
double totalPrice;
string orderDate;

public:
order(string id, buyer* c, string date);
void addItem(book* b, int quantity);
void calculateTotal();
void display();
};

class bookstoreSystem
{
private:
vector<buyer> buyers;
vector<book
> books;
vector<order*> orders;
string customerFile = "customers.txt";
string bookFile = "books.txt";

public:
void loadCustomers();
void loadBooks();
void showMenu();
void showAllBuyers();
void showAllBooks();
void createOrder();
buyer* findBuyer(int id);
book* findBook(string id);
string generateOrderID();
};

buyer::buyer()
{
name = "";
buyerID = 0;
address = "";
pay = 0;
}

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

double buyer::getpay() { return pay; }
string buyer::getaddress() { return address; }
string buyer::getbuyname() { return name; }
int buyer::getid() { return buyerID; }

void member::display()
{
cout << "购书人姓名:" << name << "\t";
cout << "编号:" << buyerID << "\t";
cout << "会员级别:" << leaguer_grade << endl;
cout << "地址:" << address << "\t";
cout << "累计消费:" << pay << endl;
}

void member::setpay(double p)
{
if (leaguer_grade == 1) pay = 0.95 * p + pay;
else if (leaguer_grade == 2) pay = 0.90 * p + pay;
else if (leaguer_grade == 3) pay = 0.85 * p + pay;
else if (leaguer_grade == 4) pay = 0.80 * p + pay;
else if (leaguer_grade == 5) pay = 0.70 * p + pay;
else cout << "级别错误!" << endl;
}

void honoured_guest::display()
{
cout << "购书人姓名:" << name << "\t";
cout << "编号:" << buyerID << "\t";
cout << "折扣率:" << discount_rate * 100 << "%" << endl;
cout << "地址:" << address << "\t";
cout << "累计消费:" << pay << endl;
}

void honoured_guest::setpay(double p)
{
pay = pay + (1 - discount_rate) * p;
}

void layfolk::display()
{
cout << "购书人姓名:" << name << "\t";
cout << "编号:" << buyerID << "\t";
cout << "普通用户" << endl;
cout << "地址:" << address << "\t";
cout << "累计消费:" << pay << endl;
}

void layfolk::setpay(double p)
{
pay = pay + p;
}

book::book(string b_id, string b_n, string au, string pu, double pr, int st)
{
book_ID = b_id;
book_name = b_n;
author = au;
publishing = pu;
price = pr;
stock = st;
}

book::book()
{
book_ID = "";
book_name = "";
author = "";
publishing = "";
price = 0;
stock = 0;
}

void book::display()
{
cout << "书号:" << book_ID << "\t";
cout << "书名:" << book_name << "\t";
cout << "作者:" << author << endl;
cout << "出版社:" << publishing << "\t";
cout << "定价:" << price << "\t";
cout << "库存:" << stock << endl;
}

string book::getbook_ID() { return book_ID; }
string book::getbook_name() { return book_name; }
string book::getauthor() { return author; }
string book::getpublishing() { return publishing; }
double book::getprice() { return price; }
int book::getStock() { return stock; }
void book::decreaseStock(int amount)
{
if (stock >= amount) stock -= amount;
else cout << "库存不足!" << endl;
}

order::order(string id, buyer* c, string date)
{
orderID = id;
customer = c;
orderDate = date;
totalPrice = 0;
}

void order::addItem(book* b, int quantity)
{
items.push_back({b, quantity});
}

void order::calculateTotal()
{
totalPrice = 0;
for (auto item : items)
{
totalPrice += item.first->getprice() * item.second;
}
customer->setpay(totalPrice);
totalPrice = customer->getpay();
}

void order::display()
{
cout << "========== 订单详情 " << endl;
cout << "订单编号:" << orderID << endl;
cout << "下单日期:" << orderDate << endl;
cout << "购书人:" << customer->getbuyname() << "(" << customer->getType() << ")" << endl;
cout << "购买书籍:" << endl;
for (auto item : items)
{
cout << " - " << item.first->getbook_name() << " x " << item.second
<< " 单价:" << item.first->getprice() << endl;
}
cout << "订单总价:" << totalPrice << endl;
cout << "
====================" << endl;
}

void bookstoreSystem::loadCustomers()
{
ifstream inFile(customerFile);
if (!inFile.is_open())
{
cout << "客户文件打开失败!" << endl;
return;
}

string type, name, address;
int id, grade;
double discount, pay;

while (inFile >> type)
{
    if (type == "member")
    {
        inFile >> name >> id >> grade >> address >> pay;
        buyers.push_back(new member(name, id, grade, address, pay));
    }
    else if (type == "honoured_guest")
    {
        inFile >> name >> id >> discount >> address >> pay;
        buyers.push_back(new honoured_guest(name, id, discount, address, pay));
    }
    else if (type == "layfolk")
    {
        inFile >> name >> id >> address >> pay;
        buyers.push_back(new layfolk(name, id, address, pay));
    }
}
inFile.close();

}

void bookstoreSystem::loadBooks()
{
ifstream inFile(bookFile);
if (!inFile.is_open())
{
cout << "书籍文件打开失败!" << endl;
return;
}

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

while (inFile >> id >> name >> author >> publishing >> price >> stock)
{
    books.push_back(new book(id, name, author, publishing, price, stock));
}
inFile.close();

}

buyer* bookstoreSystem::findBuyer(int id)
{
for (auto b : buyers)
{
if (b->getid() == id) return b;
}
return nullptr;
}

book* bookstoreSystem::findBook(string id)
{
for (auto b : books)
{
if (b->getbook_ID() == id) return b;
}
return nullptr;
}

string bookstoreSystem::generateOrderID()
{
return "ORD" + to_string(orders.size() + 1);
}

void bookstoreSystem::createOrder()
{
int buyerID;
cout << "请输入购书人编号:";
cin >> buyerID;

buyer* customer = findBuyer(buyerID);
if (!customer)
{
    cout << "购书人编号不存在!" << endl;
    return;
}

string orderID = generateOrderID();
time_t now = time(0);
tm* ltm = localtime(&now);
string date = to_string(1900 + ltm->tm_year) + "-" + to_string(1 + ltm->tm_mon) + "-" + to_string(ltm->tm_mday);

order* newOrder = new order(orderID, customer, date);

char continueShopping = 'y';
while (continueShopping == 'y' || continueShopping == 'Y')
{
    string bookID;
    int quantity;

    cout << "请输入书籍编号:";
    cin >> bookID;

    book* selectedBook = findBook(bookID);
    if (!selectedBook)
    {
        cout << "书籍编号不存在!" << endl;
    }
    else
    {
        cout << "请输入购买数量:";
        cin >> quantity;
        newOrder->addItem(selectedBook, quantity);
        cout << "已添加 " << selectedBook->getbook_name() << " x " << quantity << endl;
    }

    cout << "是否继续购物?(y/n):";
    cin >> continueShopping;
}

newOrder->calculateTotal();
newOrder->display();
orders.push_back(newOrder);

}

void bookstoreSystem::showAllBuyers()
{
cout << "========== 所有购书人 " << endl;
for (auto b : buyers) b->display();
cout << "
====================" << endl;
}

void bookstoreSystem::showAllBooks()
{
cout << "========== 所有书籍 " << endl;
for (auto b : books) b->display();
cout << "
====================" << endl;
}

void bookstoreSystem::showMenu()
{
int choice;
do
{
cout << "\n===== 网上购书系统 =====" << endl;
cout << "1. 查看所有购书人" << endl;
cout << "2. 查看所有书籍" << endl;
cout << "3. 创建订单" << endl;
cout << "0. 退出系统" << endl;
cout << "请选择操作:";
cin >> choice;

    switch (choice)
    {
    case 1: showAllBuyers(); break;
    case 2: showAllBooks(); break;
    case 3: createOrder(); break;
    case 0: cout << "退出系统..." << endl; break;
    default: cout << "输入错误,请重新选择!" << endl;
    }
} while (choice != 0);

}

int main()
{
bookstoreSystem sys;
sys.loadCustomers();
sys.loadBooks();
sys.showMenu();
return 0;
}

**完整修复代码******
点击展开完整修复代码

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

class buyer
{
protected:
string name;
int buyerID;
string address;
double pay;

public:
buyer();
buyer(string n, int b, string a, double p);
string getbuyname();
string getaddress();
double getpay();
int getid();
virtual void display() = 0;
virtual void setpay(double = 0) = 0;
virtual string getType() = 0;
};

class member : public buyer
{
private:
int leaguer_grade;

public:
member(string n, int b, int l, string a, double p) : buyer(n, b, a, p)
{
leaguer_grade = l;
}
void display();
void setpay(double p);
string getType() { return "会员"; }
};

class honoured_guest : public buyer
{
private:
double discount_rate;

public:
honoured_guest(string n, int b, double r, string a, double p) : buyer(n, b, a, p)
{
discount_rate = r;
}
void display();
void setpay(double p);
string getType() { return "贵宾"; }
};

class layfolk : public buyer
{
public:
layfolk(string n, int b, string a, double p) : buyer(n, b, a, p) {}
void display();
void setpay(double p);
string getType() { return "普通用户"; }
};

class book
{
protected:
string book_ID;
string book_name;
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 = 0);
void display();
string getbook_ID();
string getbook_name();
string getauthor();
string getpublishing();
double getprice();
int getStock();
void decreaseStock(int amount);
};

class order
{
private:
string orderID;
buyer* customer;
vector<pair<book*, int>> items;
double totalPrice;
string orderDate;

public:
order(string id, buyer* c, string date);
void addItem(book* b, int quantity);
void calculateTotal();
void display();
};

class bookstoreSystem
{
private:
vector<buyer> buyers;
vector<book
> books;
vector<order*> orders;
string customerFile = "customers.txt";
string bookFile = "books.txt";

public:
void loadCustomers();
void loadBooks();
void showMenu();
void showAllBuyers();
void showAllBooks();
void createOrder();
buyer* findBuyer(int id);
book* findBook(string id);
string generateOrderID();
};

buyer::buyer()
{
name = "";
buyerID = 0;
address = "";
pay = 0;
}

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

double buyer::getpay() { return pay; }
string buyer::getaddress() { return address; }
string buyer::getbuyname() { return name; }
int buyer::getid() { return buyerID; }

void member::display()
{
cout << "购书人姓名:" << name << "\t";
cout << "编号:" << buyerID << "\t";
cout << "会员级别:" << leaguer_grade << endl;
cout << "地址:" << address << "\t";
cout << "累计消费:" << pay << endl;
}

void member::setpay(double p)
{
if (leaguer_grade == 1) pay = 0.95 * p + pay;
else if (leaguer_grade == 2) pay = 0.90 * p + pay;
else if (leaguer_grade == 3) pay = 0.85 * p + pay;
else if (leaguer_grade == 4) pay = 0.80 * p + pay;
else if (leaguer_grade == 5) pay = 0.70 * p + pay;
else cout << "级别错误!" << endl;
}

void honoured_guest::display()
{
cout << "购书人姓名:" << name << "\t";
cout << "编号:" << buyerID << "\t";
cout << "折扣率:" << discount_rate * 100 << "%" << endl;
cout << "地址:" << address << "\t";
cout << "累计消费:" << pay << endl;
}

void honoured_guest::setpay(double p)
{
pay = pay + (1 - discount_rate) * p;
}

void layfolk::display()
{
cout << "购书人姓名:" << name << "\t";
cout << "编号:" << buyerID << "\t";
cout << "普通用户" << endl;
cout << "地址:" << address << "\t";
cout << "累计消费:" << pay << endl;
}

void layfolk::setpay(double p)
{
pay = pay + p;
}

book::book(string b_id, string b_n, string au, string pu, double pr, int st)
{
book_ID = b_id;
book_name = b_n;
author = au;
publishing = pu;
price = pr;
stock = st;
}

book::book()
{
book_ID = "";
book_name = "";
author = "";
publishing = "";
price = 0;
stock = 0;
}

void book::display()
{
cout << "书号:" << book_ID << "\t";
cout << "书名:" << book_name << "\t";
cout << "作者:" << author << endl;
cout << "出版社:" << publishing << "\t";
cout << "定价:" << price << "\t";
cout << "库存:" << stock << endl;
}

string book::getbook_ID() { return book_ID; }
string book::getbook_name() { return book_name; }
string book::getauthor() { return author; }
string book::getpublishing() { return publishing; }
double book::getprice() { return price; }
int book::getStock() { return stock; }
void book::decreaseStock(int amount)
{
if (stock >= amount) stock -= amount;
else cout << "库存不足!" << endl;
}

order::order(string id, buyer* c, string date)
{
orderID = id;
customer = c;
orderDate = date;
totalPrice = 0;
}

void order::addItem(book* b, int quantity)
{
items.push_back({b, quantity});
b->decreaseStock(quantity);
}

void order::calculateTotal()
{
totalPrice = 0;
for (auto item : items)
{
totalPrice += item.first->getprice() * item.second;
}
customer->pay = 0;
customer->setpay(totalPrice);
totalPrice = customer->getpay();
}

void order::display()
{
cout << "========== 订单详情 " << endl;
cout << "订单编号:" << orderID << endl;
cout << "下单日期:" << orderDate << endl;
cout << "购书人:" << customer->getbuyname() << "(" << customer->getType() << ")" << endl;
cout << "购买书籍:" << endl;
for (auto item : items)
{
cout << " - " << item.first->getbook_name() << " x " << item.second
<< " 单价:" << item.first->getprice() << endl;
}
cout << "订单总价:" << totalPrice << endl;
cout << "
====================" << endl;
}

void bookstoreSystem::loadCustomers()
{
ifstream inFile(customerFile);
if (!inFile.is_open())
{
cout << "客户文件打开失败!" << endl;
return;
}

string type, name, address;
int id, grade;
double discount, pay;

while (inFile >> type)
{
    if (type == "member")
    {
        inFile >> name >> id >> grade >> pay;
        getline(inFile >> ws, address);
        buyers.push_back(new member(name, id, grade, address, pay));
    }
    else if (type == "honoured_guest")
    {
        inFile >> name >> id >> discount >> pay;
        getline(inFile >> ws, address);
        buyers.push_back(new honoured_guest(name, id, discount, address, pay));
    }
    else if (type == "layfolk")
    {
        inFile >> name >> id >> pay;
        getline(inFile >> ws, address);
        buyers.push_back(new layfolk(name, id, address, pay));
    }
}
inFile.close();

}

void bookstoreSystem::loadBooks()
{
ifstream inFile(bookFile);
if (!inFile.is_open())
{
cout << "书籍文件打开失败!" << endl;
return;
}

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

while (inFile >> id >> name >> author >> publishing >> price >> stock)
{
    books.push_back(new book(id, name, author, publishing, price, stock));
}
inFile.close();

}

buyer* bookstoreSystem::findBuyer(int id)
{
for (auto b : buyers)
{
if (b->getid() == id) return b;
}
return nullptr;
}

book* bookstoreSystem::findBook(string id)
{
for (auto b : books)
{
if (b->getbook_ID() == id) return b;
}
return nullptr;
}

string bookstoreSystem::generateOrderID()
{
return "ORD" + to_string(orders.size() + 1);
}

void bookstoreSystem::createOrder()
{
int buyerID;
cout << "请输入购书人编号:";
cin >> buyerID;

buyer* customer = findBuyer(buyerID);
if (!customer)
{
    cout << "购书人编号不存在!" << endl;
    return;
}

string orderID = generateOrderID();
time_t now = time(0);
tm* ltm = localtime(&now);
string date = to_string(1900 + ltm->tm_year) + "-" + to_string(1 + ltm->tm_mon) + "-" + to_string(ltm->tm_mday);

order* newOrder = new order(orderID, customer, date);

char continueShopping = 'y';
while (continueShopping == 'y' || continueShopping == 'Y')
{
    string bookID;
    int quantity;

    cout << "请输入书籍编号:";
    cin >> bookID;

    book* selectedBook = findBook(bookID);
    if (!selectedBook)
    {
        cout << "书籍编号不存在!" << endl;
    }
    else
    {
        cout << "请输入购买数量:";
        cin >> quantity;
        newOrder->addItem(selectedBook, quantity);
        cout << "已添加 " << selectedBook->getbook_name() << " x " << quantity << endl;
    }

    cout << "是否继续购物?(y/n):";
    cin >> continueShopping;
}

newOrder->calculateTotal();
newOrder->display();
orders.push_back(newOrder);

}

void bookstoreSystem::showAllBuyers()
{
cout << "========== 所有购书人 " << endl;
for (auto b : buyers) b->display();
cout << "
====================" << endl;
}

void bookstoreSystem::showAllBooks()
{
cout << "========== 所有书籍 " << endl;
for (auto b : books) b->display();
cout << "
====================" << endl;
}

void bookstoreSystem::showMenu()
{
int choice;
do
{
cout << "\n===== 网上购书系统 =====" << endl;
cout << "1. 查看所有购书人" << endl;
cout << "2. 查看所有书籍" << endl;
cout << "3. 创建订单" << endl;
cout << "0. 退出系统" << endl;
cout << "请选择操作:";
cin >> choice;

    switch (choice)
    {
    case 1: showAllBuyers(); break;
    case 2: showAllBooks(); break;
    case 3: createOrder(); break;
    case 0: cout << "退出系统..." << endl; break;
    default: cout << "输入错误,请重新选择!" << endl;
    }
} while (choice != 0);

}

int main()
{
bookstoreSystem sys;
sys.loadCustomers();
sys.loadBooks();
sys.showMenu();
return 0;
}

修复后的运行结果
01
image

02
image

03
image

总结
编译错误排查:从 “totalPrice 未声明” 到定位类作用域问题,反复修改 setpay 方法逻辑,花费约 1 小时才理清变量归属与代码结构。
文件读取问题调试:从 “文件打开失败” 到排查路径、命名、编码,多次创建 / 移动 / 修改 txt 文件并重新运行,累计耗时约 1.5 小时,是最磨人的环境问题。
业务逻辑重构设计:针对金额叠加缺陷,反复推演 “单次订单 - 累计消费” 的业务边界,避免修改后出现 “累计消费统计错误” 的新问题,耗时约 1 小时。
功能验证调试:修改后多次创建订单,验证地址完整性、金额是否叠加、库存是否扣减,逐行核对输出结果,耗时约 1 小时。
经过这次作业 我有了下面的感悟
逆向工程的核心是 “读懂 - 发现 - 重构”:先逐行阅读原有代码,理解业务意图(比如 setpay 原本想做 “累计消费统计”,却错误地影响了订单总价),才能精准定位缺陷;再针对根源设计重构方案,最后验证效果。
细节是逆向的关键:很多缺陷藏在 “不起眼的细节” 里 —— 比如 >> 读取地址的分割逻辑、变量作用域的疏忽、流程联动的遗漏,这些都需要逐行推敲才能发现。
逆向是最好的 “反面教材”:通过分析原有代码的问题(业务概念混淆、封装混乱、流程断裂),能直观感受到糟糕设计的代价,反过来提醒自己:写代码时要明确业务边界、保证数据一致性、遵循封装原则。

posted @ 2026-03-11 12:12  AitaroKael  阅读(9)  评论(0)    收藏  举报