# 题目重述
在已有客户端功能(交易记录管理、通信记录管理、报表生成)的基础上,**完整补充服务端(Server)相关代码**,包括:
- `server.h` 和 `server.cpp` 的完整定义
- TCP 服务器监听与连接处理
- 数据库初始化(SQLite)
- 处理客户端请求:添加交易、查询交易、添加通信、查询通信等
- 响应格式统一为 JSON
---
# 给出答案(完整的 Server 端代码)
### ✅ 1. 头文件:`server.h`
```cpp
#ifndef SERVER_H
#define SERVER_H
#include <QObject>
#include <QTcpServer>
#include <QSqlDatabase>
class QTcpSocket;
class Server : public QObject
{
Q_OBJECT
public:
explicit Server(QObject *parent = nullptr);
~Server();
private slots:
void onNewConnection();
void onReadyRead(); // 处理每个 socket 的数据读取
private:
void setupDatabaseSchema(); // 创建数据库表结构
void handleTransactionGet(QTcpSocket *socket, const QJsonObject &request);
void handleInsertTransaction(QTcpSocket *socket, const QJsonObject &request);
void handleCommGet(QTcpSocket *socket, const QJsonObject &request);
void handleInsertComm(QTcpSocket *socket, const QJsonObject &request);
// 辅助函数:发送错误响应
void sendError(QTcpSocket *socket, const QString &type, const QString &message);
private:
QTcpServer *m_server;
QSqlDatabase m_db;
};
#endif // SERVER_H
```
---
### ✅ 2. 源文件:`server.cpp`
```cpp
#include "server.h"
#include <QTcpSocket>
#include <QJsonDocument>
#include <QJsonObject>
#include <QSqlQuery>
#include <QSqlError>
#include <QDebug>
#include <QByteArray>
// 自定义宏简化字符串转 QByteArray
#define _ba(s) QByteArray(s)
Server::Server(QObject *parent)
: QObject(parent)
, m_server(new QTcpServer(this))
{
// 启动 TCP 服务器
if (m_server->listen(QHostAddress::Any, 1234)) {
connect(m_server, &QTcpServer::newConnection, this, &Server::onNewConnection);
qDebug() << "✅ 服务器启动成功,监听端口:1234";
} else {
qCritical() << "❌ 服务器启动失败:" << m_server->errorString();
}
// 初始化 SQLite 数据库
m_db = QSqlDatabase::addDatabase("QSQLITE");
m_db.setDatabaseName("crm.db"); // 数据库存储路径
if (!m_db.open()) {
qCritical() << "❌ 无法打开数据库:" << m_db.lastError().text();
return;
}
setupDatabaseSchema(); // 创建所需的数据表
}
void Server::onNewConnection()
{
while (m_server->hasPendingConnections()) {
QTcpSocket *socket = m_server->nextPendingConnection();
// 使用 lambda 捕获当前 socket 并绑定 readyRead 信号
connect(socket, &QTcpSocket::readyRead, [this, socket]() {
QByteArray data = socket->readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
if (doc.isNull()) {
qWarning() << "⚠️ 收到无效JSON数据:" << data;
sendError(socket, "unknown", "Invalid JSON");
return;
}
QJsonObject request = doc.object();
QString type = request["type"].toString();
if (type == "transactionget") {
handleTransactionGet(socket, request);
}
else if (type == "inserttransaction") {
handleInsertTransaction(socket, request);
}
else if (type == "commget") {
handleCommGet(socket, request);
}
else if (type == "insertcomm") {
handleInsertComm(socket, request);
}
else {
QJsonObject response;
response["type"] = type;
response["status"] = "error";
response["message"] = "Unsupported operation";
socket->write(QJsonDocument(response).toJson());
}
});
// 客户端断开时自动释放资源
connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater);
}
}
```
---
### ✅ 3. 初始化数据库表结构
```cpp
void Server::setupDatabaseSchema()
{
QSqlQuery query(m_db);
bool success = true;
// 创建交易记录表
success &= query.exec(
"CREATE TABLE IF NOT EXISTS transactions ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"manager TEXT NOT NULL,"
"customer_name TEXT NOT NULL,"
"amount REAL NOT NULL CHECK(amount > 0),"
"details TEXT NOT NULL,"
"timestamp TEXT NOT NULL"
")"
);
// 创建通信记录表
success &= query.exec(
"CREATE TABLE IF NOT EXISTS communications ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"manager TEXT NOT NULL,"
"customer_name TEXT NOT NULL,"
"comm_type TEXT NOT NULL," // email / call / meeting
"content TEXT NOT NULL,"
"duration INTEGER DEFAULT 0," // 单位:分钟
"timestamp TEXT NOT NULL"
")"
);
// 为常用查询字段创建索引(提升性能)
query.exec("CREATE INDEX IF NOT EXISTS idx_trans_customer ON transactions(customer_name)");
query.exec("CREATE INDEX IF NOT EXISTS idx_trans_manager ON transactions(manager)");
query.exec("CREATE INDEX IF NOT EXISTS idx_comm_timestamp ON communications(timestamp)");
if (success) {
qDebug() << "📦 数据库表初始化完成(transactions, communications)";
} else {
qWarning() << "❗建表过程中出现错误:" << query.lastError().text();
}
}
```
---
### ✅ 4. 处理交易记录:查询与添加
#### 查询交易记录(支持关键词搜索和“重置”)
```cpp
void Server::handleTransactionGet(QTcpSocket *socket, const QJsonObject &request)
{
QString keyword = request["data"].toString().trimmed(); // 空表示获取全部
QSqlQuery query(m_db);
QString sql = "SELECT manager, customer_name, amount, details, timestamp "
"FROM transactions";
if (!keyword.isEmpty()) {
sql += " WHERE manager LIKE ? OR customer_name LIKE ? OR details LIKE ?";
}
sql += " ORDER BY timestamp DESC"; // 最新的在前
query.prepare(sql);
if (!keyword.isEmpty()) {
QString pattern = "%" + keyword + "%";
query.addBindValue(pattern);
query.addBindValue(pattern);
query.addBindValue(pattern);
}
if (!query.exec()) {
qWarning() << "❌ 查询交易失败:" << query.lastError().text();
sendError(socket, "transactionget", "Database query failed");
return;
}
QJsonArray dataArray;
while (query.next()) {
QJsonObject row;
row["manager"] = query.value("manager").toString();
row["customer_name"] = query.value("customer_name").toString();
row["amount"] = query.value("amount").toDouble();
row["details"] = query.value("details").toString();
row["timestamp"] = query.value("timestamp").toString();
dataArray.append(row);
}
QJsonObject response;
response["type"] = "transactionget";
response["status"] = "success";
response["data"] = dataArray;
socket->write(QJsonDocument(response).toJson());
}
```
#### 添加交易记录
```cpp
void Server::handleInsertTransaction(QTcpSocket *socket, const QJsonObject &request)
{
QStringList requiredFields = {"manager", "customer_name", "details", "timestamp"};
for (const QString &field : requiredFields) {
if (request[field].toString().trimmed().isEmpty()) {
goto fail;
}
}
double amount = request["amount"].toDouble();
if (amount <= 0) {
goto fail;
}
QSqlQuery query(m_db);
query.prepare("INSERT INTO transactions (manager, customer_name, amount, details, timestamp) "
"VALUES (?, ?, ?, ?, ?)");
query.addBindValue(request["manager"].toString());
query.addBindValue(request["customer_name"].toString());
query.addBindValue(amount);
query.addBindValue(request["details"].toString());
query.addBindValue(request["timestamp"].toString());
if (!query.exec()) {
qWarning() << "❌ 插入交易失败:" << query.lastError().text();
sendError(socket, "inserttransaction", "Save to database failed");
return;
}
QJsonObject response;
response["type"] = "inserttransaction";
response["status"] = "success";
response["message"] = "交易记录添加成功";
socket->write(QJsonDocument(response).toJson());
return;
fail:
sendError(socket, "inserttransaction", "Missing or invalid parameters");
}
```
---
### ✅ 5. 处理通信记录:查询与添加
#### 查询通信记录
```cpp
void Server::handleCommGet(QTcpSocket *socket, const QJsonObject &request)
{
QString keyword = request["data"].toString().trimmed();
QSqlQuery query(m_db);
QString sql = "SELECT manager, customer_name, comm_type, content, duration, timestamp "
"FROM communications";
if (!keyword.isEmpty()) {
sql += " WHERE manager LIKE ? OR customer_name LIKE ? OR content LIKE ?";
}
sql += " ORDER BY timestamp DESC";
query.prepare(sql);
if (!keyword.isEmpty()) {
QString pattern = "%" + keyword + "%";
query.addBindValue(pattern);
query.addBindValue(pattern);
query.addBindValue(pattern);
}
if (!query.exec()) {
sendError(socket, "commget", "Query failed");
return;
}
QJsonArray arr;
while (query.next()) {
QJsonObject row;
row["manager"] = query.value(0).toString();
row["customer_name"] = query.value(1).toString();
row["comm_type"] = query.value(2).toString();
row["content"] = query.value(3).toString();
row["duration"] = query.value(4).toInt();
row["timestamp"] = query.value(5).toString();
arr.append(row);
}
QJsonObject resp;
resp["type"] = "commget";
resp["status"] = "success";
resp["data"] = arr;
socket->write(QJsonDocument(resp).toJson());
}
```
#### 添加通信记录
```cpp
void Server::handleInsertComm(QTcpSocket *socket, const QJsonObject &request)
{
QStringList fields = {"manager", "customer_name", "comm_type", "content", "timestamp"};
for (const QString &f : fields) {
if (request[f].toString().trimmed().isEmpty()) {
sendError(socket, "insertcomm", "Missing required field: " + f);
return;
}
}
int duration = request["duration"].toInt();
QSqlQuery query(m_db);
query.prepare("INSERT INTO communications (manager, customer_name, comm_type, content, duration, timestamp) "
"VALUES (?, ?, ?, ?, ?, ?)");
query.addBindValue(request["manager"]);
query.addBindValue(request["customer_name"]);
query.addBindValue(request["comm_type"]);
query.addBindValue(request["content"]);
query.addBindValue(duration);
query.addBindValue(request["timestamp"]);
if (!query.exec()) {
sendError(socket, "insertcomm", "Database error");
return;
}
QJsonObject resp;
resp["type"] = "insertcomm";
resp["status"] = "success";
resp["message"] = "通信记录添加成功";
socket->write(QJsonDocument(resp).toJson());
}
```
---
### ✅ 6. 辅助函数:统一错误返回
```cpp
void Server::sendError(QTcpSocket *socket, const QString &type, const QString &message)
{
QJsonObject response;
response["type"] = type;
response["status"] = "error";
response["message"] = message;
socket->write(QJsonDocument(response).toJson());
}
```
---
## 📦 示例请求与响应
### 添加交易请求:
```json
{
"type": "inserttransaction",
"manager": "张经理",
"customer_name": "李四",
"amount": 2999.99,
"details": "iPhone 15 Pro",
"timestamp": "2025-04-05 14:30:22"
}
```
### 查询通信请求(含“重置”):
```json
{ "type": "commget", "data": "" } // 重置 → 获取所有
{ "type": "commget", "data": "会议" } // 搜索关键字
```
---
# 代码解析
1. **模块化设计清晰**:
- 每个操作独立成函数,职责单一。
- 使用 `sendError()` 统一错误处理。
2. **安全性高**:
- 所有 SQL 使用预编译语句防止注入。
- 输入参数严格校验。
3. **高性能**:
- 对关键字段建立数据库索引。
- 查询结果按时间倒序排列。
4. **可扩展性强**:
- 可轻松新增其他模块(如客户管理、统计图表)。
- 支持未来增加分页、权限控制等功能。
---
# 知识点(列出该代码中遇到的知识点)
- **SQLite数据库操作**:使用QSqlQuery执行增删改查语句,结合预编译防止SQL注入。
- **JSON请求响应处理**:解析客户端发来的JSON,并构造结构化响应返回。
- **TCP通信与槽函数绑定**:通过QTcpSocket接收数据,用lambda表达式捕获socket对象进行响应。