由于公司实施SAP HR项目,但是SAP HR对考勤功能真的太弱化了,直接从考勤机上读取的原始打卡记录不能直接传输到HR系统里面,因为SAP HR不能识别那些多余的打卡记录,而且必须把打卡记录进行成组标记(P10,P20),以上标红色的是SAP HR顾问给我的反馈信息。
这样以来,必须开发一套算法来把多余的打卡记录进行过滤掉,然后标记上是P10还是P20,这样以来,HR系统在做时间评估时才不会出现异常情况。
需求已经明确,那么就是设计开发的问题,要开发该功能,需要用到的资源:
1、考勤的排班数据
2、原始打卡数据
3、取原始打卡数据的算法
然后就是确定该功能在我们的外围基础数据平台开发(外围系统是用.net开发的)
由于我们公司的考勤计算方式可能会经常有变化,因为不能把打卡数据的算法写死到代码里面,这样有两种解决方案:1是用侟储过程 2是用动态编译
考虑到以后数据库的迁移问题,最终采取.net动态编译方式来处理该问题
具体的实现思想如下:
1、把算法代码存储在数据库表中
2、新建一个计算考勤的静态类,类里面定义一个计算考勤的静态方法,传入参数为两上Table类型,一个为排班表,一个为原始打卡记录表,返回参数也
为DataTable内型,为计算完成后的结果数据,把该段代码保存在一个静态变量里面。
3、调用计算方法时,把静态类的代码加上代算法代码编译成动态类。
4、利用反射技术,调用计算方法,返回结果。
关键性代码如下:
动态编译类:
DynCompiler.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Reflection;
using System.Data;
namespace EDICLibrary
{
public class DynCompiler:IDisposable
{
CodeDomProvider _codeprovide;
CompilerParameters _parameters;
CompilerResults _result = null;
MethodInfo _method = null;
Object _instance = null;
public DynCompiler()
{
_codeprovide = new CSharpCodeProvider();
_parameters = new CompilerParameters();
_parameters.GenerateExecutable = false;
_parameters.GenerateInMemory = true;
_parameters.ReferencedAssemblies.Add("System.dll");
_parameters.ReferencedAssemblies.Add("System.Data.dll");
_parameters.ReferencedAssemblies.Add("System.Xml.dll");
}
public bool SourceCompiler(string source, out string outputMsg)
{
outputMsg = "";
StringBuilder sbout = new StringBuilder();
StringBuilder sb = new StringBuilder();
if (string.IsNullOrEmpty(source))
{
outputMsg = "源代码不能为空!";
return false;
}
else
{
sb.AppendLine("using System;");
sb.AppendLine("using System.Collections.Generic;");
sb.AppendLine("using System.Text;");
sb.AppendLine("using System.Data;");
sb.AppendLine ("using System.Xml;");
sb.AppendLine("namespace JMCompiler");
sb.AppendLine("{");
sb.AppendLine("public class DynCompilerHelper");
sb.AppendLine("{");
sb.AppendLine("public DataTable Calculate(DataTable dtShiftInfo,DataTable dtTimes)");
sb.AppendLine("{");
sb.Append(source);
sb.Append("}");
sb.AppendLine("}");
sb.AppendLine("}");
}
_result = _codeprovide.CompileAssemblyFromSource(_parameters, sb.ToString());
if (_result.Errors.HasErrors)
{
foreach (string str in _result.Output)
{
sbout.AppendLine(str);
}
outputMsg = sbout.ToString();
return false;
}
else
{
Type _type = _result.CompiledAssembly.GetType("JMCompiler.DynCompilerHelper");
_instance = Activator.CreateInstance(_type);
_method = _type.GetMethod("Calculate",new Type[]{typeof(DataTable),typeof (DataTable)});
outputMsg = "编译成功";
return true;
}
}
public object GetReturnResult(List<DataTable> parameters)
{
if (_method == null)
{
throw new Exception ("未进行代码编译");
}
return _method.Invoke(_instance, parameters.ToArray());
}
#region IDisposable 成员
public void Dispose()
{
_codeprovide = null;
_parameters = null;
_result = null;
_method = null;
_instance = null;
}
#endregion
}
}
计算考勤类:
AttdCalculate.cs
public class AttdCalculate { /// <summary> /// 计算考勤 /// </summary> /// <param name="source">计算考勤算法代码</param> /// <param name="dtShiftInfo">排班信息</param> /// <param name="dtTimes">原始打卡的数据</param> /// <param name="msg">输出信息</param> /// <returns>计算后的结果表</returns> public static DataTable Calculate(string source, DataTable dtShiftInfo, DataTable dtTimes, out string msg) { if (string.IsNullOrEmpty(source)) { msg = "考勤计算逻辑不能为空"; return new DataTable(); } DynCompiler complier = new DynCompiler(); string output; bool result = complier.SourceCompiler(source, out output); List<DataTable> lstData = new List<DataTable>(); lstData.Add(dtShiftInfo); lstData.Add(dtTimes); if (result) { msg = "计算成功"; return (DataTable)complier.GetReturnResult(lstData); } else { msg = output; return new DataTable(); } } }
考勤调用代码
//计算公式 string Source = string.Empty; //获取计算工式 Source = <获取计算工式的方法> string msg; //获取考勤数据 DataTable dtResult = MIS.Util.AttdCalculate.Calculate(Source, dtShiftInfo, dtTimes, out msg);
在写这篇文章的时候,开发的功能还没有投入到正式运行,但是经过最近一段时候的测试来看,没有任何问题。