瀏覽代碼

中网小程序微信国际(香港)支付common模块提交

hyq 6 年之前
父節點
當前提交
66f57d7a8b
共有 100 個文件被更改,包括 14005 次插入0 次删除
  1. 14 0
      kmall-common/kmall-common.iml
  2. 170 0
      kmall-common/pom.xml
  3. 36 0
      kmall-common/src/main/java/com/kmall/common/Global.java
  4. 96 0
      kmall-common/src/main/java/com/kmall/common/MysqlTest.java
  5. 133 0
      kmall-common/src/main/java/com/kmall/common/advice/CustomDateEditor.java
  6. 45 0
      kmall-common/src/main/java/com/kmall/common/advice/CustomSqlDateEditor.java
  7. 41 0
      kmall-common/src/main/java/com/kmall/common/advice/CustomTimestampEditor.java
  8. 18 0
      kmall-common/src/main/java/com/kmall/common/annotation/SysLog.java
  9. 319 0
      kmall-common/src/main/java/com/kmall/common/cache/RegionCacheUtil.java
  10. 818 0
      kmall-common/src/main/java/com/kmall/common/constant/Dict.java
  11. 11 0
      kmall-common/src/main/java/com/kmall/common/constant/JxlsXmlTemplateName.java
  12. 15 0
      kmall-common/src/main/java/com/kmall/common/constant/TablePrimaryKeyPrefix.java
  13. 29 0
      kmall-common/src/main/java/com/kmall/common/controller/AbstractController.java
  14. 98 0
      kmall-common/src/main/java/com/kmall/common/controller/SysLoginController.java
  15. 205 0
      kmall-common/src/main/java/com/kmall/common/controller/SysMenuController.java
  16. 136 0
      kmall-common/src/main/java/com/kmall/common/controller/SysRoleController.java
  17. 153 0
      kmall-common/src/main/java/com/kmall/common/controller/SysUserController.java
  18. 40 0
      kmall-common/src/main/java/com/kmall/common/dao/BaseDao.java
  19. 20 0
      kmall-common/src/main/java/com/kmall/common/dao/FormIdsDao.java
  20. 25 0
      kmall-common/src/main/java/com/kmall/common/dao/SysConfigDao.java
  21. 14 0
      kmall-common/src/main/java/com/kmall/common/dao/SysLogDao.java
  22. 32 0
      kmall-common/src/main/java/com/kmall/common/dao/SysMenuDao.java
  23. 12 0
      kmall-common/src/main/java/com/kmall/common/dao/SysRegionDao.java
  24. 20 0
      kmall-common/src/main/java/com/kmall/common/dao/SysRoleDao.java
  25. 30 0
      kmall-common/src/main/java/com/kmall/common/dao/SysRoleDeptDao.java
  26. 20 0
      kmall-common/src/main/java/com/kmall/common/dao/SysRoleMenuDao.java
  27. 16 0
      kmall-common/src/main/java/com/kmall/common/dao/SysSmsLogDao.java
  28. 38 0
      kmall-common/src/main/java/com/kmall/common/dao/SysUserDao.java
  29. 22 0
      kmall-common/src/main/java/com/kmall/common/dao/SysUserRoleDao.java
  30. 18 0
      kmall-common/src/main/java/com/kmall/common/dao/TemplateConfDao.java
  31. 117 0
      kmall-common/src/main/java/com/kmall/common/entity/FormIdsEntity.java
  32. 42 0
      kmall-common/src/main/java/com/kmall/common/entity/SmsConfig.java
  33. 52 0
      kmall-common/src/main/java/com/kmall/common/entity/SysConfigEntity.java
  34. 128 0
      kmall-common/src/main/java/com/kmall/common/entity/SysLogEntity.java
  35. 190 0
      kmall-common/src/main/java/com/kmall/common/entity/SysMenuEntity.java
  36. 75 0
      kmall-common/src/main/java/com/kmall/common/entity/SysRegionEntity.java
  37. 82 0
      kmall-common/src/main/java/com/kmall/common/entity/SysRoleDeptEntity.java
  38. 162 0
      kmall-common/src/main/java/com/kmall/common/entity/SysRoleEntity.java
  39. 82 0
      kmall-common/src/main/java/com/kmall/common/entity/SysRoleMenuEntity.java
  40. 313 0
      kmall-common/src/main/java/com/kmall/common/entity/SysSmsLogEntity.java
  41. 286 0
      kmall-common/src/main/java/com/kmall/common/entity/SysUserEntity.java
  42. 82 0
      kmall-common/src/main/java/com/kmall/common/entity/SysUserRoleEntity.java
  43. 88 0
      kmall-common/src/main/java/com/kmall/common/entity/TemplateConfVo.java
  44. 166 0
      kmall-common/src/main/java/com/kmall/common/entity/Tree.java
  45. 70 0
      kmall-common/src/main/java/com/kmall/common/exception/Exceptions.java
  46. 429 0
      kmall-common/src/main/java/com/kmall/common/fileserver/common/Base64.java
  47. 216 0
      kmall-common/src/main/java/com/kmall/common/fileserver/common/IniFileReader.java
  48. 24 0
      kmall-common/src/main/java/com/kmall/common/fileserver/common/MyException.java
  49. 48 0
      kmall-common/src/main/java/com/kmall/common/fileserver/common/NameValuePair.java
  50. 304 0
      kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/ClientGlobal.java
  51. 27 0
      kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/DownloadCallback.java
  52. 44 0
      kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/DownloadStream.java
  53. 125 0
      kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/FileInfo.java
  54. 503 0
      kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/ProtoCommon.java
  55. 48 0
      kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/ProtoStructDecoder.java
  56. 66 0
      kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/ServerInfo.java
  57. 1786 0
      kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/StorageClient.java
  58. 732 0
      kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/StorageClient1.java
  59. 57 0
      kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/StorageServer.java
  60. 73 0
      kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/StructBase.java
  61. 225 0
      kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/StructGroupStat.java
  62. 982 0
      kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/StructStorageStat.java
  63. 811 0
      kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/TrackerClient.java
  64. 106 0
      kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/TrackerGroup.java
  65. 81 0
      kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/TrackerServer.java
  66. 28 0
      kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/UploadCallback.java
  67. 55 0
      kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/UploadStream.java
  68. 62 0
      kmall-common/src/main/java/com/kmall/common/fileserver/util/FastDFSFile.java
  69. 168 0
      kmall-common/src/main/java/com/kmall/common/fileserver/util/FileManager.java
  70. 123 0
      kmall-common/src/main/java/com/kmall/common/interceptor/LogInterceptor.java
  71. 32 0
      kmall-common/src/main/java/com/kmall/common/log4j/GradeLogDailyRollingFileAppender.java
  72. 54 0
      kmall-common/src/main/java/com/kmall/common/oss/AliyunCloudStorageService.java
  73. 235 0
      kmall-common/src/main/java/com/kmall/common/oss/CloudStorageConfig.java
  74. 69 0
      kmall-common/src/main/java/com/kmall/common/oss/CloudStorageService.java
  75. 37 0
      kmall-common/src/main/java/com/kmall/common/oss/OSSFactory.java
  76. 80 0
      kmall-common/src/main/java/com/kmall/common/oss/QcloudCloudStorageService.java
  77. 69 0
      kmall-common/src/main/java/com/kmall/common/oss/QiniuCloudStorageService.java
  78. 213 0
      kmall-common/src/main/java/com/kmall/common/security/JedisCacheManager.java
  79. 204 0
      kmall-common/src/main/java/com/kmall/common/security/SessionManager.java
  80. 46 0
      kmall-common/src/main/java/com/kmall/common/security/UserAuthenticationFilter.java
  81. 105 0
      kmall-common/src/main/java/com/kmall/common/security/session/JedisSessionDAO.java
  82. 39 0
      kmall-common/src/main/java/com/kmall/common/service/FormIdsService.java
  83. 65 0
      kmall-common/src/main/java/com/kmall/common/service/SysConfigService.java
  84. 30 0
      kmall-common/src/main/java/com/kmall/common/service/SysLogService.java
  85. 71 0
      kmall-common/src/main/java/com/kmall/common/service/SysMenuService.java
  86. 30 0
      kmall-common/src/main/java/com/kmall/common/service/SysRegionService.java
  87. 35 0
      kmall-common/src/main/java/com/kmall/common/service/SysRoleDeptService.java
  88. 24 0
      kmall-common/src/main/java/com/kmall/common/service/SysRoleMenuService.java
  89. 36 0
      kmall-common/src/main/java/com/kmall/common/service/SysRoleService.java
  90. 81 0
      kmall-common/src/main/java/com/kmall/common/service/SysSmsLogService.java
  91. 23 0
      kmall-common/src/main/java/com/kmall/common/service/SysUserRoleService.java
  92. 76 0
      kmall-common/src/main/java/com/kmall/common/service/SysUserService.java
  93. 96 0
      kmall-common/src/main/java/com/kmall/common/service/impl/FormIdsServiceImpl.java
  94. 77 0
      kmall-common/src/main/java/com/kmall/common/service/impl/SysConfigServiceImpl.java
  95. 53 0
      kmall-common/src/main/java/com/kmall/common/service/impl/SysLogServiceImpl.java
  96. 123 0
      kmall-common/src/main/java/com/kmall/common/service/impl/SysMenuServiceImpl.java
  97. 53 0
      kmall-common/src/main/java/com/kmall/common/service/impl/SysRegionServiceImpl.java
  98. 53 0
      kmall-common/src/main/java/com/kmall/common/service/impl/SysRoleDeptServiceImpl.java
  99. 52 0
      kmall-common/src/main/java/com/kmall/common/service/impl/SysRoleMenuServiceImpl.java
  100. 142 0
      kmall-common/src/main/java/com/kmall/common/service/impl/SysRoleServiceImpl.java

+ 14 - 0
kmall-common/kmall-common.iml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_5">
+    <output url="file://$MODULE_DIR$/target/classes" />
+    <output-test url="file://$MODULE_DIR$/target/test-classes" />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+      <excludeFolder url="file://$MODULE_DIR$/target" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 170 - 0
kmall-common/pom.xml

@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>kmall-pt-global</artifactId>
+        <groupId>com.kmall</groupId>
+        <version>3.1.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>kmall-common</artifactId>
+    <packaging>jar</packaging>
+    <description>公共模块</description>
+
+
+    <properties>
+        <qiniu-version>[7.2.0, 7.2.99]</qiniu-version>
+        <aliyun-oss-version>2.5.0</aliyun-oss-version>
+        <qcloud-cos-version>4.4</qcloud-cos-version>
+        <jackson.version>2.5.0</jackson.version>
+        <poi.version>3.15</poi.version>
+        <xmlbeans.version>2.6.0</xmlbeans.version>
+        <guava.version>17.0</guava.version>
+        <bouncycastle.version>1.45</bouncycastle.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.qiniu</groupId>
+            <artifactId>qiniu-java-sdk</artifactId>
+            <version>${qiniu-version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun.oss</groupId>
+            <artifactId>aliyun-sdk-oss</artifactId>
+            <version>${aliyun-oss-version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.qcloud</groupId>
+            <artifactId>cos_api</artifactId>
+            <version>${qcloud-cos-version}</version>
+        </dependency>
+
+        <!-- httpclient -->
+        <dependency>
+            <groupId>commons-httpclient</groupId>
+            <artifactId>commons-httpclient</artifactId>
+            <version>3.1</version>
+        </dependency>
+
+        <!-- json -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>dom4j</groupId>
+            <artifactId>dom4j</artifactId>
+            <version>1.6.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>jstl</groupId>
+            <artifactId>jstl</artifactId>
+            <version>1.2</version>
+        </dependency>
+
+        <!-- POI依赖 ,处理EXCEL WORD  PDF�?-->
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+            <version>${poi.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>${poi.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml-schemas</artifactId>
+            <version>${poi.version}</version>
+            <exclusions>
+                <exclusion>
+                    <artifactId>stax-api</artifactId>
+                    <groupId>stax</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>${poi.version}</version>
+        </dependency>
+
+        <!-- redis clients -->
+        <dependency>
+            <groupId>redis.clients</groupId>
+            <artifactId>jedis</artifactId>
+            <version>2.8.2</version>
+        </dependency>
+
+        <!-- google java lib -->
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>${guava.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.3.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jxls</groupId>
+            <artifactId>jxls</artifactId>
+            <version>2.4.7</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jxls</groupId>
+            <artifactId>jxls-poi</artifactId>
+            <version>1.0.16</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/org.jxls/jxls-jexcel -->
+        <dependency>
+            <groupId>org.jxls</groupId>
+            <artifactId>jxls-jexcel</artifactId>
+            <version>1.0.7</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/org.jxls/jxls-reader -->
+        <dependency>
+            <groupId>org.jxls</groupId>
+            <artifactId>jxls-reader</artifactId>
+            <version>2.0.5</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.apache.xmlbeans</groupId>
+            <artifactId>xmlbeans</artifactId>
+            <version>${xmlbeans.version}</version>
+            <exclusions>
+                <exclusion>
+                    <artifactId>stax-api</artifactId>
+                    <groupId>stax</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <!-- POI依赖 ,处理EXCEL WORD  PDF�?-->
+    </dependencies>
+
+</project>

+ 36 - 0
kmall-common/src/main/java/com/kmall/common/Global.java

@@ -0,0 +1,36 @@
+/*
+ * 创建时间:2017-08-06 12:30
+ * 项目名称:kmall_pt
+ * 类名称:Global.java
+ * 包名称:com.kmall.common
+ */
+package com.kmall.common;
+
+/**
+ * 名称:Global <br>
+ * 描述:全局属性<br>
+ *
+ * @author Scott
+ * @version 1.0
+ * @since 1.0.0
+ */
+public class Global {
+    /**
+     * 排序方式名称 asc:正序 | desc:倒序
+     */
+    public static final String SORT_ORDER = "sortOrder";
+    /**
+     * 当前登录用户
+     */
+    public static final String CURRENT_USER = "curUser";
+    /**
+     * 默认密码
+     */
+    public static final String DEFAULT_PASS_WORD = "111111";
+
+    /**
+     * 是/否
+     */
+    public static final String YES = "1";
+    public static final String NO = "0";
+}

+ 96 - 0
kmall-common/src/main/java/com/kmall/common/MysqlTest.java

@@ -0,0 +1,96 @@
+package com.kmall.common;
+/*
+ * 创建时间:2017-08-29 19:35
+ * 项目名称:common_utils
+ * 类名称:UpdatePassWord.java
+ * 包名称:com.joyintech
+ *
+ * 修改履历:
+ *          日期              修正者        主要内容
+ *                                      
+ *
+ * Copyright (c) 2016-2017 兆尹科技
+ */
+
+import java.sql.*;
+
+/**
+ * 名称:UpdatePassWord <br>
+ * 描述:批量更新密码<br>
+ * 注意事项:执行此类会把所有的用户(SYS_USER)密码初始化为a111111
+ *
+ * @author Scott
+ * @version 1.0
+ * @since 1.0.0
+ */
+public class MysqlTest {
+
+    /**
+     * 数据库url
+     */
+    private static final String URL = "jdbc:mysql://45.248.68.244:3306/platform_security";
+    /**
+     * 用户名
+     */
+    private static final String USER = "root";
+    /**
+     * 密码
+     */
+    private static final String PASSWORD = "mysql123456";
+    /**
+     * mysql驱动程序
+     */
+    private static final String ORACLEDRIVER = "com.mysql.jdbc.Driver";
+
+    /**
+     * 启动程序
+     *
+     * @param args
+     */
+    public static void main(String[] args) {
+        Connection con = null;
+        PreparedStatement pre = null;
+        ResultSet result = null;
+        try {
+            Class.forName(ORACLEDRIVER);
+            con = DriverManager.getConnection(URL, USER, PASSWORD);
+            System.out.println("连接成功!");
+
+            String sql = "select * from mall_user";
+            pre = con.prepareStatement(sql);
+            result = pre.executeQuery();//执行查询
+
+            while (result.next()) {
+                // 当结果集不为空时
+                String username = result.getString("username");
+                String mobile = result.getString("mobile");
+                System.out.println("会员:"+username + ";手机号:" +mobile);
+            }
+        } catch (ClassNotFoundException e) {
+            //数据库驱动类异常处理
+            System.out.println("Sorry,can`t find the Driver!");
+            e.printStackTrace();
+        } catch (SQLException e) {
+            //数据库连接失败异常处理
+            e.printStackTrace();
+        } catch (Exception e) {
+            // TODO: handle exception
+            e.printStackTrace();
+        } finally {
+            try {
+                if (result != null) {
+                    result.close();
+                }
+                if (pre != null) {
+                    pre.close();
+                }
+                if (con != null) {
+                    con.close();
+                }
+                System.out.println("数据库连接已关闭!");
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+}

+ 133 - 0
kmall-common/src/main/java/com/kmall/common/advice/CustomDateEditor.java

@@ -0,0 +1,133 @@
+package com.kmall.common.advice;
+
+import com.kmall.common.utils.DateUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.util.StringUtils;
+
+import java.beans.PropertyEditorSupport;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * 作者: @author Scott <br>
+ * 时间: 2016-09-12 17:02<br>
+ * 描述: CustomDateEditor <br>
+ */
+public class CustomDateEditor extends PropertyEditorSupport {
+
+    private static final Log logger = LogFactory.getLog(CustomDateEditor.class);
+
+    private final DateFormat dateFormat;
+
+    private final boolean allowEmpty;
+
+    // private final int exactDateLength;
+
+    /**
+     * Create a new CustomDateEditor instance, using the given DateFormat for
+     * parsing and rendering.
+     * <p/>
+     * The "allowEmpty" parameter states if an empty String should be allowed
+     * for parsing, i.e. get interpreted as null value. Otherwise, an
+     * IllegalArgumentException gets thrown in that case.
+     *
+     * @param dateFormat DateFormat to use for parsing and rendering
+     * @param allowEmpty if empty strings should be allowed
+     */
+    public CustomDateEditor(DateFormat dateFormat, boolean allowEmpty) {
+        this.dateFormat = dateFormat;
+        this.allowEmpty = allowEmpty;
+        // this.exactDateLength = -1;
+    }
+
+    /**
+     * 默认构造方法 使用标准时间格式 "yyyy-MM-dd HH:mm:ss"; <br>
+     * 前后台统一
+     */
+    public CustomDateEditor() {
+
+        this.dateFormat = new SimpleDateFormat(DateUtils.DATE_TIME_PATTERN);
+        this.allowEmpty = true;
+        // this.exactDateLength = -1;
+
+    }
+
+    /**
+     * Create a new CustomDateEditor instance, using the given DateFormat for
+     * parsing and rendering.
+     * <p/>
+     * The "allowEmpty" parameter states if an empty String should be allowed
+     * for parsing, i.e. get interpreted as null value. Otherwise, an
+     * IllegalArgumentException gets thrown in that case.
+     * <p/>
+     * The "exactDateLength" parameter states that IllegalArgumentException gets
+     * thrown if the String does not exactly match the length specified. This is
+     * useful because SimpleDateFormat does not enforce strict parsing of the
+     * year part, not even with {@code setLenient(false)}. Without an
+     * "exactDateLength" specified, the "01/01/05" would get parsed to
+     * "01/01/0005". However, even with an "exactDateLength" specified,
+     * prepended zeros in the day or month part may still allow for a shorter
+     * year part, so consider this as just one more assertion that gets you
+     * closer to the intended date format.
+     *
+     * @param dateFormat      DateFormat to use for parsing and rendering
+     * @param allowEmpty      if empty strings should be allowed
+     * @param exactDateLength the exact expected length of the date String
+     */
+    public CustomDateEditor(DateFormat dateFormat, boolean allowEmpty, int exactDateLength) {
+        this.dateFormat = dateFormat;
+        this.allowEmpty = allowEmpty;
+        // this.exactDateLength = exactDateLength;
+    }
+
+    /**
+     * Parse the Date from the given text, using the specified DateFormat.
+     */
+    @Override
+    public void setAsText(String text) throws IllegalArgumentException {
+
+        logger.debug("*****转换日期字符串:" + text + "*****");
+        // 允许空值 时 ,要转换的字符串为空 直接返回空值
+        if (this.allowEmpty && !StringUtils.hasText(text)) {
+            // Treat empty String as null value.
+            setValue(null);
+
+            return;
+
+        }
+
+        // 尝试以默认格式转换日期,失败则自动尝试其他格式
+        // 如果无匹配,则抛出异常
+        try {
+            setValue(this.dateFormat.parse(text));
+        } catch (ParseException ex) {
+
+            try {
+                // 自定义日期类型
+                String style = DateUtils.getDateFormat(text);
+                if (!StringUtils.isEmpty(style)) {
+                    SimpleDateFormat df = new SimpleDateFormat(style); // 设定前后台的格式对应
+                    df.setLenient(false);
+
+                    setValue(df.parse(text));
+
+                }
+            } catch (ParseException e) {
+                logger.error("****** 日期类型转换错误,仅记录,必要时查询数据****");
+            }
+        }
+
+    }
+
+    /**
+     * Format the Date as String, using the specified DateFormat.
+     */
+    @Override
+    public String getAsText() {
+        Date value = (Date) getValue();
+        return (value != null ? this.dateFormat.format(value) : "");
+    }
+}

+ 45 - 0
kmall-common/src/main/java/com/kmall/common/advice/CustomSqlDateEditor.java

@@ -0,0 +1,45 @@
+package com.kmall.common.advice;
+
+import java.beans.PropertyEditorSupport;
+import java.text.SimpleDateFormat;
+
+/**
+ * 作者: @author Scott <br>
+ * 时间: 2016-09-12 17:00<br>
+ * 描述: 自定义 java.sql.Date 转换器 <br>
+ */
+public class CustomSqlDateEditor extends PropertyEditorSupport {
+    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); // 可以設定任意的日期格式
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.beans.PropertyEditorSupport#getAsText()
+     */
+    @Override
+    public String getAsText() {
+
+        java.sql.Date value = (java.sql.Date) getValue();
+        return (value != null ? this.dateFormat.format(value) : "");
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
+     */
+    @Override
+    public void setAsText(String text) throws IllegalArgumentException {
+
+        if (text == null || text == "") {
+            setValue(null);
+        } else {
+
+            java.sql.Date dt = java.sql.Date.valueOf(text);
+            setValue(dt);
+
+        }
+
+    }
+
+}

+ 41 - 0
kmall-common/src/main/java/com/kmall/common/advice/CustomTimestampEditor.java

@@ -0,0 +1,41 @@
+package com.kmall.common.advice;
+
+import java.beans.PropertyEditorSupport;
+import java.sql.Timestamp;
+
+/**
+ * 作者: @author Scott <br>
+ * 时间: 2016-09-12 17:01<br>
+ * 描述: 自定义 java.sql.Date 转换器 <br>
+ */
+public class CustomTimestampEditor  extends PropertyEditorSupport {
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.beans.PropertyEditorSupport#getAsText()
+     */
+    @Override
+    public String getAsText() {
+
+        Timestamp value = (Timestamp) getValue();
+        return (value != null) ? value.toString() : "";
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
+     */
+    @Override
+    public void setAsText(String text) throws IllegalArgumentException {
+
+        if (text == null || text == "") {
+            setValue(null);
+        } else {
+            setValue(new Timestamp(Long.valueOf(text)));
+        }
+
+    }
+
+}

+ 18 - 0
kmall-common/src/main/java/com/kmall/common/annotation/SysLog.java

@@ -0,0 +1,18 @@
+package com.kmall.common.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 系统日志注解
+ *
+ * @author Scott
+ * @email
+ * @date 2017年3月8日 上午10:19:56
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface SysLog {
+
+    String value() default "操作日志";
+}

+ 319 - 0
kmall-common/src/main/java/com/kmall/common/cache/RegionCacheUtil.java

@@ -0,0 +1,319 @@
+package com.kmall.common.cache;
+
+import com.kmall.common.dao.SysRegionDao;
+import com.kmall.common.entity.SysRegionEntity;
+import com.kmall.common.utils.SpringContextUtils;
+import org.springframework.beans.factory.InitializingBean;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * 作者: @author Scott <br>
+ * 时间: 2017-08-16 10:14<br>
+ * 描述: RegionCacheUtil <br>
+ */
+public class RegionCacheUtil implements InitializingBean {
+
+    public static List<SysRegionEntity> sysRegionEntityList;
+
+    public static void init() {
+        SysRegionDao regionDao = SpringContextUtils.getBean(SysRegionDao.class);
+        if (null != regionDao) {
+            sysRegionEntityList = regionDao.queryList(new HashMap<String, Object>());
+        }
+    }
+
+    /**
+     * 获取所有国家
+     *
+     * @return
+     */
+    public static List getAllCountry() {
+        List<SysRegionEntity> resultObj = new ArrayList();
+        if (null != sysRegionEntityList) {
+            for (SysRegionEntity areaVo : sysRegionEntityList) {
+                if (areaVo.getType().equals(0)) {
+                    resultObj.add(areaVo);
+                }
+            }
+        }
+        return resultObj;
+    }
+
+    /**
+     * 获取全部省份
+     *
+     * @return
+     */
+    public static List getAllProvice() {
+        List<SysRegionEntity> resultObj = new ArrayList();
+        if (null != sysRegionEntityList) {
+            for (SysRegionEntity areaVo : sysRegionEntityList) {
+                if (areaVo.getType().equals(1)) {
+                    resultObj.add(areaVo);
+                }
+            }
+        }
+        return resultObj;
+    }
+
+    /**
+     * 获取所有城市
+     *
+     * @return
+     */
+    public static List<SysRegionEntity> getAllCity() {
+        List<SysRegionEntity> resultObj = new ArrayList();
+        if (null != sysRegionEntityList) {
+            for (SysRegionEntity areaVo : sysRegionEntityList) {
+                if (areaVo.getType().equals(2)) {
+                    resultObj.add(areaVo);
+                }
+            }
+        }
+        return resultObj;
+    }
+
+    /**
+     * 根据国家获取全部省份
+     *
+     * @return
+     */
+    public static List getAllProviceByParentId(Integer areaId) {
+        List<SysRegionEntity> resultObj = new ArrayList();
+        if (null == areaId) {
+            return resultObj;
+        }
+        if (null != sysRegionEntityList) {
+            for (SysRegionEntity areaVo : sysRegionEntityList) {
+                if (null != areaVo.getParentId() && areaVo.getType().equals(1) && areaId.equals(areaVo.getParentId())) {
+                    resultObj.add(areaVo);
+                }
+            }
+        }
+        return resultObj;
+    }
+
+    /**
+     * 获取地市
+     *
+     * @return
+     */
+    public static List getChildrenCity(Integer areaId) {
+        List<SysRegionEntity> resultObj = new ArrayList();
+        if (null == areaId) {
+            return resultObj;
+        }
+        if (null != sysRegionEntityList) {
+            for (SysRegionEntity areaVo : sysRegionEntityList) {
+                if (null != areaVo.getParentId() && areaVo.getType().equals(2) && areaId.equals(areaVo.getParentId())) {
+                    resultObj.add(areaVo);
+                }
+            }
+        }
+        return resultObj;
+    }
+
+    /**
+     * 获取地市
+     *
+     * @return
+     */
+    public static List getChildrenCity(String proviceName) {
+        List<SysRegionEntity> resultObj = new ArrayList();
+        if (null == proviceName) {
+            return resultObj;
+        }
+        if (null != sysRegionEntityList) {
+            for (SysRegionEntity areaVo : sysRegionEntityList) {
+                if (null != areaVo.getParentId() && areaVo.getType().equals(2) && proviceName.equals(areaVo.getParentName())) {
+                    resultObj.add(areaVo);
+                }
+            }
+        }
+        return resultObj;
+    }
+
+    /**
+     * 获取区县
+     *
+     * @return
+     */
+    public static List<SysRegionEntity> getChildrenDistrict(Integer areaId) {
+        List<SysRegionEntity> resultObj = new ArrayList();
+        if (null == areaId) {
+            return resultObj;
+        }
+        if (null != sysRegionEntityList) {
+            for (SysRegionEntity areaVo : sysRegionEntityList) {
+                if (null != areaVo.getParentId() && areaVo.getType().equals(3) && areaId.equals(areaVo.getParentId())) {
+                    resultObj.add(areaVo);
+                }
+            }
+        }
+        return resultObj;
+    }
+
+    /**
+     * 获取区县
+     *
+     * @return
+     */
+    public static List<SysRegionEntity> getChildrenDistrict(String provinceName, String cityName) {
+        List<SysRegionEntity> resultObj = new ArrayList();
+        if (null == provinceName || null == cityName) {
+            return resultObj;
+        }
+        if (null != sysRegionEntityList) {
+            for (SysRegionEntity areaVo : sysRegionEntityList) {
+                if (null != areaVo.getParentId() && areaVo.getType().equals(3)
+                        && cityName.equals(areaVo.getParentName())
+                        && null != getAreaByAreaId(areaVo.getParentId())
+                        && provinceName.equals(getAreaByAreaId(areaVo.getParentId()).getParentName())) {
+                    resultObj.add(areaVo);
+                }
+            }
+        }
+        return resultObj;
+    }
+
+
+    /**
+     * 获取区县
+     *
+     * @return
+     */
+    public static List<SysRegionEntity> getChildrenByParentId(Integer parentId) {
+        List<SysRegionEntity> resultObj = new ArrayList();
+        if (null == parentId) {
+            return resultObj;
+        }
+        if (null != sysRegionEntityList) {
+            for (SysRegionEntity areaVo : sysRegionEntityList) {
+                if (null != areaVo.getParentId() && parentId.equals(areaVo.getParentId())) {
+                    resultObj.add(areaVo);
+                }
+            }
+        }
+        return resultObj;
+    }
+
+    /**
+     * 获取区域名称
+     *
+     * @return
+     */
+    public static String getAreaNameByAreaId(Integer areaId) {
+        if (null == areaId) {
+            return "";
+        }
+        String resultObj = areaId.toString();
+        if (null != sysRegionEntityList) {
+            for (SysRegionEntity areaVo : sysRegionEntityList) {
+                if (areaVo.getId().equals(areaId)) {
+                    resultObj = areaVo.getName();
+                    break;
+                }
+            }
+        }
+        return resultObj;
+    }
+
+    /**
+     * 根据Id获取区域
+     *
+     * @return
+     */
+    public static SysRegionEntity getAreaByAreaId(Integer areaId) {
+        SysRegionEntity resultObj = null;
+        if (null == areaId) {
+            return resultObj;
+        }
+        if (null != sysRegionEntityList) {
+            for (SysRegionEntity areaVo : sysRegionEntityList) {
+                if (areaVo.getId().equals(areaId)) {
+                    resultObj = areaVo;
+                    break;
+                }
+            }
+        }
+        return resultObj;
+    }
+
+    /**
+     * 根据Id获取区域
+     *
+     * @return
+     */
+    public static Integer getProvinceIdByName(String areaName) {
+        Integer resultObj = null;
+        if (null == areaName) {
+            return resultObj;
+        }
+        if (null != sysRegionEntityList) {
+            for (SysRegionEntity areaVo : sysRegionEntityList) {
+                if (areaVo.getType() == 1 && areaVo.getName().equals(areaName)) {
+                    resultObj = areaVo.getId();
+                    break;
+                }
+            }
+        }
+        return resultObj;
+    }
+
+    /**
+     * 根据Id获取区域
+     *
+     * @return
+     */
+    public static Integer getCityIdByName(Integer provinceId, String areaName) {
+        Integer resultObj = null;
+        if (null == areaName) {
+            return resultObj;
+        }
+        if (null != sysRegionEntityList) {
+            for (SysRegionEntity areaVo : sysRegionEntityList) {
+                if (areaVo.getType() == 2 && areaVo.getName().equals(areaName)
+                        && areaVo.getParentId().equals(provinceId)) {
+                    resultObj = areaVo.getId();
+                    break;
+                }
+            }
+        }
+        return resultObj;
+    }
+
+
+    /**
+     * 根据Id获取区域
+     *
+     * @return
+     */
+    public static Integer getDistrictIdByName(Integer provinceId, Integer cityId, String areaName) {
+        Integer resultObj = null;
+        if (null == areaName) {
+            return resultObj;
+        }
+        if (null != sysRegionEntityList) {
+            for (SysRegionEntity areaVo : sysRegionEntityList) {
+                if (areaVo.getType() == 3 && areaVo.getName().equals(areaName)
+                        && areaVo.getParentId().equals(cityId)
+                        && null != getAreaByAreaId(areaVo.getParentId())
+                        && null != getAreaByAreaId(areaVo.getParentId()).getParentId()
+                        && getAreaByAreaId(areaVo.getParentId()).getParentId().equals(provinceId)) {
+                    resultObj = areaVo.getId();
+                    break;
+                }
+            }
+        }
+        return resultObj;
+    }
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        init();
+    }
+
+}

+ 818 - 0
kmall-common/src/main/java/com/kmall/common/constant/Dict.java

@@ -0,0 +1,818 @@
+package com.kmall.common.constant;
+
+/**
+ * @author huangyq
+ * @version 1.0
+ * 2018-10-11 09:38:30
+ */
+public class Dict {
+
+    /**
+     * 支付方式,weixin:微信;alipay:支付宝;tenpay:QQ财付通
+     */
+    public enum payFlag {
+        item_weixin("weixin", "微信"),
+        item_tenpay("tenpay", "财付通"),
+        item_alipay("alipay", "支付宝"),
+        item_pingan("pingan", "平安支付"),
+        item_wxglobalpay("wxglobalpay", "微信国际支付"),
+        item_cash("cash", "现金");
+
+        private String item;
+        private String itemName;
+
+        payFlag(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    /**
+     * 订单业务类型:00:保税备货, 02:保税展示补货,10:保税展示跨境,11:普通商品
+     */
+    public enum orderBizType {
+        item_00("00", "保税备货"),
+        item_02("02", "保税展示补货"),
+        item_10("10", "保税展示跨境"),
+        item_11("11", "普通商品");
+
+        private String item;
+        private String itemName;
+
+        orderBizType(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    /**
+     * 商户返回核验结果,0:未知,1:一致,2:不一致
+     */
+    public enum merchReturnSubjectChecked {
+        item_0("0", "未知"),
+        item_1("1", "一致"),
+        item_2("2", "不一致");
+
+        private String item;
+        private String itemName;
+
+        merchReturnSubjectChecked(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    /**
+     * 平安返回核验结果,0:未知,1:一致,2:不一致
+     */
+    public enum pinganReturnSubjectChecked {
+        item_0("0", "未知"),
+        item_1("1", "一致"),
+        item_2("2", "不一致");
+
+        private String item;
+        private String itemName;
+
+        pinganReturnSubjectChecked(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    /**
+     * 校验结果状态 1:通过,2:不通过
+     */
+    public enum subjectChecked {
+        item_1("1", "通过"),
+        item_2("2", "不通过");
+
+        private String item;
+        private String itemName;
+
+        subjectChecked(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    /**
+     * 通知状态,0:待发送,1:发送中,2:成功,3:失败
+     */
+    public enum notiStatus {
+        item_0("0", "待发送"),
+        item_1("1", "发送中"),
+        item_2("2", "成功"),
+        item_3("3", "失败");
+
+        private String item;
+        private String itemName;
+
+        notiStatus(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    /**
+     * 是否停止通知,0:否,1:是
+     */
+    public enum isStop {
+        item_0("0", "否"),
+        item_1("1", "是");
+
+        private String item;
+        private String itemName;
+
+        isStop(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    /**
+     * 是否已使用
+     */
+    public enum isUsed {
+        item_0("0", "否"),
+        item_1("1", "是");
+
+        private String item;
+        private String itemName;
+
+        isUsed(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+
+    /**
+     * 是否已发送
+     */
+    public enum isSend {
+        item_0("0", "失败"),
+        item_1("1", "成功");
+
+        private String item;
+        private String itemName;
+
+        isSend(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    /**
+     * 物流状态:0-暂无轨迹信息 2-在途中,3-签收,4-问题件
+     */
+    public enum logisticsStatus {
+        item_0("0", "暂无轨迹信息"),
+        item_2("2", "在途中"),
+        item_3("3", "签收"),
+        item_4("4", "问题件");
+
+        private String item;
+        private String itemName;
+
+        logisticsStatus(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    /**
+     * 订单状态: 0 订单创建成功等待付款,100订单付款中, 101 订单已取消, 102 订单已删除,201 订单已付款,等待发货,300 订单已发货, 301 用户确认收货,400 维权申请中
+     * 401 没有发货,退款 402 已收货,退款退货 500 订单已关闭 501 支付失败
+     */
+    public enum orderStatus {
+        item_0("0", "订单创建成功等待付款"),
+        item_100("100", "订单付款中"),
+        item_101("101", "订单已取消"),
+        item_102("102", "订单已删除"),
+        item_201("201", "订单已付款"),
+        item_300("300", "订单已发货"),
+        item_301("301", "用户确认收货"),
+//        item_400("400", "维权申请中"),
+        item_401("401", "没有发货,退款"),
+        item_402("402", "已收货,退款退货"),
+        item_500("500", "订单已关闭"),
+        item_501("501", "支付失败");
+
+        private String item;
+        private String itemName;
+
+        orderStatus(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    /**
+     * 付款状态 支付状态;0 未付款;1 付款中;2 已付款;3 退款中;4 退款; 5:退款关闭;6:退款异常 7:订单关闭 8 支付失败
+     */
+    public enum payStatus {
+        item_0("0", "未付款"),
+        item_1("1", "付款中"),
+        item_2("2", "已付款"),
+        item_3("3", "退款中"),
+        item_4("4", "退款"),
+        item_5("5", "退款关闭"),
+        item_6("6", "退款异常"),
+        item_7("7", "订单关闭"),
+        item_8("8", "支付失败");
+
+        private String item;
+        private String itemName;
+
+        payStatus(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    /**
+     * 异常状态 00 订单支付失败 01 系统异常 02 订单超时 03 订单退款失败 04 其他
+     */
+    public enum exceptionStatus {
+        item_00("00", "订单支付失败"),
+        item_01("01", "系统异常"),
+        item_02("02", "订单超时"),
+        item_03("03", "订单退款失败"),
+        item_04("04", "其他");
+
+        private String item;
+        private String itemName;
+
+        exceptionStatus(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    /**
+     * 退款类型 1 用户全额退款 2 系统部分退款
+     */
+    public enum RefundType {
+        item_1("1", "用户全额退款"),
+        item_2("2", "系统部分退款");
+
+        private String item;
+        private String itemName;
+
+        RefundType(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    /**
+     * 退款状态 1 申请中 2 退款成功 3 已拒绝  4 微信退款失败 5 微信退款处理中
+     */
+    public enum RefundStatus {
+        item_1("1", "申请中"),
+        item_2("2", "退款成功"),
+        item_3("3", "已拒绝"),
+        item_4("4", "微信退款失败"),
+        item_5("5", "微信退款处理中");
+
+        private String item;
+        private String itemName;
+
+        RefundStatus(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    /**
+     * 商品分类级别 L1:一级分类 L2:二级分类
+     */
+    public enum Level {
+        item_L1("L1", "一级分类"),
+        item_L2("L2", "二级分类");
+
+        private String item;
+        private String itemName;
+
+        Level(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    /**
+     * 是否合并支付 0:单笔支付 1:合并支付
+     */
+    public enum isMergePay {
+        item_0("0", "单笔支付"),
+        item_1("1", "合并支付");
+
+        private String item;
+        private String itemName;
+
+        isMergePay(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    /**
+     * 是否线下订单:0:线上购买 1:线下购买
+     */
+    public enum isOnfflineOrder {
+        item_0("0", "线上购买"),
+        item_1("1", "线下购买");
+
+        private String item;
+        private String itemName;
+
+        isOnfflineOrder(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    /**
+     * 角色类型:1:操作员;2:店员;3:商户管理员
+     */
+    public enum roleType {
+        item_1("1", "操作员"),
+        item_2("2", "店员"),
+        item_3("3", "商户管理员");
+
+        private String item;
+        private String itemName;
+
+        roleType(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    public enum isDelete {
+        item_0("0", "未删除"),
+        item_1("1", "已删除");
+
+        private String item;
+        private String itemName;
+
+        isDelete(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    public enum isOnSale {
+        item_0("0", "下架"),
+        item_1("1", "上架");
+
+        private String item;
+        private String itemName;
+
+        isOnSale(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    /**
+     * SUCCESS—支付成功
+     * REFUND—转入退款
+     * NOTPAY—未支付
+     * CLOSED—已关闭
+     * REVOKED—已撤销(刷卡支付)
+     * USERPAYING--用户支付中
+     * PAYERROR--支付失败(其他原因,如银行返回失败)
+     */
+    public enum tradeState {
+        item_SUCCESS("SUCCESS", "支付成功"),
+        item_REFUND("REFUND", "转入退款"),
+        item_NOTPAY("NOTPAY", "未支付"),
+        item_CLOSED("CLOSED", "已关闭"),
+        item_REVOKED("REVOKED", "已撤销(刷卡支付)"),
+        item_USERPAYING("USERPAYING", "用户支付中"),
+        item_PAYERROR("PAYERROR", "支付失败(其他原因,如银行返回失败)");
+
+        private String item;
+        private String itemName;
+
+        tradeState(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    public enum pinganPayStatus {
+        item_1("1", "交易成功"),
+        item_2("2", "待支付"),
+        item_4("4", "已取消"),
+        item_9("9", "等待用户输入密码确认");
+
+        private String item;
+        private String itemName;
+
+        pinganPayStatus(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+
+    public enum pinganRefundStatus {
+        item_1("1", "退款成功"),
+        item_2("2", "退款处理中"),
+        item_3("3", "退款异常"),
+        item_4("4", "退款关闭");
+
+        private String item;
+        private String itemName;
+
+        pinganRefundStatus(String item, String itemName) {
+            this.item = item;
+            this.itemName = itemName;
+        }
+
+        public String getItem() {
+            return item;
+        }
+
+        public void setItem(String item) {
+            this.item = item;
+        }
+
+        public String getItemName() {
+            return itemName;
+        }
+
+        public void setItemName(String itemName) {
+            this.itemName = itemName;
+        }
+    }
+}

+ 11 - 0
kmall-common/src/main/java/com/kmall/common/constant/JxlsXmlTemplateName.java

@@ -0,0 +1,11 @@
+package com.kmall.common.constant;
+
+/**
+ *
+ */
+public class JxlsXmlTemplateName {
+    //商品信息导入
+    public static final String GOODS_DTO_LIST = "/XmlTemplate/GoodsDtoList.xml";
+    //普通商品导入
+    public static final String GENERAL_GOODS_DTO_LIST = "/XmlTemplate/GeneralGoodsDtoList.xml";
+}

+ 15 - 0
kmall-common/src/main/java/com/kmall/common/constant/TablePrimaryKeyPrefix.java

@@ -0,0 +1,15 @@
+package com.kmall.common.constant;
+
+/**
+ * 表主键前缀
+ *
+ * @author Scott Chen
+ * @version 1.0
+ * 2017-09-19 15:09
+ */
+public class TablePrimaryKeyPrefix {
+    //国别地区代码
+    public static final String sys_cus_nation_code = "scnc";
+    //计量单位代码
+    public static final String sys_cus_unit_code = "scuc";
+}

+ 29 - 0
kmall-common/src/main/java/com/kmall/common/controller/AbstractController.java

@@ -0,0 +1,29 @@
+package com.kmall.common.controller;
+
+import com.kmall.common.entity.SysUserEntity;
+import com.kmall.common.utils.ShiroUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Controller公共组件
+ *
+ * @author Scott
+ * @email
+ * @date 2016年11月9日 下午9:42:26
+ */
+public abstract class AbstractController {
+    protected Logger logger = LoggerFactory.getLogger(AbstractController.class);
+
+    protected SysUserEntity getUser() {
+        return ShiroUtils.getUserEntity();
+    }
+
+    protected Long getUserId() {
+        return getUser().getUserId();
+    }
+
+    protected Long getDeptId() {
+        return getUser().getDeptId();
+    }
+}

+ 98 - 0
kmall-common/src/main/java/com/kmall/common/controller/SysLoginController.java

@@ -0,0 +1,98 @@
+package com.kmall.common.controller;
+
+import com.google.code.kaptcha.Constants;
+import com.google.code.kaptcha.Producer;
+import com.kmall.common.annotation.SysLog;
+import com.kmall.common.utils.R;
+import com.kmall.common.utils.ShiroUtils;
+import org.apache.shiro.authc.*;
+import org.apache.shiro.crypto.hash.Sha256Hash;
+import org.apache.shiro.subject.Subject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import javax.imageio.ImageIO;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * 登录相关
+ *
+ * @author Scott
+ * @email
+ * @date 2016年11月10日 下午1:15:31
+ */
+@Controller
+public class SysLoginController {
+    @Autowired
+    private Producer producer;
+
+    @RequestMapping("captcha.jpg")
+    public void captcha(HttpServletResponse response) throws ServletException, IOException {
+        response.setHeader("Cache-Control", "no-store, no-cache");
+        response.setContentType("image/jpeg");
+
+        //生成文字验证码
+        String text = producer.createText();
+        //生成图片验证码
+        BufferedImage image = producer.createImage(text);
+        //保存到shiro session
+        ShiroUtils.setSessionAttribute(Constants.KAPTCHA_SESSION_KEY, text);
+
+        ServletOutputStream out = response.getOutputStream();
+        ImageIO.write(image, "jpg", out);
+    }
+
+    /**
+     * 登录
+     */
+    @SysLog("登录")
+    @ResponseBody
+    @RequestMapping(value = "/sys/login", method = RequestMethod.POST)
+    public R login(String username, String password, String captcha) throws IOException {
+        String kaptcha = ShiroUtils.getKaptcha(Constants.KAPTCHA_SESSION_KEY);
+        if (!captcha.equalsIgnoreCase(kaptcha)) {
+            return R.error("验证码不正确");
+        }
+
+        try {
+            Subject subject = ShiroUtils.getSubject();
+            //sha256加密
+            password = new Sha256Hash(password).toHex();
+            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
+            subject.login(token);
+        } catch (UnknownAccountException e) {
+            return R.error(e.getMessage());
+        } catch (IncorrectCredentialsException e) {
+            return R.error(e.getMessage());
+        } catch (LockedAccountException e) {
+            return R.error(e.getMessage());
+        } catch (AuthenticationException e) {
+            return R.error("账户验证失败");
+        }
+
+        Set<String> permsSet = new HashSet<>();
+        if (null != ShiroUtils.getUserEntity()) {
+            permsSet = ShiroUtils.getUserEntity().getPermsSet();
+        }
+        return R.ok().put("permsSet", permsSet);
+    }
+
+    /**
+     * 退出
+     */
+    @RequestMapping(value = "logout", method = RequestMethod.GET)
+    public String logout() {
+        ShiroUtils.logout();
+        return "redirect:/";
+    }
+
+}

+ 205 - 0
kmall-common/src/main/java/com/kmall/common/controller/SysMenuController.java

@@ -0,0 +1,205 @@
+package com.kmall.common.controller;
+
+import com.kmall.common.annotation.SysLog;
+import com.kmall.common.entity.SysMenuEntity;
+import com.kmall.common.service.SysMenuService;
+import com.kmall.common.utils.*;
+import org.apache.commons.lang.StringUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 系统菜单
+ *
+ * @author Scott
+ * @email
+ * @date 2016年10月27日 下午9:58:15
+ */
+@RestController
+@RequestMapping("/sys/menu")
+public class SysMenuController extends AbstractController {
+    @Autowired
+    private SysMenuService sysMenuService;
+
+    /**
+     * 所有菜单列表
+     */
+    @RequestMapping("/list")
+    @RequiresPermissions("sys:menu:list")
+    public R list(@RequestParam Map<String, Object> params) {
+        //查询列表数据
+        Query query = new Query(params);
+        List<SysMenuEntity> menuList = sysMenuService.queryList(query);
+        int total = sysMenuService.queryTotal(query);
+
+        PageUtils pageUtil = new PageUtils(menuList, total, query.getLimit(), query.getPage());
+
+        return R.ok().put("page", pageUtil);
+    }
+
+    /**
+     * 所有菜单列表
+     */
+    @RequestMapping("/queryAll")
+    public R queryAll(@RequestParam Map<String, Object> params) {
+        //查询列表数据
+        List<SysMenuEntity> menuList = sysMenuService.queryList(params);
+
+        return R.ok().put("list", menuList);
+    }
+
+    /**
+     * 选择菜单(添加、修改菜单)
+     */
+    @RequestMapping("/select")
+    @RequiresPermissions("sys:menu:select")
+    public R select() {
+        //查询列表数据
+        List<SysMenuEntity> menuList = sysMenuService.queryNotButtonList();
+
+        //添加顶级菜单
+        SysMenuEntity root = new SysMenuEntity();
+        root.setMenuId(0L);
+        root.setName("一级菜单");
+        root.setParentId(-1L);
+        root.setOpen(true);
+        menuList.add(root);
+
+        return R.ok().put("menuList", menuList);
+    }
+
+    /**
+     * 角色授权菜单
+     */
+    @RequestMapping("/perms")
+    @RequiresPermissions("sys:menu:perms")
+    public R perms() {
+        //查询列表数据
+        List<SysMenuEntity> menuList = null;
+
+        //只有超级管理员,才能查看所有管理员列表
+        if (getUserId() == Constant.SUPER_ADMIN) {
+            menuList = sysMenuService.queryList(new HashMap<String, Object>());
+        } else {
+            menuList = sysMenuService.queryUserList(getUserId());
+        }
+
+        return R.ok().put("menuList", menuList);
+    }
+
+    /**
+     * 菜单信息
+     */
+    @RequestMapping("/info/{menuId}")
+    @RequiresPermissions("sys:menu:info")
+    public R info(@PathVariable("menuId") Long menuId) {
+        SysMenuEntity menu = sysMenuService.queryObject(menuId);
+        return R.ok().put("menu", menu);
+    }
+
+    /**
+     * 保存
+     */
+    @SysLog("保存菜单")
+    @RequestMapping("/save")
+    @RequiresPermissions("sys:menu:save")
+    public R save(@RequestBody SysMenuEntity menu) {
+        //数据校验
+        verifyForm(menu);
+
+        sysMenuService.save(menu);
+
+        return R.ok();
+    }
+
+    /**
+     * 修改
+     */
+    @SysLog("修改菜单")
+    @RequestMapping("/update")
+    @RequiresPermissions("sys:menu:update")
+    public R update(@RequestBody SysMenuEntity menu) {
+        //数据校验
+        verifyForm(menu);
+
+        sysMenuService.update(menu);
+
+        return R.ok();
+    }
+
+    /**
+     * 删除
+     */
+    @SysLog("删除菜单")
+    @RequestMapping("/delete")
+    @RequiresPermissions("sys:menu:delete")
+    public R delete(@RequestBody Long[] menuIds) {
+        for (Long menuId : menuIds) {
+            if (menuId.longValue() <= 30) {
+                return R.error("系统菜单,不能删除");
+            }
+        }
+        sysMenuService.deleteBatch(menuIds);
+
+        return R.ok();
+    }
+
+    /**
+     * 用户菜单列表
+     */
+    @RequestMapping("/user")
+    public R user() {
+        List<SysMenuEntity> menuList = sysMenuService.getUserMenuList(getUserId());
+
+        return R.ok().put("menuList", menuList);
+    }
+
+    /**
+     * 验证参数是否正确
+     */
+    private void verifyForm(SysMenuEntity menu) {
+        if (StringUtils.isBlank(menu.getName())) {
+            throw new RRException("菜单名称不能为空");
+        }
+
+        if (menu.getParentId() == null) {
+            throw new RRException("上级菜单不能为空");
+        }
+
+        //菜单
+        if (menu.getType() == Constant.MenuType.MENU.getValue()) {
+            if (StringUtils.isBlank(menu.getUrl())) {
+                throw new RRException("菜单URL不能为空");
+            }
+        }
+
+        //上级菜单类型
+        int parentType = Constant.MenuType.CATALOG.getValue();
+        if (menu.getParentId() != 0) {
+            SysMenuEntity parentMenu = sysMenuService.queryObject(menu.getParentId());
+            parentType = parentMenu.getType();
+        }
+
+        //目录、菜单
+        if (menu.getType() == Constant.MenuType.CATALOG.getValue() ||
+                menu.getType() == Constant.MenuType.MENU.getValue()) {
+            if (parentType != Constant.MenuType.CATALOG.getValue()) {
+                throw new RRException("上级菜单只能为目录类型");
+            }
+            return;
+        }
+
+        //按钮
+        if (menu.getType() == Constant.MenuType.BUTTON.getValue()) {
+            if (parentType != Constant.MenuType.MENU.getValue()) {
+                throw new RRException("上级菜单只能为菜单类型");
+            }
+            return;
+        }
+    }
+}

+ 136 - 0
kmall-common/src/main/java/com/kmall/common/controller/SysRoleController.java

@@ -0,0 +1,136 @@
+package com.kmall.common.controller;
+
+import com.kmall.common.annotation.SysLog;
+import com.kmall.common.entity.SysRoleEntity;
+import com.kmall.common.service.SysRoleDeptService;
+import com.kmall.common.service.SysRoleMenuService;
+import com.kmall.common.service.SysRoleService;
+import com.kmall.common.utils.Constant;
+import com.kmall.common.utils.PageUtils;
+import com.kmall.common.utils.Query;
+import com.kmall.common.utils.R;
+import com.kmall.common.validator.ValidatorUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 角色管理
+ *
+ * @author Scott
+ * @email
+ * @date 2016年11月8日 下午2:18:33
+ */
+@RestController
+@RequestMapping("/sys/role")
+public class SysRoleController extends AbstractController {
+    @Autowired
+    private SysRoleService sysRoleService;
+    @Autowired
+    private SysRoleMenuService sysRoleMenuService;
+    @Autowired
+    private SysRoleDeptService sysRoleDeptService;
+
+    /**
+     * 角色列表
+     */
+    @RequestMapping("/list")
+    @RequiresPermissions("sys:role:list")
+    public R list(@RequestParam Map<String, Object> params) {
+        //如果不是超级管理员,则只查询自己创建的角色列表
+        if (getUserId() != Constant.SUPER_ADMIN) {
+            params.put("createUserId", getUserId());
+        }
+
+        //查询列表数据
+        Query query = new Query(params);
+        List<SysRoleEntity> list = sysRoleService.queryList(query);
+        int total = sysRoleService.queryTotal(query);
+
+        PageUtils pageUtil = new PageUtils(list, total, query.getLimit(), query.getPage());
+
+        return R.ok().put("page", pageUtil);
+    }
+
+    /**
+     * 角色列表
+     */
+    @RequestMapping("/select")
+    @RequiresPermissions("sys:role:select")
+    public R select() {
+        Map<String, Object> map = new HashMap<>();
+
+        //如果不是超级管理员,则只查询自己所拥有的角色列表
+        if (getUserId() != Constant.SUPER_ADMIN) {
+            map.put("createUserId", getUserId());
+        }
+        List<SysRoleEntity> list = sysRoleService.queryList(map);
+
+        return R.ok().put("list", list);
+    }
+
+    /**
+     * 角色信息
+     */
+    @RequestMapping("/info/{roleId}")
+    @RequiresPermissions("sys:role:info")
+    public R info(@PathVariable("roleId") Long roleId) {
+        SysRoleEntity role = sysRoleService.queryObject(roleId);
+
+        //查询角色对应的菜单
+        List<Long> menuIdList = sysRoleMenuService.queryMenuIdList(roleId);
+        role.setMenuIdList(menuIdList);
+
+        //查询角色对应的部门
+        List<Long> deptIdList = sysRoleDeptService.queryDeptIdList(roleId);
+        role.setDeptIdList(deptIdList);
+
+        return R.ok().put("role", role);
+    }
+
+    /**
+     * 保存角色
+     */
+    @SysLog("保存角色")
+    @RequestMapping("/save")
+    @RequiresPermissions("sys:role:save")
+    public R save(@RequestBody SysRoleEntity role) {
+        // 非空检验
+        // ValidatorUtils.validateEntity(role);
+        role.setCreateUserId(getUserId());
+        sysRoleService.save(role);
+
+        return R.ok();
+    }
+
+    /**
+     * 修改角色
+     */
+    @SysLog("修改角色")
+    @RequestMapping("/update")
+    @RequiresPermissions("sys:role:update")
+    public R update(@RequestBody SysRoleEntity role) {
+//        ValidatorUtils.validateEntity(role);
+
+        role.setCreateUserId(getUserId());
+        sysRoleService.update(role);
+
+        return R.ok();
+    }
+
+    /**
+     * 删除角色
+     */
+    @SysLog("删除角色")
+    @RequestMapping("/delete")
+    @RequiresPermissions("sys:role:delete")
+    public R delete(@RequestBody Long[] roleIds) {
+        sysRoleService.deleteBatch(roleIds);
+
+        return R.ok();
+    }
+}

+ 153 - 0
kmall-common/src/main/java/com/kmall/common/controller/SysUserController.java

@@ -0,0 +1,153 @@
+package com.kmall.common.controller;
+
+import com.kmall.common.annotation.SysLog;
+import com.kmall.common.entity.SysUserEntity;
+import com.kmall.common.service.SysUserRoleService;
+import com.kmall.common.service.SysUserService;
+import com.kmall.common.utils.*;
+import com.kmall.common.validator.Assert;
+import com.kmall.common.validator.ValidatorUtils;
+import com.kmall.common.validator.group.AddGroup;
+import com.kmall.common.validator.group.UpdateGroup;
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.apache.shiro.crypto.hash.Sha256Hash;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 系统用户
+ *
+ * @author Scott
+ * @email
+ * @date 2016年10月31日 上午10:40:10
+ */
+@RestController
+@RequestMapping("/sys/user")
+public class SysUserController extends AbstractController {
+    @Autowired
+    private SysUserService sysUserService;
+    @Autowired
+    private SysUserRoleService sysUserRoleService;
+
+    /**
+     * 所有用户列表
+     */
+    @RequestMapping("/list")
+    @RequiresPermissions("sys:user:list")
+    public R list(@RequestParam Map<String, Object> params) {
+        //只有超级管理员,才能查看所有管理员列表
+        if (getUserId() != Constant.SUPER_ADMIN) {
+            params.put("createUserId", getUserId());
+        }
+
+        //查询列表数据
+        Query query = new Query(params);
+        List<SysUserEntity> userList = sysUserService.queryList(query);
+        int total = sysUserService.queryTotal(query);
+
+        PageUtils pageUtil = new PageUtils(userList, total, query.getLimit(), query.getPage());
+
+        return R.ok().put("page", pageUtil);
+    }
+
+    /**
+     * 获取登录的用户信息
+     */
+    @RequestMapping("/info")
+    public R info() {
+        return R.ok().put("user", getUser());
+    }
+
+    /**
+     * 修改登录用户密码
+     */
+    @SysLog("修改密码")
+    @RequestMapping("/password")
+    public R password(String password, String newPassword) {
+        Assert.isBlank(password, "原密码不为能空");
+        Assert.isBlank(newPassword, "新密码不为能空");
+
+        //sha256加密
+        password = new Sha256Hash(password).toHex();
+        //sha256加密
+        newPassword = new Sha256Hash(newPassword).toHex();
+
+        //更新密码
+        int count = sysUserService.updatePassword(getUserId(), password, newPassword);
+        if (count == 0) {
+            return R.error("原密码不正确");
+        }
+
+        //退出
+        ShiroUtils.logout();
+
+        return R.ok();
+    }
+
+    /**
+     * 用户信息
+     */
+    @RequestMapping("/info/{userId}")
+    @RequiresPermissions("sys:user:info")
+    public R info(@PathVariable("userId") Long userId) {
+        SysUserEntity user = sysUserService.queryObject(userId);
+
+        //获取用户所属的角色列表
+        List<Long> roleIdList = sysUserRoleService.queryRoleIdList(userId);
+        user.setRoleIdList(roleIdList);
+
+        return R.ok().put("user", user);
+    }
+
+    /**
+     * 保存用户
+     */
+    @SysLog("保存用户")
+    @RequestMapping("/save")
+    @RequiresPermissions("sys:user:save")
+    public R save(@RequestBody SysUserEntity user) {
+        //ValidatorUtils.validateEntity(user);
+        user.setCreateUserId(getUserId());
+        sysUserService.save(user);
+
+        return R.ok();
+    }
+
+    /**
+     * 修改用户
+     */
+    @SysLog("修改用户")
+    @RequestMapping("/update")
+    @RequiresPermissions("sys:user:update")
+    public R update(@RequestBody SysUserEntity user) {
+        //ValidatorUtils.validateEntity(user);
+        user.setCreateUserId(getUserId());
+        sysUserService.update(user);
+
+        return R.ok();
+    }
+
+    /**
+     * 删除用户
+     */
+    @SysLog("删除用户")
+    @RequestMapping("/delete")
+    @RequiresPermissions("sys:user:delete")
+    public R delete(@RequestBody Long[] userIds) {
+        if (ArrayUtils.contains(userIds, 1L)) {
+            return R.error("系统管理员不能删除");
+        }
+
+        if (ArrayUtils.contains(userIds, getUserId())) {
+            return R.error("当前用户不能删除");
+        }
+
+        sysUserService.deleteBatch(userIds);
+
+        return R.ok();
+    }
+}

+ 40 - 0
kmall-common/src/main/java/com/kmall/common/dao/BaseDao.java

@@ -0,0 +1,40 @@
+package com.kmall.common.dao;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 基础Dao(还需在XML文件里,有对应的SQL语句)
+ *
+ * @author Scott
+ * @email
+ * @date 2016年9月18日 上午9:31:36
+ */
+public interface BaseDao<T> {
+
+    int save(T t);
+
+    void save(Map<String, Object> map);
+
+    void saveBatch(List<T> list);
+
+    int update(T t);
+
+    int update(Map<String, Object> map);
+
+    int delete(Object id);
+
+    int delete(Map<String, Object> map);
+
+    int deleteBatch(Object[] id);
+
+    T queryObject(Object id);
+
+    List<T> queryList(Map<String, Object> map);
+
+    List<T> queryList(Object id);
+
+    Integer queryTotal(Map<String, Object> map);
+
+    Integer queryTotal();
+}

+ 20 - 0
kmall-common/src/main/java/com/kmall/common/dao/FormIdsDao.java

@@ -0,0 +1,20 @@
+package com.kmall.common.dao;
+
+
+import com.kmall.common.entity.FormIdsEntity;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.stereotype.Component;
+
+/**
+ * 小程序form_id收集Dao
+ *
+ * @author Scott
+ * @email
+ * @date 2017-12-16 15:32:50
+ */
+@Component
+public interface FormIdsDao extends BaseDao<FormIdsEntity> {
+    FormIdsEntity queryByUserId(@Param("userId") Long userId);
+
+    FormIdsEntity getFormIdsByMerchOrderSn(@Param("merchOrderSn")String merchOrderSn);
+}

+ 25 - 0
kmall-common/src/main/java/com/kmall/common/dao/SysConfigDao.java

@@ -0,0 +1,25 @@
+package com.kmall.common.dao;
+
+import com.kmall.common.entity.SysConfigEntity;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 系统配置信息
+ *
+ * @author Scott
+ * @email
+ * @date 2016年12月4日 下午6:46:16
+ */
+public interface SysConfigDao extends BaseDao<SysConfigEntity> {
+
+    /**
+     * 根据key,查询value
+     */
+    String queryByKey(String paramKey);
+
+    /**
+     * 根据key,更新value
+     */
+    int updateValueByKey(@Param("key") String key, @Param("value") String value);
+
+}

+ 14 - 0
kmall-common/src/main/java/com/kmall/common/dao/SysLogDao.java

@@ -0,0 +1,14 @@
+package com.kmall.common.dao;
+
+import com.kmall.common.entity.SysLogEntity;
+
+/**
+ * 系统日志
+ *
+ * @author Scott
+ * @email
+ * @date 2017-03-08 10:40:56
+ */
+public interface SysLogDao extends BaseDao<SysLogEntity> {
+
+}

+ 32 - 0
kmall-common/src/main/java/com/kmall/common/dao/SysMenuDao.java

@@ -0,0 +1,32 @@
+package com.kmall.common.dao;
+
+import com.kmall.common.entity.SysMenuEntity;
+
+import java.util.List;
+
+/**
+ * 菜单管理
+ *
+ * @author Scott
+ * @email
+ * @date 2016年9月18日 上午9:33:01
+ */
+public interface SysMenuDao extends BaseDao<SysMenuEntity> {
+
+    /**
+     * 根据父菜单,查询子菜单
+     *
+     * @param parentId 父菜单ID
+     */
+    List<SysMenuEntity> queryListParentId(Long parentId);
+
+    /**
+     * 获取不包含按钮的菜单列表
+     */
+    List<SysMenuEntity> queryNotButtonList();
+
+    /**
+     * 查询用户的权限列表
+     */
+    List<SysMenuEntity> queryUserList(Long userId);
+}

+ 12 - 0
kmall-common/src/main/java/com/kmall/common/dao/SysRegionDao.java

@@ -0,0 +1,12 @@
+package com.kmall.common.dao;
+
+import com.kmall.common.entity.SysRegionEntity;
+
+/**
+ * @author Scott
+ * @email
+ * @date 2017-08-11 09:16:46
+ */
+public interface SysRegionDao extends BaseDao<SysRegionEntity> {
+
+}

+ 20 - 0
kmall-common/src/main/java/com/kmall/common/dao/SysRoleDao.java

@@ -0,0 +1,20 @@
+package com.kmall.common.dao;
+
+import com.kmall.common.entity.SysRoleEntity;
+
+import java.util.List;
+
+/**
+ * 角色管理
+ *
+ * @author Scott
+ * @email
+ * @date 2016年9月18日 上午9:33:33
+ */
+public interface SysRoleDao extends BaseDao<SysRoleEntity> {
+
+    /**
+     * 查询用户创建的角色ID列表
+     */
+    List<Long> queryRoleIdList(Long createUserId);
+}

+ 30 - 0
kmall-common/src/main/java/com/kmall/common/dao/SysRoleDeptDao.java

@@ -0,0 +1,30 @@
+package com.kmall.common.dao;
+
+import com.kmall.common.entity.SysRoleDeptEntity;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 角色与部门对应关系
+ *
+ * @author Scott
+ * @email
+ * @date 2017年9月18日 上午9:18:38
+ */
+@Mapper
+public interface SysRoleDeptDao extends BaseDao<SysRoleDeptEntity> {
+
+    /**
+     * 根据角色ID,获取部门ID列表
+     */
+    List<Long> queryDeptIdList(Long roleId);
+
+    /**
+     * 根据用户ID获取权限部门列表
+     *
+     * @param userId
+     * @return
+     */
+    List<Long> queryDeptIdListByUserId(Long userId);
+}

+ 20 - 0
kmall-common/src/main/java/com/kmall/common/dao/SysRoleMenuDao.java

@@ -0,0 +1,20 @@
+package com.kmall.common.dao;
+
+import com.kmall.common.entity.SysRoleMenuEntity;
+
+import java.util.List;
+
+/**
+ * 角色与菜单对应关系
+ *
+ * @author Scott
+ * @email
+ * @date 2016年9月18日 上午9:33:46
+ */
+public interface SysRoleMenuDao extends BaseDao<SysRoleMenuEntity> {
+
+    /**
+     * 根据角色ID,获取菜单ID列表
+     */
+    List<Long> queryMenuIdList(Long roleId);
+}

+ 16 - 0
kmall-common/src/main/java/com/kmall/common/dao/SysSmsLogDao.java

@@ -0,0 +1,16 @@
+package com.kmall.common.dao;
+
+import com.kmall.common.entity.SysSmsLogEntity;
+import org.springframework.stereotype.Component;
+
+/**
+ * Dao
+ *
+ * @author Scott
+ * @date 2017-12-16 23:38:05
+ */
+@Component
+public interface SysSmsLogDao extends BaseDao<SysSmsLogEntity> {
+
+    SysSmsLogEntity querySmsCodeByUserId(Long userId);
+}

+ 38 - 0
kmall-common/src/main/java/com/kmall/common/dao/SysUserDao.java

@@ -0,0 +1,38 @@
+package com.kmall.common.dao;
+
+import com.kmall.common.entity.SysUserEntity;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 系统用户
+ *
+ * @author Scott
+ * @email
+ * @date 2016年9月18日 上午9:34:11
+ */
+public interface SysUserDao extends BaseDao<SysUserEntity> {
+
+    /**
+     * 查询用户的所有权限
+     *
+     * @param userId 用户ID
+     */
+    List<String> queryAllPerms(Long userId);
+
+    /**
+     * 查询用户的所有菜单ID
+     */
+    List<Long> queryAllMenuId(Long userId);
+
+    /**
+     * 根据用户名,查询系统用户
+     */
+    SysUserEntity queryByUserName(String username);
+
+    /**
+     * 修改密码
+     */
+    int updatePassword(Map<String, Object> map);
+}

+ 22 - 0
kmall-common/src/main/java/com/kmall/common/dao/SysUserRoleDao.java

@@ -0,0 +1,22 @@
+package com.kmall.common.dao;
+
+import com.kmall.common.entity.SysUserRoleEntity;
+
+import java.util.List;
+
+/**
+ * 用户与角色对应关系
+ *
+ * @author Scott
+ * @email
+ * @date 2016年9月18日 上午9:34:46
+ */
+public interface SysUserRoleDao extends BaseDao<SysUserRoleEntity> {
+
+    /**
+     * 根据用户ID,获取角色ID列表
+     */
+    List<Long> queryRoleIdList(Long userId);
+
+    int deleteByRoleId(Long roleId);
+}

+ 18 - 0
kmall-common/src/main/java/com/kmall/common/dao/TemplateConfDao.java

@@ -0,0 +1,18 @@
+package com.kmall.common.dao;
+
+import com.kmall.common.entity.TemplateConfVo;
+import org.springframework.stereotype.Component;
+
+/**
+ * 微信模板消息日志Dao
+ *
+ * @author Scott
+ * @email
+ * @date 2017-12-02 16:49:29
+ */
+@Component
+public interface TemplateConfDao extends BaseDao<TemplateConfVo> {
+
+    TemplateConfVo queryByTypeId(Integer templateType);
+
+}

+ 117 - 0
kmall-common/src/main/java/com/kmall/common/entity/FormIdsEntity.java

@@ -0,0 +1,117 @@
+package com.kmall.common.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 小程序form_id收集实体
+ * 表名 mall_form_ids
+ *
+ * @author Scott
+ * @email
+ * @date 2017-12-16 15:32:50
+ */
+public class FormIdsEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     *
+     */
+    private Long id;
+    /**
+     *
+     */
+    private Long userId;
+    /**
+     *
+     */
+    private String formId;
+    /**
+     * 有效次数
+     */
+    private Integer validNum;
+    /**
+     * 失效时间
+     */
+    private Date expireTime;
+
+    private String merchOrderSn;
+
+    public String getMerchOrderSn() {
+        return merchOrderSn;
+    }
+
+    public void setMerchOrderSn(String merchOrderSn) {
+        this.merchOrderSn = merchOrderSn;
+    }
+
+    /**
+     * 设置:
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * 获取:
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * 设置:
+     */
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    /**
+     * 获取:
+     */
+    public Long getUserId() {
+        return userId;
+    }
+
+    /**
+     * 设置:
+     */
+    public void setFormId(String formId) {
+        this.formId = formId;
+    }
+
+    /**
+     * 获取:
+     */
+    public String getFormId() {
+        return formId;
+    }
+
+    /**
+     * 设置:有效次数
+     */
+    public void setValidNum(Integer validNum) {
+        this.validNum = validNum;
+    }
+
+    /**
+     * 获取:有效次数
+     */
+    public Integer getValidNum() {
+        return validNum;
+    }
+
+    /**
+     * 设置:失效时间
+     */
+    public void setExpireTime(Date expireTime) {
+        this.expireTime = expireTime;
+    }
+
+    /**
+     * 获取:失效时间
+     */
+    public Date getExpireTime() {
+        return expireTime;
+    }
+}

+ 42 - 0
kmall-common/src/main/java/com/kmall/common/entity/SmsConfig.java

@@ -0,0 +1,42 @@
+package com.kmall.common.entity;
+
+import org.hibernate.validator.constraints.Range;
+
+import java.io.Serializable;
+
+/**
+ * 名称:SmsConfig <br>
+ * 描述:短信配置信息<br>
+ *
+ * @author Scott
+ * @version 1.0
+ * @since 1.0.0
+ */
+public class SmsConfig implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /**
+     * 短信发送域名
+     */
+    private String url;
+
+    /**
+     * apiKey
+     */
+    private String apiKey;
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getApiKey() {
+        return apiKey;
+    }
+
+    public void setApiKey(String apiKey) {
+        this.apiKey = apiKey;
+    }
+}

+ 52 - 0
kmall-common/src/main/java/com/kmall/common/entity/SysConfigEntity.java

@@ -0,0 +1,52 @@
+package com.kmall.common.entity;
+
+
+import org.hibernate.validator.constraints.NotBlank;
+
+/**
+ * 系统配置信息
+ *
+ * @author Scott
+ * @email
+ * @date 2016年12月4日 下午6:43:36
+ */
+public class SysConfigEntity {
+    private Long id;
+    @NotBlank(message = "参数名不能为空")
+    private String key;
+    @NotBlank(message = "参数值不能为空")
+    private String value;
+    private String remark;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public String getRemark() {
+        return remark;
+    }
+
+    public void setRemark(String remark) {
+        this.remark = remark;
+    }
+}

+ 128 - 0
kmall-common/src/main/java/com/kmall/common/entity/SysLogEntity.java

@@ -0,0 +1,128 @@
+package com.kmall.common.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 系统日志
+ *
+ * @author Scott
+ * @email
+ * @date 2017-03-08 10:40:56
+ */
+public class SysLogEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+    //用户名
+    private String username;
+    //用户操作
+    private String operation;
+    //请求方法
+    private String method;
+    //请求参数
+    private String params;
+    //IP地址
+    private String ip;
+    //创建时间
+    private Date createDate;
+
+    /**
+     * 设置:
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * 获取:
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * 设置:用户名
+     */
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    /**
+     * 获取:用户名
+     */
+    public String getUsername() {
+        return username;
+    }
+
+    /**
+     * 设置:用户操作
+     */
+    public void setOperation(String operation) {
+        this.operation = operation;
+    }
+
+    /**
+     * 获取:用户操作
+     */
+    public String getOperation() {
+        return operation;
+    }
+
+    /**
+     * 设置:请求方法
+     */
+    public void setMethod(String method) {
+        this.method = method;
+    }
+
+    /**
+     * 获取:请求方法
+     */
+    public String getMethod() {
+        return method;
+    }
+
+    /**
+     * 设置:请求参数
+     */
+    public void setParams(String params) {
+        this.params = params;
+    }
+
+    /**
+     * 获取:请求参数
+     */
+    public String getParams() {
+        return params;
+    }
+
+    /**
+     * 设置:IP地址
+     */
+    public void setIp(String ip) {
+        this.ip = ip;
+    }
+
+    /**
+     * 获取:IP地址
+     */
+    public String getIp() {
+        return ip;
+    }
+
+    /**
+     * 设置:创建时间
+     */
+    public void setCreateDate(Date createDate) {
+        this.createDate = createDate;
+    }
+
+    /**
+     * 获取:创建时间
+     */
+    public Date getCreateDate() {
+        return createDate;
+    }
+}

+ 190 - 0
kmall-common/src/main/java/com/kmall/common/entity/SysMenuEntity.java

@@ -0,0 +1,190 @@
+package com.kmall.common.entity;
+
+
+/**
+ * @author Scott
+ * @email
+ * @date 2017年6月18日 上午9:26:39
+ */
+public class SysMenuEntity extends Tree {
+
+    /**
+     * 菜单ID
+     */
+    private Long menuId;
+
+    /**
+     * 父菜单ID,一级菜单为0
+     */
+    private Long parentId;
+
+    /**
+     * 父菜单名称
+     */
+    private String parentName;
+
+    /**
+     * 菜单名称
+     */
+    private String name;
+
+    /**
+     * 菜单URL
+     */
+    private String url;
+
+    /**
+     * 授权(多个用逗号分隔,如:user:list,user:create)
+     */
+    private String perms;
+
+    /**
+     * 类型     0:目录   1:菜单   2:按钮
+     */
+    private Integer type;
+
+    /**
+     * 菜单图标
+     */
+    private String icon;
+
+    /**
+     * 排序
+     */
+    private Integer orderNum;
+
+    /**
+     * 状态
+     */
+    private Integer status;
+
+    public void setMenuId(Long menuId) {
+        this.menuId = menuId;
+    }
+
+    public Long getMenuId() {
+        return menuId;
+    }
+
+    /**
+     * 设置:父菜单ID,一级菜单为0
+     *
+     * @param parentId 父菜单ID,一级菜单为0
+     */
+    public void setParentId(Long parentId) {
+        this.parentId = parentId;
+    }
+
+    /**
+     * 获取:父菜单ID,一级菜单为0
+     *
+     * @return Long
+     */
+    public Long getParentId() {
+        return parentId;
+    }
+
+    /**
+     * 设置:菜单名称
+     *
+     * @param name 菜单名称
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * 获取:菜单名称
+     *
+     * @return String
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * 设置:菜单URL
+     *
+     * @param url 菜单URL
+     */
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    /**
+     * 获取:菜单URL
+     *
+     * @return String
+     */
+    public String getUrl() {
+        return url;
+    }
+
+    public String getPerms() {
+        return perms;
+    }
+
+    public void setPerms(String perms) {
+        this.perms = perms;
+    }
+
+    public Integer getType() {
+        return type;
+    }
+
+    public void setType(Integer type) {
+        this.type = type;
+    }
+
+    /**
+     * 设置:菜单图标
+     *
+     * @param icon 菜单图标
+     */
+    public void setIcon(String icon) {
+        this.icon = icon;
+    }
+
+    /**
+     * 获取:菜单图标
+     *
+     * @return String
+     */
+    public String getIcon() {
+        return icon;
+    }
+
+    /**
+     * 设置:排序
+     *
+     * @param orderNum 排序
+     */
+    public void setOrderNum(Integer orderNum) {
+        this.orderNum = orderNum;
+    }
+
+    /**
+     * 获取:排序
+     *
+     * @return Integer
+     */
+    public Integer getOrderNum() {
+        return orderNum;
+    }
+
+    public String getParentName() {
+        return parentName;
+    }
+
+    public void setParentName(String parentName) {
+        this.parentName = parentName;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+}

+ 75 - 0
kmall-common/src/main/java/com/kmall/common/entity/SysRegionEntity.java

@@ -0,0 +1,75 @@
+package com.kmall.common.entity;
+
+/**
+ * @author Scott
+ * @email
+ * @date 2017-08-15 08:03:41
+ */
+public class SysRegionEntity extends Tree<SysRegionEntity> {
+
+    //主键
+    private Integer id;
+    //父节点
+    private Integer parentId;
+    //区域名称
+    private String name;
+    //类型 0国家 1省份 2地市 3区县
+    private Integer type;
+    //区域代理Id
+    private Integer agencyId;
+
+    /**
+     * 翻译用字段
+     */
+    //父级名称
+    private String parentName;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getParentId() {
+        return parentId;
+    }
+
+    public void setParentId(Integer parentId) {
+        this.parentId = parentId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getType() {
+        return type;
+    }
+
+    public void setType(Integer type) {
+        this.type = type;
+    }
+
+    public Integer getAgencyId() {
+        return agencyId;
+    }
+
+    public void setAgencyId(Integer agencyId) {
+        this.agencyId = agencyId;
+    }
+
+    public String getParentName() {
+        return parentName;
+    }
+
+    public void setParentName(String parentName) {
+        this.parentName = parentName;
+    }
+
+}

+ 82 - 0
kmall-common/src/main/java/com/kmall/common/entity/SysRoleDeptEntity.java

@@ -0,0 +1,82 @@
+package com.kmall.common.entity;
+
+
+import java.io.Serializable;
+
+/**
+ * 角色与部门对应关系
+ *
+ * @author Scott
+ * @email
+ * @date 2017年9月18日 上午9:18:38
+ */
+public class SysRoleDeptEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+
+    /**
+     * 角色ID
+     */
+    private Long roleId;
+
+    /**
+     * 部门ID
+     */
+    private Long deptId;
+
+    /**
+     * 设置:
+     *
+     * @param id
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * 获取:
+     *
+     * @return Long
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * 设置:角色ID
+     *
+     * @param roleId 角色ID
+     */
+    public void setRoleId(Long roleId) {
+        this.roleId = roleId;
+    }
+
+    /**
+     * 获取:角色ID
+     *
+     * @return Long
+     */
+    public Long getRoleId() {
+        return roleId;
+    }
+
+    /**
+     * 设置:部门ID
+     *
+     * @param deptId 部门ID
+     */
+    public void setDeptId(Long deptId) {
+        this.deptId = deptId;
+    }
+
+    /**
+     * 获取:部门ID
+     *
+     * @return Long
+     */
+    public Long getDeptId() {
+        return deptId;
+    }
+
+}

+ 162 - 0
kmall-common/src/main/java/com/kmall/common/entity/SysRoleEntity.java

@@ -0,0 +1,162 @@
+package com.kmall.common.entity;
+
+
+import org.hibernate.validator.constraints.NotBlank;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 角色
+ *
+ * @author Scott
+ * @email
+ * @date 2016年9月18日 上午9:27:38
+ */
+public class SysRoleEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 角色ID
+     */
+    private Long roleId;
+
+    /**
+     * 角色名称
+     */
+    @NotBlank(message = "角色名称不能为空")
+    private String roleName;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 创建者ID
+     */
+    private Long createUserId;
+
+    private List<Long> menuIdList;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+    /**
+     * 部门ID
+     */
+    @NotNull(message = "部门不能为空")
+    private Long deptId;
+
+    /**
+     * 部门名称
+     */
+    private String deptName;
+
+    private List<Long> deptIdList;
+
+    /**
+     * 设置:
+     *
+     * @param roleId
+     */
+    public void setRoleId(Long roleId) {
+        this.roleId = roleId;
+    }
+
+    /**
+     * 获取:
+     *
+     * @return Long
+     */
+    public Long getRoleId() {
+        return roleId;
+    }
+
+    /**
+     * 设置:角色名称
+     *
+     * @param roleName 角色名称
+     */
+    public void setRoleName(String roleName) {
+        this.roleName = roleName;
+    }
+
+    /**
+     * 获取:角色名称
+     *
+     * @return String
+     */
+    public String getRoleName() {
+        return roleName;
+    }
+
+    /**
+     * 设置:备注
+     *
+     * @param remark 备注
+     */
+    public void setRemark(String remark) {
+        this.remark = remark;
+    }
+
+    /**
+     * 获取:备注
+     *
+     * @return String
+     */
+    public String getRemark() {
+        return remark;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public List<Long> getMenuIdList() {
+        return menuIdList;
+    }
+
+    public void setMenuIdList(List<Long> menuIdList) {
+        this.menuIdList = menuIdList;
+    }
+
+    public Long getCreateUserId() {
+        return createUserId;
+    }
+
+    public void setCreateUserId(Long createUserId) {
+        this.createUserId = createUserId;
+    }
+
+    public Long getDeptId() {
+        return deptId;
+    }
+
+    public void setDeptId(Long deptId) {
+        this.deptId = deptId;
+    }
+
+    public String getDeptName() {
+        return deptName;
+    }
+
+    public void setDeptName(String deptName) {
+        this.deptName = deptName;
+    }
+
+    public List<Long> getDeptIdList() {
+        return deptIdList;
+    }
+
+    public void setDeptIdList(List<Long> deptIdList) {
+        this.deptIdList = deptIdList;
+    }
+}

+ 82 - 0
kmall-common/src/main/java/com/kmall/common/entity/SysRoleMenuEntity.java

@@ -0,0 +1,82 @@
+package com.kmall.common.entity;
+
+
+import java.io.Serializable;
+
+/**
+ * 角色与菜单对应关系
+ *
+ * @author Scott
+ * @email
+ * @date 2016年9月18日 上午9:28:13
+ */
+public class SysRoleMenuEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+
+    /**
+     * 角色ID
+     */
+    private Long roleId;
+
+    /**
+     * 菜单ID
+     */
+    private Long menuId;
+
+    /**
+     * 设置:
+     *
+     * @param id
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * 获取:
+     *
+     * @return Long
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * 设置:角色ID
+     *
+     * @param roleId 角色ID
+     */
+    public void setRoleId(Long roleId) {
+        this.roleId = roleId;
+    }
+
+    /**
+     * 获取:角色ID
+     *
+     * @return Long
+     */
+    public Long getRoleId() {
+        return roleId;
+    }
+
+    /**
+     * 设置:菜单ID
+     *
+     * @param menuId 菜单ID
+     */
+    public void setMenuId(Long menuId) {
+        this.menuId = menuId;
+    }
+
+    /**
+     * 获取:菜单ID
+     *
+     * @return Long
+     */
+    public Long getMenuId() {
+        return menuId;
+    }
+
+}

+ 313 - 0
kmall-common/src/main/java/com/kmall/common/entity/SysSmsLogEntity.java

@@ -0,0 +1,313 @@
+package com.kmall.common.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 实体
+ * 表名 sys_sms_log
+ *
+ * @author Scott
+ * @date 2017-12-16 23:38:05
+ */
+public class SysSmsLogEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    private Long id;
+    /**
+     * 操作人
+     */
+    private Long userId;
+    /**
+     * 必填参数。发送内容(1-500 个汉字)UTF-8编码
+     */
+    private String content;
+    /**
+     * 必填参数。手机号码。多个以英文逗号隔开
+     */
+    private String mobile;
+    /**
+     * 可选参数。发送时间,填写时已填写的时间发送,不填时为当前时间发送
+     */
+    private Date stime;
+    /**
+     * 必填参数。用户签名
+     */
+    private String sign;
+    /**
+     * 必填参数。固定值 pt
+     */
+    private String type;
+    /**
+     * 可选参数。扩展码,用户定义扩展码,只能为数字
+     */
+    private String extno;
+    /**
+     * 1成功 0失败
+     */
+    private Integer sendStatus;
+    /**
+     * 发送编号
+     */
+    private String sendId;
+    /**
+     * 无效号码数
+     */
+    private Integer invalidNum;
+    /**
+     * 成功提交数
+     */
+    private Integer successNum;
+    /**
+     * 黑名单数
+     */
+    private Integer blackNum;
+    /**
+     * 返回消息
+     */
+    private String returnMsg;
+
+    //翻译
+    /**
+     * 操作人
+     */
+    private String userName;
+
+
+    private String smsCode;
+
+    private Integer storeId;
+
+    private String merchSn;
+
+    public Integer getStoreId() {
+        return storeId;
+    }
+
+    public void setStoreId(Integer storeId) {
+        this.storeId = storeId;
+    }
+
+    public String getMerchSn() {
+        return merchSn;
+    }
+
+    public void setMerchSn(String merchSn) {
+        this.merchSn = merchSn;
+    }
+
+    public String getSmsCode() {
+        return smsCode;
+    }
+
+    public void setSmsCode(String smsCode) {
+        this.smsCode = smsCode;
+    }
+
+    /**
+     * 设置:主键
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * 获取:主键
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * 设置:操作人
+     */
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    /**
+     * 获取:操作人
+     */
+    public Long getUserId() {
+        return userId;
+    }
+
+    /**
+     * 设置:必填参数。发送内容(1-500 个汉字)UTF-8编码
+     */
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    /**
+     * 获取:必填参数。发送内容(1-500 个汉字)UTF-8编码
+     */
+    public String getContent() {
+        return content;
+    }
+
+    /**
+     * 设置:必填参数。手机号码。多个以英文逗号隔开
+     */
+    public void setMobile(String mobile) {
+        this.mobile = mobile;
+    }
+
+    /**
+     * 获取:必填参数。手机号码。多个以英文逗号隔开
+     */
+    public String getMobile() {
+        return mobile;
+    }
+
+    /**
+     * 设置:可选参数。发送时间,填写时已填写的时间发送,不填时为当前时间发送
+     */
+    public void setStime(Date stime) {
+        this.stime = stime;
+    }
+
+    /**
+     * 获取:可选参数。发送时间,填写时已填写的时间发送,不填时为当前时间发送
+     */
+    public Date getStime() {
+        return stime;
+    }
+
+    /**
+     * 设置:必填参数。用户签名
+     */
+    public void setSign(String sign) {
+        this.sign = sign;
+    }
+
+    /**
+     * 获取:必填参数。用户签名
+     */
+    public String getSign() {
+        return sign;
+    }
+
+    /**
+     * 设置:必填参数。固定值 pt
+     */
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    /**
+     * 获取:必填参数。固定值 pt
+     */
+    public String getType() {
+        return type;
+    }
+
+    /**
+     * 设置:可选参数。扩展码,用户定义扩展码,只能为数字
+     */
+    public void setExtno(String extno) {
+        this.extno = extno;
+    }
+
+    /**
+     * 获取:可选参数。扩展码,用户定义扩展码,只能为数字
+     */
+    public String getExtno() {
+        return extno;
+    }
+
+    /**
+     * 设置:1成功 0失败
+     */
+    public void setSendStatus(Integer sendStatus) {
+        this.sendStatus = sendStatus;
+    }
+
+    /**
+     * 获取:1成功 0失败
+     */
+    public Integer getSendStatus() {
+        return sendStatus;
+    }
+
+    /**
+     * 设置:发送编号
+     */
+    public void setSendId(String sendId) {
+        this.sendId = sendId;
+    }
+
+    /**
+     * 获取:发送编号
+     */
+    public String getSendId() {
+        return sendId;
+    }
+
+    /**
+     * 设置:无效号码数
+     */
+    public void setInvalidNum(Integer invalidNum) {
+        this.invalidNum = invalidNum;
+    }
+
+    /**
+     * 获取:无效号码数
+     */
+    public Integer getInvalidNum() {
+        return invalidNum;
+    }
+
+    /**
+     * 设置:成功提交数
+     */
+    public void setSuccessNum(Integer successNum) {
+        this.successNum = successNum;
+    }
+
+    /**
+     * 获取:成功提交数
+     */
+    public Integer getSuccessNum() {
+        return successNum;
+    }
+
+    /**
+     * 设置:黑名单数
+     */
+    public void setBlackNum(Integer blackNum) {
+        this.blackNum = blackNum;
+    }
+
+    /**
+     * 获取:黑名单数
+     */
+    public Integer getBlackNum() {
+        return blackNum;
+    }
+
+    /**
+     * 设置:返回消息
+     */
+    public void setReturnMsg(String returnMsg) {
+        this.returnMsg = returnMsg;
+    }
+
+    /**
+     * 获取:返回消息
+     */
+    public String getReturnMsg() {
+        return returnMsg;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+}

+ 286 - 0
kmall-common/src/main/java/com/kmall/common/entity/SysUserEntity.java

@@ -0,0 +1,286 @@
+package com.kmall.common.entity;
+
+import com.kmall.common.validator.group.AddGroup;
+import com.kmall.common.validator.group.UpdateGroup;
+import org.hibernate.validator.constraints.Email;
+import org.hibernate.validator.constraints.NotBlank;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * 系统用户
+ *
+ * @author Scott
+ * @email
+ * @date 2016年9月18日 上午9:28:55
+ */
+public class SysUserEntity
+        implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 用户名
+     */
+    @NotBlank(message = "用户名不能为空", groups = {AddGroup.class, UpdateGroup.class})
+    private String username;
+
+    /**
+     * 密码
+     */
+    private transient String password;
+
+    /**
+     * 邮箱
+     */
+    @NotBlank(message = "邮箱不能为空", groups = {AddGroup.class, UpdateGroup.class})
+    @Email(message = "邮箱格式不正确", groups = {AddGroup.class, UpdateGroup.class})
+    private String email;
+
+    /**
+     * 手机号
+     */
+    private String mobile;
+
+    /**
+     * 状态  0:禁用   1:正常
+     */
+    private Integer status;
+
+    /**
+     * 角色ID列表
+     */
+    private List<Long> roleIdList;
+
+    /**
+     * 创建者ID
+     */
+    private Long createUserId;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    private Long deptId;
+
+    /**
+     * 部门名称
+     */
+    private String deptName;
+
+    private Long roleId;
+
+    private Integer storeId;
+
+    private String merchSn;
+
+    private String roleType;
+
+    private Set<String> permsSet;
+
+    /**
+     * 设置:
+     *
+     * @param userId
+     */
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    /**
+     * 获取:
+     *
+     * @return Long
+     */
+    public Long getUserId() {
+        return userId;
+    }
+
+    public Long getRoleId() {
+        return roleId;
+    }
+
+    public void setRoleId(Long roleId) {
+        this.roleId = roleId;
+    }
+
+    /**
+     * 设置:用户名
+     *
+     * @param username 用户名
+     */
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    /**
+     * 获取:用户名
+     *
+     * @return String
+     */
+    public String getUsername() {
+        return username;
+    }
+
+    /**
+     * 设置:密码
+     *
+     * @param password 密码
+     */
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    /**
+     * 获取:密码
+     *
+     * @return String
+     */
+    public String getPassword() {
+        return password;
+    }
+
+    /**
+     * 设置:邮箱
+     *
+     * @param email 邮箱
+     */
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    /**
+     * 获取:邮箱
+     *
+     * @return String
+     */
+    public String getEmail() {
+        return email;
+    }
+
+    /**
+     * 设置:手机号
+     *
+     * @param mobile 手机号
+     */
+    public void setMobile(String mobile) {
+        this.mobile = mobile;
+    }
+
+    /**
+     * 获取:手机号
+     *
+     * @return String
+     */
+    public String getMobile() {
+        return mobile;
+    }
+
+    public Integer getStoreId() {
+        return storeId;
+    }
+
+    public void setStoreId(Integer storeId) {
+        this.storeId = storeId;
+    }
+
+    public String getMerchSn() {
+        return merchSn;
+    }
+
+    public void setMerchSn(String merchSn) {
+        this.merchSn = merchSn;
+    }
+
+    public String getRoleType() {
+        return roleType;
+    }
+
+    public void setRoleType(String roleType) {
+        this.roleType = roleType;
+    }
+
+    /**
+     * 设置:状态  0:禁用   1:正常
+     *
+     * @param status 状态  0:禁用   1:正常
+     */
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    /**
+     * 获取:状态  0:禁用   1:正常
+     *
+     * @return Integer
+     */
+    public Integer getStatus() {
+        return status;
+    }
+
+    /**
+     * 设置:创建时间
+     *
+     * @param createTime 创建时间
+     */
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    /**
+     * 获取:创建时间
+     *
+     * @return Date
+     */
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public List<Long> getRoleIdList() {
+        return roleIdList;
+    }
+
+    public void setRoleIdList(List<Long> roleIdList) {
+        this.roleIdList = roleIdList;
+    }
+
+    public Long getCreateUserId() {
+        return createUserId;
+    }
+
+    public void setCreateUserId(Long createUserId) {
+        this.createUserId = createUserId;
+    }
+
+    public Long getDeptId() {
+        return deptId;
+    }
+
+    public void setDeptId(Long deptId) {
+        this.deptId = deptId;
+    }
+
+    public String getDeptName() {
+        return deptName;
+    }
+
+    public void setDeptName(String deptName) {
+        this.deptName = deptName;
+    }
+
+    public Set<String> getPermsSet() {
+        return permsSet;
+    }
+
+    public void setPermsSet(Set<String> permsSet) {
+        this.permsSet = permsSet;
+    }
+}

+ 82 - 0
kmall-common/src/main/java/com/kmall/common/entity/SysUserRoleEntity.java

@@ -0,0 +1,82 @@
+package com.kmall.common.entity;
+
+
+import java.io.Serializable;
+
+/**
+ * 用户与角色对应关系
+ *
+ * @author Scott
+ * @email
+ * @date 2016年9月18日 上午9:28:39
+ */
+public class SysUserRoleEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 角色ID
+     */
+    private Long roleId;
+
+    /**
+     * 设置:
+     *
+     * @param id
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * 获取:
+     *
+     * @return Long
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * 设置:用户ID
+     *
+     * @param userId 用户ID
+     */
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    /**
+     * 获取:用户ID
+     *
+     * @return Long
+     */
+    public Long getUserId() {
+        return userId;
+    }
+
+    /**
+     * 设置:角色ID
+     *
+     * @param roleId 角色ID
+     */
+    public void setRoleId(Long roleId) {
+        this.roleId = roleId;
+    }
+
+    /**
+     * 获取:角色ID
+     *
+     * @return Long
+     */
+    public Long getRoleId() {
+        return roleId;
+    }
+
+}

+ 88 - 0
kmall-common/src/main/java/com/kmall/common/entity/TemplateConfVo.java

@@ -0,0 +1,88 @@
+package com.kmall.common.entity;
+
+import java.io.Serializable;
+
+/**
+ * 微信模板消息日志实体
+ * 表名 mall_template_conf
+ *
+ * @author Scott
+ * @email
+ * @date 2017-12-02 16:49:29
+ */
+public class TemplateConfVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     *
+     */
+    private Integer id;
+    /**
+     * 模板类型 1订单付款成功 2团购成功 3拼团失败通知 4拼团进度 5 6 7订单配送 8订单评价提醒
+     */
+    private Integer templateType;
+    /**
+     * 推送模板Id
+     */
+    private String templateId;
+    /**
+     *
+     */
+    private String desc;
+
+    /**
+     * 设置:
+     */
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    /**
+     * 获取:
+     */
+    public Integer getId() {
+        return id;
+    }
+
+    /**
+     * 设置:模板类型 1订单付款成功 2团购成功 3拼团失败通知 4拼团进度 5 6 7订单配送 8订单评价提醒
+     */
+    public void setTemplateType(Integer templateType) {
+        this.templateType = templateType;
+    }
+
+    /**
+     * 获取:模板类型 1订单付款成功 2团购成功 3拼团失败通知 4拼团进度 5 6砍 7订单配送 8订单评价提醒
+     */
+    public Integer getTemplateType() {
+        return templateType;
+    }
+
+    /**
+     * 设置:推送模板Id
+     */
+    public void setTemplateId(String templateId) {
+        this.templateId = templateId;
+    }
+
+    /**
+     * 获取:推送模板Id
+     */
+    public String getTemplateId() {
+        return templateId;
+    }
+
+    /**
+     * 设置:
+     */
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+
+    /**
+     * 获取:
+     */
+    public String getDesc() {
+        return desc;
+    }
+}

+ 166 - 0
kmall-common/src/main/java/com/kmall/common/entity/Tree.java

@@ -0,0 +1,166 @@
+/*
+ * 创建时间:2017-08-14 22:08
+ * 项目名称:kmall_pt
+ * 类名称:Tree.java
+ * 包名称:com.kmall.common.entity
+ *
+ * 修改履历:
+ *          日期              修正者        主要内容
+ *                                      
+ *
+ * Copyright (c) 2016-2017 兆尹科技
+ */
+package com.kmall.common.entity;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 名称:Tree <br>
+ * 描述:<br>
+ *
+ * @author Scott
+ * @version 1.0
+ * @since 1.0.0
+ */
+public class Tree<T> implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /**********iview tree属性**************/
+    /**
+     * 标题
+     */
+    private String title;
+
+    /**
+     * 是否展开直子节点
+     */
+    private boolean expand = false;
+
+    /**
+     * 禁掉响应
+     */
+    private boolean disabled = false;
+    /**
+     * 禁掉 checkbox
+     */
+    private boolean disableCheckbox = false;
+    /**
+     * 是否选中子节点
+     */
+    private boolean selected = false;
+    /**
+     * 是否勾选(如果勾选,子节点也会全部勾选)
+     */
+    private boolean checked = false;
+
+    private boolean leaf = false;
+    /**
+     * ztree属性
+     */
+    private Boolean open;
+
+    private List<?> list;
+
+    /**
+     * 子节点属性数组
+     */
+    private List<?> children;
+    private String value;
+    private String label;
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public boolean isExpand() {
+        return expand;
+    }
+
+    public void setExpand(boolean expand) {
+        this.expand = expand;
+    }
+
+    public boolean isDisabled() {
+        return disabled;
+    }
+
+    public void setDisabled(boolean disabled) {
+        this.disabled = disabled;
+    }
+
+    public boolean isDisableCheckbox() {
+        return disableCheckbox;
+    }
+
+    public void setDisableCheckbox(boolean disableCheckbox) {
+        this.disableCheckbox = disableCheckbox;
+    }
+
+    public boolean isSelected() {
+        return selected;
+    }
+
+    public void setSelected(boolean selected) {
+        this.selected = selected;
+    }
+
+    public boolean isChecked() {
+        return checked;
+    }
+
+    public void setChecked(boolean checked) {
+        this.checked = checked;
+    }
+
+    public List<?> getChildren() {
+        return children;
+    }
+
+    public void setChildren(List<?> children) {
+        this.children = children;
+    }
+
+    public boolean isLeaf() {
+        return leaf;
+    }
+
+    public void setLeaf(boolean leaf) {
+        this.leaf = leaf;
+    }
+
+    public Boolean getOpen() {
+        return open;
+    }
+
+    public void setOpen(Boolean open) {
+        this.open = open;
+    }
+
+    public List<?> getList() {
+        return list;
+    }
+
+    public void setList(List<?> list) {
+        this.list = list;
+    }
+}

+ 70 - 0
kmall-common/src/main/java/com/kmall/common/exception/Exceptions.java

@@ -0,0 +1,70 @@
+package com.kmall.common.exception;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * 关于异常的工具类.
+ *
+ * @author calvin
+ * @version 2013-01-15
+ */
+public class Exceptions {
+
+    /**
+     * 将CheckedException转换为UncheckedException.
+     */
+    public static RuntimeException unchecked(Exception e) {
+        if (e instanceof RuntimeException) {
+            return (RuntimeException) e;
+        } else {
+            return new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 将ErrorStack转化为String.
+     */
+    public static String getStackTraceAsString(Throwable e) {
+        if (e == null) {
+            return "";
+        }
+        StringWriter stringWriter = new StringWriter();
+        e.printStackTrace(new PrintWriter(stringWriter));
+        return stringWriter.toString();
+    }
+
+    /**
+     * 判断异常是否由某些底层的异常引起.
+     */
+    public static boolean isCausedBy(Exception ex, Class<? extends Exception>... causeExceptionClasses) {
+        Throwable cause = ex.getCause();
+        while (cause != null) {
+            for (Class<? extends Exception> causeClass : causeExceptionClasses) {
+                if (causeClass.isInstance(cause)) {
+                    return true;
+                }
+            }
+            cause = cause.getCause();
+        }
+        return false;
+    }
+
+    /**
+     * 在request中获取异常类
+     *
+     * @param request
+     * @return
+     */
+    public static Throwable getThrowable(HttpServletRequest request) {
+        Throwable ex = null;
+        if (request.getAttribute("exception") != null) {
+            ex = (Throwable) request.getAttribute("exception");
+        } else if (request.getAttribute("javax.servlet.error.exception") != null) {
+            ex = (Throwable) request.getAttribute("javax.servlet.error.exception");
+        }
+        return ex;
+    }
+
+}

+ 429 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/common/Base64.java

@@ -0,0 +1,429 @@
+package com.kmall.common.fileserver.common;
+
+import java.io.IOException;
+
+/**
+ * Freeware from:
+ * Roedy Green
+ * Canadian Mind Products
+ * #327 - 964 Heywood Avenue
+ * Victoria, BC Canada V8V 2Y5
+ * tel:(250) 361-9093
+ * mailto:roedy@mindprod.com
+ */
+
+/**
+ * Encode arbitrary binary into printable ASCII using BASE64 encoding.
+ * very loosely based on the Base64 Reader by
+ * Dr. Mark Thornton
+ * Optrak Distribution Software Ltd.
+ * http://www.optrak.co.uk
+ * and Kevin Kelley's  http://www.ruralnet.net/~kelley/java/Base64.java
+ * <p>
+ * Base64 is a way of encoding 8-bit characters using only ASCII printable
+ * characters similar to UUENCODE.  UUENCODE includes a filename where BASE64 does not.
+ * The spec is described in RFC 2045.  Base64 is a scheme where
+ * 3 bytes are concatenated, then split to form 4 groups of 6-bits each; and
+ * each 6-bits gets translated to an encoded printable ASCII character, via a
+ * table lookup.  An encoded string is therefore longer than the original by
+ * about 1/3.  The "=" character is used to pad the end.  Base64 is used,
+ * among other things, to encode the user:password string in an
+ * Authorization: header for HTTP.  Don't confuse Base64 with
+ * x-www-form-urlencoded which is handled by
+ * Java.net.URLEncoder.encode/decode
+ * If you don't like this code, there is another implementation at http://www.ruffboy.com/download.htm
+ * Sun has an undocumented method called sun.misc.Base64Encoder.encode.
+ * You could use hex, simpler to code, but not as compact.
+ * <p>
+ * If you wanted to encode a giant file, you could do it in large chunks that
+ * are even multiples of 3 bytes, except for the last chunk, and append the outputs.
+ * <p>
+ * To encode a string, rather than binary data java.net.URLEncoder may be better. See
+ * printable characters in the Java glossary for a discussion of the differences.
+ * <p>
+ * version 1.4 2002 February 15  -- correct bugs with uneven line lengths,
+ * allow you to configure line separator.
+ * now need Base64 object and instance methods.
+ * new mailing address.
+ * version 1.3 2000 September 12 -- fix problems with estimating output length in encode
+ * version 1.2 2000 September 09 -- now handles decode as well.
+ * version 1.1 1999 December 04 -- more symmetrical encoding algorithm.
+ * more accurate StringBuffer allocation size.
+ * version 1.0 1999 December 03 -- posted in comp.lang.java.programmer.
+ * Futures Streams or files.
+ */
+
+public class Base64 {
+
+  /**
+   * Marker value for chars we just ignore, e.g. \n \r high ascii
+   */
+  static final int IGNORE = -1;
+  /**
+   * Marker for = trailing pad
+   */
+  static final int PAD = -2;
+  /**
+   * used to disable test driver
+   */
+  private static final boolean debug = true;
+  /**
+   * how we separate lines, e.g. \n, \r\n, \r etc.
+   */
+  private String lineSeparator = System.getProperty("line.separator");
+  /**
+   * max chars per line, excluding lineSeparator.  A multiple of 4.
+   */
+  private int lineLength = 72;
+  private char[] valueToChar = new char[64];
+  /**
+   * binary value encoded by a given letter of the alphabet 0..63
+   */
+  private int[] charToValue = new int[256];
+  private int[] charToPad = new int[4];
+
+  /* constructor */
+  public Base64() {
+    this.init('+', '/', '=');
+  }
+
+  /* constructor */
+  public Base64(char chPlus, char chSplash, char chPad, int lineLength) {
+    this.init(chPlus, chSplash, chPad);
+    this.lineLength = lineLength;
+  }
+
+  public Base64(int lineLength) {
+    this.lineLength = lineLength;
+  }
+
+  /**
+   * debug display array
+   */
+  public static void show(byte[] b) {
+    int count = 0;
+    int rows = 0;
+
+
+    for (int i = 0; i < b.length; i++) {
+      if (count == 8) {
+        System.out.print("  ");
+      } else if (count == 16) {
+        System.out.println("");
+        count = 0;
+        continue;
+      }
+      System.out.print(Integer.toHexString(b[i] & 0xFF).toUpperCase() + " ");
+      count++;
+
+    }
+    System.out.println();
+  }
+
+  /**
+   * debug display array
+   */
+  public static void display(byte[] b) {
+    for (int i = 0; i < b.length; i++) {
+      System.out.print((char) b[i]);
+    }
+    System.out.println();
+  }
+
+  public static void test() {
+    try {
+      Base64 b64 = new Base64();
+
+      //encode
+      //str64 = b64.encode(str.getBytes());
+      //System.out.println(str64);
+
+      String str64 = "CwUEFYoAAAADjQMC7ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267EI=";
+      //decode
+      byte[] theBytes = b64.decode(str64);
+      show(theBytes);
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+
+  /* initialise defaultValueToChar and defaultCharToValue tables */
+  private void init(char chPlus, char chSplash, char chPad) {
+    int index = 0;
+    // build translate this.valueToChar table only once.
+    // 0..25 -> 'A'..'Z'
+    for (int i = 'A'; i <= 'Z'; i++) {
+      this.valueToChar[index++] = (char) i;
+    }
+
+    // 26..51 -> 'a'..'z'
+    for (int i = 'a'; i <= 'z'; i++) {
+      this.valueToChar[index++] = (char) i;
+    }
+
+    // 52..61 -> '0'..'9'
+    for (int i = '0'; i <= '9'; i++) {
+      this.valueToChar[index++] = (char) i;
+    }
+
+    this.valueToChar[index++] = chPlus;
+    this.valueToChar[index++] = chSplash;
+
+    // build translate defaultCharToValue table only once.
+    for (int i = 0; i < 256; i++) {
+      this.charToValue[i] = IGNORE;  // default is to ignore
+    }
+
+    for (int i = 0; i < 64; i++) {
+      this.charToValue[this.valueToChar[i]] = i;
+    }
+
+    this.charToValue[chPad] = PAD;
+    java.util.Arrays.fill(this.charToPad, chPad);
+  }
+
+  /**
+   * Encode an arbitrary array of bytes as Base64 printable ASCII.
+   * It will be broken into lines of 72 chars each.  The last line is not
+   * terminated with a line separator.
+   * The output will always have an even multiple of data characters,
+   * exclusive of \n.  It is padded out with =.
+   */
+  public String encode(byte[] b) throws IOException {
+    // Each group or partial group of 3 bytes becomes four chars
+    // covered quotient
+    int outputLength = ((b.length + 2) / 3) * 4;
+
+    // account for trailing newlines, on all but the very last line
+    if (lineLength != 0) {
+      int lines = (outputLength + lineLength - 1) / lineLength - 1;
+      if (lines > 0) {
+        outputLength += lines * lineSeparator.length();
+      }
+    }
+
+    // must be local for recursion to work.
+    StringBuffer sb = new StringBuffer(outputLength);
+
+    // must be local for recursion to work.
+    int linePos = 0;
+
+    // first deal with even multiples of 3 bytes.
+    int len = (b.length / 3) * 3;
+    int leftover = b.length - len;
+    for (int i = 0; i < len; i += 3) {
+      // Start a new line if next 4 chars won't fit on the current line
+      // We can't encapsulete the following code since the variable need to
+      // be local to this incarnation of encode.
+      linePos += 4;
+      if (linePos > lineLength) {
+        if (lineLength != 0) {
+          sb.append(lineSeparator);
+        }
+        linePos = 4;
+      }
+
+      // get next three bytes in unsigned form lined up,
+      // in big-endian order
+      int combined = b[i + 0] & 0xff;
+      combined <<= 8;
+      combined |= b[i + 1] & 0xff;
+      combined <<= 8;
+      combined |= b[i + 2] & 0xff;
+
+      // break those 24 bits into a 4 groups of 6 bits,
+      // working LSB to MSB.
+      int c3 = combined & 0x3f;
+      combined >>>= 6;
+      int c2 = combined & 0x3f;
+      combined >>>= 6;
+      int c1 = combined & 0x3f;
+      combined >>>= 6;
+      int c0 = combined & 0x3f;
+
+      // Translate into the equivalent alpha character
+      // emitting them in big-endian order.
+      sb.append(valueToChar[c0]);
+      sb.append(valueToChar[c1]);
+      sb.append(valueToChar[c2]);
+      sb.append(valueToChar[c3]);
+    }
+
+    // deal with leftover bytes
+    switch (leftover) {
+      case 0:
+      default:
+        // nothing to do
+        break;
+
+      case 1:
+        // One leftover byte generates xx==
+        // Start a new line if next 4 chars won't fit on the current line
+        linePos += 4;
+        if (linePos > lineLength) {
+
+          if (lineLength != 0) {
+            sb.append(lineSeparator);
+          }
+          linePos = 4;
+        }
+
+        // Handle this recursively with a faked complete triple.
+        // Throw away last two chars and replace with ==
+        sb.append(encode(new byte[]{b[len], 0, 0}
+        ).substring(0, 2));
+        sb.append("==");
+        break;
+
+      case 2:
+        // Two leftover bytes generates xxx=
+        // Start a new line if next 4 chars won't fit on the current line
+        linePos += 4;
+        if (linePos > lineLength) {
+          if (lineLength != 0) {
+            sb.append(lineSeparator);
+          }
+          linePos = 4;
+        }
+        // Handle this recursively with a faked complete triple.
+        // Throw away last char and replace with =
+        sb.append(encode(new byte[]{b[len], b[len + 1], 0}
+        ).substring(0, 3));
+        sb.append("=");
+        break;
+
+    } // end switch;
+
+    if (outputLength != sb.length()) {
+      System.out.println("oops: minor program flaw: output length mis-estimated");
+      System.out.println("estimate:" + outputLength);
+      System.out.println("actual:" + sb.length());
+    }
+    return sb.toString();
+  }// end encode
+
+  /**
+   * decode a well-formed complete Base64 string back into an array of bytes.
+   * It must have an even multiple of 4 data characters (not counting \n),
+   * padded out with = as needed.
+   */
+  public byte[] decodeAuto(String s) {
+    int nRemain = s.length() % 4;
+    if (nRemain == 0) {
+      return this.decode(s);
+    } else {
+      return this.decode(s + new String(this.charToPad, 0, 4 - nRemain));
+    }
+  }
+
+  /**
+   * decode a well-formed complete Base64 string back into an array of bytes.
+   * It must have an even multiple of 4 data characters (not counting \n),
+   * padded out with = as needed.
+   */
+  public byte[] decode(String s) {
+
+    // estimate worst case size of output array, no embedded newlines.
+    byte[] b = new byte[(s.length() / 4) * 3];
+
+    // tracks where we are in a cycle of 4 input chars.
+    int cycle = 0;
+
+    // where we combine 4 groups of 6 bits and take apart as 3 groups of 8.
+    int combined = 0;
+
+    // how many bytes we have prepared.
+    int j = 0;
+    // will be an even multiple of 4 chars, plus some embedded \n
+    int len = s.length();
+    int dummies = 0;
+    for (int i = 0; i < len; i++) {
+
+      int c = s.charAt(i);
+      int value = (c <= 255) ? charToValue[c] : IGNORE;
+      // there are two magic values PAD (=) and IGNORE.
+      switch (value) {
+        case IGNORE:
+          // e.g. \n, just ignore it.
+          break;
+
+        case PAD:
+          value = 0;
+          dummies++;
+          // fallthrough
+        default:
+               /* regular value character */
+          switch (cycle) {
+            case 0:
+              combined = value;
+              cycle = 1;
+              break;
+
+            case 1:
+              combined <<= 6;
+              combined |= value;
+              cycle = 2;
+              break;
+
+            case 2:
+              combined <<= 6;
+              combined |= value;
+              cycle = 3;
+              break;
+
+            case 3:
+              combined <<= 6;
+              combined |= value;
+              // we have just completed a cycle of 4 chars.
+              // the four 6-bit values are in combined in big-endian order
+              // peel them off 8 bits at a time working lsb to msb
+              // to get our original 3 8-bit bytes back
+
+              b[j + 2] = (byte) combined;
+              combined >>>= 8;
+              b[j + 1] = (byte) combined;
+              combined >>>= 8;
+              b[j] = (byte) combined;
+              j += 3;
+              cycle = 0;
+              break;
+          }
+          break;
+      }
+    } // end for
+    if (cycle != 0) {
+      throw new ArrayIndexOutOfBoundsException("Input to decode not an even multiple of 4 characters; pad with =.");
+    }
+    j -= dummies;
+    if (b.length != j) {
+      byte[] b2 = new byte[j];
+      System.arraycopy(b, 0, b2, 0, j);
+      b = b2;
+    }
+    return b;
+
+  }// end decode
+
+  /**
+   * determines how long the lines are that are generated by encode.
+   * Ignored by decode.
+   *
+   * @param length 0 means no newlines inserted. Must be a multiple of 4.
+   */
+  public void setLineLength(int length) {
+    this.lineLength = (length / 4) * 4;
+  }
+
+  /**
+   * How lines are separated.
+   * Ignored by decode.
+   *
+   * @param lineSeparator may be "" but not null.
+   *                      Usually contains only a combination of chars \n and \r.
+   *                      Could be any chars not in set A-Z a-z 0-9 + /.
+   */
+  public void setLineSeparator(String lineSeparator) {
+    this.lineSeparator = lineSeparator;
+  }
+} // end Base64
+

+ 216 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/common/IniFileReader.java

@@ -0,0 +1,216 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ * <p>
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ **/
+
+package com.kmall.common.fileserver.common;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Hashtable;
+
+/**
+ * ini file reader / parser
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.0
+ */
+public class IniFileReader {
+  private Hashtable paramTable;
+  private String conf_filename;
+
+  /**
+   * @param conf_filename config filename
+   */
+  public IniFileReader(String conf_filename) throws IOException {
+    this.conf_filename = conf_filename;
+    loadFromFile(conf_filename);
+  }
+
+  public static ClassLoader classLoader() {
+    ClassLoader loader = Thread.currentThread().getContextClassLoader();
+    if (loader == null) {
+      loader = ClassLoader.getSystemClassLoader();
+    }
+    return loader;
+  }
+
+  public static InputStream loadFromOsFileSystemOrClasspathAsStream(String filePath) {
+    InputStream in = null;
+    try {
+      // 优先从文件系统路径加载
+      if (new File(filePath).exists()) {
+        in = new FileInputStream(filePath);
+        //System.out.println("loadFrom...file path done");
+      }
+      // 从类路径加载
+      else {
+        in = classLoader().getResourceAsStream(filePath);
+        //System.out.println("loadFrom...class path done");
+      }
+    } catch (Exception ex) {
+      ex.printStackTrace();
+    }
+    return in;
+  }
+
+  /**
+   * get the config filename
+   *
+   * @return config filename
+   */
+  public String getConfFilename() {
+    return this.conf_filename;
+  }
+
+  /**
+   * get string value from config file
+   *
+   * @param name item name in config file
+   * @return string value
+   */
+  public String getStrValue(String name) {
+    Object obj;
+    obj = this.paramTable.get(name);
+    if (obj == null) {
+      return null;
+    }
+
+    if (obj instanceof String) {
+      return (String) obj;
+    }
+
+    return (String) ((ArrayList) obj).get(0);
+  }
+
+  /**
+   * get int value from config file
+   *
+   * @param name          item name in config file
+   * @param default_value the default value
+   * @return int value
+   */
+  public int getIntValue(String name, int default_value) {
+    String szValue = this.getStrValue(name);
+    if (szValue == null) {
+      return default_value;
+    }
+
+    return Integer.parseInt(szValue);
+  }
+
+  /**
+   * get boolean value from config file
+   *
+   * @param name          item name in config file
+   * @param default_value the default value
+   * @return boolean value
+   */
+  public boolean getBoolValue(String name, boolean default_value) {
+    String szValue = this.getStrValue(name);
+    if (szValue == null) {
+      return default_value;
+    }
+
+    return szValue.equalsIgnoreCase("yes") || szValue.equalsIgnoreCase("on") ||
+      szValue.equalsIgnoreCase("true") || szValue.equals("1");
+  }
+
+  /**
+   * get all values from config file
+   *
+   * @param name item name in config file
+   * @return string values (array)
+   */
+  public String[] getValues(String name) {
+    Object obj;
+    String[] values;
+
+    obj = this.paramTable.get(name);
+    if (obj == null) {
+      return null;
+    }
+
+    if (obj instanceof String) {
+      values = new String[1];
+      values[0] = (String) obj;
+      return values;
+    }
+
+    Object[] objs = ((ArrayList) obj).toArray();
+    values = new String[objs.length];
+    System.arraycopy(objs, 0, values, 0, objs.length);
+    return values;
+  }
+
+  private void loadFromFile(String confFilePath) throws IOException {
+    InputStream in = loadFromOsFileSystemOrClasspathAsStream(confFilePath);
+    try {
+      readToParamTable(in);
+    } catch (Exception ex) {
+      ex.printStackTrace();
+    } finally {
+      try {
+        if (in != null) in.close();
+        //System.out.println("loadFrom...finally...in.close(); done");
+      } catch (Exception ex) {
+        ex.printStackTrace();
+      }
+    }
+  }
+
+  private void readToParamTable(InputStream in) throws IOException {
+    this.paramTable = new Hashtable();
+    if (in == null) return;
+    String line;
+    String[] parts;
+    String name;
+    String value;
+    Object obj;
+    ArrayList valueList;
+    InputStreamReader inReader = null;
+    BufferedReader bufferedReader = null;
+    try {
+      inReader = new InputStreamReader(in);
+      bufferedReader = new BufferedReader(inReader);
+      while ((line = bufferedReader.readLine()) != null) {
+        line = line.trim();
+        if (line.length() == 0 || line.charAt(0) == '#') {
+          continue;
+        }
+        parts = line.split("=", 2);
+        if (parts.length != 2) {
+          continue;
+        }
+        name = parts[0].trim();
+        value = parts[1].trim();
+        obj = this.paramTable.get(name);
+        if (obj == null) {
+          this.paramTable.put(name, value);
+        } else if (obj instanceof String) {
+          valueList = new ArrayList();
+          valueList.add(obj);
+          valueList.add(value);
+          this.paramTable.put(name, valueList);
+        } else {
+          valueList = (ArrayList) obj;
+          valueList.add(value);
+        }
+      }
+    } catch (Exception ex) {
+      ex.printStackTrace();
+    } finally {
+      try {
+        if (bufferedReader != null) bufferedReader.close();
+        if (inReader != null) inReader.close();
+        //System.out.println("readToParamTable...finally...bufferedReader.close();inReader.close(); done");
+      } catch (Exception ex) {
+        ex.printStackTrace();
+      }
+    }
+  }
+
+}

+ 24 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/common/MyException.java

@@ -0,0 +1,24 @@
+/*
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS Java Client may be copied only under the terms of the GNU Lesser
+* General Public License (LGPL).
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+*/
+
+package com.kmall.common.fileserver.common;
+
+/**
+ * My Exception
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.0
+ */
+public class MyException extends Exception {
+  public MyException() {
+  }
+
+  public MyException(String message) {
+    super(message);
+  }
+}

+ 48 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/common/NameValuePair.java

@@ -0,0 +1,48 @@
+/*
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS Java Client may be copied only under the terms of the GNU Lesser
+* General Public License (LGPL).
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+*/
+
+package com.kmall.common.fileserver.common;
+
+/**
+ * name(key) and value pair model
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.0
+ */
+public class NameValuePair {
+  protected String name;
+  protected String value;
+
+  public NameValuePair() {
+  }
+
+  public NameValuePair(String name) {
+    this.name = name;
+  }
+
+  public NameValuePair(String name, String value) {
+    this.name = name;
+    this.value = value;
+  }
+
+  public String getName() {
+    return this.name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getValue() {
+    return this.value;
+  }
+
+  public void setValue(String value) {
+    this.value = value;
+  }
+}

+ 304 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/ClientGlobal.java

@@ -0,0 +1,304 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ * <p>
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ **/
+
+package com.kmall.common.fileserver.fastdfs;
+
+import com.kmall.common.fileserver.common.IniFileReader;
+import com.kmall.common.fileserver.common.MyException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * Global variables
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.11
+ */
+public class ClientGlobal {
+
+	public static final String CONF_KEY_CONNECT_TIMEOUT = "connect_timeout";
+	public static final String CONF_KEY_NETWORK_TIMEOUT = "network_timeout";
+	public static final String CONF_KEY_CHARSET = "charset";
+	public static final String CONF_KEY_HTTP_ANTI_STEAL_TOKEN = "http.anti_steal_token";
+	public static final String CONF_KEY_HTTP_SECRET_KEY = "http.secret_key";
+	public static final String CONF_KEY_HTTP_TRACKER_HTTP_PORT = "http.tracker_http_port";
+	public static final String CONF_KEY_TRACKER_SERVER = "tracker_server";
+
+	public static final String PROP_KEY_CONNECT_TIMEOUT_IN_SECONDS = "fastdfs.connect_timeout_in_seconds";
+	public static final String PROP_KEY_NETWORK_TIMEOUT_IN_SECONDS = "fastdfs.network_timeout_in_seconds";
+	public static final String PROP_KEY_CHARSET = "fastdfs.charset";
+	public static final String PROP_KEY_HTTP_ANTI_STEAL_TOKEN = "fastdfs.http_anti_steal_token";
+	public static final String PROP_KEY_HTTP_SECRET_KEY = "fastdfs.http_secret_key";
+	public static final String PROP_KEY_HTTP_TRACKER_HTTP_PORT = "fastdfs.http_tracker_http_port";
+	public static final String PROP_KEY_TRACKER_SERVERS = "fastdfs.tracker_servers";
+
+	public static final int DEFAULT_CONNECT_TIMEOUT = 5; // second
+	public static final int DEFAULT_NETWORK_TIMEOUT = 30; // second
+	public static final String DEFAULT_CHARSET = "UTF-8";
+	public static final boolean DEFAULT_HTTP_ANTI_STEAL_TOKEN = false;
+	public static final String DEFAULT_HTTP_SECRET_KEY = "FastDFS1234567890";
+	public static final int DEFAULT_HTTP_TRACKER_HTTP_PORT = 80;
+
+	public static int g_connect_timeout = DEFAULT_CONNECT_TIMEOUT * 1000; // millisecond
+	public static int g_network_timeout = DEFAULT_NETWORK_TIMEOUT * 1000; // millisecond
+	public static String g_charset = DEFAULT_CHARSET;
+	public static boolean g_anti_steal_token = DEFAULT_HTTP_ANTI_STEAL_TOKEN; // if anti-steal token
+	public static String g_secret_key = DEFAULT_HTTP_SECRET_KEY; // generage token secret key
+	public static int g_tracker_http_port = DEFAULT_HTTP_TRACKER_HTTP_PORT;
+
+	public static String http_tracket_nginx_addr = null;
+	public static String http_tracket_server_port = null;
+	public static String file_author = null;
+
+	public static TrackerGroup g_tracker_group;
+
+	private ClientGlobal() {
+	}
+
+	/**
+	 * load global variables
+	 *
+	 * @param conf_filename
+	 *            config filename
+	 */
+	public static void init(String conf_filename) throws IOException, MyException {
+		IniFileReader iniReader;
+		String[] szTrackerServers;
+		String[] parts;
+
+		iniReader = new IniFileReader(conf_filename);
+
+		g_connect_timeout = iniReader.getIntValue("connect_timeout", DEFAULT_CONNECT_TIMEOUT);
+		if (g_connect_timeout < 0) {
+			g_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
+		}
+		g_connect_timeout *= 1000; // millisecond
+
+		g_network_timeout = iniReader.getIntValue("network_timeout", DEFAULT_NETWORK_TIMEOUT);
+		if (g_network_timeout < 0) {
+			g_network_timeout = DEFAULT_NETWORK_TIMEOUT;
+		}
+		g_network_timeout *= 1000; // millisecond
+
+		g_charset = iniReader.getStrValue("charset");
+		if (g_charset == null || g_charset.length() == 0) {
+			g_charset = "ISO8859-1";
+		}
+
+		szTrackerServers = iniReader.getValues("tracker_server");
+		if (szTrackerServers == null) {
+			throw new MyException("item \"tracker_server\" in " + conf_filename + " not found");
+		}
+
+		InetSocketAddress[] tracker_servers = new InetSocketAddress[szTrackerServers.length];
+		for (int i = 0; i < szTrackerServers.length; i++) {
+			parts = szTrackerServers[i].split("\\:", 2);
+			if (parts.length != 2) {
+				throw new MyException("the value of item \"tracker_server\" is invalid, the correct format is host:port");
+			}
+
+			tracker_servers[i] = new InetSocketAddress(parts[0].trim(), Integer.parseInt(parts[1].trim()));
+		}
+		g_tracker_group = new TrackerGroup(tracker_servers);
+
+		g_tracker_http_port = iniReader.getIntValue("http.tracker_http_port", 80);
+		g_anti_steal_token = iniReader.getBoolValue("http.anti_steal_token", false);
+		if (g_anti_steal_token) {
+			g_secret_key = iniReader.getStrValue("http.secret_key");
+		}
+
+		http_tracket_nginx_addr = iniReader.getStrValue("http.tracket_nginx_addr");
+		http_tracket_server_port = iniReader.getStrValue("http.tracker_server_port");
+		file_author = iniReader.getStrValue("file.author");
+
+	}
+
+	/**
+	 * load from properties file
+	 *
+	 * @param propsFilePath
+	 */
+	public static void initByProperties(String propsFilePath) throws IOException, MyException {
+		Properties props = new Properties();
+		InputStream in = IniFileReader.loadFromOsFileSystemOrClasspathAsStream(propsFilePath);
+		if (in != null) {
+			props.load(in);
+		}
+		initByProperties(props);
+	}
+
+	public static void initByProperties(Properties props) throws IOException, MyException {
+		String trackerServersConf = props.getProperty(PROP_KEY_TRACKER_SERVERS);
+		if (trackerServersConf == null || trackerServersConf.trim().length() == 0) {
+			throw new MyException(String.format("configure item %s is required", PROP_KEY_TRACKER_SERVERS));
+		}
+		initByTrackers(trackerServersConf.trim());
+
+		String connectTimeoutInSecondsConf = props.getProperty(PROP_KEY_CONNECT_TIMEOUT_IN_SECONDS);
+		String networkTimeoutInSecondsConf = props.getProperty(PROP_KEY_NETWORK_TIMEOUT_IN_SECONDS);
+		String charsetConf = props.getProperty(PROP_KEY_CHARSET);
+		String httpAntiStealTokenConf = props.getProperty(PROP_KEY_HTTP_ANTI_STEAL_TOKEN);
+		String httpSecretKeyConf = props.getProperty(PROP_KEY_HTTP_SECRET_KEY);
+		String httpTrackerHttpPortConf = props.getProperty(PROP_KEY_HTTP_TRACKER_HTTP_PORT);
+		if (connectTimeoutInSecondsConf != null && connectTimeoutInSecondsConf.trim().length() != 0) {
+			g_connect_timeout = Integer.parseInt(connectTimeoutInSecondsConf.trim()) * 1000;
+		}
+		if (networkTimeoutInSecondsConf != null && networkTimeoutInSecondsConf.trim().length() != 0) {
+			g_network_timeout = Integer.parseInt(networkTimeoutInSecondsConf.trim()) * 1000;
+		}
+		if (charsetConf != null && charsetConf.trim().length() != 0) {
+			g_charset = charsetConf.trim();
+		}
+		if (httpAntiStealTokenConf != null && httpAntiStealTokenConf.trim().length() != 0) {
+			g_anti_steal_token = Boolean.parseBoolean(httpAntiStealTokenConf);
+		}
+		if (httpSecretKeyConf != null && httpSecretKeyConf.trim().length() != 0) {
+			g_secret_key = httpSecretKeyConf.trim();
+		}
+		if (httpTrackerHttpPortConf != null && httpTrackerHttpPortConf.trim().length() != 0) {
+			g_tracker_http_port = Integer.parseInt(httpTrackerHttpPortConf);
+		}
+	}
+
+	/**
+	 * load from properties file
+	 *
+	 * @param trackerServers
+	 *            例如:"10.0.11.245:22122,10.0.11.246:22122" server的IP和端口用冒号':'分隔 server之间用逗号','分隔
+	 */
+	public static void initByTrackers(String trackerServers) throws IOException, MyException {
+		List<InetSocketAddress> list = new ArrayList();
+		String spr1 = ",";
+		String spr2 = ":";
+		String[] arr1 = trackerServers.trim().split(spr1);
+		for (String addrStr : arr1) {
+			String[] arr2 = addrStr.trim().split(spr2);
+			String host = arr2[0].trim();
+			int port = Integer.parseInt(arr2[1].trim());
+			list.add(new InetSocketAddress(host, port));
+		}
+		InetSocketAddress[] trackerAddresses = list.toArray(new InetSocketAddress[list.size()]);
+		initByTrackers(trackerAddresses);
+	}
+
+	public static void initByTrackers(InetSocketAddress[] trackerAddresses) throws IOException, MyException {
+		g_tracker_group = new TrackerGroup(trackerAddresses);
+	}
+
+	/**
+	 * construct Socket object
+	 *
+	 * @param ip_addr
+	 *            ip address or hostname
+	 * @param port
+	 *            port number
+	 * @return connected Socket object
+	 */
+	public static Socket getSocket(String ip_addr, int port) throws IOException {
+		Socket sock = new Socket();
+		sock.setSoTimeout(ClientGlobal.g_network_timeout);
+		sock.connect(new InetSocketAddress(ip_addr, port), ClientGlobal.g_connect_timeout);
+		return sock;
+	}
+
+	/**
+	 * construct Socket object
+	 *
+	 * @param addr
+	 *            InetSocketAddress object, including ip address and port
+	 * @return connected Socket object
+	 */
+	public static Socket getSocket(InetSocketAddress addr) throws IOException {
+		Socket sock = new Socket();
+		sock.setSoTimeout(ClientGlobal.g_network_timeout);
+		sock.connect(addr, ClientGlobal.g_connect_timeout);
+		return sock;
+	}
+
+	public static int getG_connect_timeout() {
+		return g_connect_timeout;
+	}
+
+	public static void setG_connect_timeout(int connect_timeout) {
+		ClientGlobal.g_connect_timeout = connect_timeout;
+	}
+
+	public static int getG_network_timeout() {
+		return g_network_timeout;
+	}
+
+	public static void setG_network_timeout(int network_timeout) {
+		ClientGlobal.g_network_timeout = network_timeout;
+	}
+
+	public static String getG_charset() {
+		return g_charset;
+	}
+
+	public static void setG_charset(String charset) {
+		ClientGlobal.g_charset = charset;
+	}
+
+	public static int getG_tracker_http_port() {
+		return g_tracker_http_port;
+	}
+
+	public static void setG_tracker_http_port(int tracker_http_port) {
+		ClientGlobal.g_tracker_http_port = tracker_http_port;
+	}
+
+	public static boolean getG_anti_steal_token() {
+		return g_anti_steal_token;
+	}
+
+	public static boolean isG_anti_steal_token() {
+		return g_anti_steal_token;
+	}
+
+	public static void setG_anti_steal_token(boolean anti_steal_token) {
+		ClientGlobal.g_anti_steal_token = anti_steal_token;
+	}
+
+	public static String getG_secret_key() {
+		return g_secret_key;
+	}
+
+	public static void setG_secret_key(String secret_key) {
+		ClientGlobal.g_secret_key = secret_key;
+	}
+
+	public static TrackerGroup getG_tracker_group() {
+		return g_tracker_group;
+	}
+
+	public static void setG_tracker_group(TrackerGroup tracker_group) {
+		ClientGlobal.g_tracker_group = tracker_group;
+	}
+
+	public static String configInfo() {
+		String trackerServers = "";
+		if (g_tracker_group != null) {
+			InetSocketAddress[] trackerAddresses = g_tracker_group.tracker_servers;
+			for (InetSocketAddress inetSocketAddress : trackerAddresses) {
+				if (trackerServers.length() > 0)
+					trackerServers += ",";
+				trackerServers += inetSocketAddress.toString().substring(1);
+			}
+		}
+		return "{" + "\n  g_connect_timeout(ms) = " + g_connect_timeout + "\n  g_network_timeout(ms) = " + g_network_timeout + "\n  g_charset = "
+				+ g_charset + "\n  g_anti_steal_token = " + g_anti_steal_token + "\n  g_secret_key = " + g_secret_key + "\n  g_tracker_http_port = "
+				+ g_tracker_http_port + "\n  trackerServers = " + trackerServers + "\n}";
+	}
+
+}

+ 27 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/DownloadCallback.java

@@ -0,0 +1,27 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ * <p>
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package com.kmall.common.fileserver.fastdfs;
+
+/**
+ * Download file callback interface
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.4
+ */
+public interface DownloadCallback {
+  /**
+   * recv file content callback function, may be called more than once when the file downloaded
+   *
+   * @param file_size file size
+   * @param data      data buff
+   * @param bytes     data bytes
+   * @return 0 success, return none zero(errno) if fail
+   */
+  public int recv(long file_size, byte[] data, int bytes);
+}

+ 44 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/DownloadStream.java

@@ -0,0 +1,44 @@
+package com.kmall.common.fileserver.fastdfs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Download file by stream (download callback class)
+ *
+ * @author zhouzezhong & Happy Fish / YuQing
+ * @version Version 1.11
+ */
+public class DownloadStream implements DownloadCallback {
+  private OutputStream out;
+  private long currentBytes = 0;
+
+  public DownloadStream(OutputStream out) {
+    super();
+    this.out = out;
+  }
+
+  /**
+   * recv file content callback function, may be called more than once when the file downloaded
+   *
+   * @param fileSize file size
+   * @param data     data buff
+   * @param bytes    data bytes
+   * @return 0 success, return none zero(errno) if fail
+   */
+  public int recv(long fileSize, byte[] data, int bytes) {
+    try {
+      out.write(data, 0, bytes);
+    } catch (IOException ex) {
+      ex.printStackTrace();
+      return -1;
+    }
+
+    currentBytes += bytes;
+    if (this.currentBytes == fileSize) {
+      this.currentBytes = 0;
+    }
+
+    return 0;
+  }
+}

+ 125 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/FileInfo.java

@@ -0,0 +1,125 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ * <p>
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package com.kmall.common.fileserver.fastdfs;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Server Info
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.23
+ */
+public class FileInfo {
+  protected String source_ip_addr;
+  protected long file_size;
+  protected Date create_timestamp;
+  protected int crc32;
+
+  /**
+   * Constructor
+   *
+   * @param file_size        the file size
+   * @param create_timestamp create timestamp in seconds
+   * @param crc32            the crc32 signature
+   * @param source_ip_addr   the source storage ip address
+   */
+  public FileInfo(long file_size, int create_timestamp, int crc32, String source_ip_addr) {
+    this.file_size = file_size;
+    this.create_timestamp = new Date(create_timestamp * 1000L);
+    this.crc32 = crc32;
+    this.source_ip_addr = source_ip_addr;
+  }
+
+  /**
+   * get the source ip address of the file uploaded to
+   *
+   * @return the source ip address of the file uploaded to
+   */
+  public String getSourceIpAddr() {
+    return this.source_ip_addr;
+  }
+
+  /**
+   * set the source ip address of the file uploaded to
+   *
+   * @param source_ip_addr the source ip address
+   */
+  public void setSourceIpAddr(String source_ip_addr) {
+    this.source_ip_addr = source_ip_addr;
+  }
+
+  /**
+   * get the file size
+   *
+   * @return the file size
+   */
+  public long getFileSize() {
+    return this.file_size;
+  }
+
+  /**
+   * set the file size
+   *
+   * @param file_size the file size
+   */
+  public void setFileSize(long file_size) {
+    this.file_size = file_size;
+  }
+
+  /**
+   * get the create timestamp of the file
+   *
+   * @return the create timestamp of the file
+   */
+  public Date getCreateTimestamp() {
+    return this.create_timestamp;
+  }
+
+  /**
+   * set the create timestamp of the file
+   *
+   * @param create_timestamp create timestamp in seconds
+   */
+  public void setCreateTimestamp(int create_timestamp) {
+    this.create_timestamp = new Date(create_timestamp * 1000L);
+  }
+
+  /**
+   * get the file CRC32 signature
+   *
+   * @return the file CRC32 signature
+   */
+  public long getCrc32() {
+    return this.crc32;
+  }
+
+  /**
+   * set the create timestamp of the file
+   *
+   * @param crc32 the crc32 signature
+   */
+  public void setCrc32(int crc32) {
+    this.crc32 = crc32;
+  }
+
+  /**
+   * to string
+   *
+   * @return string
+   */
+  public String toString() {
+    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+    return "source_ip_addr = " + this.source_ip_addr + ", " +
+      "file_size = " + this.file_size + ", " +
+      "create_timestamp = " + df.format(this.create_timestamp) + ", " +
+      "crc32 = " + this.crc32;
+  }
+}

+ 503 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/ProtoCommon.java

@@ -0,0 +1,503 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ * <p>
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ **/
+
+package com.kmall.common.fileserver.fastdfs;
+
+import com.kmall.common.fileserver.common.MyException;
+import com.kmall.common.fileserver.common.NameValuePair;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.Socket;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+/**
+ * protocol common functions
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.18
+ */
+public class ProtoCommon {
+  public static final byte FDFS_PROTO_CMD_QUIT = 82;
+  public static final byte TRACKER_PROTO_CMD_SERVER_LIST_GROUP = 91;
+  public static final byte TRACKER_PROTO_CMD_SERVER_LIST_STORAGE = 92;
+  public static final byte TRACKER_PROTO_CMD_SERVER_DELETE_STORAGE = 93;
+  public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE = 101;
+  public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE = 102;
+  public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE = 103;
+  public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ONE = 104;
+  public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ALL = 105;
+  public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ALL = 106;
+  public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ALL = 107;
+  public static final byte TRACKER_PROTO_CMD_RESP = 100;
+  public static final byte FDFS_PROTO_CMD_ACTIVE_TEST = 111;
+  public static final byte STORAGE_PROTO_CMD_UPLOAD_FILE = 11;
+  public static final byte STORAGE_PROTO_CMD_DELETE_FILE = 12;
+  public static final byte STORAGE_PROTO_CMD_SET_METADATA = 13;
+  public static final byte STORAGE_PROTO_CMD_DOWNLOAD_FILE = 14;
+  public static final byte STORAGE_PROTO_CMD_GET_METADATA = 15;
+  public static final byte STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE = 21;
+  public static final byte STORAGE_PROTO_CMD_QUERY_FILE_INFO = 22;
+  public static final byte STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE = 23;  //create appender file
+  public static final byte STORAGE_PROTO_CMD_APPEND_FILE = 24;  //append file
+  public static final byte STORAGE_PROTO_CMD_MODIFY_FILE = 34;  //modify appender file
+  public static final byte STORAGE_PROTO_CMD_TRUNCATE_FILE = 36;  //truncate appender file
+  public static final byte STORAGE_PROTO_CMD_RESP = TRACKER_PROTO_CMD_RESP;
+  public static final byte FDFS_STORAGE_STATUS_INIT = 0;
+  public static final byte FDFS_STORAGE_STATUS_WAIT_SYNC = 1;
+  public static final byte FDFS_STORAGE_STATUS_SYNCING = 2;
+  public static final byte FDFS_STORAGE_STATUS_IP_CHANGED = 3;
+  public static final byte FDFS_STORAGE_STATUS_DELETED = 4;
+  public static final byte FDFS_STORAGE_STATUS_OFFLINE = 5;
+  public static final byte FDFS_STORAGE_STATUS_ONLINE = 6;
+  public static final byte FDFS_STORAGE_STATUS_ACTIVE = 7;
+  public static final byte FDFS_STORAGE_STATUS_NONE = 99;
+  /**
+   * for overwrite all old metadata
+   */
+  public static final byte STORAGE_SET_METADATA_FLAG_OVERWRITE = 'O';
+  /**
+   * for replace, insert when the meta item not exist, otherwise update it
+   */
+  public static final byte STORAGE_SET_METADATA_FLAG_MERGE = 'M';
+  public static final int FDFS_PROTO_PKG_LEN_SIZE = 8;
+  public static final int FDFS_PROTO_CMD_SIZE = 1;
+  public static final int FDFS_GROUP_NAME_MAX_LEN = 16;
+  public static final int FDFS_IPADDR_SIZE = 16;
+  public static final int FDFS_DOMAIN_NAME_MAX_SIZE = 128;
+  public static final int FDFS_VERSION_SIZE = 6;
+  public static final int FDFS_STORAGE_ID_MAX_SIZE = 16;
+  public static final String FDFS_RECORD_SEPERATOR = "\u0001";
+  public static final String FDFS_FIELD_SEPERATOR = "\u0002";
+  public static final int TRACKER_QUERY_STORAGE_FETCH_BODY_LEN = FDFS_GROUP_NAME_MAX_LEN
+    + FDFS_IPADDR_SIZE - 1 + FDFS_PROTO_PKG_LEN_SIZE;
+  public static final int TRACKER_QUERY_STORAGE_STORE_BODY_LEN = FDFS_GROUP_NAME_MAX_LEN
+    + FDFS_IPADDR_SIZE + FDFS_PROTO_PKG_LEN_SIZE;
+  public static final byte FDFS_FILE_EXT_NAME_MAX_LEN = 6;
+  public static final byte FDFS_FILE_PREFIX_MAX_LEN = 16;
+  public static final byte FDFS_FILE_PATH_LEN = 10;
+  public static final byte FDFS_FILENAME_BASE64_LENGTH = 27;
+  public static final byte FDFS_TRUNK_FILE_INFO_LEN = 16;
+  public static final byte ERR_NO_ENOENT = 2;
+  public static final byte ERR_NO_EIO = 5;
+  public static final byte ERR_NO_EBUSY = 16;
+  public static final byte ERR_NO_EINVAL = 22;
+  public static final byte ERR_NO_ENOSPC = 28;
+  public static final byte ECONNREFUSED = 61;
+  public static final byte ERR_NO_EALREADY = 114;
+  public static final long INFINITE_FILE_SIZE = 256 * 1024L * 1024 * 1024 * 1024 * 1024L;
+  public static final long APPENDER_FILE_SIZE = INFINITE_FILE_SIZE;
+  public static final long TRUNK_FILE_MARK_SIZE = 512 * 1024L * 1024 * 1024 * 1024 * 1024L;
+  public static final long NORMAL_LOGIC_FILENAME_LENGTH = FDFS_FILE_PATH_LEN + FDFS_FILENAME_BASE64_LENGTH + FDFS_FILE_EXT_NAME_MAX_LEN + 1;
+  public static final long TRUNK_LOGIC_FILENAME_LENGTH = NORMAL_LOGIC_FILENAME_LENGTH + FDFS_TRUNK_FILE_INFO_LEN;
+  protected static final int PROTO_HEADER_CMD_INDEX = FDFS_PROTO_PKG_LEN_SIZE;
+  protected static final int PROTO_HEADER_STATUS_INDEX = FDFS_PROTO_PKG_LEN_SIZE + 1;
+
+  private ProtoCommon() {
+  }
+
+  public static String getStorageStatusCaption(byte status) {
+    switch (status) {
+      case FDFS_STORAGE_STATUS_INIT:
+        return "INIT";
+      case FDFS_STORAGE_STATUS_WAIT_SYNC:
+        return "WAIT_SYNC";
+      case FDFS_STORAGE_STATUS_SYNCING:
+        return "SYNCING";
+      case FDFS_STORAGE_STATUS_IP_CHANGED:
+        return "IP_CHANGED";
+      case FDFS_STORAGE_STATUS_DELETED:
+        return "DELETED";
+      case FDFS_STORAGE_STATUS_OFFLINE:
+        return "OFFLINE";
+      case FDFS_STORAGE_STATUS_ONLINE:
+        return "ONLINE";
+      case FDFS_STORAGE_STATUS_ACTIVE:
+        return "ACTIVE";
+      case FDFS_STORAGE_STATUS_NONE:
+        return "NONE";
+      default:
+        return "UNKOWN";
+    }
+  }
+
+  /**
+   * pack header by FastDFS transfer protocol
+   *
+   * @param cmd     which command to send
+   * @param pkg_len package body length
+   * @param errno   status code, should be (byte)0
+   * @return packed byte buffer
+   */
+  public static byte[] packHeader(byte cmd, long pkg_len, byte errno) throws UnsupportedEncodingException {
+    byte[] header;
+    byte[] hex_len;
+
+    header = new byte[FDFS_PROTO_PKG_LEN_SIZE + 2];
+    Arrays.fill(header, (byte) 0);
+
+    hex_len = ProtoCommon.long2buff(pkg_len);
+    System.arraycopy(hex_len, 0, header, 0, hex_len.length);
+    header[PROTO_HEADER_CMD_INDEX] = cmd;
+    header[PROTO_HEADER_STATUS_INDEX] = errno;
+    return header;
+  }
+
+  /**
+   * receive pack header
+   *
+   * @param in              input stream
+   * @param expect_cmd      expect response command
+   * @param expect_body_len expect response package body length
+   * @return RecvHeaderInfo: errno and pkg body length
+   */
+  public static RecvHeaderInfo recvHeader(InputStream in, byte expect_cmd, long expect_body_len) throws IOException {
+    byte[] header;
+    int bytes;
+    long pkg_len;
+
+    header = new byte[FDFS_PROTO_PKG_LEN_SIZE + 2];
+
+    if ((bytes = in.read(header)) != header.length) {
+      throw new IOException("recv package size " + bytes + " != " + header.length);
+    }
+
+    if (header[PROTO_HEADER_CMD_INDEX] != expect_cmd) {
+      throw new IOException("recv cmd: " + header[PROTO_HEADER_CMD_INDEX] + " is not correct, expect cmd: " + expect_cmd);
+    }
+
+    if (header[PROTO_HEADER_STATUS_INDEX] != 0) {
+      return new RecvHeaderInfo(header[PROTO_HEADER_STATUS_INDEX], 0);
+    }
+
+    pkg_len = ProtoCommon.buff2long(header, 0);
+    if (pkg_len < 0) {
+      throw new IOException("recv body length: " + pkg_len + " < 0!");
+    }
+
+    if (expect_body_len >= 0 && pkg_len != expect_body_len) {
+      throw new IOException("recv body length: " + pkg_len + " is not correct, expect length: " + expect_body_len);
+    }
+
+    return new RecvHeaderInfo((byte) 0, pkg_len);
+  }
+
+  /**
+   * receive whole pack
+   *
+   * @param in              input stream
+   * @param expect_cmd      expect response command
+   * @param expect_body_len expect response package body length
+   * @return RecvPackageInfo: errno and reponse body(byte buff)
+   */
+  public static RecvPackageInfo recvPackage(InputStream in, byte expect_cmd, long expect_body_len) throws IOException {
+    RecvHeaderInfo header = recvHeader(in, expect_cmd, expect_body_len);
+    if (header.errno != 0) {
+      return new RecvPackageInfo(header.errno, null);
+    }
+
+    byte[] body = new byte[(int) header.body_len];
+    int totalBytes = 0;
+    int remainBytes = (int) header.body_len;
+    int bytes;
+
+    while (totalBytes < header.body_len) {
+      if ((bytes = in.read(body, totalBytes, remainBytes)) < 0) {
+        break;
+      }
+
+      totalBytes += bytes;
+      remainBytes -= bytes;
+    }
+
+    if (totalBytes != header.body_len) {
+      throw new IOException("recv package size " + totalBytes + " != " + header.body_len);
+    }
+
+    return new RecvPackageInfo((byte) 0, body);
+  }
+
+  /**
+   * split metadata to name value pair array
+   *
+   * @param meta_buff metadata
+   * @return name value pair array
+   */
+  public static NameValuePair[] split_metadata(String meta_buff) {
+    return split_metadata(meta_buff, FDFS_RECORD_SEPERATOR, FDFS_FIELD_SEPERATOR);
+  }
+
+  /**
+   * split metadata to name value pair array
+   *
+   * @param meta_buff       metadata
+   * @param recordSeperator record/row seperator
+   * @param filedSeperator  field/column seperator
+   * @return name value pair array
+   */
+  public static NameValuePair[] split_metadata(String meta_buff,
+                                               String recordSeperator, String filedSeperator) {
+    String[] rows;
+    String[] cols;
+    NameValuePair[] meta_list;
+
+    rows = meta_buff.split(recordSeperator);
+    meta_list = new NameValuePair[rows.length];
+    for (int i = 0; i < rows.length; i++) {
+      cols = rows[i].split(filedSeperator, 2);
+      meta_list[i] = new NameValuePair(cols[0]);
+      if (cols.length == 2) {
+        meta_list[i].setValue(cols[1]);
+      }
+    }
+
+    return meta_list;
+  }
+
+  /**
+   * pack metadata array to string
+   *
+   * @param meta_list metadata array
+   * @return packed metadata
+   */
+  public static String pack_metadata(NameValuePair[] meta_list) {
+    if (meta_list.length == 0) {
+      return "";
+    }
+
+    StringBuffer sb = new StringBuffer(32 * meta_list.length);
+    sb.append(meta_list[0].getName()).append(FDFS_FIELD_SEPERATOR).append(meta_list[0].getValue());
+    for (int i = 1; i < meta_list.length; i++) {
+      sb.append(FDFS_RECORD_SEPERATOR);
+      sb.append(meta_list[i].getName()).append(FDFS_FIELD_SEPERATOR).append(meta_list[i].getValue());
+    }
+
+    return sb.toString();
+  }
+
+  /**
+   * send quit command to server and close socket
+   *
+   * @param sock the Socket object
+   */
+  public static void closeSocket(Socket sock) throws IOException {
+    byte[] header;
+    header = packHeader(FDFS_PROTO_CMD_QUIT, 0, (byte) 0);
+    sock.getOutputStream().write(header);
+    sock.close();
+  }
+
+  /**
+   * send ACTIVE_TEST command to server, test if network is ok and the server is alive
+   *
+   * @param sock the Socket object
+   */
+  public static boolean activeTest(Socket sock) throws IOException {
+    byte[] header;
+    header = packHeader(FDFS_PROTO_CMD_ACTIVE_TEST, 0, (byte) 0);
+    sock.getOutputStream().write(header);
+
+    RecvHeaderInfo headerInfo = recvHeader(sock.getInputStream(), TRACKER_PROTO_CMD_RESP, 0);
+    return headerInfo.errno == 0 ? true : false;
+  }
+
+  /**
+   * long convert to buff (big-endian)
+   *
+   * @param n long number
+   * @return 8 bytes buff
+   */
+  public static byte[] long2buff(long n) {
+    byte[] bs;
+
+    bs = new byte[8];
+    bs[0] = (byte) ((n >> 56) & 0xFF);
+    bs[1] = (byte) ((n >> 48) & 0xFF);
+    bs[2] = (byte) ((n >> 40) & 0xFF);
+    bs[3] = (byte) ((n >> 32) & 0xFF);
+    bs[4] = (byte) ((n >> 24) & 0xFF);
+    bs[5] = (byte) ((n >> 16) & 0xFF);
+    bs[6] = (byte) ((n >> 8) & 0xFF);
+    bs[7] = (byte) (n & 0xFF);
+
+    return bs;
+  }
+
+  /**
+   * buff convert to long
+   *
+   * @param bs     the buffer (big-endian)
+   * @param offset the start position based 0
+   * @return long number
+   */
+  public static long buff2long(byte[] bs, int offset) {
+    return (((long) (bs[offset] >= 0 ? bs[offset] : 256 + bs[offset])) << 56) |
+      (((long) (bs[offset + 1] >= 0 ? bs[offset + 1] : 256 + bs[offset + 1])) << 48) |
+      (((long) (bs[offset + 2] >= 0 ? bs[offset + 2] : 256 + bs[offset + 2])) << 40) |
+      (((long) (bs[offset + 3] >= 0 ? bs[offset + 3] : 256 + bs[offset + 3])) << 32) |
+      (((long) (bs[offset + 4] >= 0 ? bs[offset + 4] : 256 + bs[offset + 4])) << 24) |
+      (((long) (bs[offset + 5] >= 0 ? bs[offset + 5] : 256 + bs[offset + 5])) << 16) |
+      (((long) (bs[offset + 6] >= 0 ? bs[offset + 6] : 256 + bs[offset + 6])) << 8) |
+      ((long) (bs[offset + 7] >= 0 ? bs[offset + 7] : 256 + bs[offset + 7]));
+  }
+
+  /**
+   * buff convert to int
+   *
+   * @param bs     the buffer (big-endian)
+   * @param offset the start position based 0
+   * @return int number
+   */
+  public static int buff2int(byte[] bs, int offset) {
+    return (((int) (bs[offset] >= 0 ? bs[offset] : 256 + bs[offset])) << 24) |
+      (((int) (bs[offset + 1] >= 0 ? bs[offset + 1] : 256 + bs[offset + 1])) << 16) |
+      (((int) (bs[offset + 2] >= 0 ? bs[offset + 2] : 256 + bs[offset + 2])) << 8) |
+      ((int) (bs[offset + 3] >= 0 ? bs[offset + 3] : 256 + bs[offset + 3]));
+  }
+
+  /**
+   * buff convert to ip address
+   *
+   * @param bs     the buffer (big-endian)
+   * @param offset the start position based 0
+   * @return ip address
+   */
+  public static String getIpAddress(byte[] bs, int offset) {
+    if (bs[0] == 0 || bs[3] == 0) //storage server ID
+    {
+      return "";
+    }
+
+    int n;
+    StringBuilder sbResult = new StringBuilder(16);
+    for (int i = offset; i < offset + 4; i++) {
+      n = (bs[i] >= 0) ? bs[i] : 256 + bs[i];
+      if (sbResult.length() > 0) {
+        sbResult.append(".");
+      }
+      sbResult.append(String.valueOf(n));
+    }
+
+    return sbResult.toString();
+  }
+
+  /**
+   * md5 function
+   *
+   * @param source the input buffer
+   * @return md5 string
+   */
+  public static String md5(byte[] source) throws NoSuchAlgorithmException {
+    char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+    java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
+    md.update(source);
+    byte tmp[] = md.digest();
+    char str[] = new char[32];
+    int k = 0;
+    for (int i = 0; i < 16; i++) {
+      str[k++] = hexDigits[tmp[i] >>> 4 & 0xf];
+      str[k++] = hexDigits[tmp[i] & 0xf];
+    }
+
+    return new String(str);
+  }
+
+  /**
+   * get token for file URL
+   *
+   * @param remote_filename the filename return by FastDFS server
+   * @param ts              unix timestamp, unit: second
+   * @param secret_key      the secret key
+   * @return token string
+   */
+  public static String getToken(String remote_filename, int ts, String secret_key) throws UnsupportedEncodingException, NoSuchAlgorithmException, MyException {
+    byte[] bsFilename = remote_filename.getBytes(ClientGlobal.g_charset);
+    byte[] bsKey = secret_key.getBytes(ClientGlobal.g_charset);
+    byte[] bsTimestamp = (new Integer(ts)).toString().getBytes(ClientGlobal.g_charset);
+
+    byte[] buff = new byte[bsFilename.length + bsKey.length + bsTimestamp.length];
+    System.arraycopy(bsFilename, 0, buff, 0, bsFilename.length);
+    System.arraycopy(bsKey, 0, buff, bsFilename.length, bsKey.length);
+    System.arraycopy(bsTimestamp, 0, buff, bsFilename.length + bsKey.length, bsTimestamp.length);
+
+    return md5(buff);
+  }
+
+  /**
+   * generate slave filename
+   *
+   * @param master_filename the master filename to generate the slave filename
+   * @param prefix_name     the prefix name to generate the slave filename
+   * @param ext_name        the extension name of slave filename, null for same as the master extension name
+   * @return slave filename string
+   */
+  public static String genSlaveFilename(String master_filename,
+                                        String prefix_name, String ext_name) throws MyException {
+    String true_ext_name;
+    int dotIndex;
+
+    if (master_filename.length() < 28 + FDFS_FILE_EXT_NAME_MAX_LEN) {
+      throw new MyException("master filename \"" + master_filename + "\" is invalid");
+    }
+
+    dotIndex = master_filename.indexOf('.', master_filename.length() - (FDFS_FILE_EXT_NAME_MAX_LEN + 1));
+    if (ext_name != null) {
+      if (ext_name.length() == 0) {
+        true_ext_name = "";
+      } else if (ext_name.charAt(0) == '.') {
+        true_ext_name = ext_name;
+      } else {
+        true_ext_name = "." + ext_name;
+      }
+    } else {
+      if (dotIndex < 0) {
+        true_ext_name = "";
+      } else {
+        true_ext_name = master_filename.substring(dotIndex);
+      }
+    }
+
+    if (true_ext_name.length() == 0 && prefix_name.equals("-m")) {
+      throw new MyException("prefix_name \"" + prefix_name + "\" is invalid");
+    }
+
+    if (dotIndex < 0) {
+      return master_filename + prefix_name + true_ext_name;
+    } else {
+      return master_filename.substring(0, dotIndex) + prefix_name + true_ext_name;
+    }
+  }
+
+  /**
+   * receive package info
+   */
+  public static class RecvPackageInfo {
+    public byte errno;
+    public byte[] body;
+
+    public RecvPackageInfo(byte errno, byte[] body) {
+      this.errno = errno;
+      this.body = body;
+    }
+  }
+
+  /**
+   * receive header info
+   */
+  public static class RecvHeaderInfo {
+    public byte errno;
+    public long body_len;
+
+    public RecvHeaderInfo(byte errno, long body_len) {
+      this.errno = errno;
+      this.body_len = body_len;
+    }
+  }
+}

+ 48 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/ProtoStructDecoder.java

@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ * <p>
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package com.kmall.common.fileserver.fastdfs;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+
+/**
+ * C struct body decoder
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.17
+ */
+public class ProtoStructDecoder<T extends StructBase> {
+  /**
+   * Constructor
+   */
+  public ProtoStructDecoder() {
+  }
+
+  /**
+   * decode byte buffer
+   */
+  public T[] decode(byte[] bs, Class<T> clazz, int fieldsTotalSize) throws Exception {
+    if (bs.length % fieldsTotalSize != 0) {
+      throw new IOException("byte array length: " + bs.length + " is invalid!");
+    }
+
+    int count = bs.length / fieldsTotalSize;
+    int offset;
+    T[] results = (T[]) Array.newInstance(clazz, count);
+
+    offset = 0;
+    for (int i = 0; i < results.length; i++) {
+      results[i] = clazz.newInstance();
+      results[i].setFields(bs, offset);
+      offset += fieldsTotalSize;
+    }
+
+    return results;
+  }
+}

+ 66 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/ServerInfo.java

@@ -0,0 +1,66 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ * <p>
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package com.kmall.common.fileserver.fastdfs;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+/**
+ * Server Info
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.7
+ */
+public class ServerInfo {
+  protected String ip_addr;
+  protected int port;
+
+  /**
+   * Constructor
+   *
+   * @param ip_addr address of the server
+   * @param port    the port of the server
+   */
+  public ServerInfo(String ip_addr, int port) {
+    this.ip_addr = ip_addr;
+    this.port = port;
+  }
+
+  /**
+   * return the ip address
+   *
+   * @return the ip address
+   */
+  public String getIpAddr() {
+    return this.ip_addr;
+  }
+
+  /**
+   * return the port of the server
+   *
+   * @return the port of the server
+   */
+  public int getPort() {
+    return this.port;
+  }
+
+  /**
+   * connect to server
+   *
+   * @return connected Socket object
+   */
+  public Socket connect() throws IOException {
+    Socket sock = new Socket();
+    sock.setReuseAddress(true);
+    sock.setSoTimeout(ClientGlobal.g_network_timeout);
+    sock.connect(new InetSocketAddress(this.ip_addr, this.port), ClientGlobal.g_connect_timeout);
+    return sock;
+  }
+}

+ 1786 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/StorageClient.java

@@ -0,0 +1,1786 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ * <p>
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package com.kmall.common.fileserver.fastdfs;
+
+import com.kmall.common.fileserver.common.Base64;
+import com.kmall.common.fileserver.common.MyException;
+import com.kmall.common.fileserver.common.NameValuePair;
+
+import java.io.*;
+import java.net.Socket;
+import java.util.Arrays;
+
+/**
+ * Storage client for 2 fields file id: group name and filename
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.24
+ */
+public class StorageClient {
+  public final static Base64 base64 = new Base64('-', '_', '.', 0);
+  protected TrackerServer trackerServer;
+  protected StorageServer storageServer;
+  protected byte errno;
+
+  /**
+   * constructor using global settings in class ClientGlobal
+   */
+  public StorageClient() {
+    this.trackerServer = null;
+    this.storageServer = null;
+  }
+
+  /**
+   * constructor with tracker server and storage server
+   *
+   * @param trackerServer the tracker server, can be null
+   * @param storageServer the storage server, can be null
+   */
+  public StorageClient(TrackerServer trackerServer, StorageServer storageServer) {
+    this.trackerServer = trackerServer;
+    this.storageServer = storageServer;
+  }
+
+  /**
+   * get the error code of last call
+   *
+   * @return the error code of last call
+   */
+  public byte getErrorCode() {
+    return this.errno;
+  }
+
+  /**
+   * upload file to storage server (by file name)
+   *
+   * @param local_filename local filename to upload
+   * @param file_ext_name  file ext name, do not include dot(.), null to extract ext name from the local filename
+   * @param meta_list      meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li>results[0]: the group name to store the file </li></ul>
+   * <ul><li>results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  public String[] upload_file(String local_filename, String file_ext_name,
+                              NameValuePair[] meta_list) throws IOException, MyException {
+    final String group_name = null;
+    return this.upload_file(group_name, local_filename, file_ext_name, meta_list);
+  }
+
+  /**
+   * upload file to storage server (by file name)
+   *
+   * @param group_name     the group name to upload file to, can be empty
+   * @param local_filename local filename to upload
+   * @param file_ext_name  file ext name, do not include dot(.), null to extract ext name from the local filename
+   * @param meta_list      meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li>results[0]: the group name to store the file </li></ul>
+   * <ul><li>results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  protected String[] upload_file(String group_name, String local_filename, String file_ext_name,
+                                 NameValuePair[] meta_list) throws IOException, MyException {
+    final byte cmd = ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_FILE;
+    return this.upload_file(cmd, group_name, local_filename, file_ext_name, meta_list);
+  }
+
+  /**
+   * upload file to storage server (by file name)
+   *
+   * @param cmd            the command
+   * @param group_name     the group name to upload file to, can be empty
+   * @param local_filename local filename to upload
+   * @param file_ext_name  file ext name, do not include dot(.), null to extract ext name from the local filename
+   * @param meta_list      meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li>results[0]: the group name to store the file </li></ul>
+   * <ul><li>results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  protected String[] upload_file(byte cmd, String group_name, String local_filename, String file_ext_name,
+                                 NameValuePair[] meta_list) throws IOException, MyException {
+    File f = new File(local_filename);
+    FileInputStream fis = new FileInputStream(f);
+
+    if (file_ext_name == null) {
+      int nPos = local_filename.lastIndexOf('.');
+      if (nPos > 0 && local_filename.length() - nPos <= ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + 1) {
+        file_ext_name = local_filename.substring(nPos + 1);
+      }
+    }
+
+    try {
+      return this.do_upload_file(cmd, group_name, null, null, file_ext_name,
+        f.length(), new UploadStream(fis, f.length()), meta_list);
+    } finally {
+      fis.close();
+    }
+  }
+
+  /**
+   * upload file to storage server (by file buff)
+   *
+   * @param file_buff     file content/buff
+   * @param offset        start offset of the buff
+   * @param length        the length of buff to upload
+   * @param file_ext_name file ext name, do not include dot(.)
+   * @param meta_list     meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li>results[0]: the group name to store the file</li></ul>
+   * <ul><li>results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  public String[] upload_file(byte[] file_buff, int offset, int length, String file_ext_name,
+                              NameValuePair[] meta_list) throws IOException, MyException {
+    final String group_name = null;
+    return this.upload_file(group_name, file_buff, offset, length, file_ext_name, meta_list);
+  }
+
+  /**
+   * upload file to storage server (by file buff)
+   *
+   * @param group_name    the group name to upload file to, can be empty
+   * @param file_buff     file content/buff
+   * @param offset        start offset of the buff
+   * @param length        the length of buff to upload
+   * @param file_ext_name file ext name, do not include dot(.)
+   * @param meta_list     meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li>results[0]: the group name to store the file</li></ul>
+   * <ul><li>results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  public String[] upload_file(String group_name, byte[] file_buff, int offset, int length,
+                              String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException {
+    return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_FILE, group_name, null, null, file_ext_name,
+      length, new UploadBuff(file_buff, offset, length), meta_list);
+  }
+
+  /**
+   * upload file to storage server (by file buff)
+   *
+   * @param file_buff     file content/buff
+   * @param file_ext_name file ext name, do not include dot(.)
+   * @param meta_list     meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li>results[0]: the group name to store the file</li></ul>
+   * <ul><li>results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  public String[] upload_file(byte[] file_buff, String file_ext_name,
+                              NameValuePair[] meta_list) throws IOException, MyException {
+    final String group_name = null;
+    return this.upload_file(group_name, file_buff, 0, file_buff.length, file_ext_name, meta_list);
+  }
+
+  /**
+   * upload file to storage server (by file buff)
+   *
+   * @param group_name    the group name to upload file to, can be empty
+   * @param file_buff     file content/buff
+   * @param file_ext_name file ext name, do not include dot(.)
+   * @param meta_list     meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li>results[0]: the group name to store the file</li></ul>
+   * <ul><li>results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  public String[] upload_file(String group_name, byte[] file_buff,
+                              String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException {
+    return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_FILE, group_name, null, null, file_ext_name,
+      file_buff.length, new UploadBuff(file_buff, 0, file_buff.length), meta_list);
+  }
+
+  /**
+   * upload file to storage server (by callback)
+   *
+   * @param group_name    the group name to upload file to, can be empty
+   * @param file_size     the file size
+   * @param callback      the write data callback object
+   * @param file_ext_name file ext name, do not include dot(.)
+   * @param meta_list     meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li>results[0]: the group name to store the file</li></ul>
+   * <ul><li>results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  public String[] upload_file(String group_name, long file_size, UploadCallback callback,
+                              String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException {
+    final String master_filename = null;
+    final String prefix_name = null;
+
+    return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_FILE, group_name, master_filename, prefix_name,
+      file_ext_name, file_size, callback, meta_list);
+  }
+
+  /**
+   * upload file to storage server (by file name, slave file mode)
+   *
+   * @param group_name      the group name of master file
+   * @param master_filename the master file name to generate the slave file
+   * @param prefix_name     the prefix name to generate the slave file
+   * @param local_filename  local filename to upload
+   * @param file_ext_name   file ext name, do not include dot(.), null to extract ext name from the local filename
+   * @param meta_list       meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li>results[0]: the group name to store the file </li></ul>
+   * <ul><li>results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  public String[] upload_file(String group_name, String master_filename, String prefix_name,
+                              String local_filename, String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException {
+    if ((group_name == null || group_name.length() == 0) ||
+      (master_filename == null || master_filename.length() == 0) ||
+      (prefix_name == null)) {
+      throw new MyException("invalid arguement");
+    }
+
+    File f = new File(local_filename);
+    FileInputStream fis = new FileInputStream(f);
+
+    if (file_ext_name == null) {
+      int nPos = local_filename.lastIndexOf('.');
+      if (nPos > 0 && local_filename.length() - nPos <= ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + 1) {
+        file_ext_name = local_filename.substring(nPos + 1);
+      }
+    }
+
+    try {
+      return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, group_name, master_filename, prefix_name,
+        file_ext_name, f.length(), new UploadStream(fis, f.length()), meta_list);
+    } finally {
+      fis.close();
+    }
+  }
+
+  /**
+   * upload file to storage server (by file buff, slave file mode)
+   *
+   * @param group_name      the group name of master file
+   * @param master_filename the master file name to generate the slave file
+   * @param prefix_name     the prefix name to generate the slave file
+   * @param file_buff       file content/buff
+   * @param file_ext_name   file ext name, do not include dot(.)
+   * @param meta_list       meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li>results[0]: the group name to store the file</li></ul>
+   * <ul><li>results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  public String[] upload_file(String group_name, String master_filename, String prefix_name,
+                              byte[] file_buff, String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException {
+    if ((group_name == null || group_name.length() == 0) ||
+      (master_filename == null || master_filename.length() == 0) ||
+      (prefix_name == null)) {
+      throw new MyException("invalid arguement");
+    }
+
+    return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, group_name, master_filename, prefix_name,
+      file_ext_name, file_buff.length, new UploadBuff(file_buff, 0, file_buff.length), meta_list);
+  }
+
+  /**
+   * upload file to storage server (by file buff, slave file mode)
+   *
+   * @param group_name      the group name of master file
+   * @param master_filename the master file name to generate the slave file
+   * @param prefix_name     the prefix name to generate the slave file
+   * @param file_buff       file content/buff
+   * @param offset          start offset of the buff
+   * @param length          the length of buff to upload
+   * @param file_ext_name   file ext name, do not include dot(.)
+   * @param meta_list       meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li>results[0]: the group name to store the file</li></ul>
+   * <ul><li>results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  public String[] upload_file(String group_name, String master_filename, String prefix_name,
+                              byte[] file_buff, int offset, int length, String file_ext_name,
+                              NameValuePair[] meta_list) throws IOException, MyException {
+    if ((group_name == null || group_name.length() == 0) ||
+      (master_filename == null || master_filename.length() == 0) ||
+      (prefix_name == null)) {
+      throw new MyException("invalid arguement");
+    }
+
+    return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, group_name, master_filename, prefix_name,
+      file_ext_name, length, new UploadBuff(file_buff, offset, length), meta_list);
+  }
+
+  /**
+   * upload file to storage server (by callback, slave file mode)
+   *
+   * @param group_name      the group name to upload file to, can be empty
+   * @param master_filename the master file name to generate the slave file
+   * @param prefix_name     the prefix name to generate the slave file
+   * @param file_size       the file size
+   * @param callback        the write data callback object
+   * @param file_ext_name   file ext name, do not include dot(.)
+   * @param meta_list       meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li>results[0]: the group name to store the file</li></ul>
+   * <ul><li>results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  public String[] upload_file(String group_name, String master_filename,
+                              String prefix_name, long file_size, UploadCallback callback,
+                              String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException {
+    return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, group_name, master_filename, prefix_name,
+      file_ext_name, file_size, callback, meta_list);
+  }
+
+  /**
+   * upload appender file to storage server (by file name)
+   *
+   * @param local_filename local filename to upload
+   * @param file_ext_name  file ext name, do not include dot(.), null to extract ext name from the local filename
+   * @param meta_list      meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li>results[0]: the group name to store the file </li></ul>
+   * <ul><li>results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  public String[] upload_appender_file(String local_filename, String file_ext_name,
+                                       NameValuePair[] meta_list) throws IOException, MyException {
+    final String group_name = null;
+    return this.upload_appender_file(group_name, local_filename, file_ext_name, meta_list);
+  }
+
+  /**
+   * upload appender file to storage server (by file name)
+   *
+   * @param group_name     the group name to upload file to, can be empty
+   * @param local_filename local filename to upload
+   * @param file_ext_name  file ext name, do not include dot(.), null to extract ext name from the local filename
+   * @param meta_list      meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li>results[0]: the group name to store the file </li></ul>
+   * <ul><li>results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  protected String[] upload_appender_file(String group_name, String local_filename, String file_ext_name,
+                                          NameValuePair[] meta_list) throws IOException, MyException {
+    final byte cmd = ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE;
+    return this.upload_file(cmd, group_name, local_filename, file_ext_name, meta_list);
+  }
+
+  /**
+   * upload appender file to storage server (by file buff)
+   *
+   * @param file_buff     file content/buff
+   * @param offset        start offset of the buff
+   * @param length        the length of buff to upload
+   * @param file_ext_name file ext name, do not include dot(.)
+   * @param meta_list     meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li>results[0]: the group name to store the file</li></ul>
+   * <ul><li>results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  public String[] upload_appender_file(byte[] file_buff, int offset, int length, String file_ext_name,
+                                       NameValuePair[] meta_list) throws IOException, MyException {
+    final String group_name = null;
+    return this.upload_appender_file(group_name, file_buff, offset, length, file_ext_name, meta_list);
+  }
+
+  /**
+   * upload appender file to storage server (by file buff)
+   *
+   * @param group_name    the group name to upload file to, can be empty
+   * @param file_buff     file content/buff
+   * @param offset        start offset of the buff
+   * @param length        the length of buff to upload
+   * @param file_ext_name file ext name, do not include dot(.)
+   * @param meta_list     meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li>results[0]: the group name to store the file</li></ul>
+   * <ul><li>results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  public String[] upload_appender_file(String group_name, byte[] file_buff, int offset, int length,
+                                       String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException {
+    return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, group_name, null, null, file_ext_name,
+      length, new UploadBuff(file_buff, offset, length), meta_list);
+  }
+
+  /**
+   * upload appender file to storage server (by file buff)
+   *
+   * @param file_buff     file content/buff
+   * @param file_ext_name file ext name, do not include dot(.)
+   * @param meta_list     meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li>results[0]: the group name to store the file</li></ul>
+   * <ul><li>results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  public String[] upload_appender_file(byte[] file_buff, String file_ext_name,
+                                       NameValuePair[] meta_list) throws IOException, MyException {
+    final String group_name = null;
+    return this.upload_appender_file(group_name, file_buff, 0, file_buff.length, file_ext_name, meta_list);
+  }
+
+  /**
+   * upload appender file to storage server (by file buff)
+   *
+   * @param group_name    the group name to upload file to, can be empty
+   * @param file_buff     file content/buff
+   * @param file_ext_name file ext name, do not include dot(.)
+   * @param meta_list     meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li>results[0]: the group name to store the file</li></ul>
+   * <ul><li>results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  public String[] upload_appender_file(String group_name, byte[] file_buff,
+                                       String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException {
+    return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, group_name, null, null, file_ext_name,
+      file_buff.length, new UploadBuff(file_buff, 0, file_buff.length), meta_list);
+  }
+
+  /**
+   * upload appender file to storage server (by callback)
+   *
+   * @param group_name    the group name to upload file to, can be empty
+   * @param file_size     the file size
+   * @param callback      the write data callback object
+   * @param file_ext_name file ext name, do not include dot(.)
+   * @param meta_list     meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li>results[0]: the group name to store the file</li></ul>
+   * <ul><li>results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  public String[] upload_appender_file(String group_name, long file_size, UploadCallback callback,
+                                       String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException {
+    final String master_filename = null;
+    final String prefix_name = null;
+
+    return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, group_name, master_filename, prefix_name,
+      file_ext_name, file_size, callback, meta_list);
+  }
+
+  /**
+   * append file to storage server (by file name)
+   *
+   * @param group_name        the group name of appender file
+   * @param appender_filename the appender filename
+   * @param local_filename    local filename to append
+   * @return 0 for success, != 0 for error (error no)
+   */
+  public int append_file(String group_name, String appender_filename, String local_filename) throws IOException, MyException {
+    File f = new File(local_filename);
+    FileInputStream fis = new FileInputStream(f);
+
+    try {
+      return this.do_append_file(group_name, appender_filename, f.length(), new UploadStream(fis, f.length()));
+    } finally {
+      fis.close();
+    }
+  }
+
+  /**
+   * append file to storage server (by file buff)
+   *
+   * @param group_name        the group name of appender file
+   * @param appender_filename the appender filename
+   * @param file_buff         file content/buff
+   * @return 0 for success, != 0 for error (error no)
+   */
+  public int append_file(String group_name, String appender_filename, byte[] file_buff) throws IOException, MyException {
+    return this.do_append_file(group_name, appender_filename, file_buff.length, new UploadBuff(file_buff, 0, file_buff.length));
+  }
+
+  /**
+   * append file to storage server (by file buff)
+   *
+   * @param group_name        the group name of appender file
+   * @param appender_filename the appender filename
+   * @param file_buff         file content/buff
+   * @param offset            start offset of the buff
+   * @param length            the length of buff to append
+   * @return 0 for success, != 0 for error (error no)
+   */
+  public int append_file(String group_name, String appender_filename,
+                         byte[] file_buff, int offset, int length) throws IOException, MyException {
+    return this.do_append_file(group_name, appender_filename, length, new UploadBuff(file_buff, offset, length));
+  }
+
+  /**
+   * append file to storage server (by callback)
+   *
+   * @param group_name        the group name to append file to
+   * @param appender_filename the appender filename
+   * @param file_size         the file size
+   * @param callback          the write data callback object
+   * @return 0 for success, != 0 for error (error no)
+   */
+  public int append_file(String group_name, String appender_filename,
+                         long file_size, UploadCallback callback) throws IOException, MyException {
+    return this.do_append_file(group_name, appender_filename, file_size, callback);
+  }
+
+  /**
+   * modify appender file to storage server (by file name)
+   *
+   * @param group_name        the group name of appender file
+   * @param appender_filename the appender filename
+   * @param file_offset       the offset of appender file
+   * @param local_filename    local filename to append
+   * @return 0 for success, != 0 for error (error no)
+   */
+  public int modify_file(String group_name, String appender_filename,
+                         long file_offset, String local_filename) throws IOException, MyException {
+    File f = new File(local_filename);
+    FileInputStream fis = new FileInputStream(f);
+
+    try {
+      return this.do_modify_file(group_name, appender_filename, file_offset,
+        f.length(), new UploadStream(fis, f.length()));
+    } finally {
+      fis.close();
+    }
+  }
+
+  /**
+   * modify appender file to storage server (by file buff)
+   *
+   * @param group_name        the group name of appender file
+   * @param appender_filename the appender filename
+   * @param file_offset       the offset of appender file
+   * @param file_buff         file content/buff
+   * @return 0 for success, != 0 for error (error no)
+   */
+  public int modify_file(String group_name, String appender_filename,
+                         long file_offset, byte[] file_buff) throws IOException, MyException {
+    return this.do_modify_file(group_name, appender_filename, file_offset,
+      file_buff.length, new UploadBuff(file_buff, 0, file_buff.length));
+  }
+
+  /**
+   * modify appender file to storage server (by file buff)
+   *
+   * @param group_name        the group name of appender file
+   * @param appender_filename the appender filename
+   * @param file_offset       the offset of appender file
+   * @param file_buff         file content/buff
+   * @param buffer_offset     start offset of the buff
+   * @param buffer_length     the length of buff to modify
+   * @return 0 for success, != 0 for error (error no)
+   */
+  public int modify_file(String group_name, String appender_filename,
+                         long file_offset, byte[] file_buff, int buffer_offset, int buffer_length) throws IOException, MyException {
+    return this.do_modify_file(group_name, appender_filename, file_offset,
+      buffer_length, new UploadBuff(file_buff, buffer_offset, buffer_length));
+  }
+
+  /**
+   * modify appender file to storage server (by callback)
+   *
+   * @param group_name        the group name to modify file to
+   * @param appender_filename the appender filename
+   * @param file_offset       the offset of appender file
+   * @param modify_size       the modify size
+   * @param callback          the write data callback object
+   * @return 0 for success, != 0 for error (error no)
+   */
+  public int modify_file(String group_name, String appender_filename,
+                         long file_offset, long modify_size, UploadCallback callback) throws IOException, MyException {
+    return this.do_modify_file(group_name, appender_filename, file_offset,
+      modify_size, callback);
+  }
+
+  /**
+   * upload file to storage server
+   *
+   * @param cmd             the command code
+   * @param group_name      the group name to upload file to, can be empty
+   * @param master_filename the master file name to generate the slave file
+   * @param prefix_name     the prefix name to generate the slave file
+   * @param file_ext_name   file ext name, do not include dot(.)
+   * @param file_size       the file size
+   * @param callback        the write data callback object
+   * @param meta_list       meta info array
+   * @return 2 elements string array if success:<br>
+   * <ul><li> results[0]: the group name to store the file</li></ul>
+   * <ul><li> results[1]: the new created filename</li></ul>
+   * return null if fail
+   */
+  protected String[] do_upload_file(byte cmd, String group_name, String master_filename,
+                                    String prefix_name, String file_ext_name, long file_size, UploadCallback callback,
+                                    NameValuePair[] meta_list) throws IOException, MyException {
+    byte[] header;
+    byte[] ext_name_bs;
+    String new_group_name;
+    String remote_filename;
+    boolean bNewConnection;
+    Socket storageSocket;
+    byte[] sizeBytes;
+    byte[] hexLenBytes;
+    byte[] masterFilenameBytes;
+    boolean bUploadSlave;
+    int offset;
+    long body_len;
+
+    bUploadSlave = ((group_name != null && group_name.length() > 0) &&
+      (master_filename != null && master_filename.length() > 0) &&
+      (prefix_name != null));
+    if (bUploadSlave) {
+      bNewConnection = this.newUpdatableStorageConnection(group_name, master_filename);
+    } else {
+      bNewConnection = this.newWritableStorageConnection(group_name);
+    }
+
+    try {
+      storageSocket = this.storageServer.getSocket();
+
+      ext_name_bs = new byte[ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN];
+      Arrays.fill(ext_name_bs, (byte) 0);
+      if (file_ext_name != null && file_ext_name.length() > 0) {
+        byte[] bs = file_ext_name.getBytes(ClientGlobal.g_charset);
+        int ext_name_len = bs.length;
+        if (ext_name_len > ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN) {
+          ext_name_len = ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN;
+        }
+        System.arraycopy(bs, 0, ext_name_bs, 0, ext_name_len);
+      }
+
+      if (bUploadSlave) {
+        masterFilenameBytes = master_filename.getBytes(ClientGlobal.g_charset);
+
+        sizeBytes = new byte[2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE];
+        body_len = sizeBytes.length + ProtoCommon.FDFS_FILE_PREFIX_MAX_LEN + ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN
+          + masterFilenameBytes.length + file_size;
+
+        hexLenBytes = ProtoCommon.long2buff(master_filename.length());
+        System.arraycopy(hexLenBytes, 0, sizeBytes, 0, hexLenBytes.length);
+        offset = hexLenBytes.length;
+      } else {
+        masterFilenameBytes = null;
+        sizeBytes = new byte[1 + 1 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE];
+        body_len = sizeBytes.length + ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + file_size;
+
+        sizeBytes[0] = (byte) this.storageServer.getStorePathIndex();
+        offset = 1;
+      }
+
+      hexLenBytes = ProtoCommon.long2buff(file_size);
+      System.arraycopy(hexLenBytes, 0, sizeBytes, offset, hexLenBytes.length);
+
+      OutputStream out = storageSocket.getOutputStream();
+      header = ProtoCommon.packHeader(cmd, body_len, (byte) 0);
+      byte[] wholePkg = new byte[(int) (header.length + body_len - file_size)];
+      System.arraycopy(header, 0, wholePkg, 0, header.length);
+      System.arraycopy(sizeBytes, 0, wholePkg, header.length, sizeBytes.length);
+      offset = header.length + sizeBytes.length;
+      if (bUploadSlave) {
+        byte[] prefix_name_bs = new byte[ProtoCommon.FDFS_FILE_PREFIX_MAX_LEN];
+        byte[] bs = prefix_name.getBytes(ClientGlobal.g_charset);
+        int prefix_name_len = bs.length;
+        Arrays.fill(prefix_name_bs, (byte) 0);
+        if (prefix_name_len > ProtoCommon.FDFS_FILE_PREFIX_MAX_LEN) {
+          prefix_name_len = ProtoCommon.FDFS_FILE_PREFIX_MAX_LEN;
+        }
+        if (prefix_name_len > 0) {
+          System.arraycopy(bs, 0, prefix_name_bs, 0, prefix_name_len);
+        }
+
+        System.arraycopy(prefix_name_bs, 0, wholePkg, offset, prefix_name_bs.length);
+        offset += prefix_name_bs.length;
+      }
+
+      System.arraycopy(ext_name_bs, 0, wholePkg, offset, ext_name_bs.length);
+      offset += ext_name_bs.length;
+
+      if (bUploadSlave) {
+        System.arraycopy(masterFilenameBytes, 0, wholePkg, offset, masterFilenameBytes.length);
+        offset += masterFilenameBytes.length;
+      }
+
+      out.write(wholePkg);
+
+      if ((this.errno = (byte) callback.send(out)) != 0) {
+        return null;
+      }
+
+      ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(),
+        ProtoCommon.STORAGE_PROTO_CMD_RESP, -1);
+      this.errno = pkgInfo.errno;
+      if (pkgInfo.errno != 0) {
+        return null;
+      }
+
+      if (pkgInfo.body.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) {
+        throw new MyException("body length: " + pkgInfo.body.length + " <= " + ProtoCommon.FDFS_GROUP_NAME_MAX_LEN);
+      }
+
+      new_group_name = new String(pkgInfo.body, 0, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN).trim();
+      remote_filename = new String(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN, pkgInfo.body.length - ProtoCommon.FDFS_GROUP_NAME_MAX_LEN);
+      String[] results = new String[2];
+      results[0] = new_group_name;
+      results[1] = remote_filename;
+
+      if (meta_list == null || meta_list.length == 0) {
+        return results;
+      }
+
+      int result = 0;
+      try {
+        result = this.set_metadata(new_group_name, remote_filename,
+          meta_list, ProtoCommon.STORAGE_SET_METADATA_FLAG_OVERWRITE);
+      } catch (IOException ex) {
+        result = 5;
+        throw ex;
+      } finally {
+        if (result != 0) {
+          this.errno = (byte) result;
+          this.delete_file(new_group_name, remote_filename);
+          return null;
+        }
+      }
+
+      return results;
+    } catch (IOException ex) {
+      if (!bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+
+      throw ex;
+    } finally {
+      if (bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+    }
+  }
+
+  /**
+   * append file to storage server
+   *
+   * @param group_name        the group name of appender file
+   * @param appender_filename the appender filename
+   * @param file_size         the file size
+   * @param callback          the write data callback object
+   * @return return true for success, false for fail
+   */
+  protected int do_append_file(String group_name, String appender_filename,
+                               long file_size, UploadCallback callback) throws IOException, MyException {
+    byte[] header;
+    boolean bNewConnection;
+    Socket storageSocket;
+    byte[] hexLenBytes;
+    byte[] appenderFilenameBytes;
+    int offset;
+    long body_len;
+
+    if ((group_name == null || group_name.length() == 0) ||
+      (appender_filename == null || appender_filename.length() == 0)) {
+      this.errno = ProtoCommon.ERR_NO_EINVAL;
+      return this.errno;
+    }
+
+    bNewConnection = this.newUpdatableStorageConnection(group_name, appender_filename);
+
+    try {
+      storageSocket = this.storageServer.getSocket();
+
+      appenderFilenameBytes = appender_filename.getBytes(ClientGlobal.g_charset);
+      body_len = 2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE + appenderFilenameBytes.length + file_size;
+
+      header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_APPEND_FILE, body_len, (byte) 0);
+      byte[] wholePkg = new byte[(int) (header.length + body_len - file_size)];
+      System.arraycopy(header, 0, wholePkg, 0, header.length);
+      offset = header.length;
+
+      hexLenBytes = ProtoCommon.long2buff(appender_filename.length());
+      System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length);
+      offset += hexLenBytes.length;
+
+      hexLenBytes = ProtoCommon.long2buff(file_size);
+      System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length);
+      offset += hexLenBytes.length;
+
+      OutputStream out = storageSocket.getOutputStream();
+
+      System.arraycopy(appenderFilenameBytes, 0, wholePkg, offset, appenderFilenameBytes.length);
+      offset += appenderFilenameBytes.length;
+
+      out.write(wholePkg);
+      if ((this.errno = (byte) callback.send(out)) != 0) {
+        return this.errno;
+      }
+
+      ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(),
+        ProtoCommon.STORAGE_PROTO_CMD_RESP, 0);
+      this.errno = pkgInfo.errno;
+      if (pkgInfo.errno != 0) {
+        return this.errno;
+      }
+
+      return 0;
+    } catch (IOException ex) {
+      if (!bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+
+      throw ex;
+    } finally {
+      if (bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+    }
+  }
+
+  /**
+   * modify appender file to storage server
+   *
+   * @param group_name        the group name of appender file
+   * @param appender_filename the appender filename
+   * @param file_offset       the offset of appender file
+   * @param modify_size       the modify size
+   * @param callback          the write data callback object
+   * @return return true for success, false for fail
+   */
+  protected int do_modify_file(String group_name, String appender_filename,
+                               long file_offset, long modify_size, UploadCallback callback) throws IOException, MyException {
+    byte[] header;
+    boolean bNewConnection;
+    Socket storageSocket;
+    byte[] hexLenBytes;
+    byte[] appenderFilenameBytes;
+    int offset;
+    long body_len;
+
+    if ((group_name == null || group_name.length() == 0) ||
+      (appender_filename == null || appender_filename.length() == 0)) {
+      this.errno = ProtoCommon.ERR_NO_EINVAL;
+      return this.errno;
+    }
+
+    bNewConnection = this.newUpdatableStorageConnection(group_name, appender_filename);
+
+    try {
+      storageSocket = this.storageServer.getSocket();
+
+      appenderFilenameBytes = appender_filename.getBytes(ClientGlobal.g_charset);
+      body_len = 3 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE + appenderFilenameBytes.length + modify_size;
+
+      header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_MODIFY_FILE, body_len, (byte) 0);
+      byte[] wholePkg = new byte[(int) (header.length + body_len - modify_size)];
+      System.arraycopy(header, 0, wholePkg, 0, header.length);
+      offset = header.length;
+
+      hexLenBytes = ProtoCommon.long2buff(appender_filename.length());
+      System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length);
+      offset += hexLenBytes.length;
+
+      hexLenBytes = ProtoCommon.long2buff(file_offset);
+      System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length);
+      offset += hexLenBytes.length;
+
+      hexLenBytes = ProtoCommon.long2buff(modify_size);
+      System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length);
+      offset += hexLenBytes.length;
+
+      OutputStream out = storageSocket.getOutputStream();
+
+      System.arraycopy(appenderFilenameBytes, 0, wholePkg, offset, appenderFilenameBytes.length);
+      offset += appenderFilenameBytes.length;
+
+      out.write(wholePkg);
+      if ((this.errno = (byte) callback.send(out)) != 0) {
+        return this.errno;
+      }
+
+      ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(),
+        ProtoCommon.STORAGE_PROTO_CMD_RESP, 0);
+      this.errno = pkgInfo.errno;
+      if (pkgInfo.errno != 0) {
+        return this.errno;
+      }
+
+      return 0;
+    } catch (IOException ex) {
+      if (!bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+
+      throw ex;
+    } finally {
+      if (bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+    }
+  }
+
+  /**
+   * delete file from storage server
+   *
+   * @param group_name      the group name of storage server
+   * @param remote_filename filename on storage server
+   * @return 0 for success, none zero for fail (error code)
+   */
+  public int delete_file(String group_name, String remote_filename) throws IOException, MyException {
+    boolean bNewConnection = this.newUpdatableStorageConnection(group_name, remote_filename);
+    Socket storageSocket = this.storageServer.getSocket();
+
+    try {
+      this.send_package(ProtoCommon.STORAGE_PROTO_CMD_DELETE_FILE, group_name, remote_filename);
+      ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(),
+        ProtoCommon.STORAGE_PROTO_CMD_RESP, 0);
+
+      this.errno = pkgInfo.errno;
+      return pkgInfo.errno;
+    } catch (IOException ex) {
+      if (!bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+
+      throw ex;
+    } finally {
+      if (bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+    }
+  }
+
+  /**
+   * truncate appender file to size 0 from storage server
+   *
+   * @param group_name        the group name of storage server
+   * @param appender_filename the appender filename
+   * @return 0 for success, none zero for fail (error code)
+   */
+  public int truncate_file(String group_name, String appender_filename) throws IOException, MyException {
+    final long truncated_file_size = 0;
+    return this.truncate_file(group_name, appender_filename, truncated_file_size);
+  }
+
+  /**
+   * truncate appender file from storage server
+   *
+   * @param group_name          the group name of storage server
+   * @param appender_filename   the appender filename
+   * @param truncated_file_size truncated file size
+   * @return 0 for success, none zero for fail (error code)
+   */
+  public int truncate_file(String group_name, String appender_filename,
+                           long truncated_file_size) throws IOException, MyException {
+    byte[] header;
+    boolean bNewConnection;
+    Socket storageSocket;
+    byte[] hexLenBytes;
+    byte[] appenderFilenameBytes;
+    int offset;
+    int body_len;
+
+    if ((group_name == null || group_name.length() == 0) ||
+      (appender_filename == null || appender_filename.length() == 0)) {
+      this.errno = ProtoCommon.ERR_NO_EINVAL;
+      return this.errno;
+    }
+
+    bNewConnection = this.newUpdatableStorageConnection(group_name, appender_filename);
+
+    try {
+      storageSocket = this.storageServer.getSocket();
+
+      appenderFilenameBytes = appender_filename.getBytes(ClientGlobal.g_charset);
+      body_len = 2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE + appenderFilenameBytes.length;
+
+      header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_TRUNCATE_FILE, body_len, (byte) 0);
+      byte[] wholePkg = new byte[header.length + body_len];
+      System.arraycopy(header, 0, wholePkg, 0, header.length);
+      offset = header.length;
+
+      hexLenBytes = ProtoCommon.long2buff(appender_filename.length());
+      System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length);
+      offset += hexLenBytes.length;
+
+      hexLenBytes = ProtoCommon.long2buff(truncated_file_size);
+      System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length);
+      offset += hexLenBytes.length;
+
+      OutputStream out = storageSocket.getOutputStream();
+
+      System.arraycopy(appenderFilenameBytes, 0, wholePkg, offset, appenderFilenameBytes.length);
+      offset += appenderFilenameBytes.length;
+
+      out.write(wholePkg);
+      ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(),
+        ProtoCommon.STORAGE_PROTO_CMD_RESP, 0);
+      this.errno = pkgInfo.errno;
+      return pkgInfo.errno;
+    } catch (IOException ex) {
+      if (!bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+
+      throw ex;
+    } finally {
+      if (bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+    }
+  }
+
+  /**
+   * download file from storage server
+   *
+   * @param group_name      the group name of storage server
+   * @param remote_filename filename on storage server
+   * @return file content/buff, return null if fail
+   */
+  public byte[] download_file(String group_name, String remote_filename) throws IOException, MyException {
+    final long file_offset = 0;
+    final long download_bytes = 0;
+
+    return this.download_file(group_name, remote_filename, file_offset, download_bytes);
+  }
+
+  /**
+   * download file from storage server
+   *
+   * @param group_name      the group name of storage server
+   * @param remote_filename filename on storage server
+   * @param file_offset     the start offset of the file
+   * @param download_bytes  download bytes, 0 for remain bytes from offset
+   * @return file content/buff, return null if fail
+   */
+  public byte[] download_file(String group_name, String remote_filename, long file_offset, long download_bytes) throws IOException, MyException {
+    boolean bNewConnection = this.newReadableStorageConnection(group_name, remote_filename);
+    Socket storageSocket = this.storageServer.getSocket();
+
+    try {
+      ProtoCommon.RecvPackageInfo pkgInfo;
+
+      this.send_download_package(group_name, remote_filename, file_offset, download_bytes);
+      pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(),
+        ProtoCommon.STORAGE_PROTO_CMD_RESP, -1);
+
+      this.errno = pkgInfo.errno;
+      if (pkgInfo.errno != 0) {
+        return null;
+      }
+
+      return pkgInfo.body;
+    } catch (IOException ex) {
+      if (!bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+
+      throw ex;
+    } finally {
+      if (bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+    }
+  }
+
+  /**
+   * download file from storage server
+   *
+   * @param group_name      the group name of storage server
+   * @param remote_filename filename on storage server
+   * @param local_filename  filename on local
+   * @return 0 success, return none zero errno if fail
+   */
+  public int download_file(String group_name, String remote_filename,
+                           String local_filename) throws IOException, MyException {
+    final long file_offset = 0;
+    final long download_bytes = 0;
+    return this.download_file(group_name, remote_filename,
+      file_offset, download_bytes, local_filename);
+  }
+
+  /**
+   * download file from storage server
+   *
+   * @param group_name      the group name of storage server
+   * @param remote_filename filename on storage server
+   * @param file_offset     the start offset of the file
+   * @param download_bytes  download bytes, 0 for remain bytes from offset
+   * @param local_filename  filename on local
+   * @return 0 success, return none zero errno if fail
+   */
+  public int download_file(String group_name, String remote_filename,
+                           long file_offset, long download_bytes,
+                           String local_filename) throws IOException, MyException {
+    boolean bNewConnection = this.newReadableStorageConnection(group_name, remote_filename);
+    Socket storageSocket = this.storageServer.getSocket();
+    try {
+      ProtoCommon.RecvHeaderInfo header;
+      FileOutputStream out = new FileOutputStream(local_filename);
+      try {
+        this.errno = 0;
+        this.send_download_package(group_name, remote_filename, file_offset, download_bytes);
+
+        InputStream in = storageSocket.getInputStream();
+        header = ProtoCommon.recvHeader(in, ProtoCommon.STORAGE_PROTO_CMD_RESP, -1);
+        this.errno = header.errno;
+        if (header.errno != 0) {
+          return header.errno;
+        }
+
+        byte[] buff = new byte[256 * 1024];
+        long remainBytes = header.body_len;
+        int bytes;
+
+        //System.out.println("expect_body_len=" + header.body_len);
+
+        while (remainBytes > 0) {
+          if ((bytes = in.read(buff, 0, remainBytes > buff.length ? buff.length : (int) remainBytes)) < 0) {
+            throw new IOException("recv package size " + (header.body_len - remainBytes) + " != " + header.body_len);
+          }
+
+          out.write(buff, 0, bytes);
+          remainBytes -= bytes;
+
+          //System.out.println("totalBytes=" + (header.body_len - remainBytes));
+        }
+
+        return 0;
+      } catch (IOException ex) {
+        if (this.errno == 0) {
+          this.errno = ProtoCommon.ERR_NO_EIO;
+        }
+
+        throw ex;
+      } finally {
+        out.close();
+        if (this.errno != 0) {
+          (new File(local_filename)).delete();
+        }
+      }
+    } catch (IOException ex) {
+      if (!bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+
+      throw ex;
+    } finally {
+      if (bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+    }
+  }
+
+  /**
+   * download file from storage server
+   *
+   * @param group_name      the group name of storage server
+   * @param remote_filename filename on storage server
+   * @param callback        call callback.recv() when data arrive
+   * @return 0 success, return none zero errno if fail
+   */
+  public int download_file(String group_name, String remote_filename,
+                           DownloadCallback callback) throws IOException, MyException {
+    final long file_offset = 0;
+    final long download_bytes = 0;
+    return this.download_file(group_name, remote_filename,
+      file_offset, download_bytes, callback);
+  }
+
+  /**
+   * download file from storage server
+   *
+   * @param group_name      the group name of storage server
+   * @param remote_filename filename on storage server
+   * @param file_offset     the start offset of the file
+   * @param download_bytes  download bytes, 0 for remain bytes from offset
+   * @param callback        call callback.recv() when data arrive
+   * @return 0 success, return none zero errno if fail
+   */
+  public int download_file(String group_name, String remote_filename,
+                           long file_offset, long download_bytes,
+                           DownloadCallback callback) throws IOException, MyException {
+    int result;
+    boolean bNewConnection = this.newReadableStorageConnection(group_name, remote_filename);
+    Socket storageSocket = this.storageServer.getSocket();
+
+    try {
+      ProtoCommon.RecvHeaderInfo header;
+      this.send_download_package(group_name, remote_filename, file_offset, download_bytes);
+
+      InputStream in = storageSocket.getInputStream();
+      header = ProtoCommon.recvHeader(in, ProtoCommon.STORAGE_PROTO_CMD_RESP, -1);
+      this.errno = header.errno;
+      if (header.errno != 0) {
+        return header.errno;
+      }
+
+      byte[] buff = new byte[2 * 1024];
+      long remainBytes = header.body_len;
+      int bytes;
+
+      //System.out.println("expect_body_len=" + header.body_len);
+
+      while (remainBytes > 0) {
+        if ((bytes = in.read(buff, 0, remainBytes > buff.length ? buff.length : (int) remainBytes)) < 0) {
+          throw new IOException("recv package size " + (header.body_len - remainBytes) + " != " + header.body_len);
+        }
+
+        if ((result = callback.recv(header.body_len, buff, bytes)) != 0) {
+          this.errno = (byte) result;
+          return result;
+        }
+
+        remainBytes -= bytes;
+        //System.out.println("totalBytes=" + (header.body_len - remainBytes));
+      }
+
+      return 0;
+    } catch (IOException ex) {
+      if (!bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+
+      throw ex;
+    } finally {
+      if (bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+    }
+  }
+
+  /**
+   * get all metadata items from storage server
+   *
+   * @param group_name      the group name of storage server
+   * @param remote_filename filename on storage server
+   * @return meta info array, return null if fail
+   */
+  public NameValuePair[] get_metadata(String group_name, String remote_filename) throws IOException, MyException {
+    boolean bNewConnection = this.newUpdatableStorageConnection(group_name, remote_filename);
+    Socket storageSocket = this.storageServer.getSocket();
+
+    try {
+      ProtoCommon.RecvPackageInfo pkgInfo;
+
+      this.send_package(ProtoCommon.STORAGE_PROTO_CMD_GET_METADATA, group_name, remote_filename);
+      pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(),
+        ProtoCommon.STORAGE_PROTO_CMD_RESP, -1);
+
+      this.errno = pkgInfo.errno;
+      if (pkgInfo.errno != 0) {
+        return null;
+      }
+
+      return ProtoCommon.split_metadata(new String(pkgInfo.body, ClientGlobal.g_charset));
+    } catch (IOException ex) {
+      if (!bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+
+      throw ex;
+    } finally {
+      if (bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+    }
+  }
+
+  /**
+   * set metadata items to storage server
+   *
+   * @param group_name      the group name of storage server
+   * @param remote_filename filename on storage server
+   * @param meta_list       meta item array
+   * @param op_flag         flag, can be one of following values: <br>
+   *                        <ul><li> ProtoCommon.STORAGE_SET_METADATA_FLAG_OVERWRITE: overwrite all old
+   *                        metadata items</li></ul>
+   *                        <ul><li> ProtoCommon.STORAGE_SET_METADATA_FLAG_MERGE: merge, insert when
+   *                        the metadata item not exist, otherwise update it</li></ul>
+   * @return 0 for success, !=0 fail (error code)
+   */
+  public int set_metadata(String group_name, String remote_filename,
+                          NameValuePair[] meta_list, byte op_flag) throws IOException, MyException {
+    boolean bNewConnection = this.newUpdatableStorageConnection(group_name, remote_filename);
+    Socket storageSocket = this.storageServer.getSocket();
+
+    try {
+      byte[] header;
+      byte[] groupBytes;
+      byte[] filenameBytes;
+      byte[] meta_buff;
+      byte[] bs;
+      int groupLen;
+      byte[] sizeBytes;
+      ProtoCommon.RecvPackageInfo pkgInfo;
+
+      if (meta_list == null) {
+        meta_buff = new byte[0];
+      } else {
+        meta_buff = ProtoCommon.pack_metadata(meta_list).getBytes(ClientGlobal.g_charset);
+      }
+
+      filenameBytes = remote_filename.getBytes(ClientGlobal.g_charset);
+      sizeBytes = new byte[2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE];
+      Arrays.fill(sizeBytes, (byte) 0);
+
+      bs = ProtoCommon.long2buff(filenameBytes.length);
+      System.arraycopy(bs, 0, sizeBytes, 0, bs.length);
+      bs = ProtoCommon.long2buff(meta_buff.length);
+      System.arraycopy(bs, 0, sizeBytes, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE, bs.length);
+
+      groupBytes = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN];
+      bs = group_name.getBytes(ClientGlobal.g_charset);
+
+      Arrays.fill(groupBytes, (byte) 0);
+      if (bs.length <= groupBytes.length) {
+        groupLen = bs.length;
+      } else {
+        groupLen = groupBytes.length;
+      }
+      System.arraycopy(bs, 0, groupBytes, 0, groupLen);
+
+      header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_SET_METADATA,
+        2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE + 1 + groupBytes.length
+          + filenameBytes.length + meta_buff.length, (byte) 0);
+      OutputStream out = storageSocket.getOutputStream();
+      byte[] wholePkg = new byte[header.length + sizeBytes.length + 1 + groupBytes.length + filenameBytes.length];
+      System.arraycopy(header, 0, wholePkg, 0, header.length);
+      System.arraycopy(sizeBytes, 0, wholePkg, header.length, sizeBytes.length);
+      wholePkg[header.length + sizeBytes.length] = op_flag;
+      System.arraycopy(groupBytes, 0, wholePkg, header.length + sizeBytes.length + 1, groupBytes.length);
+      System.arraycopy(filenameBytes, 0, wholePkg, header.length + sizeBytes.length + 1 + groupBytes.length, filenameBytes.length);
+      out.write(wholePkg);
+      if (meta_buff.length > 0) {
+        out.write(meta_buff);
+      }
+
+      pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(),
+        ProtoCommon.STORAGE_PROTO_CMD_RESP, 0);
+
+      this.errno = pkgInfo.errno;
+      return pkgInfo.errno;
+    } catch (IOException ex) {
+      if (!bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+
+      throw ex;
+    } finally {
+      if (bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+    }
+  }
+
+  /**
+   * get file info decoded from the filename, fetch from the storage if necessary
+   *
+   * @param group_name      the group name
+   * @param remote_filename the filename
+   * @return FileInfo object for success, return null for fail
+   */
+  public FileInfo get_file_info(String group_name, String remote_filename) throws IOException, MyException {
+    if (remote_filename.length() < ProtoCommon.FDFS_FILE_PATH_LEN + ProtoCommon.FDFS_FILENAME_BASE64_LENGTH
+      + ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + 1) {
+      this.errno = ProtoCommon.ERR_NO_EINVAL;
+      return null;
+    }
+
+    byte[] buff = base64.decodeAuto(remote_filename.substring(ProtoCommon.FDFS_FILE_PATH_LEN,
+      ProtoCommon.FDFS_FILE_PATH_LEN + ProtoCommon.FDFS_FILENAME_BASE64_LENGTH));
+
+    long file_size = ProtoCommon.buff2long(buff, 4 * 2);
+    if (((remote_filename.length() > ProtoCommon.TRUNK_LOGIC_FILENAME_LENGTH) ||
+      ((remote_filename.length() > ProtoCommon.NORMAL_LOGIC_FILENAME_LENGTH) && ((file_size & ProtoCommon.TRUNK_FILE_MARK_SIZE) == 0))) ||
+      ((file_size & ProtoCommon.APPENDER_FILE_SIZE) != 0)) { //slave file or appender file
+      FileInfo fi = this.query_file_info(group_name, remote_filename);
+      if (fi == null) {
+        return null;
+      }
+      return fi;
+    }
+
+    FileInfo fileInfo = new FileInfo(file_size, 0, 0, ProtoCommon.getIpAddress(buff, 0));
+    fileInfo.setCreateTimestamp(ProtoCommon.buff2int(buff, 4));
+    if ((file_size >> 63) != 0) {
+      file_size &= 0xFFFFFFFFL;  //low 32 bits is file size
+      fileInfo.setFileSize(file_size);
+    }
+    fileInfo.setCrc32(ProtoCommon.buff2int(buff, 4 * 4));
+
+    return fileInfo;
+  }
+
+  /**
+   * get file info from storage server
+   *
+   * @param group_name      the group name of storage server
+   * @param remote_filename filename on storage server
+   * @return FileInfo object for success, return null for fail
+   */
+  public FileInfo query_file_info(String group_name, String remote_filename) throws IOException, MyException {
+    boolean bNewConnection = this.newUpdatableStorageConnection(group_name, remote_filename);
+    Socket storageSocket = this.storageServer.getSocket();
+
+    try {
+      byte[] header;
+      byte[] groupBytes;
+      byte[] filenameBytes;
+      byte[] bs;
+      int groupLen;
+      ProtoCommon.RecvPackageInfo pkgInfo;
+
+      filenameBytes = remote_filename.getBytes(ClientGlobal.g_charset);
+      groupBytes = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN];
+      bs = group_name.getBytes(ClientGlobal.g_charset);
+
+      Arrays.fill(groupBytes, (byte) 0);
+      if (bs.length <= groupBytes.length) {
+        groupLen = bs.length;
+      } else {
+        groupLen = groupBytes.length;
+      }
+      System.arraycopy(bs, 0, groupBytes, 0, groupLen);
+
+      header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_QUERY_FILE_INFO,
+        +groupBytes.length + filenameBytes.length, (byte) 0);
+      OutputStream out = storageSocket.getOutputStream();
+      byte[] wholePkg = new byte[header.length + groupBytes.length + filenameBytes.length];
+      System.arraycopy(header, 0, wholePkg, 0, header.length);
+      System.arraycopy(groupBytes, 0, wholePkg, header.length, groupBytes.length);
+      System.arraycopy(filenameBytes, 0, wholePkg, header.length + groupBytes.length, filenameBytes.length);
+      out.write(wholePkg);
+
+      pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(),
+        ProtoCommon.STORAGE_PROTO_CMD_RESP,
+        3 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE +
+          ProtoCommon.FDFS_IPADDR_SIZE);
+
+      this.errno = pkgInfo.errno;
+      if (pkgInfo.errno != 0) {
+        return null;
+      }
+
+      long file_size = ProtoCommon.buff2long(pkgInfo.body, 0);
+      int create_timestamp = (int) ProtoCommon.buff2long(pkgInfo.body, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+      int crc32 = (int) ProtoCommon.buff2long(pkgInfo.body, 2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+      String source_ip_addr = (new String(pkgInfo.body, 3 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE, ProtoCommon.FDFS_IPADDR_SIZE)).trim();
+      return new FileInfo(file_size, create_timestamp, crc32, source_ip_addr);
+    } catch (IOException ex) {
+      if (!bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+
+      throw ex;
+    } finally {
+      if (bNewConnection) {
+        try {
+          this.storageServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        } finally {
+          this.storageServer = null;
+        }
+      }
+    }
+  }
+
+  /**
+   * check storage socket, if null create a new connection
+   *
+   * @param group_name the group name to upload file to, can be empty
+   * @return true if create a new connection
+   */
+  protected boolean newWritableStorageConnection(String group_name) throws IOException, MyException {
+    if (this.storageServer != null) {
+      return false;
+    } else {
+      TrackerClient tracker = new TrackerClient();
+      this.storageServer = tracker.getStoreStorage(this.trackerServer, group_name);
+      if (this.storageServer == null) {
+        throw new MyException("getStoreStorage fail, errno code: " + tracker.getErrorCode());
+      }
+      return true;
+    }
+  }
+
+  /**
+   * check storage socket, if null create a new connection
+   *
+   * @param group_name      the group name of storage server
+   * @param remote_filename filename on storage server
+   * @return true if create a new connection
+   */
+  protected boolean newReadableStorageConnection(String group_name, String remote_filename) throws IOException, MyException {
+    if (this.storageServer != null) {
+      return false;
+    } else {
+      TrackerClient tracker = new TrackerClient();
+      this.storageServer = tracker.getFetchStorage(this.trackerServer, group_name, remote_filename);
+      if (this.storageServer == null) {
+        throw new MyException("getStoreStorage fail, errno code: " + tracker.getErrorCode());
+      }
+      return true;
+    }
+  }
+
+  /**
+   * check storage socket, if null create a new connection
+   *
+   * @param group_name      the group name of storage server
+   * @param remote_filename filename on storage server
+   * @return true if create a new connection
+   */
+  protected boolean newUpdatableStorageConnection(String group_name, String remote_filename) throws IOException, MyException {
+    if (this.storageServer != null) {
+      return false;
+    } else {
+      TrackerClient tracker = new TrackerClient();
+      this.storageServer = tracker.getUpdateStorage(this.trackerServer, group_name, remote_filename);
+      if (this.storageServer == null) {
+        throw new MyException("getStoreStorage fail, errno code: " + tracker.getErrorCode());
+      }
+      return true;
+    }
+  }
+
+  /**
+   * send package to storage server
+   *
+   * @param cmd             which command to send
+   * @param group_name      the group name of storage server
+   * @param remote_filename filename on storage server
+   */
+  protected void send_package(byte cmd, String group_name, String remote_filename) throws IOException {
+    byte[] header;
+    byte[] groupBytes;
+    byte[] filenameBytes;
+    byte[] bs;
+    int groupLen;
+
+    groupBytes = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN];
+    bs = group_name.getBytes(ClientGlobal.g_charset);
+    filenameBytes = remote_filename.getBytes(ClientGlobal.g_charset);
+
+    Arrays.fill(groupBytes, (byte) 0);
+    if (bs.length <= groupBytes.length) {
+      groupLen = bs.length;
+    } else {
+      groupLen = groupBytes.length;
+    }
+    System.arraycopy(bs, 0, groupBytes, 0, groupLen);
+
+    header = ProtoCommon.packHeader(cmd, groupBytes.length + filenameBytes.length, (byte) 0);
+    byte[] wholePkg = new byte[header.length + groupBytes.length + filenameBytes.length];
+    System.arraycopy(header, 0, wholePkg, 0, header.length);
+    System.arraycopy(groupBytes, 0, wholePkg, header.length, groupBytes.length);
+    System.arraycopy(filenameBytes, 0, wholePkg, header.length + groupBytes.length, filenameBytes.length);
+    this.storageServer.getSocket().getOutputStream().write(wholePkg);
+  }
+
+  /**
+   * send package to storage server
+   *
+   * @param group_name      the group name of storage server
+   * @param remote_filename filename on storage server
+   * @param file_offset     the start offset of the file
+   * @param download_bytes  download bytes
+   */
+  protected void send_download_package(String group_name, String remote_filename, long file_offset, long download_bytes) throws IOException {
+    byte[] header;
+    byte[] bsOffset;
+    byte[] bsDownBytes;
+    byte[] groupBytes;
+    byte[] filenameBytes;
+    byte[] bs;
+    int groupLen;
+
+    bsOffset = ProtoCommon.long2buff(file_offset);
+    bsDownBytes = ProtoCommon.long2buff(download_bytes);
+    groupBytes = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN];
+    bs = group_name.getBytes(ClientGlobal.g_charset);
+    filenameBytes = remote_filename.getBytes(ClientGlobal.g_charset);
+
+    Arrays.fill(groupBytes, (byte) 0);
+    if (bs.length <= groupBytes.length) {
+      groupLen = bs.length;
+    } else {
+      groupLen = groupBytes.length;
+    }
+    System.arraycopy(bs, 0, groupBytes, 0, groupLen);
+
+    header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_DOWNLOAD_FILE,
+      bsOffset.length + bsDownBytes.length + groupBytes.length + filenameBytes.length, (byte) 0);
+    byte[] wholePkg = new byte[header.length + bsOffset.length + bsDownBytes.length + groupBytes.length + filenameBytes.length];
+    System.arraycopy(header, 0, wholePkg, 0, header.length);
+    System.arraycopy(bsOffset, 0, wholePkg, header.length, bsOffset.length);
+    System.arraycopy(bsDownBytes, 0, wholePkg, header.length + bsOffset.length, bsDownBytes.length);
+    System.arraycopy(groupBytes, 0, wholePkg, header.length + bsOffset.length + bsDownBytes.length, groupBytes.length);
+    System.arraycopy(filenameBytes, 0, wholePkg, header.length + bsOffset.length + bsDownBytes.length + groupBytes.length, filenameBytes.length);
+    this.storageServer.getSocket().getOutputStream().write(wholePkg);
+  }
+
+  /**
+   * Upload file by file buff
+   *
+   * @author Happy Fish / YuQing
+   * @version Version 1.12
+   */
+  public static class UploadBuff implements UploadCallback {
+    private byte[] fileBuff;
+    private int offset;
+    private int length;
+
+    /**
+     * constructor
+     *
+     * @param fileBuff the file buff for uploading
+     */
+    public UploadBuff(byte[] fileBuff, int offset, int length) {
+      super();
+      this.fileBuff = fileBuff;
+      this.offset = offset;
+      this.length = length;
+    }
+
+    /**
+     * send file content callback function, be called only once when the file uploaded
+     *
+     * @param out output stream for writing file content
+     * @return 0 success, return none zero(errno) if fail
+     */
+    public int send(OutputStream out) throws IOException {
+      out.write(this.fileBuff, this.offset, this.length);
+
+      return 0;
+    }
+  }
+}

+ 732 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/StorageClient1.java

@@ -0,0 +1,732 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ * <p>
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package com.kmall.common.fileserver.fastdfs;
+
+import com.kmall.common.fileserver.common.MyException;
+import com.kmall.common.fileserver.common.NameValuePair;
+
+import java.io.IOException;
+
+/**
+ * Storage client for 1 field file id: combined group name and filename
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.21
+ */
+public class StorageClient1 extends StorageClient {
+  public static final String SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR = "/";
+
+  /**
+   * constructor
+   */
+  public StorageClient1() {
+    super();
+  }
+
+  /**
+   * constructor
+   *
+   * @param trackerServer the tracker server, can be null
+   * @param storageServer the storage server, can be null
+   */
+  public StorageClient1(TrackerServer trackerServer, StorageServer storageServer) {
+    super(trackerServer, storageServer);
+  }
+
+  public static byte split_file_id(String file_id, String[] results) {
+    int pos = file_id.indexOf(SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR);
+    if ((pos <= 0) || (pos == file_id.length() - 1)) {
+      return ProtoCommon.ERR_NO_EINVAL;
+    }
+
+    results[0] = file_id.substring(0, pos); //group name
+    results[1] = file_id.substring(pos + 1); //file name
+    return 0;
+  }
+
+  /**
+   * upload file to storage server (by file name)
+   *
+   * @param local_filename local filename to upload
+   * @param file_ext_name  file ext name, do not include dot(.), null to extract ext name from the local filename
+   * @param meta_list      meta info array
+   * @return file id(including group name and filename) if success, <br>
+   * return null if fail
+   */
+  public String upload_file1(String local_filename, String file_ext_name,
+                             NameValuePair[] meta_list) throws IOException, MyException {
+    String parts[] = this.upload_file(local_filename, file_ext_name, meta_list);
+    if (parts != null) {
+      return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * upload file to storage server (by file name)
+   *
+   * @param group_name     the group name to upload file to, can be empty
+   * @param local_filename local filename to upload
+   * @param file_ext_name  file ext name, do not include dot(.), null to extract ext name from the local filename
+   * @param meta_list      meta info array
+   * @return file id(including group name and filename) if success, <br>
+   * return null if fail
+   */
+  public String upload_file1(String group_name, String local_filename, String file_ext_name,
+                             NameValuePair[] meta_list) throws IOException, MyException {
+    String parts[] = this.upload_file(group_name, local_filename, file_ext_name, meta_list);
+    if (parts != null) {
+      return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * upload file to storage server (by file buff)
+   *
+   * @param file_buff     file content/buff
+   * @param file_ext_name file ext name, do not include dot(.)
+   * @param meta_list     meta info array
+   * @return file id(including group name and filename) if success, <br>
+   * return null if fail
+   */
+  public String upload_file1(byte[] file_buff, String file_ext_name,
+                             NameValuePair[] meta_list) throws IOException, MyException {
+    String parts[] = this.upload_file(file_buff, file_ext_name, meta_list);
+    if (parts != null) {
+      return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * upload file to storage server (by file buff)
+   *
+   * @param group_name    the group name to upload file to, can be empty
+   * @param file_buff     file content/buff
+   * @param file_ext_name file ext name, do not include dot(.)
+   * @param meta_list     meta info array
+   * @return file id(including group name and filename) if success, <br>
+   * return null if fail
+   */
+  public String upload_file1(String group_name, byte[] file_buff, String file_ext_name,
+                             NameValuePair[] meta_list) throws IOException, MyException {
+    String parts[] = this.upload_file(group_name, file_buff, file_ext_name, meta_list);
+    if (parts != null) {
+      return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * upload file to storage server (by callback)
+   *
+   * @param group_name    the group name to upload file to, can be empty
+   * @param file_size     the file size
+   * @param callback      the write data callback object
+   * @param file_ext_name file ext name, do not include dot(.)
+   * @param meta_list     meta info array
+   * @return file id(including group name and filename) if success, <br>
+   * return null if fail
+   */
+  public String upload_file1(String group_name, long file_size,
+                             UploadCallback callback, String file_ext_name,
+                             NameValuePair[] meta_list) throws IOException, MyException {
+    String parts[] = this.upload_file(group_name, file_size, callback, file_ext_name, meta_list);
+    if (parts != null) {
+      return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * upload appender file to storage server (by file name)
+   *
+   * @param local_filename local filename to upload
+   * @param file_ext_name  file ext name, do not include dot(.), null to extract ext name from the local filename
+   * @param meta_list      meta info array
+   * @return file id(including group name and filename) if success, <br>
+   * return null if fail
+   */
+  public String upload_appender_file1(String local_filename, String file_ext_name,
+                                      NameValuePair[] meta_list) throws IOException, MyException {
+    String parts[] = this.upload_appender_file(local_filename, file_ext_name, meta_list);
+    if (parts != null) {
+      return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * upload appender file to storage server (by file name)
+   *
+   * @param group_name     the group name to upload file to, can be empty
+   * @param local_filename local filename to upload
+   * @param file_ext_name  file ext name, do not include dot(.), null to extract ext name from the local filename
+   * @param meta_list      meta info array
+   * @return file id(including group name and filename) if success, <br>
+   * return null if fail
+   */
+  public String upload_appender_file1(String group_name, String local_filename, String file_ext_name,
+                                      NameValuePair[] meta_list) throws IOException, MyException {
+    String parts[] = this.upload_appender_file(group_name, local_filename, file_ext_name, meta_list);
+    if (parts != null) {
+      return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * upload appender file to storage server (by file buff)
+   *
+   * @param file_buff     file content/buff
+   * @param file_ext_name file ext name, do not include dot(.)
+   * @param meta_list     meta info array
+   * @return file id(including group name and filename) if success, <br>
+   * return null if fail
+   */
+  public String upload_appender_file1(byte[] file_buff, String file_ext_name,
+                                      NameValuePair[] meta_list) throws IOException, MyException {
+    String parts[] = this.upload_appender_file(file_buff, file_ext_name, meta_list);
+    if (parts != null) {
+      return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * upload appender file to storage server (by file buff)
+   *
+   * @param group_name    the group name to upload file to, can be empty
+   * @param file_buff     file content/buff
+   * @param file_ext_name file ext name, do not include dot(.)
+   * @param meta_list     meta info array
+   * @return file id(including group name and filename) if success, <br>
+   * return null if fail
+   */
+  public String upload_appender_file1(String group_name, byte[] file_buff, String file_ext_name,
+                                      NameValuePair[] meta_list) throws IOException, MyException {
+    String parts[] = this.upload_appender_file(group_name, file_buff, file_ext_name, meta_list);
+    if (parts != null) {
+      return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * upload appender file to storage server (by callback)
+   *
+   * @param group_name    the group name to upload file to, can be empty
+   * @param file_size     the file size
+   * @param callback      the write data callback object
+   * @param file_ext_name file ext name, do not include dot(.)
+   * @param meta_list     meta info array
+   * @return file id(including group name and filename) if success, <br>
+   * return null if fail
+   */
+  public String upload_appender_file1(String group_name, long file_size,
+                                      UploadCallback callback, String file_ext_name,
+                                      NameValuePair[] meta_list) throws IOException, MyException {
+    String parts[] = this.upload_appender_file(group_name, file_size, callback, file_ext_name, meta_list);
+    if (parts != null) {
+      return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * upload file to storage server (by file name, slave file mode)
+   *
+   * @param master_file_id the master file id to generate the slave file
+   * @param prefix_name    the prefix name to generate the slave file
+   * @param local_filename local filename to upload
+   * @param file_ext_name  file ext name, do not include dot(.), null to extract ext name from the local filename
+   * @param meta_list      meta info array
+   * @return file id(including group name and filename) if success, <br>
+   * return null if fail
+   */
+  public String upload_file1(String master_file_id, String prefix_name,
+                             String local_filename, String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(master_file_id, parts);
+    if (this.errno != 0) {
+      return null;
+    }
+
+    parts = this.upload_file(parts[0], parts[1], prefix_name,
+      local_filename, file_ext_name, meta_list);
+    if (parts != null) {
+      return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * upload file to storage server (by file buff, slave file mode)
+   *
+   * @param master_file_id the master file id to generate the slave file
+   * @param prefix_name    the prefix name to generate the slave file
+   * @param file_buff      file content/buff
+   * @param file_ext_name  file ext name, do not include dot(.)
+   * @param meta_list      meta info array
+   * @return file id(including group name and filename) if success, <br>
+   * return null if fail
+   */
+  public String upload_file1(String master_file_id, String prefix_name,
+                             byte[] file_buff, String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(master_file_id, parts);
+    if (this.errno != 0) {
+      return null;
+    }
+
+    parts = this.upload_file(parts[0], parts[1], prefix_name, file_buff, file_ext_name, meta_list);
+    if (parts != null) {
+      return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * upload file to storage server (by file buff, slave file mode)
+   *
+   * @param master_file_id the master file id to generate the slave file
+   * @param prefix_name    the prefix name to generate the slave file
+   * @param file_buff      file content/buff
+   * @param file_ext_name  file ext name, do not include dot(.)
+   * @param meta_list      meta info array
+   * @return file id(including group name and filename) if success, <br>
+   * return null if fail
+   */
+  public String upload_file1(String master_file_id, String prefix_name,
+                             byte[] file_buff, int offset, int length, String file_ext_name,
+                             NameValuePair[] meta_list) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(master_file_id, parts);
+    if (this.errno != 0) {
+      return null;
+    }
+
+    parts = this.upload_file(parts[0], parts[1], prefix_name, file_buff,
+      offset, length, file_ext_name, meta_list);
+    if (parts != null) {
+      return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * upload file to storage server (by callback)
+   *
+   * @param master_file_id the master file id to generate the slave file
+   * @param prefix_name    the prefix name to generate the slave file
+   * @param file_size      the file size
+   * @param callback       the write data callback object
+   * @param file_ext_name  file ext name, do not include dot(.)
+   * @param meta_list      meta info array
+   * @return file id(including group name and filename) if success, <br>
+   * return null if fail
+   */
+  public String upload_file1(String master_file_id, String prefix_name, long file_size,
+                             UploadCallback callback, String file_ext_name,
+                             NameValuePair[] meta_list) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(master_file_id, parts);
+    if (this.errno != 0) {
+      return null;
+    }
+
+    parts = this.upload_file(parts[0], parts[1], prefix_name, file_size, callback, file_ext_name, meta_list);
+    if (parts != null) {
+      return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * append file to storage server (by file name)
+   *
+   * @param appender_file_id the appender file id
+   * @param local_filename   local filename to append
+   * @return 0 for success, != 0 for error (error no)
+   */
+  public int append_file1(String appender_file_id, String local_filename) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(appender_file_id, parts);
+    if (this.errno != 0) {
+      return this.errno;
+    }
+
+    return this.append_file(parts[0], parts[1], local_filename);
+  }
+
+  /**
+   * append file to storage server (by file buff)
+   *
+   * @param appender_file_id the appender file id
+   * @param file_buff        file content/buff
+   * @return 0 for success, != 0 for error (error no)
+   */
+  public int append_file1(String appender_file_id, byte[] file_buff) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(appender_file_id, parts);
+    if (this.errno != 0) {
+      return this.errno;
+    }
+
+    return this.append_file(parts[0], parts[1], file_buff);
+  }
+
+  /**
+   * append file to storage server (by file buff)
+   *
+   * @param appender_file_id the appender file id
+   * @param file_buff        file content/buffer
+   * @param offset           start offset of the buffer
+   * @param length           the length of the buffer to append
+   * @return 0 for success, != 0 for error (error no)
+   */
+  public int append_file1(String appender_file_id, byte[] file_buff, int offset, int length) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(appender_file_id, parts);
+    if (this.errno != 0) {
+      return this.errno;
+    }
+
+    return this.append_file(parts[0], parts[1], file_buff, offset, length);
+  }
+
+  /**
+   * append file to storage server (by callback)
+   *
+   * @param appender_file_id the appender file id
+   * @param file_size        the file size
+   * @param callback         the write data callback object
+   * @return 0 for success, != 0 for error (error no)
+   */
+  public int append_file1(String appender_file_id, long file_size, UploadCallback callback) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(appender_file_id, parts);
+    if (this.errno != 0) {
+      return this.errno;
+    }
+
+    return this.append_file(parts[0], parts[1], file_size, callback);
+  }
+
+  /**
+   * modify appender file to storage server (by file name)
+   *
+   * @param appender_file_id the appender file id
+   * @param file_offset      the offset of appender file
+   * @param local_filename   local filename to append
+   * @return 0 for success, != 0 for error (error no)
+   */
+  public int modify_file1(String appender_file_id,
+                          long file_offset, String local_filename) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(appender_file_id, parts);
+    if (this.errno != 0) {
+      return this.errno;
+    }
+
+    return this.modify_file(parts[0], parts[1], file_offset, local_filename);
+  }
+
+  /**
+   * modify appender file to storage server (by file buff)
+   *
+   * @param appender_file_id the appender file id
+   * @param file_offset      the offset of appender file
+   * @param file_buff        file content/buff
+   * @return 0 for success, != 0 for error (error no)
+   */
+  public int modify_file1(String appender_file_id,
+                          long file_offset, byte[] file_buff) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(appender_file_id, parts);
+    if (this.errno != 0) {
+      return this.errno;
+    }
+
+    return this.modify_file(parts[0], parts[1], file_offset, file_buff);
+  }
+
+  /**
+   * modify appender file to storage server (by file buff)
+   *
+   * @param appender_file_id the appender file id
+   * @param file_offset      the offset of appender file
+   * @param file_buff        file content/buff
+   * @param buffer_offset    start offset of the buff
+   * @param buffer_length    the length of buff to modify
+   * @return 0 for success, != 0 for error (error no)
+   */
+  public int modify_file1(String appender_file_id,
+                          long file_offset, byte[] file_buff, int buffer_offset, int buffer_length) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(appender_file_id, parts);
+    if (this.errno != 0) {
+      return this.errno;
+    }
+
+    return this.modify_file(parts[0], parts[1], file_offset,
+      file_buff, buffer_offset, buffer_length);
+  }
+
+  /**
+   * modify appender file to storage server (by callback)
+   *
+   * @param appender_file_id the appender file id
+   * @param file_offset      the offset of appender file
+   * @param modify_size      the modify size
+   * @param callback         the write data callback object
+   * @return 0 for success, != 0 for error (error no)
+   */
+  public int modify_file1(String appender_file_id,
+                          long file_offset, long modify_size, UploadCallback callback) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(appender_file_id, parts);
+    if (this.errno != 0) {
+      return this.errno;
+    }
+
+    return this.modify_file(parts[0], parts[1], file_offset, modify_size, callback);
+  }
+
+  /**
+   * delete file from storage server
+   *
+   * @param file_id the file id(including group name and filename)
+   * @return 0 for success, none zero for fail (error code)
+   */
+  public int delete_file1(String file_id) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(file_id, parts);
+    if (this.errno != 0) {
+      return this.errno;
+    }
+
+    return this.delete_file(parts[0], parts[1]);
+  }
+
+  /**
+   * truncate appender file to size 0 from storage server
+   *
+   * @param appender_file_id the appender file id
+   * @return 0 for success, none zero for fail (error code)
+   */
+  public int truncate_file1(String appender_file_id) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(appender_file_id, parts);
+    if (this.errno != 0) {
+      return this.errno;
+    }
+
+    return this.truncate_file(parts[0], parts[1]);
+  }
+
+  /**
+   * truncate appender file from storage server
+   *
+   * @param appender_file_id    the appender file id
+   * @param truncated_file_size truncated file size
+   * @return 0 for success, none zero for fail (error code)
+   */
+  public int truncate_file1(String appender_file_id, long truncated_file_size) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(appender_file_id, parts);
+    if (this.errno != 0) {
+      return this.errno;
+    }
+
+    return this.truncate_file(parts[0], parts[1], truncated_file_size);
+  }
+
+  /**
+   * download file from storage server
+   *
+   * @param file_id the file id(including group name and filename)
+   * @return file content/buffer, return null if fail
+   */
+  public byte[] download_file1(String file_id) throws IOException, MyException {
+    final long file_offset = 0;
+    final long download_bytes = 0;
+
+    return this.download_file1(file_id, file_offset, download_bytes);
+  }
+
+  /**
+   * download file from storage server
+   *
+   * @param file_id        the file id(including group name and filename)
+   * @param file_offset    the start offset of the file
+   * @param download_bytes download bytes, 0 for remain bytes from offset
+   * @return file content/buff, return null if fail
+   */
+  public byte[] download_file1(String file_id, long file_offset, long download_bytes) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(file_id, parts);
+    if (this.errno != 0) {
+      return null;
+    }
+
+    return this.download_file(parts[0], parts[1], file_offset, download_bytes);
+  }
+
+  /**
+   * download file from storage server
+   *
+   * @param file_id        the file id(including group name and filename)
+   * @param local_filename the filename on local
+   * @return 0 success, return none zero errno if fail
+   */
+  public int download_file1(String file_id, String local_filename) throws IOException, MyException {
+    final long file_offset = 0;
+    final long download_bytes = 0;
+
+    return this.download_file1(file_id, file_offset, download_bytes, local_filename);
+  }
+
+  /**
+   * download file from storage server
+   *
+   * @param file_id        the file id(including group name and filename)
+   * @param file_offset    the start offset of the file
+   * @param download_bytes download bytes, 0 for remain bytes from offset
+   * @param local_filename the filename on local
+   * @return 0 success, return none zero errno if fail
+   */
+  public int download_file1(String file_id, long file_offset, long download_bytes, String local_filename) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(file_id, parts);
+    if (this.errno != 0) {
+      return this.errno;
+    }
+
+    return this.download_file(parts[0], parts[1], file_offset, download_bytes, local_filename);
+  }
+
+  /**
+   * download file from storage server
+   *
+   * @param file_id  the file id(including group name and filename)
+   * @param callback the callback object, will call callback.recv() when data arrive
+   * @return 0 success, return none zero errno if fail
+   */
+  public int download_file1(String file_id, DownloadCallback callback) throws IOException, MyException {
+    final long file_offset = 0;
+    final long download_bytes = 0;
+
+    return this.download_file1(file_id, file_offset, download_bytes, callback);
+  }
+
+  /**
+   * download file from storage server
+   *
+   * @param file_id        the file id(including group name and filename)
+   * @param file_offset    the start offset of the file
+   * @param download_bytes download bytes, 0 for remain bytes from offset
+   * @param callback       the callback object, will call callback.recv() when data arrive
+   * @return 0 success, return none zero errno if fail
+   */
+  public int download_file1(String file_id, long file_offset, long download_bytes, DownloadCallback callback) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(file_id, parts);
+    if (this.errno != 0) {
+      return this.errno;
+    }
+
+    return this.download_file(parts[0], parts[1], file_offset, download_bytes, callback);
+  }
+
+  /**
+   * get all metadata items from storage server
+   *
+   * @param file_id the file id(including group name and filename)
+   * @return meta info array, return null if fail
+   */
+  public NameValuePair[] get_metadata1(String file_id) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(file_id, parts);
+    if (this.errno != 0) {
+      return null;
+    }
+
+    return this.get_metadata(parts[0], parts[1]);
+  }
+
+  /**
+   * set metadata items to storage server
+   *
+   * @param file_id   the file id(including group name and filename)
+   * @param meta_list meta item array
+   * @param op_flag   flag, can be one of following values: <br>
+   *                  <ul><li> ProtoCommon.STORAGE_SET_METADATA_FLAG_OVERWRITE: overwrite all old
+   *                  metadata items</li></ul>
+   *                  <ul><li> ProtoCommon.STORAGE_SET_METADATA_FLAG_MERGE: merge, insert when
+   *                  the metadata item not exist, otherwise update it</li></ul>
+   * @return 0 for success, !=0 fail (error code)
+   */
+  public int set_metadata1(String file_id, NameValuePair[] meta_list, byte op_flag) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(file_id, parts);
+    if (this.errno != 0) {
+      return this.errno;
+    }
+
+    return this.set_metadata(parts[0], parts[1], meta_list, op_flag);
+  }
+
+  /**
+   * get file info from storage server
+   *
+   * @param file_id the file id(including group name and filename)
+   * @return FileInfo object for success, return null for fail
+   */
+  public FileInfo query_file_info1(String file_id) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(file_id, parts);
+    if (this.errno != 0) {
+      return null;
+    }
+
+    return this.query_file_info(parts[0], parts[1]);
+  }
+
+  /**
+   * get file info decoded from filename
+   *
+   * @param file_id the file id(including group name and filename)
+   * @return FileInfo object for success, return null for fail
+   */
+  public FileInfo get_file_info1(String file_id) throws IOException, MyException {
+    String[] parts = new String[2];
+    this.errno = this.split_file_id(file_id, parts);
+    if (this.errno != 0) {
+      return null;
+    }
+
+    return this.get_file_info(parts[0], parts[1]);
+  }
+}

+ 57 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/StorageServer.java

@@ -0,0 +1,57 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ * <p>
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package com.kmall.common.fileserver.fastdfs;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+/**
+ * Storage Server Info
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.11
+ */
+public class StorageServer extends TrackerServer {
+  protected int store_path_index = 0;
+
+  /**
+   * Constructor
+   *
+   * @param ip_addr    the ip address of storage server
+   * @param port       the port of storage server
+   * @param store_path the store path index on the storage server
+   */
+  public StorageServer(String ip_addr, int port, int store_path) throws IOException {
+    super(ClientGlobal.getSocket(ip_addr, port), new InetSocketAddress(ip_addr, port));
+    this.store_path_index = store_path;
+  }
+
+  /**
+   * Constructor
+   *
+   * @param ip_addr    the ip address of storage server
+   * @param port       the port of storage server
+   * @param store_path the store path index on the storage server
+   */
+  public StorageServer(String ip_addr, int port, byte store_path) throws IOException {
+    super(ClientGlobal.getSocket(ip_addr, port), new InetSocketAddress(ip_addr, port));
+    if (store_path < 0) {
+      this.store_path_index = 256 + store_path;
+    } else {
+      this.store_path_index = store_path;
+    }
+  }
+
+  /**
+   * @return the store path index on the storage server
+   */
+  public int getStorePathIndex() {
+    return this.store_path_index;
+  }
+}

+ 73 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/StructBase.java

@@ -0,0 +1,73 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ * <p>
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package com.kmall.common.fileserver.fastdfs;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Date;
+
+/**
+ * C struct body decoder
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.17
+ */
+public abstract class StructBase {
+  /**
+   * set fields
+   *
+   * @param bs     byte array
+   * @param offset start offset
+   */
+  public abstract void setFields(byte[] bs, int offset);
+
+  protected String stringValue(byte[] bs, int offset, FieldInfo filedInfo) {
+    try {
+      return (new String(bs, offset + filedInfo.offset, filedInfo.size, ClientGlobal.g_charset)).trim();
+    } catch (UnsupportedEncodingException ex) {
+      ex.printStackTrace();
+      return null;
+    }
+  }
+
+  protected long longValue(byte[] bs, int offset, FieldInfo filedInfo) {
+    return ProtoCommon.buff2long(bs, offset + filedInfo.offset);
+  }
+
+  protected int intValue(byte[] bs, int offset, FieldInfo filedInfo) {
+    return (int) ProtoCommon.buff2long(bs, offset + filedInfo.offset);
+  }
+
+  protected int int32Value(byte[] bs, int offset, FieldInfo filedInfo) {
+    return ProtoCommon.buff2int(bs, offset + filedInfo.offset);
+  }
+
+  protected byte byteValue(byte[] bs, int offset, FieldInfo filedInfo) {
+    return bs[offset + filedInfo.offset];
+  }
+
+  protected boolean booleanValue(byte[] bs, int offset, FieldInfo filedInfo) {
+    return bs[offset + filedInfo.offset] != 0;
+  }
+
+  protected Date dateValue(byte[] bs, int offset, FieldInfo filedInfo) {
+    return new Date(ProtoCommon.buff2long(bs, offset + filedInfo.offset) * 1000);
+  }
+
+  protected static class FieldInfo {
+    protected String name;
+    protected int offset;
+    protected int size;
+
+    public FieldInfo(String name, int offset, int size) {
+      this.name = name;
+      this.offset = offset;
+      this.size = size;
+    }
+  }
+}

+ 225 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/StructGroupStat.java

@@ -0,0 +1,225 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ * <p>
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package com.kmall.common.fileserver.fastdfs;
+
+/**
+ * C struct body decoder
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.18
+ */
+public class StructGroupStat extends StructBase {
+  protected static final int FIELD_INDEX_GROUP_NAME = 0;
+  protected static final int FIELD_INDEX_TOTAL_MB = 1;
+  protected static final int FIELD_INDEX_FREE_MB = 2;
+  protected static final int FIELD_INDEX_TRUNK_FREE_MB = 3;
+  protected static final int FIELD_INDEX_STORAGE_COUNT = 4;
+  protected static final int FIELD_INDEX_STORAGE_PORT = 5;
+  protected static final int FIELD_INDEX_STORAGE_HTTP_PORT = 6;
+  protected static final int FIELD_INDEX_ACTIVE_COUNT = 7;
+  protected static final int FIELD_INDEX_CURRENT_WRITE_SERVER = 8;
+  protected static final int FIELD_INDEX_STORE_PATH_COUNT = 9;
+  protected static final int FIELD_INDEX_SUBDIR_COUNT_PER_PATH = 10;
+  protected static final int FIELD_INDEX_CURRENT_TRUNK_FILE_ID = 11;
+
+  protected static int fieldsTotalSize;
+  protected static StructBase.FieldInfo[] fieldsArray = new StructBase.FieldInfo[12];
+
+  static {
+    int offset = 0;
+    fieldsArray[FIELD_INDEX_GROUP_NAME] = new StructBase.FieldInfo("groupName", offset, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + 1);
+    offset += ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + 1;
+
+    fieldsArray[FIELD_INDEX_TOTAL_MB] = new StructBase.FieldInfo("totalMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_FREE_MB] = new StructBase.FieldInfo("freeMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TRUNK_FREE_MB] = new StructBase.FieldInfo("trunkFreeMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_STORAGE_COUNT] = new StructBase.FieldInfo("storageCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_STORAGE_PORT] = new StructBase.FieldInfo("storagePort", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_STORAGE_HTTP_PORT] = new StructBase.FieldInfo("storageHttpPort", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_ACTIVE_COUNT] = new StructBase.FieldInfo("activeCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_CURRENT_WRITE_SERVER] = new StructBase.FieldInfo("currentWriteServer", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_STORE_PATH_COUNT] = new StructBase.FieldInfo("storePathCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUBDIR_COUNT_PER_PATH] = new StructBase.FieldInfo("subdirCountPerPath", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_CURRENT_TRUNK_FILE_ID] = new StructBase.FieldInfo("currentTrunkFileId", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsTotalSize = offset;
+  }
+
+  protected String groupName;  //name of this group
+  protected long totalMB;      //total disk storage in MB
+  protected long freeMB;       //free disk space in MB
+  protected long trunkFreeMB;  //trunk free space in MB
+  protected int storageCount;  //storage server count
+  protected int storagePort;   //storage server port
+  protected int storageHttpPort; //storage server HTTP port
+  protected int activeCount;     //active storage server count
+  protected int currentWriteServer; //current storage server index to upload file
+  protected int storePathCount;     //store base path count of each storage server
+  protected int subdirCountPerPath; //sub dir count per store path
+  protected int currentTrunkFileId; //current trunk file id
+
+  /**
+   * get fields total size
+   *
+   * @return fields total size
+   */
+  public static int getFieldsTotalSize() {
+    return fieldsTotalSize;
+  }
+
+  /**
+   * get group name
+   *
+   * @return group name
+   */
+  public String getGroupName() {
+    return this.groupName;
+  }
+
+  /**
+   * get total disk space in MB
+   *
+   * @return total disk space in MB
+   */
+  public long getTotalMB() {
+    return this.totalMB;
+  }
+
+  /**
+   * get free disk space in MB
+   *
+   * @return free disk space in MB
+   */
+  public long getFreeMB() {
+    return this.freeMB;
+  }
+
+  /**
+   * get trunk free space in MB
+   *
+   * @return trunk free space in MB
+   */
+  public long getTrunkFreeMB() {
+    return this.trunkFreeMB;
+  }
+
+  /**
+   * get storage server count in this group
+   *
+   * @return storage server count in this group
+   */
+  public int getStorageCount() {
+    return this.storageCount;
+  }
+
+  /**
+   * get active storage server count in this group
+   *
+   * @return active storage server count in this group
+   */
+  public int getActiveCount() {
+    return this.activeCount;
+  }
+
+  /**
+   * get storage server port
+   *
+   * @return storage server port
+   */
+  public int getStoragePort() {
+    return this.storagePort;
+  }
+
+  /**
+   * get storage server HTTP port
+   *
+   * @return storage server HTTP port
+   */
+  public int getStorageHttpPort() {
+    return this.storageHttpPort;
+  }
+
+  /**
+   * get current storage server index to upload file
+   *
+   * @return current storage server index to upload file
+   */
+  public int getCurrentWriteServer() {
+    return this.currentWriteServer;
+  }
+
+  /**
+   * get store base path count of each storage server
+   *
+   * @return store base path count of each storage server
+   */
+  public int getStorePathCount() {
+    return this.storePathCount;
+  }
+
+  /**
+   * get sub dir count per store path
+   *
+   * @return sub dir count per store path
+   */
+  public int getSubdirCountPerPath() {
+    return this.subdirCountPerPath;
+  }
+
+  /**
+   * get current trunk file id
+   *
+   * @return current trunk file id
+   */
+  public int getCurrentTrunkFileId() {
+    return this.currentTrunkFileId;
+  }
+
+  /**
+   * set fields
+   *
+   * @param bs     byte array
+   * @param offset start offset
+   */
+  public void setFields(byte[] bs, int offset) {
+    this.groupName = stringValue(bs, offset, fieldsArray[FIELD_INDEX_GROUP_NAME]);
+    this.totalMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_MB]);
+    this.freeMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_FREE_MB]);
+    this.trunkFreeMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_TRUNK_FREE_MB]);
+    this.storageCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_COUNT]);
+    this.storagePort = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_PORT]);
+    this.storageHttpPort = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_HTTP_PORT]);
+    this.activeCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_ACTIVE_COUNT]);
+    this.currentWriteServer = intValue(bs, offset, fieldsArray[FIELD_INDEX_CURRENT_WRITE_SERVER]);
+    this.storePathCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORE_PATH_COUNT]);
+    this.subdirCountPerPath = intValue(bs, offset, fieldsArray[FIELD_INDEX_SUBDIR_COUNT_PER_PATH]);
+    this.currentTrunkFileId = intValue(bs, offset, fieldsArray[FIELD_INDEX_CURRENT_TRUNK_FILE_ID]);
+  }
+}

+ 982 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/StructStorageStat.java

@@ -0,0 +1,982 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ * <p>
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package com.kmall.common.fileserver.fastdfs;
+
+import java.util.Date;
+
+/**
+ * C struct body decoder
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.25
+ */
+public class StructStorageStat extends StructBase {
+  protected static final int FIELD_INDEX_STATUS = 0;
+  protected static final int FIELD_INDEX_ID = 1;
+  protected static final int FIELD_INDEX_IP_ADDR = 2;
+  protected static final int FIELD_INDEX_DOMAIN_NAME = 3;
+  protected static final int FIELD_INDEX_SRC_IP_ADDR = 4;
+  protected static final int FIELD_INDEX_VERSION = 5;
+  protected static final int FIELD_INDEX_JOIN_TIME = 6;
+  protected static final int FIELD_INDEX_UP_TIME = 7;
+  protected static final int FIELD_INDEX_TOTAL_MB = 8;
+  protected static final int FIELD_INDEX_FREE_MB = 9;
+  protected static final int FIELD_INDEX_UPLOAD_PRIORITY = 10;
+  protected static final int FIELD_INDEX_STORE_PATH_COUNT = 11;
+  protected static final int FIELD_INDEX_SUBDIR_COUNT_PER_PATH = 12;
+  protected static final int FIELD_INDEX_CURRENT_WRITE_PATH = 13;
+  protected static final int FIELD_INDEX_STORAGE_PORT = 14;
+  protected static final int FIELD_INDEX_STORAGE_HTTP_PORT = 15;
+
+  protected static final int FIELD_INDEX_CONNECTION_ALLOC_COUNT = 16;
+  protected static final int FIELD_INDEX_CONNECTION_CURRENT_COUNT = 17;
+  protected static final int FIELD_INDEX_CONNECTION_MAX_COUNT = 18;
+
+  protected static final int FIELD_INDEX_TOTAL_UPLOAD_COUNT = 19;
+  protected static final int FIELD_INDEX_SUCCESS_UPLOAD_COUNT = 20;
+  protected static final int FIELD_INDEX_TOTAL_APPEND_COUNT = 21;
+  protected static final int FIELD_INDEX_SUCCESS_APPEND_COUNT = 22;
+  protected static final int FIELD_INDEX_TOTAL_MODIFY_COUNT = 23;
+  protected static final int FIELD_INDEX_SUCCESS_MODIFY_COUNT = 24;
+  protected static final int FIELD_INDEX_TOTAL_TRUNCATE_COUNT = 25;
+  protected static final int FIELD_INDEX_SUCCESS_TRUNCATE_COUNT = 26;
+  protected static final int FIELD_INDEX_TOTAL_SET_META_COUNT = 27;
+  protected static final int FIELD_INDEX_SUCCESS_SET_META_COUNT = 28;
+  protected static final int FIELD_INDEX_TOTAL_DELETE_COUNT = 29;
+  protected static final int FIELD_INDEX_SUCCESS_DELETE_COUNT = 30;
+  protected static final int FIELD_INDEX_TOTAL_DOWNLOAD_COUNT = 31;
+  protected static final int FIELD_INDEX_SUCCESS_DOWNLOAD_COUNT = 32;
+  protected static final int FIELD_INDEX_TOTAL_GET_META_COUNT = 33;
+  protected static final int FIELD_INDEX_SUCCESS_GET_META_COUNT = 34;
+  protected static final int FIELD_INDEX_TOTAL_CREATE_LINK_COUNT = 35;
+  protected static final int FIELD_INDEX_SUCCESS_CREATE_LINK_COUNT = 36;
+  protected static final int FIELD_INDEX_TOTAL_DELETE_LINK_COUNT = 37;
+  protected static final int FIELD_INDEX_SUCCESS_DELETE_LINK_COUNT = 38;
+  protected static final int FIELD_INDEX_TOTAL_UPLOAD_BYTES = 39;
+  protected static final int FIELD_INDEX_SUCCESS_UPLOAD_BYTES = 40;
+  protected static final int FIELD_INDEX_TOTAL_APPEND_BYTES = 41;
+  protected static final int FIELD_INDEX_SUCCESS_APPEND_BYTES = 42;
+  protected static final int FIELD_INDEX_TOTAL_MODIFY_BYTES = 43;
+  protected static final int FIELD_INDEX_SUCCESS_MODIFY_BYTES = 44;
+  protected static final int FIELD_INDEX_TOTAL_DOWNLOAD_BYTES = 45;
+  protected static final int FIELD_INDEX_SUCCESS_DOWNLOAD_BYTES = 46;
+  protected static final int FIELD_INDEX_TOTAL_SYNC_IN_BYTES = 47;
+  protected static final int FIELD_INDEX_SUCCESS_SYNC_IN_BYTES = 48;
+  protected static final int FIELD_INDEX_TOTAL_SYNC_OUT_BYTES = 49;
+  protected static final int FIELD_INDEX_SUCCESS_SYNC_OUT_BYTES = 50;
+  protected static final int FIELD_INDEX_TOTAL_FILE_OPEN_COUNT = 51;
+  protected static final int FIELD_INDEX_SUCCESS_FILE_OPEN_COUNT = 52;
+  protected static final int FIELD_INDEX_TOTAL_FILE_READ_COUNT = 53;
+  protected static final int FIELD_INDEX_SUCCESS_FILE_READ_COUNT = 54;
+  protected static final int FIELD_INDEX_TOTAL_FILE_WRITE_COUNT = 55;
+  protected static final int FIELD_INDEX_SUCCESS_FILE_WRITE_COUNT = 56;
+  protected static final int FIELD_INDEX_LAST_SOURCE_UPDATE = 57;
+  protected static final int FIELD_INDEX_LAST_SYNC_UPDATE = 58;
+  protected static final int FIELD_INDEX_LAST_SYNCED_TIMESTAMP = 59;
+  protected static final int FIELD_INDEX_LAST_HEART_BEAT_TIME = 60;
+  protected static final int FIELD_INDEX_IF_TRUNK_FILE = 61;
+
+  protected static int fieldsTotalSize;
+  protected static StructBase.FieldInfo[] fieldsArray = new StructBase.FieldInfo[62];
+
+  static {
+    int offset = 0;
+
+    fieldsArray[FIELD_INDEX_STATUS] = new StructBase.FieldInfo("status", offset, 1);
+    offset += 1;
+
+    fieldsArray[FIELD_INDEX_ID] = new StructBase.FieldInfo("id", offset, ProtoCommon.FDFS_STORAGE_ID_MAX_SIZE);
+    offset += ProtoCommon.FDFS_STORAGE_ID_MAX_SIZE;
+
+    fieldsArray[FIELD_INDEX_IP_ADDR] = new StructBase.FieldInfo("ipAddr", offset, ProtoCommon.FDFS_IPADDR_SIZE);
+    offset += ProtoCommon.FDFS_IPADDR_SIZE;
+
+    fieldsArray[FIELD_INDEX_DOMAIN_NAME] = new StructBase.FieldInfo("domainName", offset, ProtoCommon.FDFS_DOMAIN_NAME_MAX_SIZE);
+    offset += ProtoCommon.FDFS_DOMAIN_NAME_MAX_SIZE;
+
+    fieldsArray[FIELD_INDEX_SRC_IP_ADDR] = new StructBase.FieldInfo("srcIpAddr", offset, ProtoCommon.FDFS_IPADDR_SIZE);
+    offset += ProtoCommon.FDFS_IPADDR_SIZE;
+
+    fieldsArray[FIELD_INDEX_VERSION] = new StructBase.FieldInfo("version", offset, ProtoCommon.FDFS_VERSION_SIZE);
+    offset += ProtoCommon.FDFS_VERSION_SIZE;
+
+    fieldsArray[FIELD_INDEX_JOIN_TIME] = new StructBase.FieldInfo("joinTime", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_UP_TIME] = new StructBase.FieldInfo("upTime", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TOTAL_MB] = new StructBase.FieldInfo("totalMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_FREE_MB] = new StructBase.FieldInfo("freeMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_UPLOAD_PRIORITY] = new StructBase.FieldInfo("uploadPriority", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_STORE_PATH_COUNT] = new StructBase.FieldInfo("storePathCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUBDIR_COUNT_PER_PATH] = new StructBase.FieldInfo("subdirCountPerPath", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_CURRENT_WRITE_PATH] = new StructBase.FieldInfo("currentWritePath", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_STORAGE_PORT] = new StructBase.FieldInfo("storagePort", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_STORAGE_HTTP_PORT] = new StructBase.FieldInfo("storageHttpPort", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_CONNECTION_ALLOC_COUNT] = new StructBase.FieldInfo("connectionAllocCount", offset, 4);
+    offset += 4;
+
+    fieldsArray[FIELD_INDEX_CONNECTION_CURRENT_COUNT] = new StructBase.FieldInfo("connectionCurrentCount", offset, 4);
+    offset += 4;
+
+    fieldsArray[FIELD_INDEX_CONNECTION_MAX_COUNT] = new StructBase.FieldInfo("connectionMaxCount", offset, 4);
+    offset += 4;
+
+    fieldsArray[FIELD_INDEX_TOTAL_UPLOAD_COUNT] = new StructBase.FieldInfo("totalUploadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUCCESS_UPLOAD_COUNT] = new StructBase.FieldInfo("successUploadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TOTAL_APPEND_COUNT] = new StructBase.FieldInfo("totalAppendCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUCCESS_APPEND_COUNT] = new StructBase.FieldInfo("successAppendCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TOTAL_MODIFY_COUNT] = new StructBase.FieldInfo("totalModifyCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUCCESS_MODIFY_COUNT] = new StructBase.FieldInfo("successModifyCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TOTAL_TRUNCATE_COUNT] = new StructBase.FieldInfo("totalTruncateCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUCCESS_TRUNCATE_COUNT] = new StructBase.FieldInfo("successTruncateCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TOTAL_SET_META_COUNT] = new StructBase.FieldInfo("totalSetMetaCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUCCESS_SET_META_COUNT] = new StructBase.FieldInfo("successSetMetaCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TOTAL_DELETE_COUNT] = new StructBase.FieldInfo("totalDeleteCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUCCESS_DELETE_COUNT] = new StructBase.FieldInfo("successDeleteCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TOTAL_DOWNLOAD_COUNT] = new StructBase.FieldInfo("totalDownloadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUCCESS_DOWNLOAD_COUNT] = new StructBase.FieldInfo("successDownloadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TOTAL_GET_META_COUNT] = new StructBase.FieldInfo("totalGetMetaCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUCCESS_GET_META_COUNT] = new StructBase.FieldInfo("successGetMetaCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TOTAL_CREATE_LINK_COUNT] = new StructBase.FieldInfo("totalCreateLinkCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUCCESS_CREATE_LINK_COUNT] = new StructBase.FieldInfo("successCreateLinkCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TOTAL_DELETE_LINK_COUNT] = new StructBase.FieldInfo("totalDeleteLinkCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUCCESS_DELETE_LINK_COUNT] = new StructBase.FieldInfo("successDeleteLinkCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TOTAL_UPLOAD_BYTES] = new StructBase.FieldInfo("totalUploadBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUCCESS_UPLOAD_BYTES] = new StructBase.FieldInfo("successUploadBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TOTAL_APPEND_BYTES] = new StructBase.FieldInfo("totalAppendBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUCCESS_APPEND_BYTES] = new StructBase.FieldInfo("successAppendBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TOTAL_MODIFY_BYTES] = new StructBase.FieldInfo("totalModifyBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUCCESS_MODIFY_BYTES] = new StructBase.FieldInfo("successModifyBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TOTAL_DOWNLOAD_BYTES] = new StructBase.FieldInfo("totalDownloadloadBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUCCESS_DOWNLOAD_BYTES] = new StructBase.FieldInfo("successDownloadloadBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TOTAL_SYNC_IN_BYTES] = new StructBase.FieldInfo("totalSyncInBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUCCESS_SYNC_IN_BYTES] = new StructBase.FieldInfo("successSyncInBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TOTAL_SYNC_OUT_BYTES] = new StructBase.FieldInfo("totalSyncOutBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUCCESS_SYNC_OUT_BYTES] = new StructBase.FieldInfo("successSyncOutBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TOTAL_FILE_OPEN_COUNT] = new StructBase.FieldInfo("totalFileOpenCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUCCESS_FILE_OPEN_COUNT] = new StructBase.FieldInfo("successFileOpenCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TOTAL_FILE_READ_COUNT] = new StructBase.FieldInfo("totalFileReadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUCCESS_FILE_READ_COUNT] = new StructBase.FieldInfo("successFileReadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_TOTAL_FILE_WRITE_COUNT] = new StructBase.FieldInfo("totalFileWriteCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_SUCCESS_FILE_WRITE_COUNT] = new StructBase.FieldInfo("successFileWriteCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_LAST_SOURCE_UPDATE] = new StructBase.FieldInfo("lastSourceUpdate", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_LAST_SYNC_UPDATE] = new StructBase.FieldInfo("lastSyncUpdate", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_LAST_SYNCED_TIMESTAMP] = new StructBase.FieldInfo("lastSyncedTimestamp", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_LAST_HEART_BEAT_TIME] = new StructBase.FieldInfo("lastHeartBeatTime", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+    offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+    fieldsArray[FIELD_INDEX_IF_TRUNK_FILE] = new StructBase.FieldInfo("ifTrunkServer", offset, 1);
+    offset += 1;
+
+    fieldsTotalSize = offset;
+  }
+
+  protected byte status;
+  protected String id;
+  protected String ipAddr;
+  protected String srcIpAddr;
+  protected String domainName; //http domain name
+  protected String version;
+  protected long totalMB; //total disk storage in MB
+  protected long freeMB;  //free disk storage in MB
+  protected int uploadPriority;  //upload priority
+  protected Date joinTime; //storage join timestamp (create timestamp)
+  protected Date upTime;   //storage service started timestamp
+  protected int storePathCount;  //store base path count of each storage server
+  protected int subdirCountPerPath;
+  protected int storagePort;
+  protected int storageHttpPort; //storage http server port
+  protected int currentWritePath; //current write path index
+  protected int connectionAllocCount;
+  protected int connectionCurrentCount;
+  protected int connectionMaxCount;
+  protected long totalUploadCount;
+  protected long successUploadCount;
+  protected long totalAppendCount;
+  protected long successAppendCount;
+  protected long totalModifyCount;
+  protected long successModifyCount;
+  protected long totalTruncateCount;
+  protected long successTruncateCount;
+  protected long totalSetMetaCount;
+  protected long successSetMetaCount;
+  protected long totalDeleteCount;
+  protected long successDeleteCount;
+  protected long totalDownloadCount;
+  protected long successDownloadCount;
+  protected long totalGetMetaCount;
+  protected long successGetMetaCount;
+  protected long totalCreateLinkCount;
+  protected long successCreateLinkCount;
+  protected long totalDeleteLinkCount;
+  protected long successDeleteLinkCount;
+  protected long totalUploadBytes;
+  protected long successUploadBytes;
+  protected long totalAppendBytes;
+  protected long successAppendBytes;
+  protected long totalModifyBytes;
+  protected long successModifyBytes;
+  protected long totalDownloadloadBytes;
+  protected long successDownloadloadBytes;
+  protected long totalSyncInBytes;
+  protected long successSyncInBytes;
+  protected long totalSyncOutBytes;
+  protected long successSyncOutBytes;
+  protected long totalFileOpenCount;
+  protected long successFileOpenCount;
+  protected long totalFileReadCount;
+  protected long successFileReadCount;
+  protected long totalFileWriteCount;
+  protected long successFileWriteCount;
+  protected Date lastSourceUpdate;
+  protected Date lastSyncUpdate;
+  protected Date lastSyncedTimestamp;
+  protected Date lastHeartBeatTime;
+  protected boolean ifTrunkServer;
+
+  /**
+   * get fields total size
+   *
+   * @return fields total size
+   */
+  public static int getFieldsTotalSize() {
+    return fieldsTotalSize;
+  }
+
+  /**
+   * get storage status
+   *
+   * @return storage status
+   */
+  public byte getStatus() {
+    return this.status;
+  }
+
+  /**
+   * get storage server id
+   *
+   * @return storage server id
+   */
+  public String getId() {
+    return this.id;
+  }
+
+  /**
+   * get storage server ip address
+   *
+   * @return storage server ip address
+   */
+  public String getIpAddr() {
+    return this.ipAddr;
+  }
+
+  /**
+   * get source storage ip address
+   *
+   * @return source storage ip address
+   */
+  public String getSrcIpAddr() {
+    return this.srcIpAddr;
+  }
+
+  /**
+   * get the domain name of the storage server
+   *
+   * @return the domain name of the storage server
+   */
+  public String getDomainName() {
+    return this.domainName;
+  }
+
+  /**
+   * get storage version
+   *
+   * @return storage version
+   */
+  public String getVersion() {
+    return this.version;
+  }
+
+  /**
+   * get total disk space in MB
+   *
+   * @return total disk space in MB
+   */
+  public long getTotalMB() {
+    return this.totalMB;
+  }
+
+  /**
+   * get free disk space in MB
+   *
+   * @return free disk space in MB
+   */
+  public long getFreeMB() {
+    return this.freeMB;
+  }
+
+  /**
+   * get storage server upload priority
+   *
+   * @return storage server upload priority
+   */
+  public int getUploadPriority() {
+    return this.uploadPriority;
+  }
+
+  /**
+   * get storage server join time
+   *
+   * @return storage server join time
+   */
+  public Date getJoinTime() {
+    return this.joinTime;
+  }
+
+  /**
+   * get storage server up time
+   *
+   * @return storage server up time
+   */
+  public Date getUpTime() {
+    return this.upTime;
+  }
+
+  /**
+   * get store base path count of each storage server
+   *
+   * @return store base path count of each storage server
+   */
+  public int getStorePathCount() {
+    return this.storePathCount;
+  }
+
+  /**
+   * get sub dir count per store path
+   *
+   * @return sub dir count per store path
+   */
+  public int getSubdirCountPerPath() {
+    return this.subdirCountPerPath;
+  }
+
+  /**
+   * get storage server port
+   *
+   * @return storage server port
+   */
+  public int getStoragePort() {
+    return this.storagePort;
+  }
+
+  /**
+   * get storage server HTTP port
+   *
+   * @return storage server HTTP port
+   */
+  public int getStorageHttpPort() {
+    return this.storageHttpPort;
+  }
+
+  /**
+   * get current write path index
+   *
+   * @return current write path index
+   */
+  public int getCurrentWritePath() {
+    return this.currentWritePath;
+  }
+
+  /**
+   * get total upload file count
+   *
+   * @return total upload file count
+   */
+  public long getTotalUploadCount() {
+    return this.totalUploadCount;
+  }
+
+  /**
+   * get success upload file count
+   *
+   * @return success upload file count
+   */
+  public long getSuccessUploadCount() {
+    return this.successUploadCount;
+  }
+
+  /**
+   * get total append count
+   *
+   * @return total append count
+   */
+  public long getTotalAppendCount() {
+    return this.totalAppendCount;
+  }
+
+  /**
+   * get success append count
+   *
+   * @return success append count
+   */
+  public long getSuccessAppendCount() {
+    return this.successAppendCount;
+  }
+
+  /**
+   * get total modify count
+   *
+   * @return total modify count
+   */
+  public long getTotalModifyCount() {
+    return this.totalModifyCount;
+  }
+
+  /**
+   * get success modify count
+   *
+   * @return success modify count
+   */
+  public long getSuccessModifyCount() {
+    return this.successModifyCount;
+  }
+
+  /**
+   * get total truncate count
+   *
+   * @return total truncate count
+   */
+  public long getTotalTruncateCount() {
+    return this.totalTruncateCount;
+  }
+
+  /**
+   * get success truncate count
+   *
+   * @return success truncate count
+   */
+  public long getSuccessTruncateCount() {
+    return this.successTruncateCount;
+  }
+
+  /**
+   * get total set meta data count
+   *
+   * @return total set meta data count
+   */
+  public long getTotalSetMetaCount() {
+    return this.totalSetMetaCount;
+  }
+
+  /**
+   * get success set meta data count
+   *
+   * @return success set meta data count
+   */
+  public long getSuccessSetMetaCount() {
+    return this.successSetMetaCount;
+  }
+
+  /**
+   * get total delete file count
+   *
+   * @return total delete file count
+   */
+  public long getTotalDeleteCount() {
+    return this.totalDeleteCount;
+  }
+
+  /**
+   * get success delete file count
+   *
+   * @return success delete file count
+   */
+  public long getSuccessDeleteCount() {
+    return this.successDeleteCount;
+  }
+
+  /**
+   * get total download file count
+   *
+   * @return total download file count
+   */
+  public long getTotalDownloadCount() {
+    return this.totalDownloadCount;
+  }
+
+  /**
+   * get success download file count
+   *
+   * @return success download file count
+   */
+  public long getSuccessDownloadCount() {
+    return this.successDownloadCount;
+  }
+
+  /**
+   * get total get metadata count
+   *
+   * @return total get metadata count
+   */
+  public long getTotalGetMetaCount() {
+    return this.totalGetMetaCount;
+  }
+
+  /**
+   * get success get metadata count
+   *
+   * @return success get metadata count
+   */
+  public long getSuccessGetMetaCount() {
+    return this.successGetMetaCount;
+  }
+
+  /**
+   * get total create linke count
+   *
+   * @return total create linke count
+   */
+  public long getTotalCreateLinkCount() {
+    return this.totalCreateLinkCount;
+  }
+
+  /**
+   * get success create linke count
+   *
+   * @return success create linke count
+   */
+  public long getSuccessCreateLinkCount() {
+    return this.successCreateLinkCount;
+  }
+
+  /**
+   * get total delete link count
+   *
+   * @return total delete link count
+   */
+  public long getTotalDeleteLinkCount() {
+    return this.totalDeleteLinkCount;
+  }
+
+  /**
+   * get success delete link count
+   *
+   * @return success delete link count
+   */
+  public long getSuccessDeleteLinkCount() {
+    return this.successDeleteLinkCount;
+  }
+
+  /**
+   * get total upload file bytes
+   *
+   * @return total upload file bytes
+   */
+  public long getTotalUploadBytes() {
+    return this.totalUploadBytes;
+  }
+
+  /**
+   * get success upload file bytes
+   *
+   * @return success upload file bytes
+   */
+  public long getSuccessUploadBytes() {
+    return this.successUploadBytes;
+  }
+
+  /**
+   * get total append bytes
+   *
+   * @return total append bytes
+   */
+  public long getTotalAppendBytes() {
+    return this.totalAppendBytes;
+  }
+
+  /**
+   * get success append bytes
+   *
+   * @return success append bytes
+   */
+  public long getSuccessAppendBytes() {
+    return this.successAppendBytes;
+  }
+
+  /**
+   * get total modify bytes
+   *
+   * @return total modify bytes
+   */
+  public long getTotalModifyBytes() {
+    return this.totalModifyBytes;
+  }
+
+  /**
+   * get success modify bytes
+   *
+   * @return success modify bytes
+   */
+  public long getSuccessModifyBytes() {
+    return this.successModifyBytes;
+  }
+
+  /**
+   * get total download file bytes
+   *
+   * @return total download file bytes
+   */
+  public long getTotalDownloadloadBytes() {
+    return this.totalDownloadloadBytes;
+  }
+
+  /**
+   * get success download file bytes
+   *
+   * @return success download file bytes
+   */
+  public long getSuccessDownloadloadBytes() {
+    return this.successDownloadloadBytes;
+  }
+
+  /**
+   * get total sync in bytes
+   *
+   * @return total sync in bytes
+   */
+  public long getTotalSyncInBytes() {
+    return this.totalSyncInBytes;
+  }
+
+  /**
+   * get success sync in bytes
+   *
+   * @return success sync in bytes
+   */
+  public long getSuccessSyncInBytes() {
+    return this.successSyncInBytes;
+  }
+
+  /**
+   * get total sync out bytes
+   *
+   * @return total sync out bytes
+   */
+  public long getTotalSyncOutBytes() {
+    return this.totalSyncOutBytes;
+  }
+
+  /**
+   * get success sync out bytes
+   *
+   * @return success sync out bytes
+   */
+  public long getSuccessSyncOutBytes() {
+    return this.successSyncOutBytes;
+  }
+
+  /**
+   * get total file opened count
+   *
+   * @return total file opened bytes
+   */
+  public long getTotalFileOpenCount() {
+    return this.totalFileOpenCount;
+  }
+
+  /**
+   * get success file opened count
+   *
+   * @return success file opened count
+   */
+  public long getSuccessFileOpenCount() {
+    return this.successFileOpenCount;
+  }
+
+  /**
+   * get total file read count
+   *
+   * @return total file read bytes
+   */
+  public long getTotalFileReadCount() {
+    return this.totalFileReadCount;
+  }
+
+  /**
+   * get success file read count
+   *
+   * @return success file read count
+   */
+  public long getSuccessFileReadCount() {
+    return this.successFileReadCount;
+  }
+
+  /**
+   * get total file write count
+   *
+   * @return total file write bytes
+   */
+  public long getTotalFileWriteCount() {
+    return this.totalFileWriteCount;
+  }
+
+  /**
+   * get success file write count
+   *
+   * @return success file write count
+   */
+  public long getSuccessFileWriteCount() {
+    return this.successFileWriteCount;
+  }
+
+  /**
+   * get last source update timestamp
+   *
+   * @return last source update timestamp
+   */
+  public Date getLastSourceUpdate() {
+    return this.lastSourceUpdate;
+  }
+
+  /**
+   * get last synced update timestamp
+   *
+   * @return last synced update timestamp
+   */
+  public Date getLastSyncUpdate() {
+    return this.lastSyncUpdate;
+  }
+
+  /**
+   * get last synced timestamp
+   *
+   * @return last synced timestamp
+   */
+  public Date getLastSyncedTimestamp() {
+    return this.lastSyncedTimestamp;
+  }
+
+  /**
+   * get last heart beat timestamp
+   *
+   * @return last heart beat timestamp
+   */
+  public Date getLastHeartBeatTime() {
+    return this.lastHeartBeatTime;
+  }
+
+  /**
+   * if the trunk server
+   *
+   * @return true for the trunk server, otherwise false
+   */
+  public boolean isTrunkServer() {
+    return this.ifTrunkServer;
+  }
+
+  /**
+   * get connection alloc count
+   *
+   * @return connection alloc count
+   */
+  public int getConnectionAllocCount() {
+    return this.connectionAllocCount;
+  }
+
+  /**
+   * get connection current count
+   *
+   * @return connection current count
+   */
+  public int getConnectionCurrentCount() {
+    return this.connectionCurrentCount;
+  }
+
+  /**
+   * get connection max count
+   *
+   * @return connection max count
+   */
+  public int getConnectionMaxCount() {
+    return this.connectionMaxCount;
+  }
+
+  /**
+   * set fields
+   *
+   * @param bs     byte array
+   * @param offset start offset
+   */
+  public void setFields(byte[] bs, int offset) {
+    this.status = byteValue(bs, offset, fieldsArray[FIELD_INDEX_STATUS]);
+    this.id = stringValue(bs, offset, fieldsArray[FIELD_INDEX_ID]);
+    this.ipAddr = stringValue(bs, offset, fieldsArray[FIELD_INDEX_IP_ADDR]);
+    this.srcIpAddr = stringValue(bs, offset, fieldsArray[FIELD_INDEX_SRC_IP_ADDR]);
+    this.domainName = stringValue(bs, offset, fieldsArray[FIELD_INDEX_DOMAIN_NAME]);
+    this.version = stringValue(bs, offset, fieldsArray[FIELD_INDEX_VERSION]);
+    this.totalMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_MB]);
+    this.freeMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_FREE_MB]);
+    this.uploadPriority = intValue(bs, offset, fieldsArray[FIELD_INDEX_UPLOAD_PRIORITY]);
+    this.joinTime = dateValue(bs, offset, fieldsArray[FIELD_INDEX_JOIN_TIME]);
+    this.upTime = dateValue(bs, offset, fieldsArray[FIELD_INDEX_UP_TIME]);
+    this.storePathCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORE_PATH_COUNT]);
+    this.subdirCountPerPath = intValue(bs, offset, fieldsArray[FIELD_INDEX_SUBDIR_COUNT_PER_PATH]);
+    this.storagePort = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_PORT]);
+    this.storageHttpPort = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_HTTP_PORT]);
+    this.currentWritePath = intValue(bs, offset, fieldsArray[FIELD_INDEX_CURRENT_WRITE_PATH]);
+
+    this.connectionAllocCount = int32Value(bs, offset, fieldsArray[FIELD_INDEX_CONNECTION_ALLOC_COUNT]);
+    this.connectionCurrentCount = int32Value(bs, offset, fieldsArray[FIELD_INDEX_CONNECTION_CURRENT_COUNT]);
+    this.connectionMaxCount = int32Value(bs, offset, fieldsArray[FIELD_INDEX_CONNECTION_MAX_COUNT]);
+
+    this.totalUploadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_UPLOAD_COUNT]);
+    this.successUploadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_UPLOAD_COUNT]);
+    this.totalAppendCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_APPEND_COUNT]);
+    this.successAppendCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_APPEND_COUNT]);
+    this.totalModifyCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_MODIFY_COUNT]);
+    this.successModifyCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_MODIFY_COUNT]);
+    this.totalTruncateCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_TRUNCATE_COUNT]);
+    this.successTruncateCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_TRUNCATE_COUNT]);
+    this.totalSetMetaCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_SET_META_COUNT]);
+    this.successSetMetaCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_SET_META_COUNT]);
+    this.totalDeleteCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_DELETE_COUNT]);
+    this.successDeleteCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_DELETE_COUNT]);
+    this.totalDownloadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_DOWNLOAD_COUNT]);
+    this.successDownloadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_DOWNLOAD_COUNT]);
+    this.totalGetMetaCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_GET_META_COUNT]);
+    this.successGetMetaCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_GET_META_COUNT]);
+    this.totalCreateLinkCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_CREATE_LINK_COUNT]);
+    this.successCreateLinkCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_CREATE_LINK_COUNT]);
+    this.totalDeleteLinkCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_DELETE_LINK_COUNT]);
+    this.successDeleteLinkCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_DELETE_LINK_COUNT]);
+    this.totalUploadBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_UPLOAD_BYTES]);
+    this.successUploadBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_UPLOAD_BYTES]);
+    this.totalAppendBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_APPEND_BYTES]);
+    this.successAppendBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_APPEND_BYTES]);
+    this.totalModifyBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_MODIFY_BYTES]);
+    this.successModifyBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_MODIFY_BYTES]);
+    this.totalDownloadloadBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_DOWNLOAD_BYTES]);
+    this.successDownloadloadBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_DOWNLOAD_BYTES]);
+    this.totalSyncInBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_SYNC_IN_BYTES]);
+    this.successSyncInBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_SYNC_IN_BYTES]);
+    this.totalSyncOutBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_SYNC_OUT_BYTES]);
+    this.successSyncOutBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_SYNC_OUT_BYTES]);
+    this.totalFileOpenCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_FILE_OPEN_COUNT]);
+    this.successFileOpenCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_FILE_OPEN_COUNT]);
+    this.totalFileReadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_FILE_READ_COUNT]);
+    this.successFileReadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_FILE_READ_COUNT]);
+    this.totalFileWriteCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_FILE_WRITE_COUNT]);
+    this.successFileWriteCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_FILE_WRITE_COUNT]);
+    this.lastSourceUpdate = dateValue(bs, offset, fieldsArray[FIELD_INDEX_LAST_SOURCE_UPDATE]);
+    this.lastSyncUpdate = dateValue(bs, offset, fieldsArray[FIELD_INDEX_LAST_SYNC_UPDATE]);
+    this.lastSyncedTimestamp = dateValue(bs, offset, fieldsArray[FIELD_INDEX_LAST_SYNCED_TIMESTAMP]);
+    this.lastHeartBeatTime = dateValue(bs, offset, fieldsArray[FIELD_INDEX_LAST_HEART_BEAT_TIME]);
+    this.ifTrunkServer = booleanValue(bs, offset, fieldsArray[FIELD_INDEX_IF_TRUNK_FILE]);
+  }
+}

+ 811 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/TrackerClient.java

@@ -0,0 +1,811 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ * <p>
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package com.kmall.common.fileserver.fastdfs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.Arrays;
+
+/**
+ * Tracker client
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.19
+ */
+public class TrackerClient {
+  protected TrackerGroup tracker_group;
+  protected byte errno;
+
+  /**
+   * constructor with global tracker group
+   */
+  public TrackerClient() {
+    this.tracker_group = ClientGlobal.g_tracker_group;
+  }
+
+  /**
+   * constructor with specified tracker group
+   *
+   * @param tracker_group the tracker group object
+   */
+  public TrackerClient(TrackerGroup tracker_group) {
+    this.tracker_group = tracker_group;
+  }
+
+  /**
+   * get the error code of last call
+   *
+   * @return the error code of last call
+   */
+  public byte getErrorCode() {
+    return this.errno;
+  }
+
+  /**
+   * get a connection to tracker server
+   *
+   * @return tracker server Socket object, return null if fail
+   */
+  public TrackerServer getConnection() throws IOException {
+    return this.tracker_group.getConnection();
+  }
+
+  /**
+   * query storage server to upload file
+   *
+   * @param trackerServer the tracker server
+   * @return storage server Socket object, return null if fail
+   */
+  public StorageServer getStoreStorage(TrackerServer trackerServer) throws IOException {
+    final String groupName = null;
+    return this.getStoreStorage(trackerServer, groupName);
+  }
+
+  /**
+   * query storage server to upload file
+   *
+   * @param trackerServer the tracker server
+   * @param groupName     the group name to upload file to, can be empty
+   * @return storage server object, return null if fail
+   */
+  public StorageServer getStoreStorage(TrackerServer trackerServer, String groupName) throws IOException {
+    byte[] header;
+    String ip_addr;
+    int port;
+    byte cmd;
+    int out_len;
+    boolean bNewConnection;
+    byte store_path;
+    Socket trackerSocket;
+
+    if (trackerServer == null) {
+      trackerServer = getConnection();
+      if (trackerServer == null) {
+        return null;
+      }
+      bNewConnection = true;
+    } else {
+      bNewConnection = false;
+    }
+
+    trackerSocket = trackerServer.getSocket();
+    OutputStream out = trackerSocket.getOutputStream();
+
+    try {
+      if (groupName == null || groupName.length() == 0) {
+        cmd = ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE;
+        out_len = 0;
+      } else {
+        cmd = ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ONE;
+        out_len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN;
+      }
+      header = ProtoCommon.packHeader(cmd, out_len, (byte) 0);
+      out.write(header);
+
+      if (groupName != null && groupName.length() > 0) {
+        byte[] bGroupName;
+        byte[] bs;
+        int group_len;
+
+        bs = groupName.getBytes(ClientGlobal.g_charset);
+        bGroupName = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN];
+
+        if (bs.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) {
+          group_len = bs.length;
+        } else {
+          group_len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN;
+        }
+        Arrays.fill(bGroupName, (byte) 0);
+        System.arraycopy(bs, 0, bGroupName, 0, group_len);
+        out.write(bGroupName);
+      }
+
+      ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(trackerSocket.getInputStream(),
+        ProtoCommon.TRACKER_PROTO_CMD_RESP,
+        ProtoCommon.TRACKER_QUERY_STORAGE_STORE_BODY_LEN);
+      this.errno = pkgInfo.errno;
+      if (pkgInfo.errno != 0) {
+        return null;
+      }
+
+      ip_addr = new String(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN, ProtoCommon.FDFS_IPADDR_SIZE - 1).trim();
+
+      port = (int) ProtoCommon.buff2long(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN
+        + ProtoCommon.FDFS_IPADDR_SIZE - 1);
+      store_path = pkgInfo.body[ProtoCommon.TRACKER_QUERY_STORAGE_STORE_BODY_LEN - 1];
+
+      return new StorageServer(ip_addr, port, store_path);
+    } catch (IOException ex) {
+      if (!bNewConnection) {
+        try {
+          trackerServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        }
+      }
+
+      throw ex;
+    } finally {
+      if (bNewConnection) {
+        try {
+          trackerServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        }
+      }
+    }
+  }
+
+  /**
+   * query storage servers to upload file
+   *
+   * @param trackerServer the tracker server
+   * @param groupName     the group name to upload file to, can be empty
+   * @return storage servers, return null if fail
+   */
+  public StorageServer[] getStoreStorages(TrackerServer trackerServer, String groupName) throws IOException {
+    byte[] header;
+    String ip_addr;
+    int port;
+    byte cmd;
+    int out_len;
+    boolean bNewConnection;
+    Socket trackerSocket;
+
+    if (trackerServer == null) {
+      trackerServer = getConnection();
+      if (trackerServer == null) {
+        return null;
+      }
+      bNewConnection = true;
+    } else {
+      bNewConnection = false;
+    }
+
+    trackerSocket = trackerServer.getSocket();
+    OutputStream out = trackerSocket.getOutputStream();
+
+    try {
+      if (groupName == null || groupName.length() == 0) {
+        cmd = ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ALL;
+        out_len = 0;
+      } else {
+        cmd = ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ALL;
+        out_len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN;
+      }
+      header = ProtoCommon.packHeader(cmd, out_len, (byte) 0);
+      out.write(header);
+
+      if (groupName != null && groupName.length() > 0) {
+        byte[] bGroupName;
+        byte[] bs;
+        int group_len;
+
+        bs = groupName.getBytes(ClientGlobal.g_charset);
+        bGroupName = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN];
+
+        if (bs.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) {
+          group_len = bs.length;
+        } else {
+          group_len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN;
+        }
+        Arrays.fill(bGroupName, (byte) 0);
+        System.arraycopy(bs, 0, bGroupName, 0, group_len);
+        out.write(bGroupName);
+      }
+
+      ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(trackerSocket.getInputStream(),
+        ProtoCommon.TRACKER_PROTO_CMD_RESP, -1);
+      this.errno = pkgInfo.errno;
+      if (pkgInfo.errno != 0) {
+        return null;
+      }
+
+      if (pkgInfo.body.length < ProtoCommon.TRACKER_QUERY_STORAGE_STORE_BODY_LEN) {
+        this.errno = ProtoCommon.ERR_NO_EINVAL;
+        return null;
+      }
+
+      int ipPortLen = pkgInfo.body.length - (ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + 1);
+      final int recordLength = ProtoCommon.FDFS_IPADDR_SIZE - 1 + ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+      if (ipPortLen % recordLength != 0) {
+        this.errno = ProtoCommon.ERR_NO_EINVAL;
+        return null;
+      }
+
+      int serverCount = ipPortLen / recordLength;
+      if (serverCount > 16) {
+        this.errno = ProtoCommon.ERR_NO_ENOSPC;
+        return null;
+      }
+
+      StorageServer[] results = new StorageServer[serverCount];
+      byte store_path = pkgInfo.body[pkgInfo.body.length - 1];
+      int offset = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN;
+
+      for (int i = 0; i < serverCount; i++) {
+        ip_addr = new String(pkgInfo.body, offset, ProtoCommon.FDFS_IPADDR_SIZE - 1).trim();
+        offset += ProtoCommon.FDFS_IPADDR_SIZE - 1;
+
+        port = (int) ProtoCommon.buff2long(pkgInfo.body, offset);
+        offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+        results[i] = new StorageServer(ip_addr, port, store_path);
+      }
+
+      return results;
+    } catch (IOException ex) {
+      if (!bNewConnection) {
+        try {
+          trackerServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        }
+      }
+
+      throw ex;
+    } finally {
+      if (bNewConnection) {
+        try {
+          trackerServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        }
+      }
+    }
+  }
+
+  /**
+   * query storage server to download file
+   *
+   * @param trackerServer the tracker server
+   * @param groupName     the group name of storage server
+   * @param filename      filename on storage server
+   * @return storage server Socket object, return null if fail
+   */
+  public StorageServer getFetchStorage(TrackerServer trackerServer,
+                                       String groupName, String filename) throws IOException {
+    ServerInfo[] servers = this.getStorages(trackerServer, ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE,
+      groupName, filename);
+    if (servers == null) {
+      return null;
+    } else {
+      return new StorageServer(servers[0].getIpAddr(), servers[0].getPort(), 0);
+    }
+  }
+
+  /**
+   * query storage server to update file (delete file or set meta data)
+   *
+   * @param trackerServer the tracker server
+   * @param groupName     the group name of storage server
+   * @param filename      filename on storage server
+   * @return storage server Socket object, return null if fail
+   */
+  public StorageServer getUpdateStorage(TrackerServer trackerServer,
+                                        String groupName, String filename) throws IOException {
+    ServerInfo[] servers = this.getStorages(trackerServer, ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE,
+      groupName, filename);
+    if (servers == null) {
+      return null;
+    } else {
+      return new StorageServer(servers[0].getIpAddr(), servers[0].getPort(), 0);
+    }
+  }
+
+  /**
+   * get storage servers to download file
+   *
+   * @param trackerServer the tracker server
+   * @param groupName     the group name of storage server
+   * @param filename      filename on storage server
+   * @return storage servers, return null if fail
+   */
+  public ServerInfo[] getFetchStorages(TrackerServer trackerServer,
+                                       String groupName, String filename) throws IOException {
+    return this.getStorages(trackerServer, ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ALL,
+      groupName, filename);
+  }
+
+  /**
+   * query storage server to download file
+   *
+   * @param trackerServer the tracker server
+   * @param cmd           command code, ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE or
+   *                      ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE
+   * @param groupName     the group name of storage server
+   * @param filename      filename on storage server
+   * @return storage server Socket object, return null if fail
+   */
+  protected ServerInfo[] getStorages(TrackerServer trackerServer,
+                                     byte cmd, String groupName, String filename) throws IOException {
+    byte[] header;
+    byte[] bFileName;
+    byte[] bGroupName;
+    byte[] bs;
+    int len;
+    String ip_addr;
+    int port;
+    boolean bNewConnection;
+    Socket trackerSocket;
+
+    if (trackerServer == null) {
+      trackerServer = getConnection();
+      if (trackerServer == null) {
+        return null;
+      }
+      bNewConnection = true;
+    } else {
+      bNewConnection = false;
+    }
+    trackerSocket = trackerServer.getSocket();
+    OutputStream out = trackerSocket.getOutputStream();
+
+    try {
+      bs = groupName.getBytes(ClientGlobal.g_charset);
+      bGroupName = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN];
+      bFileName = filename.getBytes(ClientGlobal.g_charset);
+
+      if (bs.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) {
+        len = bs.length;
+      } else {
+        len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN;
+      }
+      Arrays.fill(bGroupName, (byte) 0);
+      System.arraycopy(bs, 0, bGroupName, 0, len);
+
+      header = ProtoCommon.packHeader(cmd, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + bFileName.length, (byte) 0);
+      byte[] wholePkg = new byte[header.length + bGroupName.length + bFileName.length];
+      System.arraycopy(header, 0, wholePkg, 0, header.length);
+      System.arraycopy(bGroupName, 0, wholePkg, header.length, bGroupName.length);
+      System.arraycopy(bFileName, 0, wholePkg, header.length + bGroupName.length, bFileName.length);
+      out.write(wholePkg);
+
+      ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(trackerSocket.getInputStream(),
+        ProtoCommon.TRACKER_PROTO_CMD_RESP, -1);
+      this.errno = pkgInfo.errno;
+      if (pkgInfo.errno != 0) {
+        return null;
+      }
+
+      if (pkgInfo.body.length < ProtoCommon.TRACKER_QUERY_STORAGE_FETCH_BODY_LEN) {
+        throw new IOException("Invalid body length: " + pkgInfo.body.length);
+      }
+
+      if ((pkgInfo.body.length - ProtoCommon.TRACKER_QUERY_STORAGE_FETCH_BODY_LEN) % (ProtoCommon.FDFS_IPADDR_SIZE - 1) != 0) {
+        throw new IOException("Invalid body length: " + pkgInfo.body.length);
+      }
+
+      int server_count = 1 + (pkgInfo.body.length - ProtoCommon.TRACKER_QUERY_STORAGE_FETCH_BODY_LEN) / (ProtoCommon.FDFS_IPADDR_SIZE - 1);
+
+      ip_addr = new String(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN, ProtoCommon.FDFS_IPADDR_SIZE - 1).trim();
+      int offset = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + ProtoCommon.FDFS_IPADDR_SIZE - 1;
+
+      port = (int) ProtoCommon.buff2long(pkgInfo.body, offset);
+      offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+      ServerInfo[] servers = new ServerInfo[server_count];
+      servers[0] = new ServerInfo(ip_addr, port);
+      for (int i = 1; i < server_count; i++) {
+        servers[i] = new ServerInfo(new String(pkgInfo.body, offset, ProtoCommon.FDFS_IPADDR_SIZE - 1).trim(), port);
+        offset += ProtoCommon.FDFS_IPADDR_SIZE - 1;
+      }
+
+      return servers;
+    } catch (IOException ex) {
+      if (!bNewConnection) {
+        try {
+          trackerServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        }
+      }
+
+      throw ex;
+    } finally {
+      if (bNewConnection) {
+        try {
+          trackerServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        }
+      }
+    }
+  }
+
+  /**
+   * query storage server to download file
+   *
+   * @param trackerServer the tracker server
+   * @param file_id       the file id(including group name and filename)
+   * @return storage server Socket object, return null if fail
+   */
+  public StorageServer getFetchStorage1(TrackerServer trackerServer, String file_id) throws IOException {
+    String[] parts = new String[2];
+    this.errno = StorageClient1.split_file_id(file_id, parts);
+    if (this.errno != 0) {
+      return null;
+    }
+
+    return this.getFetchStorage(trackerServer, parts[0], parts[1]);
+  }
+
+  /**
+   * get storage servers to download file
+   *
+   * @param trackerServer the tracker server
+   * @param file_id       the file id(including group name and filename)
+   * @return storage servers, return null if fail
+   */
+  public ServerInfo[] getFetchStorages1(TrackerServer trackerServer, String file_id) throws IOException {
+    String[] parts = new String[2];
+    this.errno = StorageClient1.split_file_id(file_id, parts);
+    if (this.errno != 0) {
+      return null;
+    }
+
+    return this.getFetchStorages(trackerServer, parts[0], parts[1]);
+  }
+
+  /**
+   * list groups
+   *
+   * @param trackerServer the tracker server
+   * @return group stat array, return null if fail
+   */
+  public StructGroupStat[] listGroups(TrackerServer trackerServer) throws IOException {
+    byte[] header;
+    String ip_addr;
+    int port;
+    byte cmd;
+    int out_len;
+    boolean bNewConnection;
+    byte store_path;
+    Socket trackerSocket;
+
+    if (trackerServer == null) {
+      trackerServer = getConnection();
+      if (trackerServer == null) {
+        return null;
+      }
+      bNewConnection = true;
+    } else {
+      bNewConnection = false;
+    }
+
+    trackerSocket = trackerServer.getSocket();
+    OutputStream out = trackerSocket.getOutputStream();
+
+    try {
+      header = ProtoCommon.packHeader(ProtoCommon.TRACKER_PROTO_CMD_SERVER_LIST_GROUP, 0, (byte) 0);
+      out.write(header);
+
+      ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(trackerSocket.getInputStream(),
+        ProtoCommon.TRACKER_PROTO_CMD_RESP, -1);
+      this.errno = pkgInfo.errno;
+      if (pkgInfo.errno != 0) {
+        return null;
+      }
+
+      ProtoStructDecoder<StructGroupStat> decoder = new ProtoStructDecoder<StructGroupStat>();
+      return decoder.decode(pkgInfo.body, StructGroupStat.class, StructGroupStat.getFieldsTotalSize());
+    } catch (IOException ex) {
+      if (!bNewConnection) {
+        try {
+          trackerServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        }
+      }
+
+      throw ex;
+    } catch (Exception ex) {
+      ex.printStackTrace();
+      this.errno = ProtoCommon.ERR_NO_EINVAL;
+      return null;
+    } finally {
+      if (bNewConnection) {
+        try {
+          trackerServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        }
+      }
+    }
+  }
+
+  /**
+   * query storage server stat info of the group
+   *
+   * @param trackerServer the tracker server
+   * @param groupName     the group name of storage server
+   * @return storage server stat array, return null if fail
+   */
+  public StructStorageStat[] listStorages(TrackerServer trackerServer, String groupName) throws IOException {
+    final String storageIpAddr = null;
+    return this.listStorages(trackerServer, groupName, storageIpAddr);
+  }
+
+  /**
+   * query storage server stat info of the group
+   *
+   * @param trackerServer the tracker server
+   * @param groupName     the group name of storage server
+   * @param storageIpAddr the storage server ip address, can be null or empty
+   * @return storage server stat array, return null if fail
+   */
+  public StructStorageStat[] listStorages(TrackerServer trackerServer,
+                                          String groupName, String storageIpAddr) throws IOException {
+    byte[] header;
+    byte[] bGroupName;
+    byte[] bs;
+    int len;
+    boolean bNewConnection;
+    Socket trackerSocket;
+
+    if (trackerServer == null) {
+      trackerServer = getConnection();
+      if (trackerServer == null) {
+        return null;
+      }
+      bNewConnection = true;
+    } else {
+      bNewConnection = false;
+    }
+    trackerSocket = trackerServer.getSocket();
+    OutputStream out = trackerSocket.getOutputStream();
+
+    try {
+      bs = groupName.getBytes(ClientGlobal.g_charset);
+      bGroupName = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN];
+
+      if (bs.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) {
+        len = bs.length;
+      } else {
+        len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN;
+      }
+      Arrays.fill(bGroupName, (byte) 0);
+      System.arraycopy(bs, 0, bGroupName, 0, len);
+
+      int ipAddrLen;
+      byte[] bIpAddr;
+      if (storageIpAddr != null && storageIpAddr.length() > 0) {
+        bIpAddr = storageIpAddr.getBytes(ClientGlobal.g_charset);
+        if (bIpAddr.length < ProtoCommon.FDFS_IPADDR_SIZE) {
+          ipAddrLen = bIpAddr.length;
+        } else {
+          ipAddrLen = ProtoCommon.FDFS_IPADDR_SIZE - 1;
+        }
+      } else {
+        bIpAddr = null;
+        ipAddrLen = 0;
+      }
+
+      header = ProtoCommon.packHeader(ProtoCommon.TRACKER_PROTO_CMD_SERVER_LIST_STORAGE, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + ipAddrLen, (byte) 0);
+      byte[] wholePkg = new byte[header.length + bGroupName.length + ipAddrLen];
+      System.arraycopy(header, 0, wholePkg, 0, header.length);
+      System.arraycopy(bGroupName, 0, wholePkg, header.length, bGroupName.length);
+      if (ipAddrLen > 0) {
+        System.arraycopy(bIpAddr, 0, wholePkg, header.length + bGroupName.length, ipAddrLen);
+      }
+      out.write(wholePkg);
+
+      ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(trackerSocket.getInputStream(),
+        ProtoCommon.TRACKER_PROTO_CMD_RESP, -1);
+      this.errno = pkgInfo.errno;
+      if (pkgInfo.errno != 0) {
+        return null;
+      }
+
+      ProtoStructDecoder<StructStorageStat> decoder = new ProtoStructDecoder<StructStorageStat>();
+      return decoder.decode(pkgInfo.body, StructStorageStat.class, StructStorageStat.getFieldsTotalSize());
+    } catch (IOException ex) {
+      if (!bNewConnection) {
+        try {
+          trackerServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        }
+      }
+
+      throw ex;
+    } catch (Exception ex) {
+      ex.printStackTrace();
+      this.errno = ProtoCommon.ERR_NO_EINVAL;
+      return null;
+    } finally {
+      if (bNewConnection) {
+        try {
+          trackerServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        }
+      }
+    }
+  }
+
+  /**
+   * delete a storage server from the tracker server
+   *
+   * @param trackerServer the connected tracker server
+   * @param groupName     the group name of storage server
+   * @param storageIpAddr the storage server ip address
+   * @return true for success, false for fail
+   */
+  private boolean deleteStorage(TrackerServer trackerServer,
+                                String groupName, String storageIpAddr) throws IOException {
+    byte[] header;
+    byte[] bGroupName;
+    byte[] bs;
+    int len;
+    Socket trackerSocket;
+
+    trackerSocket = trackerServer.getSocket();
+    OutputStream out = trackerSocket.getOutputStream();
+
+    bs = groupName.getBytes(ClientGlobal.g_charset);
+    bGroupName = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN];
+
+    if (bs.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) {
+      len = bs.length;
+    } else {
+      len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN;
+    }
+    Arrays.fill(bGroupName, (byte) 0);
+    System.arraycopy(bs, 0, bGroupName, 0, len);
+
+    int ipAddrLen;
+    byte[] bIpAddr = storageIpAddr.getBytes(ClientGlobal.g_charset);
+    if (bIpAddr.length < ProtoCommon.FDFS_IPADDR_SIZE) {
+      ipAddrLen = bIpAddr.length;
+    } else {
+      ipAddrLen = ProtoCommon.FDFS_IPADDR_SIZE - 1;
+    }
+
+    header = ProtoCommon.packHeader(ProtoCommon.TRACKER_PROTO_CMD_SERVER_DELETE_STORAGE, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + ipAddrLen, (byte) 0);
+    byte[] wholePkg = new byte[header.length + bGroupName.length + ipAddrLen];
+    System.arraycopy(header, 0, wholePkg, 0, header.length);
+    System.arraycopy(bGroupName, 0, wholePkg, header.length, bGroupName.length);
+    System.arraycopy(bIpAddr, 0, wholePkg, header.length + bGroupName.length, ipAddrLen);
+    out.write(wholePkg);
+
+    ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(trackerSocket.getInputStream(),
+      ProtoCommon.TRACKER_PROTO_CMD_RESP, 0);
+    this.errno = pkgInfo.errno;
+    return pkgInfo.errno == 0;
+  }
+
+  /**
+   * delete a storage server from the global FastDFS cluster
+   *
+   * @param groupName     the group name of storage server
+   * @param storageIpAddr the storage server ip address
+   * @return true for success, false for fail
+   */
+  public boolean deleteStorage(String groupName, String storageIpAddr) throws IOException {
+    return this.deleteStorage(ClientGlobal.g_tracker_group, groupName, storageIpAddr);
+  }
+
+  /**
+   * delete a storage server from the FastDFS cluster
+   *
+   * @param trackerGroup  the tracker server group
+   * @param groupName     the group name of storage server
+   * @param storageIpAddr the storage server ip address
+   * @return true for success, false for fail
+   */
+  public boolean deleteStorage(TrackerGroup trackerGroup,
+                               String groupName, String storageIpAddr) throws IOException {
+    int serverIndex;
+    int notFoundCount;
+    TrackerServer trackerServer;
+
+    notFoundCount = 0;
+    for (serverIndex = 0; serverIndex < trackerGroup.tracker_servers.length; serverIndex++) {
+      try {
+        trackerServer = trackerGroup.getConnection(serverIndex);
+      } catch (IOException ex) {
+        ex.printStackTrace(System.err);
+        this.errno = ProtoCommon.ECONNREFUSED;
+        return false;
+      }
+
+      try {
+        StructStorageStat[] storageStats = listStorages(trackerServer, groupName, storageIpAddr);
+        if (storageStats == null) {
+          if (this.errno == ProtoCommon.ERR_NO_ENOENT) {
+            notFoundCount++;
+          } else {
+            return false;
+          }
+        } else if (storageStats.length == 0) {
+          notFoundCount++;
+        } else if (storageStats[0].getStatus() == ProtoCommon.FDFS_STORAGE_STATUS_ONLINE ||
+          storageStats[0].getStatus() == ProtoCommon.FDFS_STORAGE_STATUS_ACTIVE) {
+          this.errno = ProtoCommon.ERR_NO_EBUSY;
+          return false;
+        }
+      } finally {
+        try {
+          trackerServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        }
+      }
+    }
+
+    if (notFoundCount == trackerGroup.tracker_servers.length) {
+      this.errno = ProtoCommon.ERR_NO_ENOENT;
+      return false;
+    }
+
+    notFoundCount = 0;
+    for (serverIndex = 0; serverIndex < trackerGroup.tracker_servers.length; serverIndex++) {
+      try {
+        trackerServer = trackerGroup.getConnection(serverIndex);
+      } catch (IOException ex) {
+        System.err.println("connect to server " + trackerGroup.tracker_servers[serverIndex].getAddress().getHostAddress() + ":" + trackerGroup.tracker_servers[serverIndex].getPort() + " fail");
+        ex.printStackTrace(System.err);
+        this.errno = ProtoCommon.ECONNREFUSED;
+        return false;
+      }
+
+      try {
+        if (!this.deleteStorage(trackerServer, groupName, storageIpAddr)) {
+          if (this.errno != 0) {
+            if (this.errno == ProtoCommon.ERR_NO_ENOENT) {
+              notFoundCount++;
+            } else if (this.errno != ProtoCommon.ERR_NO_EALREADY) {
+              return false;
+            }
+          }
+        }
+      } finally {
+        try {
+          trackerServer.close();
+        } catch (IOException ex1) {
+          ex1.printStackTrace();
+        }
+      }
+    }
+
+    if (notFoundCount == trackerGroup.tracker_servers.length) {
+      this.errno = ProtoCommon.ERR_NO_ENOENT;
+      return false;
+    }
+
+    if (this.errno == ProtoCommon.ERR_NO_ENOENT) {
+      this.errno = 0;
+    }
+
+    return this.errno == 0;
+  }
+}

+ 106 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/TrackerGroup.java

@@ -0,0 +1,106 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ * <p>
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package com.kmall.common.fileserver.fastdfs;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+/**
+ * Tracker server group
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.17
+ */
+public class TrackerGroup {
+  public int tracker_server_index;
+  public InetSocketAddress[] tracker_servers;
+  protected Integer lock;
+
+  /**
+   * Constructor
+   *
+   * @param tracker_servers tracker servers
+   */
+  public TrackerGroup(InetSocketAddress[] tracker_servers) {
+    this.tracker_servers = tracker_servers;
+    this.lock = new Integer(0);
+    this.tracker_server_index = 0;
+  }
+
+  /**
+   * return connected tracker server
+   *
+   * @return connected tracker server, null for fail
+   */
+  public TrackerServer getConnection(int serverIndex) throws IOException {
+    Socket sock = new Socket();
+    sock.setReuseAddress(true);
+    sock.setSoTimeout(ClientGlobal.g_network_timeout);
+    sock.connect(this.tracker_servers[serverIndex], ClientGlobal.g_connect_timeout);
+    return new TrackerServer(sock, this.tracker_servers[serverIndex]);
+  }
+
+  /**
+   * return connected tracker server
+   *
+   * @return connected tracker server, null for fail
+   */
+  public TrackerServer getConnection() throws IOException {
+    int current_index;
+
+    synchronized (this.lock) {
+      this.tracker_server_index++;
+      if (this.tracker_server_index >= this.tracker_servers.length) {
+        this.tracker_server_index = 0;
+      }
+
+      current_index = this.tracker_server_index;
+    }
+
+    try {
+      return this.getConnection(current_index);
+    } catch (IOException ex) {
+      System.err.println("connect to server " + this.tracker_servers[current_index].getAddress().getHostAddress() + ":" + this.tracker_servers[current_index].getPort() + " fail");
+      ex.printStackTrace(System.err);
+    }
+
+    for (int i = 0; i < this.tracker_servers.length; i++) {
+      if (i == current_index) {
+        continue;
+      }
+
+      try {
+        TrackerServer trackerServer = this.getConnection(i);
+
+        synchronized (this.lock) {
+          if (this.tracker_server_index == current_index) {
+            this.tracker_server_index = i;
+          }
+        }
+
+        return trackerServer;
+      } catch (IOException ex) {
+        System.err.println("connect to server " + this.tracker_servers[i].getAddress().getHostAddress() + ":" + this.tracker_servers[i].getPort() + " fail");
+        ex.printStackTrace(System.err);
+      }
+    }
+
+    return null;
+  }
+
+  public Object clone() {
+    InetSocketAddress[] trackerServers = new InetSocketAddress[this.tracker_servers.length];
+    for (int i = 0; i < trackerServers.length; i++) {
+      trackerServers[i] = new InetSocketAddress(this.tracker_servers[i].getAddress().getHostAddress(), this.tracker_servers[i].getPort());
+    }
+
+    return new TrackerGroup(trackerServers);
+  }
+}

+ 81 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/TrackerServer.java

@@ -0,0 +1,81 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ * <p>
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package com.kmall.common.fileserver.fastdfs;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+/**
+ * Tracker Server Info
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.11
+ */
+public class TrackerServer {
+  protected Socket sock;
+  protected InetSocketAddress inetSockAddr;
+
+  /**
+   * Constructor
+   *
+   * @param sock         Socket of server
+   * @param inetSockAddr the server info
+   */
+  public TrackerServer(Socket sock, InetSocketAddress inetSockAddr) {
+    this.sock = sock;
+    this.inetSockAddr = inetSockAddr;
+  }
+
+  /**
+   * get the connected socket
+   *
+   * @return the socket
+   */
+  public Socket getSocket() throws IOException {
+    if (this.sock == null) {
+      this.sock = ClientGlobal.getSocket(this.inetSockAddr);
+    }
+
+    return this.sock;
+  }
+
+  /**
+   * get the server info
+   *
+   * @return the server info
+   */
+  public InetSocketAddress getInetSocketAddress() {
+    return this.inetSockAddr;
+  }
+
+  public OutputStream getOutputStream() throws IOException {
+    return this.sock.getOutputStream();
+  }
+
+  public InputStream getInputStream() throws IOException {
+    return this.sock.getInputStream();
+  }
+
+  public void close() throws IOException {
+    if (this.sock != null) {
+      try {
+        ProtoCommon.closeSocket(this.sock);
+      } finally {
+        this.sock = null;
+      }
+    }
+  }
+
+  protected void finalize() throws Throwable {
+    this.close();
+  }
+}

+ 28 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/UploadCallback.java

@@ -0,0 +1,28 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ * <p>
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package com.kmall.common.fileserver.fastdfs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * upload file callback interface
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.0
+ */
+public interface UploadCallback {
+  /**
+   * send file content callback function, be called only once when the file uploaded
+   *
+   * @param out output stream for writing file content
+   * @return 0 success, return none zero(errno) if fail
+   */
+  public int send(OutputStream out) throws IOException;
+}

+ 55 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/fastdfs/UploadStream.java

@@ -0,0 +1,55 @@
+package com.kmall.common.fileserver.fastdfs;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Upload file by stream
+ *
+ * @author zhouzezhong & Happy Fish / YuQing
+ * @version Version 1.11
+ */
+public class UploadStream implements UploadCallback {
+  private InputStream inputStream; //input stream for reading
+  private long fileSize = 0;  //size of the uploaded file
+
+  /**
+   * constructor
+   *
+   * @param inputStream input stream for uploading
+   * @param fileSize    size of uploaded file
+   */
+  public UploadStream(InputStream inputStream, long fileSize) {
+    super();
+    this.inputStream = inputStream;
+    this.fileSize = fileSize;
+  }
+
+  /**
+   * send file content callback function, be called only once when the file uploaded
+   *
+   * @param out output stream for writing file content
+   * @return 0 success, return none zero(errno) if fail
+   */
+  public int send(OutputStream out) throws IOException {
+    long remainBytes = fileSize;
+    byte[] buff = new byte[256 * 1024];
+    int bytes;
+    while (remainBytes > 0) {
+      try {
+        if ((bytes = inputStream.read(buff, 0, remainBytes > buff.length ? buff.length : (int) remainBytes)) < 0) {
+          return -1;
+        }
+      } catch (IOException ex) {
+        ex.printStackTrace();
+        return -1;
+      }
+
+      out.write(buff, 0, bytes);
+      remainBytes -= bytes;
+    }
+
+    return 0;
+  }
+}

+ 62 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/util/FastDFSFile.java

@@ -0,0 +1,62 @@
+package com.kmall.common.fileserver.util;
+
+public class FastDFSFile {
+
+	private static final long serialVersionUID = 8042057955846275561L;
+	private byte[] content;
+	private String name;
+	private String ext;
+	private String length;
+	// private String author = FILE_DEFAULT_AUTHOR;
+
+	public FastDFSFile(byte[] content, String ext) {
+		this.content = content;
+		this.ext = ext;
+	}
+
+	public FastDFSFile(byte[] content, String name, String ext) {
+		this.content = content;
+		this.name = name;
+		this.ext = ext;
+	}
+
+	public FastDFSFile(byte[] content, String name, String ext, String length, String author) {
+		this.content = content;
+		this.name = name;
+		this.ext = ext;
+		this.length = length;
+	}
+
+	public byte[] getContent() {
+		return content;
+	}
+
+	public void setContent(byte[] content) {
+		this.content = content;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getExt() {
+		return ext;
+	}
+
+	public void setExt(String ext) {
+		this.ext = ext;
+	}
+
+	public String getLength() {
+		return length;
+	}
+
+	public void setLength(String length) {
+		this.length = length;
+	}
+
+}

+ 168 - 0
kmall-common/src/main/java/com/kmall/common/fileserver/util/FileManager.java

@@ -0,0 +1,168 @@
+package com.kmall.common.fileserver.util;
+
+import com.kmall.common.fileserver.common.NameValuePair;
+import com.kmall.common.fileserver.fastdfs.*;
+import org.apache.log4j.Logger;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.UUID;
+
+public class FileManager implements Serializable {
+
+    private static final long serialVersionUID = -9042217554595446459L;
+
+    public static final String PROTOCOL = "http://";
+
+    public static final String SEPARATOR = "/";
+    public static final String COLON = ":";
+
+
+    /**
+     * 配置文件名字
+     */
+    public static final String CLIENT_CONFIG_FILE = "conf/fastdfs.properties";
+
+    private static Logger logger = Logger.getLogger(FileManager.class);
+
+    private static TrackerClient trackerClient;
+    private static TrackerServer trackerServer;
+    private static StorageServer storageServer;
+    private static StorageClient storageClient;
+
+    static {
+        System.out.println("FileManager");
+        try {
+            String fdfsClientConfigFilePath = CLIENT_CONFIG_FILE;
+            ClientGlobal.init(fdfsClientConfigFilePath);
+
+            trackerClient = new TrackerClient();
+            trackerServer = trackerClient.getConnection();
+
+            storageClient = new StorageClient(trackerServer, storageServer);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * <strong>方法概要: 文件上传</strong> <br>
+     * <strong>创建时间: 2016-9-26 上午10:26:11</strong> <br>
+     *
+     * @param FastDFSFile file
+     * @return fileAbsolutePath
+     * @author Wang Liang
+     */
+    public static String upload(MultipartFile attach) {
+        String[] uploadResults = null;
+        String ext = attach.getOriginalFilename().substring(attach.getOriginalFilename().lastIndexOf(".") + 1);
+        try {
+            FastDFSFile file = new FastDFSFile(attach.getBytes(), ext);
+            NameValuePair[] meta_list = new NameValuePair[4];
+            meta_list[0] = new NameValuePair("fileName", attach.getOriginalFilename());
+            meta_list[1] = new NameValuePair("fileLength", String.valueOf(attach.getSize()));
+            meta_list[2] = new NameValuePair("fileExt", ext);
+            meta_list[3] = new NameValuePair("fileAuthor", ClientGlobal.file_author);
+            synchronized (storageClient) {
+                uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);
+            }
+        } catch (Exception e1) {
+            e1.printStackTrace();
+        }
+        String groupName = uploadResults[0];
+        String remoteFileName = uploadResults[1];
+
+        String httpAddr = ClientGlobal.http_tracket_nginx_addr;
+
+        String httpPort = ClientGlobal.http_tracket_server_port;
+
+        String fileAbsolutePath = PROTOCOL + httpAddr + COLON + httpPort + SEPARATOR + groupName + SEPARATOR + remoteFileName;
+        return fileAbsolutePath;
+    }
+
+
+    /**
+     * <strong>方法概要: 文件上传</strong> <br>
+     * <strong>创建时间: 2016-9-26 上午10:26:11</strong> <br>
+     *
+     * @return fileAbsolutePath
+     * @author Wang Liang
+     */
+    public static String upload(String originalFileName, byte[] content, String fileLength) {
+        String[] uploadResults = null;
+        String ext = originalFileName.substring(originalFileName.lastIndexOf(".") + 1);
+        try {
+            FastDFSFile file = new FastDFSFile(content, ext);
+            NameValuePair[] meta_list = new NameValuePair[4];
+            meta_list[0] = new NameValuePair("fileName", originalFileName);
+            meta_list[1] = new NameValuePair("fileLength", fileLength);
+            meta_list[2] = new NameValuePair("fileExt", ext);
+            meta_list[3] = new NameValuePair("fileAuthor", ClientGlobal.file_author);
+            synchronized (storageClient) {
+                uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);
+            }
+        } catch (Exception e1) {
+            e1.printStackTrace();
+        }
+        String groupName = uploadResults[0];
+        String remoteFileName = uploadResults[1];
+
+        String httpAddr = ClientGlobal.http_tracket_nginx_addr;
+
+        String httpPort = ClientGlobal.http_tracket_server_port;
+
+        String fileAbsolutePath = PROTOCOL + httpAddr + COLON + httpPort + SEPARATOR + groupName + SEPARATOR + remoteFileName;
+        return fileAbsolutePath;
+    }
+
+    /**
+     * <strong>方法概要: 文件下载</strong> <br>
+     * <strong>创建时间: 2016-9-26 上午10:28:21</strong> <br>
+     *
+     * @param String groupName
+     * @param String remoteFileName
+     * @return returned value comment here
+     * @author Wang Liang
+     */
+    public static ResponseEntity<byte[]> download(String filePath, String fileName) {
+        byte[] content = null;
+        HttpHeaders headers = new HttpHeaders();
+        String substr = filePath.substring(filePath.indexOf("group"));
+        String groupName = substr.split("/")[0];
+        String remoteFileName = substr.substring(substr.indexOf("/") + 1);
+        String specFileName = substr.substring(substr.indexOf("."));
+        try {
+            if (fileName == null || fileName.trim().equals("")) {
+                fileName = UUID.randomUUID() + specFileName;
+            } else {
+                fileName = fileName + specFileName;
+            }
+            content = storageClient.download_file(groupName, remoteFileName);
+            headers.setContentDispositionFormData("attachment", new String(fileName.getBytes("UTF-8"), "iso-8859-1"));
+            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return new ResponseEntity<byte[]>(content, headers, HttpStatus.CREATED);
+    }
+
+    public static FileInfo getFile(String filePath) {
+        String substr = filePath.substring(filePath.indexOf("group"));
+        String groupName = substr.split("/")[0];
+        String remoteFileName = substr.substring(substr.indexOf("/") + 1);
+        try {
+            return storageClient.get_file_info(groupName, remoteFileName);
+        } catch (IOException e) {
+            logger.error("IO Exception: Get File from Fast DFS failed", e);
+        } catch (Exception e) {
+            logger.error("Non IO Exception: Get File from Fast DFS failed", e);
+        }
+        return null;
+    }
+}

+ 123 - 0
kmall-common/src/main/java/com/kmall/common/interceptor/LogInterceptor.java

@@ -0,0 +1,123 @@
+/*
+ * 创建时间:2017-08-06 12:25
+ * 项目名称:kmall_pt
+ * 类名称:LogInterceptor.java
+ * 包名称:com.kmall.common.interceptor
+ *
+ * 修改履历:
+ *          日期              修正者        主要内容
+ *                                      
+ *
+ * Copyright (c) 2016-2017 兆尹科技
+ */
+package com.kmall.common.interceptor;
+
+import com.kmall.common.entity.SysUserEntity;
+import com.kmall.common.utils.RequestUtil;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.SecurityUtils;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+import org.springframework.web.util.UrlPathHelper;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Date;
+
+/**
+ * 名称:LogInterceptor <br>
+ * 描述:日志拦截器<br>
+ *
+ * @author Scott
+ * @version 1.0
+ * @since 1.0.0
+ */
+public class LogInterceptor extends HandlerInterceptorAdapter {
+
+    private static final Log log = LogFactory.getLog(LogInterceptor.class);
+
+    /*
+     * (non-Javadoc)
+     * @see org.springframework.web.servlet.handler.HandlerInterceptorAdapter#
+     * preHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse,
+     * java.lang.Object)
+     */
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        request.setAttribute("REQUEST_START_TIME", new Date());
+
+        return true;
+
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.springframework.web.servlet.handler.HandlerInterceptorAdapter#
+     * postHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse,
+     * java.lang.Object, org.springframework.web.servlet.ModelAndView)
+     */
+    @Override
+    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
+                           ModelAndView modelAndView) throws Exception {
+
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.springframework.web.servlet.handler.HandlerInterceptorAdapter#
+     * afterCompletion(javax.servlet.http.HttpServletRequest,
+     * javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Exception)
+     */
+    @Override
+    public void afterCompletion(HttpServletRequest request,
+                                HttpServletResponse response, Object handler,
+                                Exception ex)
+            throws Exception {
+
+        Date start = (Date) request.getAttribute("REQUEST_START_TIME");
+        Date end = new Date();
+
+        log.info("本次请求耗时:" + (end.getTime() - start.getTime()) + "毫秒;" + getRequestInfo(request).toString());
+
+    }
+
+    @Override
+    public void afterConcurrentHandlingStarted(HttpServletRequest request,
+                                               HttpServletResponse response,
+                                               Object handler)
+            throws Exception {
+        super.afterConcurrentHandlingStarted(request, response, handler);
+    }
+
+    /**
+     * 主要功能:获取请求详细信息
+     * 注意事项:无
+     *
+     * @param request 请求
+     * @return 请求信息
+     */
+    private StringBuilder getRequestInfo(HttpServletRequest request) {
+        StringBuilder reqInfo = new StringBuilder();
+        UrlPathHelper urlPathHelper = new UrlPathHelper();
+        String urlPath = urlPathHelper.getLookupPathForRequest(request);
+        reqInfo.append(" 请求路径=" + urlPath);
+        reqInfo.append(" 来源IP=" + RequestUtil.getIpAddrByRequest(request));
+
+
+        String userName = "";
+        try {
+            if (null != SecurityUtils.getSubject().getPrincipal()) {
+                SysUserEntity sysUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal();
+                if (sysUser != null) {
+                    userName = (sysUser.getUsername());
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        reqInfo.append(" 操作人=" + (userName));
+        reqInfo.append(" 请求参数=" + RequestUtil.getParameters(request).toString());
+        return reqInfo;
+    }
+}

+ 32 - 0
kmall-common/src/main/java/com/kmall/common/log4j/GradeLogDailyRollingFileAppender.java

@@ -0,0 +1,32 @@
+/*
+ * 创建时间:2017-07-31 12:01
+ * 项目名称:kmall_pt
+ * 类名称:SimLogRollingFileAppender.java
+ * 包名称:com.joyintech.log4j
+ *
+ * 修改履历:
+ *          日期              修正者        主要内容
+ *                                      
+ *
+ * Copyright (c) 2016-2017 兆尹科技
+ */
+package com.kmall.common.log4j;
+
+import org.apache.log4j.DailyRollingFileAppender;
+import org.apache.log4j.Priority;
+
+/**
+ * 名称:GradeLogDailyRollingFileAppender <br>
+ * 描述:自定义日志文件追加类,使日志分级打印配置只判断是否相等,而不是使用上限<br>
+ *
+ * @author Scott
+ * @version 1.0
+ * @since 1.0.0
+ */
+public class GradeLogDailyRollingFileAppender extends DailyRollingFileAppender {
+    @Override
+    public boolean isAsSevereAsThreshold(Priority priority) {
+        //只判断是否相等,而不判断优先级
+        return this.getThreshold().equals(priority);
+    }
+}

+ 54 - 0
kmall-common/src/main/java/com/kmall/common/oss/AliyunCloudStorageService.java

@@ -0,0 +1,54 @@
+package com.kmall.common.oss;
+
+import com.aliyun.oss.OSSClient;
+import com.kmall.common.utils.RRException;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+/**
+ * 阿里云存储
+ *
+ * @author Scott
+ * @email
+ * @date 2017-03-26 16:22
+ */
+public class AliyunCloudStorageService extends CloudStorageService {
+    private OSSClient client;
+
+    public AliyunCloudStorageService(CloudStorageConfig config) {
+        this.config = config;
+
+        //初始化
+        init();
+    }
+
+    private void init() {
+        client = new OSSClient(config.getAliyunEndPoint(), config.getAliyunAccessKeyId(),
+                config.getAliyunAccessKeySecret());
+    }
+
+    @Override
+    public String upload(MultipartFile file) throws Exception {
+        String fileName = file.getOriginalFilename();
+        String prefix = fileName.substring(fileName.lastIndexOf(".") + 1);
+        return upload(file.getBytes(), getPath(config.getAliyunPrefix()) + "." + prefix);
+    }
+
+    @Override
+    public String upload(byte[] data, String path) {
+        return upload(new ByteArrayInputStream(data), path);
+    }
+
+    @Override
+    public String upload(InputStream inputStream, String path) {
+        try {
+            client.putObject(config.getAliyunBucketName(), path, inputStream);
+        } catch (Exception e) {
+            throw new RRException("上传文件失败,请检查配置信息", e);
+        }
+
+        return config.getAliyunDomain() + "/" + path;
+    }
+}

+ 235 - 0
kmall-common/src/main/java/com/kmall/common/oss/CloudStorageConfig.java

@@ -0,0 +1,235 @@
+package com.kmall.common.oss;
+
+import com.kmall.common.validator.group.AliyunGroup;
+import com.kmall.common.validator.group.QcloudGroup;
+import com.kmall.common.validator.group.QiniuGroup;
+import org.hibernate.validator.constraints.NotBlank;
+import org.hibernate.validator.constraints.Range;
+import org.hibernate.validator.constraints.URL;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * 云存储配置信息
+ *
+ * @author Scott
+ * @email
+ * @date 2017-03-25 16:12
+ */
+public class CloudStorageConfig implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    //类型 1:七牛  2:阿里云  3:腾讯云
+    @Range(min = 1, max = 3, message = "类型错误")
+    private Integer type;
+
+    //七牛绑定的域名
+    @NotBlank(message = "七牛绑定的域名不能为空", groups = QiniuGroup.class)
+    @URL(message = "七牛绑定的域名格式不正确", groups = QiniuGroup.class)
+    private String qiniuDomain;
+    //七牛路径前缀
+    private String qiniuPrefix;
+    //七牛ACCESS_KEY
+    @NotBlank(message = "七牛AccessKey不能为空", groups = QiniuGroup.class)
+    private String qiniuAccessKey;
+    //七牛SECRET_KEY
+    @NotBlank(message = "七牛SecretKey不能为空", groups = QiniuGroup.class)
+    private String qiniuSecretKey;
+    //七牛存储空间名
+    @NotBlank(message = "七牛空间名不能为空", groups = QiniuGroup.class)
+    private String qiniuBucketName;
+
+    //阿里云绑定的域名
+    @NotBlank(message = "阿里云绑定的域名不能为空", groups = AliyunGroup.class)
+    @URL(message = "阿里云绑定的域名格式不正确", groups = AliyunGroup.class)
+    private String aliyunDomain;
+    //阿里云路径前缀
+    private String aliyunPrefix;
+    //阿里云EndPoint
+    @NotBlank(message = "阿里云EndPoint不能为空", groups = AliyunGroup.class)
+    private String aliyunEndPoint;
+    //阿里云AccessKeyId
+    @NotBlank(message = "阿里云AccessKeyId不能为空", groups = AliyunGroup.class)
+    private String aliyunAccessKeyId;
+    //阿里云AccessKeySecret
+    @NotBlank(message = "阿里云AccessKeySecret不能为空", groups = AliyunGroup.class)
+    private String aliyunAccessKeySecret;
+    //阿里云BucketName
+    @NotBlank(message = "阿里云BucketName不能为空", groups = AliyunGroup.class)
+    private String aliyunBucketName;
+
+    //腾讯云绑定的域名
+    @NotBlank(message = "腾讯云绑定的域名不能为空", groups = QcloudGroup.class)
+    @URL(message = "腾讯云绑定的域名格式不正确", groups = QcloudGroup.class)
+    private String qcloudDomain;
+    //腾讯云路径前缀
+    private String qcloudPrefix;
+    //腾讯云AppId
+    @NotNull(message = "腾讯云AppId不能为空", groups = QcloudGroup.class)
+    private Integer qcloudAppId;
+    //腾讯云SecretId
+    @NotBlank(message = "腾讯云SecretId不能为空", groups = QcloudGroup.class)
+    private String qcloudSecretId;
+    //腾讯云SecretKey
+    @NotBlank(message = "腾讯云SecretKey不能为空", groups = QcloudGroup.class)
+    private String qcloudSecretKey;
+    //腾讯云BucketName
+    @NotBlank(message = "腾讯云BucketName不能为空", groups = QcloudGroup.class)
+    private String qcloudBucketName;
+    //腾讯云COS所属地区
+    @NotBlank(message = "所属地区不能为空", groups = QcloudGroup.class)
+    private String qcloudRegion;
+
+    public Integer getType() {
+        return type;
+    }
+
+    public void setType(Integer type) {
+        this.type = type;
+    }
+
+    public String getQiniuDomain() {
+        return qiniuDomain;
+    }
+
+    public void setQiniuDomain(String qiniuDomain) {
+        this.qiniuDomain = qiniuDomain;
+    }
+
+    public String getQiniuAccessKey() {
+        return qiniuAccessKey;
+    }
+
+    public void setQiniuAccessKey(String qiniuAccessKey) {
+        this.qiniuAccessKey = qiniuAccessKey;
+    }
+
+    public String getQiniuSecretKey() {
+        return qiniuSecretKey;
+    }
+
+    public void setQiniuSecretKey(String qiniuSecretKey) {
+        this.qiniuSecretKey = qiniuSecretKey;
+    }
+
+    public String getQiniuBucketName() {
+        return qiniuBucketName;
+    }
+
+    public void setQiniuBucketName(String qiniuBucketName) {
+        this.qiniuBucketName = qiniuBucketName;
+    }
+
+    public String getQiniuPrefix() {
+        return qiniuPrefix;
+    }
+
+    public void setQiniuPrefix(String qiniuPrefix) {
+        this.qiniuPrefix = qiniuPrefix;
+    }
+
+    public String getAliyunDomain() {
+        return aliyunDomain;
+    }
+
+    public void setAliyunDomain(String aliyunDomain) {
+        this.aliyunDomain = aliyunDomain;
+    }
+
+    public String getAliyunPrefix() {
+        return aliyunPrefix;
+    }
+
+    public void setAliyunPrefix(String aliyunPrefix) {
+        this.aliyunPrefix = aliyunPrefix;
+    }
+
+    public String getAliyunEndPoint() {
+        return aliyunEndPoint;
+    }
+
+    public void setAliyunEndPoint(String aliyunEndPoint) {
+        this.aliyunEndPoint = aliyunEndPoint;
+    }
+
+    public String getAliyunAccessKeyId() {
+        return aliyunAccessKeyId;
+    }
+
+    public void setAliyunAccessKeyId(String aliyunAccessKeyId) {
+        this.aliyunAccessKeyId = aliyunAccessKeyId;
+    }
+
+    public String getAliyunAccessKeySecret() {
+        return aliyunAccessKeySecret;
+    }
+
+    public void setAliyunAccessKeySecret(String aliyunAccessKeySecret) {
+        this.aliyunAccessKeySecret = aliyunAccessKeySecret;
+    }
+
+    public String getAliyunBucketName() {
+        return aliyunBucketName;
+    }
+
+    public void setAliyunBucketName(String aliyunBucketName) {
+        this.aliyunBucketName = aliyunBucketName;
+    }
+
+    public String getQcloudDomain() {
+        return qcloudDomain;
+    }
+
+    public void setQcloudDomain(String qcloudDomain) {
+        this.qcloudDomain = qcloudDomain;
+    }
+
+    public String getQcloudPrefix() {
+        return qcloudPrefix;
+    }
+
+    public void setQcloudPrefix(String qcloudPrefix) {
+        this.qcloudPrefix = qcloudPrefix;
+    }
+
+    public Integer getQcloudAppId() {
+        return qcloudAppId;
+    }
+
+    public void setQcloudAppId(Integer qcloudAppId) {
+        this.qcloudAppId = qcloudAppId;
+    }
+
+    public String getQcloudSecretId() {
+        return qcloudSecretId;
+    }
+
+    public void setQcloudSecretId(String qcloudSecretId) {
+        this.qcloudSecretId = qcloudSecretId;
+    }
+
+    public String getQcloudSecretKey() {
+        return qcloudSecretKey;
+    }
+
+    public void setQcloudSecretKey(String qcloudSecretKey) {
+        this.qcloudSecretKey = qcloudSecretKey;
+    }
+
+    public String getQcloudBucketName() {
+        return qcloudBucketName;
+    }
+
+    public void setQcloudBucketName(String qcloudBucketName) {
+        this.qcloudBucketName = qcloudBucketName;
+    }
+
+    public String getQcloudRegion() {
+        return qcloudRegion;
+    }
+
+    public void setQcloudRegion(String qcloudRegion) {
+        this.qcloudRegion = qcloudRegion;
+    }
+}

+ 69 - 0
kmall-common/src/main/java/com/kmall/common/oss/CloudStorageService.java

@@ -0,0 +1,69 @@
+package com.kmall.common.oss;
+
+import com.kmall.common.utils.DateUtils;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.InputStream;
+import java.util.Date;
+import java.util.UUID;
+
+/**
+ * 云存储(支持七牛、阿里云、腾讯云、又拍云)
+ *
+ * @author Scott
+ * @email
+ * @date 2017-03-25 14:58
+ */
+public abstract class CloudStorageService {
+    /**
+     * 云存储配置信息
+     */
+    CloudStorageConfig config;
+
+    /**
+     * 文件路径
+     *
+     * @param prefix 前缀
+     * @return 返回上传路径
+     */
+    public String getPath(String prefix) {
+        //生成uuid
+        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
+        //文件路径
+        String path = DateUtils.format(new Date(), "yyyyMMdd") + "/" + DateUtils.format(new Date(), "HHmmssS") + uuid.substring(0, 5);
+
+        if (StringUtils.isNotBlank(prefix)) {
+            path = prefix + "/" + path;
+        }
+
+        return path;
+    }
+
+    /**
+     * 文件上传
+     *
+     * @param file 文件
+     * @return 返回http地址
+     */
+    public abstract String upload(MultipartFile file) throws Exception;
+
+    /**
+     * 文件上传
+     *
+     * @param data 文件字节数组
+     * @param path 文件路径,包含文件名
+     * @return 返回http地址
+     */
+    public abstract String upload(byte[] data, String path);
+
+    /**
+     * 文件上传
+     *
+     * @param inputStream 字节流
+     * @param path        文件路径,包含文件名
+     * @return 返回http地址
+     */
+    public abstract String upload(InputStream inputStream, String path);
+
+}

+ 37 - 0
kmall-common/src/main/java/com/kmall/common/oss/OSSFactory.java

@@ -0,0 +1,37 @@
+package com.kmall.common.oss;
+
+import com.kmall.common.service.SysConfigService;
+import com.kmall.common.utils.ConfigConstant;
+import com.kmall.common.utils.Constant;
+import com.kmall.common.utils.SpringContextUtils;
+
+/**
+ * 文件上传Factory
+ *
+ * @author Scott
+ * @email
+ * @date 2017-03-26 10:18
+ */
+public final class OSSFactory {
+    private static SysConfigService sysConfigService;
+
+    static {
+        OSSFactory.sysConfigService = (SysConfigService) SpringContextUtils.getBean("sysConfigService");
+    }
+
+    public static CloudStorageService build() {
+        //获取云存储配置信息
+        CloudStorageConfig config = sysConfigService.getConfigObject(ConfigConstant.CLOUD_STORAGE_CONFIG_KEY, CloudStorageConfig.class);
+
+        if (config.getType() == Constant.CloudService.QINIU.getValue()) {
+            return new QiniuCloudStorageService(config);
+        } else if (config.getType() == Constant.CloudService.ALIYUN.getValue()) {
+            return new AliyunCloudStorageService(config);
+        } else if (config.getType() == Constant.CloudService.QCLOUD.getValue()) {
+            return new QcloudCloudStorageService(config);
+        }
+
+        return null;
+    }
+
+}

+ 80 - 0
kmall-common/src/main/java/com/kmall/common/oss/QcloudCloudStorageService.java

@@ -0,0 +1,80 @@
+package com.kmall.common.oss;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.kmall.common.utils.RRException;
+import com.qcloud.cos.COSClient;
+import com.qcloud.cos.ClientConfig;
+import com.qcloud.cos.request.UploadFileRequest;
+import com.qcloud.cos.sign.Credentials;
+import org.apache.commons.io.IOUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * 腾讯云存储
+ *
+ * @author Scott
+ * @email
+ * @date 2017-03-26 20:51
+ */
+public class QcloudCloudStorageService extends CloudStorageService {
+    private COSClient client;
+
+    public QcloudCloudStorageService(CloudStorageConfig config) {
+        this.config = config;
+
+        //初始化
+        init();
+    }
+
+    private void init() {
+        Credentials credentials = new Credentials(config.getQcloudAppId(), config.getQcloudSecretId(),
+                config.getQcloudSecretKey());
+
+        //初始化客户端配置
+        ClientConfig clientConfig = new ClientConfig();
+        //设置bucket所在的区域,华南:gz 华北:tj 华东:sh
+        clientConfig.setRegion(config.getQcloudRegion());
+
+        client = new COSClient(clientConfig, credentials);
+    }
+
+    @Override
+    public String upload(MultipartFile file) throws Exception {
+        String fileName = file.getOriginalFilename();
+        String prefix = fileName.substring(fileName.lastIndexOf(".") + 1);
+        return upload(file.getBytes(), getPath(config.getAliyunPrefix()) + "." + prefix);
+    }
+
+    @Override
+    public String upload(byte[] data, String path) {
+        //腾讯云必需要以"/"开头
+        if (!path.startsWith("/")) {
+            path = "/" + path;
+        }
+
+        //上传到腾讯云
+        UploadFileRequest request = new UploadFileRequest(config.getQcloudBucketName(), path, data);
+        String response = client.uploadFile(request);
+
+        JSONObject jsonObject = JSON.parseObject(response);
+        if (jsonObject.getIntValue("code") != 0) {
+            throw new RRException("文件上传失败," + jsonObject.getString("message"));
+        }
+
+        return config.getQcloudDomain() + path;
+    }
+
+    @Override
+    public String upload(InputStream inputStream, String path) {
+        try {
+            byte[] data = IOUtils.toByteArray(inputStream);
+            return this.upload(data, path);
+        } catch (IOException e) {
+            throw new RRException("上传文件失败", e);
+        }
+    }
+}

+ 69 - 0
kmall-common/src/main/java/com/kmall/common/oss/QiniuCloudStorageService.java

@@ -0,0 +1,69 @@
+package com.kmall.common.oss;
+
+import com.kmall.common.utils.RRException;
+import com.qiniu.common.Zone;
+import com.qiniu.http.Response;
+import com.qiniu.storage.Configuration;
+import com.qiniu.storage.UploadManager;
+import com.qiniu.util.Auth;
+import org.apache.commons.io.IOUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * 七牛云存储
+ *
+ * @author Scott
+ * @email
+ * @date 2017-03-25 15:41
+ */
+public class QiniuCloudStorageService extends CloudStorageService {
+    private UploadManager uploadManager;
+    private String token;
+
+    public QiniuCloudStorageService(CloudStorageConfig config) {
+        this.config = config;
+
+        //初始化
+        init();
+    }
+
+    private void init() {
+        uploadManager = new UploadManager(new Configuration(Zone.autoZone()));
+        token = Auth.create(config.getQiniuAccessKey(), config.getQiniuSecretKey()).
+                uploadToken(config.getQiniuBucketName());
+    }
+
+    @Override
+    public String upload(MultipartFile file) throws Exception {
+        String fileName = file.getOriginalFilename();
+        String prefix = fileName.substring(fileName.lastIndexOf(".") + 1);
+        return upload(file.getBytes(), getPath(config.getAliyunPrefix()) + "." + prefix);
+    }
+
+    @Override
+    public String upload(byte[] data, String path) {
+        try {
+            Response res = uploadManager.put(data, path, token);
+            if (!res.isOK()) {
+                throw new RuntimeException("上传七牛出错:" + res.toString());
+            }
+        } catch (Exception e) {
+            throw new RRException("上传文件失败,请核对七牛配置信息", e);
+        }
+
+        return config.getQiniuDomain() + "/" + path;
+    }
+
+    @Override
+    public String upload(InputStream inputStream, String path) {
+        try {
+            byte[] data = IOUtils.toByteArray(inputStream);
+            return this.upload(data, path);
+        } catch (IOException e) {
+            throw new RRException("上传文件失败", e);
+        }
+    }
+}

+ 213 - 0
kmall-common/src/main/java/com/kmall/common/security/JedisCacheManager.java

@@ -0,0 +1,213 @@
+package com.kmall.common.security;
+
+import com.google.common.collect.Sets;
+import com.kmall.common.utils.ServletUtils;
+import com.kmall.common.utils.redis.JedisUtil;
+import org.apache.shiro.cache.Cache;
+import org.apache.shiro.cache.CacheException;
+import org.apache.shiro.cache.CacheManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import redis.clients.jedis.Jedis;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * 自定义授权缓存管理类
+ *
+ * @author zhuliyun
+ */
+public class JedisCacheManager implements CacheManager {
+
+    private String cacheKeyPrefix = "shiro_cache_";
+
+    @Override
+    public <K, V> Cache<K, V> getCache(String name) throws CacheException {
+        return new JedisCache<K, V>(cacheKeyPrefix + name);
+    }
+
+    public String getCacheKeyPrefix() {
+        return cacheKeyPrefix;
+    }
+
+    public void setCacheKeyPrefix(String cacheKeyPrefix) {
+        this.cacheKeyPrefix = cacheKeyPrefix;
+    }
+
+    /**
+     * 自定义授权缓存管理类
+     *
+     * @author ThinkGem
+     * @version 2014-7-20
+     */
+    public class JedisCache<K, V> implements Cache<K, V> {
+
+        private Logger logger = LoggerFactory.getLogger(getClass());
+
+        private String cacheKeyName = null;
+
+        public JedisCache(String cacheKeyName) {
+            this.cacheKeyName = cacheKeyName;
+//			if (!JedisUtil.exists(cacheKeyName)){
+//				Map<String, Object> map = Maps.newHashMap();
+//				JedisUtil.setObjectMap(cacheKeyName, map, 60 * 60 * 24);
+//			}
+//			logger.debug("Init: cacheKeyName {} ", cacheKeyName);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public V get(K key) throws CacheException {
+            if (key == null) {
+                return null;
+            }
+
+            V v = null;
+            HttpServletRequest request = ServletUtils.getRequest();
+            if (request != null) {
+                v = (V) request.getAttribute(cacheKeyName);
+                if (v != null) {
+                    return v;
+                }
+            }
+
+            V value = null;
+            Jedis jedis = null;
+            try {
+                jedis = JedisUtil.getResource();
+                value = (V) JedisUtil.toObject(jedis.hget(JedisUtil.getBytesKey(cacheKeyName), JedisUtil.getBytesKey(key)));
+                logger.debug("get {} {} {}", cacheKeyName, key, request != null ? request.getRequestURI() : "");
+            } catch (Exception e) {
+                logger.error("get {} {} {}", cacheKeyName, key, request != null ? request.getRequestURI() : "", e);
+            } finally {
+                JedisUtil.returnResource(jedis);
+            }
+
+            if (request != null && value != null) {
+                request.setAttribute(cacheKeyName, value);
+            }
+
+            return value;
+        }
+
+        @Override
+        public V put(K key, V value) throws CacheException {
+            if (key == null) {
+                return null;
+            }
+
+            Jedis jedis = null;
+            try {
+                jedis = JedisUtil.getResource();
+                jedis.hset(JedisUtil.getBytesKey(cacheKeyName), JedisUtil.getBytesKey(key), JedisUtil.toBytes(value));
+                logger.debug("put {} {} = {}", cacheKeyName, key, value);
+            } catch (Exception e) {
+                logger.error("put {} {}", cacheKeyName, key, e);
+            } finally {
+                JedisUtil.returnResource(jedis);
+            }
+            return value;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public V remove(K key) throws CacheException {
+            V value = null;
+            Jedis jedis = null;
+            try {
+                jedis = JedisUtil.getResource();
+                value = (V) JedisUtil.toObject(jedis.hget(JedisUtil.getBytesKey(cacheKeyName), JedisUtil.getBytesKey(key)));
+                jedis.hdel(JedisUtil.getBytesKey(cacheKeyName), JedisUtil.getBytesKey(key));
+                logger.debug("remove {} {}", cacheKeyName, key);
+            } catch (Exception e) {
+                logger.warn("remove {} {}", cacheKeyName, key, e);
+            } finally {
+                JedisUtil.returnResource(jedis);
+            }
+            return value;
+        }
+
+        @Override
+        public void clear() throws CacheException {
+            Jedis jedis = null;
+            try {
+                jedis = JedisUtil.getResource();
+                jedis.hdel(JedisUtil.getBytesKey(cacheKeyName));
+                logger.debug("clear {}", cacheKeyName);
+            } catch (Exception e) {
+                logger.error("clear {}", cacheKeyName, e);
+            } finally {
+                JedisUtil.returnResource(jedis);
+            }
+        }
+
+        @Override
+        public int size() {
+            int size = 0;
+            Jedis jedis = null;
+            try {
+                jedis = JedisUtil.getResource();
+                size = jedis.hlen(JedisUtil.getBytesKey(cacheKeyName)).intValue();
+                logger.debug("size {} {} ", cacheKeyName, size);
+                return size;
+            } catch (Exception e) {
+                logger.error("clear {}", cacheKeyName, e);
+            } finally {
+                JedisUtil.returnResource(jedis);
+            }
+            return size;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public Set<K> keys() {
+            Set<K> keys = Sets.newHashSet();
+            Jedis jedis = null;
+            try {
+                jedis = JedisUtil.getResource();
+                Set<byte[]> set = jedis.hkeys(JedisUtil.getBytesKey(cacheKeyName));
+                for (byte[] key : set) {
+                    Object obj = (K) JedisUtil.getObjectKey(key);
+                    if (obj != null) {
+                        keys.add((K) obj);
+                    }
+                }
+                logger.debug("keys {} {} ", cacheKeyName, keys);
+                return keys;
+            } catch (Exception e) {
+                logger.error("keys {}", cacheKeyName, e);
+            } finally {
+                JedisUtil.returnResource(jedis);
+            }
+            return keys;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public Collection<V> values() {
+            Collection<V> vals = Collections.emptyList();
+            ;
+            Jedis jedis = null;
+            try {
+                jedis = JedisUtil.getResource();
+                Collection<byte[]> col = jedis.hvals(JedisUtil.getBytesKey(cacheKeyName));
+                for (byte[] val : col) {
+                    Object obj = JedisUtil.toObject(val);
+                    if (obj != null) {
+                        vals.add((V) obj);
+                    }
+                }
+                logger.debug("values {} {} ", cacheKeyName, vals);
+                return vals;
+            } catch (Exception e) {
+                logger.error("values {}", cacheKeyName, e);
+            } finally {
+                JedisUtil.returnResource(jedis);
+            }
+            return vals;
+        }
+    }
+}

+ 204 - 0
kmall-common/src/main/java/com/kmall/common/security/SessionManager.java

@@ -0,0 +1,204 @@
+package com.kmall.common.security;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shiro.session.InvalidSessionException;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.UnknownSessionException;
+import org.apache.shiro.session.mgt.SessionContext;
+import org.apache.shiro.session.mgt.SessionKey;
+import org.apache.shiro.session.mgt.SimpleSession;
+import org.apache.shiro.web.servlet.Cookie;
+import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
+import org.apache.shiro.web.servlet.SimpleCookie;
+import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
+import org.apache.shiro.web.util.WebUtils;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Date;
+
+
+/**
+ * 自定义WEB会话管理类
+ *
+ * @author zhuliyun
+ */
+public class SessionManager extends DefaultWebSessionManager {
+
+    public SessionManager() {
+        super();
+    }
+
+    @Override
+    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
+        // 如果参数中包含“__sid”参数,则使用此sid会话。 例如:http://localhost/project?__sid=xxx&__cookie=true
+        String sid = request.getParameter("__sid");
+        if (StringUtils.isNotBlank(sid)) {
+            // 是否将sid保存到cookie,浏览器模式下使用此参数。
+            if (WebUtils.isTrue(request, "__cookie")) {
+                HttpServletRequest rq = (HttpServletRequest) request;
+                HttpServletResponse rs = (HttpServletResponse) response;
+                Cookie template = getSessionIdCookie();
+                Cookie cookie = new SimpleCookie(template);
+                cookie.setValue(sid);
+                cookie.saveTo(rq, rs);
+            }
+            // 设置当前session状态
+            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
+                    ShiroHttpServletRequest.URL_SESSION_ID_SOURCE); // session来源与url
+            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sid);
+            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
+            return sid;
+        } else {
+            return super.getSessionId(request, response);
+        }
+    }
+
+    @Override
+    public void validateSessions() {
+        super.validateSessions();
+    }
+
+    protected Session retrieveSession(SessionKey sessionKey) {
+        try {
+            return super.retrieveSession(sessionKey);
+        } catch (UnknownSessionException e) {
+            // 获取不到SESSION不抛出异常
+            return null;
+        }
+    }
+
+    public Date getStartTimestamp(SessionKey key) {
+        try {
+            return super.getStartTimestamp(key);
+        } catch (InvalidSessionException e) {
+            // 获取不到SESSION不抛出异常
+            return null;
+        }
+    }
+
+    public Date getLastAccessTime(SessionKey key) {
+        try {
+            return super.getLastAccessTime(key);
+        } catch (InvalidSessionException e) {
+            // 获取不到SESSION不抛出异常
+            return null;
+        }
+    }
+
+    public long getTimeout(SessionKey key) {
+        try {
+            return super.getTimeout(key);
+        } catch (InvalidSessionException e) {
+            // 获取不到SESSION不抛出异常
+            return 0;
+        }
+    }
+
+    public void setTimeout(SessionKey key, long maxIdleTimeInMillis) {
+        try {
+            super.setTimeout(key, maxIdleTimeInMillis);
+        } catch (InvalidSessionException e) {
+            // 获取不到SESSION不抛出异常
+        }
+    }
+
+    public void touch(SessionKey key) {
+        try {
+            super.touch(key);
+        } catch (InvalidSessionException e) {
+            // 获取不到SESSION不抛出异常
+        }
+    }
+
+    public String getHost(SessionKey key) {
+        try {
+            return super.getHost(key);
+        } catch (InvalidSessionException e) {
+            // 获取不到SESSION不抛出异常
+            return null;
+        }
+    }
+
+    public Collection<Object> getAttributeKeys(SessionKey key) {
+        try {
+            return super.getAttributeKeys(key);
+        } catch (InvalidSessionException e) {
+            // 获取不到SESSION不抛出异常
+            return null;
+        }
+    }
+
+    public Object getAttribute(SessionKey sessionKey, Object attributeKey) {
+        try {
+            return super.getAttribute(sessionKey, attributeKey);
+        } catch (InvalidSessionException e) {
+            // 获取不到SESSION不抛出异常
+            return null;
+        }
+    }
+
+    public void setAttribute(SessionKey sessionKey, Object attributeKey, Object value) {
+        try {
+            super.setAttribute(sessionKey, attributeKey, value);
+        } catch (InvalidSessionException e) {
+            // 获取不到SESSION不抛出异常
+        }
+    }
+
+    public Object removeAttribute(SessionKey sessionKey, Object attributeKey) {
+        try {
+            return super.removeAttribute(sessionKey, attributeKey);
+        } catch (InvalidSessionException e) {
+            // 获取不到SESSION不抛出异常
+            return null;
+        }
+    }
+
+    public void stop(SessionKey key) {
+        try {
+            super.stop(key);
+        } catch (InvalidSessionException e) {
+            // 获取不到SESSION不抛出异常
+        }
+    }
+
+    public void checkValid(SessionKey key) {
+        try {
+            super.checkValid(key);
+        } catch (InvalidSessionException e) {
+            // 获取不到SESSION不抛出异常
+        }
+    }
+
+    @Override
+    protected Session doCreateSession(SessionContext context) {
+        try {
+            return super.doCreateSession(context);
+        } catch (IllegalStateException e) {
+            return null;
+        }
+    }
+
+    @Override
+    protected Session newSessionInstance(SessionContext context) {
+        Session session = super.newSessionInstance(context);
+        session.setTimeout(getGlobalSessionTimeout());
+        return session;
+    }
+
+    @Override
+    public Session start(SessionContext context) {
+        try {
+            return super.start(context);
+        } catch (NullPointerException e) {
+            SimpleSession session = new SimpleSession();
+            session.setId(0);
+            return session;
+        }
+    }
+}

+ 46 - 0
kmall-common/src/main/java/com/kmall/common/security/UserAuthenticationFilter.java

@@ -0,0 +1,46 @@
+package com.kmall.common.security;
+
+import com.alibaba.fastjson.JSON;
+import com.kmall.common.utils.ServletUtils;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.mgt.eis.SessionDAO;
+import org.apache.shiro.web.filter.authc.UserFilter;
+import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
+import org.apache.shiro.web.util.WebUtils;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.HashMap;
+import java.util.Map;
+
+@Service
+public class UserAuthenticationFilter extends UserFilter {
+
+
+    @Override
+    protected boolean onAccessDenied(ServletRequest request,
+                                     ServletResponse response) throws Exception {
+        HttpServletRequest httpRequest = WebUtils.toHttp(request);
+        if (ServletUtils.isAjaxRequest(httpRequest)) {
+            String msg = "Session超时请重新登陆";
+            DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
+            SessionManager sessionManager = (SessionManager) securityManager.getSessionManager();
+            SessionDAO sessionDao = (SessionDAO) sessionManager.getSessionDAO();
+            Session session = sessionDao.readSession(httpRequest.getCookies()[0].getValue());
+            HttpServletResponse res = WebUtils.toHttp(response);
+            res.setContentType("application/json");
+            res.setCharacterEncoding("utf-8");
+            Map<String, Object> obj = new HashMap<String, Object>();
+            obj.put("requestCode", -1);
+            obj.put("msg", msg);
+            res.getWriter().print(JSON.toJSONString(obj));
+            return false;
+        }
+        saveRequestAndRedirectToLogin(request, response);
+        return false;
+    }
+}

+ 105 - 0
kmall-common/src/main/java/com/kmall/common/security/session/JedisSessionDAO.java

@@ -0,0 +1,105 @@
+package com.kmall.common.security.session;
+
+import com.kmall.common.Global;
+import com.kmall.common.utils.ServletUtils;
+import com.kmall.common.utils.redis.JedisUtil;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.Serializable;
+
+/**
+ * 自定义授权会话管理类
+ *
+ * @author zhuliyun
+ */
+public class JedisSessionDAO extends EnterpriseCacheSessionDAO {
+
+    private Logger logger = LoggerFactory.getLogger(getClass());
+
+    private String sessionKeyPrefix = "shiro_session_";
+    // session 在redis过期时间是30分钟30*60
+    private static int expireTime = 1800;
+
+    // 创建session,保存到数据库
+    @Override
+    protected Serializable doCreate(Session session) {
+        Serializable sessionId = super.doCreate(session);
+        logger.debug("创建session:{}", session.getId().toString());
+        HttpServletRequest request = ServletUtils.getRequest();
+        if (request != null) {
+            String uri = request.getServletPath();
+            // 如果是静态文件,则不创建SESSION
+            if (ServletUtils.isStaticFile(uri)) {
+                return null;
+            }
+        }
+
+        JedisUtil.setObject(sessionKeyPrefix + session.getId().toString(), session, expireTime);
+        return sessionId;
+    }
+
+    // 获取session
+    @Override
+    protected Session doReadSession(Serializable sessionId) {
+        logger.debug("获取session:{}", sessionId);
+        // 先从缓存中获取session,如果没有再去数据库中获取
+        Session session = (Session) JedisUtil.getObject(sessionKeyPrefix + sessionId.toString());
+        return session;
+    }
+
+    // 更新session的最后一次访问时间
+    @Override
+    protected void doUpdate(Session session) {
+//        super.doUpdate(session);
+        logger.debug("获取session:{}", session.getId());
+        String key = sessionKeyPrefix + session.getId().toString();
+
+        HttpServletRequest request = ServletUtils.getRequest();
+        if (request != null) {
+            String uri = request.getServletPath();
+            // 如果是静态文件,则不更新SESSION
+            if (ServletUtils.isStaticFile(uri)) {
+                return;
+            }
+            // 手动控制不更新SESSION
+            if (Global.NO.equals(request.getParameter("updateSession"))) {
+                return;
+            }
+        }
+        if (null == JedisUtil.getObject(key)) {
+            return;
+        }
+        JedisUtil.expire(key, expireTime);
+    }
+
+    // 删除session
+    @Override
+    protected void doDelete(Session session) {
+        if (session == null || session.getId() == null) {
+            return;
+        }
+        logger.debug("删除session:{}", session.getId());
+//        super.doDelete(session);
+        JedisUtil.del(sessionKeyPrefix + session.getId().toString());
+    }
+
+    public String getSessionKeyPrefix() {
+        return sessionKeyPrefix;
+    }
+
+    public void setSessionKeyPrefix(String sessionKeyPrefix) {
+        this.sessionKeyPrefix = sessionKeyPrefix;
+    }
+
+    public static int getExpireTime() {
+        return expireTime;
+    }
+
+    public static void setExpireTime(int expireTime) {
+        JedisSessionDAO.expireTime = expireTime;
+    }
+}

+ 39 - 0
kmall-common/src/main/java/com/kmall/common/service/FormIdsService.java

@@ -0,0 +1,39 @@
+package com.kmall.common.service;
+
+import com.kmall.common.entity.FormIdsEntity;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 小程序form_id收集Service实现类
+ *
+ * @author Scott
+ * @email
+ * @date 2017-12-16 15:32:50
+ */
+public interface FormIdsService {
+
+    public FormIdsEntity queryObject(Long id);
+
+    public List<FormIdsEntity> queryList(Map<String, Object> map);
+
+    public int queryTotal(Map<String, Object> map);
+
+    public int save(FormIdsEntity formIds);
+
+    public int save(Long userId, String formId, Integer validNum,String merchOrderSn);
+
+    public int update(FormIdsEntity formIds);
+
+    public int delete(Integer id);
+
+    public int deleteBatch(Integer[] ids);
+
+    public FormIdsEntity getFormIds(Long userId);
+
+    public void releaseFormIds(FormIdsEntity formIds);
+
+    public FormIdsEntity getFormIdsByMerchOrderSn(String merchOrderSn);
+
+}

+ 65 - 0
kmall-common/src/main/java/com/kmall/common/service/SysConfigService.java

@@ -0,0 +1,65 @@
+package com.kmall.common.service;
+
+import com.kmall.common.entity.SysConfigEntity;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 系统配置信息
+ *
+ * @author Scott
+ * @email
+ * @date 2016年12月4日 下午6:49:01
+ */
+public interface SysConfigService {
+
+    /**
+     * 保存配置信息
+     */
+    public void save(SysConfigEntity config);
+
+    /**
+     * 更新配置信息
+     */
+    public void update(SysConfigEntity config);
+
+    /**
+     * 根据key,更新value
+     */
+    public void updateValueByKey(String key, String value);
+
+    /**
+     * 删除配置信息
+     */
+    public void deleteBatch(Long[] ids);
+
+    /**
+     * 获取List列表
+     */
+    public List<SysConfigEntity> queryList(Map<String, Object> map);
+
+    /**
+     * 获取总记录数
+     */
+    public int queryTotal(Map<String, Object> map);
+
+    public SysConfigEntity queryObject(Long id);
+
+    /**
+     * 根据key,获取配置的value值
+     *
+     * @param key          key
+     * @param defaultValue 缺省值
+     */
+    public String getValue(String key, String defaultValue);
+
+    /**
+     * 根据key,获取value的Object对象
+     *
+     * @param key   key
+     * @param clazz Object对象
+     */
+    public <T> T getConfigObject(String key, Class<T> clazz);
+
+}

+ 30 - 0
kmall-common/src/main/java/com/kmall/common/service/SysLogService.java

@@ -0,0 +1,30 @@
+package com.kmall.common.service;
+
+import com.kmall.common.entity.SysLogEntity;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 系统日志
+ *
+ * @author Scott
+ * @email
+ * @date 2017-03-08 10:40:56
+ */
+public interface SysLogService {
+
+    SysLogEntity queryObject(Long id);
+
+    List<SysLogEntity> queryList(Map<String, Object> map);
+
+    int queryTotal(Map<String, Object> map);
+
+    void save(SysLogEntity sysLog);
+
+    void update(SysLogEntity sysLog);
+
+    void delete(Long id);
+
+    void deleteBatch(Long[] ids);
+}

+ 71 - 0
kmall-common/src/main/java/com/kmall/common/service/SysMenuService.java

@@ -0,0 +1,71 @@
+package com.kmall.common.service;
+
+import com.kmall.common.entity.SysMenuEntity;
+
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 菜单管理
+ *
+ * @author Scott
+ * @email
+ * @date 2016年9月18日 上午9:42:16
+ */
+public interface SysMenuService {
+
+    /**
+     * 根据父菜单,查询子菜单
+     *
+     * @param parentId   父菜单ID
+     * @param menuIdList 用户菜单ID
+     */
+    List<SysMenuEntity> queryListParentId(Long parentId, List<Long> menuIdList);
+
+    /**
+     * 获取不包含按钮的菜单列表
+     */
+    List<SysMenuEntity> queryNotButtonList();
+
+    /**
+     * 获取用户菜单列表
+     */
+    List<SysMenuEntity> getUserMenuList(Long userId);
+
+
+    /**
+     * 查询菜单
+     */
+    SysMenuEntity queryObject(Long menuId);
+
+    /**
+     * 查询菜单列表
+     */
+    List<SysMenuEntity> queryList(Map<String, Object> map);
+
+    /**
+     * 查询总数
+     */
+    int queryTotal(Map<String, Object> map);
+
+    /**
+     * 保存菜单
+     */
+    void save(SysMenuEntity menu);
+
+    /**
+     * 修改
+     */
+    void update(SysMenuEntity menu);
+
+    /**
+     * 删除
+     */
+    void deleteBatch(Long[] menuIds);
+
+    /**
+     * 查询用户的权限列表
+     */
+    List<SysMenuEntity> queryUserList(Long userId);
+}

+ 30 - 0
kmall-common/src/main/java/com/kmall/common/service/SysRegionService.java

@@ -0,0 +1,30 @@
+package com.kmall.common.service;
+
+import com.kmall.common.entity.SysLogEntity;
+import com.kmall.common.entity.SysRegionEntity;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ * @author Scott
+ * @email
+ * @date 2017-03-08 10:40:56
+ */
+public interface SysRegionService {
+
+    SysRegionEntity queryObject(Long id);
+
+    List<SysRegionEntity> queryList(Map<String, Object> map);
+
+    int queryTotal(Map<String, Object> map);
+
+    void save(SysRegionEntity regionEntity);
+
+    void update(SysRegionEntity regionEntity);
+
+    void delete(Long id);
+
+    void deleteBatch(Long[] ids);
+}

+ 35 - 0
kmall-common/src/main/java/com/kmall/common/service/SysRoleDeptService.java

@@ -0,0 +1,35 @@
+package com.kmall.common.service;
+
+import java.util.List;
+
+
+/**
+ * 角色与部门对应关系
+ *
+ * @author Scott
+ * @email
+ * @date 2017年9月18日 上午9:18:38
+ */
+public interface SysRoleDeptService {
+
+    /**
+     * 保存角色与菜单关系
+     *
+     * @param roleId     角色ID
+     * @param deptIdList 部门Ids
+     */
+    void saveOrUpdate(Long roleId, List<Long> deptIdList);
+
+    /**
+     * 根据角色ID,获取部门ID列表
+     */
+    List<Long> queryDeptIdList(Long roleId);
+
+    /**
+     * 根据用户ID获取权限部门列表
+     *
+     * @param userId
+     * @return
+     */
+    List<Long> queryDeptIdListByUserId(Long userId);
+}

+ 24 - 0
kmall-common/src/main/java/com/kmall/common/service/SysRoleMenuService.java

@@ -0,0 +1,24 @@
+package com.kmall.common.service;
+
+import java.util.List;
+
+
+/**
+ * 角色与菜单对应关系
+ *
+ * @author Scott
+ * @email
+ * @date 2016年9月18日 上午9:42:30
+ */
+public interface SysRoleMenuService {
+
+    void saveOrUpdate(Long roleId, List<Long> menuIdList);
+
+    /**
+     * 根据角色ID,获取菜单ID列表
+     */
+    List<Long> queryMenuIdList(Long roleId);
+
+    int delete(Long roleId);
+
+}

+ 36 - 0
kmall-common/src/main/java/com/kmall/common/service/SysRoleService.java

@@ -0,0 +1,36 @@
+package com.kmall.common.service;
+
+import com.kmall.common.entity.SysRoleEntity;
+
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 角色
+ *
+ * @author Scott
+ * @email
+ * @date 2016年9月18日 上午9:42:52
+ */
+public interface SysRoleService {
+
+    SysRoleEntity queryObject(Long roleId);
+
+    List<SysRoleEntity> queryList(Map<String, Object> map);
+
+    int queryTotal(Map<String, Object> map);
+
+    void save(SysRoleEntity role);
+
+    void update(SysRoleEntity role);
+
+    void deleteBatch(Long[] roleIds);
+
+    void delete(Long roleId);
+
+    /**
+     * 查询用户创建的角色ID列表
+     */
+    List<Long> queryRoleIdList(Long createUserId);
+}

+ 81 - 0
kmall-common/src/main/java/com/kmall/common/service/SysSmsLogService.java

@@ -0,0 +1,81 @@
+package com.kmall.common.service;
+
+import com.kmall.common.entity.SysSmsLogEntity;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Service接口
+ *
+ * @author Scott
+ * @date 2017-12-16 23:38:05
+ */
+public interface SysSmsLogService {
+
+    /**
+     * 根据主键查询实体
+     *
+     * @param id 主键
+     * @return 实体
+     */
+    SysSmsLogEntity queryObject(String id);
+
+    /**
+     * 分页查询
+     *
+     * @param map 参数
+     * @return list
+     */
+    List<SysSmsLogEntity> queryList(Map<String, Object> map);
+
+    /**
+     * 分页统计总数
+     *
+     * @param map 参数
+     * @return 总数
+     */
+    int queryTotal(Map<String, Object> map);
+
+    /**
+     * 保存实体
+     *
+     * @param smsLog 实体
+     * @return 保存条数
+     */
+    int save(SysSmsLogEntity smsLog);
+
+    /**
+     * 根据主键更新实体
+     *
+     * @param smsLog 实体
+     * @return 更新条数
+     */
+    int update(SysSmsLogEntity smsLog);
+
+    /**
+     * 根据主键删除
+     *
+     * @param id
+     * @return 删除条数
+     */
+    int delete(String id);
+
+    /**
+     * 根据主键批量删除
+     *
+     * @param ids
+     * @return 删除条数
+     */
+    int deleteBatch(String[] ids);
+
+    /**
+     * 发送短信
+     *
+     * @param smsLog
+     * @return
+     */
+    SysSmsLogEntity sendSms(SysSmsLogEntity smsLog);
+
+    SysSmsLogEntity querySmsCodeByUserId(Long userId);
+}

+ 23 - 0
kmall-common/src/main/java/com/kmall/common/service/SysUserRoleService.java

@@ -0,0 +1,23 @@
+package com.kmall.common.service;
+
+import java.util.List;
+
+
+/**
+ * 用户与角色对应关系
+ *
+ * @author Scott
+ * @email
+ * @date 2016年9月18日 上午9:43:24
+ */
+public interface SysUserRoleService {
+
+    void saveOrUpdate(Long userId, Long roleId);
+
+    /**
+     * 根据用户ID,获取角色ID列表
+     */
+    List<Long> queryRoleIdList(Long userId);
+
+    void delete(Long userId);
+}

+ 76 - 0
kmall-common/src/main/java/com/kmall/common/service/SysUserService.java

@@ -0,0 +1,76 @@
+package com.kmall.common.service;
+
+import com.kmall.common.entity.SysUserEntity;
+
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 系统用户
+ *
+ * @author Scott
+ * @email
+ * @date 2016年9月18日 上午9:43:39
+ */
+public interface SysUserService {
+
+    /**
+     * 查询用户的所有权限
+     *
+     * @param userId 用户ID
+     */
+    List<String> queryAllPerms(Long userId);
+
+    /**
+     * 查询用户的所有菜单ID
+     */
+    List<Long> queryAllMenuId(Long userId);
+
+    /**
+     * 根据用户名,查询系统用户
+     */
+    SysUserEntity queryByUserName(String username);
+
+    /**
+     * 根据用户ID,查询用户
+     *
+     * @param userId
+     * @return
+     */
+    SysUserEntity queryObject(Long userId);
+
+    /**
+     * 查询用户列表
+     */
+    List<SysUserEntity> queryList(Map<String, Object> map);
+
+    /**
+     * 查询总数
+     */
+    int queryTotal(Map<String, Object> map);
+
+    /**
+     * 保存用户
+     */
+    void save(SysUserEntity user);
+
+    /**
+     * 修改用户
+     */
+    void update(SysUserEntity user);
+
+    /**
+     * 删除用户
+     */
+    void deleteBatch(Long[] userIds);
+
+    /**
+     * 修改密码
+     *
+     * @param userId      用户ID
+     * @param password    原密码
+     * @param newPassword 新密码
+     */
+    int updatePassword(Long userId, String password, String newPassword);
+}

+ 96 - 0
kmall-common/src/main/java/com/kmall/common/service/impl/FormIdsServiceImpl.java

@@ -0,0 +1,96 @@
+package com.kmall.common.service.impl;
+
+import com.kmall.common.dao.FormIdsDao;
+import com.kmall.common.entity.FormIdsEntity;
+import com.kmall.common.service.FormIdsService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 小程序form_id收集Service实现类
+ *
+ * @author Scott
+ * @email
+ * @date 2017-12-16 15:32:50
+ */
+@Service("formidsService")
+public class FormIdsServiceImpl implements FormIdsService {
+    @Autowired
+    private FormIdsDao formIdsDao;
+
+    @Override
+    public FormIdsEntity queryObject(Long id) {
+        return formIdsDao.queryObject(id);
+    }
+
+    @Override
+    public List<FormIdsEntity> queryList(Map<String, Object> map) {
+        return formIdsDao.queryList(map);
+    }
+
+    @Override
+    public int queryTotal(Map<String, Object> map) {
+        return formIdsDao.queryTotal(map);
+    }
+
+    @Override
+    public int save(FormIdsEntity formIds) {
+        return formIdsDao.save(formIds);
+    }
+
+    @Override
+    public int update(FormIdsEntity formIds) {
+        return formIdsDao.update(formIds);
+    }
+
+    @Override
+    public int delete(Integer id) {
+        return formIdsDao.delete(id);
+    }
+
+    @Override
+    public int deleteBatch(Integer[] ids) {
+        return formIdsDao.deleteBatch(ids);
+    }
+
+    @Override
+    public FormIdsEntity getFormIds(Long userId) {
+        return formIdsDao.queryByUserId(userId);
+    }
+
+    @Override
+    public FormIdsEntity getFormIdsByMerchOrderSn(String merchOrderSn) {
+        return formIdsDao.getFormIdsByMerchOrderSn(merchOrderSn);
+    }
+
+
+    @Override
+    public void releaseFormIds(FormIdsEntity formIds) {
+        formIds.setValidNum(formIds.getValidNum() - 1);
+        formIdsDao.update(formIds);
+    }
+
+    @Override
+    public int save(Long userId, String formId, Integer validNum,String merchOrderSn) {
+        // 保存form_id
+        FormIdsEntity formIdsVo = new FormIdsEntity();
+        long currentTime = System.currentTimeMillis() + 2 * 60 * 60 * 1000;
+        Date date = new Date(currentTime);
+//        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+//        String nowTime="";
+//        nowTime= df.format(date);
+        formIdsVo.setExpireTime(date);
+        formIdsVo.setFormId(formId);
+        formIdsVo.setValidNum(3);
+        formIdsVo.setUserId(userId);
+        formIdsVo.setMerchOrderSn(merchOrderSn);
+        return formIdsDao.save(formIdsVo);
+    }
+}

+ 77 - 0
kmall-common/src/main/java/com/kmall/common/service/impl/SysConfigServiceImpl.java

@@ -0,0 +1,77 @@
+package com.kmall.common.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.kmall.common.dao.SysConfigDao;
+import com.kmall.common.entity.SysConfigEntity;
+import com.kmall.common.service.SysConfigService;
+import com.kmall.common.utils.RRException;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+
+@Service("sysConfigService")
+public class SysConfigServiceImpl implements SysConfigService {
+	@Autowired
+	private SysConfigDao sysConfigDao;
+	
+	@Override
+	public void save(SysConfigEntity config) {
+		sysConfigDao.save(config);
+	}
+
+	@Override
+	public void update(SysConfigEntity config) {
+		sysConfigDao.update(config);
+	}
+
+	@Override
+	public void updateValueByKey(String key, String value) {
+		sysConfigDao.updateValueByKey(key, value);
+	}
+
+	@Override
+	public void deleteBatch(Long[] ids) {
+		sysConfigDao.deleteBatch(ids);
+	}
+
+	@Override
+	public List<SysConfigEntity> queryList(Map<String, Object> map) {
+		return sysConfigDao.queryList(map);
+	}
+
+	@Override
+	public int queryTotal(Map<String, Object> map) {
+		return sysConfigDao.queryTotal(map);
+	}
+
+	@Override
+	public SysConfigEntity queryObject(Long id) {
+		return sysConfigDao.queryObject(id);
+	}
+
+	@Override
+	public String getValue(String key, String defaultValue) {
+		String value = sysConfigDao.queryByKey(key);
+		if(StringUtils.isBlank(value)){
+			return defaultValue;
+		}
+		return value;
+	}
+	
+	@Override
+	public <T> T getConfigObject(String key, Class<T> clazz) {
+		String value = getValue(key, null);
+		if(StringUtils.isNotBlank(value)){
+			return JSON.parseObject(value, clazz);
+		}
+
+		try {
+			return clazz.newInstance();
+		} catch (Exception e) {
+			throw new RRException("获取参数失败");
+		}
+	}
+}

+ 53 - 0
kmall-common/src/main/java/com/kmall/common/service/impl/SysLogServiceImpl.java

@@ -0,0 +1,53 @@
+package com.kmall.common.service.impl;
+
+import com.kmall.common.dao.SysLogDao;
+import com.kmall.common.entity.SysLogEntity;
+import com.kmall.common.service.SysLogService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+
+
+@Service("sysLogService")
+public class SysLogServiceImpl implements SysLogService {
+	@Autowired
+	private SysLogDao sysLogDao;
+	
+	@Override
+	public SysLogEntity queryObject(Long id){
+		return sysLogDao.queryObject(id);
+	}
+	
+	@Override
+	public List<SysLogEntity> queryList(Map<String, Object> map){
+		return sysLogDao.queryList(map);
+	}
+	
+	@Override
+	public int queryTotal(Map<String, Object> map){
+		return sysLogDao.queryTotal(map);
+	}
+	
+	@Override
+	public void save(SysLogEntity sysLog){
+		sysLogDao.save(sysLog);
+	}
+	
+	@Override
+	public void update(SysLogEntity sysLog){
+		sysLogDao.update(sysLog);
+	}
+	
+	@Override
+	public void delete(Long id){
+		sysLogDao.delete(id);
+	}
+	
+	@Override
+	public void deleteBatch(Long[] ids){
+		sysLogDao.deleteBatch(ids);
+	}
+	
+}

+ 123 - 0
kmall-common/src/main/java/com/kmall/common/service/impl/SysMenuServiceImpl.java

@@ -0,0 +1,123 @@
+package com.kmall.common.service.impl;
+
+import com.kmall.common.dao.SysMenuDao;
+import com.kmall.common.entity.SysMenuEntity;
+import com.kmall.common.service.SysMenuService;
+import com.kmall.common.service.SysRoleMenuService;
+import com.kmall.common.service.SysUserService;
+import com.kmall.common.utils.Constant;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+
+@Service("sysMenuService")
+public class SysMenuServiceImpl implements SysMenuService {
+	@Autowired
+	private SysMenuDao sysMenuDao;
+	@Autowired
+	private SysUserService sysUserService;
+	@Autowired
+	private SysRoleMenuService sysRoleMenuService;
+	
+	@Override
+	public List<SysMenuEntity> queryListParentId(Long parentId, List<Long> menuIdList) {
+		List<SysMenuEntity> menuList = sysMenuDao.queryListParentId(parentId);
+		if(menuIdList == null){
+			return menuList;
+		}
+		
+		List<SysMenuEntity> userMenuList = new ArrayList<>();
+		for(SysMenuEntity menu : menuList){
+			if(menuIdList.contains(menu.getMenuId())){
+				userMenuList.add(menu);
+			}
+		}
+		return userMenuList;
+	}
+
+	@Override
+	public List<SysMenuEntity> queryNotButtonList() {
+		return sysMenuDao.queryNotButtonList();
+	}
+
+	@Override
+	public List<SysMenuEntity> getUserMenuList(Long userId) {
+		//系统管理员,拥有最高权限
+		if(userId == 1){
+			return getAllMenuList(null);
+		}
+		
+		//用户菜单列表
+		List<Long> menuIdList = sysUserService.queryAllMenuId(userId);
+		return getAllMenuList(menuIdList);
+	}
+	
+	@Override
+	public SysMenuEntity queryObject(Long menuId) {
+		return sysMenuDao.queryObject(menuId);
+	}
+
+	@Override
+	public List<SysMenuEntity> queryList(Map<String, Object> map) {
+		return sysMenuDao.queryList(map);
+	}
+
+	@Override
+	public int queryTotal(Map<String, Object> map) {
+		return sysMenuDao.queryTotal(map);
+	}
+
+	@Override
+	public void save(SysMenuEntity menu) {
+		sysMenuDao.save(menu);
+	}
+
+	@Override
+	public void update(SysMenuEntity menu) {
+		sysMenuDao.update(menu);
+	}
+
+	@Override
+	@Transactional
+	public void deleteBatch(Long[] menuIds) {
+		sysMenuDao.deleteBatch(menuIds);
+	}
+	
+	@Override
+	public List<SysMenuEntity> queryUserList(Long userId) {
+		return sysMenuDao.queryUserList(userId);
+	}
+
+	/**
+	 * 获取所有菜单列表
+	 */
+	private List<SysMenuEntity> getAllMenuList(List<Long> menuIdList){
+		//查询根菜单列表
+		List<SysMenuEntity> menuList = queryListParentId(0L, menuIdList);
+		//递归获取子菜单
+		getMenuTreeList(menuList, menuIdList);
+		
+		return menuList;
+	}
+
+	/**
+	 * 递归
+	 */
+	private List<SysMenuEntity> getMenuTreeList(List<SysMenuEntity> menuList, List<Long> menuIdList){
+		List<SysMenuEntity> subMenuList = new ArrayList<SysMenuEntity>();
+		
+		for(SysMenuEntity entity : menuList){
+			if(entity.getType() == Constant.MenuType.CATALOG.getValue()){//目录
+				entity.setList(getMenuTreeList(queryListParentId(entity.getMenuId(), menuIdList), menuIdList));
+			}
+			subMenuList.add(entity);
+		}
+		
+		return subMenuList;
+	}
+}

+ 53 - 0
kmall-common/src/main/java/com/kmall/common/service/impl/SysRegionServiceImpl.java

@@ -0,0 +1,53 @@
+package com.kmall.common.service.impl;
+
+import com.kmall.common.dao.SysRegionDao;
+import com.kmall.common.entity.SysRegionEntity;
+import com.kmall.common.service.SysRegionService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+
+
+@Service("sysRegionService")
+public class SysRegionServiceImpl implements SysRegionService {
+	@Autowired
+	private SysRegionDao regionDao;
+	
+	@Override
+	public SysRegionEntity queryObject(Long id){
+		return regionDao.queryObject(id);
+	}
+	
+	@Override
+	public List<SysRegionEntity> queryList(Map<String, Object> map){
+		return regionDao.queryList(map);
+	}
+	
+	@Override
+	public int queryTotal(Map<String, Object> map){
+		return regionDao.queryTotal(map);
+	}
+	
+	@Override
+	public void save(SysRegionEntity sysRegionEntity){
+		regionDao.save(sysRegionEntity);
+	}
+	
+	@Override
+	public void update(SysRegionEntity sysRegionEntity){
+		regionDao.update(sysRegionEntity);
+	}
+	
+	@Override
+	public void delete(Long id){
+		regionDao.delete(id);
+	}
+	
+	@Override
+	public void deleteBatch(Long[] ids){
+		regionDao.deleteBatch(ids);
+	}
+	
+}

+ 53 - 0
kmall-common/src/main/java/com/kmall/common/service/impl/SysRoleDeptServiceImpl.java

@@ -0,0 +1,53 @@
+package com.kmall.common.service.impl;
+
+import com.kmall.common.dao.SysRoleDeptDao;
+import com.kmall.common.service.SysRoleDeptService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 角色与部门对应关系
+ *
+ * @author Scott
+ * @email
+ * @date 2017年9月18日 上午9:18:38
+ */
+@Service("sysRoleDeptService")
+public class SysRoleDeptServiceImpl implements SysRoleDeptService {
+    @Autowired
+    private SysRoleDeptDao sysRoleDeptDao;
+
+    @Override
+    @Transactional
+    public void saveOrUpdate(Long roleId, List<Long> deptIdList) {
+        //先删除角色与菜单关系
+        sysRoleDeptDao.delete(roleId);
+
+        if (deptIdList.size() == 0) {
+            return;
+        }
+
+        //保存角色与菜单关系
+        Map<String, Object> map = new HashMap<>();
+        map.put("roleId", roleId);
+        map.put("deptIdList", deptIdList);
+        sysRoleDeptDao.save(map);
+    }
+
+    @Override
+    public List<Long> queryDeptIdList(Long roleId) {
+        return sysRoleDeptDao.queryDeptIdList(roleId);
+    }
+
+    @Override
+    public List<Long> queryDeptIdListByUserId(Long userId) {
+        return sysRoleDeptDao.queryDeptIdListByUserId(userId);
+    }
+
+}

+ 52 - 0
kmall-common/src/main/java/com/kmall/common/service/impl/SysRoleMenuServiceImpl.java

@@ -0,0 +1,52 @@
+package com.kmall.common.service.impl;
+
+import com.kmall.common.dao.SysRoleMenuDao;
+import com.kmall.common.service.SysRoleMenuService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 角色与菜单对应关系
+ *
+ * @author Scott
+ * @email
+ * @date 2016年9月18日 上午9:44:35
+ */
+@Service("sysRoleMenuService")
+public class SysRoleMenuServiceImpl implements SysRoleMenuService {
+    @Autowired
+    private SysRoleMenuDao sysRoleMenuDao;
+
+    @Override
+    @Transactional
+    public void saveOrUpdate(Long roleId, List<Long> menuIdList) {
+        if (menuIdList.size() == 0) {
+            return;
+        }
+        //先删除角色与菜单关系
+        sysRoleMenuDao.delete(roleId);
+
+        //保存角色与菜单关系
+        Map<String, Object> map = new HashMap<>();
+        map.put("roleId", roleId);
+        map.put("menuIdList", menuIdList);
+        sysRoleMenuDao.save(map);
+    }
+
+    @Override
+    public List<Long> queryMenuIdList(Long roleId) {
+        return sysRoleMenuDao.queryMenuIdList(roleId);
+    }
+
+    @Override
+    public int delete(Long roleId) {
+        return sysRoleMenuDao.delete(roleId);
+    }
+
+}

+ 142 - 0
kmall-common/src/main/java/com/kmall/common/service/impl/SysRoleServiceImpl.java

@@ -0,0 +1,142 @@
+package com.kmall.common.service.impl;
+
+import com.kmall.common.dao.SysRoleDao;
+import com.kmall.common.dao.SysUserRoleDao;
+import com.kmall.common.entity.SysRoleEntity;
+import com.kmall.common.entity.SysUserRoleEntity;
+import com.kmall.common.service.SysRoleDeptService;
+import com.kmall.common.service.SysRoleMenuService;
+import com.kmall.common.service.SysRoleService;
+import com.kmall.common.service.SysUserService;
+import com.kmall.common.utils.Constant;
+import com.kmall.common.utils.RRException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 角色
+ *
+ * @author Scott
+ * @email
+ * @date 2016年9月18日 上午9:45:12
+ */
+@Service("sysRoleService")
+public class SysRoleServiceImpl implements SysRoleService {
+    @Autowired
+    private SysRoleDao sysRoleDao;
+    @Autowired
+    private SysUserRoleDao sysUserRoleDao;
+    @Autowired
+    private SysRoleMenuService sysRoleMenuService;
+    @Autowired
+    private SysUserService sysUserService;
+    @Autowired
+    private SysRoleDeptService sysRoleDeptService;
+
+    @Override
+    public SysRoleEntity queryObject(Long roleId) {
+        return sysRoleDao.queryObject(roleId);
+    }
+
+    @Override
+    public List<SysRoleEntity> queryList(Map<String, Object> map) {
+        return sysRoleDao.queryList(map);
+    }
+
+    @Override
+    public int queryTotal(Map<String, Object> map) {
+        return sysRoleDao.queryTotal(map);
+    }
+
+    @Override
+    @Transactional
+    public void save(SysRoleEntity role) {
+        if (role.getMenuIdList() == null || role.getMenuIdList().size() <= 0) {
+            throw new RRException("功能权不能为空!");
+        }
+
+        role.setCreateTime(new Date());
+        sysRoleDao.save(role);
+
+        //检查权限是否越权
+        checkPrems(role);
+
+        //保存角色与菜单关系
+        sysRoleMenuService.saveOrUpdate(role.getRoleId(), role.getMenuIdList());
+
+        //保存角色与部门关系
+        //sysRoleDeptService.saveOrUpdate(role.getRoleId(), role.getDeptIdList());
+    }
+
+    @Override
+    @Transactional
+    public void update(SysRoleEntity role) {
+        if (role.getMenuIdList() == null || role.getMenuIdList().size() <= 0) {
+            throw new RRException("功能权不能为空!");
+        }
+
+        sysRoleDao.update(role);
+
+        //检查权限是否越权
+        checkPrems(role);
+
+        //更新角色与菜单关系
+        sysRoleMenuService.saveOrUpdate(role.getRoleId(), role.getMenuIdList());
+        //保存角色与部门关系
+        //sysRoleDeptService.saveOrUpdate(role.getRoleId(), role.getDeptIdList());
+    }
+
+    @Override
+    @Transactional
+    public void deleteBatch(Long[] roleIds) {
+        for (Long id : roleIds) {
+            SysRoleEntity role = queryObject(id);
+
+            Map<String, Object> map = new HashMap<>();
+            map.put("roleId", id);
+            List<SysUserRoleEntity> userEntityList = sysUserRoleDao.queryList(map);
+
+            if (userEntityList != null && userEntityList.size() > 0) {
+                throw new RRException("角色【" + role.getRoleName() + "】还有【" + userEntityList.size() + "】个管理员正在使用,删除失败!");
+            }
+            sysUserRoleDao.deleteByRoleId(id);
+            sysRoleMenuService.delete(id);
+            delete(id);
+        }
+    }
+
+    @Override
+    public void delete(Long roleId) {
+        sysRoleDao.delete(roleId);
+    }
+
+    @Override
+    public List<Long> queryRoleIdList(Long createUserId) {
+        return sysRoleDao.queryRoleIdList(createUserId);
+    }
+
+    /**
+     * 检查权限是否越权
+     */
+    private void checkPrems(SysRoleEntity role) {
+        //如果不是超级管理员,则需要判断角色的权限是否超过自己的权限
+        if (role.getCreateUserId() == Constant.SUPER_ADMIN) {
+            return;
+        }
+
+        //查询用户所拥有的菜单列表
+        List<Long> menuIdList = sysUserService.queryAllMenuId(role.getCreateUserId());
+
+        //判断是否越权
+        if (!menuIdList.containsAll(role.getMenuIdList())) {
+            throw new RRException("新增角色的权限,已超出你的权限范围");
+        }
+    }
+}

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