list = new ArrayList<>();
+ String[] files = getFileNames(filePath);
+ if (files == null) {
+ return list;
+ }
+ for (String file : files) {
+ if (file.toLowerCase().endsWith(filters) || file.toUpperCase().endsWith(filters))
+ list.add(file);
+ }
+ return list;
+ }
+
+ /**
+ * filePath路径下是否存在文件fn
+ *
+ * @param fp 文件路径
+ * @param fn 文件名称
+ * @return 存放文件名称的数组
+ */
+ public static boolean isFileExist(String fp, String fn) {
+ if (fp == null || fn == null) return false;
+ String absPath;
+ if (fp.endsWith("/") || fp.endsWith("\\")) {
+ absPath = fp + fn;
+ } else {
+ absPath = fp + FSChar + fn;
+ }
+ return (new File(absPath)).exists();
+ }
+
+ /**
+ * 文件fn是否存在
+ *
+ * @param fn fileName
+ * @return boolean
+ */
+ public static boolean isFileExist(String fn) {
+ return !(fn == null || fn.length() < 1) && (new File(fn)).exists();
+ }
+
+ /**
+ * 获取父目录
+ *
+ * @param fan 全路径文件名
+ * @return 父目录
+ */
+ public static String getParentPath(String fan) {
+ try {
+ String pp = fan;
+ while (pp.lastIndexOf(FSChar) == (pp.length() - 1) || pp.lastIndexOf(File.separator) == (pp.length() - 1)) {
+ pp = pp.substring(0, pp.length() - 2);
+ }
+ int idx = pp.lastIndexOf(FSChar);
+ pp = pp.substring(0, idx);
+ return pp;
+ } catch (Exception e) {
+ UtilLogger.error(e.getMessage(), e);
+ return null;
+ }
+
+ }
+
+ /**
+ * 从输入流中所有内容到数组中
+ *
+ * @param is Description of Parameter
+ * @return Description of the Returned Value
+ */
+ public static byte[] readToBytes(InputStream is) {
+ byte[] buffer;
+ int SIZE = 1024 * 1024;
+ ByteArrayOutputStream bao = null;
+ try {
+ bao = new ByteArrayOutputStream();
+
+ buffer = new byte[SIZE];
+ BufferedInputStream in = new BufferedInputStream(is, 1024 * 1024);
+ int iBytes = 0;
+ while (iBytes != -1) {
+ iBytes = in.read(buffer, 0, SIZE);
+ if (iBytes != -1) {
+ bao.write(buffer, 0, iBytes);
+ }
+ }
+ return bao.toByteArray();
+ } catch (IOException ex) {
+ UtilLogger.error(ex.getMessage(), ex);
+ return null;
+ } finally {
+ try {
+ if (bao != null) bao.close();
+ } catch (Exception ignored) {
+ }
+ }
+ }
+
+ /**
+ * 读配置文件
+ * searchstr="stic";
+ * srcPath=".ini"
+ */
+ public static String ReadINI(String searchstr, String srcPath) {
+ String str;
+ String temp = null;
+ BufferedReader br = null;
+ searchstr = searchstr.toLowerCase();
+ try {
+ br = new BufferedReader(new InputStreamReader(new FileInputStream(srcPath)));
+ str = br.readLine();
+ while (str != null) {
+ str = str.trim();
+ if (str.length() > 0 && !str.startsWith("#")) {
+ if (str.toLowerCase().contains(searchstr)) {
+ temp = str.substring(str.indexOf("=") + 1);
+ temp = temp.replace('\\', '/');
+ break;
+ }
+ }
+ str = br.readLine();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (br != null) br.close();
+ } catch (Exception ignored) {
+ }
+ }
+ return temp;
+ }
+
+ /**
+ * 检查文件名是否满足Windows风格的通配符
+ * 如*表示任意多个字符,?表示任意一个字符
+ *
+ * @param fileName:原文件名
+ * @param filters: 通配符(*.*,a?.exe,*.zip)
+ * @return true false;
+ */
+ public static boolean checkFileNames(String fileName, String filters) {
+ fileName = getRelativeFileName(fileName);
+ if (fileName == null || filters == null)
+ return false;
+ String[] arr_filters = filters.split(",");
+ //没有通配符,表示任意字符
+ if (arr_filters == null || arr_filters.length == 0) return true;
+ int len = arr_filters.length;
+ String filter;
+ for (String arr_filter : arr_filters) {
+ filter = arr_filter;
+ filter = filter.trim();
+ //java中,"."表示任意字符
+ filter = filter.replaceAll("\\.", "\\\\.");
+ filter = filter.replaceAll("\\*", "\\.\\*");
+ filter = filter.replace('?', '.');
+ if (fileName.matches(filter)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * 获取文件名(绝对路径)的相对文件名
+ *
+ * @param fileAbsoluteName 绝对路径
+ * @return string
+ */
+ public static String getRelativeFileName(String fileAbsoluteName) {
+ if (fileAbsoluteName == null)
+ return null;
+ int pos1 = fileAbsoluteName.lastIndexOf("\\");
+ int pos2 = fileAbsoluteName.lastIndexOf("/");
+ int pos = Math.max(pos1, pos2);
+ if (pos == -1)
+ return fileAbsoluteName;
+ return fileAbsoluteName.substring(pos + 1);
+ }
+
+ /**
+ * 拷贝下文件(非深层次拷贝)
+ *
+ * @param srcFileDir 源
+ * @param destFileDir 目标
+ * @return 有效的拷贝结果;
+ */
+ public static boolean copyDirFileToDir(String srcFileDir, String destFileDir) {
+ String[] srcFiles = getFileNames(srcFileDir);
+ try {
+ if (srcFiles != null && srcFiles.length > 0) {
+ for (String srcFile : srcFiles) {
+ copyFile(srcFileDir + "\\" + srcFile, destFileDir + "\\" + srcFile);
+ }
+ return true;
+ }
+ } catch (Exception e) {
+ UtilLogger.error("拷贝文件出错!", e);
+ }
+ return false;
+ }
+
+ /**
+ * 重命名,通过复制的方式
+ */
+ public static boolean renameTo(String srcFile, String destFile) {
+ return copy(new File(srcFile), destFile, true);
+ }
+
+ /**
+ * 重命名,直接rename,警告,如果想跨盘move,此方法在linux下可能会失败
+ *
+ * @param srcFile 原名
+ * @param destFile 目标名字
+ * @return
+ */
+ public static boolean renameToEx(String srcFile, String destFile) {
+ File f = new File(srcFile);
+ if (!f.exists()) return false;
+ File df = new File(destFile);
+ return f.renameTo(df);
+ }
+
+ /**
+ * 从文件全路径串中分离出有效的目录名称;
+ *
+ * @param filepath
+ * @return 路径,以分隔符结束;
+ */
+ public static String getFullPath(String filepath) {
+ int index = filepath.lastIndexOf(FSChar);
+ if (index != -1) {
+ return filepath.substring(0, index + 1);
+ } else {
+ return "";
+ }
+ }
+
+ /**
+ * 获取文件全名
+ *
+ * @param fileName 文件路
+ * @return fullPathAndFullName
+ */
+ public static String getFullName(String fileName) {
+ File f = new File(fileName);
+ return f.getName();
+ }
+
+ /**
+ * 获取文件名
+ *
+ * @param fullPathAndFullName 全路径文件名;
+ * @return 文件名
+ */
+ public static String getName(String fullPathAndFullName) {
+ String fullName = getFullName(fullPathAndFullName);
+ int index = fullName.lastIndexOf(".");
+ if (index != -1) {
+ return fullName.substring(0, index);
+ } else {
+ return fullName;
+ }
+ }
+
+ /**
+ * 获取文件扩展名
+ *
+ * @param filepath
+ * @return string
+ */
+ public static String getExtName(String filepath) {
+ String fullName = getFullName(filepath);
+ int index = fullName.lastIndexOf(".");
+ if (index != -1) {
+ return fullName.substring(index + 1);
+ } else {
+ return "";
+ }
+ }
+
+ /**
+ * 文件是否存在
+ *
+ * @param fileName
+ * @return
+ */
+ public static boolean isFileExists(String fileName) {
+ File f = new File(fileName);
+ return f.exists();
+ }
+
+ //取得文件大小
+ public static long getFileSize(File f) {
+ if (f.exists()) {
+ try {
+ try (FileInputStream fis = new FileInputStream(f)) {
+ return fis.available();
+ }
+ } catch (IOException e) {
+ return 0L;
+ }
+ }
+ return 0;
+ }
+
+ public static long getFileSize(String path) {
+ return getFileSize(new File(path));
+ }
+
+ public static void main(String[] args) throws IOException {
+ // copy("f:/temp/33/worklog_new1", "f:/temp/worklog_new", true);
+ renameToEx("f:/temp/worklog_new1", "f:/temp/33/worklog_new1");
+ }
+
+ /**
+ * 获取文件列表;
+ *
+ * @param dir 文件路径
+ * @param suffix 后缀名
+ * @param max_level 遍历目录深度,0表示至只获取当前目录,1 标识下级目录, -1标识所有子目录
+ * @return List
+ */
+ public static List getDirFileList(String dir, String suffix, int max_level) {
+ File file = new File(dir);
+ List fileList = new ArrayList<>();
+ getDirFileList(fileList, file, suffix, 0, max_level);
+ return fileList;
+ }
+
+ /**
+ * 获取文件列表;
+ *
+ * @param dir 目录对象;
+ * @param suffix 后缀名
+ * @param level 深度;
+ * @param max_level 最大深度;
+ * @return
+ */
+ public static void getDirFileList(List fileList, File dir, String suffix, int level, int max_level) {
+ if (max_level >= 0 && level > max_level) return;
+
+ File[] childs = dir.listFiles();
+ if (childs == null) return;
+
+ for (File child : childs) {
+ if (child.isDirectory()) {
+ getDirFileList(fileList, child, suffix, level + 1, max_level);
+ } else {
+ //String filePath = child.getAbsolutePath();
+ String filePath = child.getName();
+ if (PubUtil.isEmpty(suffix)) {
+ //后缀名为null则为所有文件
+ fileList.add(filePath);
+ } else {
+ int begIndex = filePath.lastIndexOf(".");//最后一个.(即后缀名前面的.)的索引
+ String tempsuffix = "";
+ //防止是文件但却没有后缀名结束的文件
+ if (begIndex != -1) {
+ tempsuffix = filePath.substring(begIndex + 1);
+ }
+ if (tempsuffix.equals(suffix)) {
+ fileList.add(filePath);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * 获取文件列表;
+ *
+ * @param dir 文件路径
+ * @param suffix 后缀名
+ * @param max_level 遍历目录深度,0表示至只获取当前目录,1 标识下级目录, -1标识所有子目录
+ * @return List
+ */
+ public static List getFiles(String dir, String suffix, int max_level) {
+ File file = new File(dir);
+ List fileList = new ArrayList<>();
+ getFiles(fileList, file, suffix, 0, max_level);
+ return fileList;
+ }
+
+ /**
+ * 获取文件列表;
+ *
+ * @param dir 目录对象;
+ * @param suffix 后缀名
+ * @param level 深度;
+ * @param max_level 最大深度;
+ * @return 文件组合
+ */
+ public static void getFiles(List fileList, File dir, String suffix, int level, int max_level) {
+ if (max_level >= 0 && level > max_level) return;
+
+ File[] childs = dir.listFiles();
+ if (childs == null) return;
+
+ for (File child : childs) {
+ if (child.isDirectory()) {
+ getFiles(fileList, child, suffix, level + 1, max_level);
+ } else {
+ String filePath = child.getName();
+ if (PubUtil.isEmpty(suffix)) {
+ //后缀名为null则为所有文件
+ fileList.add(child);
+ } else {
+ int begIndex = filePath.lastIndexOf(".");//最后一个.(即后缀名前面的.)的索引
+ String tempsuffix = "";
+ //防止是文件但却没有后缀名结束的文件
+ if (begIndex != -1) {
+ tempsuffix = filePath.substring(begIndex + 1);
+ }
+ if (tempsuffix.equals(suffix)) {
+ fileList.add(child);
+ }
+ }
+ }
+ }
+ }
+
+
+ public static void zipFile(String zipFileName, File inputFile) throws Exception {
+ System.out.println("压缩中...");
+ ZipOutputStream out = new ZipOutputStream(new FileOutputStream(
+ zipFileName));
+ BufferedOutputStream bo = new BufferedOutputStream(out);
+ try {
+ zipFile(out, inputFile, inputFile.getName(), bo);
+ } finally {
+ IOUtils.closeQuietly(bo);
+ IOUtils.closeQuietly(out);
+ }
+
+ System.out.println("压缩完成");
+ }
+
+ private static void zipFile(ZipOutputStream out, File f, String base, BufferedOutputStream bo) throws Exception { // 方法重载
+ if (f.isDirectory()) {
+ File[] fl = f.listFiles();
+ if (fl.length == 0) {
+ out.putNextEntry(new ZipEntry(base + "/")); // 创建zip压缩进入点base
+// System.out.println(base + "/");
+ }
+ for (int i = 0; i < fl.length; i++) {
+ zipFile(out, fl[i], base + "/" + fl[i].getName(), bo); // 递归遍历子文件夹
+ }
+ } else {
+ out.putNextEntry(new ZipEntry(base)); // 创建zip压缩进入点base
+// System.out.println(base);
+ FileInputStream in = new FileInputStream(f);
+ BufferedInputStream bi = new BufferedInputStream(in);
+ try {
+ int b = bi.available();
+ byte[] data = new byte[b];
+ while ((bi.read(data, 0, b)) != -1) {
+ bo.write(data, 0, b); // 将字节流写入当前zip目录
+ bo.flush();
+ }
+ } finally {
+ IOUtils.closeQuietly(bi);
+ IOUtils.closeQuietly(in);
+ }
+
+ }
+ }
+
+
+ //解压缩返回解压之后的文件夹路径
+ public static String unZipFile(File zipFile, String outpath) {
+ String fileout = "";
+ long startTime = System.currentTimeMillis();
+ try {
+ ZipInputStream zin = new ZipInputStream(new FileInputStream(zipFile));//输入源zip路径
+ BufferedInputStream bin = new BufferedInputStream(zin);
+ String Parent = outpath; //输出路径(文件夹目录)
+ File Fout = null;
+ ZipEntry entry;
+ try {
+ UtilLogger.debug("开始解压文件:" + zipFile.getName());
+ while ((entry = zin.getNextEntry()) != null) {
+ String name = entry.getName();
+ Fout = new File(Parent, name);
+ if (name.split("\\/").length > 1) fileout = name.split("\\/")[0];
+ if (!Fout.exists()) {
+ (new File(Fout.getParent())).mkdirs();
+ }
+ if (entry.isDirectory()) continue;
+ UtilLogger.debug(" 解压:" + name);
+
+ FileOutputStream out = new FileOutputStream(Fout);
+ BufferedOutputStream Bout = new BufferedOutputStream(out);
+ try {
+ int b;
+ while ((b = bin.read()) != -1) {
+ Bout.write(b);
+ }
+ } finally {
+ IOUtils.closeQuietly(Bout);
+ IOUtils.closeQuietly(out);
+ }
+// System.out.println(Fout+"解压成功");
+ }
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } finally {
+ IOUtils.closeQuietly(bin);
+ IOUtils.closeQuietly(zin);
+ }
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ long endTime = System.currentTimeMillis();
+ UtilLogger.debug("解压完成!耗费时间: " + (endTime - startTime) + " ms");
+ return outpath + "\\" + fileout;
+ }
+
+ /**
+ * 读取txt文件类型
+ * @param fileName
+ * @return
+ */
+ public static String readFileContent(String fileName) {
+ File file = new File(fileName);
+ BufferedReader reader = null;
+ StringBuffer sbf = new StringBuffer();
+ try {
+ reader = new BufferedReader(new FileReader(file));
+ String tempStr;
+ while ((tempStr = reader.readLine()) != null) {
+ sbf.append(tempStr);
+ }
+ reader.close();
+ return sbf.toString();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }
+ return sbf.toString();
+ }
+ /**
+ * 解压文件到指定目录
+ *
+ * @param zipFile
+ * @param descDir
+ * @author isea533
+ */
+ public static void unZipFiles(File zipFile, String descDir) throws IOException {
+ if (!descDir.endsWith("\\") && !descDir.endsWith("/")) descDir = descDir + "/";
+ File pathFile = new File(descDir);
+ if (!pathFile.exists()) {
+ pathFile.mkdirs();
+ }
+ Charset gbk = Charset.forName("GBK");
+ ZipFile zip = new ZipFile(zipFile, gbk);
+ UtilLogger.debug("开始解压文件:" + zipFile.getName());
+ try {
+ for (Enumeration entries = zip.entries(); entries.hasMoreElements(); ) {
+ ZipEntry entry = (ZipEntry) entries.nextElement();
+ String zipEntryName = entry.getName();
+ InputStream in = zip.getInputStream(entry);
+ try {
+ String outPath = (descDir + zipEntryName).replaceAll("\\*", "/");
+
+ //判断路径是否存在,不存在则创建文件路径
+ File file = new File(outPath.substring(0, outPath.lastIndexOf('/')));
+ if (!file.exists()) {
+ file.mkdirs();
+ }
+ //判断文件全路径是否为文件夹,如果是上面已经上传,不需要解压
+ if (new File(outPath).isDirectory()) {
+ continue;
+ }
+ UtilLogger.debug(" 解压:" + zipEntryName);
+ //输出文件路径信息
+ // System.out.println(outPath);
+
+ OutputStream out = new FileOutputStream(outPath);
+ try {
+ byte[] buf1 = new byte[1024];
+ int len;
+ while ((len = in.read(buf1)) > 0) {
+ out.write(buf1, 0, len);
+ }
+ } finally {
+ IOUtils.closeQuietly(out);
+ }
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ }
+ UtilLogger.debug("解压完成! ");
+ } finally {
+ zip.close();
+ }
+
+
+ }
+
+ /**
+ * 判断是否是图片
+ *
+ * @param base64Str
+ * @return
+ */
+ public static boolean isImageFromBase64(String base64Str) {
+ boolean flag = false;
+ try {
+ BufferedImage bufImg = ImageIO.read(new ByteArrayInputStream(new BASE64Decoder().decodeBuffer(base64Str)));
+ if (null == bufImg) {
+ return flag;
+ }
+ flag = true;
+ } catch (Exception e) {
+ return flag;
+ }
+ return flag;
+ }
+}
diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/UtilLogger.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/UtilLogger.java
new file mode 100644
index 0000000..ce379cf
--- /dev/null
+++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/UtilLogger.java
@@ -0,0 +1,66 @@
+package cc.smtweb.system.bpm.util;
+
+import cc.smtweb.framework.core.util.PubUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.text.MessageFormat;
+
+@SuppressWarnings("UnusedDeclaration")
+public class UtilLogger {
+ private static final Logger logger = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
+
+ private static String getOrigMsg(String message, Throwable e) {
+ if (message != null) return message;
+ return PubUtil.getOrigMsg(e);
+ }
+
+ public static void debug(String message) {
+ logger.debug(message);
+ }
+
+ public static void debug(String message, String module) {
+ logger.debug(MessageFormat.format("[{0}]{1}", module, message));
+ }
+
+ public static void debug(String message, Throwable e) {
+ logger.debug(getOrigMsg(message, e), e);
+ }
+
+ public static void info(String message) {
+ logger.info(message);
+ }
+
+ public static void info(String message, String module) {
+ logger.info(MessageFormat.format("[{0}]{1}", module, message));
+ }
+
+ public static void info(String message, Throwable e) {
+ logger.info(getOrigMsg(message, e), e);
+ }
+
+ public static void warn(String message) {
+ logger.warn(message);
+ }
+
+ public static void warn(String message, String module) {
+ logger.warn(MessageFormat.format("[{0}]{1}", module, message));
+ }
+
+ public static void warn(String message, Throwable e) {
+ logger.warn(getOrigMsg(message, e), e);
+ }
+
+ public static void error(String message) {
+ logger.error(message);
+ }
+
+ public static void error(String message, String module) {
+ logger.error(MessageFormat.format("[{0}]{1}", module, message));
+ }
+
+ public static void error(String message, Throwable e) {
+ if (e instanceof Exception) logger.info(message + PubUtil.getOrigMsg(e));
+ else logger.error(getOrigMsg(message, e), e);
+ }
+}
diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/UtilMath.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/UtilMath.java
new file mode 100644
index 0000000..99f5613
--- /dev/null
+++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/UtilMath.java
@@ -0,0 +1,465 @@
+package cc.smtweb.system.bpm.util;
+
+import cc.smtweb.framework.core.util.PubUtil;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: AKhh
+ * Date: 12-12-25 下午1:02
+ * To change this template use File | Settings | File Templates.
+ */
+public class UtilMath {
+ //整数
+ private static DecimalFormat dfLng = new DecimalFormat("##############0");
+ private static DecimalFormat dfLong = new DecimalFormat("###,###,###,###,##0");
+ //一位小数
+ private static DecimalFormat df1 = new DecimalFormat("##############0.0");
+ private static DecimalFormat df1Format = new DecimalFormat("###,###,###,###,##0.0");
+ //两位小数
+ private static DecimalFormat df2 = new DecimalFormat("##############0.00");
+ private static DecimalFormat df2Format = new DecimalFormat("###,###,###,###,##0.00");
+ //四位小数
+ private static DecimalFormat df4 = new DecimalFormat("###,###,###,###,##0.0000");
+ //六位小数
+ private static DecimalFormat df6Number = new DecimalFormat("#######################0.000000");
+ private static DecimalFormat df6NumberF = new DecimalFormat("#,###,###,###,###,###,##0.000000");
+
+ public final static DecimalFormat stdAmountFormat = new DecimalFormat("###,###,###,###,##0.00");
+ public final static DecimalFormat stdNumberFormat = new DecimalFormat("#0.00");
+ public final static String DEF_NUM_TEN_THOUSAND = "10000";//万
+ public static final double MAX_VALUE = 9999999999999.99D;
+ public static final double MIN_VALUE = -9999999999999.99D;
+
+ private final static BigDecimal ONE_BIG = new BigDecimal(1.00D);
+ private static final String UNIT = "万仟佰拾亿仟佰拾万仟佰拾元角分";
+ private static final String DIGIT = "零壹贰叁肆伍陆柒捌玖";
+
+ /**
+ * 4舍5入double,2位小数
+ */
+ public static double roundDouble(double src) {
+ return roundDouble(src, 2);
+ }
+
+ /**
+ * 4舍5入double,N 位小数
+ * @param src
+ * @param scale 小数位数
+ * @return
+ */
+ public static double roundDouble(Object src, int scale) {
+ if (src == null) return 0.0;
+ String v = src.toString();
+ if (PubUtil.isEmpty(v)) return 0.0;
+ if (scale < 0) scale = 2;
+
+ BigDecimal src_b = new BigDecimal(v);
+ BigDecimal src_v = src_b.divide(ONE_BIG, scale + 2, BigDecimal.ROUND_HALF_UP);// 4舍5入
+ src_v = src_v.divide(ONE_BIG, scale, BigDecimal.ROUND_HALF_UP);// 4舍5入
+ return src_v.doubleValue();
+ }
+
+ /**
+ * 舍位处理,原生floor有坑,部分浮点数记录为.99999999999形式,导致floor结果错误
+ * @param d
+ * @return
+ */
+ public static double floor(double d) {
+ return Math.floor(UtilMath.roundDouble(d, 2));
+ }
+
+ /**
+ * 比较两Double是否相等,将会吧他们专程BigDecimal进行比较;
+ *
+ * @param src double1
+ * @param tag double2
+ * @return src > tag 返回1, src < tag 返回-1, 否则返回0
+ */
+ public static int compare(double src, double tag) {
+ BigDecimal src_b = new BigDecimal(src);
+ BigDecimal src_v = src_b.divide(ONE_BIG, 2, BigDecimal.ROUND_HALF_UP);// 4舍5入
+
+ BigDecimal tag_b = new BigDecimal(tag);
+ BigDecimal tag_v = tag_b.divide(ONE_BIG, 2, BigDecimal.ROUND_HALF_UP);// 4舍5入
+
+ return src_v.compareTo(tag_v);
+ }
+
+ /**
+ * 自动过滤金额中的逗号转换为double,如果出错,则返回0
+ *
+ * @param s 源串,可能为带逗号的金额串;
+ * @return double
+ */
+ public static Double toDouble(String s) {
+ return todouble(s);
+ }
+
+ /**
+ * 自动过滤金额中的逗号转换为double,如果出错,则返回0
+ *
+ * @param s 源串,可能为带逗号的金额串;
+ * @return double
+ */
+ public static double todouble(String s) {
+ try {
+ return Double.parseDouble(s.replaceAll(",", ""));
+ } catch (Exception e) {
+ return 0.00;
+ }
+ }
+
+ /**
+ * 获取double,主要过滤d为null的情况;
+ *
+ * @param d Double对象;
+ * @return double
+ */
+ public static double todouble(Double d) {
+ if (d == null) return 0.0d;
+ return d.doubleValue();
+ }
+
+ /**
+ * 自动过滤金额中的逗号转换为float,如果出错,则返回0
+ *
+ * @param s 源串,可能为带逗号的金额串;
+ * @return Float
+ */
+ public static Float toFloat(String s) {
+ return tofloat(s);
+ }
+
+ /**
+ * 自动过滤金额中的逗号转换为float,如果出错,则返回0
+ *
+ * @param s 源串,可能为带逗号的金额串;
+ * @return Float
+ */
+ public static float tofloat(String s) {
+ try {
+ return Float.parseFloat(s.replaceAll(",", ""));
+ } catch (Exception e) {
+ return 0.0f;
+ }
+ }
+
+ public static long tolong(String src, long defaultvalue) {
+ try {
+ return Long.parseLong(src);
+ } catch (Exception e) {
+ return defaultvalue;
+ }
+ }
+
+ public static int toint(String src, int defaultvalue) {
+ try {
+ return Integer.parseInt(src);
+ } catch (Exception e) {
+ return defaultvalue;
+ }
+ }
+
+ /**
+ * 考虑使用中的精度,判断一个Value是否>0,实际是>0.00001
+ *
+ * @param value double类型
+ * @return boolean
+ */
+ public static boolean isBigThanZero(double value) {
+ return (value > 0.00001);
+ }
+
+ /**
+ * 考虑使用中的精度,判断一个Value是否>0,实际是>0.00001
+ *
+ * @param value String类型
+ * @return boolean
+ */
+ public static boolean isBigThanZero(String value) {
+ return !PubUtil.isEmpty(value) && isBigThanZero(toDouble(value));
+ }
+
+ /**
+ * 考虑使用中的精度,判断一个Value是否=0,实际是给出一个值范围。
+ *
+ * @param value double类型
+ * @return boolean
+ */
+ public static boolean isEqualsZero(double value) {
+ return (-0.00001 < value && value < 0.00001);
+ }
+
+ /**
+ * 考虑使用中的精度,判断一个Value是否=0,实际是给出一个值范围。
+ *
+ * @param value String类型
+ * @return boolean
+ */
+ public static boolean isEqualsZero(String value) {
+ return PubUtil.isEmpty(value) || isEqualsZero(toDouble(value));
+ }
+
+ /**
+ * 是否是负数
+ *
+ * @param db_val 要判断的double
+ * @return 负数则返回true;
+ */
+ public static boolean isNegative(double db_val) {
+ return (compare(db_val, 0.00D) == -1);
+ }
+
+ /**
+ * 是否是正数
+ *
+ * @param db_val 要判断的double
+ * @return 正数则返回true;
+ */
+ public static boolean isPlus(double db_val) {
+ return (compare(db_val, 0.00D) == 1);
+ }
+
+ /**
+ * 得到金额字符串,保持小数点2位
+ *
+ * @param db 将double转换为金额字符串;
+ * @return 金额字符串#0.00;
+ */
+ public static String toStdNumberString(double db) {
+ try {
+ return stdNumberFormat.format(db);
+ } catch (Exception e) {
+ return "0.00";
+ }
+ }
+
+ public static String toStdNumberStringEx(double db) {
+ try {
+ if (compare(db, -1d) == 0) return "-";
+ return stdNumberFormat.format(db);
+ } catch (Exception e) {
+ return "0.00";
+ }
+ }
+
+ /**
+ * 将金额格式字符串,如23,333,093.01 去掉逗号
+ *
+ * @param s 金额串
+ * @return String 去掉逗号后的串,如果amount为空,则返回0.00
+ */
+ public static String toStdNumberString(String s) {
+ if (PubUtil.isEmpty(s))
+ return "0.00";
+ return stdNumberFormat.format(UtilMath.todouble(s));
+ }
+
+ /**
+ * 将数据转换为两位小数的数字格式;
+ *
+ * @param d 数据
+ * @param isZeroToEmpty 如果未0,是否返回“”;
+ * @return 两位小数的字符串;
+ */
+ public static String toStdNumberString(double d, boolean isZeroToEmpty) {
+ if (isEqualsZero(d)) {
+ return isZeroToEmpty ? "": "0.00";
+ }
+ return stdNumberFormat.format(d);
+ }
+
+ public static String toStdNumberString(String s, boolean isZeroToEmpty) {
+ return toStdNumberString(UtilMath.todouble(s), isZeroToEmpty);
+ }
+
+ public static String toStdNumberString(double d, int scale) {
+ DecimalFormat dfn = null;
+ if (scale == 1) dfn = df1Format;
+ if (scale == 2) dfn = df2Format;
+ else if (scale == 4) dfn = df4;
+ else if (scale == 6) dfn = df6NumberF;
+ else if (scale <= 0) dfn = dfLong;
+ else {
+ StringBuilder sb = new StringBuilder("###,###,###,###,##0.");
+ for (int i = 0; i < scale; i++) sb.append("0");
+ dfn = new DecimalFormat(sb.toString());
+ }
+ return dfn.format(d);
+ }
+
+ /**
+ * 将数字乘100,保留小数点后两位, 然后后面添加%
+ *
+ * @param d 值
+ * @param isZeroToEmpty,如果值为0,是否返回空;
+ * @return 字符串;
+ */
+ public static String toStdPercentNumberStr(double d, boolean isZeroToEmpty) {
+ if (d > -0.00000000001 && d < 0.00000000001) {
+ return isZeroToEmpty ? "": "0.00%";
+ }
+ return toStdNumberString(d * 100) + "%";
+ }
+
+
+ public static String toStdAmountString(double d) {
+ return toStdAmountString(d, false);
+ }
+
+ /**
+ * 将数据转换为两位小数的金额格式,带逗号;
+ *
+ * @param d 数据
+ * @param isZeroToEmpty 如果未0,是否返回“”;
+ * @return 金额格式的字符串;
+ */
+ public static String toStdAmountString(double d, boolean isZeroToEmpty) {
+ if (isEqualsZero(d)) {
+ return isZeroToEmpty ? "": "0.00";
+ }
+ return stdAmountFormat.format(d);
+ }
+
+ public static String toStdAmountString(String s) {
+ return toStdAmountString(UtilMath.todouble(s), false);
+ }
+
+ public static String toStdAmountString(String s, boolean isZeroToEmpty) {
+ return toStdAmountString(UtilMath.todouble(s), isZeroToEmpty);
+ }
+
+
+ /**
+ * 将小写金额转换为人民币大写金额
+ *
+ * @param s 金额格式的串
+ * @return String 转换结果
+ */
+ public static String toCapsAmountString(String s) {
+ if (PubUtil.isEmpty(s)) return "";
+ return toCapsAmountString(todouble(s));
+ }
+
+ /**
+ * 将小写金额转换为人民币大写金额
+ *
+ * @param v double
+ * @return String 转换结果
+ */
+ public static String toCapsAmountString(double v) {
+ if (v < MIN_VALUE || v > MAX_VALUE) return "参数非法!";
+
+ boolean negative = isNegative(v);
+
+ if (negative) v = Math.abs(v);
+ long l = Math.round(v * 100);
+ if (l == 0) return "零元整";
+
+ String strValue = String.valueOf(l);
+ // i用来控制数
+ int i = 0;
+ // j用来控制单位
+ int j = UNIT.length() - strValue.length();
+ StringBuilder rs = new StringBuilder(32);
+ boolean isZero = false;
+ for (; i < strValue.length(); i++, j++) {
+ char ch = strValue.charAt(i);
+ if (ch == '0') {
+ isZero = true;
+ if (UNIT.charAt(j) == '亿' || UNIT.charAt(j) == '万' || UNIT.charAt(j) == '元') {
+ rs.append(UNIT.charAt(j));
+ isZero = false;
+ }
+ } else {
+ if (isZero) {
+ rs.append('零');
+ isZero = false;
+ }
+ rs.append(DIGIT.charAt(ch - '0')).append(UNIT.charAt(j));
+ }
+ }
+ if (rs.charAt(rs.length() - 1) != '分')
+ rs.append('整');
+
+ i = rs.indexOf("亿万");
+ if (i > 0) rs.delete(i + 1, i + 2); // i+1 ->万
+
+ if (negative)
+ return rs.insert(0, '负').toString();
+ else
+ return rs.toString();
+ }
+
+ /**
+ * 返回0 到 maxvalue的随机数
+ *
+ * @param maxvalue 随机数的最大值
+ * @return int
+ */
+ public static int rnd(int maxvalue) {
+ return (int) (Math.random() * (maxvalue + 1));
+ }
+
+
+ public static double chkDbNull(Double v) {
+ return v == null ? 0: v;
+ }
+
+ public static double max(double d1, double d2) {
+ return compare(d1, d2) < 0 ? d2 : d1;
+ }
+
+ public static double min(double d1, double d2) {
+ return compare(d1, d2) < 0 ? d1 : d2;
+ }
+
+
+ public static void main(String[] args) {
+ double aa=123456789.345678900005;
+ System.out.println("args0 = " + upDouble(aa,0));
+ System.out.println("args1 = " + upDouble(aa,1));
+ System.out.println("args2 = " + upDouble(aa,2));
+ System.out.println("args3 = " + upDouble(aa,3));
+ System.out.println("args4 = " + upDouble(aa,4));
+ System.out.println("args5 = " + upDouble(aa,5));
+ System.out.println("args5 = " + upDouble(aa,6));
+ System.out.println("args5 = " + upDouble(aa,7));
+ System.out.println("args5 = " + upDouble(aa,8));
+
+ }
+
+ /**
+ * double 去尾法
+ * @param src 待处理数据
+ * @param scale 保留小数位数
+ * @return
+ */
+ public static double cutDouble(double src, int scale){
+ String v = toStdNumberString(src, 6);//先到6位小数,再去计算,否则容易出错,如8.3成8.29999999999,舍位就成了8.29了
+ DecimalFormat formater = new DecimalFormat();
+ formater.setMaximumFractionDigits(scale);
+ formater.setGroupingSize(0);
+ formater.setRoundingMode(RoundingMode.FLOOR);
+ return new BigDecimal(formater.format(toDouble(v))).doubleValue();
+ }
+
+ /**
+ * double 进位法
+ * @param src 待处理数据
+ * @param scale 保留小数位数
+ * @return
+ */
+ public static double upDouble(double src, int scale){
+ String v = toStdNumberString(src, 6);//先到6位小数,再去计算
+ DecimalFormat formater = new DecimalFormat();
+ formater.setMaximumFractionDigits(scale);
+ formater.setGroupingSize(0);
+ formater.setRoundingMode(RoundingMode.UP);
+ return new BigDecimal(formater.format(toDouble(v))).doubleValue();
+ }
+}
diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/UtilString.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/UtilString.java
new file mode 100644
index 0000000..1eda782
--- /dev/null
+++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/UtilString.java
@@ -0,0 +1,504 @@
+package cc.smtweb.system.bpm.util;
+
+
+import cc.smtweb.framework.core.util.PubUtil;
+
+import java.text.Collator;
+import java.util.*;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: AKhh
+ * Date: 12-12-24 下午5:09
+ * To change this template use File | Settings | File Templates.
+ */
+@SuppressWarnings("UnusedDeclaration")
+public class UtilString {
+ private static Collator chineseCollator = Collator.getInstance(Locale.CHINA);
+
+ /**
+ * strSrc中寻找第一个strSe并且返回以其分隔的Left部分,汉字长度也为1
+ *
+ * @param strSrc 源字符串
+ * @param strSe 分割字符
+ * @return String 返回
+ */
+ public static String getLeft(String strSrc, String strSe) {
+ if (PubUtil.isEmpty(strSrc))
+ return "";
+ if (PubUtil.isEmpty(strSe))
+ strSe = " ";
+
+ String result = "";
+ int pos = strSrc.indexOf(strSe);
+ if (pos >= 0)
+ result = strSrc.substring(0, pos);
+ return result;
+ }
+
+ /**
+ * 返回字符串的左边部分,汉字长度也为1
+ *
+ * @param strSrc 源串,如果为空,则返回“”;
+ * @param count 要获取的右边字符串长度,负数将返回“”,如果count>字符串长度,则返回整个字符串;
+ * @return String return
+ */
+ public static String getLeft(String strSrc, int count) {
+ if (PubUtil.isEmpty(strSrc) || count <= 0) {
+ return "";
+ }
+ if (strSrc.length() < count) {
+ return strSrc;
+ } else {
+ return strSrc.substring(0, count);
+ }
+ }
+
+ /**
+ * strSrc中寻找第一个strSe并且返回以其分隔的Right部分,汉字长度也为1
+ *
+ * @param strSrc 源串
+ * @param strSe 分隔符,一个字符
+ * @return String right部分
+ */
+ public static String getRight(String strSrc, String strSe) {
+ if (PubUtil.isEmpty(strSrc))
+ return "";
+ if (PubUtil.isEmpty(strSe))
+ strSe = " ";
+
+ String result = strSrc;
+ int pos = strSrc.indexOf(strSe);
+ if (pos >= 0)
+ result = strSrc.substring(pos + strSe.length());
+ return result;
+ }
+
+ /**
+ * 返回字符串的右边部分,汉字长度也为1
+ *
+ * @param strSrc 源串
+ * @param count 要获取的右边字符串长度,负数将返回“”,如果count>字符串长度,则返回整个字符串;
+ * @return String return
+ */
+ public static String getRight(String strSrc, int count) {
+ if (PubUtil.isEmpty(strSrc) || count <= 0) {
+ return "";
+ }
+ int l = strSrc.length();
+ if (l <= count) {
+ return strSrc;
+ }
+ return strSrc.substring(l - count);
+ }
+
+ /**
+ * 左边补齐字符
+ *
+ * @param src 源串
+ * @param pad 补齐字符
+ * @param length 最终长度
+ * @return 补齐后的字符串
+ */
+ public static String padLeft(String src, String pad, int length) {
+ StringBuilder sb = new StringBuilder(repeatString(pad, length));
+ sb.append(src);
+ return sb.substring(sb.length() - length);
+ }
+
+ public static String padLeft(long src, String pad, int length) {
+ StringBuilder sb = new StringBuilder(repeatString(pad, length));
+ sb.append(src);
+ return sb.substring(sb.length() - length);
+ }
+
+ public static String padRight(String src, String pad, int length) {
+ StringBuilder sb = new StringBuilder(length * pad.length() + src.length());
+ sb.append(src).append(repeatString(pad, length));
+ return sb.substring(0, length);
+ }
+
+ public static String padRight(long src, String pad, int length) {
+ StringBuilder sb = new StringBuilder(length * pad.length());
+ sb.append(src).append(repeatString(pad, length));
+ return sb.substring(0, length);
+ }
+
+ /**
+ * 由于jdk1.3提供的replace函数不能满足替换要求,自己写一个
+ *
+ * @param src 源串
+ * @param oldS 将...
+ * @param newS 替换成...
+ * @return 替换后的字符串
+ */
+ public static String replaceString(String src, String oldS, String newS) {
+ StringBuilder ret = new StringBuilder(64);
+ int pos = src.indexOf(oldS);
+ while (pos >= 0) {
+ ret.append(src, 0, pos).append(newS);
+ src = src.substring(pos + oldS.length());
+ pos = src.indexOf(oldS);
+ }
+ ret.append(src);
+ return ret.toString();
+ }
+
+ /**
+ * 取得指定字符串左边的有效数字,首先去掉两边空格
+ *
+ * @param s 源串
+ * @return 串左边的有效数字
+ */
+ public static String getStringLeftNumber(String s) {
+ String ret = "";
+ int dotCount = 0;
+ s = s.trim();
+ char[] carr = s.toCharArray();
+ for (char aCarr : carr) {
+ if (Character.isDigit(aCarr)) {
+ ret += aCarr;
+ } else if (aCarr == '.' && dotCount == 0) {
+ ret += aCarr;
+ dotCount++;
+ } else {
+ break;
+ }
+ }
+ if (ret.endsWith(".")) {
+ ret = ret.substring(0, ret.length() - 1);
+ }
+ return ret;
+ }
+
+ /**
+ * 取得重复字串
+ *
+ * @param repeatString 重复字串
+ * @param count 重复次数
+ * @return String
+ */
+ public static String repeatString(String repeatString, int count) {
+ if (count <= 0) return "";
+ StringBuilder ret = new StringBuilder(repeatString.length() * count);
+ for (int i = 1; i <= count; i++) {
+ ret.append(repeatString);
+ }
+ return ret.toString();
+ }
+
+ /**
+ * 去除字符串左边的指定字符串
+ *
+ * @param src 源字符串
+ * @param cut 要去掉的字符串;
+ * @return 处理结果
+ */
+ public static String cutStringLeft(String src, String cut) {
+ if (PubUtil.isEmpty(src) || PubUtil.isEmpty(cut)) {
+ return "";
+ }
+ if (src.startsWith(cut)) {
+ return cutStringLeft(src.substring(cut.length()), cut);
+ } else {
+ return src;
+ }
+ }
+
+ public static String cutStringRight(String src, String cut) {
+ if (PubUtil.isEmpty(src) || PubUtil.isEmpty(cut)) {
+ return "";
+ }
+ while (src.endsWith(cut))
+ src = src.substring(0, src.length() - cut.length());
+
+ return src;
+ }
+
+ /**
+ * Removes all spaces from a string
+ * 可以替换大部分空白字符, 不限于空格,\s 可以匹配空格、制表符、换页符等空白字符的其中任意一个
+ */
+ public static String removeSpaces(String str) {
+ return str.replaceAll("\\s*", "");
+ }
+
+ /**
+ * Creates a single string from a List of strings seperated by a delimiter.
+ *
+ * @param list a list of strings to join
+ * @param delim the delimiter character(s) to use. (null value will join with no delimiter)
+ * @return a String of all values in the list seperated by the delimiter
+ */
+ public static String join(List list, String delim) {
+ if (list == null || list.size() < 1)
+ return null;
+ StringBuffer buf = new StringBuffer();
+ Iterator i = list.iterator();
+
+ while (i.hasNext()) {
+ buf.append((String) i.next());
+ if (i.hasNext())
+ buf.append(delim);
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Splits a String on a delimiter into a List of Strings.
+ *
+ * @param str the String to split
+ * @param delim the delimiter character(s) to join on (null will split on whitespace)
+ * @return a list of Strings
+ */
+ public static List split(String str, String delim) {
+ List splitList = new ArrayList<>();
+ StringTokenizer st;
+ if (str == null)
+ return splitList;
+
+ if (delim != null)
+ st = new StringTokenizer(str, delim);
+ else
+ st = new StringTokenizer(str);
+
+ while (st.hasMoreTokens()) {
+ splitList.add(st.nextToken());
+ }
+ return splitList;
+ }
+
+ //是否为true,(1,y,true,yes)
+ public static boolean toBoolean(String v) {
+ return "1".equals(v) || "y".equalsIgnoreCase(v) || "true".equalsIgnoreCase(v) || "yes".equalsIgnoreCase(v);
+ }
+
+ public static int chineseCompare(String s1, String s2) {
+ return chineseCollator.compare(s1, s2);
+ }
+
+ /**
+ * 按照编码级次,得到类型的真实编码,主要用于like 'parentCode%'
+ * getSplitTypeCode('GF82000',2, 2, 1) == GF82
+ * getSplitTypeCode('GF82100',3, 2, 1) == GF821
+ * getSplitTypeCode('82100' ,3, 0, 1) == 821
+ *
+ * @param curLevel 当前编码的所在层次
+ * @param startIndex 数字编码的开始选项
+ * @param perSize 每层的数字编码长度
+ */
+ public static String getRealCode(String code, int curLevel, int startIndex, int perSize) {
+ StringBuilder sb = new StringBuilder(code.length());
+ if (startIndex > 0) sb.append(code, 0, startIndex);
+ for (int i = startIndex, l = 0; i < code.length(); i += perSize) {
+ if (l < curLevel) {
+ sb.append(code, i, i + perSize);
+ ++l;
+ } else {
+ break;
+ }
+ }
+ return sb.toString();
+ }
+
+ //函数功能: 正整数
+ public static boolean isPureNumber(String inputString) {
+ return inputString.matches("^[1-9]\\d*$");
+ }
+
+ //函数功能: 整数
+ public static boolean isNumber(String inputString) {
+ return inputString.matches("^[-+][0-9]\\d*$");
+ }
+
+ //函数功能: 浮点数
+ public static boolean isAmount(String inputString) {
+ return inputString.matches("^[-+]?[\\d,]+(\\.\\d+)?$");
+ }
+
+ //函数功能: 带千分号的整数
+ public static boolean isFormatNumber(String inputString) {
+ return inputString.matches("^[-+]?[\\d,]+(\\d+)?$");
+ }
+
+
+ //首字母大写
+ public static String upFirst(String s) {
+ return s.substring(0, 1).toUpperCase() + s.substring(1);
+ }
+
+ public static String padRightBytes(String src, String pad, int length) {
+ length -= src.replaceAll("[^\\x00-\\xff]", "**").length();
+ return src + repeatString(pad, length);
+ }
+
+ //按字节数取子串,begin不是按字节的
+ public static String substrByte(String src, int begin, int len) {
+ StringBuilder sb = new StringBuilder(32);
+ char c;
+ int tl = src.length();
+ for (int i = begin; i < len + begin && i < tl; i++) {
+
+ c = src.charAt(i);
+ sb.append(c);
+ if (String.valueOf(c).replaceAll("[^\\x00-\\xff]", "**").length() > 1) {
+ // 遇到中文汉字,截取字节总数减1
+ --len;
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * 通配符算法。 可以匹配"*"和"?"
+ * 如a*b?d可以匹配aAAAbcd
+ *
+ * @param pattern 匹配表达式
+ * @param str 匹配的字符串
+ * @return
+ */
+ public static boolean match(String pattern, String str) {
+ if (pattern == null || str == null)
+ return false;
+
+ boolean result = false;
+ char c; // 当前要匹配的字符串
+ boolean beforeStar = false; // 是否遇到通配符*
+ int back_i = 0;// 回溯,当遇到通配符时,匹配不成功则回溯
+ int back_j = 0;
+ int i, j;
+ for (i = 0, j = 0; i < str.length(); ) {
+ if (pattern.length() <= j) {
+ if (back_i != 0) {// 有通配符,但是匹配未成功,回溯
+ beforeStar = true;
+ i = back_i;
+ j = back_j;
+ back_i = 0;
+ back_j = 0;
+ continue;
+ }
+ break;
+ }
+
+ if ((c = pattern.charAt(j)) == '*') {
+ if (j == pattern.length() - 1) {// 通配符已经在末尾,返回true
+ result = true;
+ break;
+ }
+ beforeStar = true;
+ j++;
+ continue;
+ }
+
+ if (beforeStar) {
+ if (str.charAt(i) == c) {
+ beforeStar = false;
+ back_i = i + 1;
+ back_j = j;
+ j++;
+ }
+ } else {
+ if (c != '?' && c != str.charAt(i)) {
+ result = false;
+ if (back_i != 0) {// 有通配符,但是匹配未成功,回溯
+ beforeStar = true;
+ i = back_i;
+ j = back_j;
+ back_i = 0;
+ back_j = 0;
+ continue;
+ }
+ break;
+ }
+ j++;
+ }
+ i++;
+ }
+
+ if (i == str.length() && j == pattern.length())// 全部遍历完毕
+ result = true;
+ return result;
+ }
+
+ //填充变量
+ public static String myReplaceStrEx(String express, String b, String e, IStrHanlder hanlder) {
+ if (null == express) return express;
+ int keyBegin = 0, keyEnd = 0;
+ int lb = b.length(), le = e.length();
+ String fn;
+ while (true) {
+ keyBegin = express.indexOf(b, keyBegin);
+ if (keyBegin < 0) break;
+ keyEnd = express.indexOf(e, keyBegin);
+ if (keyEnd <= keyBegin) break;
+ keyBegin++;
+ fn = express.substring(keyBegin + lb - 1, keyEnd);
+ fn = hanlder.work(fn);
+ if (fn != null) {
+ express = express.substring(0, keyBegin - 1) + fn + express.substring(keyEnd + le);
+ }
+ }
+ return express;
+ }
+
+ //填充变量
+ public static String myReplaceStrEx(String express, String b, String e, final Map mapVals) {
+ return myReplaceStrEx(express, b, e, new IStrHanlder() {
+ @Override
+ public String work(String src) {
+ return PubUtil.checkNull(mapVals.get(src));
+ }
+ });
+ }
+
+ /*Blob转String*/
+// public static String blob2Str(Blob blob) {
+// if (blob == null) return "";
+// ByteArrayOutputStream outStream = null;
+// try {
+// long len = blob.length();
+// if (len == 0L) return "";
+// byte[] bytes;
+// long i = 1L;
+// outStream = new ByteArrayOutputStream();
+// while (i < len) {
+// bytes = blob.getBytes(i, 1024);
+// i += 1024L;
+// outStream.write(bytes);
+// }
+//
+// return UtilEncode.base64EncodeB(outStream.toByteArray());
+// } catch (Exception e) {
+// e.printStackTrace();
+// return "";
+// } finally {
+// IOUtils.closeQuietly(outStream);
+// }
+// }
+
+
+ public interface IStrHanlder {
+ String work(String src);
+ }
+
+ public static int[] splitStr(String src, String ch) {
+ String[] ss = src.split(ch);
+ int[] ret = new int[ss.length];
+ for (int i = 0, len = ss.length; i < len; i++) {
+ ret[i] = PubUtil.getIntIgnoreErr(ss[i]);
+ }
+ return ret;
+ }
+
+ public static void main(String[] args) {
+ String s = "a[#[#123#]bcde[aaa]bcd";
+ s = UtilString.myReplaceStrEx(s, "[#", "#]", new UtilString.IStrHanlder() {
+ @Override
+ public String work(String src) {
+ if (src.equals("123")) return "1";
+ return null;
+ }
+ });
+ System.out.println(s);
+ }
+}
diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/FtpTask.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/FtpTask.java
new file mode 100644
index 0000000..b80e141
--- /dev/null
+++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/FtpTask.java
@@ -0,0 +1,6 @@
+package cc.smtweb.system.bpm.util.ftp;
+
+//FTP执行接口
+public interface FtpTask {
+ Object execute(IFtpUtil ftp) throws Exception;
+}
diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/FtpTaskManager.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/FtpTaskManager.java
new file mode 100644
index 0000000..706fb62
--- /dev/null
+++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/FtpTaskManager.java
@@ -0,0 +1,255 @@
+package cc.smtweb.system.bpm.util.ftp;
+
+
+
+import cc.smtweb.framework.core.util.DateUtil;
+import cc.smtweb.framework.core.util.PubUtil;
+import cc.smtweb.system.bpm.util.UtilLogger;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+//ftp管理类
+public class FtpTaskManager {
+ private static Map mapInstance = null;
+
+ static {
+ mapInstance = new HashMap<>();
+ }
+
+ //多线程
+ private Map mapThreadFtp = new HashMap<>();
+
+ public static FtpTaskManager getInstance(String key, IFtpServerConfig ftpConfig) {
+ key = key + "_" + (ftpConfig.isSFTP() ? 1 : 0) + ftpConfig.getServerIp() + "_" + ftpConfig.getPort() + "_" + ftpConfig.getUserName();
+ FtpTaskManager instance = mapInstance.get(key);
+ if (instance == null) {
+ synchronized (FtpTaskManager.class) {
+ instance = mapInstance.get(key);
+ if (instance == null) {
+ FtpTaskManager inst = new FtpTaskManager();
+ inst.init(ftpConfig);
+ instance = inst;
+ mapInstance.put(key, instance);
+ }
+ }
+ }
+ return instance;
+ }
+
+ private IFtpServerConfig ftpConfig = null;
+
+ public FtpTaskManager() {
+ }
+
+ //初始化配置信息
+ public void init(IFtpServerConfig ftpConfig) {
+ this.ftpConfig = ftpConfig;
+ }
+
+ //获取一个ftp客户端
+ public IFtpUtil getFtpUtil() {
+ IFtpUtil ftp = mapThreadFtp.get(Thread.currentThread());
+ if (ftp != null) return ftp;
+
+ ftp = ftpConfig.isSFTP() ? new SFtpUtil() : new FtpUtil();
+ boolean isConnect = ftp.login(ftpConfig);
+ if (isConnect) {
+ UtilLogger.info("FTP连接成功!");
+ } else {
+ UtilLogger.info("FTP连接失败!" + ftpConfig.getServerIp() + " " + ftpConfig.getPort() + " " + ftpConfig.getUserName() + " " + ftpConfig.getPassword());
+ }
+ return ftp;
+ }
+
+ //获取并加到线程中备用
+ public IFtpUtil getThreadFtpUtil() {
+ IFtpUtil ftp = getFtpUtil();
+ ftp.setAutoFree(false);
+ mapThreadFtp.put(Thread.currentThread(), ftp);
+ return ftp;
+ }
+
+ //释放,和getThreadFtpUtil配对
+ public void dispose() {
+ IFtpUtil ftp = mapThreadFtp.remove(Thread.currentThread());
+ if (ftp == null) return;
+ ftp.setAutoFree(true);
+ ftp.disconnect();
+ }
+
+ /**
+ * 直接执行一个Ftp任务
+ */
+ public Object execute(FtpTask ftpTask) throws Exception {
+ IFtpUtil ftp = getFtpUtil();
+ if (!ftp.isConnected())
+ throw new Exception("FTP连接失败(" + ftpConfig.getServerIp() + "/" + ftpConfig.getUserName() + "/" + (ftpConfig.isSFTP() ? "sftp" : "ftp") + ")。");
+ try {
+ return ftpTask.execute(ftp);
+ } finally {
+ ftp.disconnect();
+ }
+ }
+
+ //上传文件到Ftp服务器。传完后,根据设置删除本地文件
+ public void uploadToFtp(final String remoteAttachName,
+ final String remoteFolderPath, final String localAttach,
+ final boolean isDelLocal) throws Exception {
+ uploadToFtp(remoteAttachName, remoteFolderPath, new File(localAttach), isDelLocal);
+ }
+
+ public void uploadToFtp(final String remoteAttachName,
+ final String remoteFolderPath, final File localFile,
+ final boolean isDelLocal) throws Exception {
+ this.execute(new FtpTask() {
+ public Object execute(IFtpUtil ftp) throws Exception {
+ if (ftp.uploadFile(remoteFolderPath, remoteAttachName, localFile)) {
+ if (isDelLocal) {//删除本地服务器的相应附件
+ localFile.delete();
+ }
+ }
+ return null;
+ }
+ });
+ }
+
+ private String getBakPath(String path) {
+ path = PubUtil.isEmpty(path) ? DateUtil.getNowYm() : path;
+ return "bak/" + path + "/";
+ }
+
+ //上传文件到Ftp服务器。传完后,根据设置删除本地文件
+ public void downloadFile(final String remoteAttachName,
+ final String remoteFolderPath, final String localFile, final boolean isDelLocal) throws Exception {
+ this.execute(new FtpTask() {
+ public Object execute(IFtpUtil ftp) throws Exception {
+ if (!ftp.existsFile(remoteFolderPath, remoteAttachName)) {
+ throw new Exception("服务器文件【" + remoteFolderPath + "/" + remoteAttachName + "】不存在!");
+ }
+ ftp.downloadFileEx(remoteFolderPath, remoteAttachName, localFile);
+ if (isDelLocal) {
+ ftp.setRemoteDir("/");
+ final String bakPath = getBakPath(null);
+ ftp.mkdir(remoteFolderPath + "/" + bakPath);
+ ftp.rename(remoteFolderPath, remoteAttachName, bakPath + remoteAttachName);
+ }
+ return null;
+ }
+ });
+ }
+
+ public void delAndBakFile(String remoteFolderPath, String remoteAttachName) throws Exception {
+ delAndBakFile(remoteFolderPath, remoteAttachName, null);
+
+ }
+
+ public void delAndBakFile(String remoteFolderPath, String remoteAttachName, String bakPath) throws Exception {
+ this.execute(new FtpTask() {
+ public Object execute(IFtpUtil ftp) throws Exception {
+ if (!ftp.existsFile(remoteFolderPath, remoteAttachName)) return null;
+
+ String path = getBakPath(bakPath);
+ ftp.setRemoteDir("/");
+ ftp.mkdir(remoteFolderPath + "/" + path);
+ ftp.rename(remoteFolderPath, remoteAttachName, path + remoteAttachName);
+
+ return null;
+ }
+ });
+ }
+ public void delFile(String delPath, String fileName) throws Exception {
+ this.execute(new FtpTask() {
+ public Object execute(IFtpUtil ftp) throws Exception {
+ if (!ftp.existsFile(delPath, fileName)) return null;
+ ftp.deleteFile(delPath,fileName );
+ return null;
+ }
+ });
+ }
+ //从服务器下载文件到本地。传完后,根据设置删除远程目录文件
+ public String[] listFileNames(final String remotePath) throws Exception {
+ IFtpUtil ftp = getFtpUtil();
+ if (!ftp.isConnected()) return new String[0];
+ String[] fileNames;
+ try {
+ fileNames = ftp.listFileNames(remotePath);
+ } finally {
+ ftp.disconnect();
+ }
+ return fileNames;
+ }
+
+ public String[] downFolder(final String remotePath, final String localPath, final boolean isDelLocal, final IDownloadFileWorker worker, final String fileNamePrefix) throws Exception {
+ final String[] files = listFileNames(remotePath);
+ List list = new ArrayList<>();
+ this.execute(new FtpTask() {
+ public Object execute(IFtpUtil ftp) throws Exception {
+ for (String file : files) {
+ if (worker != null && !worker.isDownload(file, fileNamePrefix)) continue;
+ if (PubUtil.isNotEmptyId(fileNamePrefix) && !file.startsWith(fileNamePrefix)) continue;
+ ftp.downloadFileEx(remotePath, file, localPath + "/" + file);
+ if (isDelLocal) {
+ ftp.setRemoteDir("/");
+ final String bakPath = getBakPath(null);
+ ftp.mkdir(remotePath + "/" + bakPath);
+ ftp.rename(remotePath, file, bakPath + file);
+ }
+ list.add(file);
+ }
+ return null;
+ }
+ });
+ return list.toArray(new String[list.size()]);
+ }
+
+ public static void main(String[] args) throws Exception {
+ FtpTaskManager ftp = FtpTaskManager.getInstance("test", new IFtpServerConfig() {
+ @Override
+ public boolean isSFTP() {
+ return false;
+ }
+
+ @Override
+ public String getServerIp() {
+ return "127.0.0.1";
+ }
+
+ @Override
+ public Integer getPort() {
+ return 21;
+ }
+
+ @Override
+ public String getUserName() {
+ return "demo";
+ }
+
+ @Override
+ public String getPassword() {
+ return "1";
+ }
+ });
+ ftp.uploadToFtp("S_2.jpg", "/", "f:\\temp\\tmp\\S_2.jpg", false);
+// ftp.uploadToFtp("民族5.png", "/", "f:\\temp\\temp\\民族5.png", false);
+// ftp.getThreadFtpUtil();
+// ftp.uploadToFtp("民族1.png", "/", "f:\\temp\\temp\\民族1.png", false);
+
+// ftp.uploadToFtp("民族2.png", "/", "f:\\temp\\temp\\民族2.png", false);
+// ftp.uploadToFtp("民族3.png", "/", "f:\\temp\\temp\\民族3.png", false);
+// ftp.downloadFile("民族2.png", "/", "f:/temp/民族2.png", true);
+ /*String[] ss = ftp.downFolder("/", "f:/temp/test", true);
+ for (String s: ss) {
+ System.out.println(s);
+ }*/
+
+ ftp.downloadFile("S_1.jpg", "/", "f:/temp/S_21.jpg", true);
+// ftp.downloadFile("freesshd.log", "/", "f:/temp/freesshd.log", false);
+ //ftp.downFolder("data/11", "f:/temp/11", true, "");
+
+ ftp.dispose();
+ }
+}
diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/FtpUtil.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/FtpUtil.java
new file mode 100644
index 0000000..edeee96
--- /dev/null
+++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/FtpUtil.java
@@ -0,0 +1,393 @@
+package cc.smtweb.system.bpm.util.ftp;
+
+import cc.smtweb.framework.core.util.PubUtil;
+import cc.smtweb.system.bpm.util.UtilFile;
+import cc.smtweb.system.bpm.util.UtilLogger;
+import org.apache.commons.net.ftp.FTPClient;
+import org.apache.commons.net.ftp.FTPFile;
+import org.apache.commons.net.ftp.FTPReply;
+
+import java.io.*;
+import java.net.UnknownHostException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 一个ftp的封装类
+ */
+@SuppressWarnings("UnusedDeclaration")
+public class FtpUtil implements IFtpUtil {
+ private static final int LOGIN_REPEAT_TIMES = 1;
+ private FTPClient ftpClient;
+ private boolean autoFree = true;
+ private IFtpServerConfig ftpConf;
+
+ public FtpUtil() {
+ this.ftpClient = new FTPClient();
+ }
+
+ @Override
+ public boolean isAutoFree() {
+ return autoFree;
+ }
+
+ @Override
+ public void setAutoFree(boolean autoFree) {
+ this.autoFree = autoFree;
+ }
+
+ //登录ftp
+ @Override
+ public boolean login(IFtpServerConfig ftpConf) {
+ try {
+ this.ftpConf = ftpConf;
+ return login(ftpConf.getServerIp(), ftpConf.getPort(), ftpConf.getUserName(), ftpConf.getPassword());
+ } catch (Exception e) {
+ logError("ftp登录失败", e);
+ return false;
+ }
+ }
+
+ //退出ftp
+ @Override
+ public void logout() {
+ try {
+ if (ftpClient.isConnected()) {
+ ftpClient.logout();
+ }
+ } catch (IOException e) {
+ logError("ftp退出登录失败", e);
+ }
+ }
+
+ //是否连接正常
+ @Override
+ public boolean isConnected() {
+ if (!ftpClient.isConnected())
+ return false;
+ try {
+ return ftpClient.sendNoOp();
+ } catch (IOException e) {
+ logError("ftp测试连接失败", e);
+ return false;
+ }
+ }
+
+ //设置远程目录
+ @Override
+ public boolean setRemoteDir(String path) {
+ try {
+ String d = gbktoiso8859(path);
+ ftpClient.changeWorkingDirectory("/");
+ if (!ftpClient.changeWorkingDirectory(d)) {
+ String[] paths = d.split("/");
+ for (String pa : paths) {
+ if (!ftpClient.changeWorkingDirectory(pa)) {
+ ftpClient.mkd(pa);
+ ftpClient.changeWorkingDirectory(pa);
+ }
+ }
+ }
+ return true;
+ } catch (IOException e) {
+ logError("ftp设置远程目录失败", e);
+ return false;
+ }
+ }
+
+ /**
+ * 创建指定文件夹
+ *
+ * @param dirName dirName
+ */
+ public void mkdir(String dirName) {
+ setRemoteDir("/");
+ dirName = gbktoiso8859(dirName);
+
+ try {
+ ftpClient.makeDirectory(dirName);
+ } catch (Exception e) {
+ logError("ftp创建远程目录错误", e);
+ }
+ }
+
+ //删除远程路径
+ @Override
+ public void deleteFolder(String path) {
+ try {
+ String d = gbktoiso8859(path);
+ ftpClient.removeDirectory(d);
+ } catch (IOException e) {
+ logError("ftp删除远程目录失败", e);
+ }
+ }
+
+ /**
+ * 从ftp下载文件到本地
+ *
+ * @param remotePath 远程路径
+ * @param fileName 文件名
+ * @param localPath 本地路径(不带文件名)
+ */
+ @Override
+ public void downloadFile(String remotePath, String fileName, String localPath) throws Exception {
+ downloadFileEx(remotePath, fileName, localPath + File.separator + fileName);
+ }
+
+ public void setPassiveNatWorkaroundStrategy() {
+ ftpClient.setPassiveNatWorkaroundStrategy(new FTPClient.HostnameResolver() {
+ @Override
+ public String resolve(String hostname) throws UnknownHostException {
+ return ftpConf.getServerIp();
+ }
+ });
+ }
+
+ /**
+ * 下载文件到本地
+ *
+ * @param remotePath 远程路径
+ * @param fileName 远程文件名
+ * @param localFileName 本地完全文件名(路径+文件名)
+ */
+ @Override
+ public void downloadFileEx(String remotePath, String fileName, String localFileName) throws Exception {
+ setPassiveNatWorkaroundStrategy();
+ ftpClient.enterLocalPassiveMode();
+ setRemoteDir(remotePath);//转移到FTP服务器目录
+ UtilLogger.info("跳转到FTP服务目录:" + remotePath);
+ File localFile = new File(localFileName);
+ if (!localFile.exists()) UtilFile.CreateFile(localFileName);
+ FileOutputStream is = null;
+ try {
+ is = new FileOutputStream(localFile);
+ ftpClient.retrieveFile(gbktoiso8859(fileName), is);
+ } catch (IOException e) {
+ localFile.deleteOnExit();
+ } finally {
+ is.close();
+ }
+ }
+
+ /**
+ * 下载文件成流,使用此方法需要注意,关闭流之后要调用completePendingCommand方法
+ *
+ * @param remotePath 远程路径
+ * @param fileName 远程文件名
+ * @return 文件流
+ */
+ @Override
+ public InputStream downloadFile(String remotePath, String fileName) throws Exception {
+ try {
+ setPassiveNatWorkaroundStrategy();
+ ftpClient.enterLocalPassiveMode();
+ setRemoteDir(remotePath);//转移到FTP服务器目录
+ return ftpClient.retrieveFileStream(gbktoiso8859(fileName));
+ } catch (IOException e) {
+ logError("ftp下载文件失败", e);
+ return null;
+ }
+ }
+
+ /**
+ * 上传文件到ftp
+ *
+ * @param path 远程路径
+ * @param filename 远程文件名
+ * @param localFileName 本地文件(含路径)
+ * @return
+ */
+ @Override
+ public boolean uploadFile(String path, String filename, String localFileName) throws Exception {
+ return uploadFile(path, filename, new File(localFileName));
+ }
+
+ @Override
+ public boolean uploadFile(String path, String filename, File localFile) throws Exception {
+ FileInputStream in = null;
+ try {
+ setPassiveNatWorkaroundStrategy();
+ ftpClient.enterLocalPassiveMode();
+ in = new FileInputStream(localFile);
+ setRemoteDir(path);
+ boolean b = ftpClient.storeFile(gbktoiso8859(filename), in);
+ if (!b) throw new Exception("FTP上传失败,请检查服务器配置!");
+ return b;
+ } finally {
+ if (in != null) in.close();
+ }
+ }
+
+ /**
+ * 删除远程文件
+ *
+ * @param path 远程路径
+ * @param filename 远程文件名
+ */
+ @Override
+ public void deleteFile(String path, String filename) {
+ try {
+ setPassiveNatWorkaroundStrategy();
+ setRemoteDir(path);
+ ftpClient.dele(gbktoiso8859(filename));
+ } catch (IOException e) {
+ logError("删除文件失败", e);
+ }
+ }
+
+ @Override
+ public boolean existsFile(String path, String filename) {
+ try {
+ setPassiveNatWorkaroundStrategy();
+ ftpClient.enterLocalPassiveMode();//设置被动模式
+ setRemoteDir(path);
+ String[] files = ftpClient.listNames(gbktoiso8859(filename));
+ return PubUtil.isNotEmpty(files);
+ } catch (IOException e) {
+ logError("判断文件是否存在失败", e);
+ return false;
+ }
+ }
+
+ @Override
+ public boolean rename(String path, String from, String to) {
+ try {
+ setPassiveNatWorkaroundStrategy();
+ setRemoteDir(path);
+ return ftpClient.rename(gbktoiso8859(from), gbktoiso8859(to));
+ } catch (IOException e) {
+ logError("重命名文件失败", e);
+ return false;
+ }
+ }
+
+ //调用返回流的方法,在处理完流后,要调用此方法。
+ @Override
+ public boolean completePendingCommand() {
+ try {
+ setPassiveNatWorkaroundStrategy();
+ return ftpClient.completePendingCommand();
+ } catch (IOException e) {
+ UtilLogger.error("ftp关闭流出错", e);
+ return false;
+ }
+ }
+
+ //断开连接
+ public FTPFile[] listFiles(String remotePath) throws IOException {
+ setPassiveNatWorkaroundStrategy();
+ return ftpClient.listFiles(remotePath);
+ }
+
+ //断开连接
+ @Override
+ public String[] listFileNames(String remotePath) throws Exception {
+ setPassiveNatWorkaroundStrategy();
+ setRemoteDir("/");
+ setRemoteDir(remotePath);
+ ftpClient.enterLocalPassiveMode();//设置被动模式
+ String[] files = ftpClient.listNames("");
+ List list = new ArrayList<>();
+ for (String s : files) {
+ if (!ftpClient.changeWorkingDirectory(s)) {
+ s = new String(s.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
+ list.add(s);
+ } else {
+ ftpClient.changeToParentDirectory();
+ }
+ }
+ return list.toArray(new String[list.size()]);
+ }
+
+ //断开连接
+ @Override
+ public String[] listAllFileNames(String remotePath) throws Exception {
+ setPassiveNatWorkaroundStrategy();
+ setRemoteDir("/");
+ setRemoteDir(remotePath);
+ ftpClient.enterLocalPassiveMode();//设置被动模式
+ String[] files = ftpClient.listNames("");
+ List list = new ArrayList<>();
+ for (String s : files) {
+ s = new String(s.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
+ list.add(s);
+ }
+ return list.toArray(new String[list.size()]);
+ }
+
+
+ //断开连接
+ @Override
+ public void disconnect() {
+ if (isAutoFree() && ftpClient.isConnected()) {
+ try {
+ setPassiveNatWorkaroundStrategy();
+ ftpClient.disconnect();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
+ //登录
+ protected boolean login(String server, int port, String user, String password) throws Exception {
+ for (int i = 0; i < LOGIN_REPEAT_TIMES; i++) {
+ ftpClient.setConnectTimeout(10000);
+ //if (!ftpClient.isConnected()) {
+ connect(server, port);
+ if (!ftpClient.isConnected()) continue;
+ //}
+ if (ftpClient.login(user, password)) {
+ ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
+ ftpClient.setDataTimeout(120000);
+ return true;
+ }
+ ftpClient.logout();
+ Thread.sleep(100L);
+ }
+ disconnect();
+ return false;
+ }
+
+ //连接
+ private void connect(String server, int port) throws Exception {
+ disconnect();
+ ftpClient.setDefaultPort(port);
+ ftpClient.connect(server);
+ int reply = ftpClient.getReplyCode();
+ if (!FTPReply.isPositiveCompletion(reply)) {
+ ftpClient.disconnect();
+ throw new Exception("服务器拒绝连接。");
+ }
+ }
+
+ private void logError(String msg, Exception e) {
+ UtilLogger.debug(msg + " " + e.getMessage());
+ }
+
+ //编码转换
+ private static String gbktoiso8859(String gbk) {
+ try {
+ if (gbk == null)
+ return "";
+ else //iso-8859-1
+ return new String(gbk.getBytes("GBK"), StandardCharsets.ISO_8859_1);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return "";
+ }
+ }
+
+ //编码转换
+ private static String iso8859toutf8(String gbk) {
+ try {
+ if (gbk == null)
+ return "";
+ else //iso-8859-1
+ return new String(gbk.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return "";
+ }
+ }
+}
diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/IDownloadFileWorker.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/IDownloadFileWorker.java
new file mode 100644
index 0000000..5a86260
--- /dev/null
+++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/IDownloadFileWorker.java
@@ -0,0 +1,6 @@
+package cc.smtweb.system.bpm.util.ftp;
+
+public interface IDownloadFileWorker {
+
+ boolean isDownload(String file, String fileNamePrefix);
+}
diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/IFtpServerConfig.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/IFtpServerConfig.java
new file mode 100644
index 0000000..28a2ec8
--- /dev/null
+++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/IFtpServerConfig.java
@@ -0,0 +1,16 @@
+package cc.smtweb.system.bpm.util.ftp;
+
+/**
+ * Created by AKmm at 2009-5-21 16:41:25
+ * ftp服务器参数配置接口
+ */
+public interface IFtpServerConfig {
+ boolean isSFTP();
+ String getServerIp();
+
+ Integer getPort();
+
+ String getUserName();
+
+ String getPassword();
+}
diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/IFtpUtil.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/IFtpUtil.java
new file mode 100644
index 0000000..cfd4459
--- /dev/null
+++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/IFtpUtil.java
@@ -0,0 +1,59 @@
+package cc.smtweb.system.bpm.util.ftp;
+
+import java.io.File;
+import java.io.InputStream;
+
+/**
+ * Created by Akmm at 2015-12-08 14:33
+ */
+public interface IFtpUtil {
+ //是否自动删除
+ boolean isAutoFree();
+ //设置自动删除
+ void setAutoFree(boolean autoFree);
+ //登录ftp
+ boolean login(IFtpServerConfig ftpConf);
+ //退出ftp
+ void logout();
+
+ //是否连接正常
+ boolean isConnected();
+
+ //设置远程目录
+ boolean setRemoteDir(String path);
+ void mkdir(String dirName);
+
+ //删除远程路径
+ void deleteFolder(String path);
+
+ void downloadFile(String remotePath, String fileName, String localPath) throws Exception;
+
+ void downloadFileEx(String remotePath, String fileName, String localFileName) throws Exception;
+
+ InputStream downloadFile(String remotePath, String fileName) throws Exception;
+
+ boolean uploadFile(String path, String filename, String localFileName) throws Exception;
+
+ boolean uploadFile(String path, String filename, File localFile) throws Exception;
+
+ void deleteFile(String path, String filename);
+ //判断文件是否存在
+ boolean existsFile(String path, String filename);
+
+ //调用返回流的方法,在处理完流后,要调用此方法。
+ boolean completePendingCommand();
+
+ //断开连接
+// FTPFile[] listFiles(String remotePath) throws IOException;
+
+ //枚举文件名
+ String[] listFileNames(String remotePath) throws Exception;
+
+ //枚举文件名,包含文件夹
+ String[] listAllFileNames(String remotePath) throws Exception;
+
+ //断开连接
+ void disconnect();
+
+ boolean rename(String remotePath, String from, String to) throws Exception;
+}
diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/SFtpUtil.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/SFtpUtil.java
new file mode 100644
index 0000000..cc1873f
--- /dev/null
+++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/util/ftp/SFtpUtil.java
@@ -0,0 +1,396 @@
+package cc.smtweb.system.bpm.util.ftp;
+
+import cc.smtweb.framework.core.util.PubUtil;
+import cc.smtweb.system.bpm.util.UtilFile;
+import cc.smtweb.system.bpm.util.UtilLogger;
+import com.jcraft.jsch.*;
+import org.apache.commons.io.IOUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+
+/**
+ * 一个sftp的封装类
+ */
+@SuppressWarnings("UnusedDeclaration")
+public class SFtpUtil implements IFtpUtil {
+ private static final int LOGIN_REPEAT_TIMES = 1;
+ private ChannelSftp sftp;
+ private boolean autoFree = true;
+
+ public SFtpUtil() {
+ this.sftp = null;
+ }
+
+ @Override
+ public boolean isAutoFree() {
+ return autoFree;
+ }
+
+ @Override
+ public void setAutoFree(boolean autoFree) {
+ this.autoFree = autoFree;
+ }
+
+ //登录ftp
+ @Override
+ public boolean login(IFtpServerConfig ftpConf) {
+ try {
+ int count = 0;
+ while (count < 3) {
+ // 设置超时时间为 5s
+ count++;
+ try {
+ JSch jsch = new JSch();
+ jsch.getSession(ftpConf.getUserName(), ftpConf.getServerIp(), ftpConf.getPort());
+ Session sshSession = jsch.getSession(ftpConf.getUserName(), ftpConf.getServerIp(), ftpConf.getPort());
+ System.out.println("Session created.");
+ sshSession.setPassword(ftpConf.getPassword());
+ Properties sshConfig = new Properties();
+ sshConfig.put("StrictHostKeyChecking", "no");
+ sshSession.setConfig(sshConfig);
+ sshSession.setTimeout(10 * 1000);
+ sshSession.connect();
+ UtilLogger.debug("Session connected[" + count + "/3].");
+ UtilLogger.debug("Opening Channel[" + count + "/3].");
+ Channel channel = sshSession.openChannel("sftp");
+ channel.connect();
+ sftp = (ChannelSftp) channel;
+ UtilLogger.debug("已连接到 " + ftpConf.getServerIp() + ";");
+ return true;
+ } catch (Exception e) {
+ UtilLogger.debug("connect fail[" + count + "/3]:" + PubUtil.getOrigMsg(e));
+ }
+ }
+ UtilLogger.debug("sftp登录失败:3次连接超时...");
+ return false;
+ } catch (Exception e) {
+ logError("sftp登录失败", e);
+ return false;
+ }
+ }
+
+ //退出ftp
+ @Override
+ public void logout() {
+ disconnect();
+ }
+
+ //是否连接正常
+ @Override
+ public boolean isConnected() {
+ return sftp != null && sftp.isConnected();
+ /*try {
+ return sftpClient.sendSignal();
+ } catch (IOException e) {
+ logError("ftp测试连接失败", e);
+ return false;
+ }*/
+ }
+
+ //设置远程目录
+ @Override
+ public boolean setRemoteDir(String path) {
+ String d = path;
+ try {
+ sftp.cd("/");
+ sftp.cd(d);
+ return true;
+ } catch (Exception e) {
+ logError("sftp进远程目录失败[" + d + "],准备创建", e);
+ }
+
+ mkdir(d);
+ try {
+ sftp.cd(d);
+ return true;
+ } catch (Exception e) {
+ logError("sftp设置远程目录失败[" + d + "]", e);
+ return false;
+ }
+ }
+
+
+ private boolean openRemoteDir(String path) {
+ String d = path;
+ try {
+ sftp.cd(d);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ /**
+ * 创建指定文件夹
+ *
+ * @param dirName dirName
+ */
+ public void mkdir(String dirName) {
+ String[] dirs = dirName.split("/");
+ try {
+ String now = sftp.pwd();
+ for (int i = 0; i < dirs.length; i++) {
+ if (PubUtil.isEmpty(dirs[i])) continue;
+ boolean dirExists = openRemoteDir(dirs[i]);
+ if (!dirExists) {
+ sftp.mkdir(dirs[i]);
+ sftp.cd(dirs[i]);
+ }
+ }
+ sftp.cd(now);
+ } catch (SftpException e) {
+ logError("sftp创建远程目录错误", e);
+ }
+ }
+
+ //删除远程路径
+ @Override
+ public void deleteFolder(String path) {
+ try {
+ String d = path;
+ sftp.rmdir(d);
+ } catch (Exception e) {
+ logError("sftp删除远程目录失败", e);
+ }
+ }
+
+ /**
+ * 从ftp下载文件到本地
+ *
+ * @param remotePath 远程路径
+ * @param fileName 文件名
+ * @param localPath 本地路径(不带文件名)
+ */
+ @Override
+ public void downloadFile(String remotePath, String fileName, String localPath) throws Exception {
+ downloadFileEx(remotePath, fileName, localPath + File.separator + fileName);
+ }
+
+ public boolean isFileExists(String filePath, String fileName) throws Exception {
+ Vector vector = sftp.ls(filePath);
+ Iterator sftpFileNames = vector.iterator();
+ while (sftpFileNames.hasNext()) {
+ String name = sftpFileNames.next().getFilename();
+ if (name.equals(fileName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 下载文件到本地
+ *
+ * @param remotePath 远程路径
+ * @param fileName 远程文件名
+ * @param localFileName 本地完全文件名(路径+文件名)
+ */
+ @Override
+ public void downloadFileEx(String remotePath, String fileName, String localFileName) throws Exception {
+ if (!isFileExists(remotePath, fileName)) {
+ throw new Exception();
+ }
+ File file = null;
+ file = new File(localFileName);
+ if (!file.exists()) UtilFile.CreateFile(localFileName);
+ FileOutputStream output = null;
+
+ try {
+ String now = sftp.pwd();
+ sftp.cd(remotePath);
+ UtilLogger.info("跳转到SFTP服务目录:" + remotePath);
+
+ output = new FileOutputStream(file);
+ sftp.get(fileName, output);
+ sftp.cd(now);
+ } catch (Exception e) {
+ file.deleteOnExit();
+ throw e;
+ } finally {
+ IOUtils.closeQuietly(output);
+ }
+ }
+
+ /**
+ * 下载文件成流,使用此方法需要注意,关闭流之后要调用completePendingCommand方法
+ *
+ * @param remotePath 远程路径
+ * @param fileName 远程文件名
+ * @return 文件流
+ */
+ @Override
+ public InputStream downloadFile(String remotePath, String fileName) throws Exception {
+ String now = sftp.pwd();
+ sftp.cd(remotePath);
+ //setRemoteDir(remotePath);//转移到FTP服务器目录 暂时注释 拷贝错误
+ InputStream is = sftp.get(fileName);
+ sftp.cd(now);
+ return is;
+
+ }
+
+ /**
+ * 上传文件到ftp
+ *
+ * @param path 远程路径
+ * @param filename 远程文件名
+ * @param localFileName 本地文件(含路径)
+ * @return
+ */
+ @Override
+ public boolean uploadFile(String path, String filename, String localFileName) throws Exception {
+ return uploadFile(path, filename, new File(localFileName));
+ }
+
+ @Override
+ public boolean uploadFile(String path, String filename, File localFile) throws Exception {
+ FileInputStream is = null;
+ try {
+ String now = sftp.pwd();
+ setRemoteDir(path);
+ is = new FileInputStream(localFile);
+ try {
+ sftp.put(is, filename);
+ } finally {
+ IOUtils.closeQuietly(is);
+ }
+ sftp.cd(now);
+ if (!existsFile(path, filename)) {
+ throw new Exception("SFTP上传失败,请检查服务器配置!");
+ }
+ return true;
+ } finally {
+ if (is != null) is.close();
+ }
+ }
+
+ /**
+ * 删除远程文件
+ *
+ * @param path 远程路径
+ * @param filename 远程文件名
+ */
+ @Override
+ public void deleteFile(String path, String filename) {
+ try {
+ String now = sftp.pwd();
+ setRemoteDir(path);
+ sftp.rm(filename);
+ sftp.cd(now);
+ } catch (Exception e) {
+ logError("删除文件失败", e);
+ }
+ }
+
+ @Override
+ public boolean rename(String path, String from, String to) throws Exception {
+ try {
+ String now = sftp.pwd();
+ setRemoteDir(path);
+ sftp.rename(from, to);
+ sftp.cd(now);
+ return true;
+ } catch (Exception e) {
+ logError("重命名文件失败", e);
+ return false;
+ }
+ }
+
+ //调用返回流的方法,在处理完流后,要调用此方法。
+ @Override
+ public boolean completePendingCommand() {
+ return true;
+ }
+
+ //断开连接
+ @Override
+ public String[] listFileNames(String remotePath) throws Exception {
+ List list = new ArrayList<>();
+ Vector sftpFile = sftp.ls(remotePath);
+ ChannelSftp.LsEntry isEntity = null;
+ String fileName = null;
+ Iterator sftpFileNames = sftpFile.iterator();
+ while (sftpFileNames.hasNext()) {
+ isEntity = sftpFileNames.next();
+ if (!isEntity.getAttrs().isDir()) {
+ fileName = isEntity.getFilename();
+
+ list.add(fileName);
+ }
+ }
+ return list.toArray(new String[list.size()]);
+ }
+
+ //断开连接
+ @Override
+ public String[] listAllFileNames(String remotePath) throws Exception {
+ List list = new ArrayList<>();
+ Vector sftpFile = sftp.ls(remotePath);
+ ChannelSftp.LsEntry isEntity = null;
+ String fileName = null;
+ Iterator sftpFileNames = sftpFile.iterator();
+ while (sftpFileNames.hasNext()) {
+ isEntity = sftpFileNames.next();
+ fileName = isEntity.getFilename();
+ list.add(fileName);
+ }
+ return list.toArray(new String[list.size()]);
+ }
+
+
+ //断开连接
+ @Override
+ public void disconnect() {
+ if (sftp != null && isAutoFree() && sftp.isConnected()) {
+ try {
+ if (sftp.getSession() != null) {
+ UtilLogger.debug("关闭连接 " + sftp.getSession().getHost() + ";");
+ sftp.getSession().disconnect();
+ UtilLogger.debug("Session disconnect.");
+ }
+ } catch (JSchException e) {
+ logError("关闭失败!", e);
+ }
+ sftp.quit();
+ sftp.disconnect();
+ UtilLogger.debug("Channel closed.");
+ sftp = null;
+ }
+ }
+
+ private void logError(String msg, Exception e) {
+ UtilLogger.debug(msg + ":" + e.getMessage());
+ }
+
+ //编码转换
+ private static String gbktoiso8859(String gbk) {
+ try {
+ if (gbk == null)
+ return "";
+ else //iso-8859-1
+ return new String(gbk.getBytes("GBK"), StandardCharsets.ISO_8859_1);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return "";
+ }
+ }
+
+ @Override
+ public boolean existsFile(String path, String filename) {
+ try {
+ String now = sftp.pwd();
+ setRemoteDir(path);
+ Vector files = sftp.ls(filename);
+ sftp.cd(now);
+ return PubUtil.isNotEmpty(files);
+ } catch (SftpException e) {
+ logError("判断文件是否存在失败", e);
+ return false;
+ }
+ }
+}
diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/base/attach/AttachInfo.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/base/attach/AttachInfo.java
index 83c79ec..421d27a 100644
--- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/base/attach/AttachInfo.java
+++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/base/attach/AttachInfo.java
@@ -62,12 +62,12 @@ public class AttachInfo extends DefaultEntity {
put("attach_path", attach_path);
}
/** 附件类别 */
- public int getType() {
- return getInt("attach_type");
+ public String getType() {
+ return getStr("attach_type");
}
/** 附件类别 */
- public void setType(int attach_type) {
+ public void setType(String attach_type) {
put("attach_type", attach_type);
}
/** 附件大小 */
@@ -115,4 +115,14 @@ public class AttachInfo extends DefaultEntity {
public void setUserId(long attach_user_id) {
put("attach_user_id", attach_user_id);
}
+
+ /** 下载次数 */
+ public int getCount() {
+ return getInt("attach_count");
+ }
+
+ /** 下载次数 */
+ public void setCount(int attach_count) {
+ put("attach_count", attach_count);
+ }
}
diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/base/attachFolder/pojo/AttachFolderPojo.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/base/attachFolder/pojo/AttachFolderPojo.java
new file mode 100644
index 0000000..a0b9187
--- /dev/null
+++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/base/attachFolder/pojo/AttachFolderPojo.java
@@ -0,0 +1,16 @@
+package cc.smtweb.system.bpm.web.sys.base.attachFolder.pojo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class AttachFolderPojo implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private Long folderId;//
+ private String folderAttachPath;//
+ private String folderPeriod;//
+ private int folderMaxSeq;//
+ private Long folderFileCount;//
+}
+
diff --git a/smtweb-framework/bpm/src/main/resources/config/application.yaml b/smtweb-framework/bpm/src/main/resources/config/application.yaml
index be65cfd..5c15030 100644
--- a/smtweb-framework/bpm/src/main/resources/config/application.yaml
+++ b/smtweb-framework/bpm/src/main/resources/config/application.yaml
@@ -3,6 +3,22 @@ smtweb:
file:
local-path: /data/sw/files/
url: http://127.0.0.1:8888/sw/files/
+ #访问路径,需/结尾
+ attach-http-path: 'http://bjjt.jujiatech.cn/files/'
+ #此配置原则上可以和local-path一致,可本机路径可ftp附件路径,需/结尾
+ attach-path: /jjkj/attach/
+ #临时文件路径,需/结尾
+ attach-temp-path: /jujia/tomcat_api/webapps/files/tempFile/
+ #附件上传方式 sftp/ftp/local
+ attach-type: sftp
+ #ftp IP地址
+ attach-ftp-ip: 172.26.60.191
+ #ftp 端口
+ attach-ftp-port: 22
+ #ftp 用户名
+ attach-ftp-user: gzhfile
+ #ftp 密码
+ attach-ftp-pwd: Bjjt@2021
bpm:
debug: true
# 有.idea或pom.xml文件的目录
diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/dao/AbstractEntityDao.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/dao/AbstractEntityDao.java
index 6980fb4..79f15cb 100644
--- a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/dao/AbstractEntityDao.java
+++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/dao/AbstractEntityDao.java
@@ -14,6 +14,8 @@ import cc.smtweb.framework.core.exception.DbException;
import cc.smtweb.framework.core.util.DateUtil;
import cc.smtweb.framework.core.util.SpringUtil;
import cc.smtweb.framework.core.util.VariableUtil;
+import com.esotericsoftware.minlog.Log;
+import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ClassUtils;
@@ -31,6 +33,7 @@ import java.util.Map;
*
* @param 数据库值对象类型
*/
+@Slf4j
public abstract class AbstractEntityDao {
protected ModelTable modelTable;
protected String tableName;
@@ -62,6 +65,9 @@ public abstract class AbstractEntityDao {
tableName = table.value();
modelTable = ModelTableCache.getInstance().getByName(tableName);
+ if(null == modelTable){
+ log.error("未找到缓存表结构:"+tableName);
+ }
if (DefaultEntity.class.isAssignableFrom(type)) {
for (ModelField field : modelTable.getFields()) {
add(field, null, null);
diff --git a/smtweb-framework/pom.xml b/smtweb-framework/pom.xml
index 38a69dc..99edd0c 100644
--- a/smtweb-framework/pom.xml
+++ b/smtweb-framework/pom.xml
@@ -27,5 +27,6 @@
core
bpm
+ unit