先说一下,我们公司是六点下班,超过7点开始算加班,但是加班的时间是从六点开始计算,以0.5个小时为计数,就是你到了六点半,不算加班半小时,但是加班到七点半,就是加班了一个半小时。

一、打卡记录

  首先,看一下我们公司的打卡记录,公司的打卡工具是不区分上下班的,而且一天可以打多次,也可能忘记打卡,这都是有可能的,人在观察这些数据的时候,可以轻易的分辨出什么是上班时间,什么是下班时间,并且是不是我忘打卡了,但是一旦放到程序里,判断的逻辑就复杂了。

二、加班申请单

  加班申请单,也就是程序最后需要导出的Word文件,样子是这样的,页眉处是公司的LOGO,中部是标题,然后是一个表格

三、小工具效果演示

  姓名部分如果不用选择按钮,选择部门人员名单的话,也可以手动填写

  备注、申请人和申请时间是可以不填写的,如果不填写,则最后导出的Word相应的位置就是空。

四、开发过程

1、由于我们的加班申请单,是有固定的几个部分组成的,姓名,加班类型,加班时间起、止,小时数和备注,因此,创建了一个实体类OverTimeModel

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Collections; namespace SelectWorkOvertime
{
public class OverTimeModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged; private void INotifyPropertyChanged(string name)
{
if(PropertyChanged!=null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
} private string name;//姓名
private string overtimeType;//加班类型
private string overtimeStart;//加班时间起
private string overtimeEnd;//加班时间止
private string overtimeHours;//小时数
private string remark;//备注 public string Name//姓名
{
get
{
return name;
} set
{
name = value;
INotifyPropertyChanged("Name");
}
} public string OvertimeType//加班类型
{
get
{
return overtimeType;
} set
{
overtimeType = value;
INotifyPropertyChanged("OvertimeType");
}
} public string OvertimeStart//加班时间起
{
get
{
return overtimeStart;
} set
{
overtimeStart = value;
INotifyPropertyChanged("OvertimeStart");
}
} public string OvertimeEnd//加班时间止
{
get
{
return overtimeEnd;
} set
{
overtimeEnd = value;
INotifyPropertyChanged("OvertimeEnd");
}
} public string OvertimeHours//小时数
{
get
{
return overtimeHours;
} set
{
overtimeHours = value;
INotifyPropertyChanged("OvertimeHours");
}
} public string Remark//备注
{
get
{
return remark;
} set
{
remark = value;
INotifyPropertyChanged("Remark");
}
}
}
}

OverTimeModel

2、需要写要给工具类,这个工具类包含的内容如下:

  读取Excel(打卡记录是Excel文件)

  读取TXT文档(部门人员名单是TXT文件)

  判断Excel中的所有时间是工作日还周末,还是节假日(加班类型是有这三种的,因为有的时候节假日加班我们不用调休,给双倍工资)

  计算两个时间的小时差、获取上一天等

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks; namespace SelectWorkOvertime
{
public class Tools
{
/// <summary>
/// 读取TXT文档
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public string ReadTxt(string path)
{
string lines = "";
StreamReader streamReader = new StreamReader(path, Encoding.Default);
string line;
while ((line = streamReader.ReadLine()) != null)
{
lines += line.ToString() + " ";
}
return lines;
}
/// <summary>
/// 调用远程接口
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
public string IsHoliday(string date)
{
string url = @"http://www.easybots.cn/api/holiday.php?d=";
url = url + date;
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(url);
httpRequest.Timeout = ;
httpRequest.Method = "GET";
HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse();
StreamReader sr = new StreamReader(httpResponse.GetResponseStream(), System.Text.Encoding.GetEncoding("gb2312"));
string result = sr.ReadToEnd();
result = result.Replace("\r", "").Replace("\n", "").Replace("\t", "");
int status = (int)httpResponse.StatusCode;
sr.Close();
return result;
}
/// <summary>
/// 读取Excel到Dataset
/// </summary>
/// <param name="Path"></param>
/// <param name="fileName"></param>
/// <returns></returns>
public DataSet ExcelToDS(string Path, string fileName)
{
string strConn = "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=" + Path + ";" + "Extended Properties=Excel 8.0;";
OleDbConnection conn = new OleDbConnection(strConn);
conn.Open();
string strExcel = "";
OleDbDataAdapter myCommand = null;
DataSet ds = null;
strExcel = "select * from [" + fileName + "$]";
myCommand = new OleDbDataAdapter(strExcel, strConn);
ds = new DataSet();
myCommand.Fill(ds, "table1");
return ds;
}
/// <summary>
/// 获取日期的类型,是工作日,周末或节假日
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
public string getDateType(string dateTime)
{
string date = Convert.ToDateTime(dateTime.Split(' ')[].ToString()).ToString("yyyyMMdd");//获得到日期
string isHoliday = IsHoliday(date);
string numHoliday = isHoliday.Substring(isHoliday.Length - , );
if (numHoliday == "" || numHoliday == "")//判断是不是节假日{2},或者周末{1}
{
return numHoliday;
}
else
return "";//返回工作日{0}
}
/// <summary>
/// 获取小时值
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
public string getTimeHour(string dateTime)
{
string timeSFM = dateTime.Split(' ')[].ToString();//时分秒
string timeHour = timeSFM.Split(':')[].ToString();
return timeHour;
}
/// <summary>
/// 计算小时差值 正常情况
/// </summary>
/// <param name="dateStart"></param>
/// <param name="dateEnd"></param>
/// <returns></returns>
public string CalTimesNormal(DateTime dateStart, DateTime dateEnd)
{
string numTime;
TimeSpan ts = dateEnd.Subtract(dateStart);
if (ts.Minutes >= )
{
numTime = (ts.Hours + 0.5).ToString();
}
else
{
numTime = ts.Hours.ToString();
}
return numTime;
}
/// <summary>
/// 计算小时差值 上班时间在12点之前的加班要减去1小时
/// </summary>
/// <param name="dateStart"></param>
/// <param name="dateEnd"></param>
/// <returns></returns>
public string CalTimesAllDay(DateTime dateStart, DateTime dateEnd)
{
string numTime;
TimeSpan ts = dateEnd.Subtract(dateStart);
if (ts.Minutes >= )
{
numTime = (ts.Hours + 0.5 - ).ToString();
}
else
{
numTime = (ts.Hours - ).ToString();
}
return numTime;
}
/// <summary>
/// 获取上一天
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
public string getDateBefore(string dateTime)
{
string dateBefore = Convert.ToDateTime(dateTime).AddDays(-).ToString();
return dateBefore;
}
/// <summary>
/// 比较两个时间是否相同
/// </summary>
/// <param name="dateTime1"></param>
/// <param name="dateTime2"></param>
/// <returns></returns>
public int getCompareDate(string dateTime1, string dateTime2)
{
DateTime dt1 = Convert.ToDateTime(dateTime1.Split(' ')[].ToString());
DateTime dt2 = Convert.ToDateTime(dateTime2.Split(' ')[].ToString());
if (dt1 == dt2)
return ;
return ;
}
}
}

Tool

3、写一个设定加班开始时间的类

  因为由于规则不一样,因此加班的开始时间是不一样的,例如:

  周一到周五 18:00以后下班,加班不超过当天或者加班到第二天

  周末或者假日 上班时间为9:00 之前或9:00之后

  周末或者假日 打卡时间在12点到13点之间

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace SelectWorkOvertime
{
/// <summary>
/// 计算在不同的规则下加班的开始时间
/// </summary>
public class OverTimeStart
{
Tools tools = new Tools(); /// <summary>
/// 周一到周五 18:00以后下班,加班不超过当天
/// </summary>
/// <param name="dateTime">当天下班打卡时间</param>
/// <returns></returns>
public string OverTimeStart1(string dateTime)
{
DateTime dateTimeStart = Convert.ToDateTime(dateTime.Split(' ')[].ToString() + " 18:00:00");
return dateTimeStart.ToString("yyyy-MM-dd HH:mm");
}
/// <summary>
/// 周一到周五 18:00以后下班,加班到第二天
/// </summary>
/// <param name="dateTime">当天下班打卡时间</param>
/// <returns></returns>
public string OverTimeStart2(string dateTime)
{
DateTime dateTimeStart = Convert.ToDateTime(tools.getDateBefore(dateTime).Split(' ')[].ToString() + " 18:00:00");
return dateTimeStart.ToString("yyyy-MM-dd HH:mm");
}
/// <summary>
/// 周末或者假日 上班时间为9:00 之前
/// </summary>
/// <param name="dateTime">当天上班打卡时间</param>
/// <returns></returns>
public string OverTimeStart3(string dateTime)
{
DateTime dateTimeStart = Convert.ToDateTime(dateTime.Split(' ')[].ToString() + " 9:00:00");
return dateTimeStart.ToString("yyyy-MM-dd HH:mm");
}
/// <summary>
/// 周末或者假日 上班时间为9:00 之后
/// </summary>
/// <param name="dateTime">当天上班打卡时间</param>
/// <returns></returns>
public string OverTimeStart4(string dateTime)
{
DateTime dateTimeStart = Convert.ToDateTime(dateTime);
return dateTimeStart.ToString("yyyy-MM-dd HH:mm");
}
/// <summary>
/// 周末或者假日 打卡时间在12点到13点之间
/// </summary>
/// <param name="dateTime">当天上班打卡时间</param>
/// <returns></returns>
public string OverTimeStart5(string dateTime)
{
DateTime dateTimeStart = Convert.ToDateTime(dateTime.Split(' ')[].ToString() + " 13:00:00");
return dateTimeStart.ToString("yyyy-MM-dd HH:mm");
}
}
}

OverTimeStart

4、写主页面的内容

  由于公司没有重名的,因此在读取了Excel后,只从里面取了姓名和时间字段,其他字段都抛弃掉,而加班申请表上的“加班类型,加班时间起、止,小时数”这四列,都是根据时间字段来进行计算得出的。

  由于打卡时间本身的问题,我在前面也提到了,可能存在重复打卡,漏打的问题,所以,在计算加班上,就会出现不准的现象,所以为了提醒小伙伴,就用了一个转换器,把加班小于0的,显示为红色;小于0.5小时的,显示为黄色;大于12小时的显示为绿色,大于12小时是有可能正确的,但是一般不会出现,所以提醒一下比较好,而小于0的,就是数据不正确了,如图

  小冉和王哥对应的周末加班上,由于颜色标注,就知道应该是数据方面可能存在问题了,这样就要去查看原始的打卡记录,如图

  小冉的是没有问题的

  王哥是因为都打了两次卡,导致软件在逻辑判别时出现了问题,所以加班记录里的那条负值就可以在导出的Word里删除掉了。

5、导出

  导出Word的话,就是常用的C#操作Word,没什么。

6、总结

  软件本身在技术上不是很难,用到异步线程、Linq、对文件的操作、转换器,基本都是常用的技术,难点其实就是在逻辑的判断上,无法做到全面的无误的去合理的计算出加班时间和加班类型,十分感谢彭哥,因为在写第一个版本的时候,让逻辑都快搞崩溃了,是彭哥告诉我,可以写每一个小的逻辑,然后再把小的逻辑组合成大的逻辑。总起来说,做这个小软件还有有技术上和思维上的收获的,仁者见仁智者见智吧,最起码,以后基本上不需要人手动的去写加班申请了,谁让懒是程序员的天性呢。

注:软件存在以下BUG

  1、还是逻辑上不完善

  2、由于是调用的WEB上提供的接口去判断,是工作日、节假日、周末,例如阅兵就是节假日,所以,软件运行时需要联网

GitHub源码

希望园里的朋友批评指正,大家一起探讨,共同进步。

WPF开发查询加班小工具的更多相关文章

  1. python开发目录合并小工具 PathMerge

    前言 这个程序陆陆续续开发了几天,正好我在学Python,就一边做一边学,倒是学到不少东西. 不得不说python是快速开发的好工具. 程序做了一些改进,这两天又忙着毕设,现在才想起来发到博客上.想想 ...

  2. 几款Android开发人员必备小工具

    在这里我介绍一下我常常在Android Studio里面使用的小工具吧,这些工具都能够在plugin里面搜索到. (当然了哈.我也是从网上找的.用着挺方便的,在这里总结一下) Gsonformat: ...

  3. ip地址查询python3小工具_V0.0.1

    看到同事在一个一个IP地址的百度来确认导出表格中的ip地址所对应的现实世界的地址是否正确,决定给自己新开一个坑.做一个查询ip“地址”的python小工具,读取Excel表格,在表格中的后续列输出尽可 ...

  4. Python 使用 PyQt5 开发的关机小工具

    前两天简单认识了一下PyQt5,通过练习开发了一款在Window下自定义关机的小工具,代码如下 import os,sys,time from PyQt5 import QtCore,QtWidget ...

  5. C++开发的数据库连接查询修改小工具

    项目相关地址 源码:https://github.com/easonjim/SQL_Table_Tool bug提交:https://github.com/easonjim/SQL_Table_Too ...

  6. WPF数据爬取小工具-某宝推广位批量生成,及订单爬取 记:接单最痛一次的感悟

    项目由来:上月闲来无事接到接到一个单子,自动登录 X宝平台,然后重定向到指定页面批量生成推广位信息:与此同时自动定时同步订单数据到需求提供方的Java服务. 当然期间遇到一个小小的问题就是界面样式的问 ...

  7. Pyqt5开发一款小工具(翻译小助手)

    翻译小助手 开发需求 首先五月份的时候,正在学习爬虫的中级阶段,这时候肯定要接触到js逆向工程,于是上网找了一个项目来练练手,这时碰巧有如何进行对百度翻译的API破解思路,仿造网上的思路,我摸索着完成 ...

  8. AutoIt3(AU3)开发的装机小工具,实现快速检测以及一些重用快捷操作功能

    项目相关地址 源码:https://github.com/easonjim/Installed_Tools bug提交:https://github.com/easonjim/Installed_To ...

  9. 简化网站开发:SiteMesh小工具

    在一个站点的制备,几乎所有的页面将具有相同的部分.导航栏例如,顶,每一页都是一样的,在底部的版权声明,每一页还都是一样的. 因此,在顶部导航栏的准备.第一种方法是直接复制的所有导航栏的代码,这种方法是 ...

随机推荐

  1. T-SQL Recipes之Customized Database Objects

    The Problem 创建灵活自定义对象决非是一个简单的任务.比如HR想看每种工作职称在所有年限里面的入职累计情况 The Solution 我们一步一步来拆解吧: 获取入职年限的集合,如1999, ...

  2. svn客户端重新设置用户名和密码

    在第一次使用TortoiseSVN从服务器CheckOut的时候,会要求输入用户名和密码,这时输入框下面有个选项是保存认证信息,如果选了这个选项,那么以后就不用每次都输入一遍用户名密码了. 不过,如果 ...

  3. asp.net两种方式的短信接口使用(提供接口的都是收费的)

    一种是http请求的方式,另一种就是提供WebService接口供调用的. //服务商 sms.webchinese.cn //sms_url="http://sms.webchinese. ...

  4. sql 存储过程 output参数的使用

    /*嵌套存储过程中需要输出来的参数*/output 就是这个存储过程返回的值 也可以说输出的值--创建存储过程 求最大值CREATE PROCEDURE [dbo].[P_Max]@a int, -- ...

  5. 从Erlang进程看协程思想

    从Erlang进程看协程思想 多核慢慢火了以后,协程类编程也开始越来越火了.比较有代表性的有Go的goroutine.Erlang的Erlang进程.Scala的actor.windows下的fibr ...

  6. Android :fragment介绍

    一.关于Fragmemt 1.Fragment(片段),主要是为了支持更多的动态和灵活的用户界面设计,如平板电脑.Fragment允许组合和交换用户界面组件,而不需要更改视图层次结构.通过把Activ ...

  7. Odoo 二次开发教程(三)-第一个Model及Form、Tree视图

    创建完我们的模块,接下来我们就要为我们的模块添加一些对象.今天我们将要创建一个学生对象(tech.student)和一些基本的属性,并将用form和tree视图将其展示出来: 一. 创建tech.st ...

  8. CF2.C

    C. Vladik and fractions time limit per test 1 second memory limit per test 256 megabytes input stand ...

  9. 深入理解css BFC 模型

    如果要深入理解css布局的各种原理,要在重构页面做得心应手的话,那么你就必须先理解这玩意 "BFC" , BlockFormatting Context(块级格式化上下文): 这里 ...

  10. 谢欣伦 - OpenDev原创教程 - 蓝牙设备查找类CxBthRemoteDeviceFind

    这是一个精练的蓝牙设备查找类,类名.函数名和变量名均采用匈牙利命名法.小写的x代表我的姓氏首字母(谢欣伦),个人习惯而已,如有雷同,纯属巧合. CxBthRemoteDeviceFind的使用如下: ...