C语言三级项目(考勤管理系统)

本文介绍了一个使用C语言开发的考勤管理系统,涵盖了项目概述、需求分析、系统设计、问题与解决方案以及展望与心得。系统采用三层架构,包括用户界面层、业务逻辑层和数据存储层,通过命令行界面实现员工和管理员的考勤管理功能,如打卡、请假、员工信息管理等。开发工具为Visual Studio 2022,强调了代码的可读性、系统稳定性和可扩展性。
摘要由CSDN通过智能技术生成

考勤管理系统

1. 项目概述

1.1 项目名称

考勤管理系统

1.2 项目背景

随着社会的发展和企业规模的扩大,对于员工考勤的管理变得愈加重要。考勤管理系统旨在帮助企业轻松记录和管理员工的出勤情况,提高工作效率。

1.3 开发工具

Visual Studio 2022

2. 需求分析

2.1 功能需求
  • 员工

    1. 注册账号
    2. 打卡
    3. 请假
    4. 签退
    5. (自己)考勤记录的打印
  • 管理员

    1. 员工的增添
    2. 打印员工信息
    3. 删除某一员工
    4. 修改员工信息
    5. 请假的批准,驳回,结束假期等
  • 其他功能需求

    1. 文件的存入,存出(实现存储信息的持久性)
    2. 账户系统(实现功能的模块化设计)
2.2 非功能需求
  • 容错性高

    考虑到用户的错误输入,有有效应对措施

  • 易用性需求:

    用户界面友好: 设计直观、易用的用户界面,以提高用户体验。

  • 系统稳定性

    合理开辟使用释放空间,防止内存泄漏,调用野指针,解引用空指针等情况存在

  • 可扩展性需求

    考虑未来可能的需求变化,确保系统能够方便地扩展和修改。

  • 代码可读性

    系统中变量均采用易懂的英文单词或缩写,并有适量注释,方便阅读

    编写清晰,功能模块化,易于理解

3. 系统设计

3.1 系统架构

功能一览

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.1.1 模块划分

考勤管理系统采用简单的三层架构,包括用户界面层、业务逻辑层和数据存储层。

  • 节点展示

    typedef struct StuffTime
    {
         
    	time_t checkInTime;//存储打卡开始时间
    	time_t checkOutTime;//存储打卡结束时间
    	int checkIn;//存储有效打卡数
    	int checkOut;//存储有效签退数
    }STime;
    typedef struct PersonData
    {
         
    	char account[50];//账号
    	char password[20];//密码
    	char user[20];//用户名
    	STime time;//存储时间
    	int leave;//员工的假期状态(假期中,申请请假中,请假被驳回)
    	int judge;//判断是员工还是管理员
    }PData;
    typedef PData InfoDate;
    
    typedef struct PersonInfo//节点
    {
         
    	InfoDate data;//信息
    	struct PersonInfo* next;//下一个的地址
    }PInfo;
    

    其中typedef PData InfoDate;后再将数据存入节点原因有下:

    1.让节点结构看着清晰简洁,存储数据和下一个节点的地址

    2.一开始调试时我是typedef int InfoDate;这样干的,便于调试,调试完成后再将改回结构体

  • 用户界面层: 负责与用户进行交互,提供命令行界面,包括签到、请假申请等功能。

    交互界面

    void Staff(PInfo** head)
    {
         
    	system("cls");
    	loadStaff(*head);//载入数据
    	int temp = 1;
    	char option;
    	do
    	{
         
    		system("cls");
    		printf("**************************\n");
    		printf("*      请输入选项        *\n");
    		printf("**************************\n");
    		printf("*                        *\n");
    		printf("*         1.创建账号     *\n");
    		printf("*         2.登录账号     *\n");
    		printf("*       0.返回选择界面   *\n");
    		printf("*                        *\n");
    		printf("**************************\n");
    
    		option = getch();
    		system("cls");
    		switch (option)
    		{
         
    		case '1':
    		{
         
    			PushBack(*head, 0);
    			break;
    		}
    		case '2':
    		{
         
    			PInfo* current = StuffAccount(*head);
    			system("cls");
    			if (current != NULL)
    			{
         
    				int tem = 1;
    				do
    				{
         
    					system("cls");
    					MenuStuff();
    					char op = getch();
    					switch (op)
    					{
         
    					case '1':
    					{
         
    						system("cls");
    						printf("打卡\n");
    						CheckIntime(head, current);
    						system("pause");
    						break;
    					}
    					case '2':
    					{
         
    						system("cls");
    						printf("请假\n");
    						RequestLeave(head, current);
    						system("pause");
    						break;
    					}
    					case '3':
    					{
         
    						system("cls");
    						printf("签退\n");
    						CheckOutTime(head, current);
    						system("pause");
    						break;
    					}
    					case '4':
    					{
         
    						system("cls");
    						printf("考勤记录");
    						Attdend(head, current);
    						system("pause");
    						break;
    					}
    					case '0':
    					{
         
    						system("cls");
    						printf("退出登录");
    						tem = 0;
    						break;
    					}
    					default:
    					{
         
    						system("cls");
    						printf("输入错误请重新输入:\n");
    						system("pause");
    					}
    					}
    				} while (tem);
    			}
    			else
    			{
         
    				printf("登陆失败");
    			}
    			break;
    		}
    		case '0':
    		{
         
    			printf("程序退出\n");
    			temp = 0;
    			break;
    		}
    		default:
    			printf("输入错误,请重新输入\n");
    			break;
    		}
    	} while (temp);
    
    
    	SaveStaff(*head);
    }
    
    1. 整体采用do while循环和switch语句,实现程序的重复运行
    2. 使用非标准函数getch();方便用户交互

    注册界面

    void PushBack(PInfo* head, int judge)
    {
         
    	loadStaff(head);
    	PInfo* tem = (PInfo*)malloc(sizeof(PInfo));
    	if (!tem)
    	{
         
    		printf("内存分配失败\n");
    		return;
    	}
    	InfoDate PersonData;
    	printf("请输入账户:\n");
    	scanf("%s", PersonData.account);
    	printf("请输入用户名:\n");
    	scanf("%s", PersonData.user);
    
    	PInfo* current = head;
    	while (current != NULL)
    	{
         
    		if (strcmp(current->data.account, PersonData.account) == 0)
    		{
         
    			printf("账户名已存在\n");
    			system("pause");
    			return;
    		}
    		if (strcmp(current->data.user, PersonData.user) == 0)
    		{
         
    			printf("用户名已存在\n");
    			system("pause");
    			return;
    		}
    		current = current->next;
    	}//遍历链表,防止存在相同用户名或者账户名
    
    	printf("请输入密码:\n");
    	scanf("%s", PersonData.password);
    	tem->next = NULL;
    	tem->data.leave = 0;
    	tem->data.time.checkIn = 0;
    	tem->data.time.checkOut = 0;
    	tem->data.judge = judge;
    	strcpy(tem->data.account, PersonData.account);
    	strcpy(tem->data.password, PersonData.password);
    	strcpy(tem->data.user, PersonData.user);//初始化
    
    	current = head;
    	while (current->next != NULL)
    	{
         
    		current = current->next;
    	}
    	current->next = tem;//将新数据存在最后一个节点的末尾,实现尾增
    	SaveStaff(head);//保存数据
    	printf("注册成功");
    	system("pause");
    }
    
    1. 开始开辟空间失败,直接返回,不会访问无效地址
    2. 创建用户时,利用循环有效防止相同用户名,账户名存在
    3. 注册界面公用,其中的参数judge如果是1,就是注册管理员,如果是0,就是注册员工

    登录界面

    PInfo* StuffAccount(PInfo* head)
    {
         
    	char c;
    	int op = 1;
    	while (op)
    	{
         
    		system("cls");
    		printf("请输入你的员工账号:\n");
    		char Account[20];
    		scanf("%s", Account);
    		printf("请输入密码:\n");
    		char Password[20];
    		scanf("%s", Password);
    		PInfo* current = head;
    		while (current->next != NULL)
    		{
         
    			current = current->next;
    			if (strcmp(current->data.account, Account) == 0)
    				if (strcmp(current->data.password, Password) == 0)
    					if (current->data.judge == 0)//判断是否为员工
    						return current;//遍历,判断节点中是否存在账号密码都存在的数据,如果存在,返回这个节点
    
    		}
    		int a = 1;
    		printf("账号或密码存在错误\n");
    		while (a)//如果没找到,可以进入循环,方便退出或者再次寻找
    		{
         
    			printf("**************************\n");
    			printf("*        请选择          *\n");
    			printf("**************************\n");
    			printf("*                        *\n");
    			printf("*       1.重新输入       *\n");
    			printf("*       0.退出程序       *\n");
    			printf("*                        *\n");
    			printf("**************************\n");
    
    			c = getch();
    			switch (c)
    			{
         
    			case '1':
    			{
         
    				a = 0;
    				break;
    			}
    			case '0':
    			{
         
    				op = 0;
    				a = 0;
    				break;
    			}
    			default:
    			{
         
    				system("cls");
    				printf("输入错误,请重新输入\n");
    				break;
    			}
    			}
    		}
    
    	}
    	return NULL;//没找到返回空指针
    }
    
    1. 用户可以选择返回,防止陷入死循环
    2. 直接返回节点,登录成功后方便后续使用

    打卡签退界面

    void CheckIntime(PInfo* head, PInfo* current)//打卡
    {
         
    	loadStaff(head);
    	PInfo* cur = current;//存储当前已经登陆的节点
    	PInfo* tem = head;//存储头节点,用于最后的信息存储
    
    	cur->data.time.checkInTime = time(NULL);//存储当前时间
    	struct tm checkInTime = *localtime(&cur->data.time.checkInTime);
    
    	// 显示打卡时间
    	char timeStr[100];
    	strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", &checkInTime);//将当前时间转换成字符格式
    	printf("%s的打卡时间是: %s\n", cur->data.user, timeStr);
    
    	// 判断是否在8:00之前打卡
    	if (checkInTime.tm_hour < 8)
    	{
         
    		printf("打卡成功!\n");
    		cur->data.time.checkIn++;//如果打卡成功,打卡成功数加1
    	}
    	else {
         
    		printf("打卡失败,未在8:00前打卡。\n");
    	}
    	SaveStaff(head);
    }
    
    void CheckOutTime(PInfo* head, PInfo* current)//签退
    {
         
    	loadStaff(head);
    	PInfo* cur = current;
    	PInfo* tem = head;
    
    	cur->data.time.checkOutTime = time(NULL);
    	struct tm checkInTime = *localtime(&cur->data.time.checkOutTime);
    
    	// 显示签退时间
    	char timeStr[100];
    	strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", &checkInTime);
    	printf("%s的签退时间是: %s\n", cur->data.user, timeStr);
    
    	// 判断是否在8:00之前签退
    	if (checkInTime.tm_hour > 8)
    	{
         
    		printf("签退成功!\n");
    		cur->data.time.checkOut++;
    	}
    	else {
         
    		printf("打卡签退,未在8:00前签退。\n");
    	}
    	SaveStaff(head);
    }
    
    1. 打卡与当前系统时间联系起来,实现功能上的运用
    2. 签退功能与打卡功能类似

    考勤记录的打印(自己)

    void Attdend(PInfo* head, PInfo* cur)//cur是登录的节点
    {
         
    	printf("%s的有效打卡数为%d\n", cur->data.user, cur->data.time.checkIn);
    	printf("%s的有效签退数为%d\n", cur->data.user, cur->data.time.checkOut);
    }
    

    考勤记录的打印

    请假界面

    void RequestLeave(PInfo* head, PInfo* current)
    {
         
    	loadStaff(head);
    	PInfo* cur = current;
    	if (cur->data.leave == 0)//data.leave存储的数值是当前登录者的假期状态
    	{
         
    		printf("%s申请请假\n", cur->data.account);
    		cur->data.leave = 1;
    	}
    	else if (cur->data.leave == 1)
    	{
         
    		printf("请假进行中,请耐心等待\n");
    	}
    	else if (cur->data.leave == 2)//如果没有批准请假,自动将其状态变为未请假状态
    	{
         
    		printf("您的请假没有被批准,如需请假,请重新申请\n");
    		cur->data.leave == 0;
    	}
    	else
    		printf("申请成功,正在休假");
    	SaveStaff(head);
    }
    

    和管理员功能搭配起来,管理员的批准请假负责修改其状态

  • 管理界面层:

    管理员交互界面

    void Admin(PInfo** head)
    {
         
    	loadStaff(*head);
    	int temp = 1;
    	char option;
    	do
    	{
         
    		system("cls");
    		printf("**************************\n");
    		printf("*      请输入选项        *\n");
    		printf("**************************\n");
    		printf("*                        *\n");
    		printf("*         1.注册账号     *\n");
    		printf("*         2.登录账号     *\n");
    		printf("*       0.返回选择界面   *\n");
    		printf("*                        *\n");
    		printf("**************************\n");
    
    		option = getch();
    		switch (option)
    		{
         
    		case '1':
    		{
         
    			PushBack(*head, 1);
    			break;
    		}
    		case '2':
    		{
         
    			if (AdminAccount(*head))
    			{
         
    
    				int tem = 1;
    				char op;
    				do
    				{
         
    					system("cls");
    					MenuAdmin();
    					op = getch();
    					switch (op)
    					{
         
    					case '1':
    					{
         
    						system("cls");
    						printf("增加员工\n");
    						PushBack(*head, 0);
    						break;
    					}
    					case '2':
    					{
         
    						system("cls");
    						printf("查看员工信息\n");
    						PrintData(*head);
    						system("pause");
    						break;
    					}case '3':
    					{
         
    						system("cls");
    						PopMid(*head);
    						system("pause");
    						break;
    					}case '4':
    					{
         
    						system("cls");
    						printf("修改员工信息\n");
    						UpdateStaffInfo(*head);
    						system("pause");
    						break;
    					}case '5':
    					{
         
    						system("cls");
    						printf("请假批准\n");
    						approveLeaveRequest(*head);
    						system("pause");
    						break;
    					}case '0':
    					{
         
    						system("cls");
    						printf("退出登录\n");
    						tem = 0;
    						break;
    					}
    					default:
    					{
         
    						system("cls");
    						printf("输入错误请重新输入:\n");
    						system("pause");
    						break;
    					}
    					}
    
    				} while (tem);
    			}
    			break;
    		}
    		case '0':
    		{
         
    			printf("程序退出\n");
    			temp = 0;
    			break;
    		}
    		default:
    			printf("输入错误,请重新输入\n");
  • 56
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值