From 502c18091261580f5ffb2bdc7f592ad8745223b4 Mon Sep 17 00:00:00 2001 From: wanggeng888 <450292408@qq.com> Date: Tue, 9 Feb 2021 23:54:18 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=9F=BA=E7=A1=80=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interfaces/consts/IFileConstant.java | 24 ++ .../interfaces/user/IUserBaseService.java | 16 + .../ink/wgink/util/request/RequestUtil.java | 24 ++ .../wgink/common/advice/ResponseAdvice.java | 13 + .../wgink/common/aspect/ApiParamsAspect.java | 310 ++++++++++++++++ .../common/base/DefaultBaseController.java | 11 +- .../wgink/common/base/DefaultBaseService.java | 6 +- .../ink/wgink/common/config/BeanConfig.java | 73 ++++ .../common/config/TransactionConfig.java | 13 +- .../ink/wgink/common/config/WebConfig.java | 31 ++ .../ink/wgink/module/file/dao/IFileDao.java | 6 +- .../error/AbstractErrorExcelHandler.java | 1 + .../file/startup/ModuleFileStartUp.java | 45 +++ .../resources/mybatis/mapper/file-mapper.xml | 279 ++++++++++++++ .../user/controller/api/UserController.java | 53 +++ .../controller/route/UserRouteController.java | 72 ++++ .../ink/wgink/module/user/dao/IUserDao.java | 7 + .../wgink/module/user/excel/UserExcel.java | 72 ++++ .../module/user/excel/UserExcelError.java | 84 +++++ .../module/user/excel/UserExcelListener.java | 48 +++ .../module/user/pojo/vos/RestPasswordVO.java | 40 ++ .../user/pojo/vos/UpdateUsernameVO.java | 53 +++ .../module/user/service/IUserService.java | 34 +- .../user/service/impl/UserServiceImpl.java | 132 ++++++- .../user/startup/ServiceUserStartUp.java | 62 ++++ .../resources/mybatis/mapper/user-mapper.xml | 37 ++ .../main/resources/templates/user/list.html | 349 ++++++++++++++++++ .../templates/user/rest-password.html | 86 +++++ .../main/resources/templates/user/save.html | 169 +++++++++ .../templates/user/update-password.html | 104 ++++++ .../templates/user/update-username.html | 102 +++++ .../main/resources/templates/user/update.html | 198 ++++++++++ .../user/upload/upload-excel-template.xls | Bin 0 -> 18944 bytes .../templates/user/upload/upload-excel.html | 90 +++++ 34 files changed, 2624 insertions(+), 20 deletions(-) create mode 100644 basic-interface/src/main/java/ink/wgink/interfaces/consts/IFileConstant.java create mode 100644 common/src/main/java/ink/wgink/common/aspect/ApiParamsAspect.java create mode 100644 common/src/main/java/ink/wgink/common/config/BeanConfig.java create mode 100644 common/src/main/java/ink/wgink/common/config/WebConfig.java create mode 100644 module-file/src/main/java/ink/wgink/module/file/startup/ModuleFileStartUp.java create mode 100644 module-file/src/main/resources/mybatis/mapper/file-mapper.xml create mode 100644 service-user/src/main/java/ink/wgink/module/user/controller/route/UserRouteController.java create mode 100644 service-user/src/main/java/ink/wgink/module/user/excel/UserExcel.java create mode 100644 service-user/src/main/java/ink/wgink/module/user/excel/UserExcelError.java create mode 100644 service-user/src/main/java/ink/wgink/module/user/excel/UserExcelListener.java create mode 100644 service-user/src/main/java/ink/wgink/module/user/pojo/vos/RestPasswordVO.java create mode 100644 service-user/src/main/java/ink/wgink/module/user/pojo/vos/UpdateUsernameVO.java create mode 100644 service-user/src/main/java/ink/wgink/module/user/startup/ServiceUserStartUp.java create mode 100644 service-user/src/main/resources/templates/user/list.html create mode 100644 service-user/src/main/resources/templates/user/rest-password.html create mode 100644 service-user/src/main/resources/templates/user/save.html create mode 100644 service-user/src/main/resources/templates/user/update-password.html create mode 100644 service-user/src/main/resources/templates/user/update-username.html create mode 100644 service-user/src/main/resources/templates/user/update.html create mode 100644 service-user/src/main/resources/templates/user/upload/upload-excel-template.xls create mode 100644 service-user/src/main/resources/templates/user/upload/upload-excel.html diff --git a/basic-interface/src/main/java/ink/wgink/interfaces/consts/IFileConstant.java b/basic-interface/src/main/java/ink/wgink/interfaces/consts/IFileConstant.java new file mode 100644 index 00000000..9438df98 --- /dev/null +++ b/basic-interface/src/main/java/ink/wgink/interfaces/consts/IFileConstant.java @@ -0,0 +1,24 @@ +package ink.wgink.interfaces.consts; + +/** + * When you feel like quitting. Think about why you started + * 当你想要放弃的时候,想想当初你为何开始 + * + * @ClassName: IFileConstant + * @Description: 文件常量 + * @Author: wanggeng + * @Date: 2021/2/9 8:15 下午 + * @Version: 1.0 + */ +public interface IFileConstant { + + /** + * Excel .xls 后缀 + */ + String EXCEL_SUFFIX_XLS = ".xls"; + /** + * Excel .xlsx 后缀 + */ + String EXCEL_SUFFIX_XLSX = ".xlsx"; + +} diff --git a/basic-interface/src/main/java/ink/wgink/interfaces/user/IUserBaseService.java b/basic-interface/src/main/java/ink/wgink/interfaces/user/IUserBaseService.java index 179867c3..7417b6ed 100644 --- a/basic-interface/src/main/java/ink/wgink/interfaces/user/IUserBaseService.java +++ b/basic-interface/src/main/java/ink/wgink/interfaces/user/IUserBaseService.java @@ -43,6 +43,22 @@ public interface IUserBaseService { */ List listByUserIds(List userIds); + /** + * 用户列表 + * + * @param usernames 用户名列表 + * @return + */ + List listByUsernames(List usernames); + + /** + * 用户列表 + * + * @param params 参数 + * @return + */ + List list(Map params); + /** * 用户分页列表 * diff --git a/basic-util/src/main/java/ink/wgink/util/request/RequestUtil.java b/basic-util/src/main/java/ink/wgink/util/request/RequestUtil.java index 343a6690..8e5cd78a 100644 --- a/basic-util/src/main/java/ink/wgink/util/request/RequestUtil.java +++ b/basic-util/src/main/java/ink/wgink/util/request/RequestUtil.java @@ -4,6 +4,9 @@ import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URLEncoder; /** * @ClassName: RequestUtil @@ -64,4 +67,25 @@ public class RequestUtil { return ip; } + /** + * 下载 + * + * @param response 响应 + * @param outFile 输出的文件 + * @param outFileName 输出的名字 + */ + public static void download(HttpServletResponse response, File outFile, String outFileName) throws IOException { + try (FileInputStream fileInputStream = new FileInputStream(outFile); + OutputStream outputStream = response.getOutputStream()) { + response.setHeader("content-type", "application/octet-stream"); + response.setContentType("application/octet-stream"); + response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(outFileName, "UTF-8")); + byte[] buf = new byte[1024]; + for (int readLength; (readLength = fileInputStream.read(buf)) > 0; ) { + outputStream.write(buf, 0, readLength); + } + outputStream.flush(); + } + } + } diff --git a/common/src/main/java/ink/wgink/common/advice/ResponseAdvice.java b/common/src/main/java/ink/wgink/common/advice/ResponseAdvice.java index 595b9951..728c8aba 100644 --- a/common/src/main/java/ink/wgink/common/advice/ResponseAdvice.java +++ b/common/src/main/java/ink/wgink/common/advice/ResponseAdvice.java @@ -4,9 +4,12 @@ import com.alibaba.fastjson.JSON; import ink.wgink.common.enums.ErrorResultCodeEnum; import ink.wgink.exceptions.*; import ink.wgink.exceptions.base.SystemException; +import ink.wgink.interfaces.consts.ISystemConstant; import ink.wgink.pojo.result.ErrorResult; import ink.wgink.util.AesUtil; import ink.wgink.util.map.HashMapUtil; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; @@ -20,7 +23,14 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.sql.SQLSyntaxErrorException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Pattern; /** @@ -43,6 +53,8 @@ public class ResponseAdvice { } if (e instanceof FileException) { LOG.error(e.getMessage()); + } else if(e instanceof ParamsException){ + LOG.error(e.getMessage()); } else { LOG.error(e.getMessage(), e); } @@ -88,6 +100,7 @@ public class ResponseAdvice { } String contentType = request.getContentType(); if (contentType != null && contentType.contains(MediaType.APPLICATION_JSON_VALUE)) { + response.setCharacterEncoding(ISystemConstant.CHARSET_UTF8); response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setStatus(HttpStatus.BAD_REQUEST.value()); response.getWriter().write(JSON.toJSONString(result)); diff --git a/common/src/main/java/ink/wgink/common/aspect/ApiParamsAspect.java b/common/src/main/java/ink/wgink/common/aspect/ApiParamsAspect.java new file mode 100644 index 00000000..03d767a2 --- /dev/null +++ b/common/src/main/java/ink/wgink/common/aspect/ApiParamsAspect.java @@ -0,0 +1,310 @@ +package ink.wgink.common.aspect; + +import ink.wgink.annotation.*; +import ink.wgink.exceptions.ParamsException; +import ink.wgink.util.RegexUtil; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.Signature; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.RequestBody; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Pattern; + +/** + * When you feel like quitting. Think about why you started + * 当你想要放弃的时候,想想当初你为何开始 + * + * @ClassName: ApiParamsAspect + * @Description: api接口 + * @Author: WangGeng + * @Date: 2019/11/14 15:36 + * @Version: 1.0 + **/ +@Aspect +@Component +@Order(1) +public class ApiParamsAspect { + private static final Logger LOG = LoggerFactory.getLogger(ApiParamsAspect.class); + + @Pointcut("execution(public * *..*Controller.*(..))") + public void apiCutPoint() { + } + + @Before("apiCutPoint()") + public void beforeApiCutPoint(JoinPoint joinPoint) throws ParamsException { + beforeCutPoint(joinPoint); + } + + private void beforeCutPoint(JoinPoint joinPoint) throws ParamsException { + Signature signature = joinPoint.getSignature(); + Object[] args = joinPoint.getArgs(); + Class targetClazz = joinPoint.getTarget().getClass(); + Method method = getMethod(signature.getName(), targetClazz.getMethods(), args); + if (method == null) { + return; + } + + if (method.isAnnotationPresent(CheckRequestBodyAnnotation.class)) { + LOG.debug("校验参数"); + Parameter[] parameters = method.getParameters(); + for (Parameter parameter : parameters) { + if (parameter.isAnnotationPresent(RequestBody.class)) { + for (Object arg : args) { + if (parameter.getType() == arg.getClass()) { + checkField(arg); + } + } + } + } + } + } + + private Method getMethod(String methodName, Method[] methods, Object[] args) { + for (Method method : methods) { + Class[] parameterTypes = method.getParameterTypes(); + // 方法名称相同 + if (StringUtils.equals(methodName, method.getName())) { + // 参数长度相同 + if (args.length == parameterTypes.length) { + // 参数类型顺序相同 + boolean isThisMethod = true; + for (int argIndex = 0; argIndex < args.length; argIndex++) { + if (!parameterTypes[argIndex].isInstance(args[argIndex])) { + isThisMethod = false; + break; + } + } + if (isThisMethod) { + return method; + } + } + } + } + return null; + } + + /** + * 校验字段 + * + * @param object + * @throws ParamsException + */ + public void checkField(Object object) throws ParamsException { + Class clazz = object.getClass(); + List fields = new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())); + setSuperClassField(clazz, fields); + Method[] methods = clazz.getMethods(); + try { + for (Field field : fields) { + Method method = getGetMethodByField(methods, field.getName()); + if (method == null) { + continue; + } + Object fieldValue = method.invoke(object); + if (field.isAnnotationPresent(CheckNullAnnotation.class)) { + CheckNullAnnotation checkNullAnnotation = field.getAnnotation(CheckNullAnnotation.class); + if (fieldValue == null) { + throw new ParamsException(String.format("%s不能为空", checkNullAnnotation.name())); + } + checkTypes(checkNullAnnotation.name(), fieldValue.toString(), checkNullAnnotation.types()); + } else if (field.isAnnotationPresent(CheckEmptyAnnotation.class)) { + CheckEmptyAnnotation checkEmptyAnnotation = field.getAnnotation(CheckEmptyAnnotation.class); + if (fieldValue == null || StringUtils.isBlank(fieldValue.toString())) { + throw new ParamsException(String.format("%s不能为空或空串", checkEmptyAnnotation.name())); + } + checkRegular(checkEmptyAnnotation.name(), fieldValue.toString(), checkEmptyAnnotation.verifyType(), checkEmptyAnnotation.regex()); + checkTypes(checkEmptyAnnotation.name(), fieldValue.toString(), checkEmptyAnnotation.types()); + } else if (field.isAnnotationPresent(CheckNumberAnnotation.class)) { + CheckNumberAnnotation checkNumberAnnotation = field.getAnnotation(CheckNumberAnnotation.class); + if (fieldValue == null || !NumberUtils.isNumber(fieldValue.toString())) { + throw new ParamsException(String.format("%s必须为数字", checkNumberAnnotation.name())); + } + Double value = Double.parseDouble(fieldValue.toString()); + if (value < checkNumberAnnotation.min()) { + throw new ParamsException(String.format("%s最小值为%f", checkNumberAnnotation.name(), checkNumberAnnotation.min())); + } + if (value > checkNumberAnnotation.max()) { + throw new ParamsException(String.format("%s最大值为%f", checkNumberAnnotation.name(), checkNumberAnnotation.max())); + } + checkTypes(checkNumberAnnotation.name(), fieldValue.toString(), checkNumberAnnotation.types()); + } else if (field.isAnnotationPresent(CheckBooleanAnnotation.class)) { + CheckBooleanAnnotation checkBooleanAnnotation = field.getAnnotation(CheckBooleanAnnotation.class); + if (fieldValue == null) { + throw new ParamsException(String.format("%s必须为布尔", checkBooleanAnnotation.name())); + } + checkTypes(checkBooleanAnnotation.name(), fieldValue.toString(), checkBooleanAnnotation.types()); + } else if (field.isAnnotationPresent(CheckListAnnotation.class)) { + CheckListAnnotation checkListAnnotation = field.getAnnotation(CheckListAnnotation.class); + if (fieldValue == null) { + throw new ParamsException(String.format("%s不能为空", checkListAnnotation.name())); + } + if (fieldValue instanceof List) { + List fieldValueList = (List) fieldValue; + if (fieldValueList.isEmpty()) { + throw new ParamsException(String.format("%s不能为空", checkListAnnotation.name())); + } + for (Object obj : fieldValueList) { + checkField(obj); + } + } else if (fieldValue instanceof String[]) { + String[] fieldValueArray = (String[]) fieldValue; + for (Object obj : fieldValueArray) { + checkField(obj); + } + } + } + } + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + throw new ParamsException("系统错误"); + } + } + + /** + * 检查类型 + * + * @param name + * @param value + * @param types + * @throws ParamsException + */ + private void checkTypes(String name, String value, String[] types) throws ParamsException { + if (types != null && types.length > 0) { + StringBuilder typeSB = new StringBuilder(); + for (String type : types) { + if (StringUtils.equals(value, type)) { + return; + } + if (typeSB.length() > 0) { + typeSB.append(","); + } + typeSB.append("\"").append(type).append("\""); + } + throw new ParamsException(String.format("%s必须是如下类型:[%s]", name, typeSB.toString())); + } + } + + /** + * 检查正则 + * + * @param name + * @param value + * @param verifyType + * @param regex + */ + private void checkRegular(String name, String value, String verifyType, String regex) { + if (StringUtils.isBlank(verifyType)) { + return; + } + if (StringUtils.equals("username", verifyType)) { + if (!RegexUtil.isUsername(value)) { + throw new ParamsException(String.format("%s格式只能是字母、数字和下划线", name)); + } + return; + } else if (StringUtils.equals("phone", verifyType)) { + if (!RegexUtil.isPhone(value)) { + throw new ParamsException(String.format("%s格式非手机格式", name)); + } + return; + } else if (StringUtils.equals("email", verifyType)) { + if (!RegexUtil.isEmail(value)) { + throw new ParamsException(String.format("%s格式非邮件格式", name)); + } + return; + } else if (StringUtils.equals("url", verifyType)) { + if (!RegexUtil.isUrl(value)) { + throw new ParamsException(String.format("%s格式非url格式", name)); + } + return; + } else if (StringUtils.equals("number", verifyType)) { + if (!NumberUtils.isNumber(value)) { + throw new ParamsException(String.format("%s格式非数字格式", name)); + } + return; + } else if (StringUtils.equals("date", verifyType)) { + if (!RegexUtil.isDate(value)) { + throw new ParamsException(String.format("%s格式非日期格式", name)); + } + return; + } else if (StringUtils.equals("datetime", verifyType)) { + if (!RegexUtil.isDatetime(value)) { + throw new ParamsException(String.format("%s格式非时间戳格式", name)); + } + return; + } else if (StringUtils.equals("identity", verifyType)) { + if (!RegexUtil.isIdentity(value)) { + throw new ParamsException(String.format("%s格式非身份证格式", name)); + } + return; + } else if (StringUtils.equals("letter", verifyType)) { + if (!RegexUtil.isLetter(value)) { + throw new ParamsException(String.format("%s格式非字母格式", name)); + } + return; + } else if (StringUtils.equals("chinese", verifyType)) { + if (!RegexUtil.isLetter(value)) { + throw new ParamsException(String.format("%s格式非中文格式", name)); + } + return; + } else if (StringUtils.equals("custom", verifyType)) { + if (StringUtils.isBlank(regex)) { + return; + } + if (!Pattern.compile(regex).matcher(value).matches()) { + throw new ParamsException(String.format("%s格式不正确", name)); + } + } + } + + /** + * 通过字段获取get方法 + * + * @param methods + * @param fieldName + * @return + */ + public Method getGetMethodByField(Method[] methods, String fieldName) { + String getMethodName = String.format("get%s", fieldName).toLowerCase(); + for (Method method : methods) { + if (StringUtils.equals(getMethodName, method.getName().toLowerCase())) { + return method; + } + } + return null; + } + + /** + * 设置父类属性列表 + * + * @param clazz + * @param fields + */ + private void setSuperClassField(Class clazz, List fields) { + Class superClazz = clazz.getSuperclass(); + if (superClazz == null) { + return; + } + Field[] superFields = superClazz.getDeclaredFields(); + if (superFields == null) { + return; + } + fields.addAll(Arrays.asList(superFields)); + setSuperClassField(superClazz, fields); + } + +} diff --git a/common/src/main/java/ink/wgink/common/base/DefaultBaseController.java b/common/src/main/java/ink/wgink/common/base/DefaultBaseController.java index 1c48837a..a6672740 100644 --- a/common/src/main/java/ink/wgink/common/base/DefaultBaseController.java +++ b/common/src/main/java/ink/wgink/common/base/DefaultBaseController.java @@ -6,7 +6,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.springframework.stereotype.Controller; import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; @@ -21,7 +24,7 @@ import java.util.Map; * @Date: 2018/9/27 下午3:47 * @Version: 1.0 **/ -@Component +@Controller public class DefaultBaseController { protected static final Logger LOG = LoggerFactory.getLogger(DefaultBaseController.class); @@ -29,6 +32,12 @@ public class DefaultBaseController { @Autowired private HttpSession httpSession; + @GetMapping("index") + public ModelAndView goIndex() { + ModelAndView mv = new ModelAndView("index"); + return mv; + } + /** * 获取请求参数 * diff --git a/common/src/main/java/ink/wgink/common/base/DefaultBaseService.java b/common/src/main/java/ink/wgink/common/base/DefaultBaseService.java index 51bdca24..f77f7be8 100644 --- a/common/src/main/java/ink/wgink/common/base/DefaultBaseService.java +++ b/common/src/main/java/ink/wgink/common/base/DefaultBaseService.java @@ -80,7 +80,11 @@ public class DefaultBaseService { */ protected void setUpdateInfo(Map params) { UserInfoBO userInfoBO = securityComponent.getCurrentUser(); - setUpdate(userInfoBO.getUserId(), params); + if (userInfoBO != null) { + setUpdate(userInfoBO.getUserId(), params); + } else { + setUpdate("1", params); + } } /** diff --git a/common/src/main/java/ink/wgink/common/config/BeanConfig.java b/common/src/main/java/ink/wgink/common/config/BeanConfig.java new file mode 100644 index 00000000..c338f950 --- /dev/null +++ b/common/src/main/java/ink/wgink/common/config/BeanConfig.java @@ -0,0 +1,73 @@ +package ink.wgink.common.config; + +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson.support.config.FastJsonConfig; +import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +/** + * When you feel like quitting. Think about why you started + * 当你想要放弃的时候,想想当初你为何开始 + * + * @ClassName: BeanConfig + * @Description: bean配置 + * @Author: wanggeng + * @Date: 2021/2/5 6:44 下午 + * @Version: 1.0 + */ +@Configuration +public class BeanConfig { + + @Bean + public HttpMessageConverter configureMessageConverters() { + FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); + FastJsonConfig config = new FastJsonConfig(); + config.setSerializerFeatures( + // 保留map空的字段 + SerializerFeature.WriteMapNullValue, + // 将String类型的null转成"" + SerializerFeature.WriteNullStringAsEmpty, + // 将Number类型的null转成0 + SerializerFeature.WriteNullNumberAsZero, + // 将List类型的null转成[] + SerializerFeature.WriteNullListAsEmpty, + // 将Boolean类型的null转成false + SerializerFeature.WriteNullBooleanAsFalse, + // 避免循环引用 + SerializerFeature.DisableCircularReferenceDetect); + + converter.setFastJsonConfig(config); + converter.setDefaultCharset(Charset.forName("UTF-8")); + List mediaTypeList = new ArrayList<>(); + // 解决中文乱码问题,相当于在Controller上的@RequestMapping中加了个属性produces = "application/json" + mediaTypeList.add(MediaType.APPLICATION_JSON); + converter.setSupportedMediaTypes(mediaTypeList); + return converter; + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new PasswordEncoder() { + @Override + public String encode(CharSequence charSequence) { + return charSequence.toString(); + } + + @Override + public boolean matches(CharSequence charSequence, String s) { + return true; + } + }; + // return new BCryptPasswordEncoder(); + } + +} diff --git a/common/src/main/java/ink/wgink/common/config/TransactionConfig.java b/common/src/main/java/ink/wgink/common/config/TransactionConfig.java index 5dd5fb15..c8849d8e 100644 --- a/common/src/main/java/ink/wgink/common/config/TransactionConfig.java +++ b/common/src/main/java/ink/wgink/common/config/TransactionConfig.java @@ -63,9 +63,10 @@ public class TransactionConfig { public TransactionInterceptor txAdvice() { DefaultTransactionAttribute required = new DefaultTransactionAttribute(); required.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); + required.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE); DefaultTransactionAttribute readOnly = new DefaultTransactionAttribute(); - readOnly.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); + required.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS); readOnly.setReadOnly(true); List saveList = transactionProperties.getSaveList(); @@ -104,11 +105,11 @@ public class TransactionConfig { @Bean public Advisor txAdviceAdvisor() { - StringBuilder expressionSB = new StringBuilder("execution(* ink.wgink..*.service..*(..))"); - List servicePackageList = transactionProperties.getServicePackageList(); - for (String servicePackage : servicePackageList) { - expressionSB.append(" or execution(* " + servicePackage + "..*.service..*(..))"); - } + StringBuilder expressionSB = new StringBuilder("execution(* *..service..*(..))"); +// List servicePackageList = transactionProperties.getServicePackageList(); +// for (String servicePackage : servicePackageList) { +// expressionSB.append(" or execution(* " + servicePackage + "..*.service..*(..))"); +// } AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression(expressionSB.toString()); diff --git a/common/src/main/java/ink/wgink/common/config/WebConfig.java b/common/src/main/java/ink/wgink/common/config/WebConfig.java new file mode 100644 index 00000000..2999d428 --- /dev/null +++ b/common/src/main/java/ink/wgink/common/config/WebConfig.java @@ -0,0 +1,31 @@ +package ink.wgink.common.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * When you feel like quitting. Think about why you started + * 当你想要放弃的时候,想想当初你为何开始 + * + * @ClassName: WebConfig + * @Description: Web配置 + * @Author: wanggeng + * @Date: 2021/2/5 7:44 下午 + * @Version: 1.0 + */ +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/").setViewName("forward:/index"); + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/static/assets/").setCachePeriod(7 * 24 * 3600); + } + +} diff --git a/module-file/src/main/java/ink/wgink/module/file/dao/IFileDao.java b/module-file/src/main/java/ink/wgink/module/file/dao/IFileDao.java index e6ff0adf..780dad48 100644 --- a/module-file/src/main/java/ink/wgink/module/file/dao/IFileDao.java +++ b/module-file/src/main/java/ink/wgink/module/file/dao/IFileDao.java @@ -22,6 +22,11 @@ import java.util.Map; @Repository public interface IFileDao { + /** + * 建表 + */ + void createTable(); + /** * 保存文件 * @@ -99,5 +104,4 @@ public interface IFileDao { */ List listByMd5(String fileMd5) throws SearchException; - } diff --git a/module-file/src/main/java/ink/wgink/module/file/excel/error/AbstractErrorExcelHandler.java b/module-file/src/main/java/ink/wgink/module/file/excel/error/AbstractErrorExcelHandler.java index e0908b22..0cf74361 100644 --- a/module-file/src/main/java/ink/wgink/module/file/excel/error/AbstractErrorExcelHandler.java +++ b/module-file/src/main/java/ink/wgink/module/file/excel/error/AbstractErrorExcelHandler.java @@ -3,6 +3,7 @@ package ink.wgink.module.file.excel.error; import com.alibaba.excel.EasyExcel; import ink.wgink.module.file.service.IFileService; import ink.wgink.util.UUIDUtil; +import ink.wgink.util.request.RequestUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/module-file/src/main/java/ink/wgink/module/file/startup/ModuleFileStartUp.java b/module-file/src/main/java/ink/wgink/module/file/startup/ModuleFileStartUp.java new file mode 100644 index 00000000..aca247c9 --- /dev/null +++ b/module-file/src/main/java/ink/wgink/module/file/startup/ModuleFileStartUp.java @@ -0,0 +1,45 @@ +package ink.wgink.module.file.startup; + +import ink.wgink.module.file.dao.IFileDao; +import ink.wgink.pojo.dtos.user.UserDTO; +import ink.wgink.util.date.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +/** + * When you feel like quitting. Think about why you started + * 当你想要放弃的时候,想想当初你为何开始 + * + * @ClassName: ModuleFileStartUp + * @Description: 文件模块启动 + * @Author: wanggeng + * @Date: 2021/2/9 9:18 下午 + * @Version: 1.0 + */ +@Component +public class ModuleFileStartUp implements ApplicationRunner { + private static final Logger LOG = LoggerFactory.getLogger(ModuleFileStartUp.class); + + @Autowired + private IFileDao fileDao; + + @Override + public void run(ApplicationArguments args) throws Exception { + initTable(); + } + + /** + * 建表 + */ + private void initTable() { + LOG.debug("创建 sys_file 表"); + fileDao.createTable(); + } +} diff --git a/module-file/src/main/resources/mybatis/mapper/file-mapper.xml b/module-file/src/main/resources/mybatis/mapper/file-mapper.xml new file mode 100644 index 00000000..ec919816 --- /dev/null +++ b/module-file/src/main/resources/mybatis/mapper/file-mapper.xml @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CREATE TABLE IF NOT EXISTS `sys_file` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `file_id` char(36) NOT NULL, + `file_name` varchar(255) DEFAULT NULL COMMENT '文件名称', + `file_path` varchar(500) DEFAULT NULL COMMENT '文件路径', + `file_url` varchar(500) DEFAULT NULL COMMENT '文件URL', + `file_type` varchar(20) DEFAULT NULL COMMENT '文件类型', + `file_size` varchar(100) DEFAULT NULL COMMENT '文件大小', + `file_summary` varchar(255) DEFAULT NULL COMMENT '文件说明', + `is_back` int(2) DEFAULT '0', + `creator` char(36) DEFAULT NULL COMMENT '创建人', + `gmt_create` datetime DEFAULT NULL COMMENT '创建时间', + `modifier` char(36) DEFAULT NULL COMMENT '修改人', + `gmt_modified` datetime DEFAULT NULL, + `is_delete` int(2) DEFAULT '0', + PRIMARY KEY (`id`,`file_id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + + + + INSERT INTO sys_file( + file_id, + file_name, + file_path, + file_url, + file_type, + file_size, + file_summary, + is_back, + creator, + gmt_create, + modifier, + gmt_modified, + is_delete + ) VALUES( + #{fileId}, + #{fileName}, + #{filePath}, + #{fileUrl}, + #{fileType}, + #{fileSize}, + #{fileSummary}, + #{isBack}, + #{creator}, + #{gmtCreate}, + #{modifier}, + #{gmtModified}, + #{isDelete} + ) + + + + + UPDATE + sys_file + SET + is_delete = 1, + modifier = #{modifier}, + gmt_modified = #{gmtModified} + WHERE + file_id IN + + #{fileIds[${index}]} + + + + + + DELETE FROM + sys_file + WHERE + file_id IN + + #{fileIds[${index}]} + + + + + + UPDATE + sys_file + SET + file_summary = #{fileSummary} + WHERE + + file_id = #{fileId} + + + file_id IN + + #{fileIds[${index}]} + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/service-user/src/main/java/ink/wgink/module/user/controller/api/UserController.java b/service-user/src/main/java/ink/wgink/module/user/controller/api/UserController.java index f89ad895..a8027ff5 100644 --- a/service-user/src/main/java/ink/wgink/module/user/controller/api/UserController.java +++ b/service-user/src/main/java/ink/wgink/module/user/controller/api/UserController.java @@ -1,8 +1,12 @@ package ink.wgink.module.user.controller.api; +import ink.wgink.annotation.CheckRequestBodyAnnotation; import ink.wgink.common.base.DefaultBaseController; import ink.wgink.exceptions.ParamsException; +import ink.wgink.interfaces.consts.IFileConstant; import ink.wgink.interfaces.consts.ISystemConstant; +import ink.wgink.module.user.pojo.vos.RestPasswordVO; +import ink.wgink.module.user.pojo.vos.UpdateUsernameVO; import ink.wgink.pojo.ListPage; import ink.wgink.pojo.dtos.user.UserDTO; import ink.wgink.pojo.result.ErrorResult; @@ -10,15 +14,19 @@ import ink.wgink.pojo.result.SuccessResult; import ink.wgink.pojo.result.SuccessResultList; import ink.wgink.module.user.pojo.vos.UserVO; import ink.wgink.module.user.service.IUserService; +import ink.wgink.pojo.result.UploadExcelResultDTO; import ink.wgink.util.RegexUtil; import io.swagger.annotations.*; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Objects; /** * When you feel like quitting. Think about why you started @@ -85,6 +93,41 @@ public class UserController extends DefaultBaseController { } } + @ApiOperation(value = "重置密码", notes = "重置密码接口") + @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) + @PutMapping("resetpassword/{userId}") + @CheckRequestBodyAnnotation + public SuccessResult resetPassword(@PathVariable("userId") String userId, @RequestBody RestPasswordVO restPasswordVO) { + userService.resetPassword(userId, restPasswordVO); + return new SuccessResult(); + } + + @ApiOperation(value = "修改用户名", notes = "修改用户名密码接口") + @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) + @PutMapping("updateusername/{userId}") + @CheckRequestBodyAnnotation + public SuccessResult updateUsername(@PathVariable("userId") String userId, @RequestBody UpdateUsernameVO updateUsernameVO) throws Exception { + userService.updateUsername(userId, updateUsernameVO); + return new SuccessResult(); + } + + @ApiOperation(value = "导入Excel", notes = "导入Excel接口") + @ApiImplicitParams({ + @ApiImplicitParam(name = "excel", value = "文件名称", paramType = "query"), + }) + @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) + @PostMapping("importexcel") + public UploadExcelResultDTO importExcel(MultipartFile excel) throws IOException { + if (Objects.isNull(excel)) { + throw new ParamsException("Excel不能为空"); + } + if (!excel.getOriginalFilename().endsWith(IFileConstant.EXCEL_SUFFIX_XLS) && + !excel.getOriginalFilename().endsWith(IFileConstant.EXCEL_SUFFIX_XLSX)) { + throw new ParamsException("文件格式为Excel"); + } + return userService.importExcel(excel); + } + @ApiOperation(value = "用户列表", notes = "用户列表接口") @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) @GetMapping("list") @@ -109,4 +152,14 @@ public class UserController extends DefaultBaseController { return userService.listPage(page); } + @ApiOperation(value = "用户详情", notes = "用户详情接口") + @ApiImplicitParams({ + @ApiImplicitParam(name = "userId", value = "用户ID", paramType = "path") + }) + @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) + @GetMapping("get/{userId}") + public UserDTO getUser(@PathVariable("userId") String userId) { + return userService.get(userId); + } + } diff --git a/service-user/src/main/java/ink/wgink/module/user/controller/route/UserRouteController.java b/service-user/src/main/java/ink/wgink/module/user/controller/route/UserRouteController.java new file mode 100644 index 00000000..4eac1351 --- /dev/null +++ b/service-user/src/main/java/ink/wgink/module/user/controller/route/UserRouteController.java @@ -0,0 +1,72 @@ +package ink.wgink.module.user.controller.route; + +import ink.wgink.interfaces.consts.ISystemConstant; +import ink.wgink.util.ResourceUtil; +import ink.wgink.util.request.RequestUtil; +import io.swagger.annotations.Api; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; + +/** + * When you feel like quitting. Think about why you started + * 当你想要放弃的时候,想想当初你为何开始 + * + * @ClassName: UserRouteController + * @Description: 用户路由 + * @Author: wanggeng + * @Date: 2021/2/5 7:50 下午 + * @Version: 1.0 + */ +@Api(tags = ISystemConstant.API_TAGS_SYSTEM_PREFIX + "用户路由接口") +@Controller +@RequestMapping(ISystemConstant.ROUTE_PREFIX + "/user") +public class UserRouteController { + + @GetMapping("list") + public ModelAndView list() { + return new ModelAndView("user/list"); + } + + @GetMapping("save") + public ModelAndView save() { + return new ModelAndView("user/save"); + } + + @GetMapping("update") + public ModelAndView update() { + return new ModelAndView("user/update"); + } + + @GetMapping("rest-password") + public ModelAndView restPassword() { + return new ModelAndView("user/rest-password"); + } + + @GetMapping("update-username") + public ModelAndView updateUsername() { + return new ModelAndView("user/update-username"); + } + + @GetMapping("update-password") + public ModelAndView updatePassword() { + return new ModelAndView("user/update-password"); + } + + @GetMapping("upload/upload-excel") + public ModelAndView uploadExcel() { + return new ModelAndView("user/upload/upload-excel"); + } + + @GetMapping("upload/upload-excel-template") + public void excelTemplate(HttpServletResponse response) throws IOException { + File template = ResourceUtil.getResourceFile("templates/user/upload/upload-excel-template.xls"); + RequestUtil.download(response, template, "用户导入模板.xls"); + } + +} diff --git a/service-user/src/main/java/ink/wgink/module/user/dao/IUserDao.java b/service-user/src/main/java/ink/wgink/module/user/dao/IUserDao.java index f7e13f66..a255011f 100644 --- a/service-user/src/main/java/ink/wgink/module/user/dao/IUserDao.java +++ b/service-user/src/main/java/ink/wgink/module/user/dao/IUserDao.java @@ -24,6 +24,13 @@ import java.util.Map; @Repository public interface IUserDao { + /** + * 建表 + * + * @throws UpdateException + */ + void createTable() throws UpdateException; + /** * 新增 * diff --git a/service-user/src/main/java/ink/wgink/module/user/excel/UserExcel.java b/service-user/src/main/java/ink/wgink/module/user/excel/UserExcel.java new file mode 100644 index 00000000..6b805ce1 --- /dev/null +++ b/service-user/src/main/java/ink/wgink/module/user/excel/UserExcel.java @@ -0,0 +1,72 @@ +package ink.wgink.module.user.excel; + +import com.alibaba.excel.annotation.ExcelProperty; + +/** + * When you feel like quitting. Think about why you started + * 当你想要放弃的时候,想想当初你为何开始 + * + * @ClassName: UserExcel + * @Description: 用户Excel + * @Author: WangGeng + * @Date: 2020/1/6 11:11 下午 + * @Version: 1.0 + **/ +public class UserExcel { + + @ExcelProperty(index = 0) + private String userUsername; + @ExcelProperty(index = 1) + private String userName; + @ExcelProperty(index = 2) + private String userPhone; + @ExcelProperty(index = 3) + private String userEmail; + + public String getUserUsername() { + return userUsername == null ? "" : userUsername.trim().replaceAll("\\s", ""); + } + + public void setUserUsername(String userUsername) { + this.userUsername = userUsername; + } + + public String getUserName() { + return userName == null ? "" : userName.trim().replaceAll("\\s", ""); + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getUserPhone() { + return userPhone == null ? "" : userPhone.trim().replaceAll("\\s", ""); + } + + public void setUserPhone(String userPhone) { + this.userPhone = userPhone; + } + + public String getUserEmail() { + return userEmail == null ? "" : userEmail.trim().replaceAll("\\s", ""); + } + + public void setUserEmail(String userEmail) { + this.userEmail = userEmail; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("{"); + sb.append("\"userUsername\":") + .append("\"").append(userUsername).append("\""); + sb.append(",\"userName\":") + .append("\"").append(userName).append("\""); + sb.append(",\"userPhone\":") + .append("\"").append(userPhone).append("\""); + sb.append(",\"userEmail\":") + .append("\"").append(userEmail).append("\""); + sb.append('}'); + return sb.toString(); + } +} diff --git a/service-user/src/main/java/ink/wgink/module/user/excel/UserExcelError.java b/service-user/src/main/java/ink/wgink/module/user/excel/UserExcelError.java new file mode 100644 index 00000000..33848bd4 --- /dev/null +++ b/service-user/src/main/java/ink/wgink/module/user/excel/UserExcelError.java @@ -0,0 +1,84 @@ +package ink.wgink.module.user.excel; + +import com.alibaba.excel.annotation.ExcelProperty; + +/** + * When you feel like quitting. Think about why you started + * 当你想要放弃的时候,想想当初你为何开始 + * + * @ClassName: UserExcelError + * @Description: 人员Excel异常 + * @Author: WangGeng + * @Date: 2020/1/7 12:28 下午 + * @Version: 1.0 + **/ +public class UserExcelError { + + @ExcelProperty(index = 0) + private String userUsername; + @ExcelProperty(index = 1) + private String userName; + @ExcelProperty(index = 2) + private String userPhone; + @ExcelProperty(index = 3) + private String userEmail; + @ExcelProperty(index = 4) + private String reason; + + public String getUserUsername() { + return userUsername == null ? "" : userUsername.trim(); + } + + public void setUserUsername(String userUsername) { + this.userUsername = userUsername; + } + + public String getUserName() { + return userName == null ? "" : userName.trim(); + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getUserPhone() { + return userPhone == null ? "" : userPhone.trim(); + } + + public void setUserPhone(String userPhone) { + this.userPhone = userPhone; + } + + public String getUserEmail() { + return userEmail == null ? "" : userEmail.trim(); + } + + public void setUserEmail(String userEmail) { + this.userEmail = userEmail; + } + + public String getReason() { + return reason == null ? "" : reason.trim(); + } + + public void setReason(String reason) { + this.reason = reason; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("{"); + sb.append("\"userUsername\":") + .append("\"").append(userUsername).append("\""); + sb.append(",\"userName\":") + .append("\"").append(userName).append("\""); + sb.append(",\"userPhone\":") + .append("\"").append(userPhone).append("\""); + sb.append(",\"userEmail\":") + .append("\"").append(userEmail).append("\""); + sb.append(",\"reason\":") + .append("\"").append(reason).append("\""); + sb.append('}'); + return sb.toString(); + } +} \ No newline at end of file diff --git a/service-user/src/main/java/ink/wgink/module/user/excel/UserExcelListener.java b/service-user/src/main/java/ink/wgink/module/user/excel/UserExcelListener.java new file mode 100644 index 00000000..baeaaaa2 --- /dev/null +++ b/service-user/src/main/java/ink/wgink/module/user/excel/UserExcelListener.java @@ -0,0 +1,48 @@ +package ink.wgink.module.user.excel; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * When you feel like quitting. Think about why you started + * 当你想要放弃的时候,想想当初你为何开始 + * + * @ClassName: UserExcelListener + * @Description: 用户Excel监听器 + * @Author: WangGeng + * @Date: 2020/1/6 11:06 下午 + * @Version: 1.0 + **/ +public abstract class UserExcelListener extends AnalysisEventListener { + + private static final Logger LOG = LoggerFactory.getLogger(UserExcelListener.class); + private List userExcels = new ArrayList<>(0); + + @Override + public void invoke(UserExcel userExcel, AnalysisContext analysisContext) { + userExcels.add(userExcel); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext analysisContext) { + try { + listUserExcel(userExcels); + } catch (Exception e) { + LOG.error(e.getMessage()); + } + userExcels.clear(); + } + + /** + * 获取用户列表 + * + * @param userExcels + */ + public abstract void listUserExcel(List userExcels) throws Exception; + +} diff --git a/service-user/src/main/java/ink/wgink/module/user/pojo/vos/RestPasswordVO.java b/service-user/src/main/java/ink/wgink/module/user/pojo/vos/RestPasswordVO.java new file mode 100644 index 00000000..de47a922 --- /dev/null +++ b/service-user/src/main/java/ink/wgink/module/user/pojo/vos/RestPasswordVO.java @@ -0,0 +1,40 @@ +package ink.wgink.module.user.pojo.vos; + +import ink.wgink.annotation.CheckEmptyAnnotation; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +/** + * When you feel like quitting. Think about why you started + * 当你想要放弃的时候,想想当初你为何开始 + * + * @ClassName: UpdateUserUsernameVO + * @Description: 修改用户名 + * @Author: WangGeng + * @Date: 2020/5/21 10:04 下午 + * @Version: 1.0 + **/ +@ApiModel +public class RestPasswordVO { + + @ApiModelProperty(name = "updateReason", value = "修改原因") + @CheckEmptyAnnotation(name = "修改原因") + private String updateReason; + + public String getUpdateReason() { + return updateReason == null ? "" : updateReason.trim(); + } + + public void setUpdateReason(String updateReason) { + this.updateReason = updateReason; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("{"); + sb.append("\"updateReason\":\"") + .append(updateReason).append('\"'); + sb.append('}'); + return sb.toString(); + } +} diff --git a/service-user/src/main/java/ink/wgink/module/user/pojo/vos/UpdateUsernameVO.java b/service-user/src/main/java/ink/wgink/module/user/pojo/vos/UpdateUsernameVO.java new file mode 100644 index 00000000..c57371fb --- /dev/null +++ b/service-user/src/main/java/ink/wgink/module/user/pojo/vos/UpdateUsernameVO.java @@ -0,0 +1,53 @@ +package ink.wgink.module.user.pojo.vos; + +import ink.wgink.annotation.CheckEmptyAnnotation; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +/** + * When you feel like quitting. Think about why you started + * 当你想要放弃的时候,想想当初你为何开始 + * + * @ClassName: UpdateUserUsernameVO + * @Description: 修改用户名 + * @Author: WangGeng + * @Date: 2020/5/21 10:04 下午 + * @Version: 1.0 + **/ +@ApiModel +public class UpdateUsernameVO { + + @ApiModelProperty(name = "username", value = "用户名") + @CheckEmptyAnnotation(name = "用户名", regex = "username") + private String username; + @ApiModelProperty(name = "updateReason", value = "修改原因") + @CheckEmptyAnnotation(name = "修改原因") + private String updateReason; + + public String getUsername() { + return username == null ? "" : username.trim(); + } + + public void setUsername(String username) { + this.username = username; + } + + public String getUpdateReason() { + return updateReason == null ? "" : updateReason.trim(); + } + + public void setUpdateReason(String updateReason) { + this.updateReason = updateReason; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("{"); + sb.append("\"username\":\"") + .append(username).append('\"'); + sb.append(",\"updateReason\":\"") + .append(updateReason).append('\"'); + sb.append('}'); + return sb.toString(); + } +} diff --git a/service-user/src/main/java/ink/wgink/module/user/service/IUserService.java b/service-user/src/main/java/ink/wgink/module/user/service/IUserService.java index 6a7f8936..1ddd9817 100644 --- a/service-user/src/main/java/ink/wgink/module/user/service/IUserService.java +++ b/service-user/src/main/java/ink/wgink/module/user/service/IUserService.java @@ -1,9 +1,14 @@ package ink.wgink.module.user.service; import ink.wgink.interfaces.user.IUserBaseService; +import ink.wgink.module.user.pojo.vos.RestPasswordVO; +import ink.wgink.module.user.pojo.vos.UpdateUsernameVO; import ink.wgink.pojo.dtos.user.UserDTO; import ink.wgink.module.user.pojo.vos.UserVO; +import ink.wgink.pojo.result.UploadExcelResultDTO; +import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; import java.util.List; import java.util.Map; @@ -46,17 +51,36 @@ public interface IUserService extends IUserBaseService { /** * 修改用户 * - * @param userId - * @param userVO + * @param userId 用户ID + * @param userVO 用户内容 * @return */ void update(String userId, UserVO userVO); /** - * 用户列表 + * 重置用户密码 * - * @param params + * @param userId 用户ID + * @param restPasswordVO 重置密码 * @return */ - List list(Map params); + void resetPassword(String userId, RestPasswordVO restPasswordVO); + + /** + * 修改用户名 + * + * @param userId 用户ID + * @param updateUsernameVO 更新用户名 + */ + void updateUsername(String userId, UpdateUsernameVO updateUsernameVO); + + /** + * 导入Excel + * + * @param excel + * @return + * @throws IOException + */ + UploadExcelResultDTO importExcel(MultipartFile excel) throws IOException; + } diff --git a/service-user/src/main/java/ink/wgink/module/user/service/impl/UserServiceImpl.java b/service-user/src/main/java/ink/wgink/module/user/service/impl/UserServiceImpl.java index a377f9e2..fbe4e08f 100644 --- a/service-user/src/main/java/ink/wgink/module/user/service/impl/UserServiceImpl.java +++ b/service-user/src/main/java/ink/wgink/module/user/service/impl/UserServiceImpl.java @@ -1,23 +1,36 @@ package ink.wgink.module.user.service.impl; +import com.alibaba.excel.EasyExcel; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import ink.wgink.common.base.DefaultBaseService; import ink.wgink.exceptions.SearchException; +import ink.wgink.module.file.excel.error.AbstractErrorExcelHandler; +import ink.wgink.module.file.service.IFileService; +import ink.wgink.module.user.dao.IUserDao; +import ink.wgink.module.user.excel.UserExcel; +import ink.wgink.module.user.excel.UserExcelError; +import ink.wgink.module.user.excel.UserExcelListener; +import ink.wgink.module.user.pojo.vos.RestPasswordVO; +import ink.wgink.module.user.pojo.vos.UpdateUsernameVO; +import ink.wgink.module.user.pojo.vos.UserVO; +import ink.wgink.module.user.service.IUserService; import ink.wgink.pojo.ListPage; import ink.wgink.pojo.dtos.user.UserDTO; import ink.wgink.pojo.result.SuccessResultList; -import ink.wgink.module.user.dao.IUserDao; -import ink.wgink.module.user.pojo.vos.UserVO; -import ink.wgink.module.user.service.IUserService; +import ink.wgink.pojo.result.UploadExcelResultDTO; import ink.wgink.util.UUIDUtil; import ink.wgink.util.map.HashMapUtil; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -38,9 +51,13 @@ public class UserServiceImpl extends DefaultBaseService implements IUserService private IUserDao userDao; @Autowired private PasswordEncoder passwordEncoder; + @Value("${user.default-password:88888888}") + private String defaultPassword; + @Autowired + private IFileService fileService; @Override - public synchronized void save(UserVO userVO) { + public void save(UserVO userVO) { saveUser(userVO, false); } @@ -56,7 +73,7 @@ public class UserServiceImpl extends DefaultBaseService implements IUserService * @param isRegister 是否注册 */ private void saveUser(UserVO userVO, boolean isRegister) { - UserDTO userDTO = get(userVO.getUserUsername()); + UserDTO userDTO = getByUsername(userVO.getUserUsername()); if (userDTO != null) { throw new SearchException("用户已经存在"); } @@ -75,7 +92,7 @@ public class UserServiceImpl extends DefaultBaseService implements IUserService @Override public void remove(List ids) { Map params = getHashMap(2); - params.put("ids", ids); + params.put("userIds", ids); setUpdateInfo(params); userDao.remove(params); } @@ -87,9 +104,88 @@ public class UserServiceImpl extends DefaultBaseService implements IUserService } Map params = HashMapUtil.beanToMap(userVO); setUpdateInfo(params); + params.put("userId", userId); userDao.update(params); } + @Override + public void resetPassword(String userId, RestPasswordVO restPasswordVO) { + UserDTO userDTO = get(userId); + if (userDTO == null) { + throw new SearchException("修改用户不存在"); + } + Map params = getHashMap(4); + params.put("userId", userId); + params.put("userPassword", passwordEncoder.encode(DigestUtils.md5Hex(DigestUtils.md5Hex(DigestUtils.md5Hex(defaultPassword))))); + userDao.updatePassword(params); + } + + @Override + public void updateUsername(String userId, UpdateUsernameVO updateUsernameVO) { + UserDTO oldUserDTO = get(userId); + if (oldUserDTO == null) { + throw new SearchException("用户不存在"); + } + Map params = getHashMap(2); + params.put("userUsername", updateUsernameVO.getUsername()); + UserDTO userDTO = userDao.get(params); + if (userDTO != null && !StringUtils.equals(userId, userDTO.getUserId())) { + throw new SearchException("新用户名已经存在"); + } + params.put("userId", userId); + userDao.updateUsername(params); + } + + @Override + public UploadExcelResultDTO importExcel(MultipartFile excel) throws IOException { + Map params = getHashMap(16); + // 错误列表 + List userExcelErrors = new ArrayList<>(); + long startTime = System.currentTimeMillis(); + LOG.debug("读取Excel"); + EasyExcel.read(excel.getInputStream(), UserExcel.class, new UserExcelListener() { + @Override + public void listUserExcel(List userExcels) throws Exception { + for (UserExcel userExcel : userExcels) { + // 判断昵称存在 + String userUsername = userExcel.getUserUsername(); + String userName = userExcel.getUserName(); + if (StringUtils.isBlank(userUsername) || StringUtils.isBlank(userName)) { + userExcelErrors.add(getUserExcelError(userExcel, "必填值存在空值,导入失败")); + continue; + } + String userPhone = userExcel.getUserPhone(); + String userEmail = userExcel.getUserEmail(); + try { + // 保存用户 + UserVO userVO = new UserVO(); + userVO.setUserUsername(userUsername); + userVO.setUserName(userName); + userVO.setUserPassword(defaultPassword); + userVO.setUserPhone(userPhone); + userVO.setUserEmail(userEmail); + userVO.setUserType(2); + userVO.setUserState(0); + saveUser(userVO, false); + } catch (Exception e) { + userExcelErrors.add(getUserExcelError(userExcel, e.getMessage())); + } + } + } + }).headRowNumber(2).sheet().doRead(); + long endTime = System.currentTimeMillis(); + String excelFileId = null; + if (userExcelErrors.size() > 0) { + excelFileId = new AbstractErrorExcelHandler(fileService) { + @Override + public List> excelHeaderNames() { + return simpleExcelHeader(new String[]{"用户名", "昵称", "手机", "邮箱", "错误原因"}); + } + }.saveErrorExcel(userExcelErrors); + } + return new UploadExcelResultDTO(userExcelErrors.size(), endTime - startTime, excelFileId); + } + @Override public UserDTO get(String userId) { Map params = getHashMap(2); @@ -111,6 +207,13 @@ public class UserServiceImpl extends DefaultBaseService implements IUserService return list(params); } + @Override + public List listByUsernames(List usernames) { + Map params = getHashMap(2); + params.put("userUsernames", usernames); + return list(params); + } + @Override public List list(Map params) { return userDao.list(params); @@ -124,4 +227,21 @@ public class UserServiceImpl extends DefaultBaseService implements IUserService return new SuccessResultList<>(userDTOs, pageInfo.getPageNum(), pageInfo.getTotal()); } + /** + * Excel导入错误对象 + * + * @param userExcel + * @param reason + * @return + */ + private UserExcelError getUserExcelError(UserExcel userExcel, String reason) { + UserExcelError userExcelError = new UserExcelError(); + userExcelError.setUserUsername(userExcel.getUserUsername()); + userExcelError.setUserName(userExcel.getUserName()); + userExcelError.setUserPhone(userExcel.getUserPhone()); + userExcelError.setUserEmail(userExcel.getUserEmail()); + userExcelError.setReason(reason); + return userExcelError; + } + } diff --git a/service-user/src/main/java/ink/wgink/module/user/startup/ServiceUserStartUp.java b/service-user/src/main/java/ink/wgink/module/user/startup/ServiceUserStartUp.java new file mode 100644 index 00000000..b7a5da2a --- /dev/null +++ b/service-user/src/main/java/ink/wgink/module/user/startup/ServiceUserStartUp.java @@ -0,0 +1,62 @@ +package ink.wgink.module.user.startup; + +import ink.wgink.module.user.dao.IUserDao; +import ink.wgink.pojo.dtos.user.UserDTO; +import ink.wgink.util.date.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +/** + * When you feel like quitting. Think about why you started + * 当你想要放弃的时候,想想当初你为何开始 + * + * @ClassName: ServiceUserStartUp + * @Description: 用户业务 + * @Author: wanggeng + * @Date: 2021/2/5 8:59 下午 + * @Version: 1.0 + */ +@Component +public class ServiceUserStartUp implements ApplicationRunner { + private static final Logger LOG = LoggerFactory.getLogger(ServiceUserStartUp.class); + + @Autowired + private IUserDao userDao; + + @Override + public void run(ApplicationArguments args) throws Exception { + initTable(); + } + + /** + * 建表 + */ + private void initTable() { + LOG.debug("创建 sys_user 表"); + userDao.createTable(); + LOG.debug("检查 admin 是否存在"); + Map params = new HashMap<>(2); + params.put("userUsername", "admin"); + UserDTO userDTO = userDao.get(params); + if (userDTO == null) { + LOG.debug("admin 不存在,新增"); + String currentDate = DateUtil.getTime(); + params.put("userId", "1"); + params.put("userPassword", ""); + params.put("userName", "超级管理员"); + params.put("gmtCreate", currentDate); + params.put("creator", "1"); + params.put("gmtModified", currentDate); + params.put("modifier", "1"); + params.put("isDelete", 0); + userDao.save(params); + } + } +} diff --git a/service-user/src/main/resources/mybatis/mapper/user-mapper.xml b/service-user/src/main/resources/mybatis/mapper/user-mapper.xml index bee6e607..f6fb6f54 100644 --- a/service-user/src/main/resources/mybatis/mapper/user-mapper.xml +++ b/service-user/src/main/resources/mybatis/mapper/user-mapper.xml @@ -45,6 +45,43 @@ + + CREATE TABLE IF NOT EXISTS `sys_user` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `user_id` char(36) NOT NULL, + `user_username` varchar(255) NOT NULL COMMENT '用户名', + `user_password` varchar(255) DEFAULT NULL COMMENT '密码', + `user_name` varchar(255) DEFAULT NULL COMMENT '姓名', + `user_phone` varchar(20) DEFAULT NULL COMMENT '电话', + `user_email` varchar(255) DEFAULT NULL COMMENT '邮箱', + `user_ukey` varchar(32) DEFAULT NULL COMMENT 'UKey', + `user_ukey_electronic_secret_key` text COMMENT '用户UKey电子秘钥', + `user_type` int(2) DEFAULT '2', + `user_state` int(2) DEFAULT '0' COMMENT '用户状态', + `user_avatar` char(36) DEFAULT NULL COMMENT '头像', + `user_longitude` varchar(255) DEFAULT '0' COMMENT '经度', + `user_latitude` varchar(255) DEFAULT '0' COMMENT '纬度', + `last_login_address` varchar(255) DEFAULT NULL COMMENT '最后登录地址', + `last_login_time` datetime DEFAULT NULL COMMENT '最后登录时间', + `login_type` int(1) DEFAULT '1' COMMENT '登录类型', + `gmt_password_modified` date DEFAULT NULL COMMENT '密码修改时间', + `remarks` varchar(255) DEFAULT NULL COMMENT '备注', + `creator` char(36) DEFAULT NULL, + `gmt_create` datetime DEFAULT NULL, + `modifier` char(36) DEFAULT NULL, + `gmt_modified` datetime DEFAULT NULL, + `is_delete` int(2) DEFAULT '0', + PRIMARY KEY (`id`,`user_id`,`user_username`), + UNIQUE KEY `user_username` (`user_username`) USING BTREE, + KEY `user_id` (`user_id`) USING BTREE, + KEY `user_name` (`user_name`) USING BTREE, + KEY `user_id_2` (`user_id`,`user_username`,`user_name`) USING BTREE, + KEY `user_id_3` (`user_id`,`user_name`) USING BTREE, + KEY `user_id_4` (`user_id`,`user_name`,`user_avatar`) USING BTREE, + KEY `is_delete_idx` (`is_delete`) USING BTREE + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + INSERT INTO sys_user( diff --git a/service-user/src/main/resources/templates/user/list.html b/service-user/src/main/resources/templates/user/list.html new file mode 100644 index 00000000..97892903 --- /dev/null +++ b/service-user/src/main/resources/templates/user/list.html @@ -0,0 +1,349 @@ + + + + + + + + + + + + + + +
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+ + +
+
+ + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/service-user/src/main/resources/templates/user/rest-password.html b/service-user/src/main/resources/templates/user/rest-password.html new file mode 100644 index 00000000..116f7e81 --- /dev/null +++ b/service-user/src/main/resources/templates/user/rest-password.html @@ -0,0 +1,86 @@ + + + + + + + + + + + + + +
+
+
+
+
+ +
+ +
+
+ +
+
+ +
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/service-user/src/main/resources/templates/user/save.html b/service-user/src/main/resources/templates/user/save.html new file mode 100644 index 00000000..b0d7aa28 --- /dev/null +++ b/service-user/src/main/resources/templates/user/save.html @@ -0,0 +1,169 @@ + + + + + + + + + + + + + +
+
+ +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+ + +
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/service-user/src/main/resources/templates/user/update-password.html b/service-user/src/main/resources/templates/user/update-password.html new file mode 100644 index 00000000..1e0bb16f --- /dev/null +++ b/service-user/src/main/resources/templates/user/update-password.html @@ -0,0 +1,104 @@ + + + + + + + + + + + + + +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+ +
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/service-user/src/main/resources/templates/user/update-username.html b/service-user/src/main/resources/templates/user/update-username.html new file mode 100644 index 00000000..093eaa9f --- /dev/null +++ b/service-user/src/main/resources/templates/user/update-username.html @@ -0,0 +1,102 @@ + + + + + + + + + + + + + +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+ +
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/service-user/src/main/resources/templates/user/update.html b/service-user/src/main/resources/templates/user/update.html new file mode 100644 index 00000000..43ca8748 --- /dev/null +++ b/service-user/src/main/resources/templates/user/update.html @@ -0,0 +1,198 @@ + + + + + + + + + + + + + +
+
+ +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+ + +
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/service-user/src/main/resources/templates/user/upload/upload-excel-template.xls b/service-user/src/main/resources/templates/user/upload/upload-excel-template.xls new file mode 100644 index 0000000000000000000000000000000000000000..1e5d85f4bd05427d682e75f7ffff0633dca80a5d GIT binary patch literal 18944 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 literal 0 HcmV?d00001 diff --git a/service-user/src/main/resources/templates/user/upload/upload-excel.html b/service-user/src/main/resources/templates/user/upload/upload-excel.html new file mode 100644 index 00000000..f0092d12 --- /dev/null +++ b/service-user/src/main/resources/templates/user/upload/upload-excel.html @@ -0,0 +1,90 @@ + + + + + + + + + + + + + +
+
+
+
下载“下载模板”整理数据,点击“导入数据”上传,格式为xls或xlsx
+ + +
+
+
+ + + + \ No newline at end of file