教室预订管理系统的二次开发与架构升级

作者: hjy
课程: 软件开发与创新
项目来源: 基于室友原有项目的二次开发
开发环境: Visual Studio 2022, Windows 11
改进系统运行环境

操作系统: Windows 11
开发工具: VS Code / Visual Studio 2022
编译器: g++ 9.4.0 / MSVC v142
C++标准: C++11
数据库: SQLite

该系统主要用于学校教室资源的预订与管理,包括以下核心功能:

  • 用户管理: 支持管理员和普通教师两种角色
  • 教室管理: 支持实验室、多媒体教室、录课教室三种类型
  • 预订管理: 教师可以申请预订教室,管理员负责审核
  • 身份验证: 基于工号和密码的登录系统
  • 数据持久化: 使用文本文件存储所有数据
    源代码如下
点击查看代码
#pragma execution_character_set("utf-8")

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <ctime>
#include <iomanip>
#include <algorithm>
#include <sstream>
#include <exception>
#include <cstdlib>

using namespace std;


class BookingException : public exception {
private:
    string message;
public:
    BookingException(const string& msg) : message(msg) {}
    const char* what() const noexcept override { return message.c_str(); }
};


class TimeSlot {
private:
    string date;       // 格式: YYYY-MM-DD
    int startHour;     // 开始时间 (24小时制)
    int duration;      // 持续时间(小时)

public:
    TimeSlot(const string& d = "", int start = 0, int dur = 0)
        : date(d), startHour(start), duration(dur) {
    }

    // Getter方法
    string getDate() const { return date; }
    int getStartHour() const { return startHour; }
    int getDuration() const { return duration; }
    int getEndHour() const { return startHour + duration; }

    // 检查时间冲突
    bool isConflict(const TimeSlot& other) const {
        if (date != other.date) return false;
        int thisEnd = startHour + duration;
        int otherEnd = other.startHour + other.duration;
        return (startHour < otherEnd && thisEnd > other.startHour);
    }

    // 获取时间描述
    string getTimeDescription() const {
        stringstream ss;
        ss << date << " "
            << setw(2) << setfill('0') << startHour << ":00-"
            << setw(2) << setfill('0') << (startHour + duration) << ":00";
        return ss.str();
    }

    // 检查时间段是否有效
    bool isValid() const {
        return (startHour >= 8 && startHour <= 20 &&
            duration > 0 && startHour + duration <= 22);
    }

    // 文件操作
    string toFileString() const {
        return date + "," + to_string(startHour) + "," + to_string(duration);
    }

    void fromFileString(const string& str) {
        stringstream ss(str);
        string item;
        getline(ss, item, ','); date = item;
        getline(ss, item, ','); startHour = stoi(item);
        getline(ss, item, ','); duration = stoi(item);
    }
};

// 基础教室类
class Classroom {
protected:
    int roomId;
    string roomName;
    int capacity;
    bool isOccupied;
    int roomType; // 1-实验室, 2-多媒体, 3-录课教室
    int attributeCode;

public:
    Classroom(int id = 0, const string& name = "", int cap = 0,
        bool occupied = false, int type = 0, int attr = 0)
        : roomId(id), roomName(name), capacity(cap),
        isOccupied(occupied), roomType(type), attributeCode(attr) {
    }

    virtual ~Classroom() = default;

    // Getter方法
    int getRoomId() const { return roomId; }
    string getRoomName() const { return roomName; }
    int getCapacity() const { return capacity; }
    bool getIsOccupied() const { return isOccupied; }
    int getRoomType() const { return roomType; }
    int getAttributeCode() const { return attributeCode; }

    // Setter方法
    void setOccupied(bool occupied) { isOccupied = occupied; }

    // 虚函数 - 获取教室类型描述
    virtual string getTypeDescription() const = 0;
    virtual string getAttributeDescription() const = 0;

    // 运算符重载
    friend ostream& operator<<(ostream& os, const Classroom& room);
    friend istream& operator>>(istream& is, Classroom& room);

    // 文件操作
    virtual string toFileString() const {
        stringstream ss;
        ss << roomId << "," << roomName << "," << capacity << ","
            << isOccupied << "," << roomType << "," << attributeCode;
        return ss.str();
    }

    virtual void fromFileString(const string& line) {
        stringstream ss(line);
        string item;
        getline(ss, item, ','); roomId = stoi(item);
        getline(ss, item, ','); roomName = item;
        getline(ss, item, ','); capacity = stoi(item);
        getline(ss, item, ','); isOccupied = (item == "1");
        getline(ss, item, ','); roomType = stoi(item);
        getline(ss, item, ','); attributeCode = stoi(item);
    }
};

// 实验室类
class Laboratory : public Classroom {
public:
    Laboratory(int id = 0, const string& name = "", int cap = 0,
        bool occupied = false, int labType = 1)
        : Classroom(id, name, cap, occupied, 1, labType) {
    }

    string getTypeDescription() const override { return "实验室"; }

    string getAttributeDescription() const override {
        switch (attributeCode) {
        case 1: return "化学实验室";
        case 2: return "物理实验室";
        case 3: return "生物实验室";
        case 4: return "计算机实验室";
        case 5: return "水环境实验室";
        default: return "未知类型实验室";
        }
    }
};

// 多媒体教室类
class MultimediaRoom : public Classroom {
public:
    MultimediaRoom(int id = 0, const string& name = "", int cap = 0,
        bool occupied = false, int aircon = 1)
        : Classroom(id, name, cap, occupied, 2, aircon) {
    }

    string getTypeDescription() const override { return "多媒体教室"; }
    string getAttributeDescription() const override {
        return attributeCode == 1 ? "无空调" : "有空调";
    }
};

// 录课教室类
class RecordingRoom : public Classroom {
public:
    RecordingRoom(int id = 0, const string& name = "", int cap = 0,
        bool occupied = false, int cameras = 0)
        : Classroom(id, name, cap, occupied, 3, cameras) {
    }

    string getTypeDescription() const override { return "录课教室"; }
    string getAttributeDescription() const override {
        return "摄像头数量: " + to_string(attributeCode);
    }
};

// 基础教师类
class Teacher {
protected:
    string employeeId;
    string name;
    int permission; // 1-管理员, 2-普通教师
    string phone;
    string office;
    string password;  // 新增密码字段

public:
    Teacher(const string& id = "", const string& n = "", int perm = 2,
        const string& ph = "", const string& off = "", const string& pwd = "123456")
        : employeeId(id), name(n), permission(perm), phone(ph), office(off), password(pwd) {
    }

    virtual ~Teacher() = default;

    // Getter方法
    string getEmployeeId() const { return employeeId; }
    string getName() const { return name; }
    int getPermission() const { return permission; }
    string getPassword() const { return password; }  // 新增
    string getPhone() const { return phone; }        // 新增
    string getOffice() const { return office; }      // 新增

    // Setter方法
    void setPassword(const string& pwd) { password = pwd; }  // 新增

    // 虚函数
    virtual string getPermissionDescription() const = 0;

    // 验证密码
    bool verifyPassword(const string& pwd) const {
        return password == pwd;
    }

    // 运算符重载
    friend ostream& operator<<(ostream& os, const Teacher& teacher);
    friend istream& operator>>(istream& is, Teacher& teacher);

    // 文件操作
    virtual string toFileString() const {
        return employeeId + "," + name + "," + to_string(permission) + "," +
            phone + "," + office + "," + password;
    }

    virtual void fromFileString(const string& line) {
        stringstream ss(line);
        string item;
        getline(ss, item, ','); employeeId = item;
        getline(ss, item, ','); name = item;
        getline(ss, item, ','); permission = stoi(item);
        getline(ss, item, ','); phone = item;
        getline(ss, item, ','); office = item;
        getline(ss, item, ','); password = item;
        if (password.empty()) password = "123456";  // 默认密码
    }
};

// 管理员类
class Administrator : public Teacher {
public:
    Administrator(const string& id = "", const string& n = "",
        const string& ph = "", const string& off = "", const string& pwd = "123456")
        : Teacher(id, n, 1, ph, off, pwd) {
    }

    string getPermissionDescription() const override { return "系统管理员"; }
};

// 普通教师类
class RegularTeacher : public Teacher {
public:
    RegularTeacher(const string& id = "", const string& n = "",
        const string& ph = "", const string& off = "", const string& pwd = "123456")
        : Teacher(id, n, 2, ph, off, pwd) {
    }

    string getPermissionDescription() const override { return "普通教师"; }
};

// 预订记录类
class BookingRecord {
private:
    static int nextBookingId;
    int bookingId;
    string teacherId;
    int roomId;
    TimeSlot timeSlot;
    string submitTime;
    int status; // 0-待审核, 1-已通过, 2-已拒绝, 3-已取消, 4-已完成

public:
    BookingRecord(const string& tId = "", int rId = 0,
        const TimeSlot& ts = TimeSlot())
        : bookingId(nextBookingId++), teacherId(tId), roomId(rId),
        timeSlot(ts), status(0) {
        // 获取当前时间
        time_t now = time(0);
        struct tm timeinfo;

#ifdef _WIN32
        localtime_s(&timeinfo, &now);
#else
        timeinfo = *localtime(&now);
#endif

        char timeStr[100];
        strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", &timeinfo);
        submitTime = timeStr;
    }

    // Getter方法
    int getBookingId() const { return bookingId; }
    string getTeacherId() const { return teacherId; }
    int getRoomId() const { return roomId; }
    const TimeSlot& getTimeSlot() const { return timeSlot; }
    int getStatus() const { return status; }

    // Setter方法
    void setStatus(int s) {
        if (s < 0 || s > 4) {
            throw BookingException("无效的状态值");
        }
        status = s;
    }

    string getStatusDescription() const {
        switch (status) {
        case 0: return "待审核";
        case 1: return "已通过";
        case 2: return "已拒绝";
        case 3: return "已取消";
        case 4: return "已完成";
        default: return "未知状态";
        }
    }

    // 检查预订是否已过期
    bool isExpired() const {
        // 获取当前时间
        time_t now = time(0);
        struct tm timeinfo;

#ifdef _WIN32
        localtime_s(&timeinfo, &now);
#else
        timeinfo = *localtime(&now);
#endif

        // 构建当前日期字符串
        char dateStr[11];
        strftime(dateStr, sizeof(dateStr), "%Y-%m-%d", &timeinfo);
        string currentDate = dateStr;

        // 比较日期
        if (timeSlot.getDate() < currentDate) {
            return true;
        }

        return false;
    }

    // 文件操作
    string toFileString() const {
        return to_string(bookingId) + "," + teacherId + "," + to_string(roomId) + ","
            + timeSlot.toFileString() + "," + submitTime + "," + to_string(status);
    }

    void fromFileString(const string& line) {
        stringstream ss(line);
        string item;

        getline(ss, item, ','); bookingId = stoi(item);
        getline(ss, item, ','); teacherId = item;
        getline(ss, item, ','); roomId = stoi(item);

        // 读取TimeSlot信息
        string timeSlotStr;
        for (int i = 0; i < 3; i++) {
            getline(ss, item, ',');
            timeSlotStr += item;
            if (i < 2) timeSlotStr += ",";
        }
        timeSlot.fromFileString(timeSlotStr);

        getline(ss, item, ','); submitTime = item;
        getline(ss, item, ','); status = stoi(item);

        // 更新下一个预订ID
        if (bookingId >= nextBookingId) {
            nextBookingId = bookingId + 1;
        }
    }

    friend ostream& operator<<(ostream& os, const BookingRecord& record);

    // 静态成员函数
    static void setNextBookingId(int id) { nextBookingId = id; }
    static int getNextBookingId() { return nextBookingId; }
};

// 静态成员初始化
int BookingRecord::nextBookingId = 1;

// 教室预订管理系统类
class BookingSystem {
private:
    vector<Classroom*> classrooms;
    vector<Teacher*> teachers;
    vector<BookingRecord> bookings;
    Teacher* currentUser;

    // 文件路径
    const string CLASSROOM_FILE = "classrooms.txt";
    const string TEACHER_FILE = "teachers.txt";
    const string BOOKING_FILE = "bookings.txt";

public:
    BookingSystem() : currentUser(nullptr) {
        initializeSystem();
    }

    ~BookingSystem() {
        saveToFiles();
        // 清理动态分配的内存
        for (auto& classroom : classrooms) delete classroom;
        for (auto& teacher : teachers) delete teacher;
    }

    // 系统初始化
    void initializeSystem() {
        // 创建默认管理员账户(带默认密码)
        teachers.push_back(new Administrator("10001", "Vincenzo老师", "13812345678", "A101", "admin123"));
        teachers.push_back(new Administrator("10002", "Tina老师", "13823456789", "A102", "admin123"));
        teachers.push_back(new Administrator("10003", "Lauv老师", "13834567890", "A103", "admin123"));

        // 创建普通教师账户(带默认密码)
        teachers.push_back(new RegularTeacher("20001", "Sandy老师", "13845678901", "B201", "teacher123"));
        teachers.push_back(new RegularTeacher("20002", "Alexandra老师", "13856789012", "B202", "teacher123"));
        teachers.push_back(new RegularTeacher("20003", "Vivienne老师", "13867890123", "B203", "teacher123"));

        // 创建示例教室
        classrooms.push_back(new Laboratory(101, "化学实验室1", 30, false, 1));
        classrooms.push_back(new MultimediaRoom(201, "多媒体教室1", 50, false, 2));
        classrooms.push_back(new RecordingRoom(301, "录课教室1", 40, false, 2));

        loadFromFiles();
        checkExpiredBookings();
    }

    // 显示欢迎信息
    void showWelcomeInfo() {
        std::cout << "========== 系统初始化完成 ==========" << endl;
        std::cout << "首次登录提示: " << endl;
        std::cout << " 管理员请使用工号登录后修改默认密码" << endl;
        std::cout << " 教师可以注册新账号或联系管理员" << endl;
        std::cout << " 建议定期备份系统数据" << endl;
        std::cout << "===================================" << endl;
    }

    // 文件操作
    void loadFromFiles();
    void saveToFiles();

    // 用户管理
    bool login(const string& employeeId, const string& password);  // 修改
    void registerTeacher();

    // 教室管理
    void browseClassrooms();
    void searchClassroom();
    void addClassroom();

    // 预订管理
    void browseBookings();
    void makeBooking();
    void cancelBooking();
    void approveBooking();
    void checkExpiredBookings();

    // 用户密码管理
    void changePassword();

    // 界面管理
    void showMainMenu();
    void showAdminMenu();
    void showTeacherMenu();
    void run();
};

// 实现运算符重载和其他成员函数

// Classroom类的运算符重载
ostream& operator<<(ostream& os, const Classroom& room) {
    os << "教室号: " << room.roomId
        << ", 名称: " << room.roomName
        << ", 容量: " << room.capacity
        << ", 类型: " << room.getTypeDescription()
        << ", 属性: " << room.getAttributeDescription()
        << ", 状态: " << (room.isOccupied ? "占用" : "空闲");
    return os;
}

istream& operator>>(istream& is, Classroom& room) {
    std::cout << "请输入教室号: ";
    is >> room.roomId;
    std::cout << "请输入教室名称: ";
    is >> room.roomName;
    std::cout << "请输入容量: ";
    is >> room.capacity;

    // 根据教室类型显示不同的属性选项
    if (room.roomType == 1) {  // 实验室
        std::cout << "请选择实验室类型:" << endl;
        std::cout << "1 - 化学实验室" << endl;
        std::cout << "2 - 物理实验室" << endl;
        std::cout << "3 - 生物实验室" << endl;
        std::cout << "4 - 计算机实验室" << endl;
        std::cout << "5 - 水环境实验室" << endl;
        std::cout << "请输入对应数字: ";
    }
    else if (room.roomType == 2) {  // 多媒体教室
        std::cout << "请选择空调配置:" << endl;
        std::cout << "1 - 无空调" << endl;
        std::cout << "2 - 有空调" << endl;
        std::cout << "请输入对应数字: ";
    }
    else if (room.roomType == 3) {  // 录课教室
        std::cout << "请输入摄像头数量: ";
    }

    is >> room.attributeCode;
    room.isOccupied = false;
    return is;
}

// Teacher类的运算符重载
ostream& operator<<(ostream& os, const Teacher& teacher) {
    os << "工号: " << teacher.employeeId
        << ", 姓名: " << teacher.name
        << ", 权限: " << teacher.getPermissionDescription();
    return os;
}

istream& operator>>(istream& is, Teacher& teacher) {
    std::cout << "请输入工号: ";
    is >> teacher.employeeId;
    std::cout << "请输入姓名: ";
    is >> teacher.name;
    std::cout << "请输入电话: ";
    is >> teacher.phone;
    std::cout << "请输入办公室: ";
    is >> teacher.office;
    std::cout << "请设置密码: ";
    is >> teacher.password;
    return is;
}

// BookingRecord类的运算符重载
ostream& operator<<(ostream& os, const BookingRecord& record) {
    os << "预订号: " << record.bookingId
        << ", 教师ID: " << record.teacherId
        << ", 教室号: " << record.roomId
        << ", 预订时间: " << record.timeSlot.getTimeDescription()
        << ", 提交时间: " << record.submitTime
        << ", 状态: " << record.getStatusDescription();

    if (record.isExpired() && record.getStatus() == 1) {
        os << " (已过期)";
    }

    return os;
}

// BookingSystem类的成员函数实现
void BookingSystem::loadFromFiles() {
    try {
        // 加载教室信息
        ifstream classroomFile(CLASSROOM_FILE);
        if (classroomFile.is_open()) {
            string line;
            while (getline(classroomFile, line) && !line.empty()) {
                stringstream ss(line);
                string item;
                vector<string> tokens;

                while (getline(ss, item, ',')) {
                    tokens.push_back(item);
                }

                if (tokens.size() >= 6) {
                    int roomType = stoi(tokens[4]);
                    Classroom* room = nullptr;

                    switch (roomType) {
                    case 1: room = new Laboratory(); break;
                    case 2: room = new MultimediaRoom(); break;
                    case 3: room = new RecordingRoom(); break;
                    default: continue;
                    }

                    if (room) {
                        room->fromFileString(line);
                        classrooms.push_back(room);
                    }
                }
            }
            classroomFile.close();
        }

        // 加载教师信息
        ifstream teacherFile(TEACHER_FILE);
        if (teacherFile.is_open()) {
            string line;
            while (getline(teacherFile, line) && !line.empty()) {
                stringstream ss(line);
                string item;
                vector<string> tokens;

                while (getline(ss, item, ',')) {
                    tokens.push_back(item);
                }

                if (tokens.size() >= 5) {
                    int permission = stoi(tokens[2]);
                    Teacher* teacher = nullptr;

                    if (permission == 1) {
                        teacher = new Administrator();
                    }
                    else {
                        teacher = new RegularTeacher();
                    }

                    if (teacher) {
                        teacher->fromFileString(line);
                        teachers.push_back(teacher);
                    }
                }
            }
            teacherFile.close();
        }

        // 加载预订记录
        ifstream bookingFile(BOOKING_FILE);
        if (bookingFile.is_open()) {
            string line;
            while (getline(bookingFile, line) && !line.empty()) {
                BookingRecord record;
                record.fromFileString(line);
                bookings.push_back(record);
            }
            bookingFile.close();
        }
    }
    catch (const exception& e) {
        std::cout << "加载文件时出错: " << e.what() << endl;
    }
}

void BookingSystem::saveToFiles() {
    try {
        // 保存教室信息
        ofstream classroomFile(CLASSROOM_FILE);
        if (classroomFile.is_open()) {
            for (const auto& classroom : classrooms) {
                classroomFile << classroom->toFileString() << endl;
            }
            classroomFile.close();
        }

        // 保存教师信息
        ofstream teacherFile(TEACHER_FILE);
        if (teacherFile.is_open()) {
            for (const auto& teacher : teachers) {
                teacherFile << teacher->toFileString() << endl;
            }
            teacherFile.close();
        }

        // 保存预订记录
        ofstream bookingFile(BOOKING_FILE);
        if (bookingFile.is_open()) {
            for (const auto& booking : bookings) {
                bookingFile << booking.toFileString() << endl;
            }
            bookingFile.close();
        }
    }
    catch (const exception& e) {
        std::cout << "保存文件时出错: " << e.what() << endl;
    }
}

bool BookingSystem::login(const string& employeeId, const string& password) {
    for (auto& teacher : teachers) {
        if (teacher->getEmployeeId() == employeeId) {
            if (teacher->verifyPassword(password)) {
                currentUser = teacher;
                return true;
            }
            else {
                std::cout << "密码错误!" << endl;
                return false;
            }
        }
    }
    std::cout << "工号不存在!" << endl;
    return false;
}

void BookingSystem::registerTeacher() {
    try {
        std::cout << "\n=== 教师注册 ===" << endl;
        RegularTeacher* newTeacher = new RegularTeacher();
        cin >> *newTeacher;

        // 检查工号是否已存在
        for (const auto& teacher : teachers) {
            if (teacher->getEmployeeId() == newTeacher->getEmployeeId()) {
                std::cout << "该工号已存在!" << endl;
                delete newTeacher;
                return;
            }
        }

        teachers.push_back(newTeacher);
        std::cout << "注册成功!" << endl;
    }
    catch (const exception& e) {
        std::cout << "注册失败: " << e.what() << endl;
    }
}

void BookingSystem::browseClassrooms() {
    std::cout << "\n=== 教室列表 ===" << endl;
    if (classrooms.empty()) {
        std::cout << "暂无教室信息。" << endl;
        return;
    }

    for (const auto& classroom : classrooms) {
        std::cout << *classroom << endl;
    }
}

void BookingSystem::searchClassroom() {
    std::cout << "\n=== 搜索教室 ===" << endl;
    std::cout << "请选择搜索方式:" << endl;
    std::cout << "1. 按名称搜索" << endl;
    std::cout << "2. 按容量搜索" << endl;
    std::cout << "3. 按类型搜索" << endl;
    std::cout << "请选择: ";

    int choice;
    cin >> choice;

    bool found = false;

    switch (choice) {
    case 1: {
        std::cout << "请输入教室名称: ";
        string name;
        cin >> name;

        for (const auto& classroom : classrooms) {
            if (classroom->getRoomName().find(name) != string::npos) {
                std::cout << *classroom << endl;
                found = true;
            }
        }
        break;
    }
    case 2: {
        std::cout << "请输入最小容量: ";
        int minCapacity;
        cin >> minCapacity;

        for (const auto& classroom : classrooms) {
            if (classroom->getCapacity() >= minCapacity) {
                std::cout << *classroom << endl;
                found = true;
            }
        }
        break;
    }
    case 3: {
        std::cout << "请选择教室类型:" << endl;
        std::cout << "1. 实验室" << endl;
        std::cout << "2. 多媒体教室" << endl;
        std::cout << "3. 录课教室" << endl;
        std::cout << "请选择: ";

        int typeChoice;
        cin >> typeChoice;

        for (const auto& classroom : classrooms) {
            if (classroom->getRoomType() == typeChoice) {
                std::cout << *classroom << endl;
                found = true;
            }
        }
        break;
    }
    default:
        std::cout << "无效选择!" << endl;
        return;
    }

    if (!found) {
        std::cout << "未找到匹配的教室。" << endl;
    }
}

void BookingSystem::addClassroom() {
    try {
        std::cout << "\n=== 添加教室 ===" << endl;
        std::cout << "请选择教室类型:" << endl;
        std::cout << "1. 实验室" << endl;
        std::cout << "2. 多媒体教室" << endl;
        std::cout << "3. 录课教室" << endl;
        std::cout << "请选择: ";

        int choice;
        cin >> choice;

        Classroom* newRoom = nullptr;
        switch (choice) {
        case 1: newRoom = new Laboratory(); break;
        case 2: newRoom = new MultimediaRoom(); break;
        case 3: newRoom = new RecordingRoom(); break;
        default:
            std::cout << "无效选择!" << endl;
            return;
        }

        cin >> *newRoom;

        // 检查教室号是否已存在
        for (const auto& classroom : classrooms) {
            if (classroom->getRoomId() == newRoom->getRoomId()) {
                std::cout << "该教室号已存在!" << endl;
                delete newRoom;
                return;
            }
        }

        classrooms.push_back(newRoom);
        std::cout << "教室添加成功!" << endl;
    }
    catch (const exception& e) {
        std::cout << "添加教室失败: " << e.what() << endl;
    }
}

void BookingSystem::browseBookings() {
    std::cout << "\n=== 预订记录 ===" << endl;
    if (bookings.empty()) {
        std::cout << "暂无预订记录。" << endl;
        return;
    }

    // 过滤选项
    std::cout << "请选择查看方式:" << endl;
    std::cout << "1. 查看所有预订" << endl;
    std::cout << "2. 按日期查看" << endl;
    std::cout << "3. 按状态查看" << endl;
    std::cout << "请选择: ";

    int choice;
    cin >> choice;

    vector<BookingRecord> filteredBookings;

    switch (choice) {
    case 1:
        filteredBookings = bookings;
        break;
    case 2: {
        std::cout << "请输入日期 (YYYY-MM-DD): ";
        string date;
        cin >> date;

        for (const auto& booking : bookings) {
            if (booking.getTimeSlot().getDate() == date) {
                filteredBookings.push_back(booking);
            }
        }
        break;
    }
    case 3: {
        std::cout << "请选择状态:" << endl;
        std::cout << "0. 待审核" << endl;
        std::cout << "1. 已通过" << endl;
        std::cout << "2. 已拒绝" << endl;
        std::cout << "3. 已取消" << endl;
        std::cout << "4. 已完成" << endl;
        std::cout << "请选择: ";

        int statusChoice;
        cin >> statusChoice;

        for (const auto& booking : bookings) {
            if (booking.getStatus() == statusChoice) {
                filteredBookings.push_back(booking);
            }
        }
        break;
    }
    default:
        std::cout << "无效选择!" << endl;
        return;
    }

    // 如果是普通教师,只显示自己的预订记录
    if (currentUser->getPermission() == 2) {
        vector<BookingRecord> teacherBookings;
        for (const auto& booking : filteredBookings) {
            if (booking.getTeacherId() == currentUser->getEmployeeId()) {
                teacherBookings.push_back(booking);
            }
        }
        filteredBookings = teacherBookings;
    }

    if (filteredBookings.empty()) {
        std::cout << "未找到符合条件的预订记录。" << endl;
        return;
    }

    for (const auto& booking : filteredBookings) {
        std::cout << booking << endl;
    }
}

void BookingSystem::makeBooking() {
    try {
        std::cout << "\n=== 预订教室 ===" << endl;

        // 显示可用教室
        std::cout << "可用教室列表:" << endl;
        for (const auto& classroom : classrooms) {
            if (!classroom->getIsOccupied()) {
                std::cout << *classroom << endl;
            }
        }

        std::cout << "请输入要预订的教室号: ";
        int roomId;
        cin >> roomId;

        // 检查教室是否存在
        bool roomExists = false;
        for (const auto& classroom : classrooms) {
            if (classroom->getRoomId() == roomId) {
                roomExists = true;
                break;
            }
        }

        if (!roomExists) {
            std::cout << "教室不存在!" << endl;
            return;
        }

        // 输入预订日期和时间
        std::cout << "请输入预订日期 (YYYY-MM-DD): ";
        string date;
        cin >> date;

        std::cout << "请输入开始时间 (8-20之间的整数小时): ";
        int startHour;
        cin >> startHour;

        std::cout << "请输入持续时间 (小时): ";
        int duration;
        cin >> duration;

        // 创建时间段
        TimeSlot timeSlot(date, startHour, duration);

        // 验证时间段是否有效
        if (!timeSlot.isValid()) {
            std::cout << "无效的时间段!可用时间为8:00-22:00。" << endl;
            return;
        }

        // 检查时间冲突
        for (const auto& booking : bookings) {
            if (booking.getRoomId() == roomId &&
                booking.getStatus() == 1 &&
                booking.getTimeSlot().isConflict(timeSlot)) {
                std::cout << "该时间段已被预订!" << endl;
                return;
            }
        }

        // 创建预订记录
        BookingRecord newBooking(currentUser->getEmployeeId(), roomId, timeSlot);
        bookings.push_back(newBooking);

        std::cout << "预订申请提交成功,等待管理员审核。" << endl;
    }
    catch (const exception& e) {
        std::cout << "预订失败: " << e.what() << endl;
    }
}

void BookingSystem::cancelBooking() {
    try {
        std::cout << "\n=== 取消预订 ===" << endl;


        std::cout << "您的预订记录:" << endl;
        vector<BookingRecord*> userBookings;
        for (auto& booking : bookings) {
            if (booking.getTeacherId() == currentUser->getEmployeeId() &&
                booking.getStatus() != 3 && booking.getStatus() != 4) {
                std::cout << booking << endl;
                userBookings.push_back(&booking);
            }
        }

        if (userBookings.empty()) {
            std::cout << "您没有可取消的预订记录。" << endl;
            return;
        }

        std::cout << "请输入要取消的预订号: ";
        int bookingId;
        cin >> bookingId;

        for (auto& booking : bookings) {
            if (booking.getBookingId() == bookingId &&
                booking.getTeacherId() == currentUser->getEmployeeId()) {
                booking.setStatus(3); // 设置为已取消
                std::cout << "预订取消成功!" << endl;
                return;
            }
        }

        std::cout << "未找到指定的预订记录或无权限取消。" << endl;
    }
    catch (const BookingException& e) {
        std::cout << "取消预订失败: " << e.what() << endl;
    }
}

void BookingSystem::approveBooking() {
    try {
        std::cout << "\n=== 审核预订 ===" << endl;

        // 显示待审核的预订记录
        std::cout << "待审核预订记录:" << endl;
        vector<BookingRecord*> pendingBookings;
        for (auto& booking : bookings) {
            if (booking.getStatus() == 0) {
                std::cout << booking << endl;
                pendingBookings.push_back(&booking);
            }
        }

        if (pendingBookings.empty()) {
            std::cout << "没有待审核的预订记录。" << endl;
            return;
        }

        std::cout << "请输入要审核的预订号: ";
        int bookingId;
        cin >> bookingId;

        std::cout << "请选择审核结果:" << endl;
        std::cout << "1. 通过" << endl;
        std::cout << "2. 拒绝" << endl;
        std::cout << "请选择: ";
        int choice;
        cin >> choice;

        int newStatus = (choice == 1) ? 1 : 2;

        for (auto& booking : bookings) {
            if (booking.getBookingId() == bookingId && booking.getStatus() == 0) {
                // 如果选择通过,先检查是否有时间冲突
                if (newStatus == 1) {
                    for (const auto& existingBooking : bookings) {
                        if (existingBooking.getRoomId() == booking.getRoomId() &&
                            existingBooking.getStatus() == 1 &&
                            existingBooking.getTimeSlot().isConflict(booking.getTimeSlot())) {
                            std::cout << "无法通过: 该时间段已被其他预订占用!" << endl;
                            return;
                        }
                    }
                }

                booking.setStatus(newStatus);

                // 如果通过,更新教室占用状态
                if (newStatus == 1) {
                    for (auto& classroom : classrooms) {
                        if (classroom->getRoomId() == booking.getRoomId()) {
                            classroom->setOccupied(true);
                            break;
                        }
                    }
                }

                std::cout << "审核完成!" << endl;
                return;
            }
        }

        std::cout << "未找到指定的预订记录。" << endl;
    }
    catch (const BookingException& e) {
        std::cout << "审核失败: " << e.what() << endl;
    }
}

void BookingSystem::checkExpiredBookings() {
    for (auto& booking : bookings) {
        if (booking.getStatus() == 1 && booking.isExpired()) {
            booking.setStatus(4); // 设置为已完成

            // 更新教室状态
            for (auto& classroom : classrooms) {
                if (classroom->getRoomId() == booking.getRoomId()) {
                    classroom->setOccupied(false);
                    break;
                }
            }
        }
    }
}

void BookingSystem::changePassword() {
    std::cout << "\n=== 修改密码 ===" << endl;

    // 验证当前密码
    std::cout << "请输入当前密码: ";
    string currentPassword;
    cin >> currentPassword;

    if (!currentUser->verifyPassword(currentPassword)) {
        std::cout << "当前密码错误!" << endl;
        return;
    }

    // 输入新密码
    std::cout << "请输入新密码: ";
    string newPassword;
    cin >> newPassword;

    std::cout << "请再次输入新密码: ";
    string confirmPassword;
    cin >> confirmPassword;

    // 验证两次输入是否一致
    if (newPassword != confirmPassword) {
        std::cout << "两次输入的密码不一致!" << endl;
        return;
    }

    // 验证密码长度
    if (newPassword.length() < 6) {
        std::cout << "密码长度不能少于6位!" << endl;
        return;
    }

    // 更新密码
    currentUser->setPassword(newPassword);

    std::cout << "密码修改成功!" << endl;
    saveToFiles(); // 立即保存到文件
}

void BookingSystem::showMainMenu() {
    std::cout << "\n========== 教室预订管理系统 ==========" << endl;
    std::cout << "1. 登录" << endl;
    std::cout << "2. 教师注册" << endl;
    std::cout << "3. 退出" << endl;
    std::cout << "请选择: ";
}

void BookingSystem::showAdminMenu() {
    std::cout << "\n========== 管理员界面 ==========" << endl;
    std::cout << "1. 浏览教室" << endl;
    std::cout << "2. 添加教室" << endl;
    std::cout << "3. 浏览预订记录" << endl;
    std::cout << "4. 审核预订记录" << endl;
    std::cout << "5. 修改密码" << endl;
    std::cout << "6. 退出登录" << endl;
    std::cout << "请选择: ";
}

void BookingSystem::showTeacherMenu() {
    std::cout << "\n========== 教师界面 ==========" << endl;
    std::cout << "1. 浏览教室" << endl;
    std::cout << "2. 搜索教室" << endl;
    std::cout << "3. 浏览预订记录" << endl;
    std::cout << "4. 预订教室" << endl;
    std::cout << "5. 取消预订" << endl;
    std::cout << "6. 修改密码" << endl;
    std::cout << "7. 退出登录" << endl;
    std::cout << "请选择: ";
}

void BookingSystem::run() {
    int choice;

    start_flag:
    while (true) {
        // 每次循环检查过期预订
        checkExpiredBookings();

        showMainMenu();
        cin >> choice;
        if (cin.fail()) {
            cin.clear();
            cin.ignore(100, '\n');
            std::cout << "\x1b[1;31m\x1b[4m请输入有效的数字!\x1b[0m" << std::endl;
            goto start_flag;
        }
        switch (choice) {
        case 1: {
            std::cout << "请输入工号: ";
            string employeeId;
            cin >> employeeId;
            std::cout << "请输入密码: ";
            string password;
            cin >> password;

            if (login(employeeId, password)) {
                std::cout << "登录成功!欢迎 " << currentUser->getName() << endl;


                if (currentUser->getPermission() == 1) {
                    // 管理员菜单
                    int adminChoice;
                    while (true) {
                        showAdminMenu();
                        cin >> adminChoice;

                        switch (adminChoice) {
                        case 1:
                            browseClassrooms();
                            break;
                        case 2:
                            addClassroom();
                            break;
                        case 3:
                            browseBookings();
                            break;
                        case 4:
                            approveBooking();
                            break;
                        case 5:
                            changePassword();
                            break;
                        case 6:
                            currentUser = nullptr;
                            std::cout << "已退出登录。" << endl;
                            goto main_menu;
                        default:
                            std::cout << "无效选择,请重新输入。" << endl;
                        }
                    }
                }
                else {
                    // 普通教师菜单
                    int teacherChoice;
                    while (true) {
                        showTeacherMenu();
                        cin >> teacherChoice;

                        switch (teacherChoice) {
                        case 1:
                            browseClassrooms();
                            break;
                        case 2:
                            searchClassroom();
                            break;
                        case 3:
                            browseBookings();
                            break;
                        case 4:
                            makeBooking();
                            break;
                        case 5:
                            cancelBooking();
                            break;
                        case 6:
                            changePassword();
                            break;
                        case 7:
                            currentUser = nullptr;
                            std::cout << "已退出登录。" << endl;
                            goto main_menu;
                        default:
                            std::cout << "无效选择,请重新输入。" << endl;
                        }
                    }
                }
            }
            else {
                std::cout << "登录失败!" << endl;
            }
            break;
        }
        case 2:
            registerTeacher();
            break;
        case 3:
            std::cout << "感谢使用教室预订管理系统,再见!" << endl;
            return;
        default:
            std::cout << "无效选择,请重新输入。" << endl;
        }
    main_menu:;
    }
}


int main() {
    system("chcp 65001");
    system("cls");
    try {
        BookingSystem BookSystem;
        BookSystem.showWelcomeInfo();
        std::cout << "\n默认管理员账号: 10001, 10002, 10003" << endl;
        std::cout << "默认管理员密码: admin123" << endl;
        std::cout << "默认教师账号: 20001, 20002, 20003" << endl;
        std::cout << "默认教师密码: teacher123" << endl;
        std::cout << "===================================" << endl;

        BookSystem.run();
    }
    catch (const exception& e) {
        std::cout << "系统错误: " << e.what() << endl;
        return 1;
    }

    return 0;
}


原项目采用**单文件架构**,所有代码集中在一个CPP文件中,于是我想到了,将代码拆分更利于修改升级维护查找问题。 要查找单独修改一个代码很麻烦,于是将代码分块 ![image](https://img2024.cnblogs.com/blog/3772953/202603/3772953-20260310113354646-1316191812.png)
  1. 数据持久化升级: 从文本文件迁移到SQLite数据库
  2. 架构重构: 实现分层架构设计(数据层、业务逻辑层、表示层)
  3. 代码模块化: 将单文件代码拆分为多模块结构
  4. 异常处理增强: 完善错误处理机制
  5. 性能优化: 改进数据查询效率
  6. 可维护性提升: 增强代码的可读性和可扩展性
    image
    `#pragma execution_character_set("utf-8")
    改进后image

image
image

原代码片段 (文件I/O部分):

void BookingSystem::saveToFiles() {
    // 保存教室信息
    ofstream classroomFile(CLASSROOM_FILE);
    if (classroomFile.is_open()) {
        for (const auto& classroom : classrooms) {
            classroomFile << classroom->toFileString() << endl;
        }
        classroomFile.close();
    }
    
    // 保存教师信息
    ofstream teacherFile(TEACHER_FILE);
    // ... 类似操作
    
    // 保存预订记录
    ofstream bookingFile(BOOKING_FILE);
    // ... 类似操作
}

教室类继承示例:

// 基类定义
class Classroom {
protected:
    int roomId;
    string roomName;
    int capacity;
    bool isOccupied;
    int roomType;
    int attributeCode;

public:
    virtual string getTypeDescription() const = 0;  // 纯虚函数
    virtual string getAttributeDescription() const = 0;
    // ...
};

// 派生类实现
class Laboratory : public Classroom {
public:
    string getTypeDescription() const override { return "实验室"; }
    string getAttributeDescription() const override {
        switch (attributeCode) {
            case 1: return "化学实验室";
            case 2: return "物理实验室";
            // ...
        }
    }
};
bool TimeSlot::isConflict(const TimeSlot& other) const {
    if (date != other.date) return false;
    int thisEnd = startHour + duration;
    int otherEnd = other.startHour + other.duration;
    return (startHour < otherEnd && thisEnd > other.startHour);
}

原系统核心代码片段:

// 原系统: 文件读写
void BookingSystem::saveToFiles() {
    ofstream bookingFile(BOOKING_FILE);
    if (bookingFile.is_open()) {
        for (const auto& booking : bookings) {
            bookingFile << booking.toFileString() << endl;
        }
        bookingFile.close();
    }
}

// 原系统: 时间冲突检测(遍历)
for (const auto& booking : bookings) {
    if (booking.getRoomId() == roomId &&
        booking.getStatus() == 1 &&
        booking.getTimeSlot().isConflict(timeSlot)) {
        cout << "该时间段已被预订!" << endl;
        return;
    }
}

改进系统核心代码片段:

// 改进系统: 数据库操作
int Database::insertBooking(const BookingRecord& booking) {
    const char* sql = R"(
        INSERT INTO bookings (teacher_id, room_id, booking_date, 
                            start_hour, duration, status)
        VALUES (?, ?, ?, ?, ?, ?);
    )";
    sqlite3_stmt* stmt;
    sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
    // 预编译语句防止SQL注入
    sqlite3_bind_text(stmt, 1, booking.getTeacherId().c_str(), -1, SQLITE_TRANSIENT);
    sqlite3_bind_int(stmt, 2, booking.getRoomId());
    // ...
    return sqlite3_last_insert_rowid(db);
}

// 改进系统: 时间冲突检测(SQL查询+索引)
bool Database::checkTimeConflict(int roomId, const TimeSlot& timeSlot) {
    const char* sql = R"(
        SELECT COUNT(*) FROM bookings 
        WHERE room_id = ? AND booking_date = ? AND status = 1
        AND NOT (start_hour + duration <= ? OR start_hour >= ?);
    )";
    // 利用索引 idx_booking_room 和 idx_booking_date
    // 时间复杂度从 O(n) 降低到 O(log n)
}

原实现存在的问题

数据存储层面的问题

文本文件的局限性

重构实现:

// 创建数据库表结构
CREATE TABLE bookings (
    booking_id INTEGER PRIMARY KEY AUTOINCREMENT,
    teacher_id TEXT NOT NULL,
    room_id INTEGER NOT NULL,
    booking_date TEXT NOT NULL,
    start_hour INTEGER NOT NULL,
    duration INTEGER NOT NULL,
    status INTEGER DEFAULT 0,
    FOREIGN KEY (teacher_id) REFERENCES teachers(employee_id),
    FOREIGN KEY (room_id) REFERENCES classrooms(room_id)
);

// 创建索引优化查询
CREATE INDEX idx_booking_date ON bookings(booking_date);
CREATE INDEX idx_booking_room ON bookings(room_id);
CREATE INDEX idx_booking_status ON bookings(status);

架构层面的问题

BookingSystem 类承担了过多功能:

class BookingSystem {
private:
    vector classrooms;     // 数据存储
    vector teachers;
    vector bookings;
    Teacher* currentUser;              // 会话管理
    
public:
    void loadFromFiles();              // 文件I/O
    void saveToFiles();
    bool login(...);                   // 身份验证
    void browseClassrooms();           // UI交互
    void makeBooking();                // 业务逻辑
    void showMainMenu();               // 界面显示
    void run();                        // 主循环控制
};

重构实现:

// 数据访问层 - 只负责数据库操作
class Database {
public:
    bool insertBooking(const BookingRecord& booking);
    BookingRecord getBookingById(int id);
    bool updateBookingStatus(int id, BookingStatus status);
    bool checkTimeConflict(int roomId, const TimeSlot& timeSlot);
};

// 业务逻辑层 - 负责业务规则
class BookingManager {
private:
    Database* database;
public:
    bool approveBooking(int bookingId) {
        // 业务规则: 审核时检查冲突,更新教室状态
        database->beginTransaction();
        try {
            if (database->checkTimeConflict(...)) {
                throw BookingException("时间冲突");
            }
            database->updateBookingStatus(id, APPROVED);
            database->updateClassroomOccupied(roomId, true);
            database->commitTransaction();
        } catch (...) {
            database->rollbackTransaction();
        }
    }
};

// 表示层 - 负责UI交互
class BookingSystemUI {
public:
    void adminMenu();    // 管理员菜单
    void teacherMenu();  // 教师菜单
    void makeBooking();  // 预订界面
};

一个类同时负责了:

  1. 数据存储 (Data Layer)
  2. 业务逻辑 (Business Logic Layer)
  3. 用户界面 (Presentation Layer)
  4. 文件I/O (Persistence Layer)

改进:引入实体类(Entities)作为数据在各层之间传递的载体,同时使用单例模式或依赖注入来管理这些模块。
struct Classroom { int id; string name; bool isAvailable; }; struct Teacher { int id; string name; string password; }; struct BookingRecord { int id; int teacherId; int roomId; string timeSlot; };
`class Database {
public:
// 专门负责持久化
void loadData(vector& rooms, vector& teachers);
void saveData(const vector& bookings);

// 原子操作
bool updateBookingStatus(int id, int status);
bool checkTimeConflict(int roomId, const string& slot);
}; class BookingManager {
private:
Database& db; // 引用注入,方便单元测试
Teacher* currentUser = nullptr;

public:
BookingManager(Database& database) : db(database) {}

bool login(int id, const string& pwd) {
// 逻辑:验证身份并设置 currentUser
return true;
}

bool requestBooking(int roomId, const string& slot) {
if (!currentUser) return false;
if (db.checkTimeConflict(roomId, slot)) return false;
// 执行预订...
return true;
}
};`

代码质量问题

错误处理不完善

void BookingSystem::loadFromFiles() {
    try {
        ifstream classroomFile(CLASSROOM_FILE);
        if (classroomFile.is_open()) {
            // 读取数据...
        }
    }
    catch (const exception& e) {
        cout << "加载文件时出错: " << e.what() << endl;
        // 仅打印错误,没有恢复机制
    }
}

问题:

  • 文件打开失败时没有创建默认文件
  • 数据格式错误时可能导致程序崩溃
  • 缺乏日志记录机制

本次二次开发采用以下改进策略:

原系统 改进后系统
文本文件存储 SQLite数据库
单文件架构 多模块分层架构
全量内存加载 按需查询
简单异常处理 完善的错误处理

技术选型

数据库选择: SQLite

架构选择: 三层架构

数据库设计

1. 教室表 (classrooms)

CREATE TABLE classrooms (
    room_id INTEGER PRIMARY KEY,
    room_name TEXT NOT NULL,
    capacity INTEGER NOT NULL CHECK(capacity > 0),
    is_occupied INTEGER DEFAULT 0 CHECK(is_occupied IN (0,1)),
    room_type INTEGER NOT NULL CHECK(room_type IN (1,2,3)),
    attribute_code INTEGER NOT NULL,
    created_at TEXT DEFAULT CURRENT_TIMESTAMP
);

-- 索引优化
CREATE INDEX idx_room_type ON classrooms(room_type);
CREATE INDEX idx_capacity ON classrooms(capacity);

2. 教师表 (teachers)

CREATE TABLE teachers (
    employee_id TEXT PRIMARY KEY,
    name TEXT NOT NULL,
    permission INTEGER NOT NULL CHECK(permission IN (1,2)),
    phone TEXT,
    office TEXT,
    password TEXT NOT NULL,
    created_at TEXT DEFAULT CURRENT_TIMESTAMP
);

-- 索引优化
CREATE INDEX idx_permission ON teachers(permission);

3. 预订记录表 (bookings)

CREATE TABLE bookings (
    booking_id INTEGER PRIMARY KEY AUTOINCREMENT,
    teacher_id TEXT NOT NULL,
    room_id INTEGER NOT NULL,
    booking_date TEXT NOT NULL,
    start_hour INTEGER NOT NULL CHECK(start_hour >= 8 AND start_hour <= 20),
    duration INTEGER NOT NULL CHECK(duration > 0),
    submit_time TEXT DEFAULT CURRENT_TIMESTAMP,
    status INTEGER DEFAULT 0 CHECK(status IN (0,1,2,3,4)),
    FOREIGN KEY (teacher_id) REFERENCES teachers(employee_id),
    FOREIGN KEY (room_id) REFERENCES classrooms(room_id)
);

-- 索引优化
CREATE INDEX idx_booking_teacher ON bookings(teacher_id);
CREATE INDEX idx_booking_room ON bookings(room_id);
CREATE INDEX idx_booking_date ON bookings(booking_date);
CREATE INDEX idx_booking_status ON bookings(status);

状态码说明:

  • 0: 待审核
  • 1: 已通过
  • 2: 已拒绝
  • 3: 已取消
  • 4: 已完成
改进后的项目/
├── include/              # 头文件目录
│   ├── database.h        # 数据库操作层
│   ├── models.h          # 数据模型定义
│   ├── business.h        # 业务逻辑层
│   └── ui.h              # 用户界面层
├── src/                  # 源文件目录
│   ├── database.cpp
│   ├── models.cpp
│   ├── business.cpp
│   ├── ui.cpp
│   └── main.cpp          # 主程序入口
├── classroom_booking.db  # SQLite数据库文件
└── README.md             # 项目说明

类设计

**数据访问层 **:

class Database {
private:
    sqlite3* db;
    string dbPath;
    
public:
    // 基础操作
    bool connect();
    void disconnect();
    bool execute(const string& sql);
    
    // 教室CRUD
    bool insertClassroom(const Classroom& room);
    bool updateClassroom(const Classroom& room);
    bool deleteClassroom(int roomId);
    vector getAllClassrooms();
    Classroom* getClassroomById(int id);
    
    // 教师CRUD
    bool insertTeacher(const Teacher& teacher);
    Teacher* getTeacherById(const string& id);
    
    // 预订CRUD
    bool insertBooking(const BookingRecord& booking);
    bool updateBookingStatus(int bookingId, int status);
    vector getBookingsByTeacher(const string& teacherId);
    vector getBookingsByDate(const string& date);
    
    // 查询优化
    bool checkTimeConflict(int roomId, const TimeSlot& timeSlot);
};

业务逻辑层 (BLL):

class BookingManager {
private:
    Database* database;
    Teacher* currentUser;
    
public:
    // 身份验证
    bool login(const string& employeeId, const string& password);
    void logout();
    
    // 业务规则
    bool canMakeBooking(const BookingRecord& booking);
    bool validateTimeSlot(const TimeSlot& timeSlot);
    bool checkRoomAvailability(int roomId, const TimeSlot& timeSlot);
    
    // 高级功能
    vector searchClassrooms(const SearchCriteria& criteria);
    bool approveBooking(int bookingId);
    bool rejectBooking(int bookingId);
    void checkAndUpdateExpiredBookings();
};

数据库初始化

bool Database::connect() {
    int rc = sqlite3_open(dbPath.c_str(), &db);
    if (rc != SQLITE_OK) {
        cerr << "无法打开数据库: " << sqlite3_errmsg(db) << endl;
        return false;
    }
    
    // 创建表结构
    const char* createClassroomsTable = R"(
        CREATE TABLE IF NOT EXISTS classrooms (
            room_id INTEGER PRIMARY KEY,
            room_name TEXT NOT NULL,
            capacity INTEGER NOT NULL CHECK(capacity > 0),
            is_occupied INTEGER DEFAULT 0 CHECK(is_occupied IN (0,1)),
            room_type INTEGER NOT NULL CHECK(room_type IN (1,2,3)),
            attribute_code INTEGER NOT NULL,
            created_at TEXT DEFAULT CURRENT_TIMESTAMP
        );
    )";
    
    if (!execute(createClassroomsTable)) {
        return false;
    }
    
    // 创建索引
    execute("CREATE INDEX IF NOT EXISTS idx_room_type ON classrooms(room_type);");
    execute("CREATE INDEX IF NOT EXISTS idx_capacity ON classrooms(capacity);");
    
    // ... 创建其他表和索引
    
    return true;
}

插入操作(使用预编译语句防止SQL注入)

bool Database::insertClassroom(const Classroom& room) {
    const char* sql = R"(
        INSERT INTO classrooms (room_id, room_name, capacity, 
                               is_occupied, room_type, attribute_code)
        VALUES (?, ?, ?, ?, ?, ?);
    )";
    
    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
    
    if (rc != SQLITE_OK) {
        cerr << "SQL准备失败: " << sqlite3_errmsg(db) << endl;
        return false;
    }
    
    // 绑定参数
    sqlite3_bind_int(stmt, 1, room.getRoomId());
    sqlite3_bind_text(stmt, 2, room.getRoomName().c_str(), -1, SQLITE_TRANSIENT);
    sqlite3_bind_int(stmt, 3, room.getCapacity());
    sqlite3_bind_int(stmt, 4, room.getIsOccupied() ? 1 : 0);
    sqlite3_bind_int(stmt, 5, room.getRoomType());
    sqlite3_bind_int(stmt, 6, room.getAttributeCode());
    
    // 执行
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    if (rc != SQLITE_DONE) {
        cerr << "插入失败: " << sqlite3_errmsg(db) << endl;
        return false;
    }
    
    return true;
}

查询操作(时间冲突检测)

bool Database::checkTimeConflict(int roomId, const TimeSlot& timeSlot) {
    const char* sql = R"(
        SELECT COUNT(*) FROM bookings 
        WHERE room_id = ? 
          AND booking_date = ? 
          AND status = 1
          AND NOT (start_hour + duration <= ? OR start_hour >= ?);
    )";
    
    sqlite3_stmt* stmt;
    sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
    
    sqlite3_bind_int(stmt, 1, roomId);
    sqlite3_bind_text(stmt, 2, timeSlot.getDate().c_str(), -1, SQLITE_TRANSIENT);
    sqlite3_bind_int(stmt, 3, timeSlot.getStartHour());
    sqlite3_bind_int(stmt, 4, timeSlot.getEndHour());
    
    bool hasConflict = false;
    if (sqlite3_step(stmt) == SQLITE_ROW) {
        int count = sqlite3_column_int(stmt, 0);
        hasConflict = (count > 0);
    }
    
    sqlite3_finalize(stmt);
    return hasConflict;
}

事务处理实现

bool BookingManager::approveBooking(int bookingId) {
    // 开启事务
    database->execute("BEGIN TRANSACTION;");
    
    try {
        // 1. 获取预订信息
        BookingRecord booking = database->getBookingById(bookingId);
        
        if (booking.getStatus() != 0) {
            throw BookingException("该预订已被处理");
        }
        
        // 2. 检查时间冲突
        if (database->checkTimeConflict(booking.getRoomId(), 
                                       booking.getTimeSlot())) {
            throw BookingException("该时间段已被占用");
        }
        
        // 3. 更新预订状态
        if (!database->updateBookingStatus(bookingId, 1)) {
            throw BookingException("更新预订状态失败");
        }
        
        // 4. 更新教室占用状态
        if (!database->updateClassroomOccupied(booking.getRoomId(), true)) {
            throw BookingException("更新教室状态失败");
        }
        
        // 提交事务
        database->execute("COMMIT;");
        return true;
        
    } catch (const BookingException& e) {
        // 回滚事务
        database->execute("ROLLBACK;");
        cerr << "审核失败: " << e.what() << endl;
        return false;
    }
}

高级搜索功能

vector BookingManager::searchClassrooms(
    const SearchCriteria& criteria) {
    
    string sql = "SELECT * FROM classrooms WHERE 1=1";
    vector conditions;
    
    // 动态构建查询条件
    if (criteria.hasRoomType()) {
        conditions.push_back(" AND room_type = " + 
                           to_string(criteria.getRoomType()));
    }
    
    if (criteria.hasMinCapacity()) {
        conditions.push_back(" AND capacity >= " + 
                           to_string(criteria.getMinCapacity()));
    }
    
    if (!criteria.getRoomName().empty()) {
        conditions.push_back(" AND room_name LIKE '%" + 
                           criteria.getRoomName() + "%'");
    }
    
    // 组合查询
    for (const auto& condition : conditions) {
        sql += condition;
    }
    
    // 排序
    sql += " ORDER BY " + criteria.getSortField() + " " + 
           criteria.getSortOrder();
    
    return database->executeQuery(sql);
}

测试场景: 查询特定日期的所有预订

原系统实现:

// 遍历vector,时间复杂度 O(n)
for (const auto& booking : bookings) {
    if (booking.getTimeSlot().getDate() == date) {
        filteredBookings.push_back(booking);
    }
}

改进后系统:

// 使用索引查询,时间复杂度 O(log n)
SELECT * FROM bookings 
WHERE booking_date = '2026-03-15'
-- 利用 idx_booking_date 索引

原系统:

// 职责混乱
class BookingSystem {
    void browseBookings() {
        // UI代码
        cout << "=== 预订记录 ===" << endl;
        
        // 数据操作
        vector filteredBookings;
        for (const auto& booking : bookings) {
            if (booking.getTimeSlot().getDate() == date) {
                filteredBookings.push_back(booking);
            }
        }
        
        // 业务逻辑
        if (currentUser->getPermission() == 2) {
            // 过滤逻辑...
        }
        
        // UI输出
        for (const auto& booking : filteredBookings) {
            cout << booking << endl;
        }
    }
};

改进后:

// 职责清晰,分层明确

// 数据访问层
class Database {
    vector getBookingsByDate(const string& date) {
        // 纯粹的数据库查询
    }
};

// 业务逻辑层
class BookingManager {
    vector filterBookingsForUser(
        const vector& bookings, 
        const Teacher* user) {
        // 纯粹的业务规则
    }
};

// 表示层
class BookingUI {
    void displayBookings(const vector& bookings) {
        // 纯粹的UI渲染
    }
};

image
image
总结:主要成果与技术收获
成果:成功将原本 “万能类” BookingSystem 拆分为表示层(UI)、业务逻辑层(Service)、数据持久层(Database)和领域对象(Entity)。程序跑起来和以前一样稳,但代码读起来爽多了。

技术收获:

好的代码应该像乐高积木,每一块都能拆下来单独换。
理解了职责单一原则 :现在每一个类只管一件事,改 Bug 也不用满世界搜变量了。
一开始的想法:想一口气把所有问题都改掉,结果改着改着就乱了,不知道哪个功能还能用,哪个已经坏了。
先把数据库基本功能写好,测试能用
再加事务,测试能用
再优化性能,测试能用
每改一步就用Git提交一次,出问题还能回退。
数据库真的比txt文件靠谱 - 虽然用起来麻烦点,但用起来安全性实用性更好
代码分文件分层很重要 - 看着清楚,改起来方便
测试 - 自己觉得没问题,测一下才发现一堆bug

posted @ 2026-03-10 11:30  VincenzoHolmes  阅读(23)  评论(0)    收藏  举报