Jelajahi Sumber

支持用户数据导入

RuoYi 6 tahun lalu
induk
melakukan
ec8c7f1d79
20 mengubah file dengan 1043 tambahan dan 209 penghapusan
  1. 1 1
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysJobController.java
  2. 1 1
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysJobLogController.java
  3. 1 1
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java
  4. 1 1
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java
  5. 1 1
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java
  6. 1 1
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java
  7. 1 1
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java
  8. 1 1
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java
  9. 1 1
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
  10. 24 2
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
  11. 44 9
      ruoyi-admin/src/main/resources/static/ruoyi/css/ry-ui.css
  12. 63 1
      ruoyi-admin/src/main/resources/static/ruoyi/js/ry-ui.js
  13. 1 1
      ruoyi-admin/src/main/resources/templates/system/user/edit.html
  14. 18 0
      ruoyi-admin/src/main/resources/templates/system/user/user.html
  15. 22 1
      ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java
  16. 406 0
      ruoyi-common/src/main/java/com/ruoyi/common/reflect/ReflectUtils.java
  17. 349 164
      ruoyi-common/src/main/java/com/ruoyi/common/utils/ExcelUtil.java
  18. 5 0
      ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUser.java
  19. 8 0
      ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
  20. 94 22
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java

+ 1 - 1
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysJobController.java

@@ -59,7 +59,7 @@ public class SysJobController extends BaseController
     {
         List<SysJob> list = jobService.selectJobList(job);
         ExcelUtil<SysJob> util = new ExcelUtil<SysJob>(SysJob.class);
-        return util.exportExcel(list, "job");
+        return util.exportExcel(list, "定时任务");
     }
 
     @Log(title = "定时任务", businessType = BusinessType.DELETE)

+ 1 - 1
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysJobLogController.java

@@ -58,7 +58,7 @@ public class SysJobLogController extends BaseController
     {
         List<SysJobLog> list = jobLogService.selectJobLogList(jobLog);
         ExcelUtil<SysJobLog> util = new ExcelUtil<SysJobLog>(SysJobLog.class);
-        return util.exportExcel(list, "jobLog");
+        return util.exportExcel(list, "调度日志");
     }
 
     @Log(title = "调度日志", businessType = BusinessType.DELETE)

+ 1 - 1
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java

@@ -56,7 +56,7 @@ public class SysLogininforController extends BaseController
     {
         List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
         ExcelUtil<SysLogininfor> util = new ExcelUtil<SysLogininfor>(SysLogininfor.class);
-        return util.exportExcel(list, "logininfor");
+        return util.exportExcel(list, "登陆日志");
     }
 
     @RequiresPermissions("monitor:logininfor:remove")

+ 1 - 1
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java

@@ -58,7 +58,7 @@ public class SysOperlogController extends BaseController
     {
         List<SysOperLog> list = operLogService.selectOperLogList(operLog);
         ExcelUtil<SysOperLog> util = new ExcelUtil<SysOperLog>(SysOperLog.class);
-        return util.exportExcel(list, "operLog");
+        return util.exportExcel(list, "操作日志");
     }
 
     @RequiresPermissions("monitor:operlog:remove")

+ 1 - 1
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java

@@ -62,7 +62,7 @@ public class SysConfigController extends BaseController
     {
         List<SysConfig> list = configService.selectConfigList(config);
         ExcelUtil<SysConfig> util = new ExcelUtil<SysConfig>(SysConfig.class);
-        return util.exportExcel(list, "config");
+        return util.exportExcel(list, "参数数据");
     }
 
     /**

+ 1 - 1
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java

@@ -59,7 +59,7 @@ public class SysDictDataController extends BaseController
     {
         List<SysDictData> list = dictDataService.selectDictDataList(dictData);
         ExcelUtil<SysDictData> util = new ExcelUtil<SysDictData>(SysDictData.class);
-        return util.exportExcel(list, "dictData");
+        return util.exportExcel(list, "字典数据");
     }
 
     /**

+ 1 - 1
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java

@@ -60,7 +60,7 @@ public class SysDictTypeController extends BaseController
 
         List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
         ExcelUtil<SysDictType> util = new ExcelUtil<SysDictType>(SysDictType.class);
-        return util.exportExcel(list, "dictType");
+        return util.exportExcel(list, "字典类型");
     }
 
     /**

+ 1 - 1
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java

@@ -59,7 +59,7 @@ public class SysPostController extends BaseController
     {
         List<SysPost> list = postService.selectPostList(post);
         ExcelUtil<SysPost> util = new ExcelUtil<SysPost>(SysPost.class);
-        return util.exportExcel(list, "post");
+        return util.exportExcel(list, "岗位数据");
     }
 
     @RequiresPermissions("system:post:remove")

+ 1 - 1
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java

@@ -60,7 +60,7 @@ public class SysRoleController extends BaseController
     {
         List<SysRole> list = roleService.selectRoleList(role);
         ExcelUtil<SysRole> util = new ExcelUtil<SysRole>(SysRole.class);
-        return util.exportExcel(list, "role");
+        return util.exportExcel(list, "角色数据");
     }
 
     /**

+ 24 - 2
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java

@@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartFile;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.base.AjaxResult;
 import com.ruoyi.common.enums.BusinessType;
@@ -19,11 +20,11 @@ import com.ruoyi.common.utils.ExcelUtil;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.framework.shiro.service.SysPasswordService;
 import com.ruoyi.framework.util.ShiroUtils;
+import com.ruoyi.framework.web.base.BaseController;
 import com.ruoyi.system.domain.SysUser;
 import com.ruoyi.system.service.ISysPostService;
 import com.ruoyi.system.service.ISysRoleService;
 import com.ruoyi.system.service.ISysUserService;
-import com.ruoyi.framework.web.base.BaseController;
 
 /**
  * 用户信息
@@ -73,7 +74,28 @@ public class SysUserController extends BaseController
     {
         List<SysUser> list = userService.selectUserList(user);
         ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
-        return util.exportExcel(list, "user");
+        return util.exportExcel(list, "用户数据");
+    }
+
+    @Log(title = "用户管理", businessType = BusinessType.IMPORT)
+    @RequiresPermissions("system:user:import")
+    @PostMapping("/importData")
+    @ResponseBody
+    public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
+    {
+        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
+        List<SysUser> userList = util.importExcel(file.getInputStream());
+        String message = userService.importUser(userList, updateSupport);
+        return AjaxResult.success(message);
+    }
+
+    @RequiresPermissions("system:user:view")
+    @GetMapping("/importTemplate")
+    @ResponseBody
+    public AjaxResult importTemplate()
+    {
+        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
+        return util.importTemplateExcel("用户数据");
     }
 
     /**

+ 44 - 9
ruoyi-admin/src/main/resources/static/ruoyi/css/ry-ui.css

@@ -3,7 +3,42 @@
  * Copyright (c) 2018 ruoyi
  */
 
-/** 用户管理 样式布局 */
+/** 基础通用 **/
+.pt5 {
+	padding-top: 5px;
+}
+.pr5 {
+	padding-right: 5px;
+}
+.pb5 {
+	padding-bottom: 5px;
+}
+.mt10 {
+	margin-top: 10px;
+}
+.mr10 {
+	margin-right: 10px;
+}
+.mb10 {
+	margin-bottom: 10px;
+}
+.ml0 {
+	margin-left: 10px;
+}
+.mt20 {
+	margin-top: 20px;
+}
+.mr20 {
+	margin-right: 20px;
+}
+.mb20 {
+	margin-bottom: 20px;
+}
+.ml20 {
+	margin-left: 20px;
+}
+
+/** 用户管理 样式布局 **/
 .box {
 	position: relative;
 	border-radius: 3px;
@@ -91,7 +126,7 @@
 	margin: 5px 0 5px -25px
 }
 
-/** select2 样式修改 */
+/** select2 样式修改 **/
 .select2-container--default .select2-selection--multiple .select2-selection__choice {
 	background-color: #1AB394;
 	border-color: #1AB394;
@@ -112,7 +147,7 @@
 	padding-right: 10px
 }
 
-/** 表单验证 样式布局 */
+/** 表单验证 样式布局 **/
 label.error {
 	position: absolute;
 	right: 18px;
@@ -143,7 +178,7 @@ label.error {
 	max-width: none;
 }
 
-/** 复选框&单选框  */
+/** 复选框&单选框  **/
 .check-box,.radio-box {
 	display: inline-block;
 	box-sizing: border-box;
@@ -160,7 +195,7 @@ label.error {
 	left: 0
 }
 
-/* iCheck */
+/** iCheck **/
 .icheckbox-blue,.iradio-blue {
 	display: block;
 	margin: 0;
@@ -214,7 +249,7 @@ label.error {
 	background-position: -180px 0
 }
 
-/** 遮罩层 */
+/** 遮罩层 **/
 .loaderbox {
 	display: inline-block;
 	min-width: 125px;
@@ -281,7 +316,7 @@ label.error {
 	}
 }
 
-/** 表单查询条件 */
+/** 表单查询条件 **/
 ul {
 	margin: 0;
 	padding: 0;
@@ -409,7 +444,7 @@ label {
 	cursor: pointer;
 }
 
-/** 表格查询数据 */
+/** 表格查询数据 **/
 .table-striped {
 	min-height: 75%;
 }
@@ -437,7 +472,7 @@ label {
 	border: 1px solid #ddd!important
 }
 
-/** 首页样式 */
+/** 首页样式 **/
 .ax_close_max {
 	position: fixed;
 	top: 5px;

+ 63 - 1
ruoyi-admin/src/main/resources/static/ruoyi/js/ry-ui.js

@@ -92,7 +92,7 @@
     		    }
     		    $("#bootstrap-table").bootstrapTable('refresh', params);
     		},
-    		// 下载-默认第一个form
+    		// 导出数据
     		exportExcel: function(formId) {
     			var currentId = $.common.isEmpty(formId) ? $('form').attr('id') : formId;
     			$.modal.loading("正在导出数据,请稍后...");
@@ -105,6 +105,64 @@
     				$.modal.closeLoading();
     			});
     		},
+    		// 下载模板
+    		importTemplate: function() {
+    			$.get($.table._option.importTemplateUrl, function(result) {
+    				if (result.code == web_status.SUCCESS) {
+    			        window.location.href = ctx + "common/download?fileName=" + result.msg + "&delete=" + true;
+    				} else {
+    					$.modal.alertError(result.msg);
+    				}
+    			});
+            },
+            // 导入数据
+            importExcel: function(formId) {
+            	var currentId = $.common.isEmpty(formId) ? 'importForm' : formId;
+            	$.form.reset(currentId);
+            	layer.open({
+            		type: 1,
+            		area: ['400px'],
+            		fix: false,
+            		//不固定
+            		maxmin: true,
+            		shade: 0.3,
+            		title: '导入' + $.table._option.modalName + '数据',
+            		content: $('#' + currentId),
+            		btn: ['<i class="fa fa-check"></i> 导入', '<i class="fa fa-remove"></i> 取消'],
+            		// 弹层外区域关闭
+            		shadeClose: true,
+            		btn1: function(index, layero){
+            			var file = layero.find('#file').val();
+            			if (file == '' || (!$.common.endWith(file, '.xls') && !$.common.endWith(file, '.xlsx'))){
+            				$.modal.msgWarning("请选择后缀为 “xls”或“xlsx”的文件。");
+            				return false;
+            			}
+            			var index = layer.load(2, {shade: false});
+            			var url = prefix + "/importData";
+            			var formData = new FormData();
+            			formData.append("file", $('#file')[0].files[0]);
+            			formData.append("updateSupport", $("input[name='updateSupport']").is(':checked'));
+            			$.ajax({
+            				url: url,
+            				data: formData,
+            				cache: false,
+            				contentType: false,
+            				processData: false,
+            				type: 'POST',
+            				success: function (result) {
+            					if (result.code == web_status.SUCCESS) {
+            						$.modal.closeAll();
+            						$.modal.alertSuccess(result.msg);
+            						$.table.refresh();
+            					} else {
+            						layer.close(index);
+            						$.modal.alertError(result.msg);
+            					}
+            				}
+            			});
+            		}
+            	});
+            },
             // 刷新表格
             refresh: function() {
                 $("#bootstrap-table").bootstrapTable('refresh', {
@@ -288,6 +346,10 @@
             	var index = parent.layer.getFrameIndex(window.name);
                 parent.layer.close(index);
             },
+            // 关闭全部窗体
+            closeAll: function () {
+                layer.closeAll();
+            },
             // 确认窗体
             confirm: function (content, callBack) {
             	layer.confirm(content, {

+ 1 - 1
ruoyi-admin/src/main/resources/templates/system/user/edit.html

@@ -184,7 +184,7 @@
 
         /*用户管理-修改-选择部门树*/
         function selectDeptTree() {
-        	var deptId = $("#treeId").val();
+        	var deptId = $.common.isEmpty($("#treeId").val()) ? "100" : $("#treeId").val();
             var url = ctx + "system/dept/selectDeptTree/" + deptId;
 		    var options = {
 				title: '选择部门',

+ 18 - 0
ruoyi-admin/src/main/resources/templates/system/user/user.html

@@ -73,6 +73,9 @@
 	            <a class="btn btn-danger btn-del disabled" onclick="$.operate.removeAll()" shiro:hasPermission="system:user:remove">
 	                <i class="fa fa-remove"></i> 删除
 	            </a>
+	            <a class="btn btn-info" onclick="$.table.importExcel()" shiro:hasPermission="system:user:import">
+		            <i class="fa fa-download"></i> 导入
+		        </a>
 	            <a class="btn btn-warning" onclick="$.table.exportExcel()" shiro:hasPermission="system:user:export">
 		            <i class="fa fa-download"></i> 导出
 		        </a>
@@ -83,6 +86,7 @@
 			</div>
 		</div>
 	</div>
+	
 	<div th:include="include :: footer"></div>
 	<script th:src="@{/ajax/libs/jquery-layout/jquery.layout-latest.js}"></script>
 	<script th:src="@{/ajax/libs/jquery-ztree/3.5/js/jquery.ztree.all-3.5.js}"></script>
@@ -106,6 +110,8 @@
 		        updateUrl: prefix + "/edit/{id}",
 		        removeUrl: prefix + "/remove",
 		        exportUrl: prefix + "/export",
+		        importUrl: prefix + "/importData",
+		        importTemplateUrl: prefix + "/importTemplate",
 		        sortName: "createTime",
 		        sortOrder: "desc",
 		        modalName: "用户",
@@ -214,4 +220,16 @@
 		}
 	</script>
 </body>
+<form id="importForm" enctype="multipart/form-data" class="mt20 mb10" style="display: none;">
+	<div class="col-xs-offset-1">
+		<input type="file" id="file" name="file"/>
+		<div class="mt10 pt5">
+			<input type="checkbox" id="updateSupport" name="updateSupport" title="如果登录账户已经存在,更新这条数据。"> 是否更新已经存在的用户数据
+			 &nbsp;	<a onclick="$.table.importTemplate()" class="btn btn-default btn-xs"><i class="fa fa-file-excel-o"></i> 下载模板</a>
+		</div>
+		<font color="red" class="pull-left mt10">
+			提示:仅允许导入“xls”或“xlsx”格式文件!
+		</font>
+	</div>
+</form>
 </html>

+ 22 - 1
ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java

@@ -37,7 +37,7 @@ public @interface Excel
     /**
      * 导出时在excel中每个列的宽 单位为字符
      */
-    public double width() default 20;
+    public double width() default 16;
 
     /**
      * 文字后缀,如% 90 变成90%
@@ -68,4 +68,25 @@ public @interface Excel
      * 另一个类中的属性名称,支持多级获取,以小数点隔开
      */
     public String targetAttr() default "";
+
+    /**
+     * 字段类型( 1:仅导出;2:仅导入)
+     */
+    Type type() default Type.EXPORT;
+
+    public enum Type
+    {
+        EXPORT(1), IMPORT(2);
+        private final int value;
+
+        Type(int value)
+        {
+            this.value = value;
+        }
+
+        public int value()
+        {
+            return this.value;
+        }
+    }
 }

+ 406 - 0
ruoyi-common/src/main/java/com/ruoyi/common/reflect/ReflectUtils.java

@@ -0,0 +1,406 @@
+package com.ruoyi.common.reflect;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Date;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+import org.apache.poi.ss.usermodel.DateUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.ruoyi.common.support.Convert;
+import com.ruoyi.common.utils.DateUtils;
+
+/**
+ * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
+ * 
+ * @author ruoyi
+ */
+@SuppressWarnings("rawtypes")
+public class ReflectUtils
+{
+    private static final String SETTER_PREFIX = "set";
+
+    private static final String GETTER_PREFIX = "get";
+
+    private static final String CGLIB_CLASS_SEPARATOR = "$$";
+
+    private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class);
+
+    /**
+     * 调用Getter方法.
+     * 支持多级,如:对象名.对象名.方法
+     */
+    @SuppressWarnings("unchecked")
+    public static <E> E invokeGetter(Object obj, String propertyName)
+    {
+        Object object = obj;
+        for (String name : StringUtils.split(propertyName, "."))
+        {
+            String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
+            object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
+        }
+        return (E) object;
+    }
+
+    /**
+     * 调用Setter方法, 仅匹配方法名。
+     * 支持多级,如:对象名.对象名.方法
+     */
+    public static <E> void invokeSetter(Object obj, String propertyName, E value)
+    {
+        Object object = obj;
+        String[] names = StringUtils.split(propertyName, ".");
+        for (int i = 0; i < names.length; i++)
+        {
+            if (i < names.length - 1)
+            {
+                String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
+                object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
+            }
+            else
+            {
+                String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
+                invokeMethodByName(object, setterMethodName, new Object[] { value });
+            }
+        }
+    }
+
+    /**
+     * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
+     */
+    @SuppressWarnings("unchecked")
+    public static <E> E getFieldValue(final Object obj, final String fieldName)
+    {
+        Field field = getAccessibleField(obj, fieldName);
+        if (field == null)
+        {
+            logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
+            return null;
+        }
+        E result = null;
+        try
+        {
+            result = (E) field.get(obj);
+        }
+        catch (IllegalAccessException e)
+        {
+            logger.error("不可能抛出的异常{}", e.getMessage());
+        }
+        return result;
+    }
+
+    /**
+     * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
+     */
+    public static <E> void setFieldValue(final Object obj, final String fieldName, final E value)
+    {
+        Field field = getAccessibleField(obj, fieldName);
+        if (field == null)
+        {
+            // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
+            logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
+            return;
+        }
+        try
+        {
+            field.set(obj, value);
+        }
+        catch (IllegalAccessException e)
+        {
+            logger.error("不可能抛出的异常: {}", e.getMessage());
+        }
+    }
+
+    /**
+     * 直接调用对象方法, 无视private/protected修饰符.
+     * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
+     * 同时匹配方法名+参数类型,
+     */
+    @SuppressWarnings("unchecked")
+    public static <E> E invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
+            final Object[] args)
+    {
+        if (obj == null || methodName == null)
+        {
+            return null;
+        }
+        Method method = getAccessibleMethod(obj, methodName, parameterTypes);
+        if (method == null)
+        {
+            logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
+            return null;
+        }
+        try
+        {
+            return (E) method.invoke(obj, args);
+        }
+        catch (Exception e)
+        {
+            String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
+            throw convertReflectionExceptionToUnchecked(msg, e);
+        }
+    }
+
+    /**
+     * 直接调用对象方法, 无视private/protected修饰符,
+     * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
+     * 只匹配函数名,如果有多个同名函数调用第一个。
+     */
+    @SuppressWarnings("unchecked")
+    public static <E> E invokeMethodByName(final Object obj, final String methodName, final Object[] args)
+    {
+        Method method = getAccessibleMethodByName(obj, methodName, args.length);
+        if (method == null)
+        {
+            // 如果为空不报错,直接返回空。
+            logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
+            return null;
+        }
+        try
+        {
+            // 类型转换(将参数数据类型转换为目标方法参数类型)
+            Class<?>[] cs = method.getParameterTypes();
+            for (int i = 0; i < cs.length; i++)
+            {
+                if (args[i] != null && !args[i].getClass().equals(cs[i]))
+                {
+                    if (cs[i] == String.class)
+                    {
+                        args[i] = Convert.toStr(args[i]);
+                        if (StringUtils.endsWith((String) args[i], ".0"))
+                        {
+                            args[i] = StringUtils.substringBefore((String) args[i], ".0");
+                        }
+                    }
+                    else if (cs[i] == Integer.class)
+                    {
+                        args[i] = Convert.toInt(args[i]);
+                    }
+                    else if (cs[i] == Long.class)
+                    {
+                        args[i] = Convert.toLong(args[i]);
+                    }
+                    else if (cs[i] == Double.class)
+                    {
+                        args[i] = Convert.toDouble(args[i]);
+                    }
+                    else if (cs[i] == Float.class)
+                    {
+                        args[i] = Convert.toFloat(args[i]);
+                    }
+                    else if (cs[i] == Date.class)
+                    {
+                        if (args[i] instanceof String)
+                        {
+                            args[i] = DateUtils.parseDate(args[i]);
+                        }
+                        else
+                        {
+                            args[i] = DateUtil.getJavaDate((Double) args[i]);
+                        }
+                    }
+                }
+            }
+            return (E) method.invoke(obj, args);
+        }
+        catch (Exception e)
+        {
+            String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
+            throw convertReflectionExceptionToUnchecked(msg, e);
+        }
+    }
+
+    /**
+     * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
+     * 如向上转型到Object仍无法找到, 返回null.
+     */
+    public static Field getAccessibleField(final Object obj, final String fieldName)
+    {
+        // 为空不报错。直接返回 null
+        if (obj == null)
+        {
+            return null;
+        }
+        Validate.notBlank(fieldName, "fieldName can't be blank");
+        for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass())
+        {
+            try
+            {
+                Field field = superClass.getDeclaredField(fieldName);
+                makeAccessible(field);
+                return field;
+            }
+            catch (NoSuchFieldException e)
+            {
+                continue;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
+     * 如向上转型到Object仍无法找到, 返回null.
+     * 匹配函数名+参数类型。
+     * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
+     */
+    public static Method getAccessibleMethod(final Object obj, final String methodName,
+            final Class<?>... parameterTypes)
+    {
+        // 为空不报错。直接返回 null
+        if (obj == null)
+        {
+            return null;
+        }
+        Validate.notBlank(methodName, "methodName can't be blank");
+        for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass())
+        {
+            try
+            {
+                Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
+                makeAccessible(method);
+                return method;
+            }
+            catch (NoSuchMethodException e)
+            {
+                continue;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
+     * 如向上转型到Object仍无法找到, 返回null.
+     * 只匹配函数名。
+     * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
+     */
+    public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum)
+    {
+        // 为空不报错。直接返回 null
+        if (obj == null)
+        {
+            return null;
+        }
+        Validate.notBlank(methodName, "methodName can't be blank");
+        for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass())
+        {
+            Method[] methods = searchType.getDeclaredMethods();
+            for (Method method : methods)
+            {
+                if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum)
+                {
+                    makeAccessible(method);
+                    return method;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
+     */
+    public static void makeAccessible(Method method)
+    {
+        if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
+                && !method.isAccessible())
+        {
+            method.setAccessible(true);
+        }
+    }
+
+    /**
+     * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
+     */
+    public static void makeAccessible(Field field)
+    {
+        if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())
+                || Modifier.isFinal(field.getModifiers())) && !field.isAccessible())
+        {
+            field.setAccessible(true);
+        }
+    }
+
+    /**
+     * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
+     * 如无法找到, 返回Object.class.
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> Class<T> getClassGenricType(final Class clazz)
+    {
+        return getClassGenricType(clazz, 0);
+    }
+
+    /**
+     * 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
+     * 如无法找到, 返回Object.class.
+     */
+    public static Class getClassGenricType(final Class clazz, final int index)
+    {
+        Type genType = clazz.getGenericSuperclass();
+
+        if (!(genType instanceof ParameterizedType))
+        {
+            logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType");
+            return Object.class;
+        }
+
+        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
+
+        if (index >= params.length || index < 0)
+        {
+            logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
+                    + params.length);
+            return Object.class;
+        }
+        if (!(params[index] instanceof Class))
+        {
+            logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
+            return Object.class;
+        }
+
+        return (Class) params[index];
+    }
+
+    public static Class<?> getUserClass(Object instance)
+    {
+        if (instance == null)
+        {
+            throw new RuntimeException("Instance must not be null");
+        }
+        Class clazz = instance.getClass();
+        if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR))
+        {
+            Class<?> superClass = clazz.getSuperclass();
+            if (superClass != null && !Object.class.equals(superClass))
+            {
+                return superClass;
+            }
+        }
+        return clazz;
+
+    }
+
+    /**
+     * 将反射时的checked exception转换为unchecked exception.
+     */
+    public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e)
+    {
+        if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
+                || e instanceof NoSuchMethodException)
+        {
+            return new IllegalArgumentException(msg, e);
+        }
+        else if (e instanceof InvocationTargetException)
+        {
+            return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException());
+        }
+        return new RuntimeException(msg, e);
+    }
+}

+ 349 - 164
ruoyi-common/src/main/java/com/ruoyi/common/utils/ExcelUtil.java

@@ -7,7 +7,7 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
-import java.text.SimpleDateFormat;
+import java.text.DecimalFormat;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
@@ -15,30 +15,32 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 import org.apache.poi.hssf.usermodel.DVConstraint;
-import org.apache.poi.hssf.usermodel.HSSFCell;
-import org.apache.poi.hssf.usermodel.HSSFCellStyle;
 import org.apache.poi.hssf.usermodel.HSSFDataValidation;
+import org.apache.poi.hssf.usermodel.HSSFDateUtil;
 import org.apache.poi.hssf.usermodel.HSSFFont;
-import org.apache.poi.hssf.usermodel.HSSFRow;
-import org.apache.poi.hssf.usermodel.HSSFSheet;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.hssf.util.HSSFColor.HSSFColorPredefined;
 import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
 import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.DateUtil;
 import org.apache.poi.ss.usermodel.FillPatternType;
+import org.apache.poi.ss.usermodel.Font;
 import org.apache.poi.ss.usermodel.HorizontalAlignment;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.apache.poi.ss.usermodel.VerticalAlignment;
 import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.ss.usermodel.WorkbookFactory;
 import org.apache.poi.ss.util.CellRangeAddressList;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.annotation.Excel.Type;
 import com.ruoyi.common.base.AjaxResult;
 import com.ruoyi.common.config.Global;
 import com.ruoyi.common.exception.BusinessException;
+import com.ruoyi.common.reflect.ReflectUtils;
 
 /**
  * Excel相关处理
@@ -49,6 +51,44 @@ public class ExcelUtil<T>
 {
     private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);
 
+    /**
+     * Excel sheet最大行数,默认65536
+     */
+    public static final int sheetSize = 65536;
+
+    /**
+     * 工作表名称
+     */
+    private String sheetName;
+
+    /**
+     * 导出类型(EXPORT:导出数据;IMPORT:导入模板)
+     */
+    private Type type;
+
+    /**
+     * 工作薄对象
+     */
+    private Workbook wb;
+
+    /**
+     * 工作表对象
+     */
+    private Sheet sheet;
+
+    /**
+     * 导入导出数据列表
+     */
+    private List<T> list;
+
+    /**
+     * 注解列表
+     */
+    private List<Field> fields;
+
+    /**
+     * 实体对象
+     */
     public Class<T> clazz;
 
     public ExcelUtil(Class<T> clazz)
@@ -56,15 +96,28 @@ public class ExcelUtil<T>
         this.clazz = clazz;
     }
 
+    public void init(List<T> list, String sheetName, Type type)
+    {
+        if (list == null)
+        {
+            list = new ArrayList<T>();
+        }
+        this.list = list;
+        this.sheetName = sheetName;
+        this.type = type;
+        createExcelField();
+        createWorkbook();
+    }
+
     /**
      * 对excel表单默认第一个索引名转换成list
      * 
      * @param input 输入流
      * @return 转换后集合
      */
-    public List<T> importExcel(InputStream input) throws Exception
+    public List<T> importExcel(InputStream is) throws Exception
     {
-        return importExcel(StringUtils.EMPTY, input);
+        return importExcel(StringUtils.EMPTY, is);
     }
 
     /**
@@ -74,21 +127,20 @@ public class ExcelUtil<T>
      * @param input 输入流
      * @return 转换后集合
      */
-    public List<T> importExcel(String sheetName, InputStream input) throws Exception
+    public List<T> importExcel(String sheetName, InputStream is) throws Exception
     {
+        this.wb = new XSSFWorkbook(is);
         List<T> list = new ArrayList<T>();
-
-        Workbook workbook = WorkbookFactory.create(input);
         Sheet sheet = null;
         if (StringUtils.isNotEmpty(sheetName))
         {
             // 如果指定sheet名,则取指定sheet中的内容.
-            sheet = workbook.getSheet(sheetName);
+            sheet = wb.getSheet(sheetName);
         }
         else
         {
             // 如果传入的sheet名不存在则默认指向第1个sheet.
-            sheet = workbook.getSheetAt(0);
+            sheet = wb.getSheetAt(0);
         }
 
         if (sheet == null)
@@ -123,88 +175,77 @@ public class ExcelUtil<T>
                 Row row = sheet.getRow(i);
                 int cellNum = serialNum;
                 T entity = null;
-                for (int j = 0; j < cellNum; j++)
+                for (int column = 0; column < cellNum; column++)
                 {
-                    Cell cell = row.getCell(j);
-                    if (cell == null)
-                    {
-                        continue;
-                    }
-                    else
-                    {
-                        // 先设置Cell的类型,然后就可以把纯数字作为String类型读进来了
-                        row.getCell(j).setCellType(CellType.STRING);
-                        cell = row.getCell(j);
-                    }
-
-                    String c = cell.getStringCellValue();
-                    if (StringUtils.isEmpty(c))
-                    {
-                        continue;
-                    }
+                    Object val = this.getCellValue(row, column);
 
                     // 如果不存在实例则新建.
                     entity = (entity == null ? clazz.newInstance() : entity);
                     // 从map中得到对应列的field.
-                    Field field = fieldsMap.get(j + 1);
+                    Field field = fieldsMap.get(column + 1);
                     // 取得类型,并根据对象类型设置值.
                     Class<?> fieldType = field.getType();
                     if (String.class == fieldType)
                     {
-                        field.set(entity, String.valueOf(c));
+                        String s = String.valueOf(val.toString());
+                        if (StringUtils.endsWith(s, ".0"))
+                        {
+                            val = StringUtils.substringBefore(s, ".0");
+                        }
+                        else
+                        {
+                            val = String.valueOf(val.toString());
+                        }
                     }
                     else if ((Integer.TYPE == fieldType) || (Integer.class == fieldType))
                     {
-                        field.set(entity, Integer.parseInt(c));
+                        val = Double.valueOf(val.toString()).intValue();
                     }
                     else if ((Long.TYPE == fieldType) || (Long.class == fieldType))
                     {
-                        field.set(entity, Long.valueOf(c));
+                        val = Double.valueOf(val.toString()).longValue();
                     }
-                    else if ((Float.TYPE == fieldType) || (Float.class == fieldType))
-                    {
-                        field.set(entity, Float.valueOf(c));
-                    }
-                    else if ((Short.TYPE == fieldType) || (Short.class == fieldType))
+                    else if ((Double.TYPE == fieldType) || (Double.class == fieldType))
                     {
-                        field.set(entity, Short.valueOf(c));
+                        val = Double.valueOf(val.toString());
                     }
-                    else if ((Double.TYPE == fieldType) || (Double.class == fieldType))
+                    else if ((Float.TYPE == fieldType) || (Float.class == fieldType))
                     {
-                        field.set(entity, Double.valueOf(c));
+                        val = Float.valueOf(val.toString());
                     }
-                    else if (Character.TYPE == fieldType)
+                    else if (Date.class == fieldType)
                     {
-                        if ((c != null) && (c.length() > 0))
+                        if (val instanceof String)
+                        {
+                            val = DateUtils.parseDate(val);
+                        }
+                        else if (val instanceof Double)
                         {
-                            field.set(entity, Character.valueOf(c.charAt(0)));
+                            val = DateUtil.getJavaDate((Double) val);
                         }
                     }
-                    else if (java.util.Date.class == fieldType)
+                    if (StringUtils.isNotNull(fieldType))
                     {
-                        if (cell.getCellTypeEnum() == CellType.NUMERIC)
+                        Excel attr = field.getAnnotation(Excel.class);
+                        if (StringUtils.isNotEmpty(attr.targetAttr()))
                         {
-                            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-                            cell.setCellValue(sdf.format(cell.getNumericCellValue()));
-                            c = sdf.format(cell.getNumericCellValue());
+
+                            ReflectUtils.invokeSetter(entity, field.getName() + "." + attr.targetAttr(), val);
+                        }
+                        else if (StringUtils.isNotEmpty(attr.readConverterExp()))
+                        {
+                            String value = reverseByExp(String.valueOf(val), attr.readConverterExp());
+                            ReflectUtils.invokeSetter(entity, field.getName() + "." + attr.targetAttr(), value);
                         }
                         else
                         {
-                            c = cell.getStringCellValue();
+                            ReflectUtils.invokeSetter(entity, field.getName(), val);
                         }
                     }
-                    else if (java.math.BigDecimal.class == fieldType)
-                    {
-                        c = cell.getStringCellValue();
-                    }
-                }
-                if (entity != null)
-                {
-                    list.add(entity);
                 }
+                list.add(entity);
             }
         }
-
         return list;
     }
 
@@ -216,47 +257,42 @@ public class ExcelUtil<T>
      * @return 结果
      */
     public AjaxResult exportExcel(List<T> list, String sheetName)
+    {
+        this.init(list, sheetName, Type.EXPORT);
+        return exportExcel();
+    }
+
+    /**
+     * 对list数据源将其里面的数据导入到excel表单
+     * 
+     * @param sheetName 工作表的名称
+     * @return 结果
+     */
+    public AjaxResult importTemplateExcel(String sheetName)
+    {
+        this.init(null, sheetName, Type.IMPORT);
+        return exportExcel();
+    }
+
+    /**
+     * 对list数据源将其里面的数据导入到excel表单
+     * 
+     * @return 结果
+     */
+    public AjaxResult exportExcel()
     {
         OutputStream out = null;
-        HSSFWorkbook workbook = null;
         try
         {
-            // 得到所有定义字段
-            Field[] allFields = clazz.getDeclaredFields();
-            List<Field> fields = new ArrayList<Field>();
-            // 得到所有field并存放到一个list中.
-            for (Field field : allFields)
-            {
-                if (field.isAnnotationPresent(Excel.class))
-                {
-                    fields.add(field);
-                }
-            }
-
-            // 产生工作薄对象
-            workbook = new HSSFWorkbook();
-            // excel2003中每个sheet中最多有65536行
-            int sheetSize = 65536;
             // 取出一共有多少个sheet.
             double sheetNo = Math.ceil(list.size() / sheetSize);
             for (int index = 0; index <= sheetNo; index++)
             {
-                // 产生工作表对象
-                HSSFSheet sheet = workbook.createSheet();
-                if (sheetNo == 0)
-                {
-                    workbook.setSheetName(index, sheetName);
-                }
-                else
-                {
-                    // 设置工作表的名称.
-                    workbook.setSheetName(index, sheetName + index);
-                }
-                HSSFRow row;
-                HSSFCell cell; // 产生单元格
+                createSheet(sheetNo, index);
+                Cell cell = null; // 产生单元格
 
                 // 产生一行
-                row = sheet.createRow(0);
+                Row row = sheet.createRow(0);
                 // 写入各个字段的列头名称
                 for (int i = 0; i < fields.size(); i++)
                 {
@@ -266,12 +302,12 @@ public class ExcelUtil<T>
                     cell = row.createCell(i);
                     // 设置列中写入内容为String类型
                     cell.setCellType(CellType.STRING);
-                    HSSFCellStyle cellStyle = workbook.createCellStyle();
+                    CellStyle cellStyle = wb.createCellStyle();
                     cellStyle.setAlignment(HorizontalAlignment.CENTER);
                     cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
                     if (attr.name().indexOf("注:") >= 0)
                     {
-                        HSSFFont font = workbook.createFont();
+                        Font font = wb.createFont();
                         font.setColor(HSSFFont.COLOR_RED);
                         cellStyle.setFont(font);
                         cellStyle.setFillForegroundColor(HSSFColorPredefined.YELLOW.getIndex());
@@ -279,7 +315,7 @@ public class ExcelUtil<T>
                     }
                     else
                     {
-                        HSSFFont font = workbook.createFont();
+                        Font font = wb.createFont();
                         // 粗体显示
                         font.setBold(true);
                         // 选择需要用到的字体格式
@@ -309,72 +345,14 @@ public class ExcelUtil<T>
                         setHSSFValidation(sheet, attr.combo(), 1, 100, i, i);
                     }
                 }
-
-                int startNo = index * sheetSize;
-                int endNo = Math.min(startNo + sheetSize, list.size());
-                // 写入各条记录,每条记录对应excel表中的一行
-                HSSFCellStyle cs = workbook.createCellStyle();
-                cs.setAlignment(HorizontalAlignment.CENTER);
-                cs.setVerticalAlignment(VerticalAlignment.CENTER);
-                for (int i = startNo; i < endNo; i++)
+                if (Type.EXPORT.equals(type))
                 {
-                    row = sheet.createRow(i + 1 - startNo);
-                    // 得到导出对象.
-                    T vo = (T) list.get(i);
-                    for (int j = 0; j < fields.size(); j++)
-                    {
-                        // 获得field.
-                        Field field = fields.get(j);
-                        // 设置实体类私有属性可访问
-                        field.setAccessible(true);
-                        Excel attr = field.getAnnotation(Excel.class);
-                        try
-                        {
-                            // 设置行高
-                            row.setHeight((short) (attr.height() * 20));
-                            // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
-                            if (attr.isExport())
-                            {
-                                // 创建cell
-                                cell = row.createCell(j);
-                                cell.setCellStyle(cs);
-                                if (vo == null)
-                                {
-                                    // 如果数据存在就填入,不存在填入空格.
-                                    cell.setCellValue("");
-                                    continue;
-                                }
-
-                                // 用于读取对象中的属性
-                                Object value = getTargetValue(vo, field, attr);
-                                String dateFormat = attr.dateFormat();
-                                String readConverterExp = attr.readConverterExp();
-                                if (StringUtils.isNotEmpty(dateFormat))
-                                {
-                                    cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) value));
-                                }
-                                else if (StringUtils.isNotEmpty(readConverterExp))
-                                {
-                                    cell.setCellValue(convertByExp(String.valueOf(value), readConverterExp));
-                                }
-                                else
-                                {
-                                    cell.setCellType(CellType.STRING);
-                                    // 如果数据存在就填入,不存在填入空格.
-                                    cell.setCellValue(StringUtils.isNull(value) ? attr.defaultValue() : value + attr.suffix());
-                                }
-                            }
-                        }
-                        catch (Exception e)
-                        {
-                            log.error("导出Excel失败{}", e.getMessage());
-                        }
-                    }
+                    fillExcelData(index, row, cell);
                 }
             }
             String filename = encodingFilename(sheetName);
             out = new FileOutputStream(getAbsoluteFile(filename));
-            workbook.write(out);
+            wb.write(out);
             return AjaxResult.success(filename);
         }
         catch (Exception e)
@@ -384,11 +362,11 @@ public class ExcelUtil<T>
         }
         finally
         {
-            if (workbook != null)
+            if (wb != null)
             {
                 try
                 {
-                    workbook.close();
+                    wb.close();
                 }
                 catch (IOException e1)
                 {
@@ -409,6 +387,78 @@ public class ExcelUtil<T>
         }
     }
 
+    /**
+     * 填充excel数据
+     * 
+     * @param index 序号
+     * @param row 单元格行
+     * @param cell 类型单元格
+     */
+    public void fillExcelData(int index, Row row, Cell cell)
+    {
+        int startNo = index * sheetSize;
+        int endNo = Math.min(startNo + sheetSize, list.size());
+        // 写入各条记录,每条记录对应excel表中的一行
+        CellStyle cs = wb.createCellStyle();
+        cs.setAlignment(HorizontalAlignment.CENTER);
+        cs.setVerticalAlignment(VerticalAlignment.CENTER);
+        for (int i = startNo; i < endNo; i++)
+        {
+            row = sheet.createRow(i + 1 - startNo);
+            // 得到导出对象.
+            T vo = (T) list.get(i);
+            for (int j = 0; j < fields.size(); j++)
+            {
+                // 获得field.
+                Field field = fields.get(j);
+                // 设置实体类私有属性可访问
+                field.setAccessible(true);
+                Excel attr = field.getAnnotation(Excel.class);
+                try
+                {
+                    // 设置行高
+                    row.setHeight((short) (attr.height() * 20));
+                    // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
+                    if (attr.isExport())
+                    {
+                        // 创建cell
+                        cell = row.createCell(j);
+                        cell.setCellStyle(cs);
+                        if (vo == null)
+                        {
+                            // 如果数据存在就填入,不存在填入空格.
+                            cell.setCellValue("");
+                            continue;
+                        }
+
+                        // 用于读取对象中的属性
+                        Object value = getTargetValue(vo, field, attr);
+                        String dateFormat = attr.dateFormat();
+                        String readConverterExp = attr.readConverterExp();
+                        if (StringUtils.isNotEmpty(dateFormat))
+                        {
+                            cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) value));
+                        }
+                        else if (StringUtils.isNotEmpty(readConverterExp))
+                        {
+                            cell.setCellValue(convertByExp(String.valueOf(value), readConverterExp));
+                        }
+                        else
+                        {
+                            cell.setCellType(CellType.STRING);
+                            // 如果数据存在就填入,不存在填入空格.
+                            cell.setCellValue(StringUtils.isNull(value) ? attr.defaultValue() : value + attr.suffix());
+                        }
+                    }
+                }
+                catch (Exception e)
+                {
+                    log.error("导出Excel失败{}", e.getMessage());
+                }
+            }
+        }
+    }
+
     /**
      * 设置单元格上提示
      * 
@@ -421,8 +471,8 @@ public class ExcelUtil<T>
      * @param endCol 结束列
      * @return 设置好的sheet.
      */
-    public static HSSFSheet setHSSFPrompt(HSSFSheet sheet, String promptTitle, String promptContent, int firstRow,
-            int endRow, int firstCol, int endCol)
+    public static Sheet setHSSFPrompt(Sheet sheet, String promptTitle, String promptContent, int firstRow, int endRow,
+            int firstCol, int endCol)
     {
         // 构造constraint对象
         DVConstraint constraint = DVConstraint.createCustomFormulaConstraint("DD1");
@@ -446,8 +496,8 @@ public class ExcelUtil<T>
      * @param endCol 结束列
      * @return 设置好的sheet.
      */
-    public static HSSFSheet setHSSFValidation(HSSFSheet sheet, String[] textlist, int firstRow, int endRow,
-            int firstCol, int endCol)
+    public static Sheet setHSSFValidation(Sheet sheet, String[] textlist, int firstRow, int endRow, int firstCol,
+            int endCol)
     {
         // 加载下拉列表内容
         DVConstraint constraint = DVConstraint.createExplicitListConstraint(textlist);
@@ -488,12 +538,41 @@ public class ExcelUtil<T>
         return propertyValue;
     }
 
+    /**
+     * 反向解析值 男=0,女=1,未知=2
+     * 
+     * @param propertyValue 参数值
+     * @param converterExp 翻译注解
+     * @return 解析后值
+     * @throws Exception
+     */
+    public static String reverseByExp(String propertyValue, String converterExp) throws Exception
+    {
+        try
+        {
+            String[] convertSource = converterExp.split(",");
+            for (String item : convertSource)
+            {
+                String[] itemArray = item.split("=");
+                if (itemArray[1].equals(propertyValue))
+                {
+                    return itemArray[0];
+                }
+            }
+        }
+        catch (Exception e)
+        {
+            throw e;
+        }
+        return propertyValue;
+    }
+
     /**
      * 编码文件名
      */
     public String encodingFilename(String filename)
     {
-        filename = UUID.randomUUID().toString() + "_" + filename + ".xls";
+        filename = UUID.randomUUID().toString() + "_" + filename + ".xlsx";
         return filename;
     }
 
@@ -563,4 +642,110 @@ public class ExcelUtil<T>
         }
         return o;
     }
+
+    /**
+     * 得到所有定义字段
+     */
+    private void createExcelField()
+    {
+        this.fields = new ArrayList<Field>();
+        Field[] allFields = clazz.getDeclaredFields();
+        // 得到所有field并存放到一个list中.
+        for (Field field : allFields)
+        {
+            if (field.isAnnotationPresent(Excel.class))
+            {
+                fields.add(field);
+            }
+        }
+    }
+
+    /**
+     * 创建一个工作簿
+     */
+    public void createWorkbook()
+    {
+        this.wb = new SXSSFWorkbook(500);
+    }
+
+    /**
+     * 创建工作表
+     * 
+     * @param sheetName,指定Sheet名称
+     * @param sheetNo sheet数量
+     * @param index 序号
+     */
+    public void createSheet(double sheetNo, int index)
+    {
+        this.sheet = wb.createSheet();
+        // 设置工作表的名称.
+        if (sheetNo == 0)
+        {
+            wb.setSheetName(index, sheetName);
+        }
+        else
+        {
+            wb.setSheetName(index, sheetName + index);
+        }
+    }
+
+    /**
+     * 获取单元格值
+     * 
+     * @param row 获取的行
+     * @param column 获取单元格列号
+     * @return 单元格值
+     */
+    public Object getCellValue(Row row, int column)
+    {
+        if (row == null)
+        {
+            return row;
+        }
+        Object val = "";
+        try
+        {
+            Cell cell = row.getCell(column);
+            if (cell != null)
+            {
+                if (cell.getCellTypeEnum() == CellType.NUMERIC)
+                {
+                    val = cell.getNumericCellValue();
+                    if (HSSFDateUtil.isCellDateFormatted(cell))
+                    {
+                        val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换
+                    }
+                    else
+                    {
+                        if ((Double) val % 1 > 0)
+                        {
+                            val = new DecimalFormat("0.00").format(val);
+                        }
+                        else
+                        {
+                            val = new DecimalFormat("0").format(val);
+                        }
+                    }
+                }
+                else if (cell.getCellTypeEnum() == CellType.STRING)
+                {
+                    val = cell.getStringCellValue();
+                }
+                else if (cell.getCellTypeEnum() == CellType.BOOLEAN)
+                {
+                    val = cell.getBooleanCellValue();
+                }
+                else if (cell.getCellTypeEnum() == CellType.ERROR)
+                {
+                    val = cell.getErrorCellValue();
+                }
+
+            }
+        }
+        catch (Exception e)
+        {
+            return val;
+        }
+        return val;
+    }
 }

+ 5 - 0
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUser.java

@@ -244,6 +244,10 @@ public class SysUser extends BaseEntity
 
     public SysDept getDept()
     {
+        if (dept == null)
+        {
+            dept = new SysDept();
+        }
         return dept;
     }
 
@@ -304,6 +308,7 @@ public class SysUser extends BaseEntity
             .append("updateBy", getUpdateBy())
             .append("updateTime", getUpdateTime())
             .append("remark", getRemark())
+            .append("dept", getDept())
             .toString();
     }
 }

+ 8 - 0
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java

@@ -138,4 +138,12 @@ public interface ISysUserService
      * @return 结果
      */
     public String selectUserPostGroup(Long userId);
+
+    /**
+     * 导入用户数据
+     * 
+     * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据
+     * @return 结果
+     */
+    public String importUser(List<SysUser> userList, Boolean isUpdateSupport);
 }

+ 94 - 22
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java

@@ -2,6 +2,8 @@ package com.ruoyi.system.service.impl;
 
 import java.util.ArrayList;
 import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.ruoyi.common.annotation.DataScope;
@@ -29,6 +31,8 @@ import com.ruoyi.system.service.ISysUserService;
 @Service
 public class SysUserServiceImpl implements ISysUserService
 {
+    private static final Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class);
+
     @Autowired
     private SysUserMapper userMapper;
 
@@ -212,18 +216,22 @@ public class SysUserServiceImpl implements ISysUserService
      */
     public void insertUserRole(SysUser user)
     {
-        // 新增用户与角色管理
-        List<SysUserRole> list = new ArrayList<SysUserRole>();
-        for (Long roleId : user.getRoleIds())
+        Long[] roles = user.getRoleIds();
+        if (StringUtils.isNotNull(roles))
         {
-            SysUserRole ur = new SysUserRole();
-            ur.setUserId(user.getUserId());
-            ur.setRoleId(roleId);
-            list.add(ur);
-        }
-        if (list.size() > 0)
-        {
-            userRoleMapper.batchUserRole(list);
+            // 新增用户与角色管理
+            List<SysUserRole> list = new ArrayList<SysUserRole>();
+            for (Long roleId : roles)
+            {
+                SysUserRole ur = new SysUserRole();
+                ur.setUserId(user.getUserId());
+                ur.setRoleId(roleId);
+                list.add(ur);
+            }
+            if (list.size() > 0)
+            {
+                userRoleMapper.batchUserRole(list);
+            }
         }
     }
 
@@ -234,18 +242,22 @@ public class SysUserServiceImpl implements ISysUserService
      */
     public void insertUserPost(SysUser user)
     {
-        // 新增用户与岗位管理
-        List<SysUserPost> list = new ArrayList<SysUserPost>();
-        for (Long postId : user.getPostIds())
+        Long[] posts = user.getPostIds();
+        if (StringUtils.isNotNull(posts))
         {
-            SysUserPost up = new SysUserPost();
-            up.setUserId(user.getUserId());
-            up.setPostId(postId);
-            list.add(up);
-        }
-        if (list.size() > 0)
-        {
-            userPostMapper.batchUserPost(list);
+            // 新增用户与岗位管理
+            List<SysUserPost> list = new ArrayList<SysUserPost>();
+            for (Long postId : posts)
+            {
+                SysUserPost up = new SysUserPost();
+                up.setUserId(user.getUserId());
+                up.setPostId(postId);
+                list.add(up);
+            }
+            if (list.size() > 0)
+            {
+                userPostMapper.batchUserPost(list);
+            }
         }
     }
 
@@ -345,4 +357,64 @@ public class SysUserServiceImpl implements ISysUserService
         }
         return idsStr.toString();
     }
+
+    /**
+     * 导入用户数据
+     * 
+     * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据
+     * @return 结果
+     */
+    public String importUser(List<SysUser> userList, Boolean isUpdateSupport)
+    {
+        if (StringUtils.isNull(userList) || userList.size() == 0)
+        {
+            throw new BusinessException("导入用户数据不能为空!");
+        }
+        int successNum = 0;
+        int failureNum = 0;
+        StringBuilder successMsg = new StringBuilder();
+        StringBuilder failureMsg = new StringBuilder();
+        for (SysUser user : userList)
+        {
+            try
+            {
+                // 验证是否存在这个用户
+                SysUser u = userMapper.selectUserByLoginName(user.getLoginName());
+                if (StringUtils.isNull(u))
+                {
+                    this.insertUser(user);
+                    successNum++;
+                    successMsg.append("<br/>" + successNum + "、账号 " + user.getLoginName() + " 导入成功");
+                }
+                else if (isUpdateSupport)
+                {
+                    this.updateUser(user);
+                    successNum++;
+                    successMsg.append("<br/>" + successNum + "、账号 " + user.getLoginName() + " 更新成功");
+                }
+                else
+                {
+                    failureNum++;
+                    failureMsg.append("<br/>" + failureNum + "、账号 " + user.getLoginName() + " 已存在");
+                }
+            }
+            catch (Exception e)
+            {
+                failureNum++;
+                String msg = "<br/>" + failureNum + "、账号 " + user.getLoginName() + " 导入失败:";
+                failureMsg.append(msg + e.getMessage());
+                log.error(msg, e);
+            }
+        }
+        if (failureNum > 0)
+        {
+            failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
+            throw new BusinessException(failureMsg.toString());
+        }
+        else
+        {
+            successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
+        }
+        return successMsg.toString();
+    }
 }