haogh 11 months ago
parent
commit
6a91773057
100 changed files with 9846 additions and 0 deletions
  1. 44 0
      ses-scan/pom.xml
  2. 127 0
      ses-scan/src/main/java/cn/hmsoft/frame/control/FrameLoginControl.java
  3. 183 0
      ses-scan/src/main/java/cn/hmsoft/frame/module/FrameModuleUtil.java
  4. 121 0
      ses-scan/src/main/java/cn/hmsoft/frame/timer/FrameTimerConfig.java
  5. 198 0
      ses-scan/src/main/java/cn/hmsoft/frame/timer/FrameTimerUtil.java
  6. 48 0
      ses-scan/src/main/java/cn/hmsoft/frame/timer/TimerModuleConfig.java
  7. 95 0
      ses-scan/src/main/java/cn/hmsoft/scan/common/AppConfig.java
  8. 113 0
      ses-scan/src/main/java/cn/hmsoft/scan/common/aspect/OperateLogAspect.java
  9. 26 0
      ses-scan/src/main/java/cn/hmsoft/scan/common/codegen/CodeGenerateMain.java
  10. 53 0
      ses-scan/src/main/java/cn/hmsoft/scan/common/codegen/ftl/control.ftl
  11. 35 0
      ses-scan/src/main/java/cn/hmsoft/scan/common/codegen/ftl/dao.ftl
  12. 38 0
      ses-scan/src/main/java/cn/hmsoft/scan/common/codegen/ftl/po.ftl
  13. 17 0
      ses-scan/src/main/java/cn/hmsoft/scan/common/codegen/ftl/service.ftl
  14. 38 0
      ses-scan/src/main/java/cn/hmsoft/scan/common/codegen/ftl/serviceImpl.ftl
  15. 173 0
      ses-scan/src/main/java/cn/hmsoft/scan/common/codegen/support/CodeGenJdbc.java
  16. 49 0
      ses-scan/src/main/java/cn/hmsoft/scan/common/codegen/support/SesGenerateCode.java
  17. 29 0
      ses-scan/src/main/java/cn/hmsoft/scan/common/constants/ApplySourceConst.java
  18. 37 0
      ses-scan/src/main/java/cn/hmsoft/scan/common/constants/MatchStatusConst.java
  19. 43 0
      ses-scan/src/main/java/cn/hmsoft/scan/common/constants/SysConst.java
  20. 65 0
      ses-scan/src/main/java/cn/hmsoft/scan/control/by/ByScanStdControl.java
  21. 52 0
      ses-scan/src/main/java/cn/hmsoft/scan/control/by/FtpFileStatControl.java
  22. 44 0
      ses-scan/src/main/java/cn/hmsoft/scan/control/by/StdMatchControl.java
  23. 58 0
      ses-scan/src/main/java/cn/hmsoft/scan/data/dao/by/ByApplyHisDao.java
  24. 11 0
      ses-scan/src/main/java/cn/hmsoft/scan/data/dao/by/ByScanJsonDao.java
  25. 73 0
      ses-scan/src/main/java/cn/hmsoft/scan/data/dao/by/ByScanStdDao.java
  26. 11 0
      ses-scan/src/main/java/cn/hmsoft/scan/data/dao/by/ByScanStdJsonDao.java
  27. 16 0
      ses-scan/src/main/java/cn/hmsoft/scan/data/dao/by/ByStdFilesStatDao.java
  28. 16 0
      ses-scan/src/main/java/cn/hmsoft/scan/data/dao/cf/CfMajorDao.java
  29. 17 0
      ses-scan/src/main/java/cn/hmsoft/scan/data/dao/cf/CfScanRuleDao.java
  30. 98 0
      ses-scan/src/main/java/cn/hmsoft/scan/data/model/by/ByApplyHis.java
  31. 61 0
      ses-scan/src/main/java/cn/hmsoft/scan/data/model/by/ByScanJson.java
  32. 267 0
      ses-scan/src/main/java/cn/hmsoft/scan/data/model/by/ByScanStd.java
  33. 49 0
      ses-scan/src/main/java/cn/hmsoft/scan/data/model/by/ByScanStdJson.java
  34. 81 0
      ses-scan/src/main/java/cn/hmsoft/scan/data/model/by/ByStdFilesStat.java
  35. 36 0
      ses-scan/src/main/java/cn/hmsoft/scan/data/model/cf/CfMajor.java
  36. 66 0
      ses-scan/src/main/java/cn/hmsoft/scan/data/model/cf/CfScanRule.java
  37. 33 0
      ses-scan/src/main/java/cn/hmsoft/scan/data/vo/FileVo.java
  38. 23 0
      ses-scan/src/main/java/cn/hmsoft/scan/service/iface/by/IByScanStdService.java
  39. 11 0
      ses-scan/src/main/java/cn/hmsoft/scan/service/iface/by/IByStdFilesStatService.java
  40. 16 0
      ses-scan/src/main/java/cn/hmsoft/scan/service/iface/by/IStdMatchService.java
  41. 10 0
      ses-scan/src/main/java/cn/hmsoft/scan/service/iface/by/IStdPicOcrService.java
  42. 569 0
      ses-scan/src/main/java/cn/hmsoft/scan/service/impl/by/ByScanStdServiceImpl.java
  43. 137 0
      ses-scan/src/main/java/cn/hmsoft/scan/service/impl/by/ByStdFilesStatImpl.java
  44. 133 0
      ses-scan/src/main/java/cn/hmsoft/scan/service/impl/by/StdMatchServiceImp.java
  45. 289 0
      ses-scan/src/main/java/cn/hmsoft/scan/service/impl/by/StdPicOcrServiceImpl.java
  46. 85 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/CacheUtil.java
  47. 112 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/ChineseDateUtil.java
  48. 40 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/CommonToolUtil.java
  49. 47 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/CommonUtils.java
  50. 121 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/DbfUtil.java
  51. 58 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/FmtMicrometer.java
  52. 702 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/FrameIdCardUtil.java
  53. 296 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/GoogleBarCodeUtils.java
  54. 168 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/HandwritingUtil.java
  55. 34 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/HttpResult.java
  56. 370 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/HttpUtil.java
  57. 264 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/ImageUtil.java
  58. 105 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/JdbcEncryptHelper.java
  59. 39 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/MainTest.java
  60. 30 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/PatternUtil.java
  61. 63 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/PdfWatermarkUtil.java
  62. 77 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/PicToPdfUtil.java
  63. 141 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/QRCodeUtil.java
  64. 160 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/RegexUtil.java
  65. 43 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/SSLClient.java
  66. 57 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/ScanFileHelper.java
  67. 72 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/SesFileHelper.java
  68. 111 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/VerifyCode.java
  69. 240 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/ZipUtil.java
  70. 66 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/baidu/Base64Util.java
  71. 72 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/baidu/FileUtil.java
  72. 29 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/baidu/GsonUtils.java
  73. 74 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/baidu/HttpUtil.java
  74. 109 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/baidu/auth/BaiduAuthService.java
  75. 73 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/baidu/doc/DocAnalysis.java
  76. 92 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/baidu/doc/Handwriting.java
  77. 18 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/dbf/DBFBase.java
  78. 16 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/dbf/DBFException.java
  79. 110 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/dbf/DBFFactory.java
  80. 265 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/dbf/DBFField.java
  81. 134 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/dbf/DBFHeader.java
  82. 304 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/dbf/DBFReader.java
  83. 349 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/dbf/DBFWriter.java
  84. 321 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/dbf/Utils.java
  85. 23 0
      ses-scan/src/main/java/cn/hmsoft/scan/util/job/StdMatchJob.java
  86. 287 0
      ses-scan/src/main/java/cn/hmsoft/web/config/WebConfig.java
  87. 40 0
      ses-scan/src/main/java/cn/hmsoft/web/file/FileConsumer.java
  88. 55 0
      ses-scan/src/main/java/cn/hmsoft/web/file/FileInitThread.java
  89. 45 0
      ses-scan/src/main/java/cn/hmsoft/web/file/FileListener.java
  90. 48 0
      ses-scan/src/main/java/cn/hmsoft/web/file/FileMonitorStart.java
  91. 116 0
      ses-scan/src/main/java/cn/hmsoft/web/filter/LoginFilter.java
  92. 31 0
      ses-scan/src/main/java/cn/hmsoft/web/thread/StdPicOcrThread.java
  93. 5 0
      ses-scan/src/main/resources/config.properties
  94. 6 0
      ses-scan/src/main/resources/gen.code.properties
  95. 10 0
      ses-scan/src/main/resources/jdbc.properties
  96. 0 0
      ses-scan/src/main/webapp/d2-admin/css/app.d5a1ea54.css
  97. 1 0
      ses-scan/src/main/webapp/d2-admin/css/chunk-02d6.c8955061.css
  98. 1 0
      ses-scan/src/main/webapp/d2-admin/css/chunk-0394.0acedd11.css
  99. 1 0
      ses-scan/src/main/webapp/d2-admin/css/chunk-040b.c0f60328.css
  100. 1 0
      ses-scan/src/main/webapp/d2-admin/css/chunk-048a.00b07696.css

+ 44 - 0
ses-scan/pom.xml

@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>cn.hmsoft</groupId>
+    <artifactId>ses.parent</artifactId>
+    <version>1.0</version>
+  </parent>
+  <groupId>cn.hmsoft</groupId>
+  <artifactId>ses-scan</artifactId>
+  <version>1.0</version>
+  <packaging>war</packaging>
+  <name>ses-scan</name>
+  <dependencies>
+  <!--
+    <dependency>
+      <groupId>cn.hmsoft</groupId>
+      <artifactId>ses-model-v1</artifactId>
+      <version>1.0</version>
+    </dependency>-->
+    <!--字符繁体简体转换-->
+	<dependency>
+		<groupId>com.github.houbb</groupId>
+		<artifactId>opencc4j</artifactId>
+		<version>1.1.0</version>
+	</dependency>
+	<!-- 对PDF文件的操作 -->
+	<dependency>
+	    <groupId>com.itextpdf</groupId>
+	    <artifactId>itextpdf</artifactId>
+	    <version>5.5.13.1</version>
+	</dependency>
+	<!-- PDF文件 字体 防止中文乱码 -->
+	<dependency>
+	    <groupId>com.itextpdf</groupId>
+	    <artifactId>itext-asian</artifactId>
+	    <version>5.2.0</version>
+	</dependency>
+  </dependencies>
+  <build>
+    <finalName>ses-scan</finalName>
+  </build>
+</project>

+ 127 - 0
ses-scan/src/main/java/cn/hmsoft/frame/control/FrameLoginControl.java

@@ -0,0 +1,127 @@
+package cn.hmsoft.frame.control;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.hmsoft.frame.constants.FrameStatus;
+import cn.hmsoft.frame.data.model.FrameOptr;
+import cn.hmsoft.frame.data.model.FrameRes;
+import cn.hmsoft.frame.data.model.FrameRole;
+import cn.hmsoft.frame.exception.web.BusinessException;
+import cn.hmsoft.frame.exception.web.MvcParamException;
+import cn.hmsoft.frame.exception.web.WebException;
+import cn.hmsoft.frame.service.IFrameLoginService;
+import cn.hmsoft.frame.service.IFrameOptrService;
+import cn.hmsoft.frame.service.IFrameResService;
+import cn.hmsoft.frame.service.IFrameRoleService;
+import cn.hmsoft.frame.util.FrameAssertUtil;
+import cn.hmsoft.frame.util.RequestContextUtil;
+import cn.hmsoft.helper.LogHelper;
+import cn.hmsoft.helper.SecureHelper;
+import cn.hmsoft.helper.StringHelper;
+import cn.hmsoft.web.config.SpringConfig;
+import cn.hmsoft.web.control.AjaxControl;
+import cn.hmsoft.web.entity.Ajax;
+
+@RestController
+public class FrameLoginControl extends AjaxControl {
+
+	@Autowired
+	private IFrameOptrService frameOptrService;
+	@Autowired
+	private IFrameRoleService frameRoleService;
+	@Autowired
+	private IFrameResService frameResService;
+	@Autowired
+	private IFrameLoginService frameLoginService;
+
+	@RequestMapping({ "frame/optr/login" })
+	@ResponseBody
+	public Ajax login(String loginName, String loginPass, String loginCode, String login_url) {
+		final Ajax ajax = new Ajax();
+		try {
+			if (StringHelper.isEmpty(loginName) || StringHelper.isEmpty(loginPass)) {
+				throw new MvcParamException();
+			}
+//			if (FrameParamConstants.AppDebug || !FrameParamUtil.FrameLoginValidCode) {
+//
+//			} else if (!FrameRandomCodeUtil.validRandomCode(this.getRequest(), loginCode)) {
+//				throw new BusinessException("验证码错误,请重试");
+//			}
+			final FrameOptr optr = frameOptrService.find(FrameOptr.class, "login_name", loginName);
+			FrameAssertUtil.isNotNull(optr, "登录失败:不存在登录名称为[" + loginName + "]的操作员");
+			if ((StringHelper.isEmpty(optr.getLogin_pass())) || (optr.getOptr_role() == null)) {
+				throw new BusinessException("登录失败:该操作员未初始化,请联系系统管理员");
+			}
+			final FrameRole role = (FrameRole) frameRoleService.getFrameRole(optr.getOptr_role());
+			if (optr.getOptr_id().intValue() != 0) {
+				FrameAssertUtil.isNotNull(role, "未找到该操作员所属的角色");
+				FrameAssertUtil.isEqual(role.getRole_status(), FrameStatus.Active.toString(), "登录失败:该操作员所属角色已经被禁用");
+			}
+			FrameAssertUtil.isEqual(SecureHelper.md5(loginPass), optr.getLogin_pass(), "登录失败:该操作员登录密码错误");
+			FrameAssertUtil.isEqual(optr.getOptr_status(), FrameStatus.Active.toString(), "登录失败:该操作员登录已经被禁用");
+			if (role != null) {
+				optr.setHome_page(role.getHome_page());
+				optr.setRole_name(role.getRole_name());
+//				optr.setThread_max(role.getThread_max());
+			}
+			List<FrameRes> list = null;
+			if (optr.getOptr_role() - 0 == 0)
+				list = this.frameResService.findFrameResList(0, null);
+			else
+				list = this.frameResService.findFrameResList(optr.getOptr_role(), null);
+			FrameAssertUtil.hasValue(list, "登录失败:该操作员未初始化,请联系系统管理员");
+
+			optr.setResMap(new HashMap<Integer, Integer>());
+			for (FrameRes res : list) {
+				optr.getResMap().put(res.getRes_id(), res.getRes_id());
+			}
+			// log
+			frameLoginService.optrLogin(optr, this.getRequest());
+			RequestContextUtil.setCurrentLoginUser(optr);
+			ajax.setLogin(true);
+			ajax.setSuccess(true);
+		} catch (WebException e) {
+			ajax.setLogin(false);
+			ajax.setSuccess(false);
+			ajax.setErrorCode(e.getErr_code());
+			ajax.setErrorMsg(e.getMessage());
+		}
+		return ajax;
+	}
+
+	@RequestMapping("frame/optr/logout")
+	public Ajax logout() {
+		this.getSession().invalidate();
+		return new Ajax();
+	}
+	
+	
+	@RequestMapping("frame/template/download")
+	public void download(String file_name) {
+		File file = new File(SpringConfig.WebServletContext.getRealPath("/") + "template" + File.separator + file_name);
+		LogHelper.info(file.getPath());
+		if (file.exists())
+			// this.writeAlert("未找到模版文件,请联系系统管理员");
+			this.downloadFile(file, file_name);
+		else
+			this.writeAlert("未找到模版文件,请联系系统管理员");
+	}
+	@RequestMapping("frame/template/temp/download")
+	public void downloadTemp(String file_name) {
+		File file = new File(SpringConfig.WebServletContext.getRealPath("/") + "template" + File.separator + "temp"
+				+ File.separator + file_name);
+		if (file.exists())
+			// this.writeAlert("未找到模版文件,请联系系统管理员");
+			this.downloadFile(file, file_name,true);
+		else
+			this.writeAlert("未找到模版文件,请联系系统管理员");
+	}
+
+}

+ 183 - 0
ses-scan/src/main/java/cn/hmsoft/frame/module/FrameModuleUtil.java

@@ -0,0 +1,183 @@
+package cn.hmsoft.frame.module;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import cn.hmsoft.frame.constants.FrameParamConstants;
+import cn.hmsoft.frame.constants.FrameStatus;
+import cn.hmsoft.frame.data.dao.FrameModuleDao;
+import cn.hmsoft.frame.data.model.FrameModule;
+import cn.hmsoft.helper.LogHelper;
+import cn.hmsoft.helper.StringHelper;
+import cn.hmsoft.web.config.SpringConfig;
+
+/**********************
+ * 模块化组件加载工具
+ * 
+ * @author zxq
+ * @create 2018-05-10 13:07:53
+ * @version 5.0.0
+ * @email: revisit@126.com
+ * @Company: www.hmsoft.cn
+ */
+public final class FrameModuleUtil {
+
+	
+	private static List<FrameModuleConfig> FrameModuleConfigArray = null;
+
+	private static FrameModuleUtil util = null;
+
+	private FrameModuleUtil() {
+		FrameModuleConfigArray = new ArrayList<FrameModuleConfig>();
+	}
+
+	public static FrameModuleUtil getInstance() {
+		if (util == null) {
+			synchronized (FrameModuleUtil.class) {
+				if (util == null)
+					util = new FrameModuleUtil();
+			}
+		}
+		return util;
+	}
+
+	/************************
+	 * 加载数据所有模块
+	 */
+	public void loadModule() {
+		synchronized (FrameModuleUtil.class) {
+			FrameModuleConfigArray = new ArrayList<FrameModuleConfig>();
+			try {
+				for (FrameModule module : SpringConfig.getBean(FrameModuleDao.class).listModule(FrameParamConstants.AppName)) {
+					if (module.getModule_status().equals(FrameStatus.Active.toString())) {
+						FrameModuleConfig config = this.loadModule(module);
+						if (config != null) {
+							FrameModuleConfigArray.add(config);
+							LogHelper.log("frame module [" + module.getModule_name() + "] load complete,version ["
+									+ module.getModule_version() + "]");
+						}
+					}
+				}
+			} catch (Exception ex) {
+
+			}
+		}
+
+	}
+
+	/***************************************
+	 * 加载一个模块
+	 * 
+	 * @param module
+	 */
+	public FrameModuleConfig loadModule(FrameModule module) {
+		if (module == null)
+			return null;
+		if (module.getModule_id() == null) {
+			LogHelper.warn("frame module [" + module.getModule_name() + "] load error:module id not config");
+			return null;
+		}
+		if (StringHelper.isEmpty(module.getModule_name()) || StringHelper.isEmpty(module.getModule_version())) {
+			LogHelper.warn("frame module [" + module.getModule_name() + "] load error:module param not config");
+			return null;
+		}
+		FrameModuleConfig loader = null;
+		synchronized (FrameModuleUtil.class) {
+			try {
+				loader = (FrameModuleConfig) Class.forName(module.getModule_class()).newInstance();
+			} catch (Exception e) {
+				LogHelper.warn(e.getMessage());
+				LogHelper.warn("frame module [" + module.getModule_name() + "] load error:module class config error");
+			}
+			if (loader == null)
+				return null;
+
+			if (loader.load(module))
+				return loader;
+			return null;
+		}
+	}
+
+	/*************************
+	 * 重启一个模块
+	 */
+	public boolean reloadModule(FrameModule module) {
+
+		if (module == null)
+			return false;
+		if (module.getModule_id() == null) {
+			LogHelper.warn("frame module [" + module.getModule_name() + "] load error:module id not config");
+			return false;
+		}
+		if (StringHelper.isEmpty(module.getModule_name()) || StringHelper.isEmpty(module.getModule_version())) {
+			LogHelper.warn("frame module [" + module.getModule_name() + "] load error:module param not config");
+			return false;
+		}
+
+		synchronized (FrameModuleUtil.class) {
+			FrameModuleConfig loader = null;
+			for (FrameModuleConfig config : FrameModuleConfigArray) {
+				if (config.getModule().getModule_id() - module.getModule_id() == 0) {
+					loader = config;
+					break;
+				}
+			}
+			// 之前没启动过
+			if (loader == null) {
+				if (module.getModule_status().equals(FrameStatus.Active.toString())) 
+					return false;
+				loader = this.loadModule(module);
+				if (loader != null) {
+					if (FrameModuleConfigArray == null)
+						FrameModuleConfigArray = new ArrayList<FrameModuleConfig>();
+					FrameModuleConfigArray.add(loader);
+					return true;
+				}
+				return false;
+
+			} else {
+				try {
+					if (module.getModule_status().equals(FrameStatus.Active.toString())) 
+						return loader.reload();
+					else
+						return loader.stop();
+				} catch (Exception e) {
+
+				}
+				return false;
+
+			}
+		}
+	}
+
+	/************************
+	 * 重新加载所有已经运行模块
+	 */
+	public void reloadModuleFromRunning() {
+		synchronized (FrameModuleUtil.class) {
+			for (FrameModuleConfig config : FrameModuleConfigArray) {
+				try {
+					config.reload();
+				} catch (Exception e) {
+
+				}
+			}
+		}
+	}
+
+	/************************
+	 * 从数据库重新加载
+	 */
+	public void reloadModuleFomConfig() {
+		synchronized (FrameModuleUtil.class) {
+			for (FrameModuleConfig config : FrameModuleConfigArray) {
+				try {
+					config.stop();
+				} catch (Exception e) {
+
+				}
+			}
+			this.loadModule();
+		}
+	}
+}

+ 121 - 0
ses-scan/src/main/java/cn/hmsoft/frame/timer/FrameTimerConfig.java

@@ -0,0 +1,121 @@
+package cn.hmsoft.frame.timer;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+
+import cn.hmsoft.frame.constants.FrameStatus;
+import cn.hmsoft.frame.data.dao.FrameTimerDao;
+import cn.hmsoft.frame.data.model.FrameTimer;
+import cn.hmsoft.frame.data.model.FrameTimerLog;
+import cn.hmsoft.helper.DateHelper;
+import cn.hmsoft.helper.LogHelper;
+import cn.hmsoft.web.config.SpringConfig;
+
+/*************************
+ * 系统定时器基类,所哟定时器必须继承此类才能正常被系统初始化
+ * 
+ * @author zxq
+ * @create 2018-05-10 16:59:23
+ * @version 5.0.0
+ * @email: revisit@126.com
+ * @Company: www.hmsoft.cn
+ */
+public abstract class FrameTimerConfig implements Job {
+
+	protected FrameTimer timer;
+	protected FrameTimerLog log;
+	protected FrameTimerDao daoTimer;
+
+	/*********************
+	 * 单独运行
+	 */
+	public void execute(FrameTimer timer) {
+		new Thread(new Runnable() {
+			@Override
+			public void run() {
+				execute(timer, null);
+			}
+		}).start();
+	}
+
+	private void execute(FrameTimer timer, JobExecutionContext jobContext) {
+		daoTimer = SpringConfig.getBean(FrameTimerDao.class);
+		if (FrameStatus.Active.toString().equals(timer.getReload_param_flag()) && daoTimer != null) {
+			// 重新加载参数
+			try {
+				FrameTimer t = daoTimer.find(FrameTimer.class, timer.getTimer_id());
+				t.prepareParamMap();
+				this.timer = t;
+			} catch (Exception e) {
+
+			}
+		}
+
+		this.start();
+		try {
+			this.execute();
+		} catch (Exception e) {
+			if (this.log != null)
+				this.log.setLog_note(
+						e.getMessage() + (this.log.getLog_note() == null ? "" : "|" + this.log.getLog_note()));
+			LogHelper.error(e);
+		}
+		if (jobContext != null) {
+			Date date = jobContext.getNextFireTime();
+			if (date != null)
+				timer.setTimer_next_time(DateHelper.toLocalDateTime(date));
+		}
+		this.end();
+	}
+
+	@Override
+	public void execute(JobExecutionContext jobContext) throws JobExecutionException {
+
+		timer = (FrameTimer) jobContext.getJobDetail().getJobDataMap().get(FrameTimerUtil.TimerJobParamName);
+		this.execute(timer, jobContext);
+
+	}
+
+	/*******************
+	 * 实际执行
+	 */
+	protected abstract boolean execute() throws Exception;
+
+	/*********************
+	 * 定时运行
+	 */
+	protected void start() {
+		if (daoTimer != null) {
+			// 如果数据可用
+			if (timer.getLog_flag().equals(FrameStatus.Active.toString())) {
+				log = new FrameTimerLog();
+				log.setTimer_id(timer.getTimer_id());
+				log.setRun_start_time(LocalDateTime.now());
+				log.setLog_param(timer.getTimer_param());
+				this.daoTimer.insert(log);
+			}
+
+			this.daoTimer.updateRunningStatus(this.timer.getTimer_id(), FrameStatus.Active);
+		}
+	}
+
+	/*********************
+	 * 本地运行结束
+	 */
+	protected void end() {
+		if (daoTimer != null) {
+			// 如果数据可用
+			if (this.log != null) {
+				log.setRun_end_time(LocalDateTime.now());
+				this.daoTimer.update(log);
+			}
+
+			this.daoTimer.updateRunningStatus(this.timer.getTimer_id(), FrameStatus.Active);
+		}
+	}
+
+}

+ 198 - 0
ses-scan/src/main/java/cn/hmsoft/frame/timer/FrameTimerUtil.java

@@ -0,0 +1,198 @@
+package cn.hmsoft.frame.timer;
+
+import java.time.LocalDateTime;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.quartz.CronExpression;
+import org.quartz.CronScheduleBuilder;
+import org.quartz.CronTrigger;
+import org.quartz.Job;
+import org.quartz.JobBuilder;
+import org.quartz.JobDetail;
+import org.quartz.JobKey;
+import org.quartz.SchedulerFactory;
+import org.quartz.TriggerBuilder;
+import org.quartz.impl.StdSchedulerFactory;
+
+import cn.hmsoft.frame.constants.FrameParamConstants;
+import cn.hmsoft.frame.constants.FrameStatus;
+import cn.hmsoft.frame.data.dao.FrameTimerDao;
+import cn.hmsoft.frame.data.model.FrameTimer;
+import cn.hmsoft.helper.DateHelper;
+import cn.hmsoft.helper.LogHelper;
+import cn.hmsoft.helper.StringHelper;
+import cn.hmsoft.web.config.SpringConfig;
+
+/*************************
+ * 系统定时器辅助类
+ * 
+ * @author zxq
+ * @create 2018-05-10 15:31:38
+ * @version 5.0.0
+ * @email: revisit@126.com
+ * @Company: www.hmsoft.cn
+ */
+public final class FrameTimerUtil {
+
+	private final static String JobGroupName = "HmSoftJobGroup";
+	private final static String JobNameStartWith = "HmSoftJobName";
+	public final static String TimerJobParamName = "TimerJobParamName";
+
+	private static SchedulerFactory JobSchedulerFactory = null;
+	private static Map<Integer, FrameTimer> TimerMap = null;
+	/*********************
+	 * 系统定时器模块是否正常启用
+	 */
+	private static boolean FrameTimerStatus = false;
+	/***************************
+	 * 数据库是否可用
+	 */
+	private static boolean TimerDatabaseFlag = false;
+	/*************************************************
+	 * 设置基本参数
+	 */
+	static {
+		synchronized (FrameTimerUtil.class) {
+			if (JobSchedulerFactory == null) {
+				Properties p = new Properties();
+				// 表明scheduler的主线程是否为守护线程
+				p.setProperty("org.quartz.scheduler.makeSchedulerThreadDaemon",
+						TimerModuleConfig.FrameTimerThreadDaemon);
+				// 使用ThreadPool的实现的名字。Quartz自带的线程池是“org.quartz.simpl.SimpleThreadPool”,几乎满足所有用户的需求。它的行为很简单,测试的也很好。它提供了固定大小的线程池,生命周期与Scheduler相同。
+				p.setProperty("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
+				// 可以是任意的正整数。实际上只有1到100会用到。这是并行执行job可用的线程数。如果只有几个job,一天也只有几次触发,那么一个线程就足够了!如果有成百上千的job,且每一个每一分钟都会触发。那么可能想要让线程的数量达到50或者100了。
+				p.setProperty("org.quartz.threadPool.threadCount", "" + TimerModuleConfig.FrameTimerThreadCount);
+				// 可以设为true,那么线程池中创建的线程都是守护线程。默认为false
+				p.setProperty("org.quartz.threadPool.makeThreadsDaemons", TimerModuleConfig.FrameTimerThreadDaemon);
+
+				TimerMap = new LinkedHashMap<Integer, FrameTimer>();
+				try {
+					// 启动任务池
+					JobSchedulerFactory = new StdSchedulerFactory(p);
+					JobSchedulerFactory.getScheduler().start();
+					FrameTimerStatus = true;
+				} catch (Exception e) {
+					LogHelper.error("frame timer module load error , please check quartz's file");
+				}
+			}
+		}
+
+	}
+
+	private static FrameTimerUtil util = null;
+
+	private FrameTimerUtil() {
+		TimerMap = new LinkedHashMap<Integer, FrameTimer>();
+	}
+
+	public static FrameTimerUtil getInstance() {
+		if (util == null) {
+			synchronized (FrameTimerUtil.class) {
+				if (util == null)
+					util = new FrameTimerUtil();
+				// 只有正常初始quartz之后,以下操作才有意义
+				if (FrameTimerStatus == true) {
+					List<FrameTimer> timerArray = SpringConfig.getBean(FrameTimerDao.class)
+							.listTimer(FrameParamConstants.AppName);
+					for (FrameTimer timer : timerArray) {
+						util.initTimer(timer);
+					}
+
+				}
+			}
+		}
+		return util;
+	}
+
+	/************************
+	 * 启动一个定时器
+	 */
+	public boolean initTimer(FrameTimer timer) {
+		if (!timer.getTimer_status().equals(FrameStatus.Active.toString())
+				|| (timer.getTimer_end_time() != null && timer.getTimer_end_time().isBefore(LocalDateTime.now())))
+			return false;
+		String timer_cron = timer.getTimer_cron();
+		if (StringHelper.isEmpty(timer_cron)) {
+			// 一次性任务,直接运行
+			FrameTimerConfig config = null;
+			try {
+				config = (FrameTimerConfig) Class.forName(timer.getTimer_class()).newInstance();
+			} catch (Exception e) {
+				// 不是FrameTimerConfig子类,无法正常初始化
+				LogHelper.error(
+						"frame timer [" + timer.getTimer_name() + "] timer_class instanct fail,please check code !");
+				return false;
+			}
+			timer.prepareParamMap();
+			// 直接执行..
+			try {
+				config.execute(timer);
+				return true;
+			} catch (Exception e) {
+				LogHelper.log(e);
+				return false;
+			}
+		} else {
+			// 定时任务
+			// 判断表达式是否正确
+			if (!CronExpression.isValidExpression(timer_cron)) {
+				LogHelper.error("frame timer [" + timer.getTimer_name() + "],cron expression [" + timer.getTimer_cron()
+						+ "] is error !");
+				return false;
+			}
+			// 初始化JOB
+			try {
+				JobKey key = new JobKey(JobNameStartWith + timer.getTimer_id(), JobGroupName);
+
+				@SuppressWarnings("unchecked")
+				JobDetail jobDetail = JobBuilder.newJob((Class<Job>) Class.forName(timer.getTimer_class()))
+						.withIdentity(key).build();
+				jobDetail.getJobDataMap().put(TimerJobParamName, timer);
+
+				LocalDateTime start_time = timer.getTimer_start_time(), end_time = timer.getTimer_end_time();
+
+				// 没有配置结束时间,加30年
+				if (end_time == null)
+					end_time = LocalDateTime.now().plusYears(30);
+				// 如果没有开始时间,10秒后开始
+				if (start_time == null)
+					start_time = LocalDateTime.now().plusSeconds(10);
+
+				CronTrigger trigger = TriggerBuilder.newTrigger()
+						.withIdentity(JobNameStartWith + timer.getTimer_id(), JobGroupName)
+						.withSchedule(CronScheduleBuilder.cronSchedule(timer_cron))
+						.startAt(DateHelper.toDate(start_time)).endAt(DateHelper.toDate(end_time)).build();
+				JobSchedulerFactory.getScheduler().scheduleJob(jobDetail, trigger);
+
+				if (trigger.getNextFireTime() != null)
+					timer.setTimer_next_time(DateHelper.toLocalDateTime(trigger.getNextFireTime()));
+				// 如果数据库可用,更新数据库
+				if (TimerDatabaseFlag)
+					SpringConfig.GobalDao.update(timer);
+				LogHelper.info("frame timer [" + timer.getTimer_name() + "] init:" + timer.getTimer_class());
+
+				TimerMap.put(timer.getTimer_id(), timer);
+				// 如果是自动启动,马上进行调度
+				if (timer.getAuto_run_flag().equals(FrameStatus.Active.toString()))
+					JobSchedulerFactory.getScheduler().triggerJob(key);
+			} catch (Exception e) {
+				LogHelper.error(e);
+				return false;
+			}
+		}
+		return true;
+	}
+	public boolean shutdown() {
+		try {
+			JobSchedulerFactory.getScheduler().shutdown();
+			return true;
+		} catch (Exception e) {
+			LogHelper.error(e);
+			return false;
+		}
+	}
+
+}

+ 48 - 0
ses-scan/src/main/java/cn/hmsoft/frame/timer/TimerModuleConfig.java

@@ -0,0 +1,48 @@
+package cn.hmsoft.frame.timer;
+
+import cn.hmsoft.frame.module.FrameModuleConfig;
+
+public class TimerModuleConfig extends FrameModuleConfig {
+
+	/*********************************
+	 * 定时器是否以守护进程模式
+	 */
+
+	public static String FrameTimerThreadDaemon = "true";
+
+	/*********************************
+	 * 定时器最大的线程数目
+	 */
+
+	public static int FrameTimerThreadCount = 100;
+
+	@Override
+	public String setModule_name() {
+		return "FrameTimer";
+	}
+
+	@Override
+	public String setModule_version() {
+		return "1.0.0";
+	}
+
+	@Override
+	protected boolean start() throws Exception {
+		FrameTimerThreadDaemon = this.getModule().getParamMap().getOrDefault("FrameTimerThreadDaemon",
+				FrameTimerThreadDaemon);
+		try {
+			FrameTimerThreadCount = Integer.parseInt(
+					this.getModule().getParamMap().getOrDefault("FrameTimerThreadCount", FrameTimerThreadCount + ""));
+		} catch (Exception e) {
+
+		}
+		FrameTimerUtil.getInstance();
+		return true;
+	}
+
+	@Override
+	protected boolean stop() throws Exception {
+		return FrameTimerUtil.getInstance().shutdown();
+	}
+
+}

+ 95 - 0
ses-scan/src/main/java/cn/hmsoft/scan/common/AppConfig.java

@@ -0,0 +1,95 @@
+package cn.hmsoft.scan.common;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.stereotype.Component;
+
+
+/**
+ * 系统配置常量类.
+ *
+ */
+@Configuration
+@PropertySource(
+  value={    
+    "classpath:config.properties"
+  },
+  ignoreResourceNotFound=true
+)
+@Component
+public class AppConfig {
+
+	/**
+	 * 考生登记表扫描目录
+	 */
+	@Value("${file.scan.dir}")
+	private String fileScanDir;
+
+	/**
+	 * 临时文件路径.
+	 */
+	@Value("${download.temp}")
+	private String downloadTemp;
+	
+	/**
+	 * ocr识别成功后,移植的目录
+	 */
+	@Value("${file.ocr.result.dir}")
+	private String resultDir;
+	
+	
+	/**
+	 * 消费者线程的数目,需要根据百度的QPS大小来配置
+	 */
+	@Value("${scan.consume.thread.num}")
+	private int consumerTheadNum;
+	
+	/**
+	 * opencv切割成图片后的 考生登记表存放目录
+	 */
+	@Value("${file.scan.opencv.dir}")
+	private String fileOpencvDir;
+	
+	
+	public String getFileScanDir() {
+		return fileScanDir;
+	}
+
+	public void setFileScanDir(String fileScanDir) {
+		this.fileScanDir = fileScanDir;
+	}
+
+	public String getDownloadTemp() {
+		return downloadTemp;
+	}
+
+	public void setDownloadTemp(String downloadTemp) {
+		this.downloadTemp = downloadTemp;
+	}
+
+	public String getResultDir() {
+		return resultDir;
+	}
+
+	public void setResultDir(String resultDir) {
+		this.resultDir = resultDir;
+	}
+
+	public int getConsumerTheadNum() {
+		return consumerTheadNum;
+	}
+
+	public void setConsumerTheadNum(int consumerTheadNum) {
+		this.consumerTheadNum = consumerTheadNum;
+	}
+
+	public String getFileOpencvDir() {
+		return fileOpencvDir;
+	}
+
+	public void setFileOpencvDir(String fileOpencvDir) {
+		this.fileOpencvDir = fileOpencvDir;
+	}
+	
+}

+ 113 - 0
ses-scan/src/main/java/cn/hmsoft/scan/common/aspect/OperateLogAspect.java

@@ -0,0 +1,113 @@
+//package cn.hmsoft.scan.common.aspect;
+//
+//import java.util.Arrays;
+//import java.util.Map;
+//import java.util.Map.Entry;
+//
+//import javax.servlet.http.HttpServletRequest;
+//
+//import org.aspectj.lang.JoinPoint;
+//import org.aspectj.lang.annotation.After;
+//import org.aspectj.lang.annotation.Aspect;
+//import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+//import org.springframework.stereotype.Component;
+//import org.springframework.web.context.request.RequestAttributes;
+//import org.springframework.web.context.request.RequestContextHolder;
+//import org.springframework.web.context.request.ServletRequestAttributes;
+//
+//import cn.hmsoft.frame.data.model.FrameOptr;
+//import cn.hmsoft.frame.util.RequestContextUtil;
+//import cn.hmsoft.web.config.SpringConfig;
+//
+///**
+// * 	暂时不用-后期用的时候打开
+// */
+//@Aspect
+//@Component("operateLogAspect")
+//public class OperateLogAspect {
+//
+//	@After("within(@org.springframework.web.bind.annotation.RestController *)")  
+//	public void after(final JoinPoint joinPoint) throws Throwable {
+//		final HttpServletRequest request = getRequest();
+//		if(request == null) {
+//			return; 
+//		}
+//		final String uri = request.getRequestURI();
+//		String methodName = joinPoint.getSignature().getName().toLowerCase();
+//		
+//		if (methodName != null && (methodName.endsWith("query") 
+//				|| methodName.endsWith("init") 
+//				|| methodName.endsWith("all") 
+//				|| methodName.endsWith("list") 
+//				|| methodName.endsWith("get") 
+//				|| methodName.endsWith("page") 
+//				|| methodName.endsWith("find") 
+//				|| methodName.endsWith("validatecode") 
+//				
+//				|| methodName.startsWith("query")
+//				|| methodName.startsWith("init")
+//				|| methodName.startsWith("all")
+//				|| methodName.startsWith("list")
+//				|| methodName.startsWith("get")
+//				|| methodName.startsWith("page")
+//				|| methodName.startsWith("find")
+//				)) {
+//			return;
+//		}
+//		
+//		final FrameOptr user = (FrameOptr) RequestContextUtil.currentLoginUser();
+//		final String remoteAddr = getIpAddr(request);
+//		final String sessionId = request.getSession() != null ? request.getSession().getId() : null;
+//		final String params = getParamString(request.getParameterMap());
+//		SpringConfig.getBean(ThreadPoolTaskExecutor.class).submit(new Runnable() {
+//
+//			@Override
+//			public void run() {
+//			}
+//			
+//		});
+//	}
+//	
+//	private String getParamString(Map<String, String[]> map) {
+//		StringBuilder sb = new StringBuilder();
+//		for (Entry<String, String[]> e : map.entrySet()) {
+//			sb.append(e.getKey()).append("=");
+//			String[] value = e.getValue();
+//			if (value != null && value.length == 1) {
+//				sb.append(value[0]).append("&");
+//			} else {
+//				sb.append(Arrays.toString(value)).append("&");
+//			}
+//		}
+//		if (sb.length() > 0) {
+//			return sb.substring(0, sb.length() - 1);
+//		}
+//		return "";
+//	}
+//	
+//	private HttpServletRequest getRequest() {
+//		RequestAttributes ra = RequestContextHolder.getRequestAttributes();  
+//		ServletRequestAttributes sra = (ServletRequestAttributes) ra;  
+//		return sra.getRequest();
+//	}
+//	
+//	/**
+//	 * 获取登录用户远程主机ip地址
+//	 * 
+//	 * @param request
+//	 * @return
+//	 */
+//	private String getIpAddr(HttpServletRequest request) {
+//		String ip = request.getHeader("x-forwarded-for");
+//		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+//			ip = request.getHeader("Proxy-Client-IP");
+//		}
+//		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+//			ip = request.getHeader("WL-Proxy-Client-IP");
+//		}
+//		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+//			ip = request.getRemoteAddr();
+//		}
+//		return ip;
+//	}
+//}

+ 26 - 0
ses-scan/src/main/java/cn/hmsoft/scan/common/codegen/CodeGenerateMain.java

@@ -0,0 +1,26 @@
+package cn.hmsoft.scan.common.codegen;
+
+import cn.hmsoft.scan.common.codegen.support.CodeGenJdbc;
+import cn.hmsoft.scan.common.codegen.support.SesGenerateCode;
+import cn.hmsoft.web.config.SpringConfig;
+
+/**
+ * 代码生成入口.
+ * 先配置gen.code.properties.
+ * @author zq
+ *
+ */
+public class CodeGenerateMain {
+
+	public static void main(String[] args) {
+		try {
+			String basePkg = "cn.hmsoft.scan";
+			String baseModelPkg = "cn.hmsoft.scan.data.model";
+			SesGenerateCode.getInstance().build(SpringConfig.class, CodeGenJdbc.class, 
+					basePkg, baseModelPkg);
+		} catch (Exception e) {
+			e.printStackTrace();
+		};
+	}
+
+}

+ 53 - 0
ses-scan/src/main/java/cn/hmsoft/scan/common/codegen/ftl/control.ftl

@@ -0,0 +1,53 @@
+package ${packageName};
+
+<#list importList as importStr>
+${importStr}
+</#list>
+
+/**
+ * ${tableDesc} 控制器.
+ * 
+ * @author: ${author}
+ * @date: ${date}
+ * @version: ${version}
+ */
+@RestController
+public class ${poClassName}Control extends AjaxControl {
+
+    @Autowired
+    private I${poClassName}Service ${lowerFirstPoClassName}Service;
+	
+    @RequestMapping("${modulePrefix}/${lowerFirstPoClassName}/page")
+    public Ajax page(String query, Integer limit, Integer start, String order, String type) {
+        if (StringHelper.isEmpty(order)) {
+        	//TODO 修改为排序字段
+        	order = "1";
+        }
+        return new Ajax(this.${lowerFirstPoClassName}Service.page(query, start, limit, this.getQueryOrder(order, type)));
+    }
+    
+    @RequestMapping("${modulePrefix}/${lowerFirstPoClassName}/add")
+    public Ajax add(${poClassName} ${lowerFirstPoClassName}) {
+        this.${lowerFirstPoClassName}Service.insert(${lowerFirstPoClassName});
+        return new Ajax();
+    }
+    
+    @RequestMapping("${modulePrefix}/${lowerFirstPoClassName}/edit")
+    public Ajax edit(${poClassName} ${lowerFirstPoClassName}) {
+        this.${lowerFirstPoClassName}Service.edit(${lowerFirstPoClassName});
+        return new Ajax();
+    }
+    
+    @RequestMapping("${modulePrefix}/${lowerFirstPoClassName}/delete")
+    public Ajax delete(Integer[] ids) {
+    	for (Integer id : ids) {
+    		this.${lowerFirstPoClassName}Service.delete(id);
+    	}
+        return new Ajax();
+    }    
+    
+    @RequestMapping("${modulePrefix}/${lowerFirstPoClassName}/get")
+    public Ajax get(Integer id) {
+        return new Ajax(this.${lowerFirstPoClassName}Service.find(id));
+    }    
+}

+ 35 - 0
ses-scan/src/main/java/cn/hmsoft/scan/common/codegen/ftl/dao.ftl

@@ -0,0 +1,35 @@
+package ${packageName};
+
+<#list importList as importStr>
+${importStr}
+</#list>
+
+/**
+ * ${tableDesc} 数据库处理.
+ * 
+ * @author: ${author}
+ * @date: ${date}
+ * @version: ${version}
+ */
+@Repository
+public class ${poClassName}Dao extends PlatformDaoSupport<${poClassName}> {
+
+	/**
+	 * 构建QueryOrder(order,type)过滤.
+	 * @param query
+	 * @param start
+	 * @param limit
+	 * @param order
+	 * @param type
+	 * @return
+	 */
+	public Pager page(String query, Integer start, Integer limit, QueryOrder queryOrder) {
+		String sql = "select * from ${tableName} ";
+		if (StringHelper.isEmpty(query)) {
+			return this.pageMapBySql(queryOrder, start, limit, sql);
+		} else {
+			return this.pageMapBySql(queryOrder, start, limit,
+					sql + "where 1 like ?", generateLikeParamter(query));		
+		}
+	}
+}

+ 38 - 0
ses-scan/src/main/java/cn/hmsoft/scan/common/codegen/ftl/po.ftl

@@ -0,0 +1,38 @@
+package ${packageName};
+
+<#list importList as importStr>
+${importStr}
+</#list>
+
+/**
+ * ${tableDesc} 模型.
+ * 
+ * @author: ${author}
+ * @date: ${date}
+ * @version: ${version}
+ */
+@Table(tableName = "${tableName}", keyColumn = "id", sequenceName = "SEQ_${tableName}")
+public class ${className} implements Serializable {
+	private static final long serialVersionUID = ${serialVersionUID};
+  	<#-- 循环类型及属性 -->
+  	<#list attrs as attr>
+  	//${attr.comment}
+  	private ${attr.type} ${attr.name};
+  	</#list>
+  
+  	<#-- 循环生成set get方法 -->
+  	<#list attrs as attr>
+	/**
+	 * ${attr.comment}
+	 */	
+  	public void ${attr.setFuncName}(${attr.type} ${attr.name}) {
+		this.${attr.name} = ${attr.name};
+  	}
+ 	/**
+	 * ${attr.comment}
+	 */	 	
+  	public ${attr.type} ${attr.getFuncName}() {
+		return ${attr.name};
+  	}
+  	</#list>
+}

+ 17 - 0
ses-scan/src/main/java/cn/hmsoft/scan/common/codegen/ftl/service.ftl

@@ -0,0 +1,17 @@
+package ${packageName};
+
+<#list importList as importStr>
+${importStr}
+</#list>
+
+/**
+ * ${tableDesc} 服务接口.
+ * 
+ * @author: ${author}
+ * @date: ${date}
+ * @version: ${version}
+ */
+public interface I${poClassName}Service extends IBaseService<${poClassName}> {
+
+	Pager page(String query, Integer start, Integer limit, QueryOrder queryOrder);
+}

+ 38 - 0
ses-scan/src/main/java/cn/hmsoft/scan/common/codegen/ftl/serviceImpl.ftl

@@ -0,0 +1,38 @@
+package ${packageName};
+
+<#list importList as importStr>
+${importStr}
+</#list>
+
+/**
+ * ${tableDesc} 服务实现.
+ * 
+ * @author: ${author}
+ * @date: ${date}
+ * @version: ${version}
+ */
+@Service
+public class ${poClassName}ServiceImpl extends BaseServiceImpl<${poClassName}> implements I${poClassName}Service {
+
+	@Autowired
+	private ${poClassName}Dao ${lowerFirstPoClassName}Dao;
+	
+	@Override
+	public PlatformDaoSupport<${poClassName}> getDao() {
+		return this.${lowerFirstPoClassName}Dao;
+	}
+	
+	/**
+	 * 分页查询.
+	 * @param query
+	 * @param start
+	 * @param limit
+	 * @param order
+	 * @param type
+	 * @return
+	 */
+	public Pager page(String query, Integer start, Integer limit, QueryOrder queryOrder) {
+		Pager page${poClassName} = this.${lowerFirstPoClassName}Dao.page(query, start, limit, queryOrder);
+		return page${poClassName};
+	}	
+}

+ 173 - 0
ses-scan/src/main/java/cn/hmsoft/scan/common/codegen/support/CodeGenJdbc.java

@@ -0,0 +1,173 @@
+package cn.hmsoft.scan.common.codegen.support;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.sql.DataSource;
+
+import org.springframework.aop.PointcutAdvisor;
+import org.springframework.aop.support.RegexpMethodPointcutAdvisor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+import org.springframework.jdbc.support.lob.DefaultLobHandler;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+import org.springframework.transaction.interceptor.TransactionInterceptor;
+
+import com.mchange.v2.c3p0.ComboPooledDataSource;
+
+import cn.hmsoft.frame.constants.FrameParamConstants;
+import cn.hmsoft.helper.EntityHelper;
+import cn.hmsoft.helper.StringHelper;
+import cn.hmsoft.jdbc.entity.DatabaseType;
+import cn.hmsoft.jdbc.generator.AccessSQLGenerator;
+import cn.hmsoft.jdbc.generator.ISQLGenerator;
+import cn.hmsoft.jdbc.generator.MysqlSQLGenerator;
+import cn.hmsoft.jdbc.generator.OracleSQLGenerator;
+import cn.hmsoft.jdbc.generator.SQLServerSQLGenerator;
+import cn.hmsoft.jdbc.generator.SqliteSQLGenerator;
+import cn.hmsoft.web.config.C3P0Config;
+import cn.hmsoft.web.config.WebConfig;
+
+/**
+ * 代码生成启动jdbc配置.
+ * @author Lenovo
+ *
+ */
+@Configuration // 标记为spring配置文件
+@EnableAspectJAutoProxy // 标记为允许自动注入
+@EnableTransactionManagement // 标记为允许事务自动管理
+public class CodeGenJdbc {
+	public static void  LoadJdbcConfig(){
+		// 首选获取配置文件应用配置文件
+		File configPath = null;
+		try {
+			configPath = new File(java.net.URLDecoder.decode(CodeGenJdbc.class.getResource("/").getFile(), "utf-8"));
+		} catch (Exception e) {
+		}
+		// 判断系统是否启用了数据库
+		Map<String, String> jdbcConfigMap = new HashMap<String, String>();
+		if (StringHelper.isNotEmpty(FrameParamConstants.JdbcConfigFileName)) {
+			try {
+				jdbcConfigMap = WebConfig.loadProperites(configPath + File.separator + FrameParamConstants.JdbcConfigFileName);
+			} catch (Exception e) {
+			}
+		}
+		try {
+			EntityHelper.setStaticField(C3P0Config.class, jdbcConfigMap);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		if (C3P0Config.JdbcDriverClass.equals("oracle.jdbc.driver.OracleDriver"))
+			C3P0Config.DataType = DatabaseType.Oracle;
+		else if (C3P0Config.JdbcDriverClass.startsWith("com.mysql."))
+			C3P0Config.DataType = DatabaseType.MySql;
+		else if (C3P0Config.JdbcDriverClass.equals("org.sqlite.JDBC"))
+			C3P0Config.DataType = DatabaseType.Sqlite;
+		else if (C3P0Config.JdbcDriverClass.equals("com.microsoft.sqlserver.jdbc.SQLServerDriver"))
+			C3P0Config.DataType = DatabaseType.SQLServer;
+		else if (C3P0Config.JdbcDriverClass.equals("com.hxtt.sql.access.AccessDriver"))
+			C3P0Config.DataType = DatabaseType.Access;
+	}
+	
+	@Bean(destroyMethod = "close", name = "dataSource")
+	public DataSource getDataSource() {
+		CodeGenJdbc.LoadJdbcConfig();
+		ComboPooledDataSource cbDataSource = new ComboPooledDataSource();
+		try {
+			cbDataSource.setAcquireIncrement(C3P0Config.JdbcAcquireincrement);
+			cbDataSource.setCheckoutTimeout(C3P0Config.JdbcCheckouttimeout);
+			cbDataSource.setDriverClass(C3P0Config.JdbcDriverClass);
+			cbDataSource.setMaxIdleTime(C3P0Config.JdbcMaxidletime);
+			cbDataSource.setMaxPoolSize(C3P0Config.JdbcMaxpoolsize);
+			cbDataSource.setMaxStatements(C3P0Config.JdbcMaxstatments);
+			cbDataSource.setPassword(C3P0Config.JdbcPassword);
+			cbDataSource.setJdbcUrl(C3P0Config.JdbcUrl);
+			cbDataSource.setUser(C3P0Config.JdbcUser);
+			cbDataSource.setMinPoolSize(C3P0Config.JdbcMinpoolsize);
+			cbDataSource.setInitialPoolSize(C3P0Config.JdbcInitialpoolsize);
+			cbDataSource.setAcquireRetryDelay(C3P0Config.JdbcAcquireertydelay);
+			cbDataSource.setDebugUnreturnedConnectionStackTraces(true);
+			cbDataSource.setAutoCommitOnClose(false);
+		} catch (Exception e) {
+
+		}
+		return cbDataSource;
+	}
+
+	@Bean(name = "jdbcTemplate")
+	public JdbcTemplate getJdbcTemplate() {
+		JdbcTemplate jdbc = new JdbcTemplate();
+		jdbc.setDataSource(this.getDataSource());
+		return jdbc;
+	}
+
+	/******************************
+	 * 定义事务
+	 */
+	@Bean(name="transactionManager")
+	public DataSourceTransactionManager getDataSourceTransactionManager() {
+		DataSourceTransactionManager ds = new DataSourceTransactionManager();
+		ds.setDataSource(this.getDataSource());
+		return ds;
+	}
+
+	/******************************
+	 * 定义sql生成类
+	 */
+	@Bean(name = "sqlGenerator")
+	public ISQLGenerator getSqlGenerator() {
+		ISQLGenerator sqlGenerator = null;
+		CodeGenJdbc.LoadJdbcConfig();
+		if (C3P0Config.DataType == DatabaseType.Oracle)
+			sqlGenerator = new OracleSQLGenerator();
+		else if (C3P0Config.DataType == DatabaseType.MySql)
+			sqlGenerator = new MysqlSQLGenerator();
+		else if (C3P0Config.DataType == DatabaseType.Sqlite)
+			sqlGenerator = new SqliteSQLGenerator();
+		else if (C3P0Config.DataType == DatabaseType.Access)
+			sqlGenerator = new AccessSQLGenerator();
+		else if (C3P0Config.DataType == DatabaseType.SQLServer)
+			sqlGenerator = new SQLServerSQLGenerator();
+		else sqlGenerator = new OracleSQLGenerator();
+		return sqlGenerator;
+	}
+
+	/*********************************
+	 * Oracle Blob定义
+	 */
+	@Bean
+	public DefaultLobHandler getDefaultLobHandler() {
+		return new DefaultLobHandler();
+	}
+
+	/******************************
+	 * 定义事务切片
+	 */
+	@Bean
+	public TransactionInterceptor getTransactionInterceptor() {
+		TransactionInterceptor tv = new TransactionInterceptor();
+		tv.setTransactionManager(this.getDataSourceTransactionManager());
+		Properties prop = new Properties();
+		prop.put("find*", "PROPAGATION_REQUIRED,readOnly");
+		prop.put("get*", "PROPAGATION_REQUIRED,readOnly");
+		prop.put("query*", "PROPAGATION_REQUIRED,readOnly");
+		prop.put("*", "PROPAGATION_REQUIRED,-Exception");
+		tv.setTransactionAttributes(prop);
+		return tv;
+	}
+
+	/******************************
+	 * 将以Service的类全部设置为事务模式
+	 */
+	@Bean
+	public PointcutAdvisor getPointcutAdvisor() {
+		PointcutAdvisor advisor = new RegexpMethodPointcutAdvisor("cn.hmsoft..*Service.*(..)",
+				this.getTransactionInterceptor());
+		return advisor;
+	}
+}

+ 49 - 0
ses-scan/src/main/java/cn/hmsoft/scan/common/codegen/support/SesGenerateCode.java

@@ -0,0 +1,49 @@
+package cn.hmsoft.scan.common.codegen.support;
+
+import cn.hmsoft.code.AbstractGenerateCode;
+import cn.hmsoft.code.freemark.FreeMarkerInit;
+import freemarker.template.Template;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class SesGenerateCode extends AbstractGenerateCode {
+
+	private static SesGenerateCode instance = new SesGenerateCode();
+	private static final String folderPath = "src/main/java/cn/hmsoft/scan/common/codegen/ftl";
+	
+	public static SesGenerateCode getInstance() {
+		return instance;
+	}
+	
+	private SesGenerateCode() {
+		
+	}
+
+	@Override
+	public Template loadDaoDefinedTemplate() throws Exception {
+		return FreeMarkerInit.getInstance().getDefinedTemplate(folderPath, "dao.ftl");
+	}
+
+	@Override
+	public Template loadPoDefinedTemplate() throws Exception {
+		return FreeMarkerInit.getInstance().getDefinedTemplate(folderPath, "po.ftl");
+	}
+
+	@Override
+	public Template loadServiceDefinedTemplate() throws Exception {
+		return FreeMarkerInit.getInstance().getDefinedTemplate(folderPath, "service.ftl");
+	}
+
+	@Override
+	public Template loadServiceImplDefinedTemplate() throws Exception {
+		return FreeMarkerInit.getInstance().getDefinedTemplate(folderPath, "serviceImpl.ftl");
+	}
+	
+	@Override
+	public Template loadControlDefinedTemplate() throws Exception {
+		return FreeMarkerInit.getInstance().getDefinedTemplate(folderPath, "control.ftl");
+	}
+}

+ 29 - 0
ses-scan/src/main/java/cn/hmsoft/scan/common/constants/ApplySourceConst.java

@@ -0,0 +1,29 @@
+package cn.hmsoft.scan.common.constants;
+
+/**  
+ * @Description: 匹配毕业表的常量类
+ * @author hgh
+ * @date 2023-08-21 10:17:32 
+ */
+public enum ApplySourceConst {
+
+	/**
+	 * 历史毕业表的数据  by_apply_his
+	 */
+	His("历史毕业表"),
+	/**
+	 * 毕业表最新的数据  by_apply
+	 */
+	Latest("最新毕业表");
+
+	private String value;
+
+	private ApplySourceConst(String value) {
+		this.value = value;
+	}
+
+	public String toString() {
+		return this.value;
+	}
+
+}

+ 37 - 0
ses-scan/src/main/java/cn/hmsoft/scan/common/constants/MatchStatusConst.java

@@ -0,0 +1,37 @@
+package cn.hmsoft.scan.common.constants;
+
+/**
+ * @Description: 匹配类型常量类
+ * @author hgh
+ * @date 2023-08-01 05:14:17
+ */
+public enum MatchStatusConst {
+
+	/**
+	 * 待匹配-0
+	 */
+	NULL("待匹配"),
+	/**
+	 * 成功匹配-1
+	 */
+	SUCESS_MATCH("成功匹配"),
+	/**
+	 * 存疑需要确认-2
+	 */
+	NEED_CONFIRM("存疑需要确认"),
+	/**
+	 * 未能找到匹配-3
+	 */
+	FAIL_MATCH("未能找到匹配");
+
+	private String value;
+
+	private MatchStatusConst(String value) {
+		this.value = value;
+	}
+
+	public String toString() {
+		return this.value;
+	}
+
+}

+ 43 - 0
ses-scan/src/main/java/cn/hmsoft/scan/common/constants/SysConst.java

@@ -0,0 +1,43 @@
+package cn.hmsoft.scan.common.constants;
+
+import java.io.File;
+import org.springframework.web.context.ContextLoader;
+import org.springframework.web.context.WebApplicationContext;
+
+import cn.hmsoft.scan.common.AppConfig;
+
+/******************************
+ * 系统常量类
+ */
+public class SysConst {
+	//web应用上下文.
+	private static WebApplicationContext WEBAPPLICATIONCONTEXT = null;
+	
+	//考试院顶级代码
+	public static String TOP_ORG_CODE = "999";
+	
+	//上传路径base
+	public static final String UPLOAD_BASE = "/home/upload/enrol";
+	
+	public static String IMG_FACE_PREFIX =  File.separator + "face" + File.separator;
+	
+    /**
+     * 获取当前WEB应用上下文.
+     * @return 当前WEB应用上下文.
+     */
+    public static WebApplicationContext getCurrentWebApplicationContext() {
+    	if(WEBAPPLICATIONCONTEXT == null){
+    		WEBAPPLICATIONCONTEXT = ContextLoader.getCurrentWebApplicationContext();
+    	}
+    	return WEBAPPLICATIONCONTEXT;
+    }
+    
+    /**
+     * 获取配置对象.
+     */
+    public static AppConfig getAppConfig() {
+    	return getCurrentWebApplicationContext().getBean(AppConfig.class);
+    }
+    
+    
+}

+ 65 - 0
ses-scan/src/main/java/cn/hmsoft/scan/control/by/ByScanStdControl.java

@@ -0,0 +1,65 @@
+package cn.hmsoft.scan.control.by;
+
+import cn.hmsoft.web.entity.Ajax;
+import cn.hmsoft.helper.StringHelper;
+import cn.hmsoft.web.control.AjaxControl;
+import cn.hmsoft.scan.data.model.by.ByScanStd;
+import cn.hmsoft.scan.service.iface.by.IByScanStdService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 扫描登记表的考生信息  控制器.
+ * 
+ * @author: hgh
+ * @date: 2023-07-27 14:45:17
+ * @version: 1.0
+ * @email: hgh@qmth.com.cn
+ */
+@RestController
+public class ByScanStdControl extends AjaxControl {
+
+    @Autowired
+    private IByScanStdService byScanStdService;
+	
+    @RequestMapping("by/byScanStd/page")
+    public Ajax page(String query, Integer limit, Integer start, String order, String type) {
+        if (StringHelper.isEmpty(order)) {
+        	order = "1";
+        }
+        return new Ajax(this.byScanStdService.page(query, start, limit, this.getQueryOrder(order, type)));
+    }
+    
+    @RequestMapping("by/byScanStd/add")
+    public Ajax add(ByScanStd byScanStd) {
+        this.byScanStdService.insert(byScanStd);
+        return new Ajax();
+    }
+    
+    @RequestMapping("by/byScanStd/edit")
+    public Ajax edit(ByScanStd byScanStd) {
+        this.byScanStdService.edit(byScanStd);
+        return new Ajax();
+    }
+    
+    @RequestMapping("by/byScanStd/delete")
+    public Ajax delete(Integer[] ids) {
+    	for (Integer id : ids) {
+    		this.byScanStdService.delete(id);
+    	}
+        return new Ajax();
+    }    
+    
+    @RequestMapping("by/byScanStd/get")
+    public Ajax get(Integer id) {
+        return new Ajax(this.byScanStdService.find(id));
+    } 
+    
+    
+    @RequestMapping("by/byScanStd/fillYearMonth")
+    public Ajax fillYearMonth() {
+    	byScanStdService.fillYearMonth();
+    	return new Ajax();
+    }
+}

+ 52 - 0
ses-scan/src/main/java/cn/hmsoft/scan/control/by/FtpFileStatControl.java

@@ -0,0 +1,52 @@
+package cn.hmsoft.scan.control.by;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.hmsoft.scan.service.iface.by.IByStdFilesStatService;
+import cn.hmsoft.web.control.AjaxControl;
+import cn.hmsoft.web.entity.Ajax;
+
+/**  
+ * @Description: 统计Ftp服务器上文件的数量,按照年份/月份/专业统计
+ * @author hgh
+ * @date 2023-10-10 10:05:00 
+ */
+@RestController
+public class FtpFileStatControl extends AjaxControl{
+
+	@Autowired
+	private IByStdFilesStatService filesStatService;
+	
+	/**
+	 * @Description: 统计FTP服务器下某个或者所有文件夹下的数量
+	 * @param id
+	 * @date 2023-10-10 10:34:52 
+	 */  
+	@RequestMapping("ftp/file/stat")
+	public Ajax stat(Integer id) {
+		filesStatService.statFiles(id);
+		return new Ajax();
+	}
+	
+	/**
+	 * @Description: 初始化FTP服务器上文件夹到数据库中
+	 * @date 2023-10-10 10:47:28 
+	 */  
+	@RequestMapping("ftp/file/init")
+	public Ajax initFiles() {
+		filesStatService.initFiles();
+		return new Ajax();
+	}
+	
+	/**
+	 * @Description: 根据数据库中的数据,创建文件夹
+	 * @date 2023-10-11 10:16:23 
+	 */  
+	@RequestMapping("ftp/file/fold/create")
+	public Ajax createFold() {
+		filesStatService.createFold();
+		return new Ajax();
+	}
+}

+ 44 - 0
ses-scan/src/main/java/cn/hmsoft/scan/control/by/StdMatchControl.java

@@ -0,0 +1,44 @@
+package cn.hmsoft.scan.control.by;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.hmsoft.scan.service.iface.by.IStdMatchService;
+import cn.hmsoft.scan.util.CacheUtil;
+import cn.hmsoft.web.control.AjaxControl;
+import cn.hmsoft.web.entity.Ajax;
+
+@RestController
+public class StdMatchControl extends AjaxControl {
+
+	@Autowired
+	private IStdMatchService matchService;
+	
+    /**
+     * @Description: 毕业考生匹配
+     * @date 2023-08-02 09:53:42 
+     */  
+    @RequestMapping("by/std/match")
+    public Ajax match() {
+        this.matchService.match();
+        return new Ajax();
+    }
+    
+    /**
+     * @Description: 刷新缓存
+     * @date 2023-08-21 10:21:02 
+     */  
+    @RequestMapping("by/std/cache/refresh")
+    public Ajax refreshCache() {
+    	CacheUtil.instance(true);
+    	return new Ajax();
+    }
+    
+    
+    @RequestMapping("by/std/make/dir")
+    public Ajax makeDir() {
+        this.matchService.makeDir();
+        return new Ajax();
+    }
+}

+ 58 - 0
ses-scan/src/main/java/cn/hmsoft/scan/data/dao/by/ByApplyHisDao.java

@@ -0,0 +1,58 @@
+package cn.hmsoft.scan.data.dao.by;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.stereotype.Repository;
+
+import cn.hmsoft.helper.StringHelper;
+import cn.hmsoft.jdbc.core.PlatformDaoSupport;
+import cn.hmsoft.scan.data.model.by.ByApplyHis;
+
+@Repository
+public class ByApplyHisDao  extends PlatformDaoSupport<ByApplyHis>{
+
+	/**
+	 * @Description: 根据考生姓名和专业-查询毕业表中的考生 (考生姓名中有全角的空格,在查询的时候,需要去除)   
+	 * 					2001年之前 毕业考生关联该表(项目一期),2004年6月(包含)之前的毕业考生数据,都可以查询这张表,2004年12月(包含)之后的数据,需要查询by_apply表
+	 * @param std_name 考生姓名
+	 * @param graduate_major 毕业专业名称
+	 * @param edu_level 专科/本科
+	 * @param graduate_no 毕业证号
+	 * @date 2023-08-01 04:20:03 
+	 */  
+	public List<ByApplyHis> listByData(String std_name, String graduate_major, String edu_level, String graduate_no) {
+		StringBuffer sql = new StringBuffer(" select * from by_apply_his where trim(replace(replace(std_name,' ',''), ' ',''))=? and zy=? ");
+		sql.append(" and (zylx=? or 1=?) and (substr(diaplma_no,instr(diaplma_no,')')+1)=? or 1=? ) ");
+		return this.listBySql(sql.toString(), std_name, graduate_major, edu_level, StringHelper.isEmpty(edu_level) ? 1 : 2,
+				graduate_no, StringHelper.isEmpty(graduate_no) ? 1 : 2);
+	}
+	
+	
+	/**
+	 * @Description: 查询考生毕业数据:2004年12月(包含)之后的数据 (纸质毕业登记表中-都有毕业证号)
+	 * @param std_name 考生姓名
+	 * @param graduate_major 毕业专业名称
+	 * @param edu_level 专科/本科
+	 * @param graduate_no 毕业证号
+	 * @date 2023-08-18 10:57:05 
+	 */  
+	public List<Map<String,Object>> listApplyData(String std_name, String graduate_major, String edu_level, String graduate_no) {
+		StringBuffer sql = new StringBuffer();
+		sql.append(" select a.* from by_apply a,std_reg_info r where a.std_id=r.id and a.status=5 and  a.diaplma_no=? ");
+		return this.listMapBySql(sql.toString(), graduate_no);
+	}
+	
+	
+
+	/**
+	 * @Description: 根据毕业证号,查找毕业考生
+	 * @param graduate_no 毕业证号
+	 * @param xm 姓名
+	 * @date 2023-08-01 06:28:47 
+	 */  
+	public ByApplyHis findDataByGraduateNo(String graduate_no, String std_name) {
+		String sql = "select * from by_apply_his where substr(diaplma_no,instr(diaplma_no,')')+1) = ? and trim(replace(replace(std_name,' ',''), ' ',''))=? ";
+		return this.findBySql(sql, graduate_no, std_name);
+	}
+}

+ 11 - 0
ses-scan/src/main/java/cn/hmsoft/scan/data/dao/by/ByScanJsonDao.java

@@ -0,0 +1,11 @@
+package cn.hmsoft.scan.data.dao.by;
+
+import org.springframework.stereotype.Repository;
+
+import cn.hmsoft.jdbc.core.PlatformDaoSupport;
+import cn.hmsoft.scan.data.model.by.ByScanJson;
+
+@Repository
+public class ByScanJsonDao extends PlatformDaoSupport<ByScanJson> {
+
+}

+ 73 - 0
ses-scan/src/main/java/cn/hmsoft/scan/data/dao/by/ByScanStdDao.java

@@ -0,0 +1,73 @@
+package cn.hmsoft.scan.data.dao.by;
+
+import cn.hmsoft.jdbc.entity.Pager;
+import cn.hmsoft.helper.StringHelper;
+import cn.hmsoft.jdbc.entity.QueryOrder;
+
+import java.util.List;
+
+import org.springframework.stereotype.Repository;
+import cn.hmsoft.jdbc.core.PlatformDaoSupport;
+import cn.hmsoft.scan.data.model.by.ByScanStd;
+
+/**
+ * 扫描登记表的考生信息  数据库处理.
+ * 
+ * @author: hgh
+ * @date: 2023-07-27 14:45:17
+ * @version: 1.0
+ */
+@Repository
+public class ByScanStdDao extends PlatformDaoSupport<ByScanStd> {
+
+	/**
+	 * 构建QueryOrder(order,type)过滤.
+	 * @return
+	 */
+	public Pager page(String query, Integer start, Integer limit, QueryOrder queryOrder) {
+		String sql = "select * from by_scan_std ";
+		if (StringHelper.isEmpty(query)) {
+			return this.pageMapBySql(queryOrder, start, limit, sql);
+		} else {
+			return this.pageMapBySql(queryOrder, start, limit,
+					sql + "where 1 like ?", generateLikeParamter(query));		
+		}
+	}
+
+	/**
+	 * @Description: 查找所有未匹配的考生
+	 * @param matchStatus 匹配状态
+	 * @date 2023-08-01 03:16:58 
+	 */  
+	public List<ByScanStd> listNoMatchStd(Integer matchStatus) {
+		String sql = "select * from by_scan_std where 1=1 ";
+		if(matchStatus == null) {
+			sql += " and match_status is null  ";
+		} else {
+			sql += " and match_status=" +matchStatus;
+		}
+		return this.listBySql(sql);
+	}
+
+	public List<ByScanStd> listStd() {
+		String sql = "select * from by_scan_std where year is null";
+		return this.listBySql(sql);
+	}
+
+	public List<ByScanStd> listScanStd(Integer match_status) {
+		String sql = " select s.* from by_scan_std s,by_std_ids si where s.id=si.id ";
+		if(match_status != null) {
+			sql += " and match_status=" + match_status;
+		}
+		return this.listBySql(sql);
+	}
+	
+	public List<ByScanStd> listPath() {
+		String sql = " select distinct substr(storage_path,0,INSTR(SUBSTR(storage_path, 1, LENGTH(storage_path)), '/', -1)) storage_path from by_scan_std s,by_std_ids si where s.id=si.id ";
+		//String sql = " select distinct substr(storage_path,0,INSTR(SUBSTR(storage_path, 1, LENGTH(storage_path)), '\\', -1)) storage_path from by_scan_std ";
+		return this.listBySql(sql);
+	}
+	
+	
+	
+}

+ 11 - 0
ses-scan/src/main/java/cn/hmsoft/scan/data/dao/by/ByScanStdJsonDao.java

@@ -0,0 +1,11 @@
+package cn.hmsoft.scan.data.dao.by;
+
+import org.springframework.stereotype.Repository;
+
+import cn.hmsoft.jdbc.core.PlatformDaoSupport;
+import cn.hmsoft.scan.data.model.by.ByScanStdJson;
+
+@Repository
+public class ByScanStdJsonDao extends PlatformDaoSupport<ByScanStdJson>{
+
+}

+ 16 - 0
ses-scan/src/main/java/cn/hmsoft/scan/data/dao/by/ByStdFilesStatDao.java

@@ -0,0 +1,16 @@
+package cn.hmsoft.scan.data.dao.by;
+
+import org.springframework.stereotype.Repository;
+
+import cn.hmsoft.jdbc.core.PlatformDaoSupport;
+import cn.hmsoft.scan.data.model.by.ByStdFilesStat;
+
+@Repository
+public class ByStdFilesStatDao extends PlatformDaoSupport<ByStdFilesStat>{
+
+	public ByStdFilesStat findFilesStat(String year, String month, String majorName) {
+		String sql = " select * from by_std_files_stat where year=? and month=? and major_name=? ";
+		return this.findBySql(sql, year, month, majorName);
+	}
+
+}

+ 16 - 0
ses-scan/src/main/java/cn/hmsoft/scan/data/dao/cf/CfMajorDao.java

@@ -0,0 +1,16 @@
+package cn.hmsoft.scan.data.dao.cf;
+
+import org.springframework.stereotype.Repository;
+
+import cn.hmsoft.jdbc.core.PlatformDaoSupport;
+import cn.hmsoft.scan.data.model.cf.CfMajor;
+
+/**  
+ * @Description: 专业字典表Dao
+ * @author hgh
+ * @date 2023-07-31 03:19:58 
+ */
+@Repository
+public class CfMajorDao extends PlatformDaoSupport<CfMajor>{
+
+}

+ 17 - 0
ses-scan/src/main/java/cn/hmsoft/scan/data/dao/cf/CfScanRuleDao.java

@@ -0,0 +1,17 @@
+package cn.hmsoft.scan.data.dao.cf;
+
+import org.springframework.stereotype.Repository;
+
+import cn.hmsoft.jdbc.core.PlatformDaoSupport;
+import cn.hmsoft.scan.data.model.cf.CfScanRule;
+
+@Repository
+public class CfScanRuleDao extends PlatformDaoSupport<CfScanRule>{
+
+	public CfScanRule findScanRule(String year, String month) {
+		String sql = "select * from cf_scan_rule where year=? and month=?";
+		return this.findBySql(sql, year, Integer.parseInt(month));
+	}
+	
+
+}

+ 98 - 0
ses-scan/src/main/java/cn/hmsoft/scan/data/model/by/ByApplyHis.java

@@ -0,0 +1,98 @@
+package cn.hmsoft.scan.data.model.by;
+
+import java.io.Serializable;
+
+import cn.hmsoft.jdbc.entity.Table;
+
+/**  
+ * @Description: 2001年之前所有的毕业的考生
+ * @date 2023-08-10 02:06:30 
+ */
+@Table(tableName = "BY_APPLY_HIS", keyColumn = "ID", sequenceName = "SEQ_BY_APPLY_HIS")
+public class ByApplyHis implements Serializable {
+	private static final long serialVersionUID = 8437701093601121489L;
+
+	private Integer id; //主键
+	private String diaplma_no; //毕业证号
+	private String ticket_no; //准考证号
+	private String std_name; //姓名
+	private String std_sex; //性别
+	private String zy; //专业
+	private String zylx; //专业层次
+	private String bysj; //毕业时间
+	private String zkyx; //主考学校
+	
+
+	public Integer getId() {
+		return id;
+	}
+
+	public void setId(Integer id) {
+		this.id = id;
+	}
+
+	public String getDiaplma_no() {
+		return diaplma_no;
+	}
+
+	public void setDiaplma_no(String diaplma_no) {
+		this.diaplma_no = diaplma_no;
+	}
+
+	public String getTicket_no() {
+		return ticket_no;
+	}
+
+	public void setTicket_no(String ticket_no) {
+		this.ticket_no = ticket_no;
+	}
+
+	public String getStd_name() {
+		return std_name;
+	}
+
+	public void setStd_name(String std_name) {
+		this.std_name = std_name;
+	}
+
+	public String getStd_sex() {
+		return std_sex;
+	}
+
+	public void setStd_sex(String std_sex) {
+		this.std_sex = std_sex;
+	}
+
+	public String getZy() {
+		return zy;
+	}
+
+	public void setZy(String zy) {
+		this.zy = zy;
+	}
+
+	public String getZylx() {
+		return zylx;
+	}
+
+	public void setZylx(String zylx) {
+		this.zylx = zylx;
+	}
+
+	public String getBysj() {
+		return bysj;
+	}
+
+	public void setBysj(String bysj) {
+		this.bysj = bysj;
+	}
+
+	public String getZkyx() {
+		return zkyx;
+	}
+
+	public void setZkyx(String zkyx) {
+		this.zkyx = zkyx;
+	}
+
+}

+ 61 - 0
ses-scan/src/main/java/cn/hmsoft/scan/data/model/by/ByScanJson.java

@@ -0,0 +1,61 @@
+package cn.hmsoft.scan.data.model.by;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+import cn.hmsoft.jdbc.entity.Table;
+
+/**
+ * 扫描登记表的考生信息 模型.
+ * 
+ * @author: hgh
+ * @date: 2023-07-27 14:45:17
+ * @version: 1.0
+ * @email: hgh@qmth.com.cn
+ * @Company: www.hmsoft.cn
+ */
+@Table(tableName = "by_scan_json")
+public class ByScanJson implements Serializable {
+	private static final long serialVersionUID = -6526237063042334477L;
+
+	// ocr识别json数据
+	private String std_json;
+
+	private LocalDateTime create_date;
+
+	private Integer scan_std_id;
+	private Integer is_substring;
+
+	public String getStd_json() {
+		return std_json;
+	}
+
+	public void setStd_json(String std_json) {
+		this.std_json = std_json;
+	}
+
+	public LocalDateTime getCreate_date() {
+		return create_date;
+	}
+
+	public void setCreate_date(LocalDateTime create_date) {
+		this.create_date = create_date;
+	}
+
+	public Integer getScan_std_id() {
+		return scan_std_id;
+	}
+
+	public void setScan_std_id(Integer scan_std_id) {
+		this.scan_std_id = scan_std_id;
+	}
+
+	public Integer getIs_substring() {
+		return is_substring;
+	}
+
+	public void setIs_substring(Integer is_substring) {
+		this.is_substring = is_substring;
+	}
+
+}

+ 267 - 0
ses-scan/src/main/java/cn/hmsoft/scan/data/model/by/ByScanStd.java

@@ -0,0 +1,267 @@
+package cn.hmsoft.scan.data.model.by;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+import cn.hmsoft.jdbc.entity.Table;
+
+/**
+ * 扫描登记表的考生信息  模型.
+ * 
+ * @author: hgh
+ * @date: 2023-07-27 14:45:17
+ * @version: 1.0
+ * @email: hgh@qmth.com.cn
+ * @Company: www.hmsoft.cn
+ */
+@Table(tableName = "by_scan_std", keyColumn = "id", sequenceName = "SEQ_BY_SCAN_STD")
+public class ByScanStd implements Serializable {
+	private static final long serialVersionUID = -6516237063042334477L;
+  	//证件号码
+  	private String cert_id;
+  	//毕业专业名称
+  	private String graduate_major;
+  	//毕业证号码
+  	private String graduate_no;
+  	//毕业时间
+  	private String graduate_time;
+  	//主键
+  	private Integer id;
+  	//匹配毕业表的ID,暂时放到这张表,二期优化到毕业表中
+  	private Integer match_by_id;
+  	//匹配状态: null待匹配,1:已匹配,2:存疑需确认,3:未匹配
+  	private Integer match_status;
+  	//出生日期
+  	private String std_birth;
+  	//考生姓名
+  	private String std_name;
+  	//考生性别
+  	private String std_sex;
+  	//OCR识别后的存放地址
+  	private String storage_path;
+  	//扫描文件存放地址
+  	private String scan_path;
+  	//本科专科
+  	private String edu_level;
+  	//创建时间
+  	private LocalDateTime create_time;
+  	//匹配那张毕业表的数据: by_apply_history, by_apply。值为:his、latest
+  	private String flag;
+  	//毕业年份
+  	private String year;
+  	//毕业月份
+  	private String month;
+  	//袋号
+  	private String tag;
+  	
+  
+	/**
+	 * 证件号码
+	 */	
+  	public void setCert_id(String cert_id) {
+		this.cert_id = cert_id;
+  	}
+ 	/**
+	 * 证件号码
+	 */	 	
+  	public String getCert_id() {
+		return cert_id;
+  	}
+	/**
+	 * 毕业专业名称
+	 */	
+  	public void setGraduate_major(String graduate_major) {
+		this.graduate_major = graduate_major;
+  	}
+ 	/**
+	 * 毕业专业名称
+	 */	 	
+  	public String getGraduate_major() {
+		return graduate_major;
+  	}
+	/**
+	 * 毕业证号码
+	 */	
+  	public void setGraduate_no(String graduate_no) {
+		this.graduate_no = graduate_no;
+  	}
+ 	/**
+	 * 毕业证号码
+	 */	 	
+  	public String getGraduate_no() {
+		return graduate_no;
+  	}
+	/**
+	 * 毕业时间
+	 */	
+  	public void setGraduate_time(String graduate_time) {
+		this.graduate_time = graduate_time;
+  	}
+ 	/**
+	 * 毕业时间
+	 */	 	
+  	public String getGraduate_time() {
+		return graduate_time;
+  	}
+	/**
+	 * 主键
+	 */	
+  	public void setId(Integer id) {
+		this.id = id;
+  	}
+ 	/**
+	 * 主键
+	 */	 	
+  	public Integer getId() {
+		return id;
+  	}
+	/**
+	 * 匹配毕业表的ID,暂时放到这张表,二期优化到毕业表中
+	 */	
+  	public void setMatch_by_id(Integer match_by_id) {
+		this.match_by_id = match_by_id;
+  	}
+ 	/**
+	 * 匹配毕业表的ID,暂时放到这张表,二期优化到毕业表中
+	 */	 	
+  	public Integer getMatch_by_id() {
+		return match_by_id;
+  	}
+	/**
+	 * 匹配状态: null待匹配,1:已匹配,2:存疑需确认,3:未匹配
+	 */	
+  	public void setMatch_status(Integer match_status) {
+		this.match_status = match_status;
+  	}
+ 	/**
+	 * 匹配状态: null待匹配,1:已匹配,2:存疑需确认,3:未匹配
+	 */	 	
+  	public Integer getMatch_status() {
+		return match_status;
+  	}
+	/**
+	 * 出生日期
+	 */	
+  	public void setStd_birth(String std_birth) {
+		this.std_birth = std_birth;
+  	}
+ 	/**
+	 * 出生日期
+	 */	 	
+  	public String getStd_birth() {
+		return std_birth;
+  	}
+	/**
+	 * 考生姓名
+	 */	
+  	public void setStd_name(String std_name) {
+		this.std_name = std_name;
+  	}
+ 	/**
+	 * 考生姓名
+	 */	 	
+  	public String getStd_name() {
+		return std_name;
+  	}
+	/**
+	 * 考生性别
+	 */	
+  	public void setStd_sex(String std_sex) {
+		this.std_sex = std_sex;
+  	}
+ 	/**
+	 * 考生性别
+	 */	 	
+  	public String getStd_sex() {
+		return std_sex;
+  	}
+	/**
+	 * 存放地址
+	 */	
+  	public void setStorage_path(String storage_path) {
+		this.storage_path = storage_path;
+  	}
+ 	/**
+	 * 存放地址
+	 */	 	
+  	public String getStorage_path() {
+		return storage_path;
+  	}
+	/**
+	 * 本科、专科
+	 */	
+	public String getEdu_level() {
+		return edu_level;
+	}
+	/**
+	 * 本科、专科
+	 */	
+	public void setEdu_level(String edu_level) {
+		this.edu_level = edu_level;
+	}
+	/**
+	 * 创建时间
+	 */	
+	public LocalDateTime getCreate_time() {
+		return create_time;
+	}
+	/**
+	 * 创建时间
+	 */	
+	public void setCreate_time(LocalDateTime create_time) {
+		this.create_time = create_time;
+	}
+	
+	/**
+	 * OCR识别后的存放地址
+	 */
+	public String getScan_path() {
+		return scan_path;
+	}
+	
+	/**
+	 * OCR识别后的存放地址
+	 */
+	public void setScan_path(String scan_path) {
+		this.scan_path = scan_path;
+	}
+	
+	/**
+	 * 匹配那张毕业表的数据: by_apply_history, by_apply。值为:his、latest
+	 */
+	public String getFlag() {
+		return flag;
+	}
+	
+	/**
+	 * 匹配那张毕业表的数据: by_apply_history, by_apply。值为:his、latest
+	 */
+	public void setFlag(String flag) {
+		this.flag = flag;
+	}
+
+	public String getYear() {
+		return year;
+	}
+
+	public void setYear(String year) {
+		this.year = year;
+	}
+
+	public String getMonth() {
+		return month;
+	}
+
+	public void setMonth(String month) {
+		this.month = month;
+	}
+
+	public String getTag() {
+		return tag;
+	}
+
+	public void setTag(String tag) {
+		this.tag = tag;
+	}
+  	
+}

+ 49 - 0
ses-scan/src/main/java/cn/hmsoft/scan/data/model/by/ByScanStdJson.java

@@ -0,0 +1,49 @@
+package cn.hmsoft.scan.data.model.by;
+
+import java.io.Serializable;
+
+import cn.hmsoft.jdbc.entity.Table;
+
+/**
+ * 扫描登记表的考生信息 模型.
+ * 
+ * @author: hgh
+ * @date: 2023-07-27 14:45:17
+ * @version: 1.0
+ * @email: hgh@qmth.com.cn
+ * @Company: www.hmsoft.cn
+ */
+@Table(tableName = "by_scan_std_json", keyColumn = "id", sequenceName = "SEQ_BY_SCAN_STD_JSON")
+public class ByScanStdJson implements Serializable {
+	private static final long serialVersionUID = -6516237063042334477L;
+	// 主键
+	private Integer id;
+	private Integer scan_std_id;
+	// ocr识别json数据
+	private String std_json;
+
+	public Integer getId() {
+		return id;
+	}
+
+	public void setId(Integer id) {
+		this.id = id;
+	}
+
+	public Integer getScan_std_id() {
+		return scan_std_id;
+	}
+
+	public void setScan_std_id(Integer scan_std_id) {
+		this.scan_std_id = scan_std_id;
+	}
+
+	public String getStd_json() {
+		return std_json;
+	}
+
+	public void setStd_json(String std_json) {
+		this.std_json = std_json;
+	}
+
+}

+ 81 - 0
ses-scan/src/main/java/cn/hmsoft/scan/data/model/by/ByStdFilesStat.java

@@ -0,0 +1,81 @@
+package cn.hmsoft.scan.data.model.by;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+import cn.hmsoft.jdbc.entity.Table;
+
+/**
+ * @Description: 已扫描-考生毕业电子档案统计
+ * @author hgh
+ * @date 2023-10-10 10:25:09
+ */
+@Table(tableName = "BY_STD_FILES_STAT", keyColumn = "ID", sequenceName = "SEQ_BY_STD_FILES_STAT")
+public class ByStdFilesStat implements Serializable {
+
+	private static final long serialVersionUID = -75526416168569327L;
+	private Integer id;
+	private String year; // 毕业年份
+	private String month; // 毕业月份
+	private String major_name; // 专业名称
+	private Integer orig_num; // 已经扫描未ocr识别的数量
+	private Integer ocr_num; // 已经ocr识别的数量
+	private LocalDateTime update_time; // 更新时间
+
+	public Integer getId() {
+		return id;
+	}
+
+	public void setId(Integer id) {
+		this.id = id;
+	}
+
+	public String getYear() {
+		return year;
+	}
+
+	public void setYear(String year) {
+		this.year = year;
+	}
+
+	public String getMonth() {
+		return month;
+	}
+
+	public void setMonth(String month) {
+		this.month = month;
+	}
+
+	public String getMajor_name() {
+		return major_name;
+	}
+
+	public void setMajor_name(String major_name) {
+		this.major_name = major_name;
+	}
+
+	public Integer getOrig_num() {
+		return orig_num;
+	}
+
+	public void setOrig_num(Integer orig_num) {
+		this.orig_num = orig_num;
+	}
+
+	public Integer getOcr_num() {
+		return ocr_num;
+	}
+
+	public void setOcr_num(Integer ocr_num) {
+		this.ocr_num = ocr_num;
+	}
+
+	public LocalDateTime getUpdate_time() {
+		return update_time;
+	}
+
+	public void setUpdate_time(LocalDateTime update_time) {
+		this.update_time = update_time;
+	}
+
+}

+ 36 - 0
ses-scan/src/main/java/cn/hmsoft/scan/data/model/cf/CfMajor.java

@@ -0,0 +1,36 @@
+package cn.hmsoft.scan.data.model.cf;
+
+import java.io.Serializable;
+
+import cn.hmsoft.jdbc.entity.Table;
+
+/**  
+ * @Description: 专业字典表,ocr识别专业的时候使用
+ * @author hgh
+ * @date 2023-07-31 03:18:37 
+ */
+@Table(tableName = "CF_MAJOR", keyColumn = "id", sequenceName = "SEQ_CF_MAJOR")
+public class CfMajor implements Serializable {
+
+	private static final long serialVersionUID = 1917836435548502548L;
+	public Integer id;
+	// 专业名称
+	public String major_name;
+
+	public Integer getId() {
+		return id;
+	}
+
+	public void setId(Integer id) {
+		this.id = id;
+	}
+
+	public String getMajor_name() {
+		return major_name;
+	}
+
+	public void setMajor_name(String major_name) {
+		this.major_name = major_name;
+	}
+
+}

+ 66 - 0
ses-scan/src/main/java/cn/hmsoft/scan/data/model/cf/CfScanRule.java

@@ -0,0 +1,66 @@
+package cn.hmsoft.scan.data.model.cf;
+
+import java.io.Serializable;
+
+import cn.hmsoft.jdbc.entity.Table;
+
+/**  
+ * @Description: 扫描-规则配置表
+ * @author hgh
+ * @date 2023-07-31 05:49:30 
+ */
+@Table(tableName = "CF_SCAN_RULE", keyColumn = "id", sequenceName = "SEQ_CF_SCAN_RULE")
+public class CfScanRule implements Serializable {
+
+	private static final long serialVersionUID = -4300780291967952041L;
+	private Integer id;
+	// 年份
+	private Integer year;
+	// 月份
+	private String month;
+	// 类型:默认为毕业证号的匹配规则,后期根据实际情况添加其他类型
+	private String rule_type;
+	// 正则表达式
+	private String rule_value;
+
+	public Integer getId() {
+		return id;
+	}
+
+	public void setId(Integer id) {
+		this.id = id;
+	}
+
+	public Integer getYear() {
+		return year;
+	}
+
+	public void setYear(Integer year) {
+		this.year = year;
+	}
+
+	public String getMonth() {
+		return month;
+	}
+
+	public void setMonth(String month) {
+		this.month = month;
+	}
+
+	public String getRule_type() {
+		return rule_type;
+	}
+
+	public void setRule_type(String rule_type) {
+		this.rule_type = rule_type;
+	}
+
+	public String getRule_value() {
+		return rule_value;
+	}
+
+	public void setRule_value(String rule_value) {
+		this.rule_value = rule_value;
+	}
+
+}

+ 33 - 0
ses-scan/src/main/java/cn/hmsoft/scan/data/vo/FileVo.java

@@ -0,0 +1,33 @@
+package cn.hmsoft.scan.data.vo;
+
+public class FileVo {
+
+	private String firstFoldName;
+	private String secondFoldName;
+	private String thirdFoldName;
+
+	public String getFirstFoldName() {
+		return firstFoldName;
+	}
+
+	public void setFirstFoldName(String firstFoldName) {
+		this.firstFoldName = firstFoldName;
+	}
+
+	public String getSecondFoldName() {
+		return secondFoldName;
+	}
+
+	public void setSecondFoldName(String secondFoldName) {
+		this.secondFoldName = secondFoldName;
+	}
+
+	public String getThirdFoldName() {
+		return thirdFoldName;
+	}
+
+	public void setThirdFoldName(String thirdFoldName) {
+		this.thirdFoldName = thirdFoldName;
+	}
+
+}

+ 23 - 0
ses-scan/src/main/java/cn/hmsoft/scan/service/iface/by/IByScanStdService.java

@@ -0,0 +1,23 @@
+package cn.hmsoft.scan.service.iface.by;
+
+import cn.hmsoft.jdbc.entity.Pager;
+import cn.hmsoft.jdbc.entity.QueryOrder;
+import cn.hmsoft.web.service.IBaseService;
+import cn.hmsoft.scan.data.model.by.ByScanStd;
+
+/**
+ * 扫描登记表的考生信息  服务接口.
+ * 
+ * @author: hgh
+ * @date: 2023-07-27 14:45:17
+ * @version: 1.0
+ */
+public interface IByScanStdService extends IBaseService<ByScanStd> {
+
+	Pager page(String query, Integer start, Integer limit, QueryOrder queryOrder);
+	
+	void insertAnalysisResult(String firstNumContent, String secondNumContent, String pdfPath) throws Exception;
+
+	void fillYearMonth();
+	
+}

+ 11 - 0
ses-scan/src/main/java/cn/hmsoft/scan/service/iface/by/IByStdFilesStatService.java

@@ -0,0 +1,11 @@
+package cn.hmsoft.scan.service.iface.by;
+
+public interface IByStdFilesStatService {
+
+	void statFiles(Integer id);
+
+	void initFiles();
+
+	void createFold();
+
+}

+ 16 - 0
ses-scan/src/main/java/cn/hmsoft/scan/service/iface/by/IStdMatchService.java

@@ -0,0 +1,16 @@
+package cn.hmsoft.scan.service.iface.by;
+
+import cn.hmsoft.scan.data.model.by.ByScanStd;
+import cn.hmsoft.web.service.IBaseService;
+
+/**  
+ * @Description: ocr解析出来的考生登记表和现有考生匹配
+ * @author hgh
+ * @date 2023-08-01 03:05:28 
+ */
+public interface IStdMatchService extends IBaseService<ByScanStd>{
+
+	void match();
+
+	void makeDir();
+}

+ 10 - 0
ses-scan/src/main/java/cn/hmsoft/scan/service/iface/by/IStdPicOcrService.java

@@ -0,0 +1,10 @@
+package cn.hmsoft.scan.service.iface.by;
+
+import cn.hmsoft.scan.data.model.by.ByScanStd;
+import cn.hmsoft.scan.data.model.by.ByScanStdJson;
+import cn.hmsoft.web.service.IBaseService;
+
+public interface IStdPicOcrService extends IBaseService<ByScanStdJson>{
+
+	void picOcr(ByScanStd std);
+}

+ 569 - 0
ses-scan/src/main/java/cn/hmsoft/scan/service/impl/by/ByScanStdServiceImpl.java

@@ -0,0 +1,569 @@
+package cn.hmsoft.scan.service.impl.by;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.google.common.io.Files;
+
+import cn.hmsoft.frame.exception.web.BusinessException;
+import cn.hmsoft.helper.StringHelper;
+import cn.hmsoft.jdbc.core.PlatformDaoSupport;
+import cn.hmsoft.jdbc.entity.Pager;
+import cn.hmsoft.jdbc.entity.QueryOrder;
+import cn.hmsoft.scan.common.constants.SysConst;
+import cn.hmsoft.scan.data.dao.by.ByScanJsonDao;
+import cn.hmsoft.scan.data.dao.by.ByScanStdDao;
+import cn.hmsoft.scan.data.model.by.ByScanJson;
+import cn.hmsoft.scan.data.model.by.ByScanStd;
+import cn.hmsoft.scan.data.model.cf.CfScanRule;
+import cn.hmsoft.scan.service.iface.by.IByScanStdService;
+import cn.hmsoft.scan.util.CacheUtil;
+import cn.hmsoft.scan.util.RegexUtil;
+import cn.hmsoft.web.service.impl.BaseServiceImpl;
+
+/**
+ * 扫描登记表的考生信息  服务实现.
+ * 
+ * @author: hgh
+ * @date: 2023-07-27 14:45:17
+ * @version: 1.0
+ */
+@Service
+public class ByScanStdServiceImpl extends BaseServiceImpl<ByScanStd> implements IByScanStdService {
+
+	@Autowired
+	private ByScanStdDao byScanStdDao;
+	@Autowired
+	private ByScanJsonDao byScanJsonDao;
+	
+	@Override
+	public PlatformDaoSupport<ByScanStd> getDao() {
+		return this.byScanStdDao;
+	}
+	
+	/**
+	 * 分页查询.
+	 */
+	public Pager page(String query, Integer start, Integer limit, QueryOrder queryOrder) {
+		Pager pageByScanStd = this.byScanStdDao.page(query, start, limit, queryOrder);
+		return pageByScanStd;
+	}
+	
+	public static void main(String[] args) {
+		String firstNumContent = "{\"words_result\":[{\"words\":\"广西壮族自治区高等教育自学考试\",\"location\":{\"top\":109,\"left\":297,\"width\":328,\"height\":24}},{\"words\":\"毕业生登记表\",\"location\":{\"top\":158,\"left\":252,\"width\":418,\"height\":39}},{\"words\":\"0982001100184\",\"location\":{\"top\":204,\"left\":697,\"width\":101,\"height\":15}},{\"words\":\"(大学专科)\",\"location\":{\"top\":222,\"left\":392,\"width\":139,\"height\":26}},{\"words\":\"冯春燕\",\"location\":{\"top\":228,\"left\":729,\"width\":47,\"height\":17}},{\"words\":\"市地:百色市县(市):百色市直\",\"location\":{\"top\":274,\"left\":155,\"width\":549,\"height\":30}},{\"words\":\"主考学校:\",\"location\":{\"top\":317,\"left\":156,\"width\":74,\"height\":18}},{\"words\":\"广西财政高等专科学校\",\"location\":{\"top\":307,\"left\":277,\"width\":153,\"height\":18}},{\"words\":\"专业:\",\"location\":{\"top\":317,\"left\":474,\"width\":74,\"height\":19}},{\"words\":\"会计\",\"location\":{\"top\":308,\"left\":656,\"width\":30,\"height\":16}},{\"words\":\"毕业证号:\",\"location\":{\"top\":349,\"left\":155,\"width\":74,\"height\":18}},{\"words\":\"12#@#6745100103200016";
+		ByScanStd std = new ByScanStd();
+		std.setGraduate_no("65451001032000160");
+		
+		//如果毕业证号为空或者不是17位,则重新按65或66规则查找毕业证号
+		if(StringUtils.isEmpty(std.getGraduate_no()) || !(std.getGraduate_no().startsWith("65") || std.getGraduate_no().startsWith("66"))){
+			int start = -1;
+			String key = null;
+			if(firstNumContent.indexOf("6545") != -1){
+				key = "6545";
+				start = firstNumContent.indexOf(key);
+			}else if(firstNumContent.indexOf("6645") != -1){
+				key = "6645";
+				start = firstNumContent.indexOf(key);
+			}
+			
+			if (start != -1){
+				String subStr = firstNumContent.substring(start);
+				if(subStr.length()>=17){
+					subStr = firstNumContent.substring(start,start+17);
+					if(RegexUtil.isNumber(subStr)){
+						std.setGraduate_no(subStr);
+					}
+				}
+			}
+			
+		}
+	}
+
+	/***
+	 * @Description: 解析ocr识别的结果,并匹配考生
+	 * @param firstNumContent 第一页登记表识别出的内容 (专业和填表日期)
+	 * @param secondNumContent 第二页登记表识别出的内容(考生姓名、出生日期)
+	 * @throws Exception 抛出异常,不要在方法里面处理异常 (否则事务不起作用)
+	 * @date 2023-07-27 02:55:06
+	 */
+	@Override
+	@Transactional
+	public void insertAnalysisResult(String firstNumContent, String secondNumContent, String pdfPath) throws Exception {
+		ByScanStd std = new ByScanStd();
+		std.setStd_name(getStdName(firstNumContent, "姓名"));
+		std.setGraduate_major(getMajorName(firstNumContent, "专业"));
+		std.setGraduate_no(getGraduate_no(firstNumContent, pdfPath, "毕业证号"));
+		std.setEdu_level(getEduLevel(firstNumContent));
+		std.setScan_path(getScanPath(pdfPath));
+		std.setCreate_time(LocalDateTime.now());
+		
+		std.setCert_id(getCertId(firstNumContent, "身份证号"));
+		
+		setStdYearMonth(std, pdfPath);
+		
+		// 如果毕业证号为空或者6545或6645开头,则重新按6545或6645规则查找毕业证号
+		if (StringUtils.isEmpty(std.getGraduate_no())
+				|| !(std.getGraduate_no().startsWith("6545") || std.getGraduate_no().startsWith("6645"))) {
+			int start = -1;
+			String key = null;
+			if (firstNumContent.indexOf("6545") != -1) {
+				key = "6545";
+				start = firstNumContent.indexOf(key);
+			} else if (firstNumContent.indexOf("6645") != -1) {
+				key = "6645";
+				start = firstNumContent.indexOf(key);
+			}
+
+			if (start != -1) {
+				String subStr = firstNumContent.substring(start);
+				if (subStr.length() >= 17) {
+					subStr = firstNumContent.substring(start, start + 17);
+					if (RegexUtil.isNumber(subStr)) {
+						std.setGraduate_no(subStr);
+					}
+				}
+			}
+
+		}
+		
+		Integer scan_std_id = this.byScanStdDao.insert(std);
+		//迁移文件
+		std.setStorage_path(this.movePdfToResultDir(pdfPath));
+		this.byScanStdDao.update(std);
+		
+		ByScanJson byScanJson = new ByScanJson();
+		byScanJson.setScan_std_id(scan_std_id);
+		
+		
+		if (null != firstNumContent) {
+			byte[] bytes = firstNumContent.getBytes(StandardCharsets.UTF_8);
+			if (bytes.length > 4000) {
+				byte[] substringBytes = Arrays.copyOfRange(bytes, 0, 4000);
+				firstNumContent = new String(substringBytes, StandardCharsets.UTF_8);
+				byScanJson.setIs_substring(1);
+			}else{
+				byScanJson.setIs_substring(0);
+			}
+		}else{
+			byScanJson.setIs_substring(0);
+		}
+		
+		
+		byScanJson.setStd_json(firstNumContent);
+		
+		this.byScanJsonDao.insert(byScanJson);
+	}
+	
+	
+	private String getScanPath(String pdfPath) {
+		//TODO 实际过程中,要根据系统的不同调整,linux下使用File.separator windows下使用"\\\\"
+		String[] arr = pdfPath.split(File.separator);
+		if(arr.length != 9) 
+			return pdfPath;
+		return arr[4] +File.separator + arr[5] + File.separator + arr[6] + File.separator + arr[7] + File.separator + arr[8];
+	}
+	
+	
+	
+	
+	private void setStdYearMonth(ByScanStd std, String pdfPath) {
+		//TODO 实际过程中,要根据系统的不同调整,linux下使用File.separator windows下使用"\\\\"
+		String[] arr = pdfPath.split(File.separator);
+		std.setYear(arr[5]);
+		std.setMonth(arr[6].length() < 2 ? "0" + arr[6] : arr[6]);
+		std.setTag(arr[7]);
+	}
+
+	/**
+	 * @Description: 将ocr识别的结果移植到待匹配结果表中
+	 * @param id ocr识别结果表的ID
+	 * @param pdfPath 登记表所在路径
+	 * @date 2023-08-01 10:52:40 
+	 */
+	private String movePdfToResultDir(String pdfPath) throws Exception {
+		String resultDirPath = SysConst.getAppConfig().getResultDir();
+		File stdPdf = new File(pdfPath);
+		//TODO 实际过程中,要根据系统的不同调整,linux下使用File.separator windows下使用"\\\\"
+		String[] arr = pdfPath.split(File.separator);
+		if (arr.length <= 5)
+			throw new BusinessException("扫描目录配置错误,请在扫描的时候,先创建目录:按照登记表年份/月份/专业名称 来创建!");
+		String path = resultDirPath + File.separator + arr[5] + File.separator + arr[6] + File.separator + arr[7];
+		File resultDir = new File(path);
+		if (!resultDir.exists())
+			resultDir.mkdirs();
+		Files.move(stdPdf, new File(resultDir+ File.separator + stdPdf.getName()));
+//		return resultDir+ File.separator + stdPdf.getName();
+		return  arr[5] + File.separator + arr[6] + File.separator + arr[7] + File.separator + stdPdf.getName();
+	}
+
+	/**
+	 * @Description: 查找毕业证号--2001年之前,每年的毕业证号的规则不一样,需要根据毕业年份和月份,配置正则表达式
+	 * @param firstNumContent
+	 * @param key
+	 * @param pafPath 如:/opt/dir/1999/6/哲学(或者自己定义的内部编号)/111.pdf
+	 * @date 2023-07-31 05:27:19 
+	 */  
+	private String getGraduate_no(String firstNumContent, String pdfPath, String key) {
+		String content = analysisContent(firstNumContent, "毕业证号");
+		// TODO修改为实时读取
+		CacheUtil cache = CacheUtil.instance(false);
+		int start = firstNumContent.indexOf("\"" + key + "\"");
+		// 如果不存在关键字,则查找关键字:
+		if(start == -1){
+			start = firstNumContent.indexOf("\""+key+":\"");
+		}
+		
+		if (start == -1) {
+			// 处理内容如:66450101042028609身份证号45081198411300444
+			if (null != content && content.indexOf("身份证号") != -1) {
+				content = content.substring(0, content.indexOf("身份证号"));
+				return content;
+			} else {
+				return content;
+			}
+		}
+			
+		//获取存放路径的年份和月份
+		//TODO 实际过程中,要根据系统的不同,调整,linux下使用File.separator,windows下使用"\\\\"
+		String[] arr = pdfPath.split(File.separator);
+		//if(arr.length != 6) return content;
+		key = arr[5] + "-" + arr[6];
+		CfScanRule rule = cache.getScanRule(key);
+		if (rule != null) {
+			if (null != content && !content.matches(rule.getRule_value())) {
+				//向下查找
+				String subStr = firstNumContent.substring(start);
+				String no = findGraduateNo(subStr, rule.getRule_value(), 2, 3, "down");
+				// 向上查找
+				if (!no.matches(rule.getRule_value())) {
+					subStr = firstNumContent.substring(0, start);
+					return findGraduateNo(subStr, rule.getRule_value(), 1, 4, "up");
+				} else {
+					return no;
+				}
+			}
+		}
+		
+		//向上查找
+		if (StringUtils.isEmpty(content)||!RegexUtil.isPositiveNumber(content)) {
+			String subStr = firstNumContent.substring(0, start);
+			content = findUpContent(subStr, 1);
+			
+			// 向下查找
+			if (StringUtils.isEmpty(content)||!RegexUtil.isPositiveNumber(content)) {
+				subStr = firstNumContent.substring(start);
+				content = findDownContent(subStr, 2);
+			}
+			
+		}
+		
+		if(!StringUtils.isEmpty(content) && !RegexUtil.isPositiveNumber(content.trim())){
+			return null;
+		}
+		
+		return content;
+	}
+	
+	/**
+	 * @Description: 查找专业名称
+	 * @param str
+	 * @param start 起始查找位置
+	 * @param level 要查找的层级
+	 * @date 2023-07-31 04:47:39 
+	 */  
+	private String findGraduateNo(String str, String rule, int start, int level, String flag) {
+		String graduateNo = null;
+		while(true) {
+			if("up".equals(flag)) {
+				graduateNo = findUpContent(str, start);
+			} else if("down".equals(flag)) {
+				graduateNo = findDownContent(str, start);
+			}
+			start++;
+			if(graduateNo.matches(rule) || level < start) break;
+		}
+		return graduateNo;
+	}
+	
+	
+	
+	/**
+	 * @Description: 查找专业名称
+	 * @param firstNumContent ocr识别出来的json字符串
+	 * @param key 待查找的关键字
+	 * @date 2023-07-31 09:24:09 
+	 */  
+	private String getMajorName(String firstNumContent, String key) {
+		String majorName = analysisContent(firstNumContent,key);
+		//CacheUtil cache = CacheUtil.instance();
+		int start = firstNumContent.indexOf("\"" + key + "\"");
+		// 如果不存在关键字,则查找"关键字:
+		if(start == -1){
+			start = firstNumContent.indexOf("\""+key+":\"");
+		}
+		
+		
+		if (start == -1){
+			// 解决:"words":"主考学校:广西大学专业:英语(外贸英语方向)"
+			if(firstNumContent.indexOf(key+":") != -1){
+				// 如果不存在关键字,则查找关键字:
+				start = firstNumContent.indexOf(key+":");
+				majorName = firstNumContent.substring(start);
+				if(null != majorName && majorName.indexOf("\",") != -1){
+					majorName = majorName.substring(majorName.indexOf(":")+1, majorName.indexOf("\","));
+				}
+				
+			}else{
+				return majorName;
+			}
+		}
+			
+		
+		// 向下查找
+		if (StringUtils.isEmpty(majorName)) {
+			String subStr = firstNumContent.substring(start);
+			majorName = findDownContent(subStr, 2);
+		}
+		
+		//向下查找
+//		if (!cache.isContainsMajor(majorName)) {
+//			String subStr = firstNumContent.substring(start);
+//			//实际解析过程中,可根据实际情况,灵活调整起始和层级数
+//			String name = findMajorName(subStr, 2, 3, "down");
+//			// 向上查找
+//			if (!cache.isContainsMajor(name)) {
+//				subStr = firstNumContent.substring(0, start);
+//				return findMajorName(subStr, 1, 4, "up");
+//			} else {
+//				return name;
+//			}
+//		}
+		return majorName;
+	}
+	
+	
+	/**
+	 * @Description: 查找专业名称
+	 * @param firstNumContent ocr识别出来的json字符串
+	 * @param key 待查找的关键字
+	 * @date 2023-07-31 09:24:09 
+	 */  
+	private String getCertId(String firstNumContent, String key) {
+		String certId = analysisContent(firstNumContent,key);
+		//CacheUtil cache = CacheUtil.instance();
+		int start = firstNumContent.indexOf("\"" + key + "\"");
+		// 如果不存在关键字,则查找关键字:
+		if(start == -1){
+			start = firstNumContent.indexOf("\""+key+":\"");
+		}
+		
+		if (start == -1)
+			return certId;
+		
+		// 向下查找
+		if (StringUtils.isEmpty(certId)||!RegexUtil.isPositiveNumber(certId.replaceAll("X", "").replaceAll("x", ""))) {
+			String subStr = firstNumContent.substring(start);
+			certId = findDownContent(subStr, 2);
+		}
+		
+		if(!StringUtils.isEmpty(certId) && !RegexUtil.isPositiveNumber(certId.replaceAll("X", "").replaceAll("x", ""))){
+			return null;
+		}
+		
+		return certId;
+	}
+	
+	/**
+	 * @Description: 查找专业名称
+	 * @param str ocr识别出来的json字符串
+	 * @param start 起始查找位置
+	 * @param level 要查找的层级
+	 * @date 2023-07-31 04:47:39 
+	 */  
+	private String findMajorName(String str, int start, int level, String flag) {
+		CacheUtil cache = CacheUtil.instance();
+		String majorName = null;
+		while(true) {
+			if("up".equals(flag)) {
+				majorName = findUpContent(str, start);
+			} else if("down".equals(flag)) {
+				majorName = findDownContent(str, start);
+			}
+			start++;
+			if(cache.isContainsMajor(majorName) || level < start) break;
+		}
+		return majorName;
+	}
+	
+	
+	/**
+	 * @Description: 解析考生的姓名,一级一级的查,最大查询二层
+	 * @param firstNumContent ocr识别出来的json字符串
+	 * @date 2023-07-28 04:25:18
+	 */
+	private String getStdName(String firstNumContent, String key) {
+		String stdName = analysisContent(firstNumContent, key);
+		int start = firstNumContent.indexOf("\"" + key + "\"");
+		// 如果不存在关键字,则查找关键字:
+		if(start == -1){
+			start = firstNumContent.indexOf("\""+key+":\"");
+		}
+				
+		if (start == -1)
+			return stdName;
+		//向下查找
+		if (!RegexUtil.validateChineseName(stdName)) {
+			String subStr = firstNumContent.substring(start);
+			String name = findDownContent(subStr, 2);
+			// 向上查找一层
+			if (!RegexUtil.validateChineseName(name)) {
+				subStr = firstNumContent.substring(0, start);
+				name = findUpContent(subStr, 1);
+				//向上查找二层
+				if (!RegexUtil.validateChineseName(name)) {
+					name = findUpContent(subStr, 2);
+					return name;
+				} else {
+					return name;
+				}
+			} else {
+				return name;
+			}
+
+		}
+		return stdName;
+	}
+	
+	/**
+	 * @Description: 向上查找
+	 * @param subStr 待查找的字符串
+	 * @param level 查找的层级数
+	 * @date 2023-07-28 06:32:14 
+	 */  
+	private String findUpContent(String subStr, int level) {
+		if(subStr == null) 
+			return null;
+		String str = subStr.substring(StringUtils.lastOrdinalIndexOf(subStr, "\"words\"", level+1),
+				StringUtils.lastOrdinalIndexOf(subStr, ",\"location\"", level));
+		return str.substring(str.indexOf("words\":") + 7).replace("\"", "");
+	}
+	
+	/**
+	 * @Description: 向下查找
+	 * @param subStr 待查找的字符串
+	 * @param level 查找的层级数
+	 * @date 2023-07-28 06:32:14 
+	 */  
+	private String findDownContent(String subStr, int level) {
+		if(subStr == null) 
+			return null;
+		String str = subStr.substring(StringUtils.ordinalIndexOf(subStr, ",\"location\"", level),
+				StringUtils.ordinalIndexOf(subStr, ",\"location\"", level+1));
+		return str.substring(str.indexOf("words\":") + 7).replace("\"", "");
+	}
+	
+	/**
+	 * @Description: 解析并返回key对应的值
+	 * @param content ocr解析的内容
+	 * @param key 待查找的key
+	 * @date 2023-07-27 17:06:21 
+	 */  
+	private String analysisContent(String content, String key) {
+		int sameLine = content.indexOf("\""+key+"\"");
+		// 如果不存在关键字,则查找关键字:
+		if(sameLine == -1){
+			sameLine = content.indexOf("\""+key+":\"");
+		}
+		
+		// ocr识别,key和对应的值是否在同一行
+		int start = content.indexOf("\""+key);
+		if(start == -1) return null;
+		String subStr = content.substring(start);
+		if (sameLine > 0) {
+			String str = subStr.substring(0, StringUtils.ordinalIndexOf(subStr, ",\"location\"", 2));
+			return str.substring(str.indexOf("words\":") + 7).replace("\"", "").replace(":", "").replace(":", "");
+		} else {
+			String str = subStr.substring(0, subStr.indexOf("\","));
+			return str.replace("\""+key, "").replace(":", "").replace(":", "");
+		} 
+	}
+	
+	
+	@Deprecated
+	private String getGradteTime(String firstNumContent) {
+		String key = firstNumContent.contains("填表日期:") ? "填表日期:" : "填表日期";
+		String time = analysisContent(firstNumContent, key);
+		if(StringHelper.isNotEmpty(time) && !time.contains("月")) {
+			return analysisGradteTime(firstNumContent, key);
+		}
+		return time;
+	}
+
+	
+	
+	/**
+	 * @Description: 填表日期-特殊处理(针对日期分了好几行的情况)
+	 * @param content ocr解析的内容
+	 * @param key 待查找的key
+	 * @date 2023-07-28 10:04:19 
+	 */ 
+	@Deprecated
+	private String analysisGradteTime(String content, String key) {
+		int start = content.indexOf("\"" + key);
+		if(start> 0) {
+			String subStr = content.substring(start);
+			String str = subStr.substring(0, subStr.indexOf("\","));
+			String year = str.replace("\"" + key, "");
+			if (StringHelper.isNotEmpty(year) && !year.contains("月")) {
+				String monthStr = subStr.substring(0, StringUtils.ordinalIndexOf(subStr, ",\"location\"", 2));
+				String month = monthStr.substring(monthStr.indexOf("words\":") + 7).replace("\"", "");
+				return year + month;
+			}
+			return year;
+		}
+		return null;
+	}
+	
+	/**
+	 * @Description: 获取教育层次
+	 * @param content
+	 * @date 2023-07-27 05:31:03 
+	 */  
+	private String getEduLevel(String content) {
+		if (content.contains("\"(专科)\"") || content.contains("\"(专科)\"") || content.contains("\"(大学专科)\"")
+				|| content.contains("\"(大学专科)\""))
+			return "专科";
+		else if (content.contains("\"(本科)\"") || content.contains("\"(本科)\"") || content.contains("\"(大学本科)\"")
+				|| content.contains("\"(大学本科)\""))
+			return "本科";
+		else if (content.contains("\"(中专)\"") || content.contains("\"(中专)\""))
+			return "中专";
+		return null;
+	}
+
+	@Override
+	public void fillYearMonth() {
+		List<ByScanStd> listStd = byScanStdDao.listStd();
+		String path = null;
+		for (ByScanStd std : listStd) {
+			path = std.getScan_path();
+			//TODO 实际过程中,要根据系统的不同调整,linux下使用File.separator windows下使用"\\\\"
+			String[] arr = path.split(File.separator);
+			std.setYear(arr[5]);
+			std.setMonth(arr[6].length() < 2 ? "0" + arr[6] : arr[6]);
+			std.setTag(arr[7]);
+			byScanStdDao.update(std);
+		}
+	}
+	
+	
+	
+}

+ 137 - 0
ses-scan/src/main/java/cn/hmsoft/scan/service/impl/by/ByStdFilesStatImpl.java

@@ -0,0 +1,137 @@
+package cn.hmsoft.scan.service.impl.by;
+
+import java.io.File;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import cn.hmsoft.scan.common.constants.SysConst;
+import cn.hmsoft.scan.data.dao.by.ByStdFilesStatDao;
+import cn.hmsoft.scan.data.model.by.ByStdFilesStat;
+import cn.hmsoft.scan.data.vo.FileVo;
+import cn.hmsoft.scan.service.iface.by.IByStdFilesStatService;
+
+@Service
+public class ByStdFilesStatImpl implements IByStdFilesStatService {
+	
+	@Autowired
+	private ByStdFilesStatDao filesStatDao;
+
+	@Override
+	public void statFiles(Integer id) {
+		String origDir = SysConst.getAppConfig().getFileScanDir();
+		String ocrDir = SysConst.getAppConfig().getResultDir();
+		//统计文件的数量
+		if(id != null) { //对某个批次专业进行统计
+			ByStdFilesStat stat = filesStatDao.find(id);
+			stat.setOrig_num(getFileNum(origDir, stat));
+			stat.setOcr_num(getFileNum(ocrDir, stat));
+			stat.setUpdate_time(LocalDateTime.now());
+			filesStatDao.update(stat);
+		} else {
+			List<ByStdFilesStat> statList = filesStatDao.all();
+			for(ByStdFilesStat stat : statList) {
+				stat.setOrig_num(getFileNum(origDir, stat));
+				stat.setOcr_num(getFileNum(ocrDir, stat));
+				stat.setUpdate_time(LocalDateTime.now());
+				filesStatDao.update(stat);
+			}
+		}
+		
+	}
+
+	private Integer getFileNum(String dir, ByStdFilesStat stat) {
+		File file = new File(dir);
+		if(file.isDirectory()) {
+			File[] files = file.listFiles();
+			for(File firstFile : files) {
+				if(firstFile.isDirectory() && firstFile.getName().equals(stat.getYear())) {
+					for(File secondFile: firstFile.listFiles()) {
+						if(secondFile.isDirectory() && secondFile.getName().equals(stat.getMonth())) {
+							for(File thirdFile : secondFile.listFiles()) {
+								if(thirdFile.isDirectory() && thirdFile.getName().equals(stat.getMajor_name())) {
+									return thirdFile.listFiles().length;
+								}
+							}
+						}
+					}
+				}
+			}
+			
+		}
+		return 0;
+	}
+
+	@Override
+	public void initFiles() {
+		// 遍历未ocr识别的文件夹
+		String origDir = SysConst.getAppConfig().getFileScanDir();
+		List<FileVo> origList = listAllFolders(new File(origDir), 3);
+		for (FileVo vo : origList) {
+			ByStdFilesStat filesStat = filesStatDao.findFilesStat(vo.getFirstFoldName(), vo.getSecondFoldName(),
+					vo.getThirdFoldName());
+			if(filesStat == null) {
+				ByStdFilesStat stat = new ByStdFilesStat();
+				stat.setYear(vo.getFirstFoldName());
+				stat.setMonth(vo.getSecondFoldName());
+				stat.setMajor_name(vo.getThirdFoldName());
+				stat.setUpdate_time(LocalDateTime.now());
+				filesStatDao.insert(stat);
+			}
+		}
+	}
+	
+	public List<FileVo> listAllFolders(File dir, int level) {
+		List<FileVo> folders = new ArrayList<FileVo>();
+		if (!dir.isDirectory())
+			return null;
+		File[] files = dir.listFiles();
+		String firstFoldName = null;
+		String secondFoldName = null;
+		String thirdFoldName = null;
+		for (File file : files) {
+			if (file.isFile())
+				continue;
+			firstFoldName = file.getName(); // 第一级
+			for (File secondFile : file.listFiles()) {
+				if (secondFile.isFile())
+					continue;
+				secondFoldName = secondFile.getName(); // 第二级
+				for (File thirdFile : secondFile.listFiles()) {
+					if (thirdFile.isFile())
+						continue;
+					thirdFoldName = thirdFile.getName(); // 第三级
+					FileVo fileVo = new FileVo();
+					fileVo.setFirstFoldName(firstFoldName);
+					fileVo.setSecondFoldName(secondFoldName);
+					fileVo.setThirdFoldName(thirdFoldName);
+					folders.add(fileVo);
+				}
+			}
+		}
+		return folders;
+	}
+
+	@Override
+	public void createFold() {
+		List<ByStdFilesStat> list = filesStatDao.all();
+		String origDir = SysConst.getAppConfig().getFileScanDir();
+		for (ByStdFilesStat stat : list) {
+			File firstFold = new File(origDir+File.separator+stat.getYear());
+			if(!firstFold.exists()) 
+				firstFold.mkdirs();
+			File secondFold = new File(origDir+File.separator+stat.getYear() + File.separator + stat.getMonth());
+			if(!secondFold.exists()) 
+				secondFold.mkdirs();
+			File thirdFold = new File(origDir+File.separator+stat.getYear() + File.separator + stat.getMonth() + File.separator + stat.getMajor_name());
+			if(!thirdFold.exists())
+				thirdFold.mkdirs();
+		}
+	}
+	
+
+}
+

+ 133 - 0
ses-scan/src/main/java/cn/hmsoft/scan/service/impl/by/StdMatchServiceImp.java

@@ -0,0 +1,133 @@
+package cn.hmsoft.scan.service.impl.by;
+
+
+import java.io.File;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import cn.hmsoft.frame.exception.web.BusinessException;
+import cn.hmsoft.helper.LogHelper;
+import cn.hmsoft.helper.StringHelper;
+import cn.hmsoft.jdbc.core.PlatformDaoSupport;
+import cn.hmsoft.scan.common.constants.MatchStatusConst;
+import cn.hmsoft.scan.common.constants.SysConst;
+import cn.hmsoft.scan.data.dao.by.ByApplyHisDao;
+import cn.hmsoft.scan.data.dao.by.ByScanStdDao;
+import cn.hmsoft.scan.data.model.by.ByApplyHis;
+import cn.hmsoft.scan.data.model.by.ByScanStd;
+import cn.hmsoft.scan.service.iface.by.IStdMatchService;
+import cn.hmsoft.scan.util.CommonUtils;
+import cn.hmsoft.web.service.impl.BaseServiceImpl;
+
+@Service
+public class StdMatchServiceImp extends BaseServiceImpl<ByScanStd> implements IStdMatchService {
+	
+	@Autowired
+	private ByScanStdDao scanStdDao;
+	@Autowired
+	private ByApplyHisDao daoApply;
+
+	@Override
+	public PlatformDaoSupport<ByScanStd> getDao() {
+		return this.scanStdDao;
+	}
+
+	@Override
+	@Transactional
+	public void match() {
+		List<ByScanStd> stdList = scanStdDao.listNoMatchStd(null);
+		for (ByScanStd std : stdList) {
+			//TODO 有考生填写的为繁体字, 需要转换: ZhConverterUtil.convertToSimple(std.getStd_name())
+			List<ByApplyHis> dataList = daoApply.listByData(std.getStd_name(), std.getGraduate_major(), std.getEdu_level(), std.getGraduate_no());
+			//查询不到
+			if(dataList == null || dataList.size() == 0) {
+				std.setMatch_status(MatchStatusConst.FAIL_MATCH.ordinal());
+			} else if(dataList.size() > 1) { //大于一条,需要人工确认
+				std.setMatch_status(MatchStatusConst.NEED_CONFIRM.ordinal());
+			} else if(dataList.size() == 1) { //成功匹配
+				std.setMatch_status(MatchStatusConst.SUCESS_MATCH.ordinal());
+				std.setMatch_by_id(dataList.get(0).getId());
+				//更新文件的名称
+				File file = new File(std.getStorage_path());
+				if(!file.exists()) {
+					LogHelper.error(file.getAbsolutePath()+"文件不存在!");
+					continue;
+				}
+				String suffix = StringUtils.getFilenameExtension(file.getAbsolutePath());
+				File destFile = new File(file.getParent() + File.separator + dataList.get(0).getDiaplma_no() + "." + suffix);
+				file.renameTo(destFile);
+				std.setStorage_path(destFile.getAbsolutePath());
+				scanStdDao.update(std);
+			}
+		}
+		
+	}
+	
+	
+	
+
+	/**
+	 * @Description: 处理查询结果大于1条的数据
+	 * @param dataList
+	 * @param std
+	 * @date 2023-08-01 05:59:39 
+	 */
+	@Deprecated
+	private void filterMoreThenOne(List<ByApplyHis> dataList, ByScanStd std) {
+		boolean flag = false;
+		if(StringHelper.isNotEmpty(std.getGraduate_no())) {
+			for (ByApplyHis by : dataList) {
+				String byzh = CommonUtils.getStringTrimVal(by.getDiaplma_no());
+				String result = byzh.contains(")") ? byzh.substring(byzh.indexOf(")")+1) : byzh;
+				//匹配上了
+				if(result.equals(std.getGraduate_no())) {
+					std.setMatch_status(MatchStatusConst.SUCESS_MATCH.ordinal());
+					std.setMatch_by_id(dataList.get(0).getId());
+					scanStdDao.update(std);
+					flag = true;
+					break;
+				}
+			}
+		}
+		//需要人工确认
+		if(!flag) {
+			std.setMatch_status(MatchStatusConst.NEED_CONFIRM.ordinal());
+			scanStdDao.update(std);
+		}
+	}
+
+	@Override
+	public void makeDir() {
+		String rootDir = SysConst.getAppConfig().getFileOpencvDir();
+		List<ByScanStd> stdList = scanStdDao.listPath();
+		for (ByScanStd std : stdList) {
+			String path = std.getStorage_path();
+			String[] arr = path.split(File.separator);
+			if(arr.length != 3) {
+				throw new BusinessException("目录错误,请查看错误!" + path);
+			}
+			//第一层目录
+			File firstDir = new File(rootDir + File.separator + arr[0]);
+			if(!firstDir.exists()) {
+				firstDir.mkdir();
+			}
+			//第二层目录
+			File secondDir = new File(rootDir + File.separator + arr[0]+File.separator + arr[1]);
+			if(!secondDir.exists()) {
+				secondDir.mkdir();
+			}
+			//第三层目录
+			File thirdDir = new File(rootDir + File.separator + arr[0]+File.separator + arr[1] + File.separator + arr[2]);
+			if(!thirdDir.exists()) {
+				thirdDir.mkdir();
+			}
+		}
+	}
+	
+
+
+}

+ 289 - 0
ses-scan/src/main/java/cn/hmsoft/scan/service/impl/by/StdPicOcrServiceImpl.java

@@ -0,0 +1,289 @@
+package cn.hmsoft.scan.service.impl.by;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+
+import cn.hmsoft.frame.exception.web.BusinessException;
+import cn.hmsoft.helper.LogHelper;
+import cn.hmsoft.helper.StringHelper;
+import cn.hmsoft.jdbc.core.PlatformDaoSupport;
+import cn.hmsoft.scan.common.constants.SysConst;
+import cn.hmsoft.scan.data.dao.by.ByScanStdDao;
+import cn.hmsoft.scan.data.dao.by.ByScanStdJsonDao;
+import cn.hmsoft.scan.data.dao.cf.CfScanRuleDao;
+import cn.hmsoft.scan.data.model.by.ByScanStd;
+import cn.hmsoft.scan.data.model.by.ByScanStdJson;
+import cn.hmsoft.scan.data.model.cf.CfScanRule;
+import cn.hmsoft.scan.service.iface.by.IStdPicOcrService;
+import cn.hmsoft.scan.util.RegexUtil;
+import cn.hmsoft.scan.util.baidu.doc.Handwriting;
+import cn.hmsoft.web.service.impl.BaseServiceImpl;
+
+/**  
+ * @Description: opencv-切割后的图片,识别
+ * @author hgh
+ * @date 2023-11-23 03:54:43 
+ */
+@Service
+public class StdPicOcrServiceImpl extends BaseServiceImpl<ByScanStdJson> implements IStdPicOcrService {
+
+	@Autowired
+	private ByScanStdDao scanStdDao;
+	@Autowired
+	private ByScanStdJsonDao stdJsonDao;
+	@Autowired
+	private CfScanRuleDao ruleDao;
+	
+	
+	@Override
+	public PlatformDaoSupport<ByScanStdJson> getDao() {
+		return this.stdJsonDao;
+	}
+
+	@Override
+	public void picOcr(ByScanStd std) {
+		//未成功核验的考生
+		//List<ByScanStd> stdList = scanStdDao.listScanStd(3);
+		String rootDir = SysConst.getAppConfig().getFileOpencvDir();
+		if(StringHelper.isEmpty(rootDir)) {
+			throw new BusinessException("你未配置opencv切割图片的目录,请先在config.properties中配置!");
+		}
+//		for(ByScanStd std : stdList) {
+			File file = new File(rootDir + std.getStorage_path());
+//			if(!file.exists()) {
+//				LogHelper.error(file.getAbsolutePath()+"文件不存在!");
+//				return;
+//			}
+			//组装切割后的图片文件
+			File newFile = makeNewFile(file, std);
+//			if(!newFile.exists()) {
+//				LogHelper.error(newFile.getAbsolutePath()+"文件不存在!是否未完成切割?");
+//				return;
+//			}
+			ByScanStdJson exists = stdJsonDao.find("scan_std_id", std.getId());
+			String json = null;
+			if(exists == null) {
+//				json = Handwriting.handwriting(newFile.getPath(), 1);
+//				insertStdJson(std, json);
+			} else {
+				json  = exists.getStd_json();
+				List<String> list = stdJsonToList(json);
+				if(list.size() == 0) return;
+				std.setStd_name(getStdName(list, "姓名"));
+				std.setGraduate_major(getMajorName(list, "专业"));
+				std.setGraduate_no(getGraduate_no(list, std, "毕业证号"));
+				std.setEdu_level(getEduLevel(json));
+				scanStdDao.update(std);
+			}
+			
+//		}
+	}
+	
+	/**
+	 * @Description: 解析考生的姓名,一级一级的查,最大查询二层
+	 */
+	private String getStdName(List<String> list, String key) {
+		String stdName = null;
+		for (int i = 0; i < list.size(); i++) {
+			if(list.get(i).equals("姓名") || list.get(i).equals("姓名:")) {
+				stdName = list.get(i+1);
+//				//不是中文,向上查找一层,会有多种场景,需要根据不同的场景修改
+//				if (!RegexUtil.validateChineseName(stdName)) {
+//					stdName = list.get(i-1);
+//				}
+				if(stdName.length() == 1) {
+					stdName = stdName + list.get(i+2);
+				}
+				if(list.get(i+3).length() == 1) {
+					stdName = stdName + list.get(i+3);
+				}
+			} else if(list.get(i).equals("姓")) { //分行了
+				if(list.get(i+1).equals("名")) {
+					stdName = list.get(i+2);
+				} else if(list.get(i+1).startsWith("名")) {
+					stdName = list.get(i+1).replace("名", "");
+				}
+			}
+		}
+		return stdName;
+	}
+	
+	
+	
+	/**
+	 * @Description: 查找专业名称
+	 * @param firstNumContent ocr识别出来的json字符串
+	 * @param key 待查找的关键字
+	 */  
+	private String getMajorName(List<String> list, String key) {
+		String majorName = null;
+		for (int i = 0; i < list.size(); i++) {
+			if(list.get(i).equals("专业") || list.get(i).equals("专业:")) {
+				majorName = list.get(i+1);
+			}
+		}
+		return majorName;
+	}
+	
+	
+	
+	/**
+	 * @Description: 查找毕业证号--2001年之前,每年的毕业证号的规则不一样,需要根据毕业年份和月份,配置正则表达式
+	 * @param key
+	 */
+	private String getGraduate_no(List<String> list,ByScanStd std, String key) {
+		String graduateNo = null;
+		CfScanRule rule = ruleDao.findScanRule(std.getYear(), std.getMonth());
+		for (int i = 0; i < list.size(); i++) {
+			String result = list.get(i);
+			//配置了正则表达式,全量查找
+			if(rule != null && StringHelper.isNotEmpty(result)) {
+				if(result.matches(rule.getRule_value())) {
+					return result;
+				}
+			} else {
+				if(result.equals("毕业证号") || result.equals("毕业证号:")) {
+					if(!RegexUtil.isPositiveNumber(list.get(i+1))) {
+						graduateNo = list.get(i+2);
+					} else {
+						graduateNo = list.get(i+1);
+					}
+				}
+			}
+		}
+		return graduateNo;
+	}
+	
+	
+	/**
+	 * @Description: 考生ocr识别结果json数据-转换为List
+	 * @param json 考生ocr识别结果
+	 * @date 2023-11-24 11:32:02 
+	 */  
+	public List<String> stdJsonToList(String json) {
+		List<String> list = new ArrayList<String>();
+		try {
+			JSONObject jo=JSONObject.parseObject(json);
+			if(jo == null) return list;
+			JSONArray js = jo.getJSONArray("words_result");
+			if(js == null) return list;
+			for(int i = 0; i<js.size(); i++) {
+				JSONObject jobj = JSONObject.parseObject(String.valueOf(js.get(i)));
+				list.add(jobj.get("words") == null ? null : String.valueOf(jobj.get("words")));
+			}
+		} catch(Exception e) {
+			e.printStackTrace();
+			return list;
+		}
+		return list;
+	}
+	
+	
+	/**
+	 * @Description: 获取教育层次
+	 * @param content
+	 * @date 2023-07-27 05:31:03 
+	 */  
+	private String getEduLevel(String content) {
+		if(content.contains("\"(专科)\"") || content.contains("\"(专科)\"") ) 
+			return "专科";
+		else if(content.contains("\"(本科)\"") || content.contains("\"(本科)\"")) 
+			return "本科";
+		else if(content.contains("\"(中专)\"") || content.contains("\"(中专)\"")) 
+			return "中专";
+		return null;
+	}
+	
+	
+	private File makeNewFile(File file, ByScanStd std) {
+		String rootDir = SysConst.getAppConfig().getFileOpencvDir();
+		String fileName = file.getName().substring(0, file.getName().lastIndexOf("."))+".png";
+		//TODO 
+		String[] filePathArr = std.getStorage_path().split(File.separator);
+		String filePath = "";
+		for(int i=0;i<filePathArr.length-1; i++) {
+			filePath += filePathArr[i]+File.separator;
+		}
+		return new File(rootDir + filePath+File.separator + fileName);
+	}
+	
+	/**
+	 * @Description: 将json数据写入到临时表中
+	 * @param std
+	 * @param json
+	 * @date 2023-11-23 05:21:21 
+	 */  
+	private final void insertStdJson(ByScanStd std, String json) {
+		if(json.length()<=3500) {
+			ByScanStdJson ss = new ByScanStdJson();
+			ss.setScan_std_id(std.getId());
+			ss.setStd_json(json);
+			stdJsonDao.insert(ss);
+		} else {
+			LogHelper.info(std.getId() + "超过了4000个字符!");
+		}
+	}
+	
+	
+	
+	
+	
+	/**
+	 * @Description: 查找专业名称
+	 * @param str
+	 * @param start 起始查找位置
+	 * @param level 要查找的层级
+	 * @date 2023-07-31 04:47:39 
+	 */  
+	private String findGraduateNo(String str, String rule, int start, int level, String flag) {
+		String graduateNo = null;
+		while(true) {
+			if("up".equals(flag)) {
+				graduateNo = findUpContent(str, start);
+			} else if("down".equals(flag)) {
+				graduateNo = findDownContent(str, start);
+			}
+			start++;
+			if(graduateNo.matches(rule) || level < start) break;
+		}
+		return graduateNo;
+	}
+	
+	/**
+	 * @Description: 向上查找
+	 * @param subStr 待查找的字符串
+	 * @param level 查找的层级数
+	 * @date 2023-07-28 06:32:14 
+	 */  
+	private String findUpContent(String subStr, int level) {
+		if(subStr == null) 
+			return null;
+		String str = subStr.substring(StringUtils.lastOrdinalIndexOf(subStr, "\"words\"", level+1),
+				StringUtils.lastOrdinalIndexOf(subStr, ",\"location\"", level));
+		return str.substring(str.indexOf("words\":") + 7).replace("\"", "");
+	}
+	
+	/**
+	 * @Description: 向下查找
+	 * @param subStr 待查找的字符串
+	 * @param level 查找的层级数
+	 * @date 2023-07-28 06:32:14 
+	 */  
+	private String findDownContent(String subStr, int level) {
+		if(subStr == null) 
+			return null;
+		String str = subStr.substring(StringUtils.ordinalIndexOf(subStr, ",\"location\"", level),
+				StringUtils.ordinalIndexOf(subStr, ",\"location\"", level+1));
+		return str.substring(str.indexOf("words\":") + 7).replace("\"", "");
+	}
+	
+	
+}

+ 85 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/CacheUtil.java

@@ -0,0 +1,85 @@
+package cn.hmsoft.scan.util;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.collections4.map.HashedMap;
+
+import cn.hmsoft.scan.common.constants.SysConst;
+import cn.hmsoft.scan.data.dao.cf.CfMajorDao;
+import cn.hmsoft.scan.data.dao.cf.CfScanRuleDao;
+import cn.hmsoft.scan.data.model.cf.CfMajor;
+import cn.hmsoft.scan.data.model.cf.CfScanRule;
+
+/**  
+ * @Description: 系统使用的缓存,后期使用的缓存加在这里
+ * @author hgh
+ * @date 2023-07-31 03:15:41 
+ */
+public final class CacheUtil {
+
+	private static CacheUtil util = null;
+	private static List<String> majorList = null; // 专业字典缓存
+	private static Map<String, CfScanRule> ruleMap;// 规则缓存
+	private static CfMajorDao majorDao;
+	private static CfScanRuleDao ruleDao;
+	
+	private CacheUtil() {
+		majorDao = SysConst.getCurrentWebApplicationContext().getBean(CfMajorDao.class);
+		ruleDao = SysConst.getCurrentWebApplicationContext().getBean(CfScanRuleDao.class);
+	}
+	
+	static {
+		util = new CacheUtil();
+	}
+	
+	public static CacheUtil instance() {
+		return instance(false);
+	}
+
+	public static CacheUtil instance(boolean refresh) {
+		if(majorList == null || majorList.size() == 0 || refresh) {
+			synchronized (CacheUtil.class) {
+				if(majorList == null || majorList.size() == 0 || refresh) {
+					List<String> tmpList = new ArrayList<>();
+					List<CfMajor> cmList = majorDao.all();
+					for(CfMajor major : cmList) {
+						tmpList.add(major.getMajor_name());
+					}
+					majorList = tmpList;
+					Map<String,CfScanRule> ruleTmpMap = new HashedMap<>();
+					List<CfScanRule> ruleList = ruleDao.all();
+					for (CfScanRule rule : ruleList) {
+						ruleTmpMap.put(rule.getYear()+"-"+rule.getMonth(), rule);
+					}
+					ruleMap = ruleTmpMap;
+				}
+			}
+		}
+		return util;
+	}
+	
+	
+	/**
+	 * @Description: 是否在专业缓存中
+	 * @param majorName 专业名称
+	 * @date 2023-07-31 03:40:02 
+	 */  
+	public boolean isContainsMajor(String majorName) {
+		instance();
+		return majorList.contains(majorName);
+	}
+	
+	
+	/**
+	 * @Description: 获取扫描规则缓存
+	 * @date 2023-07-31 05:58:00 
+	 */  
+	public CfScanRule getScanRule(String key) {
+		instance();
+		return ruleMap.get(key);
+	}
+	
+	
+}

+ 112 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/ChineseDateUtil.java

@@ -0,0 +1,112 @@
+package cn.hmsoft.scan.util;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * @author hgh
+ *  日期转中文
+ */
+public class ChineseDateUtil {
+
+	/**
+	 * @param date: yyyy-MM
+	 * @return 返回年月日期的中文
+	 */
+	public static String getChineseDate(LocalDate date) {
+		String chineseDate = "";
+		String dateStr = date.format(DateTimeFormatter.ofPattern("yyyy-MM"));
+		String[] strs = dateStr.split("-");
+		// 年
+		for (int i = 0; i < strs[0].length(); i++) {
+			chineseDate += formatDigit(strs[0].charAt(i));
+		}
+		// 月
+		char c1 = strs[1].charAt(0);
+		char c2 = strs[1].charAt(1);
+		String newmonth = "";
+		if (c1 == '0') {
+			newmonth = String.valueOf(formatDigit(c2));
+		} else if (c1 == '1' && c2 == '0') {
+			newmonth = "十";
+		} else if (c1 == '1' && c2 != '0') {
+			newmonth = "十" + formatDigit(c2);
+		}
+		chineseDate = chineseDate + "年" + newmonth + "月";
+		return chineseDate;
+	}
+	
+	/**
+	 * @param date: yyyy-MM-dd
+	 * @return 返回年月日期的中文
+	 */
+	public static String queryChineseDate(LocalDate date) {
+		String chineseDate = "";
+		String dateStr = date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+		String[] strs = dateStr.split("-");
+		// 年
+		for (int i = 0; i < strs[0].length(); i++) {
+			chineseDate += formatDigit(strs[0].charAt(i));
+		}
+		// 月
+		char c1 = strs[1].charAt(0);
+		char c2 = strs[1].charAt(1);
+		String newmonth = "";
+		if (c1 == '0') {
+			newmonth = String.valueOf(formatDigit(c2));
+		} else if (c1 == '1' && c2 == '0') {
+			newmonth = "十";
+		} else if (c1 == '1' && c2 != '0') {
+			newmonth = "十" + formatDigit(c2);
+		}
+		
+		// 日
+		char d1 = strs[2].charAt(0);
+		char d2 = strs[2].charAt(1);
+		String newdate = "";
+		if (d1 == '0') {
+			newdate = String.valueOf(formatDigit(d2));
+		} else if (d1 == '1' && d2 == '0') {
+			newdate = "十";
+		} else if (d1 == '2' && d2 == '0') {
+			newdate = "二十";
+		} else if (d1 == '3' && d2 == '0') {
+			newdate = "三十";
+		} else if (d1 == '1' && d2 != '0') {
+			newdate = "十" + formatDigit(d2);
+		} else if (d1 == '2' && d2 != '0') {
+			newdate = "二十" + formatDigit(d2);
+		} else if (d1 == '3' && d2 != '0') {
+			newdate = "三十" + formatDigit(d2);
+		}
+				
+				
+		chineseDate = chineseDate + "年" + newmonth + "月"+newdate+"日";
+		return chineseDate;
+	}
+
+	public static char formatDigit(char sign) {
+		if (sign == '0')
+			sign = '〇';
+		if (sign == '1')
+			sign = '一';
+		if (sign == '2')
+			sign = '二';
+		if (sign == '3')
+			sign = '三';
+		if (sign == '4')
+			sign = '四';
+		if (sign == '5')
+			sign = '五';
+		if (sign == '6')
+			sign = '六';
+		if (sign == '7')
+			sign = '七';
+		if (sign == '8')
+			sign = '八';
+		if (sign == '9')
+			sign = '九';
+		return sign;
+	}
+
+}

+ 40 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/CommonToolUtil.java

@@ -0,0 +1,40 @@
+package cn.hmsoft.scan.util;
+
+import java.util.List;
+
+import org.springframework.util.StringUtils;
+
+import cn.hmsoft.frame.data.model.FrameDict;
+
+public class CommonToolUtil {
+	public static boolean isWindowSystem(){
+		String os = System.getProperty("os.name");  
+		if(os.toLowerCase().startsWith("win")){
+			return true;
+		}else {
+			return false;
+		}
+	}
+	
+	public static boolean isLinuxSystem(){
+		String os = System.getProperty("os.name");  
+		if(os.toLowerCase().startsWith("win")){
+			return true;
+		}else {
+			return false;
+		}
+	}
+	
+	public static String getDictText(List<FrameDict> list, String value){
+		for (FrameDict frameDict : list) {
+			if(frameDict.getDict_value().equals(value)){
+				return frameDict.getDict_text();
+			}
+		}
+		return StringUtils.isEmpty(value)?"":value;
+	}
+	
+	public static String null2blank(Object val){
+		return (StringUtils.isEmpty(val) || val == null)?"":String.valueOf(val);
+	}
+}

+ 47 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/CommonUtils.java

@@ -0,0 +1,47 @@
+package cn.hmsoft.scan.util;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.springframework.util.StringUtils;
+
+import cn.hmsoft.frame.data.model.FrameDict;
+import cn.hmsoft.helper.StringHelper;
+
+public class CommonUtils {
+
+	public static String getDictText(List<FrameDict> list, String value){
+		for (FrameDict frameDict : list) {
+			if(frameDict.getDict_value().equals(value)){
+				return frameDict.getDict_text();
+			}
+		}
+		return value;
+	}
+	
+	public static String null2blank(Object val){
+		return (StringUtils.isEmpty(val) || val == null || (Integer)val == 0)?"":String.valueOf(val);
+	}
+	
+	public static String zero2blank(Integer val){
+		if (val == null) {
+			return "";
+		}
+		return val==0?"":String.valueOf(val);
+	}
+	
+	public static String getStringTrimVal(String str) {
+		if (StringHelper.isNotEmpty(str)) {
+			String dest = null;
+			Pattern p = Pattern.compile("\\s*|\t|\r|\n");
+			Matcher m = p.matcher(str);
+			dest = m.replaceAll("");
+			dest = dest.replaceAll(" ", "");
+			
+			return dest;
+		}
+		
+		return "";
+	}
+}

+ 121 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/DbfUtil.java

@@ -0,0 +1,121 @@
+package cn.hmsoft.scan.util;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.linuxense.javadbf.DBFField;
+import com.linuxense.javadbf.DBFReader;
+import com.linuxense.javadbf.DBFWriter;
+
+public class DbfUtil {
+
+	public static void readDBF(String path)
+	{
+		InputStream fis = null;
+		try {
+			// 读取文件的输入流
+			fis = new FileInputStream(path);
+			// 根据输入流初始化一个DBFReader实例,用来读取DBF文件信息
+			DBFReader reader = new DBFReader(fis);
+			reader.setCharactersetName("GBK");
+			// 调用DBFReader对实例方法得到path文件中字段的个数
+			int fieldsCount = reader.getFieldCount();
+			System.out.println("字段数:" + fieldsCount);
+			// 取出字段信息
+			for (int i = 0; i < fieldsCount; i++) {
+				DBFField field = reader.getField(i);
+				System.out.println(field.getName());
+			}
+			Object[] rowValues;
+			
+			// 一条条取出path文件中记录
+			while ((rowValues = reader.nextRecord()) != null) {
+				for (int i = 0; i < rowValues.length; i++) {
+					System.out.println(rowValues[i]);
+				}
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		} finally {
+			try {
+				fis.close();
+			} catch (Exception e) {
+			}
+		}
+	}
+
+	public static void writeDBF(String path) {
+
+		OutputStream fos = null;
+		try {
+			// 定义DBF文件字段
+			DBFField[] fields = new DBFField[3];
+			// 分别定义各个字段信息,setFieldName和setName作用相同,
+			// 只是setFieldName已经不建议使用
+			fields[0] = new DBFField();
+			// fields[0].setFieldName("emp_code");
+			fields[0].setName("semp_code");
+			fields[0].setDataType(DBFField.FIELD_TYPE_C);
+			fields[0].setFieldLength(10);
+
+			fields[1] = new DBFField();
+			// fields[1].setFieldName("emp_name");
+			fields[1].setName("emp_name");
+			fields[1].setDataType(DBFField.FIELD_TYPE_C);
+			fields[1].setFieldLength(20);
+
+			fields[2] = new DBFField();
+			// fields[2].setFieldName("salary");
+			fields[2].setName("salary");
+			fields[2].setDataType(DBFField.FIELD_TYPE_N);
+			fields[2].setFieldLength(12);
+			fields[2].setDecimalCount(2);
+
+			// DBFWriter writer = new DBFWriter(new File(path));
+
+			// 定义DBFWriter实例用来写DBF文件
+			DBFWriter writer = new DBFWriter();
+			// 把字段信息写入DBFWriter实例,即定义表结构
+			writer.setFields(fields);
+			// 一条条的写入记录
+			Object[] rowData = new Object[3];
+			rowData[0] = "1000";
+			rowData[1] = "John";
+			rowData[2] = new Double(5000.00);
+			writer.addRecord(rowData);
+
+			rowData = new Object[3];
+			rowData[0] = "1001";
+			rowData[1] = "Lalit";
+			rowData[2] = new Double(3400.00);
+			writer.addRecord(rowData);
+
+			rowData = new Object[3];
+			rowData[0] = "1002";
+			rowData[1] = "Rohit";
+			rowData[2] = new Double(7350.00);
+			writer.addRecord(rowData);
+
+			// 定义输出流,并关联的一个文件
+			fos = new FileOutputStream(path);
+			// 写入数据
+			writer.write(fos);
+
+			// writer.write();
+		} catch (Exception e) {
+			e.printStackTrace();
+		} finally {
+			try {
+				fos.close();
+			} catch (Exception e) {
+			}
+		}
+	}
+
+	public static void main(String[] args) {
+		readDBF("C:\\Users\\huangzibo\\Desktop\\档案管理\\by_data.dbf");
+	}
+
+}

+ 58 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/FmtMicrometer.java

@@ -0,0 +1,58 @@
+package cn.hmsoft.scan.util;
+import java.text.DecimalFormat;
+
+import org.springframework.util.StringUtils;
+ 
+/**
+ * @ClassName: FmtMicrometer
+ * @Description: 格式化数字为千分位工具类
+ * @author wsq	E-mail:
+ * @date 2017-6-1 下午02:25:57
+ * 
+ */
+public class FmtMicrometer {
+	
+	/**
+	 * @Title: fmtMicrometer
+	 * @Description: 格式化数字为千分位
+	 * @param text
+	 * @return    设定文件
+	 * @return String    返回类型
+	 */
+	public static String fmtMicrometer(String text) {
+		if(StringUtils.isEmpty(text) || "0".equals(text) || "0.0".equals(text)){
+			return "-";
+		}
+		if (text.endsWith(".0")) {
+			text = text.substring(0, text.length() -2);
+		}
+		DecimalFormat df = null;
+		if (text.indexOf(".") > 0) {
+			if (text.length() - text.indexOf(".") - 1 == 0) {
+				df = new DecimalFormat("###,##0.");
+			} else if (text.length() - text.indexOf(".") - 1 == 1) {
+				df = new DecimalFormat("###,##0.0");
+			} else {
+				df = new DecimalFormat("###,##0.00");
+			}
+		} else {
+			df = new DecimalFormat("###,##0");
+		}
+		double number = 0.0;
+		try {
+			number = Double.parseDouble(text);
+		} catch (Exception e) {
+			number = 0.0;
+		}
+		return df.format(number);
+	}
+	
+	public static void main(String[] args) {
+		String text = "123123123.0";
+		text = text.substring(0, text.length() -2);
+		System.out.println(text);
+	}
+	
+}
+
+ 

+ 702 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/FrameIdCardUtil.java

@@ -0,0 +1,702 @@
+package cn.hmsoft.scan.util;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**************************
+ * @note 身份证工具
+ */
+public class FrameIdCardUtil {
+	/** 中国公民身份证号码最小长度。 */
+	public static final int CHINA_ID_MIN_LENGTH = 15;
+
+	/** 中国公民身份证号码最大长度。 */
+	public static final int CHINA_ID_MAX_LENGTH = 18;
+
+	/** 省、直辖市代码表 */
+	public static final String cityCode[] = { "11", "12", "13", "14", "15", "21", "22", "23", "31", "32", "33", "34", "35", "36", "37", "41", "42", "43", "44",
+			"45", "46", "50", "51", "52", "53", "54", "61", "62", "63", "64", "65", "71", "81", "82", "91" };
+
+	/** 每位加权因子 */
+	public static final int power[] = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };
+
+	/** 第18位校检码 */
+	public static final String verifyCode[] = { "1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2" };
+	/** 最低年限 */
+	public static final int MIN = 1930;
+	public static Map<String, String> cityCodes = new HashMap<String, String>();
+	/** 台湾身份首字母对应数字 */
+	public static Map<String, Integer> twFirstCode = new HashMap<String, Integer>();
+	/** 香港身份首字母对应数字 */
+	public static Map<String, Integer> hkFirstCode = new HashMap<String, Integer>();
+	static {
+		cityCodes.put("11", "北京");
+		cityCodes.put("12", "天津");
+		cityCodes.put("13", "河北");
+		cityCodes.put("14", "山西");
+		cityCodes.put("15", "内蒙古");
+		cityCodes.put("21", "辽宁");
+		cityCodes.put("22", "吉林");
+		cityCodes.put("23", "黑龙江");
+		cityCodes.put("31", "上海");
+		cityCodes.put("32", "江苏");
+		cityCodes.put("33", "浙江");
+		cityCodes.put("34", "安徽");
+		cityCodes.put("35", "福建");
+		cityCodes.put("36", "江西");
+		cityCodes.put("37", "山东");
+		cityCodes.put("41", "河南");
+		cityCodes.put("42", "湖北");
+		cityCodes.put("43", "湖南");
+		cityCodes.put("44", "广东");
+		cityCodes.put("45", "广西");
+		cityCodes.put("46", "海南");
+		cityCodes.put("50", "重庆");
+		cityCodes.put("51", "四川");
+		cityCodes.put("52", "贵州");
+		cityCodes.put("53", "云南");
+		cityCodes.put("54", "西藏");
+		cityCodes.put("61", "陕西");
+		cityCodes.put("62", "甘肃");
+		cityCodes.put("63", "青海");
+		cityCodes.put("64", "宁夏");
+		cityCodes.put("65", "新疆");
+		cityCodes.put("71", "台湾");
+		cityCodes.put("81", "香港");
+		cityCodes.put("82", "澳门");
+		cityCodes.put("91", "国外");
+		twFirstCode.put("A", 10);
+		twFirstCode.put("B", 11);
+		twFirstCode.put("C", 12);
+		twFirstCode.put("D", 13);
+		twFirstCode.put("E", 14);
+		twFirstCode.put("F", 15);
+		twFirstCode.put("G", 16);
+		twFirstCode.put("H", 17);
+		twFirstCode.put("J", 18);
+		twFirstCode.put("K", 19);
+		twFirstCode.put("L", 20);
+		twFirstCode.put("M", 21);
+		twFirstCode.put("N", 22);
+		twFirstCode.put("P", 23);
+		twFirstCode.put("Q", 24);
+		twFirstCode.put("R", 25);
+		twFirstCode.put("S", 26);
+		twFirstCode.put("T", 27);
+		twFirstCode.put("U", 28);
+		twFirstCode.put("V", 29);
+		twFirstCode.put("X", 30);
+		twFirstCode.put("Y", 31);
+		twFirstCode.put("W", 32);
+		twFirstCode.put("Z", 33);
+		twFirstCode.put("I", 34);
+		twFirstCode.put("O", 35);
+		hkFirstCode.put("A", 1);
+		hkFirstCode.put("B", 2);
+		hkFirstCode.put("C", 3);
+		hkFirstCode.put("R", 18);
+		hkFirstCode.put("U", 21);
+		hkFirstCode.put("Z", 26);
+		hkFirstCode.put("X", 24);
+		hkFirstCode.put("W", 23);
+		hkFirstCode.put("O", 15);
+		hkFirstCode.put("N", 14);
+	}
+
+	/**
+	 * 将15位身份证号码转换为18位
+	 * 
+	 * @param idCard
+	 *            15位身份编码
+	 * @return 18位身份编码
+	 */
+	public static String conver15CardTo18(String idCard) {
+		String idCard18 = "";
+		if (idCard.length() != CHINA_ID_MIN_LENGTH) {
+			return null;
+		}
+		if (isNum(idCard)) {
+			// 获取出生年月日
+			String birthday = idCard.substring(6, 12);
+			Date birthDate = null;
+			try {
+				birthDate = new SimpleDateFormat("yyMMdd").parse(birthday);
+			} catch (ParseException e) {
+				e.printStackTrace();
+			}
+			Calendar cal = Calendar.getInstance();
+			if (birthDate != null)
+				cal.setTime(birthDate);
+			// 获取出生年(完全表现形式,如:2010)
+			String sYear = String.valueOf(cal.get(Calendar.YEAR));
+			idCard18 = idCard.substring(0, 6) + sYear + idCard.substring(8);
+			// 转换字符数组
+			char[] cArr = idCard18.toCharArray();
+			if (cArr != null) {
+				int[] iCard = converCharToInt(cArr);
+				int iSum17 = getPowerSum(iCard);
+				// 获取校验位
+				String sVal = getCheckCode18(iSum17);
+				if (sVal.length() > 0) {
+					idCard18 += sVal;
+				} else {
+					return null;
+				}
+			}
+		} else {
+			return null;
+		}
+		return idCard18;
+	}
+
+	/**
+	 * 验证身份证是否合法
+	 */
+	public static boolean validateCard(String idCard) {
+		String card = idCard.trim();
+		if (validateIdCard18(card)) {
+			return true;
+		}
+		if (validateIdCard15(card)) {
+			return true;
+		}
+		String[] cardval = validateIdCard10(card);
+		if (cardval != null) {
+			if (cardval[2].equals("true")) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * 验证18位身份编码是否合法
+	 * 
+	 * @param idCard
+	 *            身份编码
+	 * @return 是否合法
+	 */
+	public static boolean validateIdCard18(String idCard) {
+		boolean bTrue = false;
+		if (idCard.length() == CHINA_ID_MAX_LENGTH) {
+			// 前17位
+			String code17 = idCard.substring(0, 17);
+			// 第18位
+			String code18 = idCard.substring(17, CHINA_ID_MAX_LENGTH);
+			if (isNum(code17)) {
+				char[] cArr = code17.toCharArray();
+				if (cArr != null) {
+					int[] iCard = converCharToInt(cArr);
+					int iSum17 = getPowerSum(iCard);
+					// 获取校验位
+					String val = getCheckCode18(iSum17);
+					if (val.length() > 0) {
+						if (val.equalsIgnoreCase(code18)) {
+							bTrue = true;
+						}
+					}
+				}
+			}
+		}
+		return bTrue;
+	}
+
+	/**
+	 * 验证15位身份编码是否合法
+	 * 
+	 * @param idCard
+	 *            身份编码
+	 * @return 是否合法
+	 */
+	public static boolean validateIdCard15(String idCard) {
+		if (idCard.length() != CHINA_ID_MIN_LENGTH) {
+			return false;
+		}
+		if (isNum(idCard)) {
+			String proCode = idCard.substring(0, 2);
+			if (cityCodes.get(proCode) == null) {
+				return false;
+			}
+			String birthCode = idCard.substring(6, 12);
+			Date birthDate = null;
+			try {
+				birthDate = new SimpleDateFormat("yy").parse(birthCode.substring(0, 2));
+			} catch (ParseException e) {
+				e.printStackTrace();
+			}
+			Calendar cal = Calendar.getInstance();
+			if (birthDate != null)
+				cal.setTime(birthDate);
+			if (!valiDate(cal.get(Calendar.YEAR), Integer.valueOf(birthCode.substring(2, 4)), Integer.valueOf(birthCode.substring(4, 6)))) {
+				return false;
+			}
+		} else {
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * 验证10位身份编码是否合法
+	 * 
+	 * @param idCard
+	 *            身份编码
+	 * @return 身份证信息数组
+	 *         <p>
+	 *         [0] - 台湾、澳门、香港 [1] - 性别(男M,女F,未知N) [2] - 是否合法(合法true,不合法false)
+	 *         若不是身份证件号码则返回null
+	 *         </p>
+	 */
+	public static String[] validateIdCard10(String idCard) {
+		String[] info = new String[3];
+		String card = idCard.replaceAll("[\\(|\\)]", "");
+		if (card.length() != 8 && card.length() != 9 && idCard.length() != 10) {
+			return null;
+		}
+		if (idCard.matches("^[a-zA-Z][0-9]{9}{1}")) { // 台湾
+			info[0] = "台湾";
+			System.out.println("11111");
+			String char2 = idCard.substring(1, 2);
+			if (char2.equals("1")) {
+				info[1] = "M";
+				System.out.println("MMMMMMM");
+			} else if (char2.equals("2")) {
+				info[1] = "F";
+				System.out.println("FFFFFFF");
+			} else {
+				info[1] = "N";
+				info[2] = "false";
+				System.out.println("NNNN");
+				return info;
+			}
+			info[2] = validateTWCard(idCard) ? "true" : "false";
+		} else if (idCard.matches("^[1|5|7][0-9]{6}\\(?[0-9A-Z]\\)?{1}")) { // 澳门
+			info[0] = "澳门";
+			info[1] = "N";
+		} else if (idCard.matches("^[A-Z]{1,2}[0-9]{6}\\(?[0-9A]\\)?{1}")) { // 香港
+			info[0] = "香港";
+			info[1] = "N";
+			info[2] = validateHKCard(idCard) ? "true" : "false";
+		} else {
+			return null;
+		}
+		return info;
+	}
+
+	/**
+	 * 验证台湾身份证号码
+	 * 
+	 * @param idCard
+	 *            身份证号码
+	 * @return 验证码是否符合
+	 */
+	public static boolean validateTWCard(String idCard) {
+		String start = idCard.substring(0, 1);
+		String mid = idCard.substring(1, 9);
+		String end = idCard.substring(9, 10);
+		Integer iStart = twFirstCode.get(start);
+		Integer sum = iStart / 10 + (iStart % 10) * 9;
+		char[] chars = mid.toCharArray();
+		Integer iflag = 8;
+		for (char c : chars) {
+			sum = sum + Integer.valueOf(c + "") * iflag;
+			iflag--;
+		}
+		return (sum % 10 == 0 ? 0 : (10 - sum % 10)) == Integer.valueOf(end) ? true : false;
+	}
+
+	/**
+	 * 验证香港身份证号码(存在Bug,部份特殊身份证无法检查)
+	 * <p>
+	 * 身份证前2位为英文字符,如果只出现一个英文字符则表示第一位是空格,对应数字58 前2位英文字符A-Z分别对应数字10-35
+	 * 最后一位校验码为0-9的数字加上字符"A","A"代表10
+	 * </p>
+	 * <p>
+	 * 将身份证号码全部转换为数字,分别对应乘9-1相加的总和,整除11则证件号码有效
+	 * </p>
+	 * 
+	 * @param idCard
+	 *            身份证号码
+	 * @return 验证码是否符合
+	 */
+	public static boolean validateHKCard(String idCard) {
+		String card = idCard.replaceAll("[\\(|\\)]", "");
+		Integer sum = 0;
+		if (card.length() == 9) {
+			sum = (Integer.valueOf(card.substring(0, 1).toUpperCase().toCharArray()[0]) - 55) * 9
+					+ (Integer.valueOf(card.substring(1, 2).toUpperCase().toCharArray()[0]) - 55) * 8;
+			card = card.substring(1, 9);
+		} else {
+			sum = 522 + (Integer.valueOf(card.substring(0, 1).toUpperCase().toCharArray()[0]) - 55) * 8;
+		}
+		String mid = card.substring(1, 7);
+		String end = card.substring(7, 8);
+		char[] chars = mid.toCharArray();
+		Integer iflag = 7;
+		for (char c : chars) {
+			sum = sum + Integer.valueOf(c + "") * iflag;
+			iflag--;
+		}
+		if (end.toUpperCase().equals("A")) {
+			sum = sum + 10;
+		} else {
+			sum = sum + Integer.valueOf(end);
+		}
+		return (sum % 11 == 0) ? true : false;
+	}
+
+	/**
+	 * 将字符数组转换成数字数组
+	 * 
+	 * @param ca
+	 *            字符数组
+	 * @return 数字数组
+	 */
+	private static int[] converCharToInt(char[] ca) {
+		int len = ca.length;
+		int[] iArr = new int[len];
+		try {
+			for (int i = 0; i < len; i++) {
+				iArr[i] = Integer.parseInt(String.valueOf(ca[i]));
+			}
+		} catch (NumberFormatException e) {
+			e.printStackTrace();
+		}
+		return iArr;
+	}
+
+	/**
+	 * 将身份证的每位和对应位的加权因子相乘之后,再得到和值
+	 * 
+	 * @param iArr
+	 * @return 身份证编码。
+	 */
+	private static int getPowerSum(int[] iArr) {
+		int iSum = 0;
+		if (power.length == iArr.length) {
+			for (int i = 0; i < iArr.length; i++) {
+				for (int j = 0; j < power.length; j++) {
+					if (i == j) {
+						iSum = iSum + iArr[i] * power[j];
+					}
+				}
+			}
+		}
+		return iSum;
+	}
+
+	/**
+	 * 将power和值与11取模获得余数进行校验码判断
+	 * 
+	 * @param iSum
+	 * @return 校验位
+	 */
+	private static String getCheckCode18(int iSum) {
+		String sCode = "";
+		switch (iSum % 11) {
+		case 10:
+			sCode = "2";
+			break;
+		case 9:
+			sCode = "3";
+			break;
+		case 8:
+			sCode = "4";
+			break;
+		case 7:
+			sCode = "5";
+			break;
+		case 6:
+			sCode = "6";
+			break;
+		case 5:
+			sCode = "7";
+			break;
+		case 4:
+			sCode = "8";
+			break;
+		case 3:
+			sCode = "9";
+			break;
+		case 2:
+			sCode = "x";
+			break;
+		case 1:
+			sCode = "0";
+			break;
+		case 0:
+			sCode = "1";
+			break;
+		}
+		return sCode;
+	}
+
+	/**
+	 * 根据身份编号获取年龄
+	 * 
+	 * @param idCard
+	 *            身份编号
+	 * @return 年龄
+	 */
+	public static int getAgeByIdCard(String idCard) {
+		int iAge = 0;
+		if (idCard.length() == CHINA_ID_MIN_LENGTH) {
+			idCard = conver15CardTo18(idCard);
+		}
+		String year = idCard.substring(6, 10);
+		Calendar cal = Calendar.getInstance();
+		int iCurrYear = cal.get(Calendar.YEAR);
+		iAge = iCurrYear - Integer.valueOf(year);
+		return iAge;
+	}
+
+	/**
+	 * 根据身份编号获取生日
+	 * 
+	 * @param idCard
+	 *            身份编号
+	 * @return 生日(yyyyMMdd)
+	 */
+//	public static Date getBirthByIdCard(String idCard) {
+//		Integer len = idCard.length();
+//		if (len < CHINA_ID_MIN_LENGTH) {
+//			return null;
+//		} else if (len == CHINA_ID_MIN_LENGTH) {
+//			idCard = conver15CardTo18(idCard);
+//		}
+//		
+//		return DateHelper.par(idCard.substring(6, 14),DateHelper.ShortTimeStringWithoutSplit);
+//	}
+
+	/**
+	 * 根据身份编号获取生日年
+	 * 
+	 * @param idCard
+	 *            身份编号
+	 * @return 生日(yyyy)
+	 */
+	public static Short getYearByIdCard(String idCard) {
+		Integer len = idCard.length();
+		if (len < CHINA_ID_MIN_LENGTH) {
+			return null;
+		} else if (len == CHINA_ID_MIN_LENGTH) {
+			idCard = conver15CardTo18(idCard);
+		}
+		return Short.valueOf(idCard.substring(6, 10));
+	}
+
+	/**
+	 * 根据身份编号获取生日月
+	 * 
+	 * @param idCard
+	 *            身份编号
+	 * @return 生日(MM)
+	 */
+	public static Short getMonthByIdCard(String idCard) {
+		Integer len = idCard.length();
+		if (len < CHINA_ID_MIN_LENGTH) {
+			return null;
+		} else if (len == CHINA_ID_MIN_LENGTH) {
+			idCard = conver15CardTo18(idCard);
+		}
+		return Short.valueOf(idCard.substring(10, 12));
+	}
+
+	/**
+	 * 根据身份编号获取生日天
+	 * 
+	 * @param idCard
+	 *            身份编号
+	 * @return 生日(dd)
+	 */
+	public static Short getDateByIdCard(String idCard) {
+		Integer len = idCard.length();
+		if (len < CHINA_ID_MIN_LENGTH) {
+			return null;
+		} else if (len == CHINA_ID_MIN_LENGTH) {
+			idCard = conver15CardTo18(idCard);
+		}
+		return Short.valueOf(idCard.substring(12, 14));
+	}
+
+	/**
+	 * 根据身份编号获取性别
+	 * 
+	 * @param idCard
+	 *            身份编号
+	 * @return 性别(M-男,F-女,N-未知)
+	 */
+	public static String getGenderByIdCard(String idCard) {
+		String sGender = "男";
+		if (idCard.length() == CHINA_ID_MIN_LENGTH) {
+			idCard = conver15CardTo18(idCard);
+		}
+		String sCardNum = idCard.substring(16, 17);
+		if (Integer.parseInt(sCardNum) % 2 != 0) {
+			sGender = "男";
+		} else {
+			sGender = "女";
+		}
+		return sGender;
+	}
+
+	/**
+	 * 根据身份编号获取户籍省份
+	 * 
+	 * @param idCard
+	 *            身份编码
+	 * @return 省级编码。
+	 */
+	public static String getProvinceByIdCard(String idCard) {
+		int len = idCard.length();
+		//String sProvince = null;
+		String sProvinNum = "";
+		if (len == CHINA_ID_MIN_LENGTH || len == CHINA_ID_MAX_LENGTH) {
+			sProvinNum = idCard.substring(0, 2);
+		}
+		//sProvince = cityCodes.get(sProvinNum);
+		return sProvinNum;
+	}
+
+	/**
+	 * 数字验证
+	 * 
+	 * @param val
+	 * @return 提取的数字。
+	 */
+	public static boolean isNum(String val) {
+		return val == null || "".equals(val) ? false : val.matches("^[0-9]*{1}");
+	}
+
+	/**
+	 * 验证小于当前日期 是否有效
+	 * 
+	 * @param iYear
+	 *            待验证日期(年)
+	 * @param iMonth
+	 *            待验证日期(月 1-12)
+	 * @param iDate
+	 *            待验证日期(日)
+	 * @return 是否有效
+	 */
+	public static boolean valiDate(int iYear, int iMonth, int iDate) {
+		Calendar cal = Calendar.getInstance();
+		int year = cal.get(Calendar.YEAR);
+		int datePerMonth;
+		if (iYear < MIN || iYear >= year) {
+			return false;
+		}
+		if (iMonth < 1 || iMonth > 12) {
+			return false;
+		}
+		switch (iMonth) {
+		case 4:
+		case 6:
+		case 9:
+		case 11:
+			datePerMonth = 30;
+			break;
+		case 2:
+			boolean dm = ((iYear % 4 == 0 && iYear % 100 != 0) || (iYear % 400 == 0)) && (iYear > MIN && iYear < year);
+			datePerMonth = dm ? 29 : 28;
+			break;
+		default:
+			datePerMonth = 31;
+		}
+		return (iDate >= 1) && (iDate <= datePerMonth);
+	}
+
+	/**
+	 * 根据身份证号,自动获取对应的星座
+	 * 
+	 * @param idCard
+	 *            身份证号码
+	 * @return 星座
+	 */
+	public static String getConstellationById(String idCard) {
+		if (!validateCard(idCard))
+			return "";
+		int month = getMonthByIdCard(idCard);
+		int day = getDateByIdCard(idCard);
+		String strValue = "";
+
+		if ((month == 1 && day >= 20) || (month == 2 && day <= 18)) {
+			strValue = "水瓶座";
+		} else if ((month == 2 && day >= 19) || (month == 3 && day <= 20)) {
+			strValue = "双鱼座";
+		} else if ((month == 3 && day > 20) || (month == 4 && day <= 19)) {
+			strValue = "白羊座";
+		} else if ((month == 4 && day >= 20) || (month == 5 && day <= 20)) {
+			strValue = "金牛座";
+		} else if ((month == 5 && day >= 21) || (month == 6 && day <= 21)) {
+			strValue = "双子座";
+		} else if ((month == 6 && day > 21) || (month == 7 && day <= 22)) {
+			strValue = "巨蟹座";
+		} else if ((month == 7 && day > 22) || (month == 8 && day <= 22)) {
+			strValue = "狮子座";
+		} else if ((month == 8 && day >= 23) || (month == 9 && day <= 22)) {
+			strValue = "处女座";
+		} else if ((month == 9 && day >= 23) || (month == 10 && day <= 23)) {
+			strValue = "天秤座";
+		} else if ((month == 10 && day > 23) || (month == 11 && day <= 22)) {
+			strValue = "天蝎座";
+		} else if ((month == 11 && day > 22) || (month == 12 && day <= 21)) {
+			strValue = "射手座";
+		} else if ((month == 12 && day > 21) || (month == 1 && day <= 19)) {
+			strValue = "魔羯座";
+		}
+
+		return strValue;
+	}
+
+	/**
+	 * 根据身份证号,自动获取对应的生肖
+	 * 
+	 * @param idCard
+	 *            身份证号码
+	 * @return 生肖
+	 */
+	public static String getZodiacById(String idCard) { // 根据身份证号,自动返回对应的生肖
+		if (!validateCard(idCard))
+			return "";
+
+		String sSX[] = { "猪", "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗" };
+		int year = getYearByIdCard(idCard);
+		int end = 3;
+		int x = (year - end) % 12;
+
+		String retValue = "";
+		retValue = sSX[x];
+
+		return retValue;
+	}
+
+	/**
+	 * 根据身份证号,自动获取对应的天干地支
+	 * 
+	 * @param idCard
+	 *            身份证号码
+	 * @return 天干地支
+	 */
+	public static String getChineseEraById(String idCard) { // 根据身份证号,自动返回对应的生肖
+		if (!validateCard(idCard))
+			return "";
+
+		String sTG[] = { "癸", "甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "任" };
+		String sDZ[] = { "亥", "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌" };
+
+		int year = getYearByIdCard(idCard);
+		int i = (year - 3) % 10;
+		int j = (year - 3) % 12;
+
+		String retValue = "";
+		retValue = sTG[i] + sDZ[j];
+
+		return retValue;
+	}
+}

+ 296 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/GoogleBarCodeUtils.java

@@ -0,0 +1,296 @@
+package cn.hmsoft.scan.util;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.Stroke;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReadParam;
+import javax.imageio.ImageReader;
+import javax.imageio.stream.ImageInputStream;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.LuminanceSource;
+import com.google.zxing.MultiFormatReader;
+import com.google.zxing.Result;
+import com.google.zxing.WriterException;
+import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
+import com.google.zxing.client.j2se.MatrixToImageWriter;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.common.HybridBinarizer;
+import com.google.zxing.oned.Code128Writer;
+import com.google.zxing.qrcode.QRCodeWriter;
+
+import cn.hmsoft.helper.LogHelper;
+
+
+/**
+ * @description: 谷歌条形码打印
+ * @author: Administrator
+ * @date: 2019-03-07 14:46
+ */
+public class GoogleBarCodeUtils {
+    /** 条形码宽度 */
+    private static final int WIDTH = 115;
+
+    /** 条形码高度 */
+    private static final int HEIGHT = 30;
+
+    /** 加文字 条形码 */
+    private static final int WORDHEIGHT = 65;
+    /**
+     * 设置 条形码参数
+     */
+    private static Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>() {
+        private static final long serialVersionUID = 1L;
+        {
+            // 设置编码方式
+            put(EncodeHintType.CHARACTER_SET, "utf-8");
+        }
+    };
+    /**
+     * 生成 图片缓冲
+     * @author fxbin
+     * @param vaNumber  VA 码
+     * @return 返回BufferedImage
+     */
+    public static BufferedImage getBarCode(String vaNumber){
+        try {
+            Code128Writer writer = new Code128Writer();
+            // 编码内容, 编码类型, 宽度, 高度, 设置参数
+            BitMatrix bitMatrix = writer.encode(vaNumber, BarcodeFormat.CODE_128, WIDTH, HEIGHT, hints);
+            return MatrixToImageWriter.toBufferedImage(bitMatrix);
+        } catch (WriterException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+    /**
+     * 把带logo的二维码下面加上文字
+     * @author fxbin
+     * @param image  条形码图片
+     * @param words  文字
+     * @return 返回BufferedImage
+     */
+    public static BufferedImage insertWords(BufferedImage image, String words){
+        // 新的图片,把带logo的二维码下面加上文字
+        if (null != words) {
+
+            BufferedImage outImage = new BufferedImage(WIDTH, WORDHEIGHT, BufferedImage.TYPE_INT_RGB);
+
+            Graphics2D g2d = outImage.createGraphics();
+
+            // 抗锯齿
+            setGraphics2D(g2d);
+            // 设置白色
+            setColorWhite(g2d);
+
+            // 画条形码到新的面板
+            g2d.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);
+            // 画文字到新的面板
+            Color color=new Color(0, 0, 0);
+            g2d.setColor(color);
+            // 字体、字型、字号
+            g2d.setFont(new Font("微软雅黑", Font.PLAIN, 18));
+            //文字长度
+            int strWidth = g2d.getFontMetrics().stringWidth(words);
+            //总长度减去文字长度的一半  (居中显示)
+            int wordStartX=(WIDTH - strWidth) / 2;
+            //height + (outImage.getHeight() - height) / 2 + 12
+            int wordStartY=HEIGHT+20;
+
+            // 画文字
+            g2d.drawString(words, wordStartX, wordStartY);
+            g2d.dispose();
+            outImage.flush();
+            return outImage;
+        }
+        return null;
+    }
+
+    /**
+     * 设置 Graphics2D 属性  (抗锯齿)
+     * @param g2d  Graphics2D提供对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制
+     */
+    private static void setGraphics2D(Graphics2D g2d){
+        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT);
+        Stroke s = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER);
+        g2d.setStroke(s);
+    }
+
+    /**
+     * 设置背景为白色
+     * @param g2d Graphics2D提供对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制
+     */
+    private static void setColorWhite(Graphics2D g2d){
+        g2d.setColor(Color.WHITE);
+        //填充整个屏幕
+        g2d.fillRect(0,0,600,600);
+        //设置笔刷
+        g2d.setColor(Color.BLACK);
+    }
+    
+    /**
+     * 识别条形码
+     * @param barcodePath
+     * @return
+     */
+    public static String decodeBarcode(String barcodePath){
+        BufferedImage image;
+        Result result = null;
+        try {
+            image = ImageIO.read(new File(barcodePath));
+            if (image != null) {
+                 LuminanceSource source = new BufferedImageLuminanceSource(image);
+                BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
+                result = new MultiFormatReader().decode(bitmap, null);
+            }
+            return result.getText();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        
+        return null;
+    }
+    
+    /**
+     * 从PDF文件中获取条形码图片
+     * @param filePath
+     * @throws IOException
+     */
+	public static void getImgeFromPdf(String filePath) throws IOException {
+		// 取得图片读入器
+		Iterator readers = ImageIO.getImageReadersByFormatName("png");
+		ImageReader reader = (ImageReader) readers.next();
+		
+		// 取得图片读入流
+		InputStream source = new FileInputStream(filePath);
+		ImageInputStream iis = ImageIO.createImageInputStream(source);
+		reader.setInput(iis, true);
+		
+		// 图片参数
+		ImageReadParam param = reader.getDefaultReadParam();
+		
+		//100,200是左上起始位置,300就是取宽度为300的,就是从100开始取300宽,就是横向100~400,同理纵向200~350的区域就取高度150
+		// 左起位置,上起位置,宽度,高度
+		Rectangle rect = new Rectangle(140, 300, 200, 200);
+		param.setSourceRegion(rect);
+		BufferedImage bi = reader.read(0, param);
+		ImageIO.write(bi, "png", new File("C:\\Users\\huangzibo\\Downloads\\李发有-毕业生登记表 - 副本(3)\\李发有-毕业生登记表 - 副本_001.png"));
+
+	}
+
+    public static void main(String[] args) throws Exception {
+//        BufferedImage image = insertWords(getBarCode("123456789"), "123456789");
+//        A80/90R8A(8A侧通孔)
+        //BufferedImage image = insertWords(getBarCode("65450101081006688"), "");
+    	
+    	// 生成条形码
+        BufferedImage image = getBarCode("65450101081006688");
+        ImageIO.write(image, "png", new File("D:/barcode.png"));
+    	System.out.println(decodeBarcode("D:/barcode.png"));
+    	
+    	//String filePath = "C:\\Users\\huangzibo\\Downloads\\李发有-毕业生登记表 - 副本.pdf";
+    	//getImgeFromPdf("C:\\Users\\huangzibo\\Downloads\\李发有-毕业生登记表 - 副本(1)\\李发有-毕业生登记表 - 副本_00.png");
+    	//System.out.println(decodeBarcode("C:\\Users\\huangzibo\\Downloads\\李发有-毕业生登记表 - 副本(1)\\李发有-毕业生登记表 - 副本_001.png"));
+    	
+    	//System.out.println(decodeBarcode("C:\\Users\\huangzibo\\Downloads\\李发有-毕业生登记表 - 副本\\李发有-毕业生登记表 - 副本_00.png"));
+    	
+    	// 生成二维码
+    	//generateQRCodeImage("65450101081006688", 80, 80, "C:\\Users\\huangzibo\\Downloads\\李发有-毕业生登记表 - 副本(1)\\1.png");
+    	
+    	// 解析二维码
+    	//System.out.println(decode("C:\\Users\\huangzibo\\Downloads\\李发有-毕业生登记表 - 副本(1)\\1.png"));
+    	
+    	// 获取二维码图片
+    	//getImgeFromPdf("C:\\Users\\huangzibo\\Downloads\\李发有-毕业生登记表 - 副本(3)\\李发有-毕业生登记表 - 副本_00.png");
+    	
+    	// 解析二维码
+    	//System.out.println(decode("C:\\Users\\huangzibo\\Downloads\\李发有-毕业生登记表 - 副本(3)\\李发有-毕业生登记表 - 副本_001.png"));
+    	
+    }
+    
+    public static void generateQRCodeImage(String text, int width, int height, String filePath) {
+		File file = new File(filePath);
+		if (file.exists()) {
+			return;
+		}
+
+		QRCodeWriter qrCodeWriter = new QRCodeWriter();
+		BitMatrix bitMatrix = null;
+		try {
+			bitMatrix = qrCodeWriter.encode(text, BarcodeFormat.QR_CODE, width, height);
+			Path path = FileSystems.getDefault().getPath(filePath);
+			MatrixToImageWriter.writeToPath(bitMatrix, "PNG", path);
+		} catch (Exception e) {
+			LogHelper.error(e);
+		}
+	}
+    
+    public static String decode(String filePath) throws Exception 
+    {
+    	File whatFile = new File(filePath);
+		if (!whatFile.exists()) {
+			return null;
+		}
+		
+    	 Map<DecodeHintType,Object> tmpHintsMap = new EnumMap<DecodeHintType,Object>(DecodeHintType.class);
+         tmpHintsMap.put(DecodeHintType.TRY_HARDER,Boolean.TRUE);
+         tmpHintsMap.put(DecodeHintType.POSSIBLE_FORMATS,EnumSet.allOf(BarcodeFormat.class));
+         tmpHintsMap.put(DecodeHintType.PURE_BARCODE,Boolean.FALSE);
+         
+        BufferedImage tmpBfrImage;
+        try 
+        {
+            tmpBfrImage = ImageIO.read(whatFile);
+        } 
+        catch (IOException tmpIoe) 
+        {
+            throw new Exception(tmpIoe.getMessage());
+        }
+        if (tmpBfrImage == null)
+            throw new IllegalArgumentException("Could not decode image.");
+        LuminanceSource tmpSource = new BufferedImageLuminanceSource(tmpBfrImage);
+        BinaryBitmap tmpBitmap = new BinaryBitmap(new HybridBinarizer(tmpSource));
+        MultiFormatReader tmpBarcodeReader = new MultiFormatReader();
+        Result tmpResult;
+        String tmpFinalResult;
+        try 
+        {
+            if (tmpHintsMap != null && ! tmpHintsMap.isEmpty())
+                //tmpResult = tmpBarcodeReader.decode(tmpBitmap,tmpHintsMap);
+            	tmpResult = tmpBarcodeReader.decode(tmpBitmap);
+            else
+                tmpResult = tmpBarcodeReader.decode(tmpBitmap);
+            // setting results.
+            tmpFinalResult = String.valueOf(tmpResult.getText());
+        } 
+        catch (Exception tmpExcpt) 
+        {
+            throw new Exception("BarCodeUtil.decode Excpt err - " + tmpExcpt.toString() + " - " + tmpExcpt.getMessage());
+        }
+        return tmpFinalResult;
+   }
+
+	
+}

+ 168 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/HandwritingUtil.java

@@ -0,0 +1,168 @@
+package cn.hmsoft.scan.util;
+
+
+import java.io.FileOutputStream;
+import java.net.URLEncoder;
+
+import cn.hmsoft.scan.util.baidu.Base64Util;
+import cn.hmsoft.scan.util.baidu.FileUtil;
+import cn.hmsoft.scan.util.baidu.HttpUtil;
+
+import com.itextpdf.text.Document;
+import com.itextpdf.text.pdf.PdfCopy;
+import com.itextpdf.text.pdf.PdfImportedPage;
+import com.itextpdf.text.pdf.PdfReader;
+
+
+public class HandwritingUtil {
+
+	 /**
+	    * 重要提示代码中所需工具类
+	    * FileUtil,Base64Util,HttpUtil,GsonUtils请从
+	    * https://ai.baidu.com/file/658A35ABAB2D404FBF903F64D47C1F72
+	    * https://ai.baidu.com/file/C8D81F3301E24D2892968F09AE1AD6E2
+	    * https://ai.baidu.com/file/544D677F5D4E4F17B4122FBD60DB82B3
+	    * https://ai.baidu.com/file/470B3ACCA3FE43788B5A963BF0B625F3
+	    * 下载
+	    */
+	    public static String handwriting() {
+	        // 请求url
+	        String url = "https://aip.baidubce.com/rest/2.0/ocr/v1/handwriting";
+	        try {
+	            // 本地文件路径
+	          /*  String filePath = "C:\\Users\\huangzibo\\Downloads\\自学考试毕业生登记表(4.9).pdf";
+	            String path = "C:\\Users\\huangzibo\\Downloads\\自学考试毕业生登记表(4.9).jpg";
+				File file = new File(filePath);
+				PDDocument doc = PDDocument.load(file);
+				PDFRenderer renderer = new PDFRenderer(doc);
+				//int pageCount = doc.getNumberOfPages();
+				for (int i = 0; i < pageCount; i++) {
+					BufferedImage image = renderer.renderImageWithDPI(i, 296);
+					// BufferedImage image = renderer.renderImage(i, 2.5f);
+					ImageIO.write(image, "PNG", new File(path));
+					break;
+				}
+				
+				BufferedImage image = renderer.renderImageWithDPI(2, 296);
+				// BufferedImage image = renderer.renderImage(i, 2.5f);
+				ImageIO.write(image, "PNG", new File(path));*/
+	            
+	        	String path = "C:\\Users\\huangzibo\\Downloads\\1.jpg";
+	            byte[] imgData = FileUtil.readFileByBytes(path);
+	            String imgStr = Base64Util.encode(imgData);
+	            String imgParam = URLEncoder.encode(imgStr, "UTF-8");
+
+	            String param = "image=" + imgParam;
+
+	            // 注意这里仅为了简化编码每一次请求都去获取access_token,线上环境access_token有过期时间, 客户端可自行缓存,过期后重新获取。
+	            // https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=iXkHMXGsPZHbrAqUGw4DALGo&client_secret=KLWUGwgytzOlun6gfwvCB9E6cHl5qGLU
+	            String accessToken = "24.1219c7190f62e6f0888f7f939c032087.2592000.1620868336.282335-23978664";
+
+	            String result = HttpUtil.post(url, accessToken, param);
+	            System.out.println(result);
+	            return result;
+	        } catch (Exception e) {
+	            e.printStackTrace();
+	        }
+	        return null;
+	    }
+	    
+	/**
+	 * 拆分PDF,每两页为一个新的PDF
+	 * 
+	 * @param file_path
+	 * @param new_file_path
+	 * @throws Exception
+	 */
+	public static void splitPDF(String file_path, String new_file_path) throws Exception {
+
+		Document document = null;
+        PdfCopy copy = null;
+        try {
+            PdfReader reader = new PdfReader(file_path);
+			//获取pdf页数
+            int pages = reader.getNumberOfPages();
+            PdfImportedPage page = null;
+            
+            int index = 1;
+
+    		for (int i = 0; i < pages; i++) {
+    			// 第一次遍历
+    			if (i == 0) {
+    				document = new Document(reader.getPageSize(1));
+                    copy = new PdfCopy(document, new FileOutputStream(new_file_path+index+".pdf"));
+                    document.open();
+                    
+                   document.newPage();
+                   page = copy.getImportedPage(reader, i+1);
+                   copy.addPage(page);
+                   
+    			} else if (i % 2 == 0) {
+    				// 每两页为一个新的PDF
+    				index++;
+    				
+    				// 如果不是最后一页
+    				if (i != pages-1) {
+    					 document.close();
+    					 copy.close();
+    					
+    					 document = new Document(reader.getPageSize(1));
+    	                 copy = new PdfCopy(document, new FileOutputStream(new_file_path+index+".pdf"));
+    	                 document.open();
+    	                 
+						document.newPage();
+						page = copy.getImportedPage(reader, i + 1);
+						copy.addPage(page);
+    				}else{
+    					// 最后一页
+    					document.newPage();
+						page = copy.getImportedPage(reader, i + 1);
+						copy.addPage(page);
+						document.close();
+						copy.close();
+    				}
+
+    			} else {
+    				
+    				// 如果不是最后一页
+    				if (i != pages-1) {
+    					document.newPage();
+                        page = copy.getImportedPage(reader, i+1);
+                        copy.addPage(page);
+    				}else{
+    					// 最后一页
+    					document.newPage();
+                        page = copy.getImportedPage(reader, i+1);
+                        copy.addPage(page);
+                        
+						document.close();
+						copy.close();
+    				}
+    				
+    				
+                    
+    			}
+
+    		}
+    		
+    		reader.close();
+    		
+        } catch (Exception e) {
+			e.printStackTrace();
+        }
+
+	}
+
+
+	    public static void main(String[] args) {
+	    	// 调用百度接口获取文字
+	    	//HandwritingUtil.handwriting();
+	    	
+	    	// 拆分PDF
+	    	try {
+				splitPDF("C:\\Users\\huangzibo\\Desktop\\档案管理\\自学考试毕业生登记表(4.9).pdf", "C:\\Users\\huangzibo\\Desktop\\档案管理\\temp\\");
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
+	    }
+}

+ 34 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/HttpResult.java

@@ -0,0 +1,34 @@
+package cn.hmsoft.scan.util;
+
+import java.util.HashMap;
+
+public class HttpResult {
+	private int statusCode;
+	private String response;
+	private HashMap<String,String> map = new HashMap<String,String>();
+	public HttpResult(int statusCode, String response) {
+		super();
+		this.statusCode = statusCode;
+		this.response = response;
+	}
+	public int getStatusCode() {
+		return statusCode;
+	}
+	public void setStatusCode(int statusCode) {
+		this.statusCode = statusCode;
+	}
+	public String getResponse() {
+		return response;
+	}
+	public void setResponse(String response) {
+		this.response = response;
+	}
+	public HashMap<String, String> getMap() {
+		return map;
+	}
+	public void setMap(HashMap<String, String> map) {
+		this.map = map;
+	}
+	
+  
+}

+ 370 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/HttpUtil.java

@@ -0,0 +1,370 @@
+package cn.hmsoft.scan.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.httpclient.Header;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.RequestEntity;
+import org.apache.commons.httpclient.methods.multipart.FilePart;
+import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
+import org.apache.commons.httpclient.methods.multipart.Part;
+import org.apache.commons.httpclient.methods.multipart.StringPart;
+import org.apache.commons.httpclient.params.HttpMethodParams;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+import org.springframework.util.StringUtils;
+
+import cn.hmsoft.frame.exception.web.BusinessException;
+
+public class HttpUtil{
+
+    //设置连接参数
+     public static RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(500)
+                .setSocketTimeout(30000)
+                .setConnectTimeout(5000)
+                .build();
+
+    /**
+     * 
+     * @return 响应体的内容
+     * @throws IOException 
+     * @throws ClientProtocolException 
+     */
+    public static String doGet(String url,HashMap<String,String> headers) throws ClientProtocolException, IOException{
+
+        // 创建http GET请求
+        HttpGet httpGet = new HttpGet(url);
+        
+        for (Iterator iterator = headers.keySet().iterator(); iterator.hasNext();) {
+			String key = (String) iterator.next();
+			String value = headers.get(key);
+			httpGet.setHeader(new BasicHeader(key, value));
+		}
+        
+        httpGet.setConfig(config);//设置请求参数
+        CloseableHttpResponse response = null;
+        CloseableHttpClient httpClient = getHttpClient();
+        try {
+            // 执行请求
+            response = httpClient.execute(httpGet);
+            // 判断返回状态是否为200
+            if (response.getStatusLine().getStatusCode() == 200) {
+                String content = EntityUtils.toString(response.getEntity(), "UTF-8");
+//                System.out.println("内容长度:"+content.length());
+                return content;
+            }
+        } finally {
+            if (response != null) {
+                response.close();
+            }
+            if(httpClient!=null){
+                httpClient.close();
+            }
+        }
+        return null;
+    }
+    public static String doGet(String url) throws ClientProtocolException, IOException{
+    	
+    	// 创建http GET请求
+    	HttpGet httpGet = new HttpGet(url);
+    	httpGet.setConfig(config);//设置请求参数
+    	CloseableHttpResponse response = null;
+    	CloseableHttpClient httpClient = getHttpClient();
+    	try {
+    		// 执行请求
+    		response = httpClient.execute(httpGet);
+    		// 判断返回状态是否为200
+    		if (response.getStatusLine().getStatusCode() == 200) {
+    			String content = EntityUtils.toString(response.getEntity(), "UTF-8");
+//                System.out.println("内容长度:"+content.length());
+    			return content;
+    		}
+    	} finally {
+    		if (response != null) {
+    			response.close();
+    		}
+    		if(httpClient!=null){
+    			httpClient.close();
+    		}
+    	}
+    	return null;
+    }
+    
+	public static String doSslGet(String url) throws Exception {
+
+		// 创建http GET请求
+		HttpGet httpGet = new HttpGet(url);
+		httpGet.setConfig(config);// 设置请求参数
+		CloseableHttpResponse response = null;
+		SSLClient httpClient = new SSLClient();
+		// CloseableHttpClient httpClient = getHttpClient();
+		try {
+			// 执行请求
+			response = httpClient.execute(httpGet);
+			// 判断返回状态是否为200
+			if (response.getStatusLine().getStatusCode() == 200) {
+				String content = EntityUtils.toString(response.getEntity(), "UTF-8");
+				return content;
+			}
+		} finally {
+			if (response != null) {
+				response.close();
+			}
+			if (httpClient != null) {
+				httpClient.close();
+			}
+		}
+		return null;
+	}
+
+    /**
+     * 带有参数的get请求
+     * @param url
+     * @return
+     * @throws URISyntaxException 
+     * @throws IOException 
+     * @throws ClientProtocolException 
+     */
+    public static String doGet(String url , Map<String, String> params) throws URISyntaxException, ClientProtocolException, IOException{
+        URIBuilder uriBuilder = new URIBuilder(url);
+        if(params != null){
+            for(String key : params.keySet()){
+                uriBuilder.setParameter(key, params.get(key));
+            }
+        }//http://xxx?ss=ss
+        return doGet(uriBuilder.build().toString());
+    }
+    /**
+     * 带有参数的post请求
+     * @param url
+     * @param params
+     * @return
+     * @throws IOException 
+     * @throws ClientProtocolException 
+     */
+    public static HttpResult doPost(String url , Map<String, String> params) throws ClientProtocolException, IOException{
+
+
+        // 创建http POST请求
+        HttpPost httpPost = new HttpPost(url);
+        httpPost.setConfig(config);
+        if(params != null){
+
+            // 设置2个post参数,一个是scope、一个是q
+            List<NameValuePair> parameters = new ArrayList<NameValuePair>(0);
+
+            for(String key : params.keySet()){
+                parameters.add(new BasicNameValuePair(key, params.get(key)));
+            }
+            // 构造一个form表单式的实体
+            UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters,"UTF-8");
+            // 将请求实体设置到httpPost对象中
+            httpPost.setEntity(formEntity);
+        }
+
+        CloseableHttpResponse response = null;
+        CloseableHttpClient httpClient = getHttpClient();
+        try {
+            // 执行请求
+
+            response = httpClient.execute(httpPost);
+            // 判断返回状态是否为200
+            /*if (response.getStatusLine().getStatusCode() == 200) {
+                String content = EntityUtils.toString(response.getEntity(), "UTF-8");
+                System.out.println(content);
+            }*/
+            return new HttpResult(response.getStatusLine().getStatusCode(),EntityUtils.toString(response.getEntity(), "UTF-8"));
+        } finally {
+            if (response != null) {
+                response.close();
+            }
+            if(httpClient!=null){
+                httpClient.close();
+            }
+        }
+    }
+
+    public static HttpResult doPostJson(String url , String json) throws ClientProtocolException, IOException{
+         // 创建http POST请求
+        HttpPost httpPost = new HttpPost(url);
+        httpPost.setConfig(config);
+        if(StringUtils.hasLength(json)){
+            //标识出传递的参数是 application/json
+            StringEntity stringEntity = new StringEntity(json, ContentType.APPLICATION_JSON);
+            httpPost.setEntity(stringEntity);
+        }
+
+        CloseableHttpResponse response = null;
+        CloseableHttpClient httpClient = getHttpClient();
+        try {
+            // 执行请求
+            response = httpClient.execute(httpPost);
+            // 判断返回状态是否为200
+            /*if (response.getStatusLine().getStatusCode() == 200) {
+                String content = EntityUtils.toString(response.getEntity(), "UTF-8");
+                System.out.println(content);
+            }*/
+            return new HttpResult(response.getStatusLine().getStatusCode(),EntityUtils.toString(response.getEntity(), "UTF-8"));
+        } finally {
+            if (response != null) {
+                response.close();
+            }
+            
+            if(httpClient!=null){
+                httpClient.close();
+            }
+        }
+    }
+
+    /**
+     * 没有参数的post请求
+     * @throws IOException 
+     * @throws ClientProtocolException 
+     */
+    public static HttpResult doPost(String url) throws ClientProtocolException, IOException{
+        return doPost(url, null);
+    }
+    
+    
+	public static String doPostForPhoto(String url, String accessToken, String sourcePath) throws Exception {
+		HttpClient httpClient = new HttpClient();
+		PostMethod method = new PostMethod(url);
+		try {
+
+			Part[] parts = { new FilePart("imgFile", new File(sourcePath)) };
+			RequestEntity requestEntity = new MultipartRequestEntity(parts, method.getParams());
+			method.setRequestEntity(requestEntity);
+			Header header = new Header();
+			header.setName("token");
+			header.setValue(accessToken);
+			method.setRequestHeader(header);
+			method.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 30000);
+			httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(30000);
+			httpClient.getHttpConnectionManager().getParams().setSoTimeout(30000);
+			httpClient.executeMethod(method);
+			if (method.getStatusCode() == 200) {
+				String result = method.getResponseBodyAsString();
+				return result;
+			} else {
+				throw new BusinessException("照片合规检查接口返回失败码:"+method.getStatusCode()); 
+			}
+
+		} finally {
+			// 释放链接
+			method.releaseConnection();
+			httpClient.getHttpConnectionManager().closeIdleConnections(0);
+		}
+
+	}
+	
+	public static String doPostForPhotoReview(String url, String accessToken, Map<String, String> params) throws Exception {
+		HttpClient httpClient = new HttpClient();
+		PostMethod method = new PostMethod(url);
+		try {
+			Part[] parts = new Part[params.keySet().size()];
+			int i=0;
+			for(String key : params.keySet()){
+				if("imgFile".equals(key)){
+					FilePart part = new FilePart("imgFile", new File(params.get(key)));
+					parts[i] = part;
+				}else{
+					StringPart part = new StringPart(key,params.get(key));
+					parts[i] = part;
+				}
+				
+				i++;
+            }
+			
+			RequestEntity requestEntity = new MultipartRequestEntity(parts, method.getParams());
+			method.setRequestEntity(requestEntity);
+			Header header = new Header();
+			header.setName("token");
+			header.setValue(accessToken);
+			method.setRequestHeader(header);
+			method.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 30000);
+			httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(30000);
+			httpClient.getHttpConnectionManager().getParams().setSoTimeout(30000);
+			httpClient.executeMethod(method);
+			if (method.getStatusCode() == 200) {
+				String result = method.getResponseBodyAsString();
+				return result;
+			} else {
+				throw new BusinessException("照片合规检查接口返回失败码:"+method.getStatusCode()); 
+			}
+
+		} finally {
+			// 释放链接
+			method.releaseConnection();
+			httpClient.getHttpConnectionManager().closeIdleConnections(0);
+		}
+
+	}
+	
+	public static String doPostForCompareFeature(String url , String accessToken,Map<String, String> params) throws ClientProtocolException, IOException{
+    	HttpClient httpClient = new HttpClient();
+		PostMethod method = new PostMethod(url);
+		try {
+			Part[] parts = new Part[params.keySet().size()];
+			int i=0;
+			for(String key : params.keySet()){
+				StringPart part = new StringPart(key,params.get(key));
+				parts[i] = part;
+				i++;
+            }
+			
+			
+			RequestEntity requestEntity = new MultipartRequestEntity(parts, method.getParams());
+			method.setRequestEntity(requestEntity);
+			Header header = new Header();
+			header.setName("token");
+			header.setValue(accessToken);
+			method.setRequestHeader(header);
+			method.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 30000);
+			httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(30000);
+			httpClient.getHttpConnectionManager().getParams().setSoTimeout(30000);
+			httpClient.executeMethod(method);
+			if (method.getStatusCode() == 200) {
+				String result = method.getResponseBodyAsString();
+				return result;
+			} else {
+				throw new BusinessException("照片合规检查接口返回失败码:"+method.getStatusCode()); 
+			}
+
+		} finally {
+			// 释放链接
+			method.releaseConnection();
+			httpClient.getHttpConnectionManager().closeIdleConnections(0);
+		}
+    	
+    }
+    
+
+    public static CloseableHttpClient getHttpClient(){
+        return HttpClientBuilder.create().setMaxConnTotal(200)
+                                                                    .setMaxConnPerRoute(100)
+                                                                    .build();
+    }
+
+
+}

+ 264 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/ImageUtil.java

@@ -0,0 +1,264 @@
+package cn.hmsoft.scan.util;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+
+import org.apache.commons.io.FilenameUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import cn.hmsoft.frame.exception.web.BusinessException;
+import cn.hmsoft.helper.LogHelper;
+import cn.hmsoft.scan.common.constants.SysConst;
+
+
+public class ImageUtil {
+	private  final static int IMG_W= 288;
+	private  final static int IMG_H= 384;
+	
+    private static boolean makeDir(String rootPath){
+    	//创建目录
+    	boolean suc = false;
+		File dir = new File( rootPath + SysConst.IMG_FACE_PREFIX);
+		if(!dir.exists()) {
+			suc = dir.mkdir();
+		}
+		return suc;
+    }
+    
+	public static File inputStreamAsFile(InputStream inputStream) throws IOException{
+		String downloadTemp = SysConst.getAppConfig().getDownloadTemp();
+		makeDir(downloadTemp);
+	 	File tmp = File.createTempFile(String.valueOf(new Date().getTime()), ".tmp", new File(downloadTemp));
+	 	OutputStream os = new FileOutputStream(tmp);
+	    int bytesRead = 0;
+	    byte[] buffer = new byte[8192];
+	    while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) {
+	      os.write(buffer, 0, bytesRead);
+	    }
+	    inputStream.close();
+	    return tmp;
+	 }
+	
+	public static BufferedImage getBufferedImage(InputStream inputStream){
+		BufferedImage image = null;
+		try {
+			File imgFile = ImageUtil.inputStreamAsFile(inputStream);
+			image = ImageIO.read(imgFile);
+		} catch (IOException e) {
+			e.printStackTrace();
+			throw new BusinessException("图片处理异常");
+		}
+		return image;
+	}
+	
+	public long imageSize(MultipartFile file){
+		return file.getSize()/1024;
+	}
+	
+	/**大于 numK
+	 * @param file
+	 * @param numK
+	 * @return
+	 */
+	public static boolean imageMoreSize(MultipartFile file, long numK){
+		boolean b = file.getSize()/1024>=numK;
+		if (!b) {
+			throw new BusinessException("只允许上传"+numK+"K以上大小的照片");
+		}
+		return true;
+	}
+	
+	/**小于 numK
+	 * @param file
+	 * @param numK
+	 * @return
+	 */
+	public static boolean imageLessSize(MultipartFile file, long numK){
+		boolean b = file.getSize()/1024<=numK;
+		if (!b) {
+			throw new BusinessException("只允许上传"+numK+"K以下大小的照片");
+		}
+		return true;
+	}
+	
+	public static boolean isW_H(MultipartFile file, int w, int h){
+		BufferedImage bufferedImage = null;
+		try {
+			bufferedImage = ImageUtil.getBufferedImage(file.getInputStream());
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		if(bufferedImage == null) return false;
+		int width = bufferedImage.getWidth();
+		int height = bufferedImage.getHeight();
+		return width == w && height == h;
+	}
+			
+	public static boolean is288_384(MultipartFile file){
+		boolean rtnB = ImageUtil.isW_H(file, IMG_W, IMG_H);
+		if (!rtnB) {
+			throw new BusinessException("只允许上传("+ IMG_W + "*" +IMG_H + ")分辨率照片");
+		}
+		return rtnB;
+	}
+	
+	
+	public static void validateUploadFileExt(MultipartFile file){
+		String extension = FilenameUtils.getExtension(file.getOriginalFilename());
+		extension = extension.toLowerCase();
+		if (!"jpg".equals(extension) && !"jpeg".equals(extension) && !"png".equals(extension) && !"zip".equals(extension)) {
+			throw new BusinessException("上传图片必须是JPG/JPEG/PNG格式或者ZIP打包文件");
+		}
+		
+		long sizeM = file.getSize()/(1024*1024);
+		if ("zip".equals(extension)) {
+			if (sizeM >= 10) {
+				throw new BusinessException("上传zip压缩文件大小不能超过 10MB!");
+			}
+		}else{
+			if (sizeM >= 2) {
+				throw new BusinessException("上传图片大小不能超过 2MB!");
+				
+			}
+		}
+	}
+	
+	 /**
+     * 常用文件的文件头如下:(以前六位为准)
+     * JPEG (jpg),文件头:FFD8FF
+     * PNG (png),文件头:89504E47
+     * GIF (gif),文件头:47494638
+     * TIFF (tif),文件头:49492A00
+     * Windows Bitmap (bmp),文件头:424D
+     * CAD (dwg),文件头:41433130
+     * Adobe Photoshop (psd),文件头:38425053
+     * Rich Text Format (rtf),文件头:7B5C727466
+     * XML (xml),文件头:3C3F786D6C
+     * HTML (html),文件头:68746D6C3E
+     * Email [thorough only] (eml),文件头:44656C69766572792D646174653A
+     * Outlook Express (dbx),文件头:CFAD12FEC5FD746F
+     * Outlook (pst),文件头:2142444E
+     * MS Word/Excel (xls.or.doc),文件头:D0CF11E0
+     * MS Access (mdb),文件头:5374616E64617264204A
+     * WordPerfect (wpd),文件头:FF575043
+     * Postscript (eps.or.ps),文件头:252150532D41646F6265
+     * Adobe Acrobat (pdf),文件头:255044462D312E
+     * Quicken (qdf),文件头:AC9EBD8F
+     * Windows Password (pwl),文件头:E3828596
+     * ZIP Archive (zip),文件头:504B0304
+     * RAR Archive (rar),文件头:52617221
+     * Wave (wav),文件头:57415645
+     * AVI (avi),文件头:41564920
+     * Real Audio (ram),文件头:2E7261FD
+     * Real Media (rm),文件头:2E524D46
+     * MPEG (mpg),文件头:000001BA
+     * MPEG (mpg),文件头:000001B3
+     * Quicktime (mov),文件头:6D6F6F76
+     * Windows Media (asf),文件头:3026B2758E66CF11
+     * MIDI (mid),文件头:4D546864
+     */
+
+
+    public static final String TYPE_JPG = ".jpg";
+    public static final String TYPE_GIF = ".gif";
+    public static final String TYPE_PNG = ".png";
+    public static final String TYPE_BMP = ".bmp";
+    public static final String TYPE_WEBP = ".webp";
+    public static final String TYPE_TIF = ".tif";
+    public static final String TYPE_UNKNOWN = "unknown";
+
+
+    public static void main(String[] args) throws FileNotFoundException {
+        List<String> list = new ArrayList<String>();
+        list.add("D:\\22120110514301565843605799605249.jpeg");
+        list.add("D:\\机打.jpeg");
+        list.add("D:\\22112811493801563698430201598977.png");
+        list.add("D:\\452129200203291021.jpg");
+        
+      
+        list.forEach(filename -> {
+            try {
+                File pdfFile = new File(filename);
+                // test code
+                System.out.println("图片格式:" + getPicType(new FileInputStream(pdfFile)));
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        });
+    }
+
+    /**
+     * byte数组转换成16进制字符串
+     *
+     * @param src
+     * @return
+     */
+    public static String bytesToHexString(byte[] src) {
+        StringBuilder stringBuilder = new StringBuilder();
+        if (src == null || src.length <= 0) {
+            return null;
+        }
+        for (int i = 0; i < src.length; i++) {
+            int v = src[i] & 0xFF;
+            String hv = Integer.toHexString(v);
+            if (hv.length() < 2) {
+                stringBuilder.append(0);
+            }
+            stringBuilder.append(hv);
+        }
+        return stringBuilder.toString();
+    }
+
+    /**
+     * 根据文件流判断图片类型
+     *
+     * @param fis
+     * @return jpg/png/gif/bmp
+     */
+    public static String getPicType(FileInputStream fis) {
+        //读取文件的前几个字节来判断图片格式
+        byte[] b = new byte[4];
+        try {
+            fis.read(b, 0, b.length);
+            String type = bytesToHexString(b).toUpperCase();
+            if (type.contains("FFD8FF")) {
+                return TYPE_JPG;
+            } else if (type.contains("89504E47")) {
+                return TYPE_PNG;
+            } else if (type.contains("47494638")) {
+                return TYPE_GIF;
+            } else if (type.contains("424D")) {
+                return TYPE_BMP;
+            } else if (type.contains("52494646")) {
+                return TYPE_WEBP;
+            } else if (type.contains("49492A00")) {
+                return TYPE_TIF;
+            } else {
+                return null;
+            }
+        } catch (IOException e) {
+        	LogHelper.error(e);
+        } finally {
+            if (fis != null) {
+                try {
+                    fis.close();
+                } catch (IOException e) {
+                    LogHelper.error(e);
+                }
+            }
+        }
+        return null;
+    }
+
+}

+ 105 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/JdbcEncryptHelper.java

@@ -0,0 +1,105 @@
+package cn.hmsoft.scan.util;
+
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import javax.servlet.ServletException;
+
+import cn.hmsoft.helper.SecureHelper;
+
+/**************************
+ * 安全相关辅助类
+ * 
+ */
+public class JdbcEncryptHelper {
+
+
+	
+	public static void main(String[] args) throws IOException {
+		System.out.println("JdbcUrl="+SecureHelper.des3Encrypt("jdbc:oracle:thin:@10.16.13.22:1521/gxzk",  SecureHelper.getSecureKey("ses-enrol-v1")));
+
+		System.out.println("JdbcUser="+SecureHelper.des3Encrypt("gxzkyw2019",  SecureHelper.getSecureKey("ses-enrol-v1")));
+		System.out.println("JdbcPassword="+SecureHelper.des3Encrypt("gxzkyw2019",  SecureHelper.getSecureKey("ses-enrol-v1")));
+		
+		System.out.println("JdbcUrl="+SecureHelper.des3Encrypt("jdbc:oracle:thin:@x25w995739.qicp.vip:15431/gxzk",  SecureHelper.getSecureKey("ses-manage-v1")));
+		System.out.println("JdbcUser="+SecureHelper.des3Encrypt("gxzk2019",  SecureHelper.getSecureKey("ses-manage-v1")));
+		System.out.println("JdbcPassword="+SecureHelper.des3Encrypt("gxzk2019",  SecureHelper.getSecureKey("ses-manage-v1")));
+		/*LogHelper.info("log4j2 for frame log,log_type:" + LogHelper.LOGTYPE.toString());
+		// 判断系统是否启用了数据库
+		File configPath = null;
+		try {
+			String path = JdbcEncryptHelper.class.getProtectionDomain().getCodeSource().getLocation().getFile();
+			System.out.println(path);
+//			int lastIndex = path.lastIndexOf(File.separator);
+//			path = path.substring(0, lastIndex);
+			configPath = new File(java.net.URLDecoder.decode(path, "utf-8"));
+			System.out.println(configPath);
+		} catch (Exception e) {
+			LogHelper.error(e.getMessage());
+		}
+		Map<String, String> jdbcConfigMap = new HashMap<String, String>();
+		if (StringHelper.isNotEmpty(FrameParamConstants.JdbcConfigFileName)) {
+			try {
+				jdbcConfigMap = loadProperites(configPath + File.separator + FrameParamConstants.JdbcConfigFileName);
+			} catch (Exception e) {
+			}
+		}
+		if (jdbcConfigMap.size() == 0) {
+			LogHelper.warn("database config not found");
+		} else {
+			// 设置数据库参数信息
+			LogHelper.info(
+					"found database file[" + FrameParamConstants.JdbcConfigFileName + "],des3Encrypt database info");
+			
+			BufferedWriter writer = new BufferedWriter(new FileWriter("e_jdbc.properties", false));
+			writer.write("JdbcDriverClass=oracle.jdbc.driver.OracleDriver");writer.newLine();
+			writer.write("JdbcUrl="+SecureHelper.des3Encrypt(jdbcConfigMap.get("JdbcUrl"),  SecureHelper.getSecureKey(jdbcConfigMap.get("JdbcSecureKey"))));writer.newLine();
+			writer.write("JdbcUser="+SecureHelper.des3Encrypt(jdbcConfigMap.get("JdbcUser"),  SecureHelper.getSecureKey(jdbcConfigMap.get("JdbcSecureKey"))));writer.newLine();
+			writer.write("JdbcPassword="+SecureHelper.des3Encrypt(jdbcConfigMap.get("JdbcPassword"),  SecureHelper.getSecureKey(jdbcConfigMap.get("JdbcSecureKey"))));writer.newLine();
+			writer.write("JdbcSecureKey="+jdbcConfigMap.get("JdbcSecureKey"));
+			writer.close();
+			
+			LogHelper.info(
+					"des3Encrypt database info into file e_jdbc.properties");
+			
+		}*/
+
+	}
+	
+	public static Map<String, String> loadProperites(String fileName) throws ServletException {
+		Map<String, String> map = new HashMap<String, String>();
+		File file = new File(fileName);
+		if (!file.exists())
+			throw new ServletException("file [" + fileName + "] not found");
+		if (file.isFile()) {
+			loadProperites(file, map);
+		} else {
+			// 遍历获取
+			for (File f : file.listFiles()) {
+				// 只获取properties结尾的文件
+				if (f.getName().toLowerCase().endsWith(".properties"))
+					loadProperites(file, map);
+			}
+		}
+		return map;
+	}
+
+	private static void loadProperites(File file, Map<String, String> map) throws ServletException {
+		Properties prop = new Properties();
+		if (!file.exists())
+			throw new ServletException("file [" + file.getName() + "] not found");
+		try {
+			prop.load(new FileReader(file));
+			for (Object key : prop.keySet()) {
+				map.put(key.toString().trim(), prop.get(key).toString().trim());
+			}
+		} catch (Exception e) {
+			throw new ServletException("initialize [" + file.getName() + "] config file error");
+		}
+	}
+	
+}

+ 39 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/MainTest.java

@@ -0,0 +1,39 @@
+package cn.hmsoft.scan.util;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.github.houbb.opencc4j.util.ZhConverterUtil;
+
+import cn.hmsoft.helper.StringHelper;
+
+/**  
+ * @Description: 广西自考给的毕业表中,有一个特殊字符,不是空格,是全角,我们的输入发默认都是半角,所以这里识别不出来,需要替换掉之后,trim()后在做匹配
+ * @author hgh
+ * @date 2023-07-21 11:05:19 
+ */
+public class MainTest {
+
+	public static void main(String[] args) {
+//		String str = "陈 岗";
+//		System.out.println(str + "**" + str.length());
+//		str = getStringTrimVal(str);
+////		str = str.replace(" ", "");
+//		System.out.println(str + "--" + str.length());
+		System.out.println(ZhConverterUtil.convertToSimple(null));
+	}
+	
+	private static String getStringTrimVal(String str) {
+		if (StringHelper.isNotEmpty(str)) {
+			String dest = null;
+			Pattern p = Pattern.compile("\\s*|\t|\r|\n");
+			Matcher m = p.matcher(str);
+			dest = m.replaceAll("");
+			dest = dest.replaceAll(" ", "");
+			
+			return dest;
+		}
+		
+		return "";
+	}
+}

+ 30 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/PatternUtil.java

@@ -0,0 +1,30 @@
+package cn.hmsoft.scan.util;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.springframework.util.StringUtils;
+
+public class PatternUtil {
+
+	/**
+	 * 校验考生姓名合法性
+	 * 
+	 * @param std_name
+	 * @return
+	 */
+	public static boolean checkStdName(String std_name) {
+		if (StringUtils.isEmpty(std_name)) {
+			return false;
+		} else {
+			Pattern pattern = Pattern.compile(
+					"^((?![\\u3000-\\u303F])[\\u2E80-\uFE4F]|\\·)*(?![\\u3000-\\u303F])[\\u2E80-\\uFE4F](\\·)*$");
+			Matcher name = pattern.matcher(std_name);
+
+			return name.matches();
+
+		}
+
+	}
+
+}

+ 63 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/PdfWatermarkUtil.java

@@ -0,0 +1,63 @@
+package cn.hmsoft.scan.util;
+
+import com.itextpdf.text.Element;
+import com.itextpdf.text.pdf.*;
+
+import java.io.*;
+
+
+/**
+ * @desc: 给pdf文件添加水印-帮助类
+ */
+public class PdfWatermarkUtil {
+
+	public static void main(String[] args) {
+		addWaterMark("D:/ks_01.pdf", "D:/ks_01_result3.pdf");
+	}
+
+	/**
+	 * @desc 给PDF文档添加水印
+	 * @param pdfFilePath 待被加水印的pdf文件路径
+	 * @param outputFilePath 已加水印的pdf输出路径
+	 */
+	public static void addWaterMark(String pdfFilePath, String outputFilePath) {
+		try {
+			// 原PDF文件
+			PdfReader reader = new PdfReader(pdfFilePath);
+			// 输出的PDF文件内容
+			PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(outputFilePath));
+			// 字体 来源于 itext-asian JAR包
+			BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", true);
+			PdfGState gs = new PdfGState();
+			// 设置透明度
+			gs.setFillOpacity(0.1f);
+			gs.setStrokeOpacity(0.4f);
+			int totalPage = reader.getNumberOfPages() + 1;
+			for (int i = 1; i < totalPage; i++) {
+				// 内容上层
+				 PdfContentByte content = stamper.getOverContent(i);
+				// 内容下层
+				//PdfContentByte content = stamper.getUnderContent(i);
+				content.beginText();
+				// 字体添加透明度
+				content.setGState(gs);
+				// 添加字体大小等
+				content.setFontAndSize(baseFont, 50);
+				// 添加范围
+				content.setTextMatrix(70, 200);
+				// 具体位置 内容 旋转多少度 共360度
+				content.showTextAligned(Element.ALIGN_CENTER, "机密文件", 550, 500, 300);
+				// content.showTextAligned(Element.ALIGN_TOP, "机密文件", 100, 100, 5);
+				// content.showTextAligned(Element.ALIGN_BOTTOM, "机密文件", 400, 400, 75);
+				content.endText();
+			}
+			//关闭
+			stamper.close();
+			reader.close();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+	
+
+}

+ 77 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/PicToPdfUtil.java

@@ -0,0 +1,77 @@
+package cn.hmsoft.scan.util;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import com.itextpdf.text.Document;
+import com.itextpdf.text.Image;
+import com.itextpdf.text.Rectangle;
+import com.itextpdf.text.pdf.PdfWriter;
+
+import cn.hmsoft.helper.LogHelper;
+
+public class PicToPdfUtil {
+	public static void convert(String path, String dest_Path, String diaplma_no) {
+		Document document = new Document();
+		// 设置文档页边距
+		document.setMargins(0, 0, 0, 0);
+		FileOutputStream fos = null;
+		try {
+
+			fos = new FileOutputStream(dest_Path);
+			PdfWriter.getInstance(document, fos);
+			// 打开文档
+			document.open();
+
+			// 添加第一张图片
+			addPicToPdf(path + diaplma_no + "-1.jpg", document);
+
+			// 添加第二张图片
+			addPicToPdf(path + diaplma_no + "-2.jpg", document);
+
+			// 添加第三张图片
+			addPicToPdf(path + diaplma_no + "-3.jpg", document);
+
+		} catch (Exception e) {
+			LogHelper.error(e);
+		} finally {
+			// 关闭文档
+			document.close();
+
+			try {
+				fos.flush();
+				fos.close();
+			} catch (IOException e) {
+				LogHelper.error(e);
+			}
+		}
+
+	}
+
+	private static void addPicToPdf(String path, Document document) throws Exception {
+		File desFile = new File(path);
+		if (desFile.exists()) {
+			Image image = Image.getInstance(path);
+
+			// 获取图片的宽高
+			float imageHeight = image.getScaledHeight();
+			float imageWidth = image.getScaledWidth();
+			// 设置页面宽高与图片一致
+			Rectangle rectangle = new Rectangle(imageWidth, imageHeight);
+			document.setPageSize(rectangle);
+			// 图片居中
+			image.setAlignment(Image.ALIGN_CENTER);
+			// 新建一页添加图片
+			document.newPage();
+			document.add(image);
+		}
+	}
+
+	public static void main(String[] args) {
+		String source = "D:/image/车辆颜色.png";
+		String target = "D:/aaa/1.pdf";
+		// convert(source, target);
+	}
+
+}

+ 141 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/QRCodeUtil.java

@@ -0,0 +1,141 @@
+package cn.hmsoft.scan.util;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.rendering.PDFRenderer;
+
+import com.swetake.util.Qrcode;
+
+import jp.sourceforge.qrcode.QRCodeDecoder;
+import jp.sourceforge.qrcode.data.QRCodeImage;
+
+public class QRCodeUtil {
+
+	public static void qrCodeEncode(String encodeddata, String path,int width,int height) throws IOException {
+		File destFile = new File(path);
+		Qrcode qrcode = new Qrcode();
+		qrcode.setQrcodeErrorCorrect('M'); // 纠错级别(L 7%、M 15%、Q 25%、H 30%)和版本有关
+		qrcode.setQrcodeEncodeMode('B');
+		qrcode.setQrcodeVersion(7); // 设置Qrcode包的版本
+
+		byte[] d = encodeddata.getBytes("GBK"); // 字符集
+		BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+		// createGraphics // 创建图层
+		Graphics2D g = bi.createGraphics();
+
+		g.setBackground(Color.WHITE); // 设置背景颜色(白色)
+		g.clearRect(0, 0, width, height); // 矩形 X、Y、width、height
+		g.setColor(Color.BLACK); // 设置图像颜色(黑色)
+
+		if (d.length > 0 && d.length < 128) {
+			boolean[][] b = qrcode.calQrcode(d);
+			for (int i = 0; i < b.length; i++) {
+				for (int j = 0; j < b.length; j++) {
+					if (b[j][i]) {
+						g.fillRect(j * 3 + 2, i * 3 + 2, 3, 3);
+					}
+				}
+			}
+		}
+
+		// Image img = ImageIO.read(new File("D:/tt.png")); logo
+		// g.drawImage(img, 25, 55,60,50, null);
+
+		g.dispose(); // 释放此图形的上下文以及它使用的所有系统资源。调用 dispose 之后,就不能再使用 Graphics 对象
+		bi.flush(); // 刷新此 Image 对象正在使用的所有可重构的资源
+
+		ImageIO.write(bi, "png", destFile);
+		// System.out.println("Input Encoded data is:" + encodeddata);
+	}
+
+	/**
+	 * 解析二维码,返回解析内容
+	 * 
+	 * @param imageFile
+	 * @return
+	 */
+	public static String qrCodeDecode(String path) {
+		File imageFile = new File(path);
+		String decodedData = null;
+		QRCodeDecoder decoder = new QRCodeDecoder();
+		BufferedImage image = null;
+		try {
+			image = ImageIO.read(imageFile);
+			decodedData = new String(decoder.decode(new J2SEImage(image)), "GBK");
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+
+		return decodedData;
+	}
+
+	static class J2SEImage implements QRCodeImage {
+		BufferedImage image;
+
+		public J2SEImage(BufferedImage image) {
+			this.image = image;
+		}
+
+		public int getWidth() {
+			return image.getWidth();
+		}
+
+		public int getHeight() {
+			return image.getHeight();
+		}
+
+		public int getPixel(int x, int y) {
+			return image.getRGB(x, y);
+		}
+	}
+
+	public static void main(String[] args) throws Exception {
+		/*String filePath = "d:/qrcode.png";
+
+		// 二维码内容
+		String encodeddata = "450121198412271219";
+		try {
+			QRCodeUtil.qrCodeEncode(encodeddata, filePath,139,139);
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+
+		// 解码
+		String reText = QRCodeUtil.qrCodeDecode(filePath);
+		System.out.println(reText);*/
+		
+		String filePath = "C:\\Users\\huangzibo\\Downloads\\黄子皮-毕业生登记表 (19).png";
+		
+		// 将PDF转成图片
+		getCodeFromPdf("C:\\Users\\huangzibo\\Downloads\\黄子皮-毕业生登记表 (19).pdf",filePath);
+		
+		// 解码
+		String reText = QRCodeUtil.qrCodeDecode(filePath);
+		System.out.println(reText);
+	}
+	
+	public static void getCodeFromPdf(String filePath,String path) throws Exception {
+		File file = new File(filePath);
+		 
+		PDDocument doc = PDDocument.load(file);
+		PDFRenderer renderer = new PDFRenderer(doc);
+		int pageCount = doc.getNumberOfPages();
+		for (int i = 0; i < pageCount; i++) {
+			BufferedImage image = renderer.renderImageWithDPI(i, 296);
+			// BufferedImage image = renderer.renderImage(i, 2.5f);
+			ImageIO.write(image, "PNG", new File(path));
+			break;
+		}
+		
+		//BufferedImage image = renderer.renderImageWithDPI(2, 296);
+		// BufferedImage image = renderer.renderImage(i, 2.5f);
+		//ImageIO.write(image, "PNG", new File(path));
+	}
+}

+ 160 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/RegexUtil.java

@@ -0,0 +1,160 @@
+package cn.hmsoft.scan.util;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.springframework.util.StringUtils;
+
+/**************
+ * 常用正则表达式
+ */
+public class RegexUtil {
+	
+	private RegexUtil() {
+		
+	}
+	
+	public static boolean isNumber(String str) {
+		if (str == null) {
+			return false;
+		}
+		str = str.trim();
+		Pattern pattern = Pattern.compile("[0-9]*$");
+		Matcher isNum = pattern.matcher(str);
+		return isNum.matches();
+	}
+	
+	/**
+	 * 正数
+	 * @param str
+	 * @return
+	 */
+	public static boolean isPositiveNumber(String str) {
+		if (str == null) {
+			return false;
+		}
+		str = str.trim();
+		Pattern pattern = Pattern.compile("[0-9]*");
+		Matcher isNum = pattern.matcher(str);
+		return isNum.matches();
+	}
+	/**
+	 * 校验是否为正浮点数
+	 * @param strNumber
+	 * @return
+	 */
+	public static boolean isFloatNum(String strNumber) {
+		Pattern pattern = Pattern.compile("^(-)?[0-9]\\d*\\.{0,1}\\d*|0\\.{0,1}\\d*[0-9]\\d*$");
+		return pattern.matcher(strNumber).matches();
+	}
+	
+
+	
+	/**验证手机号
+	 * @param phone
+	 * @return
+	 */
+	public static boolean isPhone(String phone) {
+		String regex = "^(1)\\d{10}$";
+		if (phone.length() != 11) {
+			// 手机号应为11位数
+			return false;
+		} else {
+			Pattern p = Pattern.compile(regex);
+			Matcher m = p.matcher(phone);
+			boolean isMatch = m.matches();
+			return isMatch;
+		}
+
+	}
+	
+	public static void main2(String [] args){
+		System.out.println(isPhone("19177912843"));
+		System.out.println(isPostCode("030022"));
+	}
+	
+	/**邮编验证
+	 * @param postCode
+	 * @return
+	 */
+	public static boolean isPostCode(String postCode){
+		String reg = "[0-9]\\d{5}";
+		return Pattern.matches(reg, postCode);
+	}
+	
+	public static boolean isEmail(String email) {
+		boolean flag = false;
+		try {
+			String check = "^([a-z0-9A-Z]+[-|_|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
+			Pattern regex = Pattern.compile(check);
+			Matcher matcher = regex.matcher(email);
+			flag = matcher.matches();
+		} catch (Exception e) {
+			flag = false;
+		}
+		return flag;
+	}
+ 
+	/**验证日期
+	 * @param date
+	 * @return
+	 */
+	public static boolean isDate(String date) {
+		String eL = "^((//d{2}(([02468][048])|([13579][26]))[//-/////s]?((((0?[13578])|(1[02]))[//-/////s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[//-/////s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[//-/////s]?((0?[1-9])|([1-2][0-9])))))|(//d{2}(([02468][1235679])|([13579][01345789]))[//-/////s]?((((0?[13578])|(1[02]))[//-/////s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[//-/////s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[//-/////s]?((0?[1-9])|(1[0-9])|(2[0-8]))))))(//s(((0?[0-9])|([1][0-9])|([2][0-3]))//:([0-5]?[0-9])((//s)|(//:([0-5]?[0-9])))))?$";
+		Pattern p = Pattern.compile(eL);
+		Matcher m = p.matcher(date);
+		return m.matches();
+
+	}
+	
+	public static boolean isBirthDay(String birthday) {
+		if (StringUtils.isEmpty(birthday)) {
+			return false;
+		}
+		if (birthday.length() != 8) {
+			return false;
+		}
+		Pattern pattern = Pattern.compile("^[1,2]\\d{3}(0[1-9]||1[0-2])(0[1-9]||[1,2][0-9]||3[0,1])$");
+		Matcher matcher = pattern.matcher(birthday);
+		if (!matcher.matches()) {
+			return false;
+		}
+		Date birth = null;
+		try {
+			birth = new SimpleDateFormat("yyyyMMdd").parse(birthday);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		if (!new SimpleDateFormat("yyyyMMdd").format(birth).equals(birthday)) {
+			return false;
+		}
+		// 获取当前日期的毫秒数
+		long currentTime = System.currentTimeMillis();
+		// 获取生日的毫秒数
+		long birthTime = birth.getTime();
+		// 如果当前时间小于生日,生日不合法。反之合法
+		if (birthTime > currentTime) {
+			return false;
+		}
+		return true;
+	}
+	
+	public static boolean validateChineseName(String name) {
+		String regEx = "[\u4E00-\u9FA5]{2,5}(?:·[\u4E00-\u9FA5]{2,5})*";
+		if (StringUtils.isEmpty(name)) {
+			return false;
+		}
+		Pattern pattern = Pattern.compile(regEx);
+		Matcher matcher = pattern.matcher(name);
+		return matcher.matches();
+	}
+	
+	public static void main(String[] args) {
+//		System.err.println(validateChineseName(null));
+//		String str = "财务会计 ";
+		String str2 = "080120041";
+		System.out.println(str2.matches("^\\d{9}$"));
+	}
+}

+ 43 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/SSLClient.java

@@ -0,0 +1,43 @@
+package cn.hmsoft.scan.util;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.impl.client.DefaultHttpClient;
+
+/**
+ * 用于进行Https请求的HttpClient
+ * 
+ */
+@SuppressWarnings("deprecation")
+public class SSLClient extends DefaultHttpClient {
+	public SSLClient() throws Exception {
+		super();
+		SSLContext ctx = SSLContext.getInstance("TLS");
+		X509TrustManager tm = new X509TrustManager() {
+
+			public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+			}
+
+			public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+			}
+
+			public X509Certificate[] getAcceptedIssuers() {
+				return null;
+			}
+		};
+		ctx.init(null, new TrustManager[] { tm }, null);
+		SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+		ClientConnectionManager ccm = this.getConnectionManager();
+		SchemeRegistry sr = ccm.getSchemeRegistry();
+		sr.register(new Scheme("https", 443, ssf));
+	}
+}

+ 57 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/ScanFileHelper.java

@@ -0,0 +1,57 @@
+package cn.hmsoft.scan.util;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import cn.hmsoft.frame.exception.web.BusinessException;
+import cn.hmsoft.helper.RandomHelper;
+
+public class ScanFileHelper {
+	
+	/**************************
+	 * 创建临时文件
+	 */
+	public static File createTempFile() {
+		try {
+			return File.createTempFile("Scan" + RandomHelper.getRandomValue(32),
+					".tmp");
+		} catch (Exception e) {
+			throw new BusinessException("无法创建临时文件,请联系系统管理员!");
+		}
+	}
+	/*
+	 * 创建临时文件
+	 */
+	public static File createTempFile(String suffix) {
+		try {
+			return File.createTempFile("Scan" + RandomHelper.getRandomValue(32),
+					"."+suffix);
+		} catch (Exception e) {
+			throw new BusinessException("无法创建临时文件,请联系系统管理员!");
+		}
+	}
+	
+	/**
+	 * @Description: 获取目录下所有的文件
+	 * @param dir 目录
+	 * @return 文件List
+	 * @date 2023-07-25 01:56:07 
+	 */  
+	public static List<File> getAllFiles(File dir) {
+	    List<File> fileList = new ArrayList<File>();
+	    if(dir.isDirectory()) {
+	        File[] subFiles = dir.listFiles();
+	        if(subFiles != null){
+	            for(File subFile : subFiles) {
+	                fileList.addAll(getAllFiles(subFile));
+	            }
+	        }
+	    }
+	    else {
+	        fileList.add(dir);
+	    }
+	    return fileList;
+	}
+	
+}

+ 72 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/SesFileHelper.java

@@ -0,0 +1,72 @@
+package cn.hmsoft.scan.util;
+
+import java.io.File;
+import cn.hmsoft.frame.exception.web.BusinessException;
+import cn.hmsoft.helper.RandomHelper;
+
+/******************************
+ * @note 文件复制类
+ */
+public class SesFileHelper {
+	/**************************
+	 * 创建临时文件
+	 */
+	public static File createTempFile() {
+		try {
+			return File.createTempFile("Art" + RandomHelper.getRandomValue(32),
+					".tmp");
+		} catch (Exception e) {
+			throw new BusinessException("无法创建临时文件,请联系系统管理员!");
+		}
+	}
+	/*
+	 * 创建临时文件
+	 */
+	public static File createTempFile(String suffix) {
+		try {
+			return File.createTempFile("Art" + RandomHelper.getRandomValue(32),
+					"."+suffix);
+		} catch (Exception e) {
+			throw new BusinessException("无法创建临时文件,请联系系统管理员!");
+		}
+	}
+
+	/**********************
+	 * 获取考生照片
+	 */
+/*	public static String getStdImagePath(String std_image, String image_type) {
+		// 判断本地文件是否存在
+		File file = new File(ArtParamUtil.EnrolStdImageLocalFolder
+				+ File.separator + image_type + File.separator + std_image);
+		if (file.exists())
+			return file.getPath();
+		// 如果没有,返回网络地址
+		return ArtParamUtil.EnrolStdImageHttpUrl + "/" + image_type + "/"
+				+ std_image;
+	}*/
+
+	/************************
+	 * 获取考生报考上传的照片
+	 */
+/*	public static Image createStdImage(String std_image) {
+		String path = getStdImagePath(std_image, StdImageType.StdUpload);
+		Image imgStdUpload = null;
+		try {
+			imgStdUpload = new Image(ImageDataFactory.create(path));
+		} catch (Exception e) {
+			try {
+				// 没有考生图片,返回默认值
+				imgStdUpload = new Image(
+						ImageDataFactory.create(SpringConfig.WebServletContext
+								.getRealPath("/")
+								+ File.separator
+								+ "img"
+								+ File.separator + "cert.jpg"));
+			} catch (Exception ex) {
+				throw new BusinessException("无法找到考生照片,且无法用默认照片替代");
+			}
+		}
+		return imgStdUpload;
+	}*/
+
+}

+ 111 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/VerifyCode.java

@@ -0,0 +1,111 @@
+package cn.hmsoft.scan.util;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.util.Random;
+
+public class VerifyCode {
+
+	public static String drawRandomText(int width, int height, BufferedImage verifyImg) {
+
+		Graphics2D graphics = (Graphics2D) verifyImg.getGraphics();
+
+		graphics.setColor(Color.WHITE);// 设置画笔颜色-验证码背景色
+
+		graphics.fillRect(0, 0, width, height);// 填充背景
+
+		graphics.setFont(new Font("微软雅黑", Font.BOLD, 40));
+
+		// 数字和字母的组合
+
+		String baseNumLetter = "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
+
+		StringBuffer sBuffer = new StringBuffer();
+
+		int x = 10; // 旋转原点的 x 坐标
+
+		String ch = "";
+
+		Random random = new Random();
+
+		for (int i = 0; i < 4; i++) {
+
+			graphics.setColor(getRandomColor());
+
+			// 设置字体旋转角度
+
+			int degree = random.nextInt() % 30; // 角度小于30度
+
+			int dot = random.nextInt(baseNumLetter.length());
+
+			ch = baseNumLetter.charAt(dot) + "";
+
+			sBuffer.append(ch);
+
+			// 正向旋转
+
+			graphics.rotate(degree * Math.PI / 180, x, 45);
+
+			graphics.drawString(ch, x, 45);
+
+			// 反向旋转
+
+			graphics.rotate(-degree * Math.PI / 180, x, 45);
+
+			x += 48;
+
+		}
+
+		// 画干扰线
+
+		for (int i = 0; i < 6; i++) {
+
+			// 设置随机颜色
+
+			graphics.setColor(getRandomColor());
+
+			// 随机画线
+
+			graphics.drawLine(random.nextInt(width), random.nextInt(height),
+
+					random.nextInt(width), random.nextInt(height));
+
+		}
+
+		// 添加噪点
+
+		for (int i = 0; i < 30; i++) {
+
+			int x1 = random.nextInt(width);
+
+			int y1 = random.nextInt(height);
+
+			graphics.setColor(getRandomColor());
+
+			graphics.fillRect(x1, y1, 2, 2);
+
+		}
+
+		return sBuffer.toString();
+
+	}
+
+	/**
+	 * 随机取色
+	 */
+
+	private static Color getRandomColor() {
+
+		Random ran = new Random();
+
+		Color color = new Color(ran.nextInt(256),
+
+				ran.nextInt(256), ran.nextInt(256));
+
+		return color;
+
+	}
+
+}

+ 240 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/ZipUtil.java

@@ -0,0 +1,240 @@
+package cn.hmsoft.scan.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.nio.charset.Charset;
+import java.util.Enumeration;
+import java.util.zip.CRC32;
+import java.util.zip.CheckedOutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+import cn.hmsoft.helper.StringHelper;
+
+
+public class ZipUtil {
+	
+	/**
+	 * 递归压缩文件夹
+	 * @param srcRootDir 压缩文件夹根目录的子路径
+	 * @param file 当前递归压缩的文件或目录对象
+	 * @param zos 压缩文件存储对象
+	 * @throws Exception
+	 */
+	private static void zip(String srcRootDir, File file, ZipOutputStream zos) throws Exception
+	{
+		if (file == null) 
+		{
+			return;
+		}				
+		
+		//如果是文件,则直接压缩该文件
+		if (file.isFile())
+		{			
+			int count, bufferLen = 1024;
+			byte data[] = new byte[bufferLen];
+			
+			//获取文件相对于压缩文件夹根目录的子路径
+			String subPath = file.getAbsolutePath();
+			int index = subPath.indexOf(srcRootDir);
+			if (index != -1) 
+			{
+				subPath = subPath.substring(srcRootDir.length() + File.separator.length());
+			}
+			ZipEntry entry = new ZipEntry(subPath);
+			zos.putNextEntry(entry);
+			BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
+			while ((count = bis.read(data, 0, bufferLen)) != -1) 
+			{
+				zos.write(data, 0, count);
+			}
+			bis.close();
+			zos.closeEntry();
+		}
+		//如果是目录,则压缩整个目录
+		else 
+		{
+			//压缩目录中的文件或子目录
+			File[] childFileList = file.listFiles();
+			for (int n=0; n<childFileList.length; n++)
+			{
+				childFileList[n].getAbsolutePath().indexOf(file.getAbsolutePath());
+				zip(srcRootDir, childFileList[n], zos);
+			}
+		}
+	}
+	
+	/**
+	 * 对文件或文件目录进行压缩
+	 * @param srcPath 要压缩的源文件路径。如果压缩一个文件,则为该文件的全路径;如果压缩一个目录,则为该目录的顶层目录路径
+	 * @param zipPath 压缩文件保存的路径。注意:zipPath不能是srcPath路径下的子文件夹
+	 * @param zipFileName 压缩文件名
+	 * @throws Exception
+	 */
+	public static void zip(String srcPath, String zipPath, String zipFileName) throws Exception
+	{
+		if (StringHelper.isEmpty(srcPath) || StringHelper.isEmpty(zipPath) || StringHelper.isEmpty(zipFileName))
+		{
+			throw new NullPointerException();
+		}
+		CheckedOutputStream cos = null;
+		ZipOutputStream zos = null;						
+		try
+		{
+			File srcFile = new File(srcPath);
+			
+			//判断压缩文件保存的路径是否为源文件路径的子文件夹,如果是,则抛出异常(防止无限递归压缩的发生)
+			if (srcFile.isDirectory() && zipPath.indexOf(srcPath)!=-1) 
+			{
+				throw new NullPointerException("zipPath must not be the child directory of srcPath.");
+			}
+			
+			//判断压缩文件保存的路径是否存在,如果不存在,则创建目录
+			File zipDir = new File(zipPath);
+			if (!zipDir.exists() || !zipDir.isDirectory())
+			{
+				zipDir.mkdirs();
+			}
+			
+			//创建压缩文件保存的文件对象
+			String zipFilePath = zipPath + File.separator + zipFileName;
+			File zipFile = new File(zipFilePath);			
+			if (zipFile.exists())
+			{
+				//检测文件是否允许删除,如果不允许删除,将会抛出SecurityException
+				SecurityManager securityManager = new SecurityManager();
+				securityManager.checkDelete(zipFilePath);
+				//删除已存在的目标文件
+				zipFile.delete();				
+			}
+			
+			cos = new CheckedOutputStream(new FileOutputStream(zipFile), new CRC32());
+			zos = new ZipOutputStream(cos);
+			
+			//如果只是压缩一个文件,则需要截取该文件的父目录
+			String srcRootDir =new File(srcPath).getAbsolutePath();
+			if (srcFile.isFile())
+			{
+				int index = srcPath.lastIndexOf(File.separator);
+				if (index != -1)
+				{
+					srcRootDir = srcPath.substring(0, index);
+				}
+			}
+			//调用递归压缩方法进行目录或文件压缩
+			zip(srcRootDir, srcFile, zos);
+			zos.flush();
+		}
+		catch (Exception e) 
+		{
+			throw e;
+		}
+		finally 
+		{			
+			try
+			{
+				if (zos != null)
+				{
+					zos.close();
+				}				
+			}
+			catch (Exception e)
+			{
+				e.printStackTrace();
+			}			
+		}
+	}
+	
+	/**
+	 * 解压缩zip包
+	 * @param zipFilePath zip文件的全路径
+	 * @param unzipFilePath 解压后的文件保存的路径
+	 * @param includeZipFileName 解压后的文件保存的路径是否包含压缩文件的文件名。true-包含;false-不包含
+	 */
+	@SuppressWarnings("unchecked")
+	public static void unzip(String zipFilePath, String unzipFilePath, boolean includeZipFileName) throws Exception
+	{
+		if (StringHelper.isEmpty(zipFilePath) || StringHelper.isEmpty(unzipFilePath))
+		{
+			throw new NullPointerException();			
+		}
+		File zipFile = new File(zipFilePath);
+		//如果解压后的文件保存路径包含压缩文件的文件名,则追加该文件名到解压路径
+		if (includeZipFileName)
+		{
+			String fileName = zipFile.getName();
+			if (StringHelper.isNotEmpty(fileName))
+			{
+				fileName = fileName.substring(0, fileName.lastIndexOf("."));
+			}
+			unzipFilePath = unzipFilePath + File.separator + fileName;
+		}
+		//创建解压缩文件保存的路径
+		File unzipFileDir = new File(unzipFilePath);
+		if (!unzipFileDir.exists() || !unzipFileDir.isDirectory())
+		{
+			unzipFileDir.mkdirs();
+		}
+		
+		//开始解压
+		ZipEntry entry = null;
+		String entryFilePath = null, entryDirPath = null;
+		File entryFile = null, entryDir = null;
+		int index = 0, count = 0, bufferSize = 1024;
+		byte[] buffer = new byte[bufferSize];
+		BufferedInputStream bis = null;
+		BufferedOutputStream bos = null;
+		ZipFile zip = new ZipFile(zipFile,Charset.forName("GBK"));
+		Enumeration<ZipEntry> entries = (Enumeration<ZipEntry>)zip.entries();
+		//循环对压缩包里的每一个文件进行解压		
+		while(entries.hasMoreElements())
+		{
+			entry = entries.nextElement();
+			//构建压缩包中一个文件解压后保存的文件全路径
+			entryFilePath = unzipFilePath + File.separator + entry.getName();
+			//构建解压后保存的文件夹路径
+			index = entryFilePath.lastIndexOf(File.separator);
+			if (index != -1)
+			{
+				entryDirPath = entryFilePath.substring(0, index);
+			}
+			else
+			{
+				entryDirPath = "";
+			}			
+			entryDir = new File(entryDirPath);
+			//如果文件夹路径不存在,则创建文件夹
+			if (!entryDir.exists() || !entryDir.isDirectory())
+			{
+				entryDir.mkdirs();
+			}
+			
+			//创建解压文件
+			entryFile = new File(entryFilePath);
+			if (entryFile.exists())
+			{
+				//检测文件是否允许删除,如果不允许删除,将会抛出SecurityException
+//				SecurityManager securityManager = new SecurityManager();
+//				securityManager.checkDelete(entryFilePath);
+				//删除已存在的目标文件
+				entryFile.delete();	
+			}
+			
+			//写入文件
+			bos = new BufferedOutputStream(new FileOutputStream(entryFile));
+			bis = new BufferedInputStream(zip.getInputStream(entry));
+			while ((count = bis.read(buffer, 0, bufferSize)) != -1)
+			{
+				bos.write(buffer, 0, count);
+			}
+			bos.flush();
+			bos.close();			
+		}
+		zip.close();
+	}
+	
+}

+ 66 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/baidu/Base64Util.java

@@ -0,0 +1,66 @@
+package cn.hmsoft.scan.util.baidu;
+
+
+/**
+ * Base64 工具类
+ */
+public class Base64Util {
+    private static final char last2byte = (char) Integer.parseInt("00000011", 2);
+    private static final char last4byte = (char) Integer.parseInt("00001111", 2);
+    private static final char last6byte = (char) Integer.parseInt("00111111", 2);
+    private static final char lead6byte = (char) Integer.parseInt("11111100", 2);
+    private static final char lead4byte = (char) Integer.parseInt("11110000", 2);
+    private static final char lead2byte = (char) Integer.parseInt("11000000", 2);
+    private static final char[] encodeTable = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
+
+    public Base64Util() {
+    }
+
+    public static String encode(byte[] from) {
+        StringBuilder to = new StringBuilder((int) ((double) from.length * 1.34D) + 3);
+        int num = 0;
+        char currentByte = 0;
+
+        int i;
+        for (i = 0; i < from.length; ++i) {
+            for (num %= 8; num < 8; num += 6) {
+                switch (num) {
+                    case 0:
+                        currentByte = (char) (from[i] & lead6byte);
+                        currentByte = (char) (currentByte >>> 2);
+                    case 1:
+                    case 3:
+                    case 5:
+                    default:
+                        break;
+                    case 2:
+                        currentByte = (char) (from[i] & last6byte);
+                        break;
+                    case 4:
+                        currentByte = (char) (from[i] & last4byte);
+                        currentByte = (char) (currentByte << 2);
+                        if (i + 1 < from.length) {
+                            currentByte = (char) (currentByte | (from[i + 1] & lead2byte) >>> 6);
+                        }
+                        break;
+                    case 6:
+                        currentByte = (char) (from[i] & last2byte);
+                        currentByte = (char) (currentByte << 4);
+                        if (i + 1 < from.length) {
+                            currentByte = (char) (currentByte | (from[i + 1] & lead4byte) >>> 4);
+                        }
+                }
+
+                to.append(encodeTable[currentByte]);
+            }
+        }
+
+        if (to.length() % 4 != 0) {
+            for (i = 4 - to.length() % 4; i > 0; --i) {
+                to.append("=");
+            }
+        }
+
+        return to.toString();
+    }
+}

+ 72 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/baidu/FileUtil.java

@@ -0,0 +1,72 @@
+package cn.hmsoft.scan.util.baidu;
+
+import java.io.*;
+
+/**
+ * 文件读取工具类
+ */
+public class FileUtil {
+
+    /**
+     * 读取文件内容,作为字符串返回
+     */
+    public static String readFileAsString(String filePath) throws IOException {
+        File file = new File(filePath);
+        if (!file.exists()) {
+            throw new FileNotFoundException(filePath);
+        } 
+
+        if (file.length() > 1024 * 1024 * 1024) {
+            throw new IOException("File is too large");
+        } 
+
+        StringBuilder sb = new StringBuilder((int) (file.length()));
+        // 创建字节输入流  
+        FileInputStream fis = new FileInputStream(filePath);  
+        // 创建一个长度为10240的Buffer
+        byte[] bbuf = new byte[10240];  
+        // 用于保存实际读取的字节数  
+        int hasRead = 0;  
+        while ( (hasRead = fis.read(bbuf)) > 0 ) {  
+            sb.append(new String(bbuf, 0, hasRead));  
+        }  
+        fis.close();  
+        return sb.toString();
+    }
+
+    /**
+     * 根据文件路径读取byte[] 数组
+     */
+    public static byte[] readFileByBytes(String filePath) throws IOException {
+        File file = new File(filePath);
+        if (!file.exists()) {
+            throw new FileNotFoundException(filePath);
+        } else {
+            ByteArrayOutputStream bos = new ByteArrayOutputStream((int) file.length());
+            BufferedInputStream in = null;
+
+            try {
+                in = new BufferedInputStream(new FileInputStream(file));
+                short bufSize = 1024;
+                byte[] buffer = new byte[bufSize];
+                int len1;
+                while (-1 != (len1 = in.read(buffer, 0, bufSize))) {
+                    bos.write(buffer, 0, len1);
+                }
+
+                byte[] var7 = bos.toByteArray();
+                return var7;
+            } finally {
+                try {
+                    if (in != null) {
+                        in.close();
+                    }
+                } catch (IOException var14) {
+                    var14.printStackTrace();
+                }
+
+                bos.close();
+            }
+        }
+    }
+}

+ 29 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/baidu/GsonUtils.java

@@ -0,0 +1,29 @@
+package cn.hmsoft.scan.util.baidu;
+
+
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonParseException;
+
+import java.lang.reflect.Type;
+
+/**
+ * Json工具类.
+ */
+public class GsonUtils {
+    private static Gson gson = new GsonBuilder().create();
+
+    public static String toJson(Object value) {
+        return gson.toJson(value);
+    }
+
+    public static <T> T fromJson(String json, Class<T> classOfT) throws JsonParseException {
+        return gson.fromJson(json, classOfT);
+    }
+
+    @SuppressWarnings("unchecked")
+	public static <T> T fromJson(String json, Type typeOfT) throws JsonParseException {
+        return (T) gson.fromJson(json, typeOfT);
+    }
+}

+ 74 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/baidu/HttpUtil.java

@@ -0,0 +1,74 @@
+package cn.hmsoft.scan.util.baidu;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+/**
+ * http 工具类
+ */
+public class HttpUtil {
+
+    public static String post(String requestUrl, String accessToken, String params)
+            throws Exception {
+        String contentType = "application/x-www-form-urlencoded";
+        return HttpUtil.post(requestUrl, accessToken, contentType, params);
+    }
+
+    public static String post(String requestUrl, String accessToken, String contentType, String params)
+            throws Exception {
+        String encoding = "UTF-8";
+        if (requestUrl.contains("nlp")) {
+            encoding = "GBK";
+        }
+        return HttpUtil.post(requestUrl, accessToken, contentType, params, encoding);
+    }
+
+    public static String post(String requestUrl, String accessToken, String contentType, String params, String encoding)
+            throws Exception {
+        String url = requestUrl + "?access_token=" + accessToken;
+        return HttpUtil.postGeneralUrl(url, contentType, params, encoding);
+    }
+
+    public static String postGeneralUrl(String generalUrl, String contentType, String params, String encoding)
+            throws Exception {
+        URL url = new URL(generalUrl);
+        // 打开和URL之间的连接
+        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+        connection.setRequestMethod("POST");
+        // 设置通用的请求属性
+        connection.setRequestProperty("Content-Type", contentType);
+        connection.setRequestProperty("Connection", "Keep-Alive");
+        connection.setUseCaches(false);
+        connection.setDoOutput(true);
+        connection.setDoInput(true);
+
+        // 得到请求的输出流对象
+        DataOutputStream out = new DataOutputStream(connection.getOutputStream());
+        out.write(params.getBytes(encoding));
+        out.flush();
+        out.close();
+
+        // 建立实际的连接
+        connection.connect();
+        /** 获取所有响应头字段
+        Map<String, List<String>> headers = connection.getHeaderFields();
+                    遍历所有的响应头字段
+        for (String key : headers.keySet()) {
+            System.err.println(key + "--->" + headers.get(key));
+        }**/
+        // 定义 BufferedReader输入流来读取URL的响应
+        BufferedReader in = null;
+        in = new BufferedReader(
+                new InputStreamReader(connection.getInputStream(), encoding));
+        String result = "";
+        String getLine;
+        while ((getLine = in.readLine()) != null) {
+            result += getLine;
+        }
+        in.close();
+        return result;
+    }
+}

+ 109 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/baidu/auth/BaiduAuthService.java

@@ -0,0 +1,109 @@
+package cn.hmsoft.scan.util.baidu.auth;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Calendar;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+import cn.hmsoft.frame.data.dao.FrameParamDao;
+import cn.hmsoft.frame.data.model.FrameParam;
+import cn.hmsoft.frame.util.FrameAssertUtil;
+import cn.hmsoft.scan.common.constants.SysConst;
+
+
+/**  
+ * @Description: 百度获取授权
+ * @author hgh
+ * @date 2023-07-25 09:34:19 
+ */
+public class BaiduAuthService {
+
+	private static Calendar expireDate = null;//判断token是否过期
+	private static boolean flag = false; // 是否已经获取过了 
+	private static String accessToken = null;
+
+	
+	/**
+	 * @Description: 是否需要重新获取token
+	 * @date 2023-07-25 09:37:06 
+	 */  
+	public static Boolean needAuth() {
+		Calendar c = Calendar.getInstance();
+		c.add(Calendar.DATE, 1); // 当前日期加一天
+		return Boolean.valueOf(!flag || c.after(expireDate));
+	}
+	
+	/**
+	 * @Description: 获取权限token
+	 * @date 2023-07-25 09:37:45 
+	 */  
+	public static synchronized String getAuth() {
+//		FrameParamDao paramDao = SysConst.getCurrentWebApplicationContext().getBean(FrameParamDao.class);
+//		FrameParam param = paramDao.find("ClientId");
+//		FrameAssertUtil.isNotNull(param, "未配置clientId!");
+//		String clientId = param.getParam_value();
+//		param = paramDao.find("ClientSecret");
+//		FrameAssertUtil.isNotNull(param, "未配置ClientSecret!");
+//		String clientSecret = param.getParam_value();
+		String clientId ="p7SV9y0kapnTIj6bAE8Qjz6c";
+		String clientSecret="dALAVYkeGp7Hs3HLio0YagVDK9TXhb9Q";
+		flag = true;
+		return getAuth(clientId, clientSecret);
+	}
+	
+	private static synchronized String getAuth(String clientId, String clientSecret) {
+		// 获取token地址
+		String authHost = "https://aip.baidubce.com/oauth/2.0/token?";
+		String getAccessTokenUrl = authHost
+				// 1. grant_type为固定参数
+				+ "grant_type=client_credentials"
+				// 2. 官网获取的 API Key
+				+ "&client_id=" + clientId
+				// 3. 官网获取的 Secret Key
+				+ "&client_secret=" + clientSecret;
+		try {
+			URL realUrl = new URL(getAccessTokenUrl);
+			// 打开和URL之间的连接
+			HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();
+			connection.setRequestMethod("POST");
+			connection.connect();
+			// 定义 BufferedReader输入流来读取URL的响应
+			BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+			String result = "";
+			String line;
+			while ((line = in.readLine()) != null) {
+				result += line;
+			}
+			JsonObject json = new JsonParser().parse(result).getAsJsonObject();
+			String access_token = json.get("access_token").getAsString();
+			Integer expires_in = json.get("expires_in").getAsInt();
+			Calendar c = Calendar.getInstance();
+			c.add(Calendar.SECOND, expires_in.intValue());
+			expireDate = c;
+			accessToken = access_token;
+			System.out.println("accessToken----------------" + accessToken);
+			return access_token;
+		} catch (Exception e) {
+			System.err.printf("获取token失败!");
+			e.printStackTrace(System.err);
+		}
+		return null;
+	}
+	
+	
+	/**
+	 * @Description: 获取baidu的token
+	 * @date 2023-07-25 10:58:25
+	 */  
+	public static String getToken() {
+		if (needAuth().booleanValue())
+			getAuth();
+		return accessToken;
+	}
+	
+	
+}

+ 73 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/baidu/doc/DocAnalysis.java

@@ -0,0 +1,73 @@
+package cn.hmsoft.scan.util.baidu.doc;
+
+import java.io.File;
+import java.net.URLEncoder;
+
+import cn.hmsoft.frame.exception.web.BusinessException;
+import cn.hmsoft.helper.FileHelper;
+import cn.hmsoft.helper.StringHelper;
+import cn.hmsoft.scan.util.baidu.auth.BaiduAuthService;
+import cn.hmsoft.scan.util.baidu.Base64Util;
+import cn.hmsoft.scan.util.baidu.FileUtil;
+import cn.hmsoft.scan.util.baidu.HttpUtil;
+
+
+/**  
+ * @Description: baidu-Ocr文档识别
+ * @author hgh
+ * @date 2023-07-25 10:12:29 
+ */
+public class DocAnalysis {
+	
+	private static String url = "https://aip.baidubce.com/rest/2.0/ocr/v1/doc_analysis";
+
+	/**
+	 * @Description: 解析文档,支持pdf、图片
+	 * @param filePath 文件路径
+	 * @param pageNum 文件的页码,为pdf才使用
+	 * @return 解析后的json数据
+	 * @date 2023-07-25 11:12:38 
+	 */  
+	public static String docAnalysis(String filePath, int pageNum) {
+		File doc = new File(filePath);
+		if(!doc.exists()) 
+			throw new BusinessException("文件不存在!");
+		return docAnalysis(new File(filePath), pageNum);
+	}
+	
+	/**
+	 * @Description: 解析文档,支持pdf、图片
+	 * @param doc 待解析的文件
+	 * @param pageNum 文件的页码,为pdf才使用
+	 * @return 解析后的json数据
+	 * @date 2023-07-25 11:12:38 
+	 */  
+	public static String docAnalysis(File doc, int pageNum) {
+		String result = null;
+		try {
+			byte[] imgData = FileUtil.readFileByBytes(doc.getAbsolutePath());
+			String imgStr = Base64Util.encode(imgData);
+			String imgParam = URLEncoder.encode(imgStr, "UTF-8");
+			String suffix = FileHelper.getSuffix(doc);
+			if (StringHelper.isEmpty(suffix))
+				return null;
+			String param = "";
+			if ("pdf".equals(suffix.toLowerCase())) {
+				param = "language_type=" + "CHN_ENG" + "&result_type=" + "big" + "&pdf_file=" + imgParam
+						+ "&pdf_file_num=" + pageNum;
+			} else if ("jpg".equals(suffix.toLowerCase()) || "png".equals(suffix.toLowerCase())) {
+				param = "language_type=" + "CHN_ENG" + "&result_type=" + "big" + "&image=" + imgParam;
+			}
+			String accessToken = BaiduAuthService.getToken();
+			result = HttpUtil.post(url, accessToken, param);
+		} catch (Exception e) {
+			System.err.println("文档解析失败,文件路径:" + doc.getAbsolutePath());
+			e.printStackTrace();
+		}
+		return result;
+	}
+	
+	
+	
+	
+}

+ 92 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/baidu/doc/Handwriting.java

@@ -0,0 +1,92 @@
+package cn.hmsoft.scan.util.baidu.doc;
+
+
+
+import java.io.File;
+import java.net.URLEncoder;
+
+import cn.hmsoft.frame.exception.web.BusinessException;
+import cn.hmsoft.helper.FileHelper;
+import cn.hmsoft.helper.StringHelper;
+import cn.hmsoft.scan.util.baidu.Base64Util;
+import cn.hmsoft.scan.util.baidu.FileUtil;
+import cn.hmsoft.scan.util.baidu.HttpUtil;
+import cn.hmsoft.scan.util.baidu.auth.BaiduAuthService;
+
+/*******************************
+* 手写文字识别
+*/
+public class Handwriting {
+	private static String url = "https://aip.baidubce.com/rest/2.0/ocr/v1/handwriting";
+	
+	
+	/**
+	 * @Description: 解析文档,支持pdf、图片
+	 * @param filePath 文件路径
+	 * @param pageNum 文件的页码,为pdf才使用
+	 * @return 解析后的json数据
+	 * @date 2023-07-27 11:05:56 
+	 */  
+	public static String handwriting(String filePath, int pageNum) {
+		File doc = new File(filePath);
+		if(!doc.exists()) 
+			throw new BusinessException("文件不存在!");
+		return handwriting(new File(filePath), pageNum);
+	}
+
+
+    private static String handwriting(File file, int pageNum) {
+    	String result = null;
+		try {
+			byte[] imgData = FileUtil.readFileByBytes(file.getAbsolutePath());
+			String imgStr = Base64Util.encode(imgData);
+			String imgParam = URLEncoder.encode(imgStr, "UTF-8");
+			String suffix = FileHelper.getSuffix(file);
+			if (StringHelper.isEmpty(suffix))
+				return null;
+			String param = "";
+			if ("pdf".equals(suffix.toLowerCase())) {
+				 param = "pdf_file=" + imgParam +"&pdf_file_num=" + pageNum;
+			} else if ("jpg".equals(suffix.toLowerCase()) || "png".equals(suffix.toLowerCase())) {
+				 param = "image=" + imgParam;
+			}
+			String accessToken = BaiduAuthService.getToken();
+			result = HttpUtil.post(url, accessToken, param);
+		} catch (Exception e) {
+			System.err.println("文档解析失败,文件路径:" + file.getAbsolutePath());
+			e.printStackTrace();
+		}
+		return result;
+	}
+
+
+	public static String handwritingTest() {
+        try {
+            // 本地文件路径
+            String filePath = "d:/test/26.pdf";
+            byte[] imgData = FileUtil.readFileByBytes(filePath);
+            String imgStr = Base64Util.encode(imgData);
+            String imgParam = URLEncoder.encode(imgStr, "UTF-8");
+
+            String param = "pdf_file=" + imgParam +"&pdf_file_num=1";
+
+            // 注意这里仅为了简化编码每一次请求都去获取access_token,线上环境access_token有过期时间, 客户端可自行缓存,过期后重新获取。
+            String accessToken = "24.51e6ed6e4ee78f6b69799d022bfde9a4.2592000.1693101086.282335-22732095";
+
+            String result = HttpUtil.post(url, accessToken, param);
+            System.out.println(result);
+            return result;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public static void main(String[] args) {
+//        Handwriting.handwritingTest();
+//    	String str = "1919911111";
+//    	String match = "^\\d{10}$";
+//    	System.out.println(str.matches(match));;
+    	System.out.println(handwriting(new File("D:/10048/cut.pdf"), 1));;
+    }
+}

+ 18 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/dbf/DBFBase.java

@@ -0,0 +1,18 @@
+package cn.hmsoft.scan.util.dbf;
+
+public abstract class DBFBase {
+
+	protected String characterSetName = "8859_1";
+	protected final int END_OF_DATA = 0x1A;
+
+	public String getCharactersetName() {
+
+		return this.characterSetName;
+	}
+
+	public void setCharactersetName(String characterSetName) {
+
+		this.characterSetName = characterSetName;
+	}
+
+}

+ 16 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/dbf/DBFException.java

@@ -0,0 +1,16 @@
+package cn.hmsoft.scan.util.dbf;
+
+import java.io.IOException;
+
+public class DBFException extends IOException {
+
+	private static final long serialVersionUID = -883243652959406984L;
+
+	public DBFException() {
+		super();
+	}
+
+	public DBFException(String msg) {
+		super(msg);
+	}
+}

+ 110 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/dbf/DBFFactory.java

@@ -0,0 +1,110 @@
+package cn.hmsoft.scan.util.dbf;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+
+public class DBFFactory {
+	public static void main(String args[]) {
+		String path = "D:\\fxgmms.dbf";
+		writeDBF(path);
+	}
+
+	public static void readDBF(String path) {
+		InputStream fis = null;
+		try {
+			// 读取文件的输入流
+			fis = new FileInputStream(path);
+			// 根据输入流初始化一个DBFReader实例,用来读取DBF文件信息
+			DBFReader reader = new DBFReader(fis);
+			reader.setCharactersetName("GBK");
+			// 调用DBFReader对实例方法得到path文件中字段的个数
+			int fieldsCount = reader.getFieldCount();
+
+			// 取出字段信息
+			for (int i = 0; i < fieldsCount; i++) {
+				DBFField field = reader.getField(i);
+			}
+			Object[] rowValues;
+			// 一条条取出path文件中记录
+			while ((rowValues = reader.nextRecord()) != null) {
+				for (int i = 0; i < rowValues.length; i++) {
+				}
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		} finally {
+			try {
+				fis.close();
+			} catch (Exception e) {
+			}
+		}
+	}
+
+	public static void writeDBF(String path) {
+		OutputStream fos = null;
+		try {
+			// 定义DBF文件字段
+			DBFField[] fields = new DBFField[3];
+			// 分别定义各个字段信息,setFieldName和setName作用相同,
+			// 只是setFieldName已经不建议使用
+			fields[0] = new DBFField();
+			// fields[0].setFieldName("emp_code");
+			fields[0].setName("semp_code");
+			fields[0].setDataType(DBFField.FIELD_TYPE_C);
+			fields[0].setFieldLength(10);
+			fields[0].setDecimalCount(0);
+			fields[1] = new DBFField();
+			// fields[1].setFieldName("emp_name");
+			fields[1].setName("emp_name");
+			fields[1].setDataType(DBFField.FIELD_TYPE_C);
+			fields[1].setFieldLength(100);
+			fields[1].setDecimalCount(0);
+			fields[2] = new DBFField();
+			// fields[2].setFieldName("salary");
+			fields[2].setName("salary");
+			fields[2].setDataType(DBFField.FIELD_TYPE_N);
+			fields[2].setFieldLength(12);
+			fields[2].setDecimalCount(2);
+			// DBFWriter writer = new DBFWriter(new File(path));
+			// 定义DBFWriter实例用来写DBF文件
+			DBFWriter writer = new DBFWriter();
+			// 把字段信息写入DBFWriter实例,即定义表结构
+			writer.setFields(fields);
+			// 一条条的写入记录
+			Object[] rowData = new Object[3];
+			rowData[0] = "1000";
+			rowData[1] = "西医临床学基础(含微生物、免疫、病理、药理学)(实践)";
+			rowData[2] = new Double(5000.00);
+			writer.addRecord(rowData);
+			rowData = new Object[3];
+			rowData[0] = "1001";
+			rowData[1] = "测试";
+			rowData[2] = new Double(3400.00);
+			writer.addRecord(rowData);
+			rowData = new Object[3];
+			rowData[0] = "1002";
+			rowData[1] = "中华人民共和国";
+			rowData[2] = new Double(7350.00);
+			writer.addRecord(rowData);
+			// 定义输出流,并关联的一个文件
+			fos = new FileOutputStream(path);
+			// 写入数据
+			writer.write(fos);
+			// writer.write();
+		} catch (Exception e) {
+			e.printStackTrace();
+		} finally {
+			try {
+				fos.close();
+			} catch (Exception e) {
+			}
+		}
+	}
+
+
+
+
+}

+ 265 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/dbf/DBFField.java

@@ -0,0 +1,265 @@
+package cn.hmsoft.scan.util.dbf;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+public class DBFField {
+
+	 public static final byte FIELD_TYPE_C = (byte)'C';
+	 public static final byte FIELD_TYPE_L = (byte)'L';
+	 public static final byte FIELD_TYPE_N = (byte)'N';
+	 public static final byte FIELD_TYPE_F = (byte)'F';
+	 public static final byte FIELD_TYPE_D = (byte)'D';
+	 public static final byte FIELD_TYPE_M = (byte)'M';
+
+	 /* Field struct variables start here */
+	 byte[] fieldName = new byte[ 11]; /* 0-10*/
+	 byte dataType;                    /* 11 */
+	 int reserv1;                      /* 12-15 */
+	 int fieldLength;                 /* 16 */
+	 byte decimalCount;                /* 17 */
+	 short reserv2;                    /* 18-19 */
+	 byte workAreaId;                  /* 20 */
+	 short reserv3;                    /* 21-22 */
+	 byte setFieldsFlag;               /* 23 */
+	 byte[] reserv4 = new byte[ 7];    /* 24-30 */
+	 byte indexFieldFlag;              /* 31 */
+	 /* Field struct variables end here */
+
+	 /* other class variables */
+	 int nameNullIndex = 0;
+
+	 /**
+	 Creates a DBFField object from the data read from the given DataInputStream.
+
+	 The data in the DataInputStream object is supposed to be organised correctly
+	 and the stream "pointer" is supposed to be positioned properly.
+
+	 @param in DataInputStream
+	 @return Returns the created DBFField object.
+	 @throws IOException If any stream reading problems occures.
+	 */
+	 protected static DBFField createField( DataInput in) 
+	 throws IOException {
+
+	  DBFField field = new DBFField();
+
+	  byte t_byte = in.readByte(); /* 0 */
+	  if( t_byte == (byte)0x0d) {
+
+	   return null;
+	  }
+
+	  in.readFully( field.fieldName, 1, 10); /* 1-10 */
+	  field.fieldName[0] = t_byte;
+
+	  for( int i=0; i<field.fieldName.length; i++) {
+
+	   if( field.fieldName[ i] == (byte)0) {
+
+	    field.nameNullIndex = i;
+	    break;
+	   }
+	  }
+
+	  field.dataType = in.readByte(); /* 11 */
+	  field.reserv1 = Utils.readLittleEndianInt( in); /* 12-15 */
+	  field.fieldLength = in.readUnsignedByte();  /* 16 */
+	  field.decimalCount = in.readByte(); /* 17 */
+	  field.reserv2 = Utils.readLittleEndianShort( in); /* 18-19 */
+	  field.workAreaId = in.readByte(); /* 20 */
+	  field.reserv2 = Utils.readLittleEndianShort( in); /* 21-22 */
+	  field.setFieldsFlag = in.readByte(); /* 23 */
+	  in.readFully( field.reserv4); /* 24-30 */
+	  field.indexFieldFlag = in.readByte(); /* 31 */
+	  return field;
+	 }
+
+	 /**
+	  Writes the content of DBFField object into the stream as per
+	  DBF format specifications.
+
+	  @param os OutputStream
+	  @throws IOException if any stream related issues occur.
+	 */
+	 protected void write( DataOutput out)
+	 throws IOException {
+
+	  //DataOutputStream out = new DataOutputStream( os);
+
+	  // Field Name
+	  out.write( fieldName);        /* 0-10 */
+	  out.write( new byte[ 11 - fieldName.length]);
+
+	  // data type
+	  out.writeByte( dataType); /* 11 */
+	  out.writeInt( 0x00);   /* 12-15 */
+	  out.writeByte( fieldLength); /* 16 */
+	  out.writeByte( decimalCount); /* 17 */
+	  out.writeShort( (short)0x00); /* 18-19 */
+	  out.writeByte( (byte)0x00); /* 20 */
+	  out.writeShort( (short)0x00); /* 21-22 */
+	  out.writeByte( (byte)0x00); /* 23 */
+	  out.write( new byte[7]); /* 24-30*/
+	  out.writeByte( (byte)0x00); /* 31 */
+	 }
+
+	 /**
+	  Returns the name of the field.
+
+	  @return Name of the field as String.
+	 */
+	 public String getName() {
+
+	  return new String( this.fieldName, 0, nameNullIndex);
+	 }
+
+	 /**
+	  Returns the data type of the field.
+
+	  @return Data type as byte.
+	 */
+	 public byte getDataType() {
+
+	  return dataType;
+	 }
+
+	 /**
+	  Returns field length.
+
+	  @return field length as int.
+	 */
+	 public int getFieldLength() {
+
+	  return fieldLength;
+	 }
+
+	 /**
+	  Returns the decimal part. This is applicable
+	  only if the field type if of numeric in nature.
+
+	  If the field is specified to hold integral values
+	  the value returned by this method will be zero.
+
+	  @return decimal field size as int.
+	 */
+	 public int getDecimalCount() {
+
+	  return decimalCount;
+	 }
+
+	 // Setter methods
+
+	 // byte[] fieldName = new byte[ 11]; /* 0-10*/
+	  // byte dataType;                    /* 11 */
+	  // int reserv1;                      /* 12-15 */
+	  // byte fieldLength;                 /* 16 */
+	  // byte decimalCount;                /* 17 */
+	  // short reserv2;                    /* 18-19 */
+	  // byte workAreaId;                  /* 20 */
+	  // short reserv3;                    /* 21-22 */
+	  // byte setFieldsFlag;               /* 23 */
+	  // byte[] reserv4 = new byte[ 7];    /* 24-30 */
+	  // byte indexFieldFlag;              /* 31 */
+
+	 /**
+	  * @deprecated This method is depricated as of version 0.3.3.1 and is replaced by{@link #setName( String)}.
+	  */
+	 public void setFieldName( String value) {
+
+	  setName( value);
+	 }
+
+	 /**
+	  Sets the name of the field.
+
+	  @param name of the field as String.
+	  @since 0.3.3.1
+	 */
+	 public void setName( String value) {
+
+	  if( value == null) {
+
+	   throw new IllegalArgumentException( "Field name cannot be null");
+	  }
+
+	  if( value.length() == 0 || value.length() > 10) {
+
+	   throw new IllegalArgumentException( "Field name should be of length 0-10");
+	  }
+
+	  this.fieldName = value.getBytes();
+	  this.nameNullIndex = this.fieldName.length;
+	 }
+
+	 /**
+	  Sets the data type of the field.
+
+	  @param type of the field. One of the following:<br>
+	  C, L, N, F, D, M
+	 */
+	 public void setDataType( byte value) {
+
+	  switch( value) {
+
+	   case 'D':
+	    this.fieldLength = 8; /* fall through */
+	   case 'C':
+	   case 'L':
+	   case 'N':
+	   case 'F':
+	   case 'M':
+
+	    this.dataType = value;
+	    break;
+
+	   default:
+	    throw new IllegalArgumentException( "Unknown data type");
+	  }
+	 }
+
+	 /**
+	  Length of the field.
+	  This method should be called before calling setDecimalCount().
+
+	  @param Length of the field as int.
+	 */
+	 public void setFieldLength( int value) {
+
+	  if( value <= 0) {
+
+	   throw new IllegalArgumentException( "Field length should be a positive number");
+	  }
+
+	  if( this.dataType == FIELD_TYPE_D) {
+
+	   throw new UnsupportedOperationException( "Cannot do this on a Date field");
+	  }
+
+	  fieldLength = value;
+	 }
+
+	 /**
+	  Sets the decimal place size of the field.
+	  Before calling this method the size of the field
+	  should be set by calling setFieldLength().
+
+	  @param Size of the decimal field.
+	 */
+	 public void setDecimalCount( int value) {
+
+	  if( value < 0) {
+
+	   throw new IllegalArgumentException( "Decimal length should be a positive number");
+	  }
+
+	  if( value > fieldLength) {
+
+	   throw new IllegalArgumentException( "Decimal length should be less than field length");
+	  }
+
+	  decimalCount = (byte)value;
+	 }
+
+	}

+ 134 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/dbf/DBFHeader.java

@@ -0,0 +1,134 @@
+package cn.hmsoft.scan.util.dbf;
+
+import java.io.*;
+import java.util.*;
+
+class DBFHeader {
+
+	static final byte SIG_DBASE_III = (byte) 0x03;
+	/* DBF structure start here */
+
+	byte signature; /* 0 */
+	byte year; /* 1 */
+	byte month; /* 2 */
+	byte day; /* 3 */
+	int numberOfRecords; /* 4-7 */
+	short headerLength; /* 8-9 */
+	short recordLength; /* 10-11 */
+	short reserv1; /* 12-13 */
+	byte incompleteTransaction; /* 14 */
+	byte encryptionFlag; /* 15 */
+	int freeRecordThread; /* 16-19 */
+	int reserv2; /* 20-23 */
+	int reserv3; /* 24-27 */
+	byte mdxFlag; /* 28 */
+	byte languageDriver; /* 29 */
+	short reserv4; /* 30-31 */
+	DBFField[] fieldArray; /* each 32 bytes */
+	byte terminator1; /* n+1 */
+
+	// byte[] databaseContainer; /* 263 bytes */
+	/* DBF structure ends here */
+
+	DBFHeader() {
+
+		this.signature = SIG_DBASE_III;
+		this.terminator1 = 0x0D;
+	}
+
+	void read(DataInput dataInput) throws IOException {
+
+		signature = dataInput.readByte(); /* 0 */
+		year = dataInput.readByte(); /* 1 */
+		month = dataInput.readByte(); /* 2 */
+		day = dataInput.readByte(); /* 3 */
+		numberOfRecords = Utils.readLittleEndianInt(dataInput); /* 4-7 */
+
+		headerLength = Utils.readLittleEndianShort(dataInput); /* 8-9 */
+		recordLength = Utils.readLittleEndianShort(dataInput); /* 10-11 */
+
+		reserv1 = Utils.readLittleEndianShort(dataInput); /* 12-13 */
+		incompleteTransaction = dataInput.readByte(); /* 14 */
+		encryptionFlag = dataInput.readByte(); /* 15 */
+		freeRecordThread = Utils.readLittleEndianInt(dataInput); /* 16-19 */
+		reserv2 = dataInput.readInt(); /* 20-23 */
+		reserv3 = dataInput.readInt(); /* 24-27 */
+		mdxFlag = dataInput.readByte(); /* 28 */
+		languageDriver = dataInput.readByte(); /* 29 */
+		reserv4 = Utils.readLittleEndianShort(dataInput); /* 30-31 */
+
+		Vector v_fields = new Vector();
+
+		DBFField field = DBFField.createField(dataInput); /* 32 each */
+		while (field != null) {
+
+			v_fields.addElement(field);
+			field = DBFField.createField(dataInput);
+		}
+
+		fieldArray = new DBFField[v_fields.size()];
+
+		for (int i = 0; i < fieldArray.length; i++) {
+
+			fieldArray[i] = (DBFField) v_fields.elementAt(i);
+		}
+
+	}
+
+	void write(DataOutput dataOutput) throws IOException {
+
+		dataOutput.writeByte(signature); /* 0 */
+
+		GregorianCalendar calendar = new GregorianCalendar();
+		year = (byte) (calendar.get(Calendar.YEAR) - 1900);
+		month = (byte) (calendar.get(Calendar.MONTH) + 1);
+		day = (byte) (calendar.get(Calendar.DAY_OF_MONTH));
+
+		dataOutput.writeByte(year); /* 1 */
+		dataOutput.writeByte(month); /* 2 */
+		dataOutput.writeByte(day); /* 3 */
+
+		numberOfRecords = Utils.littleEndian(numberOfRecords);
+		dataOutput.writeInt(numberOfRecords); /* 4-7 */
+
+		headerLength = findHeaderLength();
+		dataOutput.writeShort(Utils.littleEndian(headerLength)); /* 8-9 */
+
+		recordLength = findRecordLength();
+		dataOutput.writeShort(Utils.littleEndian(recordLength)); /* 10-11 */
+
+		dataOutput.writeShort(Utils.littleEndian(reserv1)); /* 12-13 */
+		dataOutput.writeByte(incompleteTransaction); /* 14 */
+		dataOutput.writeByte(encryptionFlag); /* 15 */
+		dataOutput.writeInt(Utils.littleEndian(freeRecordThread));/* 16-19 */
+		dataOutput.writeInt(Utils.littleEndian(reserv2)); /* 20-23 */
+		dataOutput.writeInt(Utils.littleEndian(reserv3)); /* 24-27 */
+
+		dataOutput.writeByte(mdxFlag); /* 28 */
+		dataOutput.writeByte(languageDriver); /* 29 */
+		dataOutput.writeShort(Utils.littleEndian(reserv4)); /* 30-31 */
+
+		for (int i = 0; i < fieldArray.length; i++) {
+
+			fieldArray[i].write(dataOutput);
+		}
+
+		dataOutput.writeByte(terminator1); /* n+1 */
+	}
+
+	private short findHeaderLength() {
+
+		return (short) (1 + 3 + 4 + 2 + 2 + 2 + 1 + 1 + 4 + 4 + 4 + 1 + 1 + 2 + (32 * fieldArray.length) + 1);
+	}
+
+	private short findRecordLength() {
+
+		int recordLength = 0;
+		for (int i = 0; i < fieldArray.length; i++) {
+
+			recordLength += fieldArray[i].getFieldLength();
+		}
+
+		return (short) (recordLength + 1);
+	}
+}

+ 304 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/dbf/DBFReader.java

@@ -0,0 +1,304 @@
+package cn.hmsoft.scan.util.dbf;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * DBFReader class can creates objects to represent DBF data.
+ * 
+ * This Class is used to read data from a DBF file. Meta data and records can be
+ * queried against this document.
+ * 
+ * <p>
+ * DBFReader cannot write anythng to a DBF file. For creating DBF files use
+ * DBFWriter.
+ * 
+ * <p>
+ * Fetching rocord is possible only in the forward direction and cannot
+ * re-wound. In such situation, a suggested approach is to reconstruct the
+ * object.
+ * 
+ * <p>
+ * The nextRecord() method returns an array of Objects and the types of these
+ * Object are as follows:
+ * 
+ */
+public class DBFReader extends DBFBase {
+
+	DataInputStream dataInputStream;
+	DataInputStream memoInputStream;
+	DBFHeader header;
+	int blockLength;
+
+	/* Class specific variables */
+	boolean isClosed = true;
+
+	/**
+	 * Initializes a DBFReader object.
+	 * 
+	 * When this constructor returns the object will have completed reading the
+	 * hader (meta date) and header information can be quried there on. And it will
+	 * be ready to return the first row.
+	 * 
+	 * @param InputStream
+	 *            where the data is read from.
+	 */
+	public DBFReader(InputStream in) throws DBFException {
+
+		try {
+			this.dataInputStream = new DataInputStream(in);
+			this.isClosed = false;
+			this.header = new DBFHeader();
+			this.header.read(this.dataInputStream);
+			/* it might be required to leap to the start of records at times */
+			int t_dataStartIndex = this.header.headerLength - (32 + (32 * this.header.fieldArray.length)) - 1;
+			if (t_dataStartIndex > 0) {
+				dataInputStream.skip(t_dataStartIndex);
+			}
+		} catch (IOException e) {
+
+			throw new DBFException(e.getMessage());
+		}
+	}
+
+	public DBFReader(InputStream in, InputStream memo) throws DBFException {
+		this(in);
+		// 读入memo内容
+		try {
+			this.memoInputStream = new DataInputStream(memo);
+			this.initMemoInputStream();// 初始化数据流,代码见下面
+		} catch (IOException e) {
+			throw new DBFException(e.getMessage());
+		}
+	}
+
+	private void initMemoInputStream() throws IOException {
+		memoInputStream.read(new byte[6]); // 00 - 03 下一个自由块的位置1 04 – 05 未使用
+		blockLength = 0; // Utils.readHighEndianShort(memoInputStream); // 06 – 07 块大小(每个块的字节数)1
+		memoInputStream.read(new byte[504]); // 08 – 511 未使用
+	}
+
+	public String toString() {
+
+		StringBuffer sb = new StringBuffer(
+				this.header.year + "/" + this.header.month + "/" + this.header.day + "\n" + "Total records: "
+						+ this.header.numberOfRecords + "\nHEader length: " + this.header.headerLength + "");
+
+		for (int i = 0; i < this.header.fieldArray.length; i++) {
+
+			sb.append(this.header.fieldArray[i].getName());
+			sb.append("\n");
+		}
+
+		return sb.toString();
+	}
+
+	/**
+	 * Returns the number of records in the DBF.
+	 */
+	public int getRecordCount() {
+
+		return this.header.numberOfRecords;
+	}
+
+	/**
+	 * Returns the asked Field. In case of an invalid index, it returns a
+	 * ArrayIndexOutofboundsException.
+	 * 
+	 * @param index.
+	 *            Index of the field. Index of the first field is zero.
+	 */
+	public DBFField getField(int index) throws DBFException {
+
+		if (isClosed) {
+
+			throw new DBFException("Source is not open");
+		}
+
+		return this.header.fieldArray[index];
+	}
+
+	/**
+	 * Returns the number of field in the DBF.
+	 */
+	public int getFieldCount() throws DBFException {
+
+		if (isClosed) {
+
+			throw new DBFException("Source is not open");
+		}
+
+		if (this.header.fieldArray != null) {
+
+			return this.header.fieldArray.length;
+		}
+
+		return -1;
+	}
+
+	/**
+	 * Reads the returns the next row in the DBF stream.
+	 * 
+	 * @returns The next row as an Object array. Types of the elements these arrays
+	 *          follow the convention mentioned in the class description.
+	 */
+	public Object[] nextRecord() throws DBFException {
+
+		if (isClosed) {
+
+			throw new DBFException("Source is not open");
+		}
+
+		Object recordObjects[] = new Object[this.header.fieldArray.length];
+		try {
+
+			boolean isDeleted = false;
+			do {
+
+				if (isDeleted) {
+
+					dataInputStream.skip(this.header.recordLength - 1);
+				}
+
+				int t_byte = dataInputStream.readByte();
+				if (t_byte == END_OF_DATA) {
+
+					return null;
+				}
+
+				isDeleted = (t_byte == '*');
+			} while (isDeleted);
+
+			for (int i = 0; i < this.header.fieldArray.length; i++) {
+				switch (this.header.fieldArray[i].getDataType()) {
+				case 'C':
+
+					byte b_array[] = new byte[this.header.fieldArray[i].getFieldLength()];
+					dataInputStream.read(b_array);
+					recordObjects[i] = new String(b_array, characterSetName);
+					break;
+
+				case 'D':
+					byte t_byte_year[] = new byte[4];
+					dataInputStream.read(t_byte_year);
+
+					byte t_byte_month[] = new byte[2];
+					dataInputStream.read(t_byte_month);
+
+					byte t_byte_day[] = new byte[2];
+					dataInputStream.read(t_byte_day);
+					try {
+
+						GregorianCalendar calendar = new GregorianCalendar(Integer.parseInt(new String(t_byte_year)),
+								Integer.parseInt(new String(t_byte_month)) - 1,
+								Integer.parseInt(new String(t_byte_day)));
+
+						// recordObjects[i] = calendar.getTime();
+						recordObjects[i] = new String(t_byte_year) + "-" + new String(t_byte_month) + "-"
+								+ new String(t_byte_day);
+					} catch (NumberFormatException e) {
+						/* this field may be empty or may have improper value set */
+						recordObjects[i] = null;
+					}
+
+					break;
+
+				case 'F':
+
+					try {
+
+						byte t_float[] = new byte[this.header.fieldArray[i].getFieldLength()];
+						dataInputStream.read(t_float);
+						t_float = Utils.trimLeftSpaces(t_float);
+						if (t_float.length > 0 && !Utils.contains(t_float, (byte) '?')) {
+
+							recordObjects[i] = new Float(new String(t_float));
+						} else {
+
+							recordObjects[i] = null;
+						}
+					} catch (NumberFormatException e) {
+
+						throw new DBFException("Failed to parse Float: " + e.getMessage());
+					}
+
+					break;
+
+				case 'N':
+
+					try {
+
+						byte t_numeric[] = new byte[this.header.fieldArray[i].getFieldLength()];
+						dataInputStream.read(t_numeric);
+						t_numeric = Utils.trimLeftSpaces(t_numeric);
+						if (t_numeric.length > 0 && !Utils.contains(t_numeric, (byte) '?')) {
+							// modify by viwo
+							// if((new String( t_numeric)).trim().length()==0)
+							// {
+							// recordObjects[i] = null;
+							// }
+							// else
+							// {
+							recordObjects[i] = new Double(new String(t_numeric));
+							// }
+							// --------------
+						} else {
+
+							recordObjects[i] = null;
+						}
+					} catch (NumberFormatException e) {
+						e.printStackTrace();
+						throw new DBFException("Failed to parse Number: " + e.getMessage());
+					}
+
+					break;
+
+				case 'L':
+
+					byte t_logical = dataInputStream.readByte();
+					if (t_logical == 'Y' || t_logical == 't' || t_logical == 'T' || t_logical == 't') {
+
+						recordObjects[i] = Boolean.TRUE;
+					} else {
+
+						recordObjects[i] = Boolean.FALSE;
+					}
+					break;
+
+				// case 'M':
+				// recordObjects[i] = new String( "null");
+				// break;
+				case 'M':
+					// DBF跳过memo字段
+					byte b_memoProxy[] = new byte[this.header.fieldArray[i].getFieldLength()];
+					dataInputStream.read(b_memoProxy);
+					// 读取FPT字段
+					int iType = Utils.readHighEndianInt(memoInputStream);
+					if (iType == 1) {
+						int iLength = Utils.readHighEndianInt(memoInputStream);// 红色代码为自己添加的函数
+						byte b_memo[] = new byte[iLength];
+						memoInputStream.read(b_memo); // 读入的FPT文件内容
+						recordObjects[i] = new String(b_memo, characterSetName);
+						if (iLength + 8 > blockLength) {
+							memoInputStream.read(new byte[blockLength - (iLength + 8) % blockLength]);
+						} else {
+							memoInputStream.read(new byte[blockLength - 8 - iLength]);
+						}
+					}
+					break;
+
+				default:
+					recordObjects[i] = new String("null");
+				}
+			}
+		} catch (EOFException e) {
+
+			return null;
+		} catch (IOException e) {
+
+			throw new DBFException(e.getMessage());
+		}
+
+		return recordObjects;
+	}
+}

+ 349 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/dbf/DBFWriter.java

@@ -0,0 +1,349 @@
+package cn.hmsoft.scan.util.dbf;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * An object of this class can create a DBF file.
+ * 
+ * Create an object, <br>
+ * then define fields by creating DBFField objects and<br>
+ * add them to the DBFWriter object<br>
+ * add records using the addRecord() method and then<br>
+ * call write() method.
+ */
+public class DBFWriter extends DBFBase {
+
+	/* other class variables */
+	DBFHeader header;
+	Vector v_records = new Vector();
+	int recordCount = 0;
+	RandomAccessFile raf = null; /* Open and append records to an existing DBF */
+	boolean appendMode = false;
+
+	/**
+	 * Creates an empty Object.
+	 */
+	public DBFWriter() {
+
+		this.header = new DBFHeader();
+	}
+
+	/**
+	 * Creates a DBFWriter which can append to records to an existing DBF file.
+	 * 
+	 * @param dbfFile.
+	 *            The file passed in shouls be a valid DBF file.
+	 * @exception Throws
+	 *                DBFException if the passed in file does exist but not a valid
+	 *                DBF file, or if an IO error occurs.
+	 */
+	public DBFWriter(File dbfFile) throws DBFException {
+
+		try {
+
+			this.raf = new RandomAccessFile(dbfFile, "rw");
+
+			/*
+			 * before proceeding check whether the passed in File object is an
+			 * empty/non-existent file or not.
+			 */
+			if (!dbfFile.exists() || dbfFile.length() == 0) {
+
+				this.header = new DBFHeader();
+				return;
+			}
+
+			header = new DBFHeader();
+			this.header.read(raf);
+
+			/* position file pointer at the end of the raf */
+			this.raf.seek(this.raf.length() - 1 /* to ignore the END_OF_DATA byte at EoF */);
+		} catch (FileNotFoundException e) {
+
+			throw new DBFException("Specified file is not found. " + e.getMessage());
+		} catch (IOException e) {
+
+			throw new DBFException(e.getMessage() + " while reading header");
+		}
+
+		this.recordCount = this.header.numberOfRecords;
+	}
+
+	/**
+	 * Sets fields.
+	 */
+	public void setFields(DBFField[] fields) throws DBFException {
+
+		if (this.header.fieldArray != null) {
+
+			throw new DBFException("Fields has already been set");
+		}
+
+		if (fields == null || fields.length == 0) {
+
+			throw new DBFException("Should have at least one field");
+		}
+
+		for (int i = 0; i < fields.length; i++) {
+
+			if (fields[i] == null) {
+
+				throw new DBFException("Field " + (i + 1) + " is null");
+			}
+		}
+
+		this.header.fieldArray = fields;
+
+		try {
+
+			if (this.raf != null && this.raf.length() == 0) {
+
+				/*
+				 * this is a new/non-existent file. So write header before proceeding
+				 */
+				this.header.write(this.raf);
+			}
+		} catch (IOException e) {
+
+			throw new DBFException("Error accesing file");
+		}
+	}
+
+	/**
+	 * Add a record.
+	 */
+	public void addRecord(Object[] values) throws DBFException {
+
+		if (this.header.fieldArray == null) {
+
+			throw new DBFException("Fields should be set before adding records");
+		}
+
+		if (values == null) {
+
+			throw new DBFException("Null cannot be added as row");
+		}
+
+		if (values.length != this.header.fieldArray.length) {
+
+			throw new DBFException("Invalid record. Invalid number of fields in row");
+		}
+
+		for (int i = 0; i < this.header.fieldArray.length; i++) {
+
+			if (values[i] == null) {
+
+				continue;
+			}
+			// add by viwo to process BigDecimal problem
+			if (this.header.fieldArray[i].getDataType() == 78) {
+				if (values[i] instanceof java.math.BigDecimal) {
+					values[i] = new Double(((java.math.BigDecimal) values[i]).doubleValue());
+				} else {
+					values[i] = Double.valueOf(values[i].toString());
+				}
+			}
+			// ----------------
+			switch (this.header.fieldArray[i].getDataType()) {
+
+			case 'C':
+				try {
+					// add by viwo to process chinese problem
+					values[i] = new String(values[i].toString().getBytes("GBK"), "ISO-8859-1");
+					// --------------
+				} catch (Exception e) {
+					e.printStackTrace();
+				}
+				if (!(values[i] instanceof String)) {
+					throw new DBFException("Invalid value for field " + i);
+				}
+				break;
+
+			case 'L':
+				if (!(values[i] instanceof Boolean)) {
+					throw new DBFException("Invalid value for field " + i);
+				}
+				break;
+
+			case 'N':
+				if (!(values[i] instanceof Double)) {
+					throw new DBFException("Invalid value for field " + i);
+				}
+				break;
+
+			case 'D':
+				if (!(values[i] instanceof Date)) {
+					throw new DBFException("Invalid value for field " + i);
+				}
+				break;
+
+			case 'F':
+				if (!(values[i] instanceof Double)) {
+
+					throw new DBFException("Invalid value for field " + i);
+				}
+				break;
+			}
+		}
+
+		if (this.raf == null) {
+
+			v_records.addElement(values);
+		} else {
+
+			try {
+
+				writeRecord(this.raf, values);
+				this.recordCount++;
+			} catch (IOException e) {
+
+				throw new DBFException("Error occured while writing record. " + e.getMessage());
+			}
+		}
+	}
+
+	/**
+	 * Writes the set data to the OutputStream.
+	 */
+	public void write(OutputStream out) throws DBFException {
+
+		try {
+
+			if (this.raf == null) {
+
+				DataOutputStream outStream = new DataOutputStream(out);
+
+				this.header.numberOfRecords = v_records.size();
+				this.header.write(outStream);
+
+				/* Now write all the records */
+				int t_recCount = v_records.size();
+				for (int i = 0; i < t_recCount; i++) { /* iterate through records */
+
+					Object[] t_values = (Object[]) v_records.elementAt(i);
+
+					writeRecord(outStream, t_values);
+				}
+
+				outStream.write(END_OF_DATA);
+				outStream.flush();
+			} else {
+
+				/*
+				 * everything is written already. just update the header for record count and
+				 * the END_OF_DATA mark
+				 */
+				this.header.numberOfRecords = this.recordCount;
+				this.raf.seek(0);
+				this.header.write(this.raf);
+				this.raf.seek(raf.length());
+				this.raf.writeByte(END_OF_DATA);
+				this.raf.close();
+			}
+
+		} catch (IOException e) {
+
+			throw new DBFException(e.getMessage());
+		}
+	}
+
+	public void write() throws DBFException {
+
+		this.write(null);
+	}
+
+	private void writeRecord(DataOutput dataOutput, Object[] objectArray) throws IOException {
+
+		dataOutput.write((byte) ' ');
+		for (int j = 0; j < this.header.fieldArray.length; j++) { /* iterate throught fields */
+
+			switch (this.header.fieldArray[j].getDataType()) {
+
+			case 'C':
+				if (objectArray[j] != null) {
+
+					String str_value = objectArray[j].toString();
+					dataOutput.write(
+							Utils.textPadding(str_value, characterSetName, this.header.fieldArray[j].getFieldLength()));
+				} else {
+
+					dataOutput.write(
+							Utils.textPadding("", this.characterSetName, this.header.fieldArray[j].getFieldLength()));
+				}
+
+				break;
+
+			case 'D':
+				if (objectArray[j] != null) {
+
+					GregorianCalendar calendar = new GregorianCalendar();
+					calendar.setTime((Date) objectArray[j]);
+					StringBuffer t_sb = new StringBuffer();
+					dataOutput.write(String.valueOf(calendar.get(Calendar.YEAR)).getBytes());
+					dataOutput.write(Utils.textPadding(String.valueOf(calendar.get(Calendar.MONTH) + 1),
+							this.characterSetName, 2, Utils.ALIGN_RIGHT, (byte) '0'));
+					dataOutput.write(Utils.textPadding(String.valueOf(calendar.get(Calendar.DAY_OF_MONTH)),
+							this.characterSetName, 2, Utils.ALIGN_RIGHT, (byte) '0'));
+				} else {
+
+					dataOutput.write("        ".getBytes());
+				}
+
+				break;
+
+			case 'F':
+
+				if (objectArray[j] != null) {
+
+					dataOutput.write(Utils.doubleFormating((Double) objectArray[j], this.characterSetName,
+							this.header.fieldArray[j].getFieldLength(), this.header.fieldArray[j].getDecimalCount()));
+				} else {
+
+					dataOutput.write(Utils.textPadding("?", this.characterSetName,
+							this.header.fieldArray[j].getFieldLength(), Utils.ALIGN_RIGHT));
+				}
+
+				break;
+
+			case 'N':
+
+				if (objectArray[j] != null) {
+
+					dataOutput.write(Utils.doubleFormating((Double) objectArray[j], this.characterSetName,
+							this.header.fieldArray[j].getFieldLength(), this.header.fieldArray[j].getDecimalCount()));
+				} else {
+
+					dataOutput.write(Utils.textPadding("?", this.characterSetName,
+							this.header.fieldArray[j].getFieldLength(), Utils.ALIGN_RIGHT));
+				}
+
+				break;
+			case 'L':
+
+				if (objectArray[j] != null) {
+
+					if ((Boolean) objectArray[j] == Boolean.TRUE) {
+
+						dataOutput.write((byte) 'T');
+					} else {
+
+						dataOutput.write((byte) 'F');
+					}
+				} else {
+
+					dataOutput.write((byte) '?');
+				}
+
+				break;
+
+			case 'M':
+
+				break;
+
+			default:
+				throw new DBFException("Unknown field type " + this.header.fieldArray[j].getDataType());
+			}
+		} /* iterating through the fields */
+	}
+}

+ 321 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/dbf/Utils.java

@@ -0,0 +1,321 @@
+package cn.hmsoft.scan.util.dbf;
+
+import java.io.*;
+import java.util.*;
+
+import cn.hmsoft.helper.LogHelper;
+
+import java.text.*;
+
+/**
+ * Miscelaneous functions required by the JavaDBF package.
+ */
+public final class Utils {
+
+	public static final int ALIGN_LEFT = 10;
+	public static final int ALIGN_RIGHT = 12;
+	
+	/** 生僻字编码 */
+	public static Map<String, String> RAREWORDSCODE = new HashMap<String, String>();
+	static {
+		RAREWORDSCODE.put("\u2E81", "\uE815");
+		RAREWORDSCODE.put("\u20087", "\uE816");
+		RAREWORDSCODE.put("\u20089", "\uE817");
+		RAREWORDSCODE.put("\u200CC", "\uE818");
+		RAREWORDSCODE.put("\u2E84", "\uE819");
+		RAREWORDSCODE.put("\u3473", "\uE81A");
+		RAREWORDSCODE.put("\u3447", "\uE81B");
+		RAREWORDSCODE.put("\u2E88", "\uE81C");
+		RAREWORDSCODE.put("\u2E8B", "\uE81D");
+		RAREWORDSCODE.put("\u9FB4", "\uE81E");
+		RAREWORDSCODE.put("\u359E", "\uE81F");
+		RAREWORDSCODE.put("\u361A", "\uE820");
+		RAREWORDSCODE.put("\u360E", "\uE821");
+		RAREWORDSCODE.put("\u2E8C", "\uE822");
+		RAREWORDSCODE.put("\u2E97", "\uE823");
+		RAREWORDSCODE.put("\u396E", "\uE824");
+		RAREWORDSCODE.put("\u2918", "\uE825");
+		RAREWORDSCODE.put("\u9FB5", "\uE826");
+		RAREWORDSCODE.put("\u39CF", "\uE827");
+		RAREWORDSCODE.put("\u39DF", "\uE828");
+		RAREWORDSCODE.put("\u3A73", "\uE829");
+		RAREWORDSCODE.put("\u39D0", "\uE82A");
+		RAREWORDSCODE.put("\u9FB6", "\uE82B");
+		RAREWORDSCODE.put("\u9FB7", "\uE82C");
+		RAREWORDSCODE.put("\u3B4E", "\uE82D");
+		RAREWORDSCODE.put("\u3C6E", "\uE82E");
+		RAREWORDSCODE.put("\u3CE0", "\uE82F");
+		RAREWORDSCODE.put("\u2EA7", "\uE830");
+		RAREWORDSCODE.put("\u215D7", "\uE831");
+		RAREWORDSCODE.put("\u9FB8", "\uE832");
+		RAREWORDSCODE.put("\u2EAA", "\uE833");
+		RAREWORDSCODE.put("\u4056", "\uE834");
+		RAREWORDSCODE.put("\u415F", "\uE835");
+		RAREWORDSCODE.put("\u2EAE", "\uE836");
+		RAREWORDSCODE.put("\u4337", "\uE837");
+		RAREWORDSCODE.put("\u2EF3", "\uE838");
+		RAREWORDSCODE.put("\u2EB6", "\uE839");
+		RAREWORDSCODE.put("\u2EB7", "\uE83A");
+		RAREWORDSCODE.put("\u2298F", "\uE83B");
+		RAREWORDSCODE.put("\u43B1", "\uE83C");
+		RAREWORDSCODE.put("\u43AC", "\uE83D");
+		RAREWORDSCODE.put("\u2EBB", "\uE83E");
+		RAREWORDSCODE.put("\u43DD", "\uE83F");
+		RAREWORDSCODE.put("\u44D6", "\uE840");
+		RAREWORDSCODE.put("\u4661", "\uE841");
+		RAREWORDSCODE.put("\u464C", "\uE842");
+		RAREWORDSCODE.put("\u9FB9", "\uE843");
+		RAREWORDSCODE.put("\u4723", "\uE844");
+		RAREWORDSCODE.put("\u4729", "\uE845");
+		RAREWORDSCODE.put("\u477C", "\uE846");
+		RAREWORDSCODE.put("\u478D", "\uE847");
+		RAREWORDSCODE.put("\u2ECA", "\uE848");
+		RAREWORDSCODE.put("\u4947", "\uE849");
+		RAREWORDSCODE.put("\u497A", "\uE84A");
+		RAREWORDSCODE.put("\u497D", "\uE84B");
+		RAREWORDSCODE.put("\u4982", "\uE84C");
+		RAREWORDSCODE.put("\u4983", "\uE84D");
+		RAREWORDSCODE.put("\u4985", "\uE84E");
+		RAREWORDSCODE.put("\u4986", "\uE84F");
+		RAREWORDSCODE.put("\u499F", "\uE850");
+		RAREWORDSCODE.put("\u499B", "\uE851");
+		RAREWORDSCODE.put("\u49B7", "\uE852");
+		RAREWORDSCODE.put("\u49B6", "\uE853");
+		RAREWORDSCODE.put("\u9FBA", "\uE854");
+		RAREWORDSCODE.put("\u241FE", "\uE855");
+		RAREWORDSCODE.put("\u4CA3", "\uE856");
+		RAREWORDSCODE.put("\u4C9F", "\uE857");
+		RAREWORDSCODE.put("\u4CA0", "\uE858");
+		RAREWORDSCODE.put("\u4CA1", "\uE859");
+		RAREWORDSCODE.put("\u4C77", "\uE85A");
+		RAREWORDSCODE.put("\u47A2", "\uE85B");
+		RAREWORDSCODE.put("\u4D13", "\uE85C");
+		RAREWORDSCODE.put("\u4D14", "\uE85D");
+		RAREWORDSCODE.put("\u4D15", "\uE85E");
+		RAREWORDSCODE.put("\u4D16", "\uE85F");
+		RAREWORDSCODE.put("\u4D17", "\uE860");
+		RAREWORDSCODE.put("\u4D18", "\uE861");
+		RAREWORDSCODE.put("\u4D19", "\uE862");
+		RAREWORDSCODE.put("\u4DAE", "\uE863");
+		RAREWORDSCODE.put("\u9FBB", "\uE864");
+	}
+	
+
+	private Utils() {
+	}
+	
+	public static String getRareWordsCode(String std_name) {
+		if (null == std_name) {
+			return std_name;
+		}
+
+		StringBuffer sb = new StringBuffer();
+
+		String[] std_names = std_name.split("");
+
+		for (String name : std_names) {
+			if (RAREWORDSCODE.containsKey(name)) {
+				sb.append(RAREWORDSCODE.get(name));
+			} else {
+				sb.append(name);
+			}
+		}
+
+		return sb.toString();
+	}
+	
+
+	public static int readLittleEndianInt(DataInput in) throws IOException {
+
+		int bigEndian = 0;
+		for (int shiftBy = 0; shiftBy < 32; shiftBy += 8) {
+
+			bigEndian |= (in.readUnsignedByte() & 0xff) << shiftBy;
+		}
+
+		return bigEndian;
+	}
+
+	public static short readLittleEndianShort(DataInput in) throws IOException {
+
+		int low = in.readUnsignedByte() & 0xff;
+		int high = in.readUnsignedByte();
+
+		return (short) (high << 8 | low);
+	}
+
+	// add by viwo to read memo
+	public static int readHighEndianInt(DataInput in) throws IOException {
+
+		int bigEndian = 0;
+		for (int shiftBy = 24; shiftBy > -1; shiftBy -= 8) {
+
+			bigEndian |= (in.readUnsignedByte() & 0xff) << shiftBy;
+		}
+
+		return bigEndian;
+	}
+	// -------------------
+
+	public static byte[] trimLeftSpaces(byte[] arr) {
+
+		StringBuffer t_sb = new StringBuffer(arr.length);
+
+		for (int i = 0; i < arr.length; i++) {
+
+			if (arr[i] != ' ') {
+
+				t_sb.append((char) arr[i]);
+			}
+		}
+
+		return t_sb.toString().getBytes();
+	}
+
+	public static short littleEndian(short value) {
+
+		short num1 = value;
+		short mask = (short) 0xff;
+
+		short num2 = (short) (num1 & mask);
+		num2 <<= 8;
+		mask <<= 8;
+
+		num2 |= (num1 & mask) >> 8;
+
+		return num2;
+	}
+
+	public static int littleEndian(int value) {
+
+		int num1 = value;
+		int mask = 0xff;
+		int num2 = 0x00;
+
+		num2 |= num1 & mask;
+
+		for (int i = 1; i < 4; i++) {
+
+			num2 <<= 8;
+			mask <<= 8;
+			num2 |= (num1 & mask) >> (8 * i);
+		}
+
+		return num2;
+	}
+
+	public static byte[] textPadding(String text, String characterSetName, int length)
+			throws java.io.UnsupportedEncodingException {
+
+		return textPadding(text, characterSetName, length, Utils.ALIGN_LEFT);
+	}
+
+	public static byte[] textPadding(String text, String characterSetName, int length, int alignment)
+			throws java.io.UnsupportedEncodingException {
+
+		return textPadding(text, characterSetName, length, alignment, (byte) ' ');
+	}
+
+	public static byte[] textPadding(String text, String characterSetName, int length, int alignment, byte paddingByte)
+			throws java.io.UnsupportedEncodingException {
+
+		if (text.length() >= length) {
+
+			return text.substring(0, length).getBytes(characterSetName);
+		}
+
+		byte byte_array[] = new byte[length];
+		Arrays.fill(byte_array, paddingByte);
+
+		switch (alignment) {
+
+		case ALIGN_LEFT:
+			System.arraycopy(text.getBytes(characterSetName), 0, byte_array, 0, text.length());
+			break;
+
+		case ALIGN_RIGHT:
+			int t_offset = length - text.length();
+			System.arraycopy(text.getBytes(characterSetName), 0, byte_array, t_offset, text.length());
+			break;
+		}
+
+		return byte_array;
+	}
+
+	public static byte[] doubleFormating(Double doubleNum, String characterSetName, int fieldLength,
+			int sizeDecimalPart) throws java.io.UnsupportedEncodingException {
+
+		int sizeWholePart = fieldLength - (sizeDecimalPart > 0 ? (sizeDecimalPart + 1) : 0);
+
+		StringBuffer format = new StringBuffer(fieldLength);
+
+		for (int i = 0; i < sizeWholePart; i++) {
+
+			format.append("#");
+		}
+
+		if (sizeDecimalPart > 0) {
+
+			format.append(".");
+
+			for (int i = 0; i < sizeDecimalPart; i++) {
+
+				format.append("0");
+			}
+		}
+
+		DecimalFormat df = new DecimalFormat(format.toString());
+
+		return textPadding(df.format(doubleNum.doubleValue()).toString(), characterSetName, fieldLength, ALIGN_RIGHT);
+	}
+
+	public static boolean contains(byte[] arr, byte value) {
+
+		boolean found = false;
+		for (int i = 0; i < arr.length; i++) {
+
+			if (arr[i] == value) {
+
+				found = true;
+				break;
+			}
+		}
+
+		return found;
+	}
+	
+	public static Boolean isLessUseWord(String str) {
+		if(null == str){
+			return false;
+		}
+		
+		if(str.indexOf("·") != -1){
+			return false;
+		}
+		
+        try {
+			return !str.equals(new String(str.getBytes("gb18030"),"gb2312"));
+		} catch (UnsupportedEncodingException e) {
+			LogHelper.error(e);
+		}
+        
+        return false;
+    }
+	
+	/*
+	 * 字符串---->16进制数字
+	 */
+	public static String toUnicode(String s) {
+		if(null == s){
+			return s;
+		}
+		
+		String as[] = new String[s.length()];
+		StringBuffer s1 = new StringBuffer();
+		for (int i = 0; i < s.length(); i++) {
+			as[i] = Integer.toHexString(s.charAt(i) & 0xffff);
+			s1.append("\\u").append(as[i].toUpperCase());
+		}
+		return s1.toString();
+	}
+}

+ 23 - 0
ses-scan/src/main/java/cn/hmsoft/scan/util/job/StdMatchJob.java

@@ -0,0 +1,23 @@
+package cn.hmsoft.scan.util.job;
+
+import cn.hmsoft.frame.timer.FrameTimerConfig;
+import cn.hmsoft.helper.LogHelper;
+import cn.hmsoft.scan.common.constants.SysConst;
+import cn.hmsoft.scan.service.iface.by.IStdMatchService;
+
+/**  
+ * @Description: 毕业生考生登记表匹配job
+ * @author hgh
+ * @date 2023-08-02 10:48:29 
+ */
+public class StdMatchJob extends FrameTimerConfig{
+
+	@Override
+	protected boolean execute() throws Exception {
+		LogHelper.info("--------------开始匹配考生登记表------------------");
+		SysConst.getCurrentWebApplicationContext().getBean(IStdMatchService.class).match();
+		LogHelper.info("--------------结束匹配考生登记表------------------");
+		return false;
+	}
+
+}

+ 287 - 0
ses-scan/src/main/java/cn/hmsoft/web/config/WebConfig.java

@@ -0,0 +1,287 @@
+package cn.hmsoft.web.config;
+
+import java.io.File;
+import java.io.FileReader;
+import java.net.ProxySelector;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.logging.Level;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.FilterRegistration;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
+
+import org.springframework.util.StringUtils;
+import org.springframework.web.WebApplicationInitializer;
+import org.springframework.web.context.ContextLoaderListener;
+import org.springframework.web.context.request.RequestContextListener;
+import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
+import org.springframework.web.filter.CharacterEncodingFilter;
+import org.springframework.web.servlet.DispatcherServlet;
+
+import cn.hmsoft.frame.constants.FrameParamConstants;
+import cn.hmsoft.frame.data.model.FrameParam;
+import cn.hmsoft.frame.module.FrameModuleUtil;
+import cn.hmsoft.helper.DateHelper;
+import cn.hmsoft.helper.EntityHelper;
+import cn.hmsoft.helper.LogHelper;
+import cn.hmsoft.helper.SecureHelper;
+import cn.hmsoft.helper.StringHelper;
+import cn.hmsoft.jdbc.core.DatabaseDao;
+import cn.hmsoft.jdbc.entity.DatabaseType;
+import cn.hmsoft.web.file.FileInitThread;
+import cn.hmsoft.web.filter.LoginFilter;
+import cn.hmsoft.web.thread.StdPicOcrThread;
+
+/************************
+ * MVC配置文件,tomcat自动加载该文件
+ * 
+ * @author zxq
+ * @create 2018-04-07 21:18:44
+ * @version 5.0.0
+ * @email: revisit@126.com
+ * @Company: www.hmsoft.cn
+ */
+public class WebConfig implements WebApplicationInitializer {
+
+	@Override
+	public void onStartup(ServletContext appContext) throws ServletException {
+		// 解决新安全机制下,Linux连接oracle速度慢的额问题
+		ProxySelector.setDefault(null);
+		System.setProperty("java.security.egd", "file:///dev/urandom");
+		// 关闭slf4j的输出信息
+		//System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "OFF"); 
+		//System.setProperty("org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY", "OFF"); 	
+		SpringConfig.WebServletContext = appContext;
+		com.mchange.v2.log.ModifyLogLevel.modifyInfoLevel(Level.OFF);
+		//org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY = "Off";
+
+		// 首选获取配置文件应用配置文件
+		File configPath = null;
+		try {
+			configPath = new File(java.net.URLDecoder.decode(this.getClass().getResource("/").getFile(), "utf-8"));
+		} catch (Exception e) {
+		}
+
+		// 应用程序的基本配置文件必须在src下,配置文件的名字为app.properties
+		Map<String, String> appConfigMap = new HashMap<String, String>();
+
+		// com.mchange.v2.log.MLog.
+		try {
+			appConfigMap = loadProperites(configPath + File.separator + "app.properties");
+		} catch (Exception e) {
+			// LogHelper.warn("file [app.properties] not found");
+		}
+		if (appConfigMap.size() == 0) {
+			// LogHelper.warn("找到【app.properties】文件,但未设置任何参数信息");
+		} else {
+			System.out.println("found file [app.properties],set frame params from file");
+			EntityHelper.setStaticField(FrameParamConstants.class, appConfigMap);
+			if (StringHelper.isNotEmpty(FrameParamConstants.AppParamUtil)) {
+				try {
+					EntityHelper.setStaticField(Class.forName(FrameParamConstants.AppParamUtil), appConfigMap);
+				} catch (Exception e) {
+				}
+			}
+		}
+
+		LogHelper.info(
+				"==================================System Starting (v5.0.0)=============================================");
+		LogHelper.info("log4j2 for frame log,log_type:" + LogHelper.LOGTYPE.toString());
+		// 判断系统是否启用了数据库
+		Map<String, String> jdbcConfigMap = new HashMap<String, String>();
+		if (StringHelper.isNotEmpty(FrameParamConstants.JdbcConfigFileName)) {
+			try {
+				jdbcConfigMap = loadProperites(configPath + File.separator + FrameParamConstants.JdbcConfigFileName);
+			} catch (Exception e) {
+			}
+		}
+		// System.out.println(AppHelper.AppDebug);
+		if (jdbcConfigMap.size() == 0) {
+			LogHelper.warn("database config not found,frame run with none database");
+		} else {
+			// 设置数据库参数信息
+			LogHelper.info(
+					"found database file[" + FrameParamConstants.JdbcConfigFileName + "],initialize database connection");
+
+			try {
+				EntityHelper.setStaticField(C3P0Config.class, jdbcConfigMap);
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
+			
+			// 判断是否度数据库配置进行了加密
+			if (C3P0Config.JdbcSecureKey!=null) {
+				// try {
+				FrameParamConstants.DatabaseSecureKey = SecureHelper.getSecureKey(C3P0Config.JdbcSecureKey);
+				C3P0Config.JdbcUrl = SecureHelper.des3Decrypt(C3P0Config.JdbcUrl, FrameParamConstants.DatabaseSecureKey);
+				C3P0Config.JdbcUser = SecureHelper.des3Decrypt(C3P0Config.JdbcUser, FrameParamConstants.DatabaseSecureKey);
+				C3P0Config.JdbcPassword = SecureHelper.des3Decrypt(C3P0Config.JdbcPassword,
+						FrameParamConstants.DatabaseSecureKey);
+				// } catch (Exception e) {
+				if (StringHelper.isEmpty(C3P0Config.JdbcUrl)) {
+					LogHelper.error("database config with encrypt,but can't decrypt");
+					throw new ServletException("database config with encrypt,but can't decrypt");
+				}
+				// }
+			}
+			if (C3P0Config.JdbcDriverClass.equals("oracle.jdbc.driver.OracleDriver"))
+				C3P0Config.DataType = DatabaseType.Oracle;
+			else if (C3P0Config.JdbcDriverClass.startsWith("com.mysql."))
+				C3P0Config.DataType = DatabaseType.MySql;
+			else if (C3P0Config.JdbcDriverClass.equals("org.sqlite.JDBC"))
+				C3P0Config.DataType = DatabaseType.Sqlite;
+			else if (C3P0Config.JdbcDriverClass.equals("com.microsoft.sqlserver.jdbc.SQLServerDriver"))
+				C3P0Config.DataType = DatabaseType.SQLServer;
+			else if (C3P0Config.JdbcDriverClass.equals("com.hxtt.sql.access.AccessDriver"))
+				C3P0Config.DataType = DatabaseType.Access;
+		}
+
+		// 加载spring
+		AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
+		rootContext.register(SpringConfig.class);
+		//如果socket配置不为空,则加载websocket配置信息
+		if (!StringUtils.isEmpty(FrameParamConstants.WebSocketPackage))
+			rootContext.register(WebSocketConfig.class);
+
+		appContext.addListener(new ContextLoaderListener(rootContext));
+		appContext.addListener(new RequestContextListener());
+		rootContext.setServletContext(appContext);
+		LogHelper.info("initialize spring context version 5.0.3, scan cn.hmsoft.*..* to autowrid");
+
+		// 立即加载spring
+		rootContext.refresh();
+		// 设置变量
+		SpringConfig.SpringContext = rootContext;
+		// 设置通用的数据库操作基类
+		SpringConfig.GobalDao = rootContext.getBean(DatabaseDao.class);
+
+		// 获取数据库信息
+		// Map<String, Object> paramMap = new HashMap<String, Object>();
+		if (C3P0Config.DataType != DatabaseType.None) {
+			try {
+				String databaseTime = DateHelper.format(SpringConfig.GobalDao.getCurrentTime());
+				LogHelper.info("database type [" + C3P0Config.DataType.toString() + "],database current time ["
+						+ databaseTime + "]");
+
+				try {
+					for (FrameParam t : SpringConfig.GobalDao.all(FrameParam.class)) {
+						if (StringHelper.isNotEmpty(t.getParam_value()))
+							appConfigMap.put(t.getParam_name().trim(), t.getParam_value().trim());
+					}
+				} catch (Exception e) {
+				}
+				FrameParamConstants.GobalParamMap = appConfigMap;
+				 //自动加载模块
+				if(StringHelper.isNotEmpty(FrameParamConstants.AppName)) {
+					try {
+						FrameModuleUtil.getInstance().loadModule();
+					} catch(Exception e) {
+					}
+				}
+
+			} catch (Exception e) {
+				LogHelper.error("database type [" + C3P0Config.DataType.toString() + "],connect database error,url["
+						+ C3P0Config.JdbcUrl + "],user name [" + C3P0Config.JdbcUser + "],user pass ["
+						+ C3P0Config.JdbcPassword + "]");
+			}
+
+		}
+		// 设置FrameParam中定义的参数信息
+		EntityHelper.setStaticField(FrameParamConstants.class, appConfigMap);
+		FrameParamConstants.GobalParamMap = appConfigMap;
+
+		// 加载html请求的拦截器
+		ServletRegistration.Dynamic dispatcher = appContext.addServlet(FrameParamConstants.GobalServletName,
+				new DispatcherServlet(rootContext));
+		dispatcher.setLoadOnStartup(1);
+		// 定义SpringMVC拦截的请求
+		String[] array = FrameParamConstants.SpringUrlSuffix.split(",");
+		LogHelper.info("initialize spring mvc");
+		if (array.length == 1 && StringHelper.isEmpty(array[0])) {
+			dispatcher.addMapping("/*");
+			LogHelper.info("spring dispatcher all request");
+		} else {
+			for (String s : array) {
+				dispatcher.addMapping("*." + s);
+				LogHelper.info("spring dispatcher request with " + s + "");
+			}
+		}
+
+		// 统一中文处理,所有请求使用utf8编码
+		CharacterEncodingFilter f = new CharacterEncodingFilter();
+		f.setEncoding("utf-8");
+		f.setForceEncoding(true);
+		FilterRegistration.Dynamic filter = appContext.addFilter("encodingFilter", f);
+		filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
+		//login filter
+		LoginFilter lf = LoginFilter.getInstance();
+		FilterRegistration.Dynamic loginFilter = appContext.addFilter("loginFilter", lf);
+		loginFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.ASYNC), false, "/*");
+		
+		// 加载监听
+		for (String s : FrameParamConstants.AppListener.split(",")) {
+			if (StringHelper.isEmpty(s))
+				continue;
+			try {
+				appContext.addListener(s);
+				LogHelper.info("fonud listener from config file , load [" + s + "] complete");
+			} catch (Exception e) {
+				// LogHelper.error(e);
+				LogHelper.info("fonud listener from config file ,but [" + s + "] not found,or is not a listener class");
+			}
+		}
+		// 设置其他系统参数
+		if (StringHelper.isNotEmpty(FrameParamConstants.AppParamUtil)) {
+			try {
+				EntityHelper.setStaticField(Class.forName(FrameParamConstants.AppParamUtil), appConfigMap);
+			} catch (Exception e) {
+			}
+		}
+		/********
+		 * 启动文件监控线程
+		 */
+		new Thread(new FileInitThread()).start();
+//		new Thread(new StdPicOcrThread()).start();
+		
+		LogHelper.info(
+				"=======================================System Started==============================================");
+
+	}
+
+	public static Map<String, String> loadProperites(String fileName) throws ServletException {
+		Map<String, String> map = new HashMap<String, String>();
+		File file = new File(fileName);
+		if (!file.exists())
+			throw new ServletException("file [" + fileName + "] not found");
+		if (file.isFile()) {
+			loadProperites(file, map);
+		} else {
+			// 遍历获取
+			for (File f : file.listFiles()) {
+				// 只获取properties结尾的文件
+				if (f.getName().toLowerCase().endsWith(".properties"))
+					loadProperites(file, map);
+			}
+		}
+		return map;
+	}
+
+	private static void loadProperites(File file, Map<String, String> map) throws ServletException {
+		Properties prop = new Properties();
+		if (!file.exists())
+			throw new ServletException("file [" + file.getName() + "] not found");
+		try {
+			prop.load(new FileReader(file));
+			for (Object key : prop.keySet()) {
+				map.put(key.toString().trim(), prop.get(key).toString().trim());
+			}
+		} catch (Exception e) {
+			throw new ServletException("initialize [" + file.getName() + "] config file error");
+		}
+	}
+}

+ 40 - 0
ses-scan/src/main/java/cn/hmsoft/web/file/FileConsumer.java

@@ -0,0 +1,40 @@
+package cn.hmsoft.web.file;
+
+import java.util.concurrent.BlockingQueue;
+
+import cn.hmsoft.scan.common.constants.SysConst;
+import cn.hmsoft.scan.service.iface.by.IByScanStdService;
+import cn.hmsoft.scan.util.baidu.doc.Handwriting;
+
+public class FileConsumer extends Thread {
+
+	private BlockingQueue<String> blockingQueue;
+	String name;
+	int maxSize;
+
+	public FileConsumer(String name, BlockingQueue<String> queue, int maxSize) {
+		super(name);
+		this.name = name;
+		this.blockingQueue = queue;
+		this.maxSize = maxSize;
+	}
+
+	public void run() {
+		while (true) {
+			try {
+				String path = blockingQueue.take();
+				// 调用baidu-OCR文档识别
+				String firstNumContent = Handwriting.handwriting(path, 1);
+				// TODO 需要修改,根据状态码判断
+				// service业务处理,解析ocr识别结果,和毕业生登记表关联
+				IByScanStdService scanService = SysConst.getCurrentWebApplicationContext()
+						.getBean(IByScanStdService.class);
+				scanService.insertAnalysisResult(firstNumContent, null, path);
+				Thread.sleep(1000);
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
+		}
+	}
+
+}

+ 55 - 0
ses-scan/src/main/java/cn/hmsoft/web/file/FileInitThread.java

@@ -0,0 +1,55 @@
+package cn.hmsoft.web.file;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import cn.hmsoft.helper.FileHelper;
+import cn.hmsoft.scan.common.constants.SysConst;
+import cn.hmsoft.scan.util.ScanFileHelper;
+
+
+/**  
+ * @Description: 启动监控文件初始化
+ * @author hgh
+ * @date 2023-07-25 01:46:32 
+ */
+public class FileInitThread extends Thread{
+	
+
+	@Override
+	public void run() {
+		try {
+			//解决第一次启动获取不到ApplicationContext的问题
+			Thread.sleep(1000);
+			String rootDir = SysConst.getAppConfig().getFileScanDir();
+			List<File> fileList = ScanFileHelper.getAllFiles(new File(rootDir));
+			fileList = filterFile(fileList);
+			for (File file : fileList) {
+				FileListener.blockQueue.add(file.getAbsolutePath());
+			}
+			FileMonitorStart monitor = new FileMonitorStart();
+			int num = SysConst.getAppConfig().getConsumerTheadNum();
+			monitor.fileAnalysis(rootDir, num != 0 ? num : 2);
+		} catch (Exception e1) {
+			e1.printStackTrace();
+		}
+	}
+
+	/**
+	 * @Description: 过滤非pdf、图片的文件
+	 * @param fileList
+	 * @date 2023-07-25 05:48:57 
+	 */  
+	private List<File> filterFile(List<File> fileList) {
+		List<File> resultList = new ArrayList<>();
+		for(File file : fileList) {
+			String suffix = FileHelper.getSuffix(file);
+			suffix = suffix.toLowerCase();
+			if(suffix.equals("pdf") || suffix.equals("jpg") || suffix.equals("png")) {
+				resultList.add(file);
+			}
+		}
+		return resultList;
+	}
+}

+ 45 - 0
ses-scan/src/main/java/cn/hmsoft/web/file/FileListener.java

@@ -0,0 +1,45 @@
+package cn.hmsoft.web.file;
+
+import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
+import org.apache.commons.io.monitor.FileAlterationObserver;
+
+import java.io.File;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**  
+ * @Description: 监控指定目录下文件的变化
+ * @author hgh
+ * @date 2023-07-25 11:29:15 
+ */
+public class FileListener extends FileAlterationListenerAdaptor {
+	
+	static BlockingQueue<String> blockQueue = new LinkedBlockingQueue<String>(100000);
+	
+
+    /**
+     * @param observer
+     */
+    @Override
+    public void onStart(FileAlterationObserver observer) {
+        super.onStart(observer);
+    }
+
+    /**
+     * 监控创建的文件,加入到队列中
+     * @param file 文件
+     */
+    @Override
+    public void onFileCreate(File file) {
+    	blockQueue.add(file.getAbsolutePath());
+    }
+
+    /**
+     * @param observer The file system observer 
+     */
+    @Override
+    public void onStop(FileAlterationObserver observer) {
+        super.onStop(observer);
+    }
+
+}

+ 48 - 0
ses-scan/src/main/java/cn/hmsoft/web/file/FileMonitorStart.java

@@ -0,0 +1,48 @@
+package cn.hmsoft.web.file;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.io.filefilter.FileFilterUtils;
+import org.apache.commons.io.filefilter.IOFileFilter;
+import org.apache.commons.io.monitor.FileAlterationMonitor;
+import org.apache.commons.io.monitor.FileAlterationObserver;
+
+import cn.hmsoft.scan.util.baidu.auth.BaiduAuthService;
+
+/**  
+ * @Description: 文件监控和解析入口
+ * @author hgh
+ * @date 2023-07-25 11:42:59 
+ */
+public class FileMonitorStart {
+
+	/**
+	 * @Description: 监控文件目录,并开始消费文件
+	 * @param rootDir  监控的目录
+	 * @param threadNum 消费者线程的数量
+	 * @throws Exception 
+	 * @date 2023-07-25 11:47:34 
+	 */  
+	public void fileAnalysis(String rootDir, int threadNum) throws Exception {
+		long intervalTime = TimeUnit.SECONDS.toMillis(5);
+		// 后缀过滤器
+		IOFileFilter filefilter = FileFilterUtils.or(FileFilterUtils.suffixFileFilter(".pdf"),
+				FileFilterUtils.suffixFileFilter(".jpg"), FileFilterUtils.suffixFileFilter(".png"));
+		  //子目录的后缀
+	    IOFileFilter subFilefilter=FileFilterUtils.or(FileFilterUtils.directoryFileFilter(),filefilter);
+		//根目录和子目录变化
+	    IOFileFilter filter = FileFilterUtils.or(filefilter,subFilefilter);
+	    FileAlterationObserver observer = new FileAlterationObserver(rootDir,filter);
+		//设置文件变化监听器
+	    observer.addListener(new FileListener());
+	    FileAlterationMonitor monitor = new FileAlterationMonitor(intervalTime);
+	    monitor.addObserver(observer);//文件观察
+	    monitor.start();
+	    BaiduAuthService.getToken();
+	    Thread.sleep(10000);
+		for (int i = 0; i < threadNum; i++) {
+			Thread consumer = new FileConsumer("c" + i, FileListener.blockQueue, 100000);
+			consumer.start();
+		}
+	}
+}

+ 116 - 0
ses-scan/src/main/java/cn/hmsoft/web/filter/LoginFilter.java

@@ -0,0 +1,116 @@
+package cn.hmsoft.web.filter;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import cn.hmsoft.frame.data.model.FrameOptr;
+import cn.hmsoft.frame.exception.auth.LoginAuthException;
+import cn.hmsoft.frame.exception.auth.OptrAuthException;
+import cn.hmsoft.frame.util.RequestContextUtil;
+
+
+/**
+ * @author zhanqiang
+ * @Description: 登录过滤
+ * @Copyright: Copyright (c) 2018
+ * @Company: www.hmsoft.com
+ * @date 2018-7-17
+ */
+public class LoginFilter implements Filter {
+	//登录或者app第三方接口调用允许
+	private static List<String> allowedUrls;
+	
+	private static LoginFilter loginFilterInsstance;
+	
+	private LoginFilter() {
+		
+	}
+	
+	public static LoginFilter getInstance() {
+		if (loginFilterInsstance == null) {
+			loginFilterInsstance = new LoginFilter();
+			allowedUrls = new ArrayList<String>();
+		}
+		
+		allowedUrls.add("/websocket");
+		allowedUrls.add("/login.htm");
+		allowedUrls.add("/init.htm");
+		allowedUrls.add("/logout.htm");
+		allowedUrls.add("/debug/index.htm");
+		allowedUrls.add("/debug/code/generate.htm");
+		allowedUrls.add("/frame/param/findschoolcode.htm");
+		allowedUrls.add("/exam/");
+		allowedUrls.add("/pad/");
+		allowedUrls.add("/by/std/make/dir.htm");
+		return loginFilterInsstance;
+	}
+
+	public void init(FilterConfig filterConfig) throws ServletException {
+
+    }
+
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        HttpServletRequest httpRequest = (HttpServletRequest) request;
+        HttpServletResponse httpResponse = (HttpServletResponse) response;
+        // 获得用户请求的URI
+        String path = httpRequest.getRequestURI();
+        if(path.endsWith(".htm")||path.endsWith(".do")) {
+            // 1.无需登陆的URL,直接放行.
+            int urlLen = allowedUrls.size();
+            for (int i = 0; i < urlLen; i++) {
+            	final String filterUrl = allowedUrls.get(i);
+            	final Pattern pattern = Pattern.compile(filterUrl);
+            	final Matcher m = pattern.matcher(path); 
+            	if (m.find()) {
+            		//无需过滤的页面请求.
+                    chain.doFilter(httpRequest, httpResponse);
+                    return;
+            	}
+            }
+        	
+			final FrameOptr loginUser = RequestContextUtil.currentLoginUser();
+			if (loginUser == null) {
+				throw new LoginAuthException();
+			} else {
+				// 管理员默认id为0
+				if (loginUser.getOptr_id().intValue() == 0) {
+					chain.doFilter(httpRequest, httpResponse);
+					return;
+				} else {
+					// 非管理员
+					if (loginUser.getResMap().containsKey(path)) {
+						chain.doFilter(httpRequest, httpResponse);
+						return;
+					} else {
+						throw new OptrAuthException();
+					}
+				}
+			}
+        }else {
+        	chain.doFilter(httpRequest, httpResponse);
+	        return;
+        }
+        
+
+
+        
+      
+    }
+    
+    public void destroy() {
+       
+    }
+
+}

+ 31 - 0
ses-scan/src/main/java/cn/hmsoft/web/thread/StdPicOcrThread.java

@@ -0,0 +1,31 @@
+package cn.hmsoft.web.thread;
+
+import java.util.List;
+
+import cn.hmsoft.helper.LogHelper;
+import cn.hmsoft.scan.common.constants.SysConst;
+import cn.hmsoft.scan.data.dao.by.ByScanStdDao;
+import cn.hmsoft.scan.data.model.by.ByScanStd;
+import cn.hmsoft.scan.service.iface.by.IStdPicOcrService;
+
+public class StdPicOcrThread implements Runnable{
+
+	@Override
+	public void run() {
+		try {
+			Thread.sleep(10000);
+			IStdPicOcrService ocrService = SysConst.getCurrentWebApplicationContext().getBean(IStdPicOcrService.class);
+			ByScanStdDao scanStdDao = SysConst.getCurrentWebApplicationContext().getBean(ByScanStdDao.class);
+			List<ByScanStd> stdList = scanStdDao.listScanStd(3);
+			int index = 0;
+			LogHelper.info("共有【" + stdList.size() +"】数据需要ocr识别!");
+			for (ByScanStd std : stdList) {
+				ocrService.picOcr(std);
+				LogHelper.info("已ocr识别【" + ++index +"】条数据!");
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+}

+ 5 - 0
ses-scan/src/main/resources/config.properties

@@ -0,0 +1,5 @@
+download.temp=D:\\bak\\temp\\
+file.scan.dir=D:/publicdata/ses-upload/byfiles/temp/
+file.ocr.result.dir=D:/publicdata/ses-upload/byfiles/
+file.scan.opencv.dir=D:/publicdata/ses-upload/opencv/
+scan.consume.thread.num = 2

+ 6 - 0
ses-scan/src/main/resources/gen.code.properties

@@ -0,0 +1,6 @@
+author=hgh
+version=1.0
+email=hgh@qmth.com.cn
+web.project.name=ses-scan
+model.project.name=ses-scan
+table.name=by_scan_std

+ 10 - 0
ses-scan/src/main/resources/jdbc.properties

@@ -0,0 +1,10 @@
+### database config ###
+JdbcDriverClass=oracle.jdbc.driver.OracleDriver
+DataSourceType=druid
+#JdbcUrl=jdbc:oracle:thin:@51392p14k6.zicp.fun:23489/gxzk
+#JdbcUser=gxzkyw2019
+#JdbcPassword=gxzkyw2019
+
+JdbcUrl=jdbc:oracle:thin:@127.0.0.1:1521/orcl
+JdbcUser=myorcl
+JdbcPassword=123456

File diff suppressed because it is too large
+ 0 - 0
ses-scan/src/main/webapp/d2-admin/css/app.d5a1ea54.css


+ 1 - 0
ses-scan/src/main/webapp/d2-admin/css/chunk-02d6.c8955061.css

@@ -0,0 +1 @@
+.contextmenu-pad[data-v-fe530e44]{-moz-user-select:none;-ms-flex-align:center;-ms-flex-pack:center;-ms-user-select:none;-webkit-box-align:center;-webkit-box-pack:center;-webkit-user-select:none;align-items:center;background-color:#f8f8f9;border:1px solid #dcdfe6;border-radius:4px;color:#909399;display:-webkit-box;display:-ms-flexbox;display:flex;height:300px;justify-content:center;user-select:none}.contextmenu-icon[data-v-fe530e44]{font-size:16px}

+ 1 - 0
ses-scan/src/main/webapp/d2-admin/css/chunk-0394.0acedd11.css

@@ -0,0 +1 @@
+.inner[data-v-4e95db2e]{bottom:20px;left:20px;position:absolute;right:20px;top:20px}

+ 1 - 0
ses-scan/src/main/webapp/d2-admin/css/chunk-040b.c0f60328.css

@@ -0,0 +1 @@
+.demo-bs-wrapper[data-v-3fd69994]{border:1px solid #dcdfe6;border-radius:4px;height:200px;overflow:hidden;position:relative;width:300px}.demo-bs-wrapper .demo-bs-item[data-v-3fd69994]{border-bottom:1px solid #f2f6fc;line-height:40px;padding-left:10px}.demo-bs-wrapper .demo-bs-item[data-v-3fd69994]:last-child{border-bottom:none}

+ 1 - 0
ses-scan/src/main/webapp/d2-admin/css/chunk-048a.00b07696.css

@@ -0,0 +1 @@
+.inner[data-v-fabcbbfa]{bottom:20px;left:20px;position:absolute;right:20px;top:20px}

Some files were not shown because too many files changed in this diff