1. 项目背景与核心需求
去年接手了一个校园食堂订餐系统的开发需求,要求用Qt框架实现跨平台的桌面应用。系统需要支持三类用户角色:学生、工人和管理员,每种角色都有不同的权限和功能模块。作为团队主力开发,我从零开始搭建了整个系统架构,过程中踩了不少坑也积累了一些实战经验。
这个系统最核心的技术难点在于如何平衡Qt的界面开发效率与C++的业务逻辑复杂性。我们最终选择了Qt5.14+MySQL的技术栈,主要考虑到以下因素:
- Qt的信号槽机制非常适合处理用户交互事件
- QSS样式表能快速实现现代化UI
- MySQL的存储过程可以简化复杂业务逻辑
- QtSql模块对MySQL有原生支持
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计
2.1 分层架构实现
整个系统采用典型的三层架构,但在数据访问层做了特殊设计:
code复制应用层(UI)
↑↓
业务逻辑层
↑↓
数据访问层 ←─ 抽象接口
├─ MySQL实现
└─ SQLite实现(测试用)
这种设计遵循了依赖倒置原则(DIP),高层模块不直接依赖低层模块,而是通过抽象接口交互。具体实现上,我们定义了IDatabaseService纯虚类:
cpp复制class IDatabaseService {
public:
virtual bool connect(const QString& config) = 0;
virtual QVector<Dish> getDishes() = 0;
virtual bool placeOrder(const Order& order) = 0;
// ...其他抽象方法
};
2.2 数据库设计要点
MySQL数据库设计了7张核心表,这里展示用户表的创建SQL:
sql复制CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` char(60) NOT NULL COMMENT 'bcrypt哈希',
`role` enum('student','worker','admin') NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
特别注意:
- 密码存储使用bcrypt算法而非明文
- 角色字段使用ENUM类型确保数据完整性
- 为用户名添加唯一索引防止重复注册
3. 关键模块实现
3.1 用户认证模块
登录功能采用预编译SQL防止注入攻击,同时加入密码尝试限制:
cpp复制bool AuthService::login(const QString &username, const QString &password) {
if (failedAttempts[username] >= MAX_ATTEMPTS) {
QTimer::singleShot(30*60*1000, [=](){
failedAttempts.remove(username);
});
throw AuthException("尝试次数过多,请30分钟后再试");
}
QSqlQuery query;
query.prepare("SELECT password FROM users WHERE username = ?");
query.addBindValue(username);
if (!query.exec() || !query.next()) {
++failedAttempts[username];
return false;
}
QString hashedPwd = query.value(0).toString();
if (!BCrypt::validatePassword(password, hashedPwd)) {
++failedAttempts[username];
