diff --git a/antdsp-admin/pom.xml b/antdsp-admin/pom.xml index ccf175bfb131d2f3e86a188287b3b647ae316713..a037d9bdcf1946d9c095457e74a60b587c853934 100644 --- a/antdsp-admin/pom.xml +++ b/antdsp-admin/pom.xml @@ -1,23 +1,23 @@ - + 4.0.0 - + com.antdsp antdsp 0.0.1 ../pom.xml - + antdsp-admin antdsp-admin - + UTF-8 - + org.springframework.boot @@ -29,18 +29,24 @@ 1.3.2 - - org.springframework.boot - spring-boot-devtools - true - + + org.springframework.boot + spring-boot-devtools + true + + + + com.qiniu + qiniu-java-sdk + 7.2.22 + com.antdsp antdsp-core 0.0.1 - + @@ -52,9 +58,9 @@ UTF-8 true true - - com.antdsp.admin.ApplicationStarter - ZIP + + com.antdsp.admin.ApplicationStarter + ZIP diff --git a/antdsp-admin/src/main/java/com/antdsp/ApplicationStarter.java b/antdsp-admin/src/main/java/com/antdsp/ApplicationStarter.java index ad9cb78ef705ecc167541ccd201e174554697a05..fa7aaca66cf6b3f8c82d2f0f6cdfea07d4fd6303 100644 --- a/antdsp-admin/src/main/java/com/antdsp/ApplicationStarter.java +++ b/antdsp-admin/src/main/java/com/antdsp/ApplicationStarter.java @@ -4,11 +4,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.core.env.Environment; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.transaction.annotation.EnableTransactionManagement; import com.antdsp.common.AntdspRepositoryFactoryBean; +import com.antdsp.utils.ApplicationContextUtil; @SpringBootApplication @EnableTransactionManagement @@ -21,7 +22,8 @@ public class ApplicationStarter { SpringApplication app = new SpringApplication(ApplicationStarter.class); - Environment environment = app.run(args).getEnvironment(); + ConfigurableApplicationContext context = app.run(args); + ApplicationContextUtil.setApplicationContext(context); logger.info("antdsp is started"); diff --git a/antdsp-admin/src/main/java/com/antdsp/common/annotation/OperationLog.java b/antdsp-admin/src/main/java/com/antdsp/common/annotation/OperationLog.java new file mode 100644 index 0000000000000000000000000000000000000000..529482cdf5c0d3b973fe74c9947c4e30dabf5025 --- /dev/null +++ b/antdsp-admin/src/main/java/com/antdsp/common/annotation/OperationLog.java @@ -0,0 +1,21 @@ +package com.antdsp.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface OperationLog { + + /** + * 操作名称 + * @return + */ + String name() default ""; + +} diff --git a/antdsp-admin/src/main/java/com/antdsp/common/aspect/OperationLogAspect.java b/antdsp-admin/src/main/java/com/antdsp/common/aspect/OperationLogAspect.java new file mode 100644 index 0000000000000000000000000000000000000000..f6586f3898c8c3ea9327965c12258313b1ac07e6 --- /dev/null +++ b/antdsp-admin/src/main/java/com/antdsp/common/aspect/OperationLogAspect.java @@ -0,0 +1,104 @@ +package com.antdsp.common.aspect; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.shiro.SecurityUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import com.alibaba.fastjson.JSON; +import com.antdsp.common.annotation.OperationLog; +import com.antdsp.dao.jpa.system.SystemLogJpa; +import com.antdsp.data.entity.User; +import com.antdsp.data.entity.system.SystemLog; + +@Aspect +@Component +public class OperationLogAspect { + + private static final String X_REAL_IP ="x-real-ip"; + + @Autowired + private SystemLogJpa systemLogJpa; + + @Pointcut(value="@annotation(com.antdsp.common.annotation.OperationLog)") + public void pointCut() {} + + @Around(value="pointCut()") + public Object around(ProceedingJoinPoint joinPoint) throws Throwable { + long time = System.currentTimeMillis(); + + try { + Object result = joinPoint.proceed(); + time = System.currentTimeMillis() - time; + this.saveLog(joinPoint, time); + return result; + }catch(Exception e) { + + } + return new Object(); + } + + private void saveLog(ProceedingJoinPoint joinPoint , long executeTime) { + MethodSignature ms = (MethodSignature) joinPoint.getSignature(); + Method excuteMthod = ms.getMethod(); + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + + User current = (User) SecurityUtils.getSubject().getPrincipal(); + OperationLog optLog = excuteMthod.getAnnotation(OperationLog.class); + + SystemLog systemLog = new SystemLog(); + systemLog.setCreator(""); + systemLog.setModifier(""); + systemLog.setId(null); + systemLog.setOptName(optLog.name()); + systemLog.setOptorId(current.getId()); + /** + * 由于前后端分离,ant design使用代理的方式访问后台,因此getRemoteHost() 获取的总是ant design 项目的代理地址,也即本机服务器的地址 + * 因此使用nginx 做反向代理,并配置上以下信息, + * proxy_set_header Host $http_host; + * proxy_set_header X-Real-IP $remote_addr; + * + * x-real-ip 即客户端ip地址 + */ + systemLog.setOptorIp(getIp(request)); + systemLog.setOptorName(current.getLoginname()); + systemLog.setRunTime(executeTime); + systemLog.setOptType(request.getMethod()); + systemLog.setOptURI(request.getRequestURI()); + systemLog.setOptDetail(dealDetail(ms.getParameterNames(), joinPoint.getArgs())); + systemLog.onPreInsert(); + ms.getParameterNames(); + systemLogJpa.save(systemLog); + } + + private String dealDetail(String[] paramNames, Object[] args) { + if(args != null && args.length > 0) { + Map map = new HashMap<>(); + for(int i=0; i< args.length; i++) { + map.put(paramNames[i], args[i]); + } + return JSON.toJSONString(map); + } + return ""; + } + + private String getIp(HttpServletRequest request) { + String ip = request.getHeader(X_REAL_IP); + if(ip == null || ip.length() == 0 || "unknown".equals(ip)) { + ip = request.getRemoteHost(); + } + return ip; + } +} diff --git a/antdsp-admin/src/main/java/com/antdsp/common/configuration/ShiroConfiguration.java b/antdsp-admin/src/main/java/com/antdsp/common/configuration/ShiroConfiguration.java index 09fa7ef61a4e170a77a60474c7fca64645de9291..47a91316981777e6637ec5ab04846a8e320f6d5e 100644 --- a/antdsp-admin/src/main/java/com/antdsp/common/configuration/ShiroConfiguration.java +++ b/antdsp-admin/src/main/java/com/antdsp/common/configuration/ShiroConfiguration.java @@ -58,6 +58,7 @@ public class ShiroConfiguration { filterMap.put("/login","anon"); filterMap.put("/logout","anon"); filterMap.put("/unauth","anon"); + filterMap.put("/captcha.jpg","anon"); filterMap.put("/**","authc"); shiroFilterFactory.setFilterChainDefinitionMap(filterMap); diff --git a/antdsp-admin/src/main/java/com/antdsp/common/exception/AntdspException.java b/antdsp-admin/src/main/java/com/antdsp/common/exception/AntdspException.java index 722dd9410a0be506b64bf7e4822cc4764086543a..5feefc9cd7ac5ecd044744daa8169ae1b566ff47 100644 --- a/antdsp-admin/src/main/java/com/antdsp/common/exception/AntdspException.java +++ b/antdsp-admin/src/main/java/com/antdsp/common/exception/AntdspException.java @@ -27,4 +27,10 @@ public class AntdspException extends RuntimeException{ public AntdspResponse unauthorizedException() { return new AntdspResponse( ResponseCode.FORBIDDEN , false , "权限不足,请联系管理员."); } + + @ExceptionHandler(value= {CaptchaException.class}) + @ResponseBody + public AntdspResponse captchaException(CaptchaException e) { + return new AntdspResponse( ResponseCode.ERROR , false , e.getMessage()); + } } diff --git a/antdsp-admin/src/main/java/com/antdsp/common/exception/CaptchaException.java b/antdsp-admin/src/main/java/com/antdsp/common/exception/CaptchaException.java new file mode 100644 index 0000000000000000000000000000000000000000..af219016be7098d59c785c53451b11ce861c9675 --- /dev/null +++ b/antdsp-admin/src/main/java/com/antdsp/common/exception/CaptchaException.java @@ -0,0 +1,12 @@ +package com.antdsp.common.exception; + +public class CaptchaException extends RuntimeException { + + public CaptchaException() { + super(); + } + + public CaptchaException(String message) { + super(message); + } +} diff --git a/antdsp-admin/src/main/java/com/antdsp/common/shiro/AntdspShiroSessionManager.java b/antdsp-admin/src/main/java/com/antdsp/common/shiro/AntdspShiroSessionManager.java index 201e5ea3456a1a3199079be63ea31ec39c573cfa..aeb09dc0de02b70ba0626c2b63c9a305ce88e3b2 100644 --- a/antdsp-admin/src/main/java/com/antdsp/common/shiro/AntdspShiroSessionManager.java +++ b/antdsp-admin/src/main/java/com/antdsp/common/shiro/AntdspShiroSessionManager.java @@ -11,9 +11,10 @@ import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.apache.shiro.web.util.WebUtils; import org.springframework.util.StringUtils; +import com.antdsp.utils.Constants; + public class AntdspShiroSessionManager extends DefaultWebSessionManager{ - private static final String AUTHORIZATION = "Authorization"; private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request"; public AntdspShiroSessionManager() { @@ -25,7 +26,7 @@ public class AntdspShiroSessionManager extends DefaultWebSessionManager{ // TODO Auto-generated method stub HttpServletRequest httpRequest = WebUtils.toHttp(request); - String sessionId = httpRequest.getHeader(AUTHORIZATION); + String sessionId = httpRequest.getHeader(Constants.AUTHORIZATION); if(!StringUtils.isEmpty(sessionId)) { request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionId); diff --git a/antdsp-admin/src/main/java/com/antdsp/utils/FileUploadUtils.java b/antdsp-admin/src/main/java/com/antdsp/utils/FileUploadUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..b5bd355e7943bdfeef2d29685f2ccd7f2372371a --- /dev/null +++ b/antdsp-admin/src/main/java/com/antdsp/utils/FileUploadUtils.java @@ -0,0 +1,33 @@ +package com.antdsp.utils; + +import org.springframework.beans.factory.annotation.Value; + +import com.qiniu.common.Zone; +import com.qiniu.storage.Configuration; +import com.qiniu.storage.UploadManager; + +public class FileUploadUtils { + + @Value("${qiniu.accessKey}") + private static String accessKey ; + @Value("${qiniu.secretKey}") + private static String secretKey; + @Value("${qiniu.bucket}") + private static String bucket; + + private static String key; + + + + private final static Configuration cfg = new Configuration(Zone.zone0()); + + public static String uploadImage() { + + UploadManager uploadManager = new UploadManager(cfg); + + System.out.println(FileUploadUtils.accessKey); + + return null; + } + +} diff --git a/antdsp-admin/src/main/java/com/antdsp/utils/ShiroUtils.java b/antdsp-admin/src/main/java/com/antdsp/utils/ShiroUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..ecf636dccbad83ba080c6539533342b002b0fb1c --- /dev/null +++ b/antdsp-admin/src/main/java/com/antdsp/utils/ShiroUtils.java @@ -0,0 +1,36 @@ +package com.antdsp.utils; + +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.session.Session; +import org.apache.shiro.subject.Subject; + +import com.antdsp.common.exception.CaptchaException; + +public class ShiroUtils { + + public static Subject getSubject() { + return SecurityUtils.getSubject(); + } + + public static Session getSession() { + return SecurityUtils.getSubject().getSession(); + } + + public static void setSessionAttribute(Object key, Object value) { + getSession().setAttribute(key, value); + } + + public static Object getSeesionAttribute(Object key) { + return getSession().getAttribute(key); + } + + public static String getCaptcha() { + String key = Constants.CAPTCHA_SESSION_KEY; + Object captcha = getSeesionAttribute(key); + if(captcha == null) { + throw new CaptchaException("验证码已失效"); + } + getSession().removeAttribute(key); + return captcha.toString(); + } +} diff --git a/antdsp-admin/src/main/java/com/antdsp/web/dto/LoginUserInfo.java b/antdsp-admin/src/main/java/com/antdsp/web/dto/LoginUserInfo.java index 838cda9f384b747a7309be434aee6ea08a0096c3..4ab8b3c4d95c5b5e95633fffb0f4b0454b709434 100644 --- a/antdsp-admin/src/main/java/com/antdsp/web/dto/LoginUserInfo.java +++ b/antdsp-admin/src/main/java/com/antdsp/web/dto/LoginUserInfo.java @@ -4,6 +4,7 @@ public class LoginUserInfo { private String loginname; private String password; + private String code; public String getLoginname() { return loginname; @@ -17,5 +18,11 @@ public class LoginUserInfo { public void setPassword(String password) { this.password = password; } + public String getCode() { + return code; + } + public void setCode(String code) { + this.code = code; + } } diff --git a/antdsp-admin/src/main/java/com/antdsp/web/rest/FileUploadApi.java b/antdsp-admin/src/main/java/com/antdsp/web/rest/FileUploadApi.java new file mode 100644 index 0000000000000000000000000000000000000000..672e69b35381c5782e3add71632fdbe70456764f --- /dev/null +++ b/antdsp-admin/src/main/java/com/antdsp/web/rest/FileUploadApi.java @@ -0,0 +1,103 @@ +package com.antdsp.web.rest; + +import java.io.IOException; +import java.util.UUID; + +import javax.annotation.PostConstruct; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.util.StringUtils; +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.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import com.antdsp.common.AntdspResponse; +import com.qiniu.common.Zone; +import com.qiniu.http.Response; +import com.qiniu.storage.Configuration; +import com.qiniu.storage.UploadManager; +import com.qiniu.util.Auth; + +/** + * 文件上传 + *

title:FileUploadApi

+ *

Description:

+ *

Copyright: Copyright (c) 2019

+ * + * @author lijiantao + * @date 2019年6月24日 + * @email a496401006@qq.com + * + */ +@RestController +@RequestMapping("/fileupload") +public class FileUploadApi { + + private final String GROUP = "public"; + + @Value("${qiniu.accessKey}") + private String accessKey; + + @Value("${qiniu.secretKey}") + private String secretKey; + + @Value("${qiniu.bucket}") + private String bucket; + + private Auth auth ; + + private String upToken; + + private UploadManager uploadManager; + + @PostConstruct + private void init() { + + uploadManager = new UploadManager(new Configuration(Zone.zone0())); + auth = Auth.create(accessKey, secretKey); + upToken = auth.uploadToken(bucket); + } + + /** + * 上传文件 + * @param group + * @param filename + * @return + */ + @PostMapping("/{group}") + public AntdspResponse upload(@PathVariable("group") String group, @RequestParam("file") MultipartFile file) { + + if(StringUtils.isEmpty(group)) { + group = GROUP; + } + if(file == null || file.getSize() <= 0) { + return AntdspResponse.error("未找到上传文件,请重试!"); + } + + try { + + String key = fileName(group , file.getOriginalFilename()); + Response response = uploadManager.put(file.getBytes(), key, upToken); + if(response.isOK()) { + return AntdspResponse.success(key); + }else { + return AntdspResponse.error("上传文件失败,请重试或联系管理员!"); + } + + }catch(IOException e) { + return AntdspResponse.error("上传文件异常,请联系管理员!"); + } + + } + + private String fileName(String group , String originalFilename) { + + String fileSuffix = originalFilename.substring(originalFilename.lastIndexOf("."), originalFilename.length()); + String newFileName = UUID.randomUUID().toString().replaceAll("-", "") + fileSuffix; + return group +"/"+ newFileName; + } + +} diff --git a/antdsp-admin/src/main/java/com/antdsp/web/rest/Login.java b/antdsp-admin/src/main/java/com/antdsp/web/rest/Login.java index 5aeaafbb16c78ff74d952083abc74e27a18cae5b..79fcf22cff3e946bc3b8a07528d2227f145157ca 100644 --- a/antdsp-admin/src/main/java/com/antdsp/web/rest/Login.java +++ b/antdsp-admin/src/main/java/com/antdsp/web/rest/Login.java @@ -1,24 +1,42 @@ package com.antdsp.web.rest; +import java.io.IOException; +import java.io.OutputStream; + +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; + import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.session.Session; import org.apache.shiro.subject.Subject; -import org.springframework.http.HttpStatus; +import org.springframework.http.HttpHeaders; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.server.ResponseStatusException; import com.antdsp.common.AntdspResponse; import com.antdsp.common.ResponseCode; +import com.antdsp.utils.CaptchaUtils; +import com.antdsp.utils.Constants; +import com.antdsp.utils.ShiroUtils; import com.antdsp.web.dto.LoginUserInfo; @RestController public class Login { + @GetMapping("/captcha.jpg") + public void captcha(HttpServletResponse response)throws IOException{ + response.setHeader(HttpHeaders.CACHE_CONTROL, "no-store, no-cache"); + response.setContentType("image/jpeg"); + + String text = CaptchaUtils.createText(); + ShiroUtils.setSessionAttribute(Constants.CAPTCHA_SESSION_KEY , text); + ImageIO.write(CaptchaUtils.createImage(text), "jpg", response.getOutputStream()); + } + @GetMapping("/login") public AntdspResponse login() { return AntdspResponse.error(ResponseCode.UNAUTHORIZED , "请先登录"); @@ -26,12 +44,17 @@ public class Login { @PostMapping("/login") public AntdspResponse login(@RequestBody LoginUserInfo userInfo) { + + String code = userInfo.getCode(); + if(!ShiroUtils.getCaptcha().equalsIgnoreCase(code)) { + return AntdspResponse.error(ResponseCode.ERROR , "验证码错误"); + } + Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(userInfo.getLoginname() , userInfo.getPassword()); try { subject.login(token); Session session = subject.getSession(); -// session.setTimeout(180000); return AntdspResponse.success(session.getId().toString()); }catch(AuthenticationException e) { return AntdspResponse.error(e.getMessage()); @@ -41,8 +64,7 @@ public class Login { @GetMapping("/login_success") public AntdspResponse loginsuccess() { - Subject subject = SecurityUtils.getSubject(); - Session session = subject.getSession(); + Session session = ShiroUtils.getSession(); return AntdspResponse.success(session.getId().toString()); } diff --git a/antdsp-admin/src/main/java/com/antdsp/web/rest/sys/SystemLogApi.java b/antdsp-admin/src/main/java/com/antdsp/web/rest/sys/SystemLogApi.java new file mode 100644 index 0000000000000000000000000000000000000000..cf446c853697e405a0edf7d9a5e82496fe6d0c37 --- /dev/null +++ b/antdsp-admin/src/main/java/com/antdsp/web/rest/sys/SystemLogApi.java @@ -0,0 +1,39 @@ +package com.antdsp.web.rest.sys; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.antdsp.common.pagination.PaginationData; +import com.antdsp.dao.jpa.system.SystemLogJpa; +import com.antdsp.data.entity.system.SystemLog; + +@RestController("/operation/log") +public class SystemLogApi { + + @Autowired + private SystemLogJpa systemLogJpa; + + @GetMapping + public PaginationData list(int page, int count){ + + Specification specification = new Specification() { + + @Override + public Predicate toPredicate(Root root, CriteriaQuery query, + CriteriaBuilder criteriaBuilder) { + return null; + } + + }; + + return this.systemLogJpa.list(specification, page, count); + } + +} diff --git a/antdsp-admin/src/main/java/com/antdsp/web/rest/sys/SystemMenuApi.java b/antdsp-admin/src/main/java/com/antdsp/web/rest/sys/SystemMenuApi.java index 444ca2cc62c30b1091b7fc2fbcc1f3b6a4f7ae16..d01931d1d53ab365eb05988cd158b71c3740d94f 100644 --- a/antdsp-admin/src/main/java/com/antdsp/web/rest/sys/SystemMenuApi.java +++ b/antdsp-admin/src/main/java/com/antdsp/web/rest/sys/SystemMenuApi.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.stream.Collectors; import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; @@ -17,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.antdsp.common.AntdspResponse; +import com.antdsp.common.annotation.OperationLog; import com.antdsp.dao.jpa.system.MenuJpa; import com.antdsp.data.entity.User; import com.antdsp.data.entity.system.SystemMenu; @@ -31,6 +33,8 @@ public class SystemMenuApi { private MenuJpa menuJpa; @GetMapping("") + @OperationLog(name="查询菜单列表") + @RequiresPermissions(value= {"menu:list"}) public MenuTree list() { MenuTree rootMenu = new MenuTree(); @@ -45,6 +49,8 @@ public class SystemMenuApi { @PostMapping("") @Transactional + @OperationLog(name="添加菜单") + @RequiresPermissions(value= {"menu:add"}) public AntdspResponse add(@RequestBody MenuTree menu) { SystemMenu data = new SystemMenu(); @@ -59,6 +65,8 @@ public class SystemMenuApi { @PutMapping("") @Transactional + @OperationLog(name="修改菜单") + @RequiresPermissions(value= {"menu:update"}) public AntdspResponse update(@RequestBody MenuTree menu) { SystemMenu oldMenu = this.menuJpa.findById(menu.getId()).orElse(null); @@ -74,6 +82,8 @@ public class SystemMenuApi { } @DeleteMapping("/{id:\\d+}") + @OperationLog(name="删除菜单") + @RequiresPermissions(value= {"menu:delete"}) public AntdspResponse delete(@PathVariable("id") Long id) { SystemMenu oldMenu = this.menuJpa.findById(id).orElse(null); diff --git a/antdsp-admin/src/main/java/com/antdsp/web/rest/sys/SystemRoleApi.java b/antdsp-admin/src/main/java/com/antdsp/web/rest/sys/SystemRoleApi.java index 56a80c8e1fda964d2283a1c4e3e8e21e5b224105..989a0e945d84e83745a038aa960659129a705e72 100644 --- a/antdsp-admin/src/main/java/com/antdsp/web/rest/sys/SystemRoleApi.java +++ b/antdsp-admin/src/main/java/com/antdsp/web/rest/sys/SystemRoleApi.java @@ -8,6 +8,7 @@ import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; +import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -27,6 +28,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.antdsp.common.AntdspResponse; +import com.antdsp.common.annotation.OperationLog; import com.antdsp.common.pagination.PaginationData; import com.antdsp.dao.jpa.system.RoleJpa; import com.antdsp.data.entity.system.SystemRole; @@ -51,6 +53,8 @@ public class SystemRoleApi { private RoleJpa roleJpa; @GetMapping("") + @OperationLog(name="查询角色列表") + @RequiresPermissions(value= {"role:list"}) public PaginationData list(int page , int count , String roleName){ int start = (page - 1) * count; @@ -73,6 +77,8 @@ public class SystemRoleApi { } @GetMapping("/{id:\\d+}") + @OperationLog(name="查询角色信息") + @RequiresPermissions(value= {"role:list"}) public RoleDto detail(@PathVariable("id") Long id) { RoleDto dto = new RoleDto(); @@ -92,6 +98,8 @@ public class SystemRoleApi { @PostMapping("") @Transactional + @OperationLog(name="添加角色") + @RequiresPermissions(value= {"role:add"}) public AntdspResponse add(@RequestBody RoleDto role) { SystemRole data = new SystemRole(); @@ -113,6 +121,8 @@ public class SystemRoleApi { @PutMapping("") @Transactional + @OperationLog(name="修改角色") + @RequiresPermissions(value= {"role:update"}) public AntdspResponse update(@RequestBody RoleDto role) { Long roleId = role.getId(); @@ -133,6 +143,8 @@ public class SystemRoleApi { @DeleteMapping("/{id:\\d+}") @Transactional() + @OperationLog(name="删除角色") + @RequiresPermissions(value= {"role:delete"}) public AntdspResponse delete(@PathVariable("id") Long id) { SystemRole role = this.roleJpa.findById(id).orElse(null); diff --git a/antdsp-admin/src/main/java/com/antdsp/web/rest/sys/SystemUserApi.java b/antdsp-admin/src/main/java/com/antdsp/web/rest/sys/SystemUserApi.java index 94ecfa7fbd0905052679104058c60ed94022dc4b..721c0cf697d4602d5d417e7323f4cdb08a672031 100644 --- a/antdsp-admin/src/main/java/com/antdsp/web/rest/sys/SystemUserApi.java +++ b/antdsp-admin/src/main/java/com/antdsp/web/rest/sys/SystemUserApi.java @@ -31,6 +31,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.antdsp.common.AntdspResponse; +import com.antdsp.common.annotation.OperationLog; import com.antdsp.common.pagination.PaginationData; import com.antdsp.dao.jpa.UserJpa; import com.antdsp.dao.jpa.system.RoleJpa; @@ -50,11 +51,10 @@ public class SystemUserApi { private RoleJpa roleJpa; @GetMapping("") + @OperationLog(name="查询用户列表") + @RequiresPermissions(value= {"user:list"}) public PaginationData list(int page , int count , String loginname , UserStatus status){ - int start = (page - 1) * count; - Pageable pageable = PageRequest.of(start, count , Sort.by(Order.desc("creator"))); //Sort.and(Order.desc("creator")) - Specification specification = new Specification() { @Override public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder builder) { @@ -70,12 +70,13 @@ public class SystemUserApi { } }; - Page userList = userJpa.findAll(specification , pageable); - PaginationData pagination = new PaginationData<>(userList.getContent() , page , userList.getTotalElements()); + PaginationData pagination = this.userJpa.list(specification, page, count); return pagination; } @GetMapping("/{id:\\d+}") + @OperationLog(name="查询用户信息") + @RequiresPermissions(value= {"user:list"}) public SystemUserDto detail(@PathVariable("id") Long userId) { List roles = this.roleJpa.findRoleNameAndIds(); @@ -87,6 +88,8 @@ public class SystemUserApi { } User user = this.userJpa.findById(userId).orElse(null); + this.userJpa.detach(user); + if(user == null) { return result; } @@ -102,6 +105,8 @@ public class SystemUserApi { @PostMapping("") @Transactional + @OperationLog(name="添加用户") + @RequiresPermissions(value= {"user:add"}) public AntdspResponse add(@RequestBody SystemUserDto dto) { User user = dto.getUser(); @@ -113,7 +118,6 @@ public class SystemUserApi { return AntdspResponse.error("登录名重复"); } - user.setAvatar(""); user.setCreator(current.getLoginname()); user.setModifier(current.getLoginname()); user.setStatus(UserStatus.NORMAL); @@ -127,6 +131,8 @@ public class SystemUserApi { @PutMapping("/{id:\\d+}") @Transactional + @OperationLog(name="修改用户信息") + @RequiresPermissions(value= {"user:update"}) public AntdspResponse update(@RequestBody SystemUserDto dto) { User user = dto.getUser(); @@ -139,6 +145,7 @@ public class SystemUserApi { oldUser.setQq(user.getQq()); oldUser.setRealname(user.getRealname()); oldUser.setModifier(current.getLoginname()); + oldUser.setAvatar(user.getAvatar()); oldUser.onPreUpdate(); this.userJpa.save(oldUser); this.userJpa.deleteUserRoleId(user.getId()); @@ -151,6 +158,7 @@ public class SystemUserApi { @DeleteMapping("/{id:\\d+}") @RequiresPermissions(value= {"user:delete"}) @Transactional + @OperationLog(name="删除用户") public AntdspResponse delete(@PathVariable("id") Long id) { User oldUser = userJpa.findById(id).orElse(null); @@ -175,7 +183,7 @@ public class SystemUserApi { if(roleIds != null && roleIds.size() > 0) { StringBuffer sqlStr = new StringBuffer("INSERT INTO `tb_role_user` (`role_id`, `user_id`) VALUES "); sqlStr.append("("+roleIds.get(0)+ ", " + userId+")"); - if(roleIds.size()>2) { + if(roleIds.size()>=2) { for(int i= 1 ; i< roleIds.size(); i++) { sqlStr.append(",("+roleIds.get(i)+ ", " + userId+")"); } diff --git a/antdsp-admin/src/main/resources/application-dev.yml b/antdsp-admin/src/main/resources/application-dev.yml index 673bd66818f68ef53562ceddd08967e5412364ad..cb6fc7fa9a945fcc01a59d054eb83f07740cf308 100644 --- a/antdsp-admin/src/main/resources/application-dev.yml +++ b/antdsp-admin/src/main/resources/application-dev.yml @@ -10,4 +10,10 @@ spring: hibernate: ddl-auto: update show-sql: true - database-platform: org.hibernate.dialect.MySQL5InnoDBDialect \ No newline at end of file + database-platform: org.hibernate.dialect.MySQL5InnoDBDialect + +qiniu: + accessKey: xxx + secretKey: xxx + bucket: antdsp + \ No newline at end of file diff --git a/antdsp-admin/src/main/resources/application.yml b/antdsp-admin/src/main/resources/application.yml index 572f4e809981baea35134c0ba0f10c865315fa52..fcfadf9824cf0c7087e118015340b005e752cd41 100644 --- a/antdsp-admin/src/main/resources/application.yml +++ b/antdsp-admin/src/main/resources/application.yml @@ -13,3 +13,5 @@ spring: active: dev application: name: antdsp + aop: + auto: true #启动Spring AOP 配置 diff --git a/antdsp-admin/src/main/web/config/router.config.js b/antdsp-admin/src/main/web/config/router.config.js index 2246bc7477d2f841d9749a15c24f0f3909d5c800..f8bfc0cddeb3968fe13348e33054c1a5a9d1522a 100644 --- a/antdsp-admin/src/main/web/config/router.config.js +++ b/antdsp-admin/src/main/web/config/router.config.js @@ -16,32 +16,43 @@ export default [ path: '/', redirect: '/system/user', }, - // dashboard { path: '/system', name: 'system', - icon: 'setting', routes: [ { path: '/system/user', name: 'user', - icon: 'user', component: './System/User/Index', }, { path: '/system/menu', name: 'menu', - icon: 'menu', component: './System/Menu/Index', }, { path: '/system/role', name: 'role', - icon: 'team', component: './System/Role/Index', }, ], }, + { + path: '/operation', + name: 'operation', + routes:[ + { + path: '/operation/log', + name: 'log', + component: './Operation/Log/Index' + }, + { + path: '/operation/article', + name: 'article', + component: './Operation/Article/Index' + } + ] + }, { component: '404', }, diff --git a/antdsp-admin/src/main/web/src/custom/UnipicUpload.js b/antdsp-admin/src/main/web/src/custom/UnipicUpload.js new file mode 100644 index 0000000000000000000000000000000000000000..c77f891e1d0dea67f47d7a484597900beddc5dc3 --- /dev/null +++ b/antdsp-admin/src/main/web/src/custom/UnipicUpload.js @@ -0,0 +1,78 @@ +import { PureComponent } from "react"; +import { Upload, Icon, message } from "antd"; + +export default class UnipicUpload extends PureComponent{ + + state = { + imgUrl: "", + loading: false, + } + + componentDidMount(){ + const { image } = this.props; + this.setState({ + imgUrl: image + }) + } + + uploadUrl(group){ + return "/antdsp-api/fileupload/"+group; + } + + handlerBeforeUpload=(file)=>{ + const isJPG = file.type === 'image/jpeg'; + if (!isJPG) { + message.error('只能上传图片文件'); + } + const isLt2M = file.size / 1024 / 1024 < 2; + if (!isLt2M) { + message.error('图片大小必须小于2M!'); + } + return isJPG && isLt2M; + } + + handlerUploadOnChange=(info)=>{ + const { onChange } = this.props; + if (info.file.status === 'uploading') { + this.setState({ loading: true }); + return; + } + + if (info.file.status === 'done') { + const response = info.file.response; + if(response.success){ + this.setState({ + imgUrl: response.message + }); + onChange(response.message); + } + } + } + + render(){ + + const{ group } = this.props; + + const {imgUrl} = this.state; + + return( + + { + imgUrl ? + + : +
+ +
Upload
+
+ } +
+ ) + } +} \ No newline at end of file diff --git a/antdsp-admin/src/main/web/src/defaultSettings.js b/antdsp-admin/src/main/web/src/defaultSettings.js index 4e7e8018c5ed602b27f28cacff272e8f788383ca..c952cb8712888951b6439d218e60a91c2f4ba258 100644 --- a/antdsp-admin/src/main/web/src/defaultSettings.js +++ b/antdsp-admin/src/main/web/src/defaultSettings.js @@ -9,7 +9,7 @@ module.exports = { menu: { disableLocal: false, }, - title: 'Ant Design Pro', + title: 'Antdsp', pwa: true, // Your custom iconfont Symbol script Url // eg://at.alicdn.com/t/font_1039637_btcrd5co4w.js diff --git a/antdsp-admin/src/main/web/src/locales/zh-CN/menu.js b/antdsp-admin/src/main/web/src/locales/zh-CN/menu.js index ef574614d5a1b8d38da5551fbfe917c4f41a23b0..688550e974042fb1590dbce140717c3a4259824d 100644 --- a/antdsp-admin/src/main/web/src/locales/zh-CN/menu.js +++ b/antdsp-admin/src/main/web/src/locales/zh-CN/menu.js @@ -6,4 +6,7 @@ export default { 'menu.system.role': '角色管理', 'menu.account.center': '个人中心', 'menu.account.logout': '退出登录', + 'menu.operation': '运维管理', + 'menu.operation.log': '操作日志', + 'menu.operation.article': '文章管理' }; diff --git a/antdsp-admin/src/main/web/src/manifest.json b/antdsp-admin/src/main/web/src/manifest.json index 839bc5b5e4a561676fca44a61674d3990b5acd48..1a4e48888d97c560f82486f5454d0ef96f2dc034 100644 --- a/antdsp-admin/src/main/web/src/manifest.json +++ b/antdsp-admin/src/main/web/src/manifest.json @@ -1,6 +1,6 @@ { - "name": "Ant Design Pro", - "short_name": "Ant Design Pro", + "name": "Antdsp", + "short_name": "Antdsp", "display": "standalone", "start_url": "./?utm_source=homescreen", "theme_color": "#002140", diff --git a/antdsp-admin/src/main/web/src/models/login.js b/antdsp-admin/src/main/web/src/models/login.js index 122a452af5d733be08cda2f91add30cd6061f72c..88f772aee5f379a7b828581a9266956914bb9c1a 100644 --- a/antdsp-admin/src/main/web/src/models/login.js +++ b/antdsp-admin/src/main/web/src/models/login.js @@ -61,7 +61,7 @@ export default { }); reloadAuthorized(); // redirect - if (window.location.pathname !== '/login') { + if (window.location.pathname !== '/antdsp/login') { yield put( routerRedux.replace({ pathname: '/login', diff --git a/antdsp-admin/src/main/web/src/pages/Login/Index.js b/antdsp-admin/src/main/web/src/pages/Login/Index.js index af69fe2b2656947b26c7e1777268e0e8dbc65cd8..0b81d1760f21b7f7414e9c40075570892ebab046 100644 --- a/antdsp-admin/src/main/web/src/pages/Login/Index.js +++ b/antdsp-admin/src/main/web/src/pages/Login/Index.js @@ -4,6 +4,7 @@ import { Card, Form, Input, Icon, Button, message } from 'antd'; import Block from '@/custom/Block'; import styles from './style.less'; import md5 from 'js-md5' +import moment from 'moment'; const FormItem = Form.Item; @@ -14,7 +15,8 @@ const FormItem = Form.Item; export default class Login extends PureComponent{ state={ - btnLoading: false + btnLoading: false, + captcha: moment().milliseconds() } handlerLogin=()=>{ @@ -25,23 +27,31 @@ export default class Login extends PureComponent{ form.validateFields((err , fieldsValue)=>{ if(err) return this.setState({btnLoading: false}); - const loginname = fieldsValue.loginname; let password = fieldsValue.password; + dispatch({ type: 'login/login', payload:{ - loginname: loginname, + ...fieldsValue, password: md5(password) }, - callback:(result)=>{ - message.error(result.message); - this.setState({btnLoading: false}); + callback: ()=>{ + this.setState({ + btnLoading: false, + captcha: moment().milliseconds() + }); } }) }); } + changeCaptcha=()=>{ + this.setState({ + captcha: moment().milliseconds() + }); + } + render(){ const { @@ -77,6 +87,20 @@ export default class Login extends PureComponent{ } type="password" placeholder="密 码" /> )} + + {getFieldDecorator('code',{ + rules: [{ + required: true, + message: '请输入验证码', + }] + })( + + )} + + + + 点击刷新 + diff --git a/antdsp-admin/src/main/web/src/pages/Login/style.less b/antdsp-admin/src/main/web/src/pages/Login/style.less index fa972d7664b279f2f546c9b7732a0bd87ce03b02..cfc8b68c50dd9735e8d0230e4a230a6dbe058e0a 100644 --- a/antdsp-admin/src/main/web/src/pages/Login/style.less +++ b/antdsp-admin/src/main/web/src/pages/Login/style.less @@ -18,4 +18,8 @@ font-weight: bold; margin-bottom: 20px; } + .fresh { + margin-left: 40px; + color: white; + } } \ No newline at end of file diff --git a/antdsp-admin/src/main/web/src/pages/Operation/Article/Index.js b/antdsp-admin/src/main/web/src/pages/Operation/Article/Index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/antdsp-admin/src/main/web/src/pages/Operation/Article/models/article.js b/antdsp-admin/src/main/web/src/pages/Operation/Article/models/article.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/antdsp-admin/src/main/web/src/pages/Operation/Log/Index.js b/antdsp-admin/src/main/web/src/pages/Operation/Log/Index.js new file mode 100644 index 0000000000000000000000000000000000000000..d3b46762e9f52644f1ce0c345a7327ac45766e3d --- /dev/null +++ b/antdsp-admin/src/main/web/src/pages/Operation/Log/Index.js @@ -0,0 +1,109 @@ +import { PureComponent } from "react"; +import { connect } from 'dva'; +import PageHeaderWrapper from "@/components/PageHeaderWrapper"; +import Block from "@/custom/Block"; +import { Form, Table, Card } from "antd"; +import moment from "moment"; + +const FormItem = Form.Item; + +@connect(({ operlog , loading })=>({ + operlog, + loading: loading.models.operlog +})) +@Form.create() +export default class SystemLog extends PureComponent{ + + state={ + formValues:{ + page: 1, + count: 10, + } + } + + componentDidMount(){ + const{ dispatch} = this.props; + + dispatch({ + type: 'operlog/fetchAll', + payload: { + ...this.state.formValues + } + }); + } + + handlerTableOnChange = (pagination, filters, sorter) => { + const { dispatch } = this.props; + let { formValues } = this.state; + + formValues = { + ...formValues, + page: pagination.current, + count: pagination.pageSize, + }; + + this.setState({ + formValues: { + ...formValues, + }, + }); + + dispatch({ + type: 'operlog/fetchAll', + payload: { + ...formValues + } + }); + } + + render(){ + + const { operlog:{ LogList} , loading } = this.props; + + const columns = [{ + title: '用户名', + dataIndex: 'optorName' + },{ + title: '用户操作', + dataIndex: 'optName', + },{ + title: '请求URI', + dataIndex: 'optURI', + },{ + title: '请求方式', + dataIndex: 'optType', + },{ + title: '请求参数', + dataIndex: 'optDetail', + width: '100px', + },{ + title: '耗时', + dataIndex: 'runTime', + },{ + title: '操作IP', + dataIndex: 'optorIp', + },{ + title: '创建时间', + dataIndex: 'created', + render: (val)=> {moment(val).format('YYYY-MM-DD HH:mm:ss')} + }]; + + return ( + + + + + + + + + + ) + } +} \ No newline at end of file diff --git a/antdsp-admin/src/main/web/src/pages/Operation/Log/models/operlog.js b/antdsp-admin/src/main/web/src/pages/Operation/Log/models/operlog.js new file mode 100644 index 0000000000000000000000000000000000000000..e746c1f00fb8f8a3cbda13789102c3ce4a6f6c29 --- /dev/null +++ b/antdsp-admin/src/main/web/src/pages/Operation/Log/models/operlog.js @@ -0,0 +1,32 @@ +import { fetchAllLog } from "@/services/system"; + +export default { + namespace: 'operlog', + + state: { + LogList:{ + data:[], + pagination:{} + } + }, + + effects: { + *fetchAll({payload},{call, put}){ + const response = yield call(fetchAllLog, payload); + + yield put({ + type: 'reducersLogList', + payload: response, + }) + } + }, + + reducers: { + reducersLogList(state,{payload}){ + return { + ...state, + LogList: payload + } + } + } +} \ No newline at end of file diff --git a/antdsp-admin/src/main/web/src/pages/System/User/Index.js b/antdsp-admin/src/main/web/src/pages/System/User/Index.js index 5bfafcfd0e1205b81611292be58a165bc38ab861..b54e5f5a9c4438c0911e745cdcd752e2432068d5 100644 --- a/antdsp-admin/src/main/web/src/pages/System/User/Index.js +++ b/antdsp-admin/src/main/web/src/pages/System/User/Index.js @@ -14,8 +14,11 @@ import { message, Popconfirm, Checkbox, + Upload, + Icon } from 'antd'; import md5 from 'js-md5' +import UnipicUpload from '@/custom/UnipicUpload'; const FormItem = Form.Item; @@ -122,8 +125,8 @@ export default class extends PureComponent { }); }; - showModal = currentObj => { - + showModal = (e , currentObj) => { + e.preventDefault(); const { dispatch } = this.props; dispatch({ @@ -221,6 +224,11 @@ export default class extends PureComponent { { title: '头像', dataIndex: 'avatar', + render:(val)=>{ + if(val){ + return
+ } + } }, { title: '登录名', @@ -250,8 +258,8 @@ export default class extends PureComponent { { - this.showModal(record); + onClick={(e) => { + this.showModal(e, record); }} > 编辑 @@ -282,8 +290,8 @@ export default class extends PureComponent {