前言:
好久没有写博文了,最近有几位csdn的网友想找过来借鉴开发的管理系统,因为工作调整我已经不在公司管理干部了,整套系统当时本身就是为了方便自己工作捣鼓的,现在也没人用了。牵涉到打架java运行环境,数据库,整合企业微信登录等等,要用起来也不方便,所以干脆就重新把最核心的转换审批表到实体类,然后再转换为word,以及提取简历功能用c#写了winform程序了。
程序开源地址:
干部任免审批报转换为word: 将干部任免审批表批量转换为word格式
首先声明本人不是专业的程序员,代码是比较烂我自己也知道,纯属给大家提供一个解决问题的思路。
程序实现的主要代码:
一、Lrmx文件解析:
本质上中组部任免审批表的lrmx文件就是一个xml格式文档,只是后缀为lrmx格式,核心思想就是解析xml文件,让文件中的内容与我们自己所定义的类对应。
1. 用到的依赖包:
1.1 Nlog 及 Nlog.from 日志记录用
1.2 NPOI 用于操作word和excel的。用这个包最大的好处就是你不需要去考虑客户端是否有安装office。
1.3 System.Xml.XDocument 和 System.Xml.ReaderWriter xml的解析工具了。
2. 定义的几个核心实体类
2.1 最核心的GanBu类
public class GanBu
{
public String XingMing; //姓名 1,0,0 1,0,1
public String XingBie; //性别 1,0,2 1,0,3
public String ChuShengNianYue; //出生年月 YYYYMM格式 1,0,4 1,0,5
public String MinZu; //民族 1,1,0 1,1,1
public String JiGuan; //籍贯 1,1,2 1,1,3
public String ChuShengDi;// 出生地 1,1,4 1,1,5
public String RuDangShiJian;// 入党时间 1,2,0
public String CanJiaGongZuoShiJian;//参加工作时间 1,2,2
public String JianKangZhuangKuang;//健康状况 1,2,4
public String ZhuanYeJiShuZhiWu;//专业技术职务 1,3,0
public String ShuXiZhuanYeYouHeZhuanChang;//熟悉专业有何专长 1,3,2
public String QuanRiZhiJiaoYu_XueLi;//全日制教育学历 1,4,2
public String QuanRiZhiJiaoYu_XueWei;//全日制教育学位 1,4,2
public String QuanRiZhiJiaoYu_XueLi_BiYeYuanXiaoXi;//全日制毕业学校 1,4,4
public String QuanRiZhiJiaoYu_XueWei_BiYeYuanXiaoXi;//全日制毕业院校专业 1,5,4
public String ZaiZhiJiaoYu_XueLi;//在职教育学历 1,6,2
public String ZaiZhiJiaoYu_XueWei;//在职教育学位 1,6,2
public String ZaiZhiJiaoYu_XueLi_BiYeYuanXiaoXi;//在职教育毕业院校 1,6,4
public String ZaiZhiJiaoYu_XueWei_BiYeYuanXiaoXi;//在职教育毕业院校专业 1,7,4
public String XianRenZhiWu;//现任职务 1,8,0
public String NiRenZhiWu;//拟任职务 1,9,0
public String NiMianZhiWu;//拟免职务 1,10,0
public List<JianLi> JianLi; //简历 1,11,0
public String JiangChengQingKuang;//奖惩情况 2,0,0
public String NianDuKaoHeJieGuo;//年度考核结果 2,1,0
public String RenMianLiYou;//任免理由 2,2,0
public List<JiaTingChengYuan> JiaTingChengYuan;//家庭成员 2,4,1 称谓 2 姓名 3 年龄 4 政治面貌 5 工作单位及职务
public String ChengBaoDanWei;//承报单位
public String JiSuanNianLingShiJian;//计算年龄时间
public String TianBiaoShiJian;//填报时间
public String TianBiaoRen;//填报人
public String ShenFenZheng;//身份证号码
public String ZhaoPian;// 照片
public String Version;//版本
public int NianLing; //年龄
}
2.2 JianLi 类
cs
public class JianLi
{
public String KaiShiNianYue;
public String JieSuNianYue;
public String JingLi; //经历
}
2.3 JiaTingChengYuan 类
cs
public class JiaTingChengYuan
{
public String ChengWei;//称谓
public String XingMing;//姓名
public String ChuShengRiQi;//出生日期 YYYYMM
public String ZhengZhiMianMao;//政治面貌
public String GongZuoDanWeiJiZhiWu;//工作单位及职务
public int NianLing; //年龄
}
3 解析lrmx文件过程
代码就是读取xml文件,将对应的内容解析赋值给GanBu类
3.1 完整转换为类的代码
cs
private GanBu LrmxToClass(string filename) {
GanBu ganBu = new GanBu();
Type type = ganBu.GetType();
try {
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(filename);
XmlNodeList nl = xmlDoc.DocumentElement.ChildNodes;
foreach (XmlNode node in nl)
{
//Debug.WriteLine(node.Name);
if (node.Name != "JianLi" && node.Name != "JiaTingChengYuan")
{
FieldInfo fieldInfo = type.GetField(node.Name);
fieldInfo.SetValue(ganBu, node.InnerText);
}
if (node.Name == "JianLi")
{
List<JianLi> jianLiList = new List<JianLi>();
string jianli = node.InnerText;
int rowNums = 0;
string[] JianLiArrayTmp = jianli.Split('\n');
List<String> JianLiArray = new List<string>();
foreach (string s in JianLiArrayTmp)
{
if (s.Replace("\n", "").Replace("\r","").Trim() != "")
{
JianLiArray.Add(s.Replace("\r",""));
}
}
foreach (string line in JianLiArray)
{
if (line.Trim() != "")
{
if (!Tools.isStartWithNumber(line.Trim()))
{
jianLiList[rowNums - 1].JingLi = jianLiList[rowNums - 1].JingLi + " " + line.Trim();
}
else
{
JianLi jl = new JianLi();
String[] partArray = Regex.Split(line, " ");
int tmpNum = 0;
String jingli = "";
foreach (string s in partArray)
{
if (tmpNum == 0)
{
//第一列分割的用来做时间拆分
string[] a = Regex.Split(s, "--|------");
if (a.Length == 1)
{
a = Regex.Split(s, "-|---");
}
jl.KaiShiNianYue = a[0].Trim().Replace(" ","");
if (a.Length == 1)
{
jl.JieSuNianYue = "";
}
else
{
jl.JieSuNianYue += a[1].Trim().Replace(" ", "");
}
tmpNum++;
}
else
{
jingli += s.Trim();
}
}
jl.JingLi = jingli;
jianLiList.Add(jl);
rowNums++;
}
}
}
ganBu.JianLi = jianLiList;
}
if (node.Name == "JiaTingChengYuan")
{
XmlNodeList node2list = node.ChildNodes;
List<JiaTingChengYuan> jtcyList = new List<JiaTingChengYuan>();
foreach (XmlNode node2 in node2list)
{
JiaTingChengYuan jtcy = new JiaTingChengYuan();
XmlNodeList node3List = node2.ChildNodes;
Type t2 = jtcy.GetType();
foreach (XmlNode node3 in node3List)
{
FieldInfo fi = t2.GetField(node3.Name);
fi.SetValue(jtcy, node3.InnerText.Trim());
}
jtcy.ChuShengRiQi = jtcy.ChuShengRiQi.Trim().Replace("\n", "").Replace("-", "");
if (jtcy.ChuShengRiQi.Length == 6 || jtcy.ChuShengRiQi.Length == 8)
{
jtcy.NianLing = Tools.JiSuanNianLing(jtcy.ChuShengRiQi, Tools.NullToEmpty(ganBu.JiSuanNianLingShiJian), Tools.NullToEmpty(ganBu.TianBiaoShiJian));
}
jtcyList.Add(jtcy);
}
ganBu.JiaTingChengYuan = jtcyList;
}
}
//20240718 增加身份证提取出生日期
if (ganBu.ChuShengNianYue.Length == 6 || ganBu.ChuShengNianYue.Length == 8)
{
ganBu.NianLing = Tools.JiSuanNianLing(ganBu.ChuShengNianYue, Tools.NullToEmpty(ganBu.JiSuanNianLingShiJian), Tools.NullToEmpty(ganBu.TianBiaoShiJian));
}
else
{
if (!string.IsNullOrEmpty(ganBu.ShenFenZheng))
{
ganBu.ChuShengNianYue = Tools.GetDate(ganBu.ShenFenZheng);
ganBu.NianLing = Tools.JiSuanNianLing(ganBu.ChuShengNianYue, Tools.NullToEmpty(ganBu.JiSuanNianLingShiJian), Tools.NullToEmpty(ganBu.TianBiaoShiJian));
}
}
logger.Info(filename + "转换为类成功!");
return ganBu;
}catch(Exception ex)
{
logger.Error(ex.ToString());
logger.Error(filename + "转换为类失败!");
return ganBu;
}
}
3.2 转换过程中一些要点:
3.2.1 对于如何处理简历我的实现思路如下: 因为一条简历有可能会分行的,首先把简历的内容按行存到数组中,如果这行的内容不是数字开头(因为一条简历都是以年份开始的)那么这行就不是一条新的简历,而是一条简历分行了。如果再严格点可以取前四位是否为数字。
3.2.2 对于干部年龄的计算,如果有填写计算年龄时间那么就用该字段取,如果没有取填表时间,如果都没有取当前时间。如果没有填写出生日期,那么看是否有填写身份证号码。
二、 转换为word过程:
转换为word的过程采用的是按照模板来进行内容填充,本来想采用占位符替换的方式,发现有些问题,后来决定还是老老实实通过代码来实现。 审批表核心部分内容实际是一个大大表格,那么就可以按照excel单元格填充的思路来做处理了。
1. 完整代码
cs
try
{
GanBu ganBu = LrmxToClass(filename);
string[] tmpArr = filename.Split('\\');
string newFileName = tmpArr[tmpArr.Length - 1];
string filePath = Path.GetDirectoryName(filename);
String targetFilePath = ".\\docx\\" + filePath.Substring(3, filePath.Length - 3) + "\\" + newFileName.Substring(0, newFileName.Length -5) + "-任免审批表.docx";
if (File.Exists(targetFilePath))
{
File.Delete(targetFilePath);
}
XWPFDocument docx;
using (FileStream stream = File.OpenRead(".\\spbmb.docx"))
{
docx = new XWPFDocument(stream);
}
foreach (XWPFParagraph ph in docx.Paragraphs)
{
foreach (XWPFRun r in ph.Runs)
{
if (r.Text == "填表人:")
{
r.SetText("填表人:" + ganBu.TianBiaoRen);
}
}
}
if (!string.IsNullOrEmpty(ganBu.ZhaoPian.Replace("\r", "").Replace("\n", "")))
{
XWPFTableCell imageCell = docx.Tables[1].Rows[0].GetTableCells()[6];
XWPFParagraph pg = imageCell.Paragraphs[0];
XWPFRun run = pg.CreateRun();
byte[] b = Convert.FromBase64String(ganBu.ZhaoPian);
InputStream sbs = new ByteArrayInputStream(b);
run.AddPicture(new ByteArrayInputStream(b), (int)NPOI.SS.UserModel.PictureType.JPEG, "", 363 * 3600, 490 * 3600);
}
Boolean ifFirst = true;
int num = 0;
foreach (XWPFTable table in docx.Tables)
{
for (int i = 0; i < table.Rows.Count; i++)
{
for (int j = 0; j < table.Rows[i].GetTableCells().Count; j++)
{
switch (table.Rows[i].GetTableCells()[j].GetText().Trim().Replace(" ", "").Replace("\r\n",""))
{
case "姓名":
if (num == 1)
{
table.Rows[i].GetTableCells()[j + 1].SetText(ganBu.XingMing);
}
break;
case "性别":
table.Rows[i].GetTableCells()[j + 1].SetText(ganBu.XingBie);
break;
case "出生年月(岁)":
table.Rows[i].GetTableCells()[j + 1].SetText(ganBu.ChuShengNianYue.Substring(0, 4) + "." + ganBu.ChuShengNianYue.Substring(4, 2) + "(" + ganBu.NianLing + "岁)");
break;
case "民族":
table.Rows[i].GetTableCells()[j + 1].SetText(ganBu.MinZu);
break;
case "籍贯":
table.Rows[i].GetTableCells()[j + 1].SetText(ganBu.JiGuan);
break;
case "出生地":
table.Rows[i].GetTableCells()[j + 1].SetText(ganBu.ChuShengDi);
break;
case "入党时间":
table.Rows[i].GetTableCells()[j + 1].SetText(ganBu.RuDangShiJian);
break;
case "参加工作时间":
table.Rows[i].GetTableCells()[j + 1].SetText(ganBu.CanJiaGongZuoShiJian);
break;
case "健康状况":
table.Rows[i].GetTableCells()[j + 1].SetText(ganBu.JianKangZhuangKuang);
break;
case "专业技术职务":
table.Rows[i].GetTableCells()[j + 1].SetText(ganBu.ZhuanYeJiShuZhiWu);
break;
case "熟悉专业有何专长":
table.Rows[i].GetTableCells()[j + 1].SetText(ganBu.ShuXiZhuanYeYouHeZhuanChang);
break;
case "全日制教育":
XWPFParagraph pg = table.Rows[i].GetTableCells()[j + 1].Paragraphs[0];
XWPFRun run = pg.CreateRun();
run.SetText(ganBu.QuanRiZhiJiaoYu_XueLi);
run.AddBreak();
run.SetText(ganBu.QuanRiZhiJiaoYu_XueWei);
break;
case "毕业院校系及专业":
if (ifFirst)
{
XWPFParagraph pg2 = table.Rows[i].GetTableCells()[j + 1].Paragraphs[0];
XWPFRun run2 = pg2.CreateRun();
run2.SetText(ganBu.QuanRiZhiJiaoYu_XueLi_BiYeYuanXiaoXi);
run2.AddBreak();
run2.SetText(ganBu.QuanRiZhiJiaoYu_XueWei_BiYeYuanXiaoXi);
ifFirst = false;
}
else
{
XWPFParagraph pg3 = table.Rows[i].GetTableCells()[j + 1].Paragraphs[0];
XWPFRun run3 = pg3.CreateRun();
run3.SetText(ganBu.ZaiZhiJiaoYu_XueLi_BiYeYuanXiaoXi);
run3.AddBreak();
run3.SetText(ganBu.ZaiZhiJiaoYu_XueWei_BiYeYuanXiaoXi);
ifFirst = true;
}
break;
case "在职教育":
XWPFParagraph pg4 = table.Rows[i].GetTableCells()[j + 1].Paragraphs[0];
XWPFRun run4 = pg4.CreateRun();
run4.SetText(ganBu.ZaiZhiJiaoYu_XueLi);
run4.AddBreak();
run4.SetText(ganBu.ZaiZhiJiaoYu_XueWei);
break;
case "现任职务":
table.Rows[i].GetTableCells()[j + 1].SetText(ganBu.XianRenZhiWu);
break;
case "拟任职务":
table.Rows[i].GetTableCells()[j + 1].SetText(ganBu.NiRenZhiWu);
break;
case "拟免职务":
table.Rows[i].GetTableCells()[j + 1].SetText(ganBu.NiMianZhiWu);
break;
case "简历":
int lastLine = 1;
XWPFParagraph pg5 = table.Rows[i].GetTableCells()[j + 1].Paragraphs[0];
XWPFRun run5 = pg5.CreateRun();
foreach (JianLi jl in ganBu.JianLi)
{
if (lastLine != ganBu.JianLi.Count)
{
if (ifFirst)
{
run5.SetText(jl.KaiShiNianYue + "--" + jl.JieSuNianYue + " " + jl.JingLi);
ifFirst = false;
}
else
{
table.Rows[i].GetTableCells()[j + 1].AddParagraph().CreateRun().SetText(jl.KaiShiNianYue + "--" + jl.JieSuNianYue + " " + jl.JingLi);
}
lastLine++;
}
else
{
table.Rows[i].GetTableCells()[j + 1].AddParagraph().CreateRun().SetText(jl.KaiShiNianYue + "--" + " " + jl.JieSuNianYue + " " + jl.JingLi);
}
}
break;
case "奖惩情况":
table.Rows[i].GetTableCells()[j + 1].SetText(ganBu.JiangChengQingKuang);
break;
case "年核度结考果":
table.Rows[i].GetTableCells()[j + 1].SetText(ganBu.NianDuKaoHeJieGuo);
break;
case "任免理由":
table.Rows[i].GetTableCells()[j + 1].SetText(ganBu.RenMianLiYou);
break;
case "家庭主要成员及重要社会关系":
List<JiaTingChengYuan> jtcyList = ganBu.JiaTingChengYuan;
for (int m = 0; m < jtcyList.Count(); m++)
{
XWPFTableRow tr = table.Rows[i + m + 1];
tr.GetTableCells()[1].SetText(jtcyList[m].ChengWei);
tr.GetTableCells()[2].SetText(jtcyList[m].XingMing);
tr.GetTableCells()[3].SetText(jtcyList[m].NianLing.ToString());
tr.GetTableCells()[4].SetText(jtcyList[m].ZhengZhiMianMao);
tr.GetTableCells()[5].SetText(jtcyList[m].GongZuoDanWeiJiZhiWu);
}
break;
case "呈报单位":
table.Rows[i].GetTableCells()[j + 1].SetText(ganBu.ChengBaoDanWei);
break;
case "行任政免机意关见":
table.Rows[i].GetTableCells()[j + 1].SetText(" 同意 " );
break;
}
}
}
num++;
}
System.IO.FileStream output = new System.IO.FileStream(targetFilePath, System.IO.FileMode.Create, System.IO.FileAccess.ReadWrite, System.IO.FileShare.ReadWrite);
//写入文件
docx.Write(output);
output.Close();
output.Dispose();
logger.Info(targetFilePath + "文件生成成功!");
}
catch (Exception ex)
{
logger.Error(filename + "转换不成功");
logger.Error(ex.ToString());
}
2 核心思路和注意点
2.1 需要填充内容位置判断:
通过遍历表格中的文字内容来判断,填充的位置就是文字所在单元格位置横向+1 了。
2.2 需要注意点:
年度考核结果实际在解析模板中对应字符串为 "年核度结考果", 同样的还有行政任免机关意见对应为"行任政免机意关见" ,这个是个坑。
总结:
有些点说通了,会发现实现起来就没什么难度了。批量转换以及子目录这些我就不阐述了。 东西写得也比较粗,如果有需要的朋友可以留言给我,一起完善这个软件了。