【员工管理系统】

news2024/9/30 9:26:07

员工管理系统

  • 前言
  • 需求分析
  • 系统设计
    • 系统框图
    • 所需技术
  • 系统实现
    • 编写代码
    • 测试

前言

这是一个使用epoll实现TCP并发服务器,并让客户端登录服务器可以进行员工的管理,员工的信息存储在sqlite数据库中,对数据库进行增删改查实现对员工的添加,删除,修改,查询等功能;

需求分析

1)服务器负责管理所有员工表单(以数据库形式),其他客户端可通过网络连接服务器来查询员工表单。
2)需要账号密码登陆,其中需要区分管理员账号还是普通用户账号。
3)管理员账号可以查看、修改、添加、删除员工信息,同时具有查询历史记录功能,管理员要负责管理所有的普通用户。
4)普通用户只能查询修改与本人有关的相关信息,其他员工信息不得查看修改。
5)服务器能同时相应多台客户端的请求功能。实现并发

系统设计

系统框图

server端:
在这里插入图片描述
client端:

在这里插入图片描述

所需技术

一、信息存储:
使用sqlite数据库对员工信息的存储,其中包括管理员信息和普通员工信息;同时也要对历史记录进行存储;
二、TCP通信:
使用TCP服务器,实现服务器和客户端之间的接发数据,处理客户端发来的请求,实现对员工的管理;
三、并发服务器:
并发服务器的实现方法很多,可以使用多进程多线程,也可以使用IO多路复用,这里我使用了epoll的方法来实现并发服务器,可以同时处理多个客户端发来的请求;

系统实现

编写代码

一、函数和结构体的封装

#ifndef __COMMON_H__
#define __COMMON_H__
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <sqlite3.h>
#include <time.h>
//定义大小
#define NAMELEN 20
#define DATALEN 50
#define MSGLEN 500
#define MAX_EVENTS 10
//定义IP地址和端口号
#define IP "192.168.250.100"
#define PORT 8888
// 定义消息类型
#define LOGIN 1
#define ADD 2
#define DELETE 3
#define MODIFY 4
#define SEARCH 5
#define HISTORY 6
//用户类型
#define ADMIN 0
#define USER 1
//定义员工信息结构体
typedef struct staff_info
{
    int id;              // 员工编号
    int usertype;        // ADMIN 0    USER 1
    char name[NAMELEN];  // 姓名
    char passwd[8];      // 密码
    int age;             // 年龄
    char phone[NAMELEN]; // 电话
    char addr[DATALEN];  // 地址
    char work[DATALEN];  // 职位
    char date[DATALEN];  // 入职年月
    int level;           // 等级
    double salary;       // 工资
} staff_info_t;
/*定义双方通信的结构体信息*/
typedef struct
{
    int msgtype;           // 请求的消息类型
    char recvmsg[MSGLEN]; // 通信的消息
    int flags;             // 标志位
    staff_info_t info;     // 员工信息
} MSG;

//服务器用到的函数
int create_socket(const char *address, int port);
void init_sql(sqlite3 *db);
int handle_client(int clientfd, sqlite3 *db);
void getdata(char *date);   //获取时间
int do_login(int clientfd, MSG *msg, sqlite3 *db);
int do_add(int clientfd, MSG *msg, sqlite3 *db);
int do_delete(int clientfd, MSG *msg, sqlite3 *db);
int do_change(int clientfd, MSG *msg, sqlite3 *db);
int do_search(int clientfd, MSG *msg, sqlite3 *db);
int do_history(int clientfd, MSG *msg, sqlite3 *db);
//客户端用到的函数
int create_socket(const char *address, int port);
int login(int socket, MSG *msg, int flag);
int add(int sockfd, MSG *msg);
int delete(int sockfd, MSG *msg);
int change(int sockfd, MSG *msg);
int search(int sockfd, MSG *msg);
int history(int sockfd, MSG *msg);

#endif

二、epoll并发服务器模型

    epfd = epoll_create(1);
    if (epfd == -1)
    {
        perror("epoll_create1() error");
        exit(1);
    }

    // 将服务器套接字加入epoll实例的监听列表
    event.data.fd = sockfd;
    event.events = EPOLLIN;
    if (epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event) == -1)
    {
        perror("epoll_ctl() error");
        exit(1);
    }
    while (1)
    {
        epct = epoll_wait(epfd, events, MAX_EVENTS, -1); // 等待事件到来,阻塞模式
        if (epct == -1)
        {
            perror("epoll_wait() error");
            exit(1);
        }
        // 处理准备就绪的套接字
        for (i = 0; i < epct; i++)
        {
            if (events[i].data.fd == sockfd)
            {
                // 新的客户端连接请求
                clientfd = accept(sockfd, (struct sockaddr *)&cin, &cin_len);
                if (clientfd < 0)
                {
                    perror("accept() error");
                    exit(1);
                }
                printf("[%s:%d]连接到服务器..\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));
                // 将客户端套接字加入epoll实例的监听列表
                event.data.fd = clientfd;
                event.events = EPOLLIN;
                if (epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &event) == -1)
                {
                    perror("epoll_ctl() error");
                    exit(1);
                }
            }
            else
            {
                // 客户端数据可读
                handle_client(events[i].data.fd, db);
            }
        }
    }

三、初始化数据库

void init_sql(sqlite3 *db)
{

    printf("正在初始化...\n");
    // 创建表
    char sql[256] = "";
    char *errmsg = NULL;
    strcpy(sql, "create table if not exists usr (id int,name char PRIMARY KEY,passwd char,age int,phone char,addr char,work char,data char,level int,salary double);");
    if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
    {
        printf("sqlite3_exec error:%s\n", errmsg);
        return;
    }
    strcpy(sql, "create table if not exists log (name char,operations char,time char);");
    if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
    {
        printf("sqlite3_exec error:%s\n", errmsg);
        return;
    }
    // 判断管理员的信息是否在数据库中
    char **result = NULL;
    int rows = -1;
    int columns = 0;
    strcpy(sql, "select * from usr where name='admin'");
    if (sqlite3_get_table(db, sql, &result, &rows, &columns, &errmsg) != SQLITE_OK)
    {
        printf("sqlite3_get_table error:%s line=%d\n", errmsg, __LINE__);
        return;
    }
    if (rows == 0)
    {
        // 插入管理员信息
        sprintf(sql, "insert into usr values('1000','admin','admin','20','19156058040','上海','嵌入式','2022.09','4','20000');");
    }
    sqlite3_free_table(result);
}

这里直接初始化了一个管理员账户信息,管理员信息后续无法修改,有且只有一个管理员,后续对员工的管理只可以通过此管理员进行管理。

四、员工登录界面无法使用管理员账号登录
在客户端记录了登录身份信息,当处于普通员工登录界面时,无法使用管理员账户登录,服务器端会对客户端发来的数据进行判断,如果处于员工登录界面时,且用户信息是管理员,则发送登录失败。

int do_login(int clientfd, MSG *msg, sqlite3 *db)
{
    char data[128];
    int row;
    int cloumn;
    char sql[128];
    char *errmsg = NULL;
    char **result;

    // 匹配用户信息是否与密码表中相同
    sprintf(sql, "select * from usr where name = '%s' and passwd = '%s';", msg->info.name, msg->info.passwd);
    if (sqlite3_get_table(db, sql, &result, &row, &cloumn, &errmsg) != SQLITE_OK)
    {
        printf("sqlite3_get_table error:%s line=%d\n", errmsg, __LINE__);
        return -1;
    }
    // 密码表中存在改用户
    if (row == 1)
    {
        strcpy(msg->recvmsg, "登录成功!!");
        msg->flags = 1; // 代表操作成功
        // 插入记录(用户登陆成功)
        getdata(data);
        sprintf(sql, "insert into log values('%s', 'login', '%s')", msg->info.name, data);
        if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
        {
            printf("sqlite3_exec error:%s line=%d\n", errmsg, __LINE__);
            return -1;
        }
    }

    // 密码表中不存在改用户或者用户登录管理员账号
    if (row == 0 || (msg->info.usertype == 1 && strcmp(msg->info.name, "admin") == 0))
    {
        printf("登录失败\n");
        strcpy(msg->recvmsg, "登录失败!!");
        msg->flags = 0; // 代表操作失败
    }
    // 返回用户登录信息
    if (send(clientfd, msg, sizeof(MSG), 0) < 0)
    {
        perror("send err");
    }
    return 0;
}

五、查找用户信息(添加,修改,删除 功能类似)
查找分为普通员工查找和管理员查找,管理员查找又可以根据姓名查找和查找全部,由于普通员工查找信息也是根据自己的用户名查找,所以这里只需要分两种情况编写代码,一种是根据用户名,一种是查找全部,这里是通过判断客户端传来的flag来进行判断,如果是员工查找或者是管理员通过用户名查找,flag=1,查找全部flag=0。
在使用sqlite3_get_table时,循环向客户端发送信息,一行一行的发送,客户端循环接收,当循环发送结束时,向客户端发送结束标志。
客户端

int search(int sockfd, MSG *msg)
{
    int n;
    msg->msgtype = SEARCH;
    if (msg->info.usertype == ADMIN)
    {
        system("clear");
        printf("======================可查找选项==========================\n");
        printf("------------------------菜单-----------------------------\n");
        printf("\t\t\t1.根据用户名查找\n");
        printf("\t\t\t2.查找全部\n");
        printf("-----------------------------------------------------------\n");
        printf("请输入选项:\n");
        scanf("%d", &n);
        getchar();
        if (n == 1)
        {
            msg->flags = 1;
            printf("请输入要查找的用户名:\n");
            scanf("%s", msg->info.name);
            getchar();
            if (send(sockfd, msg, sizeof(MSG), 0) < 0)
            {
                printf("send err\n");
                return -1;
            }
            printf("id\t\tname\t\tpasswd\t\tage\tphone\t\taddr\twork\tdate\t\tlevel\tsalary\t\t\n");
            // 接收成功与否
            while (1)
            {
                if (recv(sockfd, msg, sizeof(MSG), 0) < 0)
                {
                    printf("recv err\n");
                    return -1;
                }
                printf("%s\n", msg->recvmsg);
                if (0 == strcmp(msg->recvmsg, "query end"))
                {
                    break;
                }
            }
            // 本次操作如果失败直接返回
            if (msg->flags == 0)
            {
                printf("查找失败%s\n2秒刷新页面\n", msg->recvmsg);
                sleep(2);
                return -1;
            }
            // 操作成功打印查找之后的信息
            printf("按任意键退出查询界面:\n");
            getchar();
            return 0;
        }
        else if (n == 2)
        {
            msg->flags = 0;
            if (send(sockfd, msg, sizeof(MSG), 0) < 0)
            {
                printf("send err\n");
                return -1;
            }
            // 接收成功与否
            printf("id\t\tname\t\tpasswd\t\tage\tphone\t\taddr\twork\tdate\t\tlevel\tsalary\t\t\n");
            while (1)
            {
                if (recv(sockfd, msg, sizeof(MSG), 0) < 0)
                {
                    printf("recv err\n");
                    return -1;
                }
                printf("%s\n", msg->recvmsg);
                if (0 == strcmp(msg->recvmsg, "query end"))
                {
                    break;
                }
            }
            // 本次操作如果失败直接返回
            if (msg->flags == 0)
            {
                printf("查找失败%s\n2秒刷新页面\n", msg->recvmsg);
                sleep(2);
                return -1;
            }
            // 操作成功打印查找之后的信息
            printf("按任意键退出查询界面:\n");
            getchar();
            return 0;
        }
        return 0;
    }
    else
    {
        // 员工查询自己
        msg->flags = 1;
        strcpy(msg->info.name, name);
        if (send(sockfd, msg, sizeof(MSG), 0) < 0)
        {
            printf("send err\n");
            return -1;
        }
        printf("id\t\tname\t\tpasswd\t\tage\tphone\t\taddr\twork\tdate\t\tlevel\tsalary\t\t\n");
        // 接收成功与否
        while (1)
        {
            if (recv(sockfd, msg, sizeof(MSG), 0) < 0)
            {
                printf("recv err\n");
                return -1;
            }
            printf("%s\n", msg->recvmsg);
            if (0 == strcmp(msg->recvmsg, "query end"))
            {
                break;
            }
        }
        // 本次操作如果失败直接返回
        if (msg->flags == 0)
        {
            printf("查找失败%s\n2秒刷新页面\n", msg->recvmsg);
            sleep(2);
            return -1;
        }
        printf("按任意键退出查询界面:\n");
        getchar();
        return 0;
    }
}

服务器

int do_search(int clientfd, MSG *msg, sqlite3 *db)
{
    char sql[128];
    char *errmsg;
    char **result;
    int row;
    int cloum;
    int i, j;
    if (msg->flags == 1) // 根据姓名查找
    {
        sprintf(sql, "select *from usr where name = '%s' ", msg->info.name);
    }
    else if (msg->flags == 0) // 查找全部
    {
        sprintf(sql, "select *from usr ");
    }
    if (sqlite3_get_table(db, sql, &result, &row, &cloum, &errmsg) != SQLITE_OK)
    {
        printf("%s\n", errmsg);
        msg->flags = 0; // 失败标志
    }
    else
    {
        msg->flags = 1; // 成功标志
    }
    for (int i = 1; i <= row; i++)
    {
        sprintf(msg->recvmsg, "%-8s\t%-8s\t%-8s\t%-5s\t%-15s\t%-8s\t%-8s\t%-10s\t%-5s\t%-10s\n", result[i * cloum], result[i * cloum + 1], result[i * cloum + 2], result[i * cloum + 3], result[i * cloum + 4], result[i * cloum + 5], result[i * cloum + 6], result[i * cloum + 7], result[i * cloum + 8], result[i * cloum + 9]);
        if (send(clientfd, msg, sizeof(MSG), 0) < 0)
        {
            perror("send err");
        }
    }
    strcpy(msg->recvmsg, "query end");
    if (send(clientfd, msg, sizeof(MSG), 0) < 0)
    {
        perror("send err");
    }
    sqlite3_free_table(result);
    return 0;
}

六、查询历史记录功能
管理员可以查询历史记录,在执行登录、添加员工、删除员工、修改员工信息时,都会将记录插入到记录表中,管理员可以查询记录表中的内容。查询记录和查询员工信息相似。

int do_history(int clientfd, MSG *msg, sqlite3 *db)
{
    char sql[128];
    char *errmsg;
    char **result;
    int row;
    int cloum;
    int i, j;
    sprintf(sql, "select *from log ");
    if (sqlite3_get_table(db, sql, &result, &row, &cloum, &errmsg) != SQLITE_OK)
    {
        printf("%s\n", errmsg);
        msg->flags = 0; // 失败标志
    }
    else
    {
        msg->flags = 1; // 成功标志
    }
    for (int i = 1; i <= row; i++)
    {
        sprintf(msg->recvmsg, "%-8s\t%-8s\t%-8s\t\n", result[i * cloum], result[i * cloum + 1], result[i * cloum + 2]);
        if (send(clientfd, msg, sizeof(MSG), 0) < 0)
        {
            perror("send err");
        }
    }
    strcpy(msg->recvmsg, "query end");
    if (send(clientfd, msg, sizeof(MSG), 0) < 0)
    {
        perror("send err");
    }
    sqlite3_free_table(result);
    return 0;
}

注:完整代码见:员工管理系统

测试

对所有功能进行测试,是否可以实现多个客户端同时登录,是否可以添加、删除、修改、查看员工信息,以及管理员查看历史记录等功能:

员工管理系统

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/375062.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一文理解服务端渲染SSR的原理,附实战基于vite和webpack打造React和Vue的SSR开发环境

SSR和CSR 首先&#xff0c;我们先要了解什么是SSR和CSR&#xff0c;SSR是服务端渲染&#xff0c;CSR是客户端渲染&#xff0c;服务端渲染是指 HTTP 服务器直接根据用户的请求&#xff0c;获取数据&#xff0c;生成完整的 HTML 页面返回给客户端&#xff08;浏览器&#xff09;展…

嵌入式 STM32 通讯协议--MODBUS

目录 一、自定义通信协议 1、协议介绍 2、网络协议 3、自定义的通信协议 二、MODBUS通信协议 1、概述 2、MODBUS帧结构 协议描述 3、MODBUS数据模型 4、MODBUS事务处理的定义 5、MODBUS功能码 6、功能码定义 7、MODBUS数据链路层 8、MODBUS地址规则 9、MO…

SpringBoot 2.x ——使用 mail 实现邮件发送

文章目录前言环境、版本等pom依赖引入springboot项目配置文件获取邮箱授权码配置properties文件定义接口信息接收类编写邮件发送服务类编写接口swagger测试1、简单邮件发送2、html格式发送(支持附件)前言 最近再看xxl-job的源码&#xff0c;其中在邮件告警通知中使用到了告警信…

Go调用dll 解决方案 dll查看工具

准备工作 Go需要1.10版本&#xff0c;即支持动态链接库 基本调用代码 lib : syscall.NewLazyDLL("lib/plugin.dll") // 读取dll f : lib.NewProc("Sum") // 调用dll函数 res, _, _ : f.Call(param) // 传值 fmt.Println(res)可能出现的问题 %1 is not a …

移动硬盘不显示怎么办?恢复硬盘的方法汇总

在日常工作和生活中&#xff0c;移动硬盘是非常重要的存储设备&#xff0c;它们可以储存大量的数据&#xff0c;比如照片、音乐、视频、文档等。但是&#xff0c;有时候你可能会遇到移动硬盘不显示的问题。这个问题通常会让人感到困惑&#xff0c;因为你无法访问移动硬盘里的数…

Appium自动化测试之启动时跳过初始化设置

Appium每次启动时都会检查和安装Appium Settings&#xff0c;这是完全没有必要的&#xff0c;在首次使用Appium连接设备是Appium Settings便已经安装好。怎样跳过安装Appium Settings呢&#xff1f;之前的做法是修改appium中的源文件中的android-helpers.js实现&#xff0c;如M…

足球俱乐部管理系统

技术&#xff1a;Java、JSP等摘要&#xff1a;网站是一种主要的渠道。人们通过互联网快速、准确的发布信息、获取信息。而足球俱乐部是足球职业化、专业化的一个标志&#xff0c;是足球运动员以足球谋生时&#xff0c;所被聘用的机构&#xff0c;应运时代发展&#xff0c;规模、…

观测云产品更新|新增用户访问监测自动化追踪;新增 CDN 质量分析;新增自定义查看器导航菜单等

观测云更新 用户访问监测优化 新增用户访问监测自动化追踪 用户访问监测新增自动化追踪&#xff0c;通过“浏览器插件”的实现方式&#xff0c;使用浏览器记录用户访问行为&#xff0c;创建无代码的端到端测试。更多详情可参考文档【 自动化追踪 】https://docs.guance.com/…

本机安装docker,redis并进行连接实战

1、背景 win10系统&#xff0c;想要在本机搭建一套开发环境&#xff0c;需要安装zk&#xff0c;redis等组件&#xff0c;一个个的安装显然效率太低且复杂&#xff0c;这里考虑安装docker及相关镜像 2、 docker安装 docker官网下载:https://docs.docker.com/desktop/install/…

Git的简述

Git 文章目录GitGit概述版本控制工具集中式管理控制工具分步式管理控制工具控制机制Git和代码托管中心安装Git软件Git常用命令Git概述 Git是一个免费的、开源的分步式版本控制系统&#xff0c;可以快速的处理从小型到大型的各种项目 Git 易于学习&#xff0c;占地面积小&…

【Linux | ELK 8.2】搭建ELKB集群Ⅱ—— 安装 Logstash 和 Kibana

目录2.3 安装Logstash&#xff08;1&#xff09;检查系统jdk版本&#xff08;2&#xff09;下载logstash&#xff08;3&#xff09;安装logstash&#xff08;4&#xff09;配置logstash&#xff08;5&#xff09;启动与测试方法1方法2&#xff08;主要的使用方式&#xff09;&a…

Python--深入浅出的装饰器--1

本章一起深入浅出一下装饰器。前面我们讲过一章装饰器了。不知道各位看懂了多少。每太看懂也没关系&#xff0c;本章就一起实操一下。简单的例子例1例2上述的两个例子&#xff0c;执行结果为&#xff1a;1423.为什么呢&#xff1f;&#xff1f;&#xff1f;解析语法糖&#xff…

sed 功能详解

介绍sedsed是一种流编辑器&#xff0c;它一次处理一行内容&#xff0c;把当前处理的行存储在临时缓冲区中&#xff08;buffer&#xff09;,称为"模式空间"&#xff0c;接着sed命令处理缓冲区中的内容&#xff0c;处理完成后&#xff0c;把缓冲区的内容送往屏幕&#…

面向Elasticsearch的高性能应用网关INFINI Gateway的介绍

1.微服务的API网关介绍网关的含义很多&#xff0c;应用范围也很广&#xff0c;不同的领域理解也不一样&#xff0c;站在分布式领域基于微服务的架构风格中&#xff0c;API网关其实就是一个微服务系统的统一入口。往往微服务是指由多个应用组成的一个个独立的服务系统&#xff0…

【C/C++】类型限定符extern、const、Volatile、register

1、extern&#xff1a; 声明一个变量&#xff0c;extern声明的变量没有建立存储空间。 extern int a ; //变量在定义的时候创建存储空间。 ①当我们在编译器中试图运行以下代码&#xff0c;系统会报错。 错误原因是“无法解析外部符号_a”.系统认为变量a是没有开辟内存空间的…

【3】MyBatis+Spring+SpringMVC+SSM整合一套通关

三、SpringMVC 1、SpringMVC简介 1.1、什么是MVC MVC是一种软件架构的思想&#xff0c;将软件按照模型、视图、控制器来划分 M&#xff1a;Model&#xff0c;模型层&#xff0c;指工程中的JavaBean&#xff0c;作用是处理数据 JavaBean分为两类&#xff1a; 一类称为实体…

网络应用之静态Web服务器-多任务版

静态Web服务器-多任务版学习目标能够写出多线程版的多任务web服务器程序1. 静态Web服务器的问题目前的Web服务器&#xff0c;不能支持多用户同时访问&#xff0c;只能一个一个的处理客户端的请求&#xff0c;那么如何开发多任务版的web服务器同时处理 多个客户端的请求?可以使…

ROS进行深度相机的标定

前言 自己使用标定板对深度相机进行标定。 参考&#xff1a;http://wiki.ros.org/camera_calibration/Tutorials/MonocularCalibration 一、准备标定板 在下面的网站中可下载棋盘格标定板&#xff0c;可用A4纸打印下来。 http://wiki.ros.org/camera_calibration/Tutorials/…

Spring Boot整合Kaptcha实现验证码功能

目录一、前言1.Kaptcha 简介2.Kaptcha 详细配置表二、实现1.整合kaptcha&#xff0c;创建kaptcha的工具类1.1 添加依赖1.2 创建KaptchaConfig工具类2 编写接口&#xff0c;在接口中使用 kaptcha 工具类来生成验证码图片&#xff08;验证码信息&#xff09;并返回3 登录时从sess…

阻塞队列BlockingQueue

一、概念阻塞队列&#xff1a;从名字可以看出&#xff0c;他也是队列的一种&#xff0c;那么他肯定是一个先进先出&#xff08;FIFO&#xff09;的数据结构。与普通队列不同的是&#xff0c;它支持两个附加操作&#xff0c;即阻塞添加和阻塞删除方法。如上图&#xff0c;线程1往…