这是重新找回老帐号的第一篇回归文
再次回归IT领域,发现很多东西发展很快,但同时很多以前的干货时不时确实可以捡起来继续用,自己知识的累计沉淀还是很有必要的,现在回过头发现过去留下的太少太少,现在开始重新积累。
背景介绍
OPICS系统是MYSIS的资金管理系统(现在已被FINSTRA收购),作为一个从KONDOR时代走来的人来说,本应是驾轻就熟的,然而网上有关的资料真是非常的稀少,作为日常运维,节假日表的维护是再正常不过的操作,然而系统架构非本地化的设计使得维护的工作显得异常痛苦并伴随着极大的操作风险。
非本地化设计的痛点
根据界面可以看到币别的节假日维护大概有如下特性:
- 指定日历日期
- 指定日期
- 固定每周的时间
这个设计本身来说灵活性还可以,但是对于CNY的特殊性,这个设计就比较有缺陷了,主要是因为周六周日可能也会有调整成工作日的可能性,以至于CNY的设置无法使用固定每周,不得不用指定日期进行维护,想到每年的周六周日日期,这个数据量着实有点吓到我了,由此引出了本篇。
技术分析
作为一个IT人,总要想办法用技术手段提高一下效率,用界面一个一个录入,且不说工作量,就正确性而言也很保证。
首先想到的就是从后台数据库着手,查找相关数据字典,得到该表,尝试插入节假日日期,发现界面并无显示,很显然可能有一堆中间业务表,至此这条路不能这样走下去;
通过与厂商沟通发现在做数据迁移时,该功能背后有一个配置文件如下,看到这个文件的时候,我很开心,对于一个和EXCEL打了很多年交道的人来说,1900-1-1至今天日期的long数字型表达实在太熟悉了,配置文件如此的清晰,立马决定尝试一下,在行内依葫芦画瓢的设了一个新的值进去,更新后发现界面也更新了,验证了一下后台数据库的数据也有了该记录,证明该方法可行,那接下来只要我们照着这样的格式就行了。
技术解决方案设计
源数据用EXCEL直接处理成long,标识币种(Excel便于模板制作,直观可控)
程序批量读取EXCEL,读取INI写入相应币种节假日(为了程序更好的使用,EXCEL操作引用NPOI,避免一定要安装EXCEL以及32,64位OFFICE DLL库引用的麻烦)
具体实现
接下来就是程序的分功能实现及组装了,网上的例子其实很多,随便找了一下能用的代码片段组装起来就好,对于不创造核心算法的IT人来说,高级语言就是LEGO组装,把功能拼装在一起实现需要的功能。
// Sample for INI read & write
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
class IniFile
{
/*
* 声明API函数
*/
public string iniPath;
[DllImport("kernel32")]
private static extern long WritePrivateProfileString(string section, string key, string val, string filePath);
[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);
/// <summary>
/// 构造函数
/// </summary>
/// <param name="iniPath">ini文件路径,默认为当前路径下default.ini</param>
public IniFile(string iniPath = "./default.ini")
{
this.iniPath = iniPath;
}
/// <summary>
/// 写入ini文件
/// </summary>
/// <param name="Section">Section</param>
/// <param name="Key">键</param>
/// <param name="Value">值</param>
public void writeIni(string Section, string Key, string Value)
{
WritePrivateProfileString(Section, Key, Value, this.iniPath);
}
/// <summary>
/// 写入ini文件,不管section,默认放在default里
/// </summary>
/// <param name="Key">键</param>
/// <param name="Value">值</param>
public void writeIni(string Key, string Value)
{
WritePrivateProfileString("default", Key, Value, this.iniPath);
}
/// <summary>
/// 读取ini文件
/// </summary>
/// <param name="Section">Section</param>
/// <param name="Key">键</param>
/// <returns>返回的值</returns>
public string readIni(string Section, string Key)
{
StringBuilder temp = new StringBuilder(10000);
int i = GetPrivateProfileString(Section, Key, "", temp, 10000, this.iniPath);
return temp.ToString();
}
/// <summary>
/// 读取section,不管section,默认从default里读取
/// </summary>
/// <param name="Key">键</param>
/// <returns>返回值</returns>
public string readIni(string Key)
{
return readIni("default", Key);
}
/// <summary>
/// 查询ini文件是否存在
/// </summary>
/// <returns>是否存在</returns>
public bool existINIFile()
{
return File.Exists(iniPath);
}
}
//Sample for simple winform
using NPOI.SS.UserModel;
using System;
using System.Windows.Forms;
namespace OpicsPlus
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
IniFile ini = new IniFile(Application.StartupPath + @"\FINCTR1.INI");
string importExcelPath = Application.StartupPath + @"\TEST.XLS";
IWorkbook workbook = WorkbookFactory.Create(importExcelPath);
ISheet sheet = workbook.GetSheetAt(workbook.GetSheetIndex("EXPORT"));
int rowCount = sheet.LastRowNum;
for (int i=0; i < rowCount;i++)
{
IRow headerRow = (IRow)sheet.GetRow(i+1);
String refBack = ini.readIni(headerRow.GetCell(1).StringCellValue, "SH");
ini.writeIni(headerRow.GetCell(1).StringCellValue, "SH", refBack + " "+ headerRow.GetCell(0).NumericCellValue.ToString());
}
reArrStr("SHANGHAI");
reArrStr("FRANKFURT");
reArrStr("NEW YORK");
reArrStr("LONDON");
reArrStr("HONGKONG");
reArrStr("TOKYO");
MessageBox.Show("Done");
}
public void reArrStr(String sectName)
{
IniFile ini = new IniFile(Application.StartupPath + @"\FINCTR1.INI");
String refBack1 = ini.readIni(sectName, "SH");
String strTemp = null;
String[] strArr = refBack1.Split(new char[1] { ' ' });
Array.Sort(strArr);
for (int j = 0; j < strArr.Length; j++)
{
if (j == 0)
{
strTemp = strArr[j];
}
else
{
strTemp += " " + strArr[j];
}
}
ini.writeIni(sectName, "SH", strTemp);
}
}
}
后序
至此,用技术手段解决了日常工作的小麻烦,作为一个IT老兵,还是很欣慰;在具体制作过程中还有一个小插曲,在核对过程中发现有一些日期在配置文件中有,但是并没有反应在系统中,对比数据后发现原来是数据的排序问题,由于是在原有数据的追加,所以特别在写入后,把全部数据再一次性读入数组进行一次排序。