当前位置:编程学习 > C#/ASP.NET >>

c#开发的工资计算程序有个不解的问题

近期开发了个带公式编辑的工资计算程序,遇到了个很疑惑的问题,希望高人指点。
开发环境:vs 2008(C#)+ sql server 2005

问题现象如下:
    用户选中一个地区(大概3900人,工资由20个需要计算的项目组成),开始计算。计算完成后发现个别人员工资某一项有误,如扣缺勤的计算公式为:扣缺勤=[底薪]/[当月标准工作小时]*[未上班工作小时],一开始认为在计算的时候是公式解析错误导致,就在每次计算的时候记下了公式解析情况。发现计算错误查看日志,发现公式在解析的时候没错,日志如下:
---------2010-07-08 14:21:20---------
李丹 扣缺勤(底薪/当月标准工作小时*未上班工作小时)。
    选中计算有误的员工,再次计算一次,就又都正确了。如果选择重新计算全部,计算完后还有错,错误的员工和工资项目每次都不一样,公式解析都没有问题。

--------------------编程问答-------------------- 核心计算代码:

using System;
using System.Collections;
using System.Linq;
using System.Text;
using System.Data;
using Common;
using System.Data.Common;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
namespace DataContent
{
    //实现薪资计算的类
    class Compute
    {
        private int _total;
        private int _complyte;
        private DataTable dt_formula;   //计算公式
        private string viewName = "view_1"; //视图名称
        private DataSet ds_employees;
        private string _SysDate;
        private bool _hasRollBack = false;
        private string errLogName;

        public bool hasRollBack
        {
            get { return _hasRollBack; }
        }

        public System.Windows.Forms.ProgressBar progressBar1;
        public System.Windows.Forms.Label lb_process;
        public Frm_cmpt parent;        

        public Compute()
        {
            _Compute("");
            
        }

        public Compute(string common)
        {
            
            _Compute(common);
        }
  
        private void _Compute(string common)
        {
            errLogName = "数据计算错误" + DateTime.Now.ToString("yyyy-MM-dd HHmm") +".txt";
            try
            {
                parent = new Frm_dataContent();
                DataSet ds = GetFormula();
                if (ds == null || ds.Tables[0].Rows.Count < 1)
                {
                    throw new Exception("没有需要计算的项目!");
                }
                dt_formula = ds.Tables[0];

                if (common == "")
                {
                    common = "1=1";
                }

                _SysDate = GetDate(common);
                ds_employees = GetViewData(viewName, common);
                if (ds_employees == null || ds_employees.Tables[0].Rows.Count < 1)
                {
                    throw new Exception("获取员工信息失败!");
                }

                _total = ds_employees.Tables[0].Rows.Count * dt_formula.Rows.Count + 1;   //+1是导入垫付扣还累计表数据
                Assay.dtFormula = dt_formula;                
            }
            catch(Exception ep)
            {
                CommonFunc com = new CommonFunc();
                com.WriteErrLog(ep);
                com.WriteLog(ep.Message, "err");
                throw ep;
            }

        }

        //开始计算
        public void StartCompute()
        {
            try
            {
                foreach (DataRow dr_formula in dt_formula.Rows)
                {
                    if (dr_formula["已计算"].ToString() != "1")
                    {
                        string itemName = dr_formula["字段"].ToString();
                        string itemFormula = dr_formula["计算公式"].ToString();
                        string Num = dr_formula["小数位数"].ToString();
                        foreach (DataRow dr_employees in ds_employees.Tables[0].Rows)
                        {
                            ComputeItem(itemName, itemFormula, Num, dr_employees);
                        }
                        RefreshMTable(dr_formula, "已计算", "1");
                    }
                }
                
                //处理垫付扣还累计数据
                ImportData(_SysDate);
                RefreshProgress();   //刷新进度
                parent.Complyte();
                if(_hasRollBack)
                {
                    MessageShow("计算已完成 但是有错误");
                    System.Diagnostics.Process.Start(Application.StartupPath + "\\Log\\" + errLogName);
                }
                else
                {
                    MessageShow("计算已完成!");
                }
            }
            catch (System.Threading.ThreadAbortException abortException)
            {
                MessageShow("计算已被用户取消!");
            }
            catch (Exception ep)
            {                
                CommonFunc com = new CommonFunc();
                com.WriteErrLog(ep);
                com.WriteLog(ep.Message, "err");
                MessageShow("计算失败!");
            }
        }        

        //提取str中包含的日期
        //str中允许的时间格式yyyy-mm-dd
        protected string GetDate(string str)
        {
            string tmp = @"[0-9]{4,4}-[0-9]{1,2}-[0-9]{1,2}";
            Regex r = new Regex(tmp);
            Match m = r.Match(str);
            if (m.Success) return m.Groups[0].Value;
            throw new Exception("无法获取有效的系统期间");
        }

        //获取计算进度
        private int GetProgress(int compl,int total)
        {
            float tmp = ((float)compl / (float)total) * 100;
            int i = (int)tmp;
            if (i > 100) return 100;
            return i;
        }

        private void RefreshProgress()
        {
            ++_complyte;
            int ProgressValue = GetProgress(_complyte,_total);
            progressBar1.Value = ProgressValue;
            lb_process.Text = ProgressValue.ToString() + "%";
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="itemName"></param>
        /// <param name="formula">计算公式,如果计算公式为""空字符串,将字段值存为""空字符串。</param>
        /// <param name="dr_employees"></param>
        private void ComputeItem(string itemName, string formula,string num, DataRow dr_employees)
        {

            Assay ass = null;   //公式解析
            string comm = "系统期间='" + dr_employees["系统期间"].ToString() + "' and 员工工号='" + dr_employees["员工工号"].ToString() + "' and 员工姓名='" + dr_employees["员工姓名"].ToString() + "'";
            string value = "";

            if (formula != "")
            {
                try
                {
                    string str_formula = null; //解析后的最终公式
                    ass = new Assay(dr_employees, formula);                    
                    str_formula = ass.GetResultEx();

                    if (str_formula != "")
                    {
                        value = ComputeItem(str_formula, viewName, comm);
                        if (value == "") value = "0";

                        int n = num == "" ? 0 : int.Parse(num);
                        value = ValueFormat(value, n);  //将计算结果保留指定小数位
                        
                        if (!Save(viewName, itemName, value, comm))
                        {
                            string err = dr_employees["员工姓名"].ToString()
                                        +"( "
                                        + dr_employees["员工工号"].ToString()
                                        +") "
                                        + itemName + " 结果保存失败!";
                            throw new Exception(err); //存入数据库
                        }
                        RefreshMTable(dr_employees, itemName, value);  //存入内存表
                        RefreshProgress();   //刷新进度
                    }

                    CommonFunc com = new CommonFunc();
                    string msg = dr_employees["员工姓名"].ToString()
                                        + "( "
                                        + dr_employees["员工工号"].ToString()
                                        + ") "
                                        + itemName
                                        + "("
                                        + str_formula
                                        + ")";
                    com.WriteLog(msg, "公式");

                }
                catch (System.Threading.ThreadAbortException abortException)
                {
                    throw abortException;
                }
                catch (Exception ep)
                {
                    if (ep.Message == "IF_ERR_NULL")
                    {
                        string err = dr_employees["员工姓名"].ToString()
                                        + " ("
                                        + dr_employees["员工工号"].ToString()
                                        + ") "
                                        + ass.errorItem[0].ToString() + " 字段值未初始化!";
                        throw new Exception(err);//null值字段不是前置字段,但是其值为null,无法参与计算  
                    }
                    else if (ep.Message == "IF_BEFORE")
                    {
                        for (int index = 0; index < ass.beforeItem.Count; ++index)
                        {
                            string tmpItemName = ass.beforeItem[index].ToString();   //前置字段名称
                            DataRow tmpFormula = GetFormulaByName(tmpItemName);

                            if (tmpFormula.IsNull("计算公式") || tmpFormula["计算公式"].ToString() == "") throw new Exception(tmpItemName + "字段计算公式为空!");
                            ComputeItem(tmpFormula);   //前置字段
                        }
                        ComputeItem(itemName, formula,num, dr_employees);
                    }
                    else
                    {
                        string err = dr_employees["员工姓名"].ToString()
                                        + " ("
                                        + dr_employees["员工工号"].ToString()
                                        + ") "
                                        + itemName + ep.Message;
                        throw new Exception(err);
                    }
                }
            }
        } --------------------编程问答-------------------- 接上
        private string ValueFormat(string value, int num)
        {
            decimal dValue = decimal.Parse(value);
            string re = "0.";
            for (int i = 0; i < num; ++i)
            {
                re += "0";
            }
            return dValue.ToString(re);
        }

        //为所有员工计算前置字段 
        private bool ComputeItem(DataRow dr_formula)
        {
            try
            {
                string itemName = dr_formula["字段"].ToString();
                string formula = dr_formula["计算公式"].ToString();
                string Num = dr_formula["小数位数"].ToString();
                foreach (DataRow dr_employees in ds_employees.Tables[0].Rows)
                {
                    ComputeItem(itemName, formula,Num, dr_employees);
                }
                RefreshMTable(dr_formula, "已计算", "1");
                return true;
            }
            catch(Exception ep)
            {
                throw new Exception(ep.Message);
            }
        }

        //将值更新到内存表中
        private void RefreshMTable(DataRow dr, string itemName, string value)
        {
            string type = dr.Table.Columns[itemName].DataType.ToString();
            switch (type)
            {
                case "System.Int32":
                    dr[itemName] = value == "" ? 0 : int.Parse(value);
                    break;
                case "System.Decimal":
                    dr[itemName] = value == "" ? 0 : decimal.Parse(value);
                    break;
                case "System.String":
                    dr[itemName] = value;
                    break;
                default:
                    dr[itemName] = value;
                    break;
            }
        }

        //从dt_formula读取指定字段名的计算公式
        private DataRow GetFormulaByName(string item)
        {
            DataRow[] result = dt_formula.Select("字段 = '" + item + "'");
            if (result == null || result.Length < 1) return null; //字段不存在计算公式表中
            return result[0];
        }

        //异常提示
        private void MessageShow(string message)
        {
            System.Windows.Forms.MessageBox.Show(message);
        }

        //获取视图数据
        private DataSet GetViewData(string ViewName, string comm)
        {
            string sql = @"select * from " + ViewName + " where " + comm;            
            return base_db.dbConn.GetDataSet(sql);
        }


        //获取公式
        private DataSet GetFormula()
        {
            string sql = @"select * from 计算公式 ";
            return base_db.dbConn.GetDataSet(sql);
        }


        /// <summary>
        /// 获取计算结果
        /// </summary>
        /// <param name="formula"></param>
        /// <param name="comm">sql条件</param>
        private string ComputeItem(string formula, string view, string comm)
        {
            string sql = @"select cast(" + formula + " as decimal(18,4)) from " + view + " where " + comm;
            DataSet ds = base_db.dbConn.GetDataSet(sql);
            if (ds == null || ds.Tables[0].Rows.Count < 1) return "";

            return ds.Tables[0].Rows[0][0].ToString();
        }
        /// <summary>
        /// 保存值到数据库
        /// </summary>
        /// <param name="view">视图名称</param>
        /// <param name="item">字段名</param>
        /// <param name="value">字段值</param>
        /// <param name="comm">sql查询条件</param>
        /// <returns></returns>
        private bool Save(string view, string item, string value, string comm)
        {
            base_db.dbConn.BeginTransaction();    //在连接上启用事务
            string sql = @"update " + view + " set " + item + " = " + value + " where " + comm;            
            try
            {                
                base_db.dbConn.ExecuteSQL(sql);
                base_db.dbConn.Commit();
                return true;
            }
            catch(Exception ep)
            {
                base_db.dbConn.RollBack();
                string num = GetNum(sql);
                CommonFunc com = new CommonFunc();
                try
                {
                    com.WriteErrLog(ep);
                    System.IO.StreamWriter sw = System.IO.File.AppendText(Application.StartupPath + "\\Log\\" + errLogName);
                    //sw.WriteLine("---------" + string.Format("{0:yyyy-MM-dd HH:mm:ss}", System.DateTime.Now) + "---------");
                    //sw.WriteLine("reqcont:");
                    sw.WriteLine(num);
                    //sw.WriteLine("");
                    //sw.WriteLine("");
                    sw.Flush();
                    sw.Close();
                    _hasRollBack = true;
                    
                }
                catch
                { }
                
                return true;
            }
        }

        // 获取员工工号
        private string GetNum(string sql)
        {
            string tmp = @"员工工号='[^']+'";
            Regex r = new Regex(tmp);
            Match m = r.Match(sql);
            if (m.Success)
            {
                string str = m.Groups[0].Value.Replace("员工工号='", "").Replace("'", "");

                return str;
            }
            return "";
        }

        
        /// <summary>
        /// 处理垫付扣还数据。分三步1.将上月数据拷贝一份,不过垫付、扣还都为0;
        /// 2.对比view_1更新当月垫付、扣还值
        /// 3.计算当月剩余值
        /// </summary>
        /// <param name="SysDate"></param>
        private void ImportData(string SysDate)
        {
            DateTime dtime = DateTime.Parse(SysDate);
            CopyData(dtime);
            UpdateExsitData(dtime);
            InsertNewData(dtime);
            UpdateResult(dtime);
        }

        /// <summary>
        /// 拷贝上月数据,清空垫付、扣还
        /// </summary>
        /// <param name="dtime">当月日期</param>
        private void CopyData(DateTime dtime)
        {
            string thisDate = dtime.ToString("yyyy-MM-dd");
            dtime = dtime.AddMonths(-1);
            string lastDate = dtime.ToString("yyyy-MM-dd");
            string sql = @"delete from 垫付扣还累计 where 系统期间='" + thisDate + @"'";
            if (!base_db.dbConn.ExecuteSQL(sql)) throw new Exception("删除垫付扣还数据失败");
            sql = @"
                insert into 垫付扣还累计
                select 姓名,身份证,部门,0 AS 垫付,0 AS 扣还,公司垫付余额,'" + thisDate + @"' AS 系统期间 
                from 垫付扣还累计 
                where 系统期间='" + lastDate + @"'
                ";
            if (!base_db.dbConn.ExecuteSQL(sql)) throw new Exception("垫付扣还数据复制失败");
        }

        //更新上月已经存在人员的垫付扣还数据
        private void UpdateExsitData(DateTime dtime)
        {
            string thisDate = dtime.ToString("yyyy-MM-dd");
            string sql = @"
                        DECLARE @tmptable table
                        (
                        员工姓名 nvarchar(200),
                        员工工号 nvarchar(200),
                        部门名称 nvarchar(200),
                        当月公司垫付 money,
                        当月公司扣还 money,
                        系统期间 datetime,
                        姓名 nvarchar(200),
                        身份证 nvarchar(200),
                        垫付 money,
                        扣还 money,
                        公司垫付余额 money
                        )

                        insert into @tmptable select *
                        from
                        (
                        select view_1.员工姓名,view_1.员工工号,view_1.部门名称,view_1.公司垫付_最终,view_1.当月公司扣还,
                        view_1.系统期间,A.姓名,A.身份证,A.垫付,A.扣还,A.公司垫付余额
                        from view_1
                        left join 垫付扣还累计 as A on A.身份证=员工工号 and a.系统期间=view_1.系统期间
                        where view_1.系统期间='" + thisDate + @"' and (view_1.公司垫付_最终<> 0 or view_1.当月公司扣还<>0)
                        ) as a
                        WHERE 身份证 IS NOT NULL

                        update 垫付扣还累计 set 垫付=
                        (
                        select 当月公司垫付 from @tmptable where 身份证=垫付扣还累计.身份证
                        ), 
                        扣还=
                        (
                        select 当月公司扣还 from @tmptable where 身份证=垫付扣还累计.身份证
                        )
                        where 系统期间='" + thisDate + @"' and 身份证 in (select 身份证 from @tmptable)
                        ";
            if (!base_db.dbConn.ExecuteSQL(sql)) throw new Exception("垫付扣还数据跟新失败");
        }

        //向垫付扣还累计插入新员工垫付扣还信息
        private void InsertNewData(DateTime dtime)
        {
            string thisDate = dtime.ToString("yyyy-MM-dd");
            string sql = @"
                        insert into 垫付扣还累计
                        select 员工姓名,员工工号,部门名称,公司垫付,当月公司扣还,0 as 公司垫付余额, 系统期间
                        from
                        (
                        select view_1.员工姓名,view_1.员工工号,view_1.部门名称,view_1.公司垫付,view_1.当月公司扣还,
                        view_1.系统期间,A.姓名,A.身份证,A.垫付,A.扣还,A.公司垫付余额
                        from view_1
                        left join 垫付扣还累计 as A on A.身份证=员工工号 and a.系统期间=view_1.系统期间
                        where view_1.系统期间='" + thisDate + @"' and (view_1.公司垫付<> 0 or view_1.当月公司扣还<>0)
                        ) as a
                        WHERE 身份证 IS NULL
                        ";
            if (!base_db.dbConn.ExecuteSQL(sql)) throw new Exception("垫付扣还数据插入失败");
        }

        //计算余额
        private void UpdateResult(DateTime dtime)
        {
            string thisDate = dtime.ToString("yyyy-MM-dd");
            string sql = @"update 垫付扣还累计 set 公司垫付余额=公司垫付余额+垫付-扣还 where 系统期间='" + thisDate + @"'";
            if (!base_db.dbConn.ExecuteSQL(sql)) throw new Exception("垫付扣还数据计算失败");
        }

    }
} --------------------编程问答-------------------- 太多
还是单步跟踪检查计算值
--------------------编程问答-------------------- 错误差多少?不是精度问题吗? --------------------编程问答-------------------- 先Debug吧 --------------------编程问答-------------------- 估计是你哪个变量在计算下一个员工前没清空吧 --------------------编程问答-------------------- 根据我的经验 有可能是 某个或某些变量初始化的时间导致的, 第一次运行时候 错误,第二次又正确了 变量,控制变量可能有问题 ,建议跟踪全部变量.第一次运行 全部变量 输出以下,第二次运行 全部变量再输出以下 ,对比一下 两次变量值 不相同的变量可能就是导致出问题的因素 --------------------编程问答-------------------- debug --------------------编程问答-------------------- 楼上的太厉害了  洋洋洒洒就写了好几百行啊 --------------------编程问答-------------------- 这样的问题还是要靠自己debug --------------------编程问答--------------------
引用 7 楼 happyer_longlong 的回复:
根据我的经验 有可能是 某个或某些变量初始化的时间导致的, 第一次运行时候 错误,第二次又正确了 变量,控制变量可能有问题 ,建议跟踪全部变量.第一次运行 全部变量 输出以下,第二次运行 全部变量再输出以下 ,对比一下 两次变量值 不相同的变量可能就是导致出问题的因素



+1  我也觉得不是你计算的问题 ,毕竟无复杂性可言 看看你变量的初始化位置 --------------------编程问答-------------------- 自己一部部调试吧。。。 --------------------编程问答-------------------- 好长啊~~~~ --------------------编程问答-------------------- 哥们,你调试一下好不?
先找到出问题的地方,再贴出代码吧。
你这样会让人头晕的
补充:.NET技术 ,  C#
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,