当前位置: 首页 > news >正文

网络编程员工管理系统

本项目是经过了上一个单机版员工管理系统改版,基于 TCP 网络编程的员工管理系统,包含客户端和服务端两部分。服务端支持多线程并发处理多个客户端连接,使用 SQLite 数据库存储员工信息。客户端与服务端通过 JSON 格式进行数据交互,利用 cJSON 库解析和构建请求与响应,实现登录、增删改查、排序、日志等功能。客户端负责菜单交互和数据展示,服务端负责业务逻辑和数据管理。用户可通过命令行界面操作,实现员工信息的管理和统计。代码如下:

// server.c —— 多客户端员工管理系统服务端(使用 TCP + 多线程 + JSON + 登录互斥)#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <errno.h>
#include <signal.h>
#include "server_logic.h"
#include <cjson/cJSON.h>#define SERVER_PORT 8888
#define MAX_CLIENTS 100
#define BUF_SIZE 4096// ================== 登录状态管理 ==================
// 定义一个结构体 LoginSession,用于存储用户的登录会话信息
typedef struct {int user_id;        // 用户唯一标识符Role role;          // 用户角色(如管理员、普通用户等)int socket_fd;      // 客户端套接字文件描述符(用于网络通信)int in_use;         // 标记该会话是否正在被使用(1=使用中,0=空闲)char name[50];      // 用户名(最大长度 50 字符)
} LoginSession;// 全局数组,存储所有客户端的登录会话
LoginSession session_list[MAX_CLIENTS];// 互斥锁,用于保护对 session_list 的并发访问(线程安全)
pthread_mutex_t session_mutex = PTHREAD_MUTEX_INITIALIZER;// 检查指定用户名的会话是否已存在(即用户是否已登录)
int is_user_already_logged_in(const char *username) {// 加锁,防止多线程同时修改 session_listpthread_mutex_lock(&session_mutex);// 遍历所有会话for (int i = 0; i < MAX_CLIENTS; i++) {// 检查会话是否在使用中,且用户名匹配if (session_list[i].in_use && strcmp(session_list[i].name, username) == 0) {// 解锁并返回 1(表示用户已登录)pthread_mutex_unlock(&session_mutex);return 1;}}// 解锁并返回 0(表示用户未登录)pthread_mutex_unlock(&session_mutex);return 0;
}// 添加一个新的登录会话到 session_list
void add_login_session(int user_id, Role role, const char *name, int sockfd) {// 加锁,防止多线程同时修改 session_listpthread_mutex_lock(&session_mutex);// 遍历所有会话,寻找空闲位置for (int i = 0; i < MAX_CLIENTS; i++) {if (!session_list[i].in_use) {// 填充会话信息session_list[i].user_id = user_id;session_list[i].role = role;session_list[i].socket_fd = sockfd;session_list[i].in_use = 1;  // 标记为使用中// 安全复制用户名(避免缓冲区溢出)strncpy(session_list[i].name, name, sizeof(session_list[i].name) - 1);break;  // 找到空闲位置后退出循环}}// 解锁pthread_mutex_unlock(&session_mutex);
}// 根据套接字文件描述符移除对应的登录会话
void remove_login_session_by_fd(int sockfd) {// 加锁,防止多线程同时修改 session_listpthread_mutex_lock(&session_mutex);// 遍历所有会话for (int i = 0; i < MAX_CLIENTS; i++) {// 检查会话是否在使用中,且套接字匹配if (session_list[i].in_use && session_list[i].socket_fd == sockfd) {session_list[i].in_use = 0;  // 标记为空闲break;  // 找到后退出循环}}// 解锁pthread_mutex_unlock(&session_mutex);
}// 根据用户名移除对应的登录会话(压缩数组)
// 移除指定用户名的在线会话(使用 in_use 标志位处理)
void remove_session_by_name(const char *name) {pthread_mutex_lock(&session_mutex); // 加锁for (int i = 0; i < MAX_CLIENTS; i++) {if (session_list[i].in_use && strcmp(session_list[i].name, name) == 0) {session_list[i].in_use = 0; // 标记为未使用break;}}pthread_mutex_unlock(&session_mutex); // 解锁
}
// ================== 线程处理函数 ==================
void *client_handler(void *arg) {int client_fd = *(int *)arg;free(arg);char buffer[BUF_SIZE];int nbytes;while ((nbytes = read(client_fd, buffer, BUF_SIZE)) > 0) {buffer[nbytes] = '\0';cJSON *request = cJSON_Parse(buffer);if (!request) {fprintf(stderr, "收到无效 JSON 请求\n");continue;}cJSON *response = handle_client_request(request, client_fd, is_user_already_logged_in,add_login_session);if (response) {char *res_str = cJSON_PrintUnformatted(response);write(client_fd, res_str, strlen(res_str));free(res_str);cJSON_Delete(response);}cJSON_Delete(request);}printf("客户端断开连接。\n");remove_login_session_by_fd(client_fd);close(client_fd);return NULL;
}int main() {if (initialize_database() != 0) {fprintf(stderr, "数据库初始化失败!\n");return 1;}int server_fd, client_fd;struct sockaddr_in server_addr, client_addr;socklen_t client_len = sizeof(client_addr);if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {perror("socket 创建失败");exit(1);}server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);server_addr.sin_addr.s_addr = INADDR_ANY;if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("bind 失败");exit(1);}if (listen(server_fd, 10) == -1) {perror("listen 失败");exit(1);}printf("服务器启动成功,监听端口:%d\n", SERVER_PORT);while (1) {client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len);if (client_fd == -1) {perror("accept 失败");continue;}printf("客户端连接:%d\n", client_fd);int *new_sock = malloc(sizeof(int));*new_sock = client_fd;pthread_t tid;if (pthread_create(&tid, NULL, client_handler, new_sock) != 0) {perror("线程创建失败");close(client_fd);free(new_sock);}pthread_detach(tid);}close(server_fd);return 0;
}
// client.c —— 员工管理系统 TCP 客户端主程序(负责菜单和功能交互)#include <stdio.h>              // 标准输入输出
#include <stdlib.h>             // exit(), malloc() 等
#include <string.h>             // 字符串函数
#include <unistd.h>             // read(), write(), close()
#include <arpa/inet.h>          // 网络地址处理
#include <cjson/cJSON.h>        // 使用 cJSON 解析/构建 JSON 数据
#include "regular.h"            // 输入验证函数(正则、范围等)
#include "server_logic.h"
// 服务器地址和端口
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8888// 缓冲区大小
#define BUF_SIZE 4096// TCP socket 文件描述符
int sock = -1;// 当前登录用户的信息(用于身份和权限判断)
int current_user_id = -1;           // 当前用户ID
int current_user_role = -1;         // 角色:0=管理员,1=员工
char current_user_name[50] = {0};   // 当前用户名// ======================
// 功能函数声明区域
// ======================// 管理员功能模块函数(由菜单调用)
void view_specific_person_logs();
void view_employees_paginated();        // 分页查看所有员工
void add_employee();                    // 添加新员工
void delete_employee();                 // 删除员工
void modify_employee();                 // 修改员工字段
void view_logs();                       // 查看系统日志
void sort_by_salary();                  // 工资降序显示
void sort_by_age();                     // 年龄升序显示
void view_department_gender_count();    // 查看部门男女员工人数// 员工功能模块函数
void change_password();                 // 修改密码(自身)
void view_department_employees();       // 查看本部门员工
void view_personal_logs();              // 查看自己日志
// 辅助函数
const char *department_to_string(Department dept);
const char *title_to_string(Title title);
const char *gender_to_string(int gender);// 向服务端发送 JSON 请求字符串
void send_request(const char *json_str) {write(sock, json_str, strlen(json_str)); // 通过 socket 写入请求内容
}// 接收服务端响应,并解析为 cJSON 对象
cJSON *receive_response() {char buffer[BUF_SIZE]; // 接收缓冲区int len = read(sock, buffer, sizeof(buffer) - 1); // 读取响应内容if (len <= 0) {printf("服务器断开连接\n");exit(1); // 断开则退出}buffer[len] = '\0'; // 添加字符串结束符return cJSON_Parse(buffer); // 解析为 cJSON 对象
}// 打印响应中的 status 和 message 字段
void print_message(cJSON *response) {if (!response) return;cJSON *status = cJSON_GetObjectItem(response, "status");   // 提取状态字段cJSON *message = cJSON_GetObjectItem(response, "message"); // 提取消息字段if (status && message && cJSON_IsString(status) && cJSON_IsString(message)) {printf("[%s] %s\n", status->valuestring, message->valuestring);}
}// 辅助函数:性别枚举转字符串
const char *gender_to_string(int gender)
{switch (gender) {case 0:return "男";case 1:return "女";default:return "未知";}
}
// 辅助函数:部门枚举转字符串
const char *department_to_string(Department dept)
{switch (dept){case SALES:return "销售";case MARKETING:return "市场";case AFTER_SALE:return "售后";default: return "未知";}
}// 辅助函数:职位枚举转字符串
const char *title_to_string(Title title)
{switch (title){case MANAGER:return "经理";case CAPTAIN:return "主管";case WORKER:return "员工";default: return "未知";}
}// 登录前主菜单
void main_menu() {printf("\n====== 欢迎使用员工管理系统 ======\n");printf(" 1. 管理员登录\n");printf(" 2. 员工登录\n");printf(" 3. 退出系统\n");
}// 登录接口(参数 role:0=管理员,1=员工)
bool login_menu(int role) {// 提示身份并输入用户名密码printf("\n=== %s登录界面 ===\n", role == 0 ? "管理员" : "员工");// 使用正则验证姓名与密码格式char *name = read_string("请输入用户名: ", validate_name);char *password = read_string("请输入密码: ", validate_password);// 构建 JSON 登录请求cJSON *req = cJSON_CreateObject();cJSON_AddStringToObject(req, "type", role == 0 ? "admin_login" : "employee_login");cJSON_AddStringToObject(req, "name", name);cJSON_AddStringToObject(req, "password", password);// 发送请求到服务器char *out = cJSON_PrintUnformatted(req);//将 JSON 对象序列化为一个无格式的字符串send_request(out);free(out);          // 释放 JSON 字符串cJSON_Delete(req);  // 释放 JSON 对象// 接收并解析响应cJSON *res = receive_response();if (!res) return false;// 提取状态与信息字段const char *status = cJSON_GetObjectItem(res, "status")->valuestring;const char *message = cJSON_GetObjectItem(res, "message")->valuestring;// 登录成功:设置当前用户信息if (strcmp(status, "success") == 0) {current_user_role = role;current_user_id = cJSON_GetObjectItem(res, role == 0 ? "admin_id" : "employee_id")->valueint;strncpy(current_user_name, name, sizeof(current_user_name) - 1);printf("[登录成功] %s\n", message);cJSON_Delete(res);free(name);free(password);return true;} else {// 登录失败printf("[登录失败] %s\n", message);cJSON_Delete(res);free(name);free(password);return false;}
}// 主动退出登录,通知服务端移除在线状态
void logout() {if (current_user_name[0] == '\0') return; // 如果没登录就返回cJSON *req = cJSON_CreateObject();                         // 创建 JSON 请求cJSON_AddStringToObject(req, "type", "logout");            // 指定请求类型为 logoutcJSON_AddStringToObject(req, "name", current_user_name);   // 添加用户名char *out = cJSON_PrintUnformatted(req);send_request(out); // 发送请求给服务端free(out);cJSON_Delete(req);// 本地状态清除current_user_id = -1;current_user_role = -1;memset(current_user_name, 0, sizeof(current_user_name));
}// 管理员菜单函数(循环)
void admin_menu() {while (1) {// 展示管理员可用功能菜单printf("\n=== 管理员菜单 ===\n");printf("1. 查看所有员工(分页)\n");printf("2. 添加员工\n");printf("3. 删除员工\n");printf("4. 修改员工信息\n");printf("5. 查看系统日志\n");printf("6. 按工资降序显示员工\n");printf("7. 按年龄升序显示员工\n");printf("8. 查看部门男女员工人数\n");printf("9. 通过 ID 查看指定人日志\n");printf("0. 退出登录\n");// 使用验证函数确保输入合法(0~8)int choice = read_int("请选择操作: ", validate_admin_choice);// 分发处理switch (choice) {case 1: view_employees_paginated(); break;case 2: add_employee(); break;case 3: delete_employee(); break;case 4: modify_employee(); break;case 5: view_logs(); break;case 6: sort_by_salary(); break;case 7: sort_by_age(); break;case 8: view_department_gender_count(); break;case 9: view_specific_person_logs(); break;case 0:logout();        // 发退出登录请求printf("管理员退出登录。\n");return; // 返回主菜单default:printf("无效操作,请重新输入。\n");break;}}
}// 员工菜单函数(循环)
void employee_menu() {while (1) {// 展示员工菜单功能printf("\n=== 员工菜单 ===\n");printf("1. 查看本部门员工\n");printf("2. 修改密码\n");printf("3. 查看我的操作日志\n");printf("0. 退出登录\n");// 输入验证:0~3int choice = read_int("请选择操作: ", validate_employee_choice);// 分发操作switch (choice) {case 1: view_department_employees(); break;case 2: change_password(); break;case 3: view_personal_logs(); break;case 0:logout();        // 发退出登录请求printf("员工退出登录。\n");return; // 返回主菜单default:printf("无效操作,请重新输入。\n");break;}}
}// ======================
// 主函数入口
// ======================int main() {// 创建 socket 并连接服务器struct sockaddr_in serv;sock = socket(AF_INET, SOCK_STREAM, 0);serv.sin_family = AF_INET;serv.sin_port = htons(SERVER_PORT);serv.sin_addr.s_addr = inet_addr(SERVER_IP);// 连接服务器失败则退出if (connect(sock, (struct sockaddr *)&serv, sizeof(serv)) < 0) {perror("连接服务器失败");exit(1);}printf("已连接服务器 %s:%d\n", SERVER_IP, SERVER_PORT);// 主循环:显示主菜单,执行登录与角色菜单while (1) {main_menu(); // 登录前菜单int choice = read_int("请选择: ", validate_main_choice); // 输入菜单选项if (choice == 1 || choice == 2) {// 登录成功后进入对应角色菜单if (login_menu(choice - 1)) {if (current_user_role == 0)admin_menu();        // 管理员菜单elseemployee_menu();     // 员工菜单}} else if (choice == 3) {// 退出系统printf("欢迎下次光临!\n");close(sock); // 关闭 socketbreak;}}return 0;
}
//========================================================管理员功能实现函数=============================================================
// 管理员通过 ID 查看指定人日志
void view_specific_person_logs() {printf("\n=== 通过 ID 查看指定人日志 ===\n");// 输入要查看日志的员工 IDint id = read_int("请输入要查看日志的员工 ID: ", validate_employee_id);// 构造请求cJSON *req = cJSON_CreateObject();cJSON_AddStringToObject(req, "type", "view_specific_person_logs");cJSON_AddNumberToObject(req, "id", id);char *out = cJSON_PrintUnformatted(req);send_request(out);free(out);cJSON_Delete(req);// 接收服务端响应cJSON *res = receive_response();if (!res) return;// 获取日志字段并显示cJSON *logs = cJSON_GetObjectItem(res, "log");if (cJSON_IsArray(logs)) {for (int i = 0; i < cJSON_GetArraySize(logs); i++) {cJSON *line = cJSON_GetArrayItem(logs, i);if (cJSON_IsString(line)) {printf("%s", line->valuestring);}}} else {printf("暂无日志记录或数据无效。\n");}cJSON_Delete(res);
}
// 分页查看所有员工信息,并格式化显示性别、部门、职位中文
void view_employees_paginated() {int page = 1; // 当前页码从1开始while (1) {// 构建请求 JSON 数据cJSON *req = cJSON_CreateObject();cJSON_AddStringToObject(req, "type", "view_employees");cJSON_AddNumberToObject(req, "page", page);// 序列化并发送请求char *out = cJSON_PrintUnformatted(req);send_request(out);free(out);cJSON_Delete(req);// 接收服务端响应cJSON *res = receive_response();if (!res) return;// 提取员工列表数组cJSON *list = cJSON_GetObjectItem(res, "data");printf("\n=== 第 %d 页员工列表 ===\n", page);if (cJSON_IsArray(list)) {for (int i = 0; i < cJSON_GetArraySize(list); i++) {cJSON *emp = cJSON_GetArrayItem(list, i);// 提取字段值int id = cJSON_GetObjectItem(emp, "id")->valueint;const char *name = cJSON_GetObjectItem(emp, "name")->valuestring;int gender = cJSON_GetObjectItem(emp, "gender")->valueint;int age = cJSON_GetObjectItem(emp, "age")->valueint;float salary = cJSON_GetObjectItem(emp, "salary")->valuedouble;int department = cJSON_GetObjectItem(emp, "department")->valueint;int title = cJSON_GetObjectItem(emp, "title")->valueint;// 使用辅助函数转换为中文描述const char *gender_str = gender_to_string(gender);const char *department_str = department_to_string(department);const char *title_str = title_to_string(title);// 输出格式化员工信息printf("ID:%d 姓名:%s 性别:%s 年龄:%d 工资:%.2f 部门:%s 职位:%s\n",id, name, gender_str, age, salary, department_str, title_str);}} else {printf("无数据或数据格式错误。\n");}// 翻页操作提示printf("\n[P]上一页 [N]下一页 [Q]退出分页: ");char cmd[10];scanf("%s", cmd);while (getchar() != '\n');// 分页控制if (strcmp(cmd, "P") == 0 || strcmp(cmd, "p") == 0) {if (page > 1) page--;} else if (strcmp(cmd, "N") == 0 || strcmp(cmd, "n") == 0) {page++;} else {cJSON_Delete(res);break;}cJSON_Delete(res); // 清理响应 JSON}
}// 添加员工(带重名校验)
void add_employee() {printf("\n=== 添加员工 ===\n");char name[50]; // 用于最终通过验证的姓名// ======= 循环验证员工是否存在 =======while (1) {char *input_name = read_string("姓名: ", validate_name);cJSON *check_req = cJSON_CreateObject();cJSON_AddStringToObject(check_req, "type", "check_employee_exist");cJSON_AddStringToObject(check_req, "name", input_name);char *check_out = cJSON_PrintUnformatted(check_req);send_request(check_out);free(check_out);cJSON_Delete(check_req);cJSON *check_res = receive_response();const char *check_status = cJSON_GetObjectItem(check_res, "status")->valuestring;if (strcmp(check_status, "fail") == 0) {// 未存在,可以使用strncpy(name, input_name, sizeof(name));free(input_name);cJSON_Delete(check_res);break;} else {printf("[提示] 员工已存在,请重新输入姓名。\n");free(input_name);cJSON_Delete(check_res);}}// ======= 继续输入其他字段 =======char *password = read_string("密码: ", validate_password);int age = read_int("年龄: ", validate_age);int gender = read_int("性别 (0=男,1=女): ", validate_gender);float salary = read_float("薪资: ", validate_salary);int department = read_int("部门编号(0销售/1市场/2售后): ", validate_department);int title = read_int("职位编号(0经理/1主管/2员工): ", validate_title);//int role = read_int("角色(0=管理员/1=员工): ", validate_role);int role = 1;// 构造添加员工请求cJSON *req = cJSON_CreateObject();cJSON_AddStringToObject(req, "type", "add_employee");cJSON_AddStringToObject(req, "name", name);cJSON_AddStringToObject(req, "password", password);cJSON_AddNumberToObject(req, "age", age);cJSON_AddNumberToObject(req, "gender", gender);cJSON_AddNumberToObject(req, "salary", salary);cJSON_AddNumberToObject(req, "department", department);cJSON_AddNumberToObject(req, "title", title);cJSON_AddNumberToObject(req, "role", role);char *out = cJSON_PrintUnformatted(req);send_request(out);free(out);cJSON_Delete(req);cJSON *res = receive_response();print_message(res);cJSON_Delete(res);free(password);
}
// 删除员工(输入ID后先检查是否存在)
void delete_employee() {printf("\n=== 删除员工 ===\n");// 1. 输入员工ID(已验证为正整数)int id = read_int("请输入要删除的员工ID: ", validate_employee_id);// 2. 向服务端发送查询请求,判断员工是否存在cJSON *check_req = cJSON_CreateObject();cJSON_AddStringToObject(check_req, "type", "check_employee_id_exist");cJSON_AddNumberToObject(check_req, "id", id);char *check_out = cJSON_PrintUnformatted(check_req);send_request(check_out);free(check_out);cJSON_Delete(check_req);cJSON *check_res = receive_response();const char *status = cJSON_GetObjectItem(check_res, "status")->valuestring;// 3. 员工不存在,终止删除if (strcmp(status, "fail") == 0) {printf("[提示] 员工ID不存在,无法删除。\n");cJSON_Delete(check_res);return;}cJSON_Delete(check_res); // 员工存在,可以删除// 4. 构造删除请求cJSON *req = cJSON_CreateObject();cJSON_AddStringToObject(req, "type", "delete_employee");cJSON_AddNumberToObject(req, "id", id);char *out = cJSON_PrintUnformatted(req);send_request(out);free(out);cJSON_Delete(req);// 5. 接收响应并显示结果cJSON *res = receive_response();print_message(res);cJSON_Delete(res);
}
// 修改员工指定字段(支持姓名、年龄、性别、工资、部门、职位、密码)
void modify_employee() {printf("\n=== 修改员工信息 ===\n");// 1. 输入员工IDint id = read_int("请输入要修改的员工ID: ", validate_employee_id);// 2. 查询员工是否存在cJSON *check_req = cJSON_CreateObject();cJSON_AddStringToObject(check_req, "type", "check_employee_id_exist");cJSON_AddNumberToObject(check_req, "id", id);char *check_out = cJSON_PrintUnformatted(check_req);send_request(check_out);free(check_out);cJSON_Delete(check_req);cJSON *check_res = receive_response();const char *status = cJSON_GetObjectItem(check_res, "status")->valuestring;if (strcmp(status, "fail") == 0) {printf("[提示] 员工ID不存在,无法修改。\n");cJSON_Delete(check_res);return;}cJSON_Delete(check_res);// 3. 选择要修改的字段printf("可修改字段:1=姓名 2=年龄 3=性别 4=工资 5=部门 6=职位 7=密码\n");int field = read_int("请输入字段编号: ", validate_modify_choice);// 4. 构造请求并输入新值cJSON *req = cJSON_CreateObject();cJSON_AddStringToObject(req, "type", "modify_employee");cJSON_AddNumberToObject(req, "id", id);cJSON_AddNumberToObject(req, "field", field);// 输入字段值(根据字段类型判断)if (field == 1) {char *val = read_string("新姓名: ", validate_name);cJSON_AddStringToObject(req, "value", val);free(val);} else if (field == 2) {int val = read_int("新年龄: ", validate_age);cJSON_AddNumberToObject(req, "value", val);} else if (field == 3) {int val = read_int("新性别(0男1女): ", validate_gender);cJSON_AddNumberToObject(req, "value", val);} else if (field == 4) {float val = read_float("新工资: ", validate_salary);cJSON_AddNumberToObject(req, "value", val);} else if (field == 5) {int val = read_int("部门编号(0销售/1市场/2售后): ", validate_department);cJSON_AddNumberToObject(req, "value", val);} else if (field == 6) {int val = read_int("职位编号(0经理/1主管/2员工): ", validate_title);cJSON_AddNumberToObject(req, "value", val);} else if (field == 7) {char *val = read_string("新密码: ", validate_password);cJSON_AddStringToObject(req, "value", val);free(val);} else {printf("无效字段编号。\n");cJSON_Delete(req);return;}char *out = cJSON_PrintUnformatted(req);send_request(out);free(out);cJSON_Delete(req);cJSON *res = receive_response();print_message(res);cJSON_Delete(res);
}
// 管理员查看系统操作日志
void view_logs() {printf("\n=== 系统日志 ===\n");cJSON *req = cJSON_CreateObject();cJSON_AddStringToObject(req, "type", "view_logs");char *out = cJSON_PrintUnformatted(req);send_request(out);free(out);cJSON_Delete(req);cJSON *res = receive_response();if (!res) return;cJSON *logs = cJSON_GetObjectItem(res, "log");if (cJSON_IsArray(logs)) {for (int i = 0; i < cJSON_GetArraySize(logs); i++) {cJSON *line = cJSON_GetArrayItem(logs, i);if (cJSON_IsString(line)) {printf("%s", line->valuestring);}}} else {printf("日志数据无效。\n");}cJSON_Delete(res);
}
// 按工资降序显示员工信息
void sort_by_salary() {cJSON *req = cJSON_CreateObject();cJSON_AddStringToObject(req, "type", "sort_by_salary");char *out = cJSON_PrintUnformatted(req);send_request(out);free(out);cJSON_Delete(req);cJSON *res = receive_response();if (!res) return;printf("\n=== 员工工资降序列表 ===\n");cJSON *list = cJSON_GetObjectItem(res, "data");if (cJSON_IsArray(list)) {for (int i = 0; i < cJSON_GetArraySize(list); i++) {cJSON *emp = cJSON_GetArrayItem(list, i);printf("ID:%d 姓名:%s 工资:%.2f\n",cJSON_GetObjectItem(emp, "id")->valueint,cJSON_GetObjectItem(emp, "name")->valuestring,cJSON_GetObjectItem(emp, "salary")->valuedouble);}}cJSON_Delete(res);
}
// 按年龄升序显示员工信息
void sort_by_age() {cJSON *req = cJSON_CreateObject();cJSON_AddStringToObject(req, "type", "sort_by_age");char *out = cJSON_PrintUnformatted(req);send_request(out);free(out);cJSON_Delete(req);cJSON *res = receive_response();if (!res) return;printf("\n=== 员工年龄升序列表 ===\n");cJSON *list = cJSON_GetObjectItem(res, "data");if (cJSON_IsArray(list)) {for (int i = 0; i < cJSON_GetArraySize(list); i++) {cJSON *emp = cJSON_GetArrayItem(list, i);printf("ID:%d 姓名:%s 年龄:%d\n",cJSON_GetObjectItem(emp, "id")->valueint,cJSON_GetObjectItem(emp, "name")->valuestring,cJSON_GetObjectItem(emp, "age")->valueint);}}cJSON_Delete(res);
}
// 显示各部门男/女员工统计
void view_department_gender_count() {cJSON *req = cJSON_CreateObject();cJSON_AddStringToObject(req, "type", "department_gender_count");char *out = cJSON_PrintUnformatted(req);send_request(out);free(out);cJSON_Delete(req);cJSON *res = receive_response();if (!res) return;printf("\n=== 各部门男女员工统计 ===\n");cJSON *data = cJSON_GetObjectItem(res, "data");if (cJSON_IsArray(data)) {for (int i = 0; i < cJSON_GetArraySize(data); i++) {cJSON *dep = cJSON_GetArrayItem(data, i);int dept = cJSON_GetObjectItem(dep, "department")->valueint;int male = cJSON_GetObjectItem(dep, "male")->valueint;int female = cJSON_GetObjectItem(dep, "female")->valueint;printf("部门:%s  男:%d  女:%d\n",department_to_string(dept), male, female);}}cJSON_Delete(res);
}
//==================================================================员工功能实现函数==================================================================
// 员工/管理员修改自己的密码(根据当前登录身份判断)
void change_password() {printf("\n=== 修改密码 ===\n");// 输入新密码(带格式验证)char *new_pwd = read_string("请输入新密码: ", validate_password);// 构造修改密码请求cJSON *req = cJSON_CreateObject();cJSON_AddStringToObject(req, "type", "change_password");cJSON_AddNumberToObject(req, "id", current_user_id);     // 当前登录用户IDcJSON_AddNumberToObject(req, "role", current_user_role); // 当前身份(0=管理员,1=员工)cJSON_AddStringToObject(req, "new_password", new_pwd);// 发送 JSON 请求char *out = cJSON_PrintUnformatted(req);send_request(out);free(out);cJSON_Delete(req);// 接收并打印服务端响应cJSON *res = receive_response();print_message(res);cJSON_Delete(res);free(new_pwd);
}
// 员工查看自己所在部门的所有同事信息
void view_department_employees() {printf("\n=== 查看本部门员工 ===\n");// 构造请求(当前用户ID,服务端根据此查出其所属部门)cJSON *req = cJSON_CreateObject();cJSON_AddStringToObject(req, "type", "view_department_employees");cJSON_AddNumberToObject(req, "id", current_user_id);char *out = cJSON_PrintUnformatted(req);send_request(out);free(out);cJSON_Delete(req);// 接收响应数据cJSON *res = receive_response();if (!res) return;cJSON *list = cJSON_GetObjectItem(res, "data");if (cJSON_IsArray(list)) {for (int i = 0; i < cJSON_GetArraySize(list); i++) {cJSON *emp = cJSON_GetArrayItem(list, i);// 提取字段const char *name = cJSON_GetObjectItem(emp, "name")->valuestring;int gender = cJSON_GetObjectItem(emp, "gender")->valueint;int age = cJSON_GetObjectItem(emp, "age")->valueint;float salary = cJSON_GetObjectItem(emp, "salary")->valuedouble;int title = cJSON_GetObjectItem(emp, "title")->valueint;// 输出格式化信息printf("姓名:%s 性别:%s 年龄:%d 工资:%.2f 职位:%s\n",name,gender_to_string(gender),age,salary,title_to_string(title));}} else {printf("无法获取本部门员工信息。\n");}cJSON_Delete(res);
}
// 查看当前员工自己的操作日志
void view_personal_logs() {printf("\n=== 我的操作日志 ===\n");// 构造请求:传入当前用户IDcJSON *req = cJSON_CreateObject();cJSON_AddStringToObject(req, "type", "view_personal_logs");cJSON_AddNumberToObject(req, "id", current_user_id);char *out = cJSON_PrintUnformatted(req);send_request(out);free(out);cJSON_Delete(req);// 接收服务端响应cJSON *res = receive_response();if (!res) return;// 获取日志字段并显示cJSON *log = cJSON_GetObjectItem(res, "log");if (cJSON_IsArray(log)) {for (int i = 0; i < cJSON_GetArraySize(log); i++) {cJSON *line = cJSON_GetArrayItem(log, i);if (cJSON_IsString(line)) {printf("%s", line->valuestring);}}} else {printf("暂无日志记录。\n");}cJSON_Delete(res);
}
#ifndef SERVER_LOGIC_H
#define SERVER_LOGIC_H#include <cjson/cJSON.h>     // 使用 cJSON 进行 JSON 解析与构建// ==========================
// 枚举定义:角色类型
// ==========================
typedef enum {ROLE_ADMIN = 0,      // 管理员ROLE_EMPLOYEE = 1    // 普通员工
} Role;typedef enum { SALES, MARKETING, AFTER_SALE 
} Department;typedef enum { MANAGER, CAPTAIN, WORKER 
} Title;// ==========================
// 外部变量声明
// ==========================
#include <sqlite3.h>
extern sqlite3 *db;// ==========================
// 日志线程写入相关函数
// ==========================// 启动异步日志线程:记录某个用户执行某操作
void writeLogThread(const char *username, const char *operation);// ==========================
// 数据库初始化与通用接口
// ==========================// 初始化 SQLite 数据库、表结构、默认管理员
int initialize_database(void);// 构建 JSON 响应对象
cJSON *build_response(const char *status, const char *message);// 管理员登录验证:返回 ID 或 -1
int verify_admin_credentials(const char *name, const char *password);// 员工登录验证:返回 ID 或 -1
int verify_employee_credentials(const char *name, const char *password);//退出登入
void remove_session_by_name(const char *name);// ==========================
// 查询工具函数(配合增删查验)
// ==========================// 通过姓名查找员工 ID(返回 0=不存在,>0=存在,<0=异常)
int find_employee_id_by_name(const char *name);// 通过 ID 获取员工姓名(并存入 name_buffer)
int find_employee_name_by_id(int employee_id, char *name_buffer, size_t buffer_size);// ==========================
// 客户端请求调度主函数
// ==========================// 中央调度函数:根据 JSON 中 type 调用对应逻辑处理函数
cJSON *handle_client_request(cJSON *request,int client_fd,int (*is_logged_in)(const char *username),void (*add_session)(int id, Role role, const char *name, int sockfd)
);// ==========================
// 管理员功能逻辑接口声明
// ==========================cJSON *handle_check_employee_exist(cJSON *request);         // 查重
cJSON *handle_check_employee_id_exist(cJSON *request);      // 查ID
cJSON *handle_add_employee(cJSON *request);                 // 添加员工
cJSON *handle_delete_employee(cJSON *request);              // 删除员工
cJSON *handle_modify_employee(cJSON *request);              // 修改员工信息
cJSON *handle_view_all_employees(cJSON *request);           // 分页查看员工
cJSON *handle_sort_by_salary(cJSON *request);               // 工资排序
cJSON *handle_sort_by_age(cJSON *request);                  // 年龄排序
cJSON *handle_view_logs(cJSON *request);                    // 查看系统日志
cJSON *handle_department_gender_count(cJSON *request);      // 部门性别统计
cJSON *handle_view_specific_person_logs(cJSON *request);		//通过id查看日志// ==========================
// 员工功能逻辑接口声明
// ==========================cJSON *handle_view_department_employees(cJSON *request);    // 查看同部门员工
cJSON *handle_view_personal_logs(cJSON *request);           // 查看个人日志// ==========================
// 通用功能
// ==========================cJSON *handle_change_password(cJSON *request);              // 修改密码
cJSON *handle_logout(cJSON *request);						//退出登入#endif // SERVER_LOGIC_H

//=======服务端逻辑实现==========
#include "server_logic.h"
#include <sqlite3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <pthread.h>
#include <time.h>
#include <assert.h>// 全局数据库连接
sqlite3 *db = NULL;// 日志互斥锁
pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;// 日志结构体,用于线程传参
typedef struct {char *username;char *operation;
} LogData;// 获取当前格式化时间
char* getTime() {time_t now;time(&now);struct tm* t = localtime(&now);char *times = malloc(64);if (!times) {perror("malloc failed");exit(EXIT_FAILURE);}strftime(times, 64, "%Y年%m月%d日 %H:%M:%S", t);return times;
}// 实际写入日志文件的函数
void writeLog(const char* username, const char* operation) {char* times = getTime();int id = find_employee_id_by_name(username); // 查ID用于日志记录pthread_mutex_lock(&log_mutex); // 加锁,防止并发写日志FILE* fd = fopen("log.txt", "a+");if (!fd) {perror("无法打开日志文件");pthread_mutex_unlock(&log_mutex);free(times);return;}fprintf(fd, "时间: %s, ID:%d, 用户: %s, 操作: %s\n", times, id, username, operation);fclose(fd);pthread_mutex_unlock(&log_mutex);free(times); // 释放时间字符串内存
}// 日志线程入口函数
void* logThreadInterface(void* arg) {LogData* data = (LogData*)arg;writeLog(data->username, data->operation);free(data->username);free(data->operation);free(data);return NULL;
}// 启动异步线程写入日志
void writeLogThread(const char* username, const char* operation) {LogData* data = malloc(sizeof(LogData));if (!data) return;data->username = strdup(username);data->operation = strdup(operation);pthread_t tid;if (pthread_create(&tid, NULL, logThreadInterface, data) != 0) {perror("创建日志线程失败");free(data->username);free(data->operation);free(data);return;}pthread_detach(tid); // 无需等待线程返回
}
//===========日志结束===================
// 初始化数据库连接与表结构
int initialize_database() {int rc = sqlite3_open("EmployeeManagementSystem.db", &db);if (rc != SQLITE_OK) return -1;const char *sql_admin ="CREATE TABLE IF NOT EXISTS admins (""id INTEGER PRIMARY KEY AUTOINCREMENT,""name TEXT NOT NULL UNIQUE,""password TEXT NOT NULL);";const char *sql_emp ="CREATE TABLE IF NOT EXISTS employees (""id INTEGER PRIMARY KEY AUTOINCREMENT,""name TEXT NOT NULL UNIQUE,""password TEXT NOT NULL,""age INTEGER,""gender INTEGER,""salary REAL,""department INTEGER,""title INTEGER,""role INTEGER);";char *err;sqlite3_exec(db, sql_admin, 0, 0, &err);sqlite3_exec(db, sql_emp, 0, 0, &err);// 初始化默认管理员(admin/12345)sqlite3_stmt *stmt;sqlite3_prepare_v2(db, "SELECT COUNT(*) FROM admins;", -1, &stmt, NULL);if (sqlite3_step(stmt) == SQLITE_ROW && sqlite3_column_int(stmt, 0) == 0) {sqlite3_exec(db, "INSERT INTO admins (name, password) VALUES ('admin', '12345');", NULL, NULL, NULL);}sqlite3_finalize(stmt);return 0;
}// 管理员登录验证
int verify_admin_credentials(const char *name, const char *password) {printf("[DEBUG] 验证管理员账户:输入用户名=%s,密码=%s\n", name, password);sqlite3_stmt *stmt;sqlite3_prepare_v2(db, "SELECT id, password FROM admins WHERE name = ?;", -1, &stmt, NULL);sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC);int id = -1;if (sqlite3_step(stmt) == SQLITE_ROW && strcmp((const char *)sqlite3_column_text(stmt, 1), password) == 0) {id = sqlite3_column_int(stmt, 0);}sqlite3_finalize(stmt);return id;
}// 员工登录验证
int verify_employee_credentials(const char *name, const char *password) {sqlite3_stmt *stmt;sqlite3_prepare_v2(db, "SELECT id, password FROM employees WHERE name = ?;", -1, &stmt, NULL);sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC);int id = -1;if (sqlite3_step(stmt) == SQLITE_ROW && strcmp((const char *)sqlite3_column_text(stmt, 1), password) == 0) {id = sqlite3_column_int(stmt, 0);}sqlite3_finalize(stmt);return id;
}// 构建统一格式的响应 JSON 对象
cJSON *build_response(const char *status, const char *message) {cJSON *resp = cJSON_CreateObject();cJSON_AddStringToObject(resp, "status", status);cJSON_AddStringToObject(resp, "message", message);return resp;
}// ===============================================================
// 处理客户端发来的 JSON 请求,根据 type 字段分发功能
// 参数:
//   - request:客户端发送的 cJSON 对象
//   - client_fd:该客户端的 socket 文件描述符
//   - is_logged_in:函数指针,检查用户名是否在线
//   - add_session:函数指针,添加用户到在线会话表
// 返回:一个响应 cJSON 对象,调用者负责释放
// ===============================================================
cJSON *handle_client_request(cJSON *request,int client_fd,int (*is_logged_in)(const char *username),void (*add_session)(int id, Role role, const char *name, int sockfd)
) {// 1. 提取请求类型字段 type(必须存在且为字符串)cJSON *type_item = cJSON_GetObjectItem(request, "type");if (!type_item || !cJSON_IsString(type_item)) {return build_response("fail", "请求缺少 type 字段");}const char *type = type_item->valuestring;// =======================// 登录处理(管理员和员工)// =======================// 管理员登录请求if (strcmp(type, "admin_login") == 0) {// 提取用户名和密码const char *name = cJSON_GetObjectItem(request, "name")->valuestring;const char *pwd  = cJSON_GetObjectItem(request, "password")->valuestring;// 检查是否已登录if (is_logged_in(name))return build_response("fail", "该管理员已在线");// 验证账号密码int id = verify_admin_credentials(name, pwd);if (id > 0) {// 登录成功,添加到会话表add_session(id, ROLE_ADMIN, name, client_fd);writeLogThread(name, "管理员登录"); // 异步写日志cJSON *resp = build_response("success", "管理员登录成功");cJSON_AddNumberToObject(resp, "admin_id", id); // 返回管理员IDreturn resp;} else {return build_response("fail", "用户名或密码错误");}}// 员工登录请求if (strcmp(type, "employee_login") == 0) {const char *name = cJSON_GetObjectItem(request, "name")->valuestring;const char *pwd  = cJSON_GetObjectItem(request, "password")->valuestring;if (is_logged_in(name))return build_response("fail", "该员工已在线");int id = verify_employee_credentials(name, pwd);if (id > 0) {add_session(id, ROLE_EMPLOYEE, name, client_fd);writeLogThread(name, "员工登录");cJSON *resp = build_response("success", "员工登录成功");cJSON_AddNumberToObject(resp, "employee_id", id); // 返回员工IDreturn resp;} else {return build_response("fail", "用户名或密码错误");}}// =======================// 管理员功能请求类型分发// =======================if (strcmp(type, "check_employee_exist") == 0)return handle_check_employee_exist(request);if (strcmp(type, "check_employee_id_exist") == 0)return handle_check_employee_id_exist(request);if (strcmp(type, "add_employee") == 0)return handle_add_employee(request);if (strcmp(type, "delete_employee") == 0)return handle_delete_employee(request);if (strcmp(type, "modify_employee") == 0)return handle_modify_employee(request);if (strcmp(type, "view_employees") == 0)return handle_view_all_employees(request);if (strcmp(type, "sort_by_salary") == 0)return handle_sort_by_salary(request);if (strcmp(type, "sort_by_age") == 0)return handle_sort_by_age(request);if (strcmp(type, "view_logs") == 0)return handle_view_logs(request);if (strcmp(type, "department_gender_count") == 0)return handle_department_gender_count(request);if (strcmp(type, "view_specific_person_logs") == 0)return handle_view_specific_person_logs(request);// =======================// 员工功能请求类型分发// =======================if (strcmp(type, "view_department_employees") == 0)return handle_view_department_employees(request);if (strcmp(type, "view_personal_logs") == 0)return handle_view_personal_logs(request);// =======================// 公共功能(管理员 + 员工)// =======================if (strcmp(type, "change_password") == 0)return handle_change_password(request);if (strcmp(type, "logout") == 0)return handle_logout(request);  // =======================// 未知类型处理// =======================return build_response("fail", "未知操作类型");
}//==============================管理员功能实现函数===============================
// 检查员工是否存在(通过名字)
// 请求示例:{ "type": "check_employee_exist", "name": "张三" }
cJSON *handle_check_employee_exist(cJSON *request) {cJSON *name_item = cJSON_GetObjectItem(request, "name");if (!name_item || !cJSON_IsString(name_item)) {return build_response("fail", "缺少或非法的 name 字段");}const char *name = name_item->valuestring;int exists = find_employee_id_by_name(name);if (exists > 0) {return build_response("success", "员工已存在");} else if (exists == 0) {return build_response("fail", "员工不存在");} else {return build_response("fail", "数据库错误");}
}
// 检查员工是否存在(通过ID)
// 请求示例:{ "type": "check_employee_id_exist", "id": 101 }
cJSON *handle_check_employee_id_exist(cJSON *request) {cJSON *id_item = cJSON_GetObjectItem(request, "id");if (!id_item || !cJSON_IsNumber(id_item)) {return build_response("fail", "缺少或非法的 id 字段");}int employee_id = id_item->valueint;char name_buffer[50];int found = find_employee_name_by_id(employee_id, name_buffer, sizeof(name_buffer));if (found == 1) {return build_response("success", "员工存在");} else if (found == 0) {return build_response("fail", "员工ID不存在");} else {return build_response("fail", "数据库错误");}
}
// 添加员工(管理员)
// 请求示例:包含 name, password, age, gender, salary, department, title, role
cJSON *handle_add_employee(cJSON *request) {const char *required_fields[] = {"name", "password", "age", "gender", "salary", "department", "title", "role"};for (int i = 0; i < 8; i++) {if (!cJSON_GetObjectItem(request, required_fields[i])) {return build_response("fail", "缺少必要字段");}}const char *name = cJSON_GetObjectItem(request, "name")->valuestring;const char *password = cJSON_GetObjectItem(request, "password")->valuestring;int age = cJSON_GetObjectItem(request, "age")->valueint;int gender = cJSON_GetObjectItem(request, "gender")->valueint;float salary = (float)cJSON_GetObjectItem(request, "salary")->valuedouble;int department = cJSON_GetObjectItem(request, "department")->valueint;int title = cJSON_GetObjectItem(request, "title")->valueint;int role = cJSON_GetObjectItem(request, "role")->valueint;if (find_employee_id_by_name(name) > 0) {return build_response("fail", "员工已存在");}const char *sql = "INSERT INTO employees ""(name, password, age, gender, salary, department, title, role) ""VALUES (?, ?, ?, ?, ?, ?, ?, ?);";sqlite3_stmt *stmt;if (sqlite3_prepare_v2(db, sql, -1, &stmt, 0) != SQLITE_OK) {return build_response("fail", "数据库预处理失败");}sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC);sqlite3_bind_text(stmt, 2, password, -1, SQLITE_STATIC); sqlite3_bind_int(stmt, 3, age);sqlite3_bind_int(stmt, 4, gender);sqlite3_bind_double(stmt, 5, salary);sqlite3_bind_int(stmt, 6, department);sqlite3_bind_int(stmt, 7, title);sqlite3_bind_int(stmt, 8, role);if (sqlite3_step(stmt) != SQLITE_DONE) {sqlite3_finalize(stmt);return build_response("fail", "添加员工失败");}sqlite3_finalize(stmt);writeLogThread(name, "被管理员添加");return build_response("success", "员工添加成功");
}// 删除员工(管理员)
// 请求示例:{ "type": "delete_employee", "id": 102 }
cJSON *handle_delete_employee(cJSON *request) {int employee_id = cJSON_GetObjectItem(request, "id")->valueint;char name_buffer[50];int found = find_employee_name_by_id(employee_id, name_buffer, sizeof(name_buffer));if (found <= 0) {return build_response("fail", "员工不存在");}const char *sql = "DELETE FROM employees WHERE id = ?;";sqlite3_stmt *stmt;if (sqlite3_prepare_v2(db, sql, -1, &stmt, 0) != SQLITE_OK) {return build_response("fail", "数据库准备失败");}sqlite3_bind_int(stmt, 1, employee_id);if (sqlite3_step(stmt) != SQLITE_DONE) {sqlite3_finalize(stmt);return build_response("fail", "删除失败");}sqlite3_finalize(stmt);writeLogThread(name_buffer, "被管理员删除");return build_response("success", "删除成功");
}
// 修改员工信息(管理员)
// 请求示例:{ "type": "modify_employee", "id": 101, "field": 2, "value": 25 }
cJSON *handle_modify_employee(cJSON *request) {int id = cJSON_GetObjectItem(request, "id")->valueint;int field = cJSON_GetObjectItem(request, "field")->valueint;cJSON *value_item = cJSON_GetObjectItem(request, "value");if (!id || !field || !value_item) {return build_response("fail", "字段不全");}const char *field_name = NULL;int is_string = 0;switch (field) {case 1: field_name = "name"; is_string = 1; break;case 2: field_name = "age"; break;case 3: field_name = "gender"; break;case 4: field_name = "salary"; break;case 5: field_name = "department"; break;case 6: field_name = "title"; break;case 7: field_name = "password"; is_string = 1; break;default: return build_response("fail", "无效字段编号");}char sql[128];snprintf(sql, sizeof(sql), "UPDATE employees SET %s = ? WHERE id = ?;", field_name);sqlite3_stmt *stmt;if (sqlite3_prepare_v2(db, sql, -1, &stmt, 0) != SQLITE_OK)return build_response("fail", "SQL 预处理失败");if (is_string)sqlite3_bind_text(stmt, 1, value_item->valuestring, -1, SQLITE_STATIC);else if (field == 4)sqlite3_bind_double(stmt, 1, value_item->valuedouble);elsesqlite3_bind_int(stmt, 1, value_item->valueint);sqlite3_bind_int(stmt, 2, id);if (sqlite3_step(stmt) != SQLITE_DONE) {sqlite3_finalize(stmt);return build_response("fail", "修改失败");}sqlite3_finalize(stmt);writeLogThread("管理员", "修改了员工信息");return build_response("success", "修改成功");
}
// 分页查看所有员工信息
// 请求示例:{ "type": "view_employees", "page": 1 }
cJSON *handle_view_all_employees(cJSON *request) {int page = 1;cJSON *page_item = cJSON_GetObjectItem(request, "page");if (page_item && cJSON_IsNumber(page_item)) {page = page_item->valueint;if (page < 1) page = 1;}const int page_size = 3; // 每页显示5条int offset = (page - 1) * page_size;const char *sql = "SELECT id, name, age, gender, salary, department, title ""FROM employees ORDER BY id LIMIT ? OFFSET ?;";sqlite3_stmt *stmt;if (sqlite3_prepare_v2(db, sql, -1, &stmt, 0) != SQLITE_OK) {return build_response("fail", "数据库查询失败");}sqlite3_bind_int(stmt, 1, page_size);sqlite3_bind_int(stmt, 2, offset);cJSON *data_array = cJSON_CreateArray();while (sqlite3_step(stmt) == SQLITE_ROW) {cJSON *emp = cJSON_CreateObject();cJSON_AddNumberToObject(emp, "id", sqlite3_column_int(stmt, 0));cJSON_AddStringToObject(emp, "name", (const char *)sqlite3_column_text(stmt, 1));cJSON_AddNumberToObject(emp, "age", sqlite3_column_int(stmt, 2));cJSON_AddNumberToObject(emp, "gender", sqlite3_column_int(stmt, 3));cJSON_AddNumberToObject(emp, "salary", sqlite3_column_double(stmt, 4));cJSON_AddNumberToObject(emp, "department", sqlite3_column_int(stmt, 5));cJSON_AddNumberToObject(emp, "title", sqlite3_column_int(stmt, 6));cJSON_AddItemToArray(data_array, emp);}sqlite3_finalize(stmt);cJSON *resp = build_response("success", "分页员工数据如下");cJSON_AddItemToObject(resp, "data", data_array);return resp;
}
// 按工资降序显示员工
// 请求示例:{ "type": "sort_by_salary" }
cJSON *handle_sort_by_salary(cJSON *request) {const char *sql = "SELECT id, name, salary FROM employees ORDER BY salary DESC;";sqlite3_stmt *stmt;if (sqlite3_prepare_v2(db, sql, -1, &stmt, 0) != SQLITE_OK)return build_response("fail", "数据库查询失败");cJSON *array = cJSON_CreateArray();while (sqlite3_step(stmt) == SQLITE_ROW) {cJSON *emp = cJSON_CreateObject();cJSON_AddNumberToObject(emp, "id", sqlite3_column_int(stmt, 0));cJSON_AddStringToObject(emp, "name", (const char *)sqlite3_column_text(stmt, 1));cJSON_AddNumberToObject(emp, "salary", sqlite3_column_double(stmt, 2));cJSON_AddItemToArray(array, emp);}sqlite3_finalize(stmt);cJSON *resp = build_response("success", "工资排序完成");cJSON_AddItemToObject(resp, "data", array);return resp;
}
// 按年龄升序显示员工
// 请求示例:{ "type": "sort_by_age" }
cJSON *handle_sort_by_age(cJSON *request) {const char *sql = "SELECT id, name, age FROM employees ORDER BY age ASC;";sqlite3_stmt *stmt;if (sqlite3_prepare_v2(db, sql, -1, &stmt, 0) != SQLITE_OK)return build_response("fail", "数据库查询失败");cJSON *array = cJSON_CreateArray();while (sqlite3_step(stmt) == SQLITE_ROW) {cJSON *emp = cJSON_CreateObject();cJSON_AddNumberToObject(emp, "id", sqlite3_column_int(stmt, 0));cJSON_AddStringToObject(emp, (const char *)"name", (const char *)sqlite3_column_text(stmt, 1));cJSON_AddNumberToObject(emp, "age", sqlite3_column_int(stmt, 2));cJSON_AddItemToArray(array, emp);}sqlite3_finalize(stmt);cJSON *resp = build_response("success", "年龄排序完成");cJSON_AddItemToObject(resp, "data", array);return resp;
}
// 查看系统操作日志(来自 log.txt 文件)
// 请求示例:{ "type": "view_logs" }
cJSON *handle_view_logs(cJSON *request) {FILE *fp = fopen("log.txt", "r");if (!fp) {return build_response("fail", "无法打开日志文件");}char line[512];cJSON *log_array = cJSON_CreateArray();while (fgets(line, sizeof(line), fp)) {cJSON_AddItemToArray(log_array, cJSON_CreateString(line));}fclose(fp);cJSON *resp = build_response("success", "日志内容如下");cJSON_AddItemToObject(resp, "log", log_array);return resp;
}
// 查看各部门的男/女员工数量
// 请求示例:{ "type": "department_gender_count" }
cJSON *handle_department_gender_count(cJSON *request) {const char *sql ="SELECT department, gender, COUNT(*) ""FROM employees GROUP BY department, gender ORDER BY department, gender;";sqlite3_stmt *stmt;if (sqlite3_prepare_v2(db, sql, -1, &stmt, 0) != SQLITE_OK)return build_response("fail", "数据库查询失败");int male[3] = {0}, female[3] = {0};while (sqlite3_step(stmt) == SQLITE_ROW) {int dept = sqlite3_column_int(stmt, 0);int gender = sqlite3_column_int(stmt, 1);int count = sqlite3_column_int(stmt, 2);if (dept >= 0 && dept <= 2) {if (gender == 0) male[dept] = count;else if (gender == 1) female[dept] = count;}}sqlite3_finalize(stmt);cJSON *array = cJSON_CreateArray();for (int i = 0; i < 3; i++) {cJSON *item = cJSON_CreateObject();cJSON_AddNumberToObject(item, "department", i);cJSON_AddNumberToObject(item, "male", male[i]);cJSON_AddNumberToObject(item, "female", female[i]);cJSON_AddItemToArray(array, item);}cJSON *resp = build_response("success", "统计完成");cJSON_AddItemToObject(resp, "data", array);return resp;
}
//===============================处理员工功能实现函数=======================================
// 查看当前员工所在部门的其他员工
// 请求示例:{ "type": "view_department_employees", "id": <员工ID> }
cJSON *handle_view_department_employees(cJSON *request) {// 提取员工 IDint emp_id = cJSON_GetObjectItem(request, "id")->valueint;// 1. 查询该员工所在部门const char *sql_dept = "SELECT department FROM employees WHERE id = ?;";sqlite3_stmt *stmt;if (sqlite3_prepare_v2(db, sql_dept, -1, &stmt, 0) != SQLITE_OK) {return build_response("fail", "查询员工部门失败");}sqlite3_bind_int(stmt, 1, emp_id);int dept = -1;if (sqlite3_step(stmt) == SQLITE_ROW) {dept = sqlite3_column_int(stmt, 0); // 获取部门编号}sqlite3_finalize(stmt);if (dept == -1) {return build_response("fail", "员工不存在或未设置部门");}// 2. 查询该部门的其他员工(不包含自己)const char *sql = "SELECT name, gender, age, salary, title ""FROM employees WHERE department = ? AND id != ?;";if (sqlite3_prepare_v2(db, sql, -1, &stmt, 0) != SQLITE_OK) {return build_response("fail", "查询部门成员失败");}sqlite3_bind_int(stmt, 1, dept);sqlite3_bind_int(stmt, 2, emp_id);cJSON *array = cJSON_CreateArray();while (sqlite3_step(stmt) == SQLITE_ROW) {cJSON *emp = cJSON_CreateObject();cJSON_AddStringToObject(emp, "name", (const char *)sqlite3_column_text(stmt, 0));cJSON_AddNumberToObject(emp, "gender", sqlite3_column_int(stmt, 1));cJSON_AddNumberToObject(emp, "age", sqlite3_column_int(stmt, 2));cJSON_AddNumberToObject(emp, "salary", sqlite3_column_double(stmt, 3));cJSON_AddNumberToObject(emp, "title", sqlite3_column_int(stmt, 4));cJSON_AddItemToArray(array, emp);}sqlite3_finalize(stmt);cJSON *resp = build_response("success", "本部门员工信息如下");cJSON_AddItemToObject(resp, "data", array);return resp;
}// 查看当前员工的操作日志(从 log.txt 中筛选含用户名的行)
// 请求示例:{ "type": "view_personal_logs", "id": <员工ID> }
cJSON *handle_view_personal_logs(cJSON *request) {int emp_id = cJSON_GetObjectItem(request, "id")->valueint;// 通过 ID 获取员工姓名char name_buffer[50];if (find_employee_name_by_id(emp_id, name_buffer, sizeof(name_buffer)) != 1) {return build_response("fail", "员工不存在");}// 打开日志文件FILE *fp = fopen("log.txt", "r");if (!fp) {return build_response("fail", "无法打开日志文件");}char line[512];cJSON *log_array = cJSON_CreateArray();// 逐行读取并查找包含该员工姓名的日志while (fgets(line, sizeof(line), fp)) {if (strstr(line, name_buffer)) {cJSON_AddItemToArray(log_array, cJSON_CreateString(line));}}fclose(fp);cJSON *resp = build_response("success", "个人日志如下");cJSON_AddItemToObject(resp, "log", log_array);return resp;
}
// 修改密码(适用于管理员或员工)
// 请求示例:{ "type": "change_password", "id": 100, "role": 0/1, "new_password": "xxx" }
cJSON *handle_change_password(cJSON *request) {int id = cJSON_GetObjectItem(request, "id")->valueint;int role = cJSON_GetObjectItem(request, "role")->valueint;const char *new_pwd = cJSON_GetObjectItem(request, "new_password")->valuestring;const char *sql = NULL;// 根据角色选择修改表if (role == 0) {sql = "UPDATE admins SET password = ? WHERE id = ?;";} else if (role == 1) {sql = "UPDATE employees SET password = ? WHERE id = ?;";} else {return build_response("fail", "角色无效");}// 预编译 SQLsqlite3_stmt *stmt;if (sqlite3_prepare_v2(db, sql, -1, &stmt, 0) != SQLITE_OK) {return build_response("fail", "SQL准备失败");}sqlite3_bind_text(stmt, 1, new_pwd, -1, SQLITE_STATIC);sqlite3_bind_int(stmt, 2, id);if (sqlite3_step(stmt) != SQLITE_DONE) {sqlite3_finalize(stmt);return build_response("fail", "密码修改失败");}sqlite3_finalize(stmt);writeLogThread(role == 0 ? "管理员" : "员工", "修改密码");return build_response("success", "密码修改成功");
}
// 请求示例:{ "type": "view_specific_person_logs", "id": <员工ID> }
cJSON *handle_view_specific_person_logs(cJSON *request) {int emp_id = cJSON_GetObjectItem(request, "id")->valueint;// 通过 ID 获取员工姓名char name_buffer[50];if (find_employee_name_by_id(emp_id, name_buffer, sizeof(name_buffer)) != 1) {return build_response("fail", "员工不存在");}// 打开日志文件FILE *fp = fopen("log.txt", "r");if (!fp) {return build_response("fail", "无法打开日志文件");}char line[512];cJSON *log_array = cJSON_CreateArray();// 逐行读取并查找包含该员工姓名的日志while (fgets(line, sizeof(line), fp)) {if (strstr(line, name_buffer)) {cJSON_AddItemToArray(log_array, cJSON_CreateString(line));}}fclose(fp);cJSON *resp = build_response("success", "指定人日志如下");cJSON_AddItemToObject(resp, "log", log_array);return resp;
}// 用户主动退出登录,移除在线状态
cJSON *handle_logout(cJSON *request) {cJSON *name_item = cJSON_GetObjectItem(request, "name");if (!name_item || !cJSON_IsString(name_item)) {return build_response("fail", "缺少用户名");}const char *name = name_item->valuestring;// 调用 server.c 中的在线表移除函数remove_session_by_name(name);return build_response("success", "已退出登录");
}
//通过名字查找id
int find_employee_id_by_name(const char *name) {assert(name);char *sql = "SELECT id FROM employees WHERE name = ?;";sqlite3_stmt *stmt;int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);if (rc != SQLITE_OK) {fprintf(stderr, "SQL错误: %s\n", sqlite3_errmsg(db));return -1;}sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC);rc = sqlite3_step(stmt);if (rc == SQLITE_ROW) {int id = sqlite3_column_int(stmt, 0);sqlite3_finalize(stmt);return id;} else if (rc == SQLITE_DONE) {sqlite3_finalize(stmt);return 0; // 未找到} else {fprintf(stderr, "SQL错误: %s\n", sqlite3_errmsg(db));sqlite3_finalize(stmt);return -1; // 错误}
}//通过id返回名字,以及是否找到
int find_employee_name_by_id(int employee_id, char *name_buffer, size_t buffer_size) {if (name_buffer == NULL || buffer_size == 0) return -1;const char *sql = "SELECT name FROM employees WHERE id = ?;";sqlite3_stmt *stmt;int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);if (rc != SQLITE_OK) {fprintf(stderr, "SQL错误: %s\n", sqlite3_errmsg(db));return -1;}sqlite3_bind_int(stmt, 1, employee_id);rc = sqlite3_step(stmt);if (rc == SQLITE_ROW) {const char *name = (const char *)sqlite3_column_text(stmt, 0);strncpy(name_buffer, name, buffer_size - 1);name_buffer[buffer_size - 1] = '\0';sqlite3_finalize(stmt);return 1;} else if (rc == SQLITE_DONE) {sqlite3_finalize(stmt);return 0; // 未找到} else {fprintf(stderr, "SQL错误: %s\n", sqlite3_errmsg(db));sqlite3_finalize(stmt);return -1;}
}
#ifndef REGULAR_H
#define REGULAR_H#include <stdbool.h>
// 正则表达式验证
bool validate_regex(const char *arr, const char *pattern);// 验证姓名是否合法
bool validate_name(const char *name);
// 验证密码是否合法
bool validate_password(const char *password);
// 验证年龄是否合法
bool validate_age(int age);
// 验证性别是否合法
bool validate_gender(int gender);
// 验证工资是否合法
bool validate_salary(float salary);
// 验证部门是否合法
bool validate_department(int department);
// 验证职位是否合法
bool validate_title(int title);
// 验证角色是否合法
bool validate_role(int role);
// 验证员工ID:大于0
bool validate_employee_id(int id);
// 员工菜单项验证:0-3
bool validate_employee_choice(int x);
// 验证主菜单选项(1-3)
bool validate_main_choice(int x);
// 管理员菜单项验证:0-8
bool validate_admin_choice(int x);
// 验证修改选项:0-退出,1-姓名,2-年龄,3-性别,4-工资,5-部门,6-职位,7-密码
bool validate_modify_choice(int x);// 读取带格式验证的字符串
char *read_string(const char *prompt, bool (*validator)(const char *));
// 读取带范围验证的整数
int read_int(const char *prompt, bool (*validator)(int));
// 读取带范围验证的浮点数
float read_float(const char *prompt, bool (*validator)(float));#endif
/***************************************************
# File Name:    regular.c
# Author:       Super ze
# Mail:         2592972473@qq.com
# Created Time: Thu 12 Jun 2025 11:17:42 AM CST
****************************************************/#include "regular.h"
#include <regex.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>// 正则表达式验证函数
bool validate_regex(const char* arr, const char* pattern)
{regex_t regex;int reti;char error_msg[100];// 编译正则表达式reti = regcomp(&regex, pattern, REG_EXTENDED);if (reti) {regerror(reti, &regex, error_msg, sizeof(error_msg));fprintf(stderr, "正则表达式编译失败: %s\n", error_msg);return false;}// 执行匹配reti = regexec(&regex, arr, 0, NULL, 0);// 释放正则表达式资源regfree(&regex);if (reti == 0) {return true;  // 匹配成功} else if (reti == REG_NOMATCH) {return false; // 匹配失败} else {regerror(reti, &regex, error_msg, sizeof(error_msg));fprintf(stderr, "正则表达式执行错误: %s\n", error_msg);return false;}
}// 验证姓名:允许字母、数字,长度2-20
bool validate_name(const char* name)
{return validate_regex(name, "^[a-zA-Z0-9]{2,20}$");
}// 验证密码:字母、数字、下划线,长度4-20
bool validate_password(const char* password)
{return validate_regex(password, "^[a-zA-Z0-9_]{2,20}$");
}// 验证年龄:18-100
bool validate_age(int age)
{return age >= 18 && age <= 100;
}// 验证性别:0-男,1-女
bool validate_gender(int gender)
{return gender == 0 || gender == 1;
}// 验证工资:正数,最多两位小数
bool validate_salary(float salary)
{return salary > 0;
}// 验证部门:0-销售,1-市场,2-售后
bool validate_department(int department)
{return department >= 0 && department <= 2;
}// 验证职位:0-经理,1-主管,2-员工
bool validate_title(int title)
{return title >= 0 && title <= 2;
}// 验证角色:0-管理员,1-员工
bool validate_role(int role)
{return role >= 0 && role <= 1;
}// 验证员工ID:大于0
bool validate_employee_id(int id)
{return id > 0;
}// 验证主菜单选项(1-3)
bool validate_main_choice(int x) {return x >= 1 && x <= 3;
}// 管理员菜单项验证:0-8
bool validate_admin_choice(int x) {return x >= 0 && x <= 9;
}// 员工菜单项验证:0-3
bool validate_employee_choice(int x) {return x >= 0 && x <= 3;
}// 验证修改选项:0-退出,1-姓名,2-年龄,3-性别,4-工资,5-部门,6-职位,7-密码
bool validate_modify_choice(int x) 
{return x >= 0 && x <= 7;
}// 读取带范围验证的整数
int read_int(const char *prompt, bool (*validator)(int)) // prompt为提示信息,validator为验证函数指针
{assert(prompt && validator);int value;char buffer[100];//读取并验证整数输入,知道输入合法为止while (1){printf("%s", prompt);if (fgets(buffer, sizeof(buffer), stdin) == NULL)// 从标准输入读取字符串到buffer{fprintf(stderr, "读取失败\n");exit(EXIT_FAILURE);}if (sscanf(buffer, "%d", &value) != 1){printf("输入无效,请输入有效数字。\n");continue;}// 检查缓冲区中是否有多余字符(如换行符外的其他字符)char *end = buffer;while (*end != '\0' && *end != '\n'){end++;}// 如果找到非空白字符且不在行末,说明输入包含多余内容if (*end != '\n'){printf("输入无效,请输入单独的整数。\n");// 清理输入缓冲区while (fgetc(stdin) != '\n');continue;} if (validator(value)){return value;}else{printf("输入超出范围,请重新输入\n");}}
}// 读取带范围验证的浮点数
float read_float(const char *prompt, bool (*validator)(float))
{assert(prompt && validator); // 确保提示和验证函数不为NULL float value;char buffer[100];//读取并验证浮点数输入,知道输入合法为止while (1){printf("%s", prompt);if (fgets(buffer, sizeof(buffer), stdin) == NULL){fprintf(stderr, "读取失败\n");exit(EXIT_FAILURE);}if (sscanf(buffer, "%f", &value) != 1){printf("请输入有效的数字\n");continue;}// 检查缓冲区中是否有多余字符(如换行符外的其他字符)char *end = buffer;while (*end != '\0' && *end != '\n'){end++;}// 如果找到非空白字符且不在行末,说明输入包含多余内容if (*end != '\n'){printf("输入无效,请输入单独的整数。\n");// 清理输入缓冲区while (fgetc(stdin) != '\n');continue;} if (validator(value)){return value;}else{printf("输入超出范围,请重新输入\n");}}
}// 安全读取字符串,避免缓冲区溢出
// 动态读取字符串(并通过验证器),返回 malloc 分配的内存
char* read_string(const char* prompt, bool (*validator)(const char*)) {char buffer[100];assert(prompt && validator);while (1) {printf("%s", prompt);if (fgets(buffer, sizeof(buffer), stdin) == NULL) {fprintf(stderr, "读取失败\n");exit(EXIT_FAILURE);}// 去除换行符size_t len = strlen(buffer);if (len > 0 && buffer[len - 1] == '\n') {buffer[len - 1] = '\0';}// 检查是否超长(缓冲区未完全读完)if (len == sizeof(buffer) - 1 && buffer[len - 1] != '\n') {printf("输入过长,请重新输入(最多99字符)\n");while (getchar() != '\n'); // 清空输入缓冲区continue;}if (validator(buffer)) {char* result = malloc(strlen(buffer) + 1);if (!result) {perror("malloc失败");exit(EXIT_FAILURE);}strcpy(result, buffer); // 拷贝输入内容return result;} else {printf("输入格式不合法,请重试。\n");}}
}
http://www.dtcms.com/a/275361.html

相关文章:

  • 【MogDB】一种基于ctid分片并发查询以提升大表查询性能的方式
  • RBAC权限模型深度解析:从理论到企业级实践
  • 贪心算法题解——跳跃游戏 II【LeetCode】
  • The Black Heart
  • 飞算 JavaAI 智能编程助手:颠覆编程旧模式,重构新生态
  • 【极客日常】后端任务动态注入执行策略的一种技术实现
  • 27.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--单体转微服务--币种服务(一)
  • .net机器学习框架:ML.NET数据处理
  • 【嵌入式硬件实例】-555定时器实现倍压电路
  • 钉钉小程序开发环境配置与前端开发指南
  • 计算机毕业设计ssm基于SSM的萌宠交流平台 基于SSM的毛孩子互动分享与领养系统 SSM架构下的宠物社交·商城·救助一体化平台
  • Mac M芯片安装RocketMQ服务
  • 【Docker基础】Dockerfile指令速览:基础常用指令详解
  • 【STM32实践篇】:最小系统组成
  • 实战指南:用pmap+gdb排查Linux进程内存问题
  • 9.4 自定义SMC服务开发
  • springboot使用@Validated不起作用
  • 加法器学习
  • AI图像修复工具CodeFormer实测:马赛克去除与画质增强效果评测
  • Java使用Langchai4j接入AI大模型的简单使用(四)--整合Springboot
  • Window/linux获得程序运行目录C++
  • C++ Map 和 Set 详解:从原理到实战应用
  • 基于 Python 的数据分析技术综述
  • 2025 全球酒店用品厂家竞争力排行榜发布:扬州卓韵领衔,布草工厂实力重塑行业格局
  • 未来软件开发的新方向:从工程到智能的深度演进
  • 利用scale实现图片放大案例
  • 商业机密保卫战:如何让离职员工带不走的客户资源?
  • TCP的socket编程
  • 【unity实战】在Unity实现低耦合可复用的交互系统
  • 科技驯服烈日狂沙:中东沙漠农场的光储革命