考勤系统实训报告

一、考勤系统需求分析

1.背景

随着信息技术的高速发展,各行各业都充分利用信息平台提高自己的管理效率。当今国内各企业已纷纷建立企业局域网,依托局域网使用各种信息管理系统,使管理效率显著提高。

2.系统目标

该系统将实现五大功能(登陆功能、注册功能、打卡功能、查询功能、统计功能),将使考勤管理更加系统化、信息化、高效化、准确化,让管理更上一层台阶;同时满足快速有效的打卡需求,提高员工的出勤率以及积极性。

3.功能需求

各大功能模块

  • 登陆功能模块
  • 注册功能模块
  • 打卡功能模块
  • 查询功能模块
  • 统计功能模块

功能模块图

功能模块图

4.非功能需求

非功能性需求是指除功能性需求以外的所有需求,一般分为部署环境需求,接口需求,安全需求,性能需求,界面需求。

(1)部署环境需求:部署环境一般是指客户所在公司或者部门的IT环境,电脑系统环境,与该软件相关的构件。

(2)接口需求:数据通信协议,比如TCP/IP、UDP协议等。

(3)安全需求:该系统对安全性需求不高,能保证数据不丢失则行。

(4)性能需求:至少可以允许同一个时间20个用户访问系统。查询服务用户通过电脑提交命令道返回结果不超过2秒钟。具有较高的稳定性

(5)界面需求:界面设计应该简洁易懂,该部分需求应该不断优化,直至符合用户习惯。

5.软件与硬件

系统拓扑图

网络拓扑图.jpg

6.设计和实现的限制

对于一个现代化的考勤来说,考勤管理是必须的。如何把每天发生的考勤信息如实地记录下来,保证考勤工作有条理地进行,同时确保相关数据的安全,信息处理的高效,并且保证系统的实用性强。详细设计阶段的根本目标是确定应该怎样具体的实现所要求的系统,也就是说,经过这个阶段的设计工作,应该得出目标系统的精确描述,从而在编码阶段可以把这个描述直接翻译成用某总程序设计语言书写的程序。详细设计的目标不仅仅是逻辑上正确地实现每个模块的功能,更重要的是设计的处理过程应该尽可能简明易懂。

  1. 设计限制
  • 系统体系结构的搭建
  • 开发软件,包括:ASP、IIS、SQL Server、Dreamweaver
  • 数据库的设计,包括:概念结构设计和逻辑结构设计
  • 软硬件平台搭建
  1. 实现限制
  • 登陆功能模块
  • 注册功能模块
  • 打卡功能模块
  • 查询功能模块
  • 统计功能模块

二、考勤系统数据库设计

1.标识实体

  • 打卡机
  • 员工

2.E-R图

ER图

3.数据表

数据表

4.数据库表

  • 一个数据库:kaoqin
  • 三个表,分别是card_machine(打卡机表)、worker(员工表)、statistic(打卡信息表)
create DATABASE kaoqin
use kaoqin
create table card_machine(
number varchar(15) primary key,
location varchar(10),
machine_type varchar(10),
methods varchar(10)
)
use kaoqin
create table worker(
number varchar(15) primary key,
gender varchar(10),
age varchar(5),
department varchar(15)
)
use kaoqin
create table statistic(
number varchar(15) primary key,
machine_number varchar(15),
worker_number varchar(15),
shijian varchar(10),
cishu varchar(10),
foreign key(machine_number) references card_machine(number),
foreign key(worker_number) references worker(number)
)

三、考勤系统界面设计

1.考勤系统功能实现——登录

(1)登录界面设计

登录界面

(2)登录界面实现代码

  • 点击登录按钮实现登录功能
// 点击“登录”按钮则登录系统
        private void bt_Login_Click(object sender, EventArgs e)
        {

            String connStr = ConfigurationManager.ConnectionStrings["Attendance"].ConnectionString;
            SqlConnection sqlConn = new SqlConnection(connStr);

            try
            {
                // 连接数据库
                sqlConn.Open();

                // 构造命令发送给数据库
                String sqlStr = "select * from employee where ID=@id and PASSWORD=@pwd";
                SqlCommand cmd = new SqlCommand(sqlStr, sqlConn);

                // 注意是用用户ID登录,而不是用户名,用户名可能会重复
                cmd.Parameters.Add(new SqlParameter("@id", this.tb_User.Text.Trim()));
                cmd.Parameters.Add(new SqlParameter("@pwd", this.tb_Password.Text.Trim()));

                SqlDataReader dr = cmd.ExecuteReader();

                // 如果从数据库中查询到记录,则表示可以登录
                if (dr.HasRows)
                {
                    dr.Read();
                    WorkerInfo.userPwd = dr["PASSWORD"].ToString(); 
                    WorkerInfo.userId = dr["ID"].ToString();
                    WorkerInfo.userDepartment = dr["department"].ToString();
                    WorkerInfo.userGender = dr["gender"].ToString();
                    WorkerInfo.userAuthority = dr["authority"].ToString();
                    WorkerInfo.userName = dr["name"].ToString()
                    MessageBox.Show(WorkerInfo.userAuthority + "登录成功");

                    if (WorkerInfo.userAuthority == "common")
                    {
                        // 显示普通员工员主界面
                        MainFormUser formUser = new MainFormUser();
                        formUser.Show();

                        // 隐藏登录界面
                        this.Hide();
                    }

                    if (WorkerInfo.userAuthority == "manager")
                    {
                        // 显示库管员主界面
                        MainFormAdmin formAdmin = new MainFormAdmin();
                        formAdmin.Show();

                        // 隐藏登录界面
                        this.Hide();
                    }
                }
                else
                {
                    MessageBox.Show("用户名或密码错误", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            catch (Exception exp)
            {
                MessageBox.Show("访问数据库错误:" + exp.Message);
            }
            finally
            {
                sqlConn.Close();
            }
        }

2.考勤系统功能实现——注册

1.员工注册界面设计

注册

2.注册界面实现代码

  • 录入界面加载同时自动给定新员工号
        private void RecordForm2_Load(object sender, EventArgs e)
        {
            String connStr = ConfigurationManager.ConnectionStrings["Attendance"].ConnectionString;

            SqlConnection sqlConn = new SqlConnection(connStr);
            try
            {
                // 连接数据库
                sqlConn.Open();

                // 构造命令发送给数据库
                String sqlStr = "select MAX(id+1) as id from employee";
                SqlCommand cmd = new SqlCommand(sqlStr, sqlConn);


                SqlDataReader dr = cmd.ExecuteReader();
                //取出数据库中的值并赋给文本框
                if (dr.HasRows)
                {
                    dr.Read();
                    this.worker_Id.Text = dr["ID"].ToString();
                }
  
            }
            catch (Exception exp)
            {
                MessageBox.Show("访问数据库错误:" + exp.Message);
            }
            finally
            {
                sqlConn.Close();
            }
        }
  • 点击“录入”按键实现注册功能
        // 点击“确认”按钮,则录入员工
        private void bt_Ok_Click(object sender, EventArgs e)
        {
            String id = this.worker_Id.Text.Trim();
            String name = this.worker_Name.Text.Trim();
            String gender = this.worker_Gender.Text.Trim();
            String department = this.worker_Department.Text.Trim();
            String authority = this.worker_Authority.Text.Trim();
            String password = this.worker_Password.Text.Trim();

            // 更新数据库
            String connStr = ConfigurationManager.ConnectionStrings["Attendance"].ConnectionString;
            SqlConnection sqlConn = new SqlConnection(connStr);
            try
            {                

                // 连接数据库
                sqlConn.Open();

                // 构造命令
                
                String sqlStr = "insert into employee(ID, name, gender, department, authority, PASSWORD) values(@id, @name, @gender, @department, @authority, @password)";
                SqlCommand cmd = new SqlCommand(sqlStr, sqlConn);
                
                // SQL字符串参数赋值
                cmd.Parameters.Add(new SqlParameter("@id", id));
                cmd.Parameters.Add(new SqlParameter("@name", name));
                cmd.Parameters.Add(new SqlParameter("@gender",gender ));
                cmd.Parameters.Add(new SqlParameter("@department", department));
                cmd.Parameters.Add(new SqlParameter("@authority", authority));
                cmd.Parameters.Add(new SqlParameter("@password", password));

                // 将命令发送给数据库
                int res = cmd.ExecuteNonQuery();

                // 根据返回值判断是否插入成功
                if (res != 0)
                {
                    MessageBox.Show("员工信息录入成功");
                }
                else
                {
                    MessageBox.Show("员工信息录入失败");
                }
            }
            catch (Exception exp)
            {
                MessageBox.Show("访问数据库错误:" + exp.Message);
            }
            finally
            {
                sqlConn.Close();
            }
        }

3.考勤系统功能实现——打卡

1.打卡界面设计

打卡界面

2.打卡界面实现代码

  • 打开串口
        private void buttonOpenCOM_Click(object sender, EventArgs e)
        {
            // 串口已打开,此时需要关闭
            if (serialPort1.IsOpen)
            {
                serialPort1.Close();
                this.toolStripStatusLabel1.Text = "已关闭串口" + serialPort1.PortName.ToString();
                buttonOpenCOM.Text = "打开";
                return;
            }
            // 否则打开串口
            else
            {
                serialPort1.PortName = comboBoxCOMList.Text;
                InitSerialPort();

                try
                {
                    serialPort1.Open();
                    this.toolStripStatusLabel1.Text = "已打开串口" + serialPort1.PortName.ToString();
                    buttonOpenCOM.Text = "关闭";
                }
                catch (Exception ex)
                {
                    this.toolStripStatusLabel1.Text = "打开串口失败,原因:" + ex.Message;
                    return;
                }
            }
        }
  • 关闭串口
        private void ServiceForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            // 如果关闭窗口时,串口仍然为打开状态,则需要关闭串口
            if (serialPort1.IsOpen)
            {
                serialPort1.Close();
            }
        }
  • 向卡片写入信息
        private void bt_Register_Click(object sender, EventArgs e)
        {
            if (!serialPort1.IsOpen)
            {
                this.toolStripStatusLabel1.Text = "请先打开串口";
                return;
            }

            this.toolStripStatusLabel1.Text = "未找到有效的卡";

            // '666'填充为'00000666'
            string stuffId = this.tb_EmployeeId.Text.PadLeft(8, '0');
            // '00000666'转变成'66060000'
            stuffId = ISO15693CardHandler.CovertEndian(stuffId);

            // 检查输入数据的错误
            // ISO15693为32位,4字节,8字符
            if (stuffId.Length != 8)
            {
                MessageBox.Show("请输入4字节的16进制数据!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            // 检查16进制字符错误
            if (!ISO15693CardHandler.CheckValidHexBytes(stuffId))
            {
                MessageBox.Show("写入数据的16进制格式错误!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            // [TODO] 寻卡,将RFID卡号读出来
            if (!serialPort1.IsOpen)
            {
                MessageBox.Show("串口未打开!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            string response_card = "";

            /*
             * 每发送完一个指令后,睡眠MILLISECOND_IN_SLEEP毫秒,防止数据无法正常读取
             */
            serialPort1.Write(ISO15693Card.COMMAND_WRITE_REG);
            Thread.Sleep(MILLISECOND_IN_SLEEP);
            if (serialPort1.BytesToRead > 0) response_card = serialPort1.ReadExisting();

            serialPort1.Write(ISO15693Card.COMMAND_SET_AGC);
            Thread.Sleep(MILLISECOND_IN_SLEEP);
            if (serialPort1.BytesToRead > 0) response_card = serialPort1.ReadExisting();

            serialPort1.Write(ISO15693Card.COMMAND_SET_RECV_MODE);
            Thread.Sleep(MILLISECOND_IN_SLEEP);
            if (serialPort1.BytesToRead > 0) response_card = serialPort1.ReadExisting();

            serialPort1.Write(ISO15693Card.COMMAND_INVEN_CARD);//寻卡
            Thread.Sleep(MILLISECOND_IN_SLEEP);
            if (serialPort1.BytesToRead > 0) response_card = serialPort1.ReadExisting();//获取返回的字符串

            List cards = ISO15693CardHandler.InventoryCard(response_card);//解析返回的字符串,获取寻到的卡ID

            // [TODO] 向RFID卡第00块写入职员编号数据,无论成功与否,均应该在状态栏显示提示信息
            //检查输入数据的错误
            //ISO15693为32位,4字节,8字符
            if (tb_EmployeeId.Text.Length != 8)
            {
                MessageBox.Show("请输入4字节的16进制数据!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            //检查16进制字符错误
            if (!ISO15693CardHandler.CheckValidHexBytes(tb_EmployeeId.Text))
            {
                MessageBox.Show("写入数据的16进制格式错误!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            WriteSingleBlock(
                ISO15693CardHandler.CovertEndian(cards[0].ID),
                "00",
                tb_EmployeeId.Text);
        }
  • 卡片打卡(向数据库写入员工号、日期、时间等信息)
            // [TODO] 向数据库中插入一条打卡记录,插入成功后显示打卡成功
            //获取时间、日期等数据;
            string date = DateTime.Now.ToString("yyyy-MM-dd");
            string time = DateTime.Now.ToString("hh:mm:ss");

            // 更新数据库
            String connStr = ConfigurationManager.ConnectionStrings["Attendance"].ConnectionString;
            SqlConnection sqlConn = new SqlConnection(connStr);
            try
            {

                // 连接数据库
                sqlConn.Open();

                // 构造命令

                String sqlStr = "insert into record(employee_id, date, time, machine_id) values(@employee_id, @date, @time, @machine_id)";
                SqlCommand cmd = new SqlCommand(sqlStr, sqlConn);

                // SQL字符串参数赋值
                cmd.Parameters.Add(new SqlParameter("@employee_id", stuffId));
                cmd.Parameters.Add(new SqlParameter("@time", time));
                cmd.Parameters.Add(new SqlParameter("@date", date));
                cmd.Parameters.Add(new SqlParameter("@machine_id", ""));

                // 将命令发送给数据库
                int res = cmd.ExecuteNonQuery();

                // 根据返回值判断是否插入成功
                if (res != 0)
                {

                    MessageBox.Show("打卡成功");
                }
                else
                {
                    MessageBox.Show("打卡失败");
                }
            }
            catch (Exception exp)
            {
                MessageBox.Show("访问数据库错误:" + exp.Message);
            }
            finally
            {
                sqlConn.Close();
            }

4.考勤系统功能实现——查询

1.查询界面设计

  • 普通员工查询界面


    普通员工查询界面

    员工查询
  • 管理员查询界面


    管理员查询界面

    管理查询

2.查询界面实现代码

  • 普通员工查询个人考勤信息
        //点击查询,查询个人信息
        private void button1_Click(object sender, EventArgs e)
        {
            // 连接字符串,注意与实际环境保持一致
            String connStr = ConfigurationManager.ConnectionStrings["Attendance"].ConnectionString;
            SqlConnection sqlConn = new SqlConnection(connStr);
            try
            {
                // 连接数据库
                sqlConn.Open();

                // 构造命令
                String sqlStr = "select * from record where employee_id = @employee ";
                SqlCommand cmd = new SqlCommand(sqlStr, sqlConn);
      
                cmd.Parameters.Add(new SqlParameter("@employee", WorkerInfo.userId));

                // 将该查询过程绑定到DataAdapter
                SqlDataAdapter adp = new SqlDataAdapter();
                adp.SelectCommand = cmd;

                // 将DataSet和DataAdapter绑定
                DataSet ds = new DataSet();
                // 自定义一个表(MyGoods)来标识数据库的record表
                adp.Fill(ds, "record");

                // 指定DataGridView的数据源为DataSet的dgv_kaoqin_m表
                this.dgv_kaoqin_m.DataSource = ds.Tables["record"];
            }
            catch (Exception exp)
            {
                MessageBox.Show("访问数据库错误:" + exp.Message);
            }
            finally
            {
                sqlConn.Close();
            }
        }
  • 管理员查询所有人员的考勤信息
        // 查询数据
        private void bt_Query_Click(object sender, EventArgs e)
        {
            // 连接字符串,注意与实际环境保持一致
            String connStr = ConfigurationManager.ConnectionStrings["Attendance"].ConnectionString;
            SqlConnection sqlConn = new SqlConnection(connStr);
            try
            {
                // 连接数据库
                sqlConn.Open();

                // 构造命令
                //符合员工号查询
                if (tb_Id.Text.Trim() != "")
                {
                    String sqlStr = "select * from record where employee_id=@id";

                    SqlCommand cmd = new SqlCommand(sqlStr, sqlConn);
                    cmd.Parameters.Add(new SqlParameter("@id", this.tb_Id.Text.Trim()));
                    // 将该查询过程绑定到DataAdapter
                    SqlDataAdapter adp = new SqlDataAdapter();
                    adp.SelectCommand = cmd;

                    // 将DataSet和DataAdapter绑定
                    DataSet ds = new DataSet();
                    // 自定义一个表(MyGoods)来标识数据库的GOODS表
                    adp.Fill(ds, "records");

                    // 指定DataGridView的数据源为DataSet的MyGoods表
                    this.dgv_kaoqin_m.DataSource = ds.Tables["records"];
                }

                //符合日期查询
                if (tb_date.Text != "")
                {
                    String sqlStr1 = "select * from record where date=@date";
                    SqlCommand cmd1 = new SqlCommand(sqlStr1, sqlConn);
                    cmd1.Parameters.Add(new SqlParameter("@date", Convert.ToDateTime(tb_date.Text)));
                    //Convert.ToDateTime(dateTimePicker1.Value)
                    // 将该查询过程绑定到DataAdapter
                    SqlDataAdapter adp = new SqlDataAdapter();
                    adp.SelectCommand = cmd1;

                    // 将DataSet和DataAdapter绑定
                    DataSet ds = new DataSet();
                    // 自定义一个表(MyGoods)来标识数据库的GOODS表
                    adp.Fill(ds, "records");

                    // 指定DataGridView的数据源为DataSet的MyGoods表
                    this.dgv_kaoqin_m.DataSource = ds.Tables["records"];
                }
                //员工号、日期共同查询
                if (tb_date.Text.Trim() != "" && tb_Id.Text.Trim() != "")
                {
                    String sqlStr2 = "select * from record where date=@date and employee_id=@id";
                    SqlCommand cmd2 = new SqlCommand(sqlStr2, sqlConn);
                    cmd2.Parameters.Add(new SqlParameter("@date", Convert.ToDateTime(tb_date.Text)));
                    cmd2.Parameters.Add(new SqlParameter("@id", this.tb_Id.Text.Trim()));
                    // 将该查询过程绑定到DataAdapter
                    SqlDataAdapter adp = new SqlDataAdapter();
                    adp.SelectCommand = cmd2;

                    // 将DataSet和DataAdapter绑定
                    DataSet ds = new DataSet();
                    // 自定义一个表(MyGoods)来标识数据库的GOODS表
                    adp.Fill(ds, "records");

                    // 指定DataGridView的数据源为DataSet的MyGoods表
                    this.dgv_kaoqin_m.DataSource = ds.Tables["records"];
                }

                //无条件查询
                if (tb_date.Text.Trim() == "" &&  tb_Id.Text.Trim() == "")
                {
                    String sqlStr3 = "select * from record where 1=1";
                    SqlCommand cmd3 = new SqlCommand(sqlStr3, sqlConn);
                    
                    //Convert.ToDateTime(dateTimePicker1.Value)
                    // 将该查询过程绑定到DataAdapter
                    SqlDataAdapter adp = new SqlDataAdapter();
                    adp.SelectCommand = cmd3;

                    // 将DataSet和DataAdapter绑定
                    DataSet ds = new DataSet();
                    // 自定义一个表(MyGoods)来标识数据库的GOODS表
                    adp.Fill(ds, "records");

                    // 指定DataGridView的数据源为DataSet的MyGoods表
                    this.dgv_kaoqin_m.DataSource = ds.Tables["records"];
                }

            }
            catch (Exception exp)
            {
                MessageBox.Show("访问数据库错误:" + exp.Message);
            }
            finally
            {
                sqlConn.Close();
            }
        }

5.考勤系统功能实现——统计

1.统计界面设计

统计界面

统计界面

2.统计界面实现代码

        private void bt_Query_Click(object sender, EventArgs e)
        {
            // 连接字符串,注意与实际环境保持一致
            String connStr = ConfigurationManager.ConnectionStrings["Attendance"].ConnectionString;
            SqlConnection sqlConn = new SqlConnection(connStr);
            try
            {
                // 连接数据库
                sqlConn.Open();

                // 构造命令
                String sqlStr = @"select t4.department,count(*) as count from(
                                    select t3.*, t.name, t.department from (
                                        select t1.employee_id, t1.date, datediff(n,t1.time,t2.time) as diff
                                        from record t1
                                        inner join record t2
                                        on t1.date = t2.date
                                        and t1.employee_id=t2.employee_id
                                        and t1.machine_id =1
                                        and t2.machine_id =2
                                        and t1.date>=@start
                                        and t1.date<=@end
                                    ) t3,employee t where t3.employee_id=t.id
                                ) t4 where t4.diff<540 group by t4.department";

                SqlCommand cmd = new SqlCommand(sqlStr, sqlConn);
                cmd.Parameters.Add(new SqlParameter("@start", this.dtp_start.Value.ToShortDateString()));
                cmd.Parameters.Add(new SqlParameter("@end", this.dtp_stop.Value.ToShortDateString()));

                // 将该查询过程绑定到DataAdapter
                SqlDataAdapter adp = new SqlDataAdapter();
                adp.SelectCommand = cmd;

                // 将DataSet和DataAdapter绑定
                DataSet ds = new DataSet();

                // 自定义一个表来标识数据库的record表
                adp.Fill(ds, "tongjib");

                // 指定DataGridView的数据源为DataSet的cx表
                this.dataGridView1.DataSource = ds.Tables["tongjib"];
            }
            catch (Exception exp)
            {
                MessageBox.Show("访问数据库错误:" + exp.Message);
            }
            finally
            {
                sqlConn.Close();
            }
        }

        private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            // 点击查看详情
            if (e.RowIndex != -1 && e.ColumnIndex == 0)
            {
                // 获取部门名称
                string department = this.dataGridView1["department", e.RowIndex].Value.ToString();
                detail detailForm = new detail(department, this.dtp_start.Value, this.dtp_stop.Value);
                detailForm.Show();
            }
        }

        private void aggregate_Load(object sender, EventArgs e)
        {
            // 固定上半部分,下半部分随窗口大小调整
            this.splitContainer1.IsSplitterFixed = false;
            this.splitContainer1.FixedPanel = FixedPanel.Panel1;

            // 设置开始时间默认值
            this.dtp_start.Text = "2014/7/1";
        }

四、考勤系统测试

  • 登录


    登录实现
  • 注册


    注册实现
  • 查询


    查询实现
查询实现1
  • 统计


    统计

五、考勤系统发布和安装

  • 在确认无误后对程序进行发布


    发布
  • 安装程序


    安装

六、实训总结

通过此次的实训,我们深入了解到前端开发的步骤和方法,在考勤系统的搭建中,先制定计划,再进行需求分析,接着拟定开发步骤,一步一个脚印、稳扎稳打地从登录到注册、打卡、查询、统计;虽然开发过程中遇到很多的困难和疑问,但有老师的指导和引领,我们也在其中成长了,也收获了许多。
C#作为面向对象的开发语言,对于前端开发十分重要,相对于web开发,C#有着更易于设计的优势,更易懂的编程语言;但是遗憾的是,C#语言中有许多生僻的变量、属性我们从未接触过,在完成此次实训后,我们对其有了更深的见解。

你可能感兴趣的:(考勤系统实训报告)