diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8cfd370 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +# http://editorconfig.org +root = true + +# 空格替代Tab缩进在各种编辑工具下效果一致 +[*] +indent_style = space +indent_size = 4 +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true + +[*.java] +indent_style = tab + +[*.{json,yml}] +indent_size = 2 + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2c0e049 --- /dev/null +++ b/.gitignore @@ -0,0 +1,44 @@ +###################################################################### +# Build Tools + +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +target/ +!.mvn/wrapper/maven-wrapper.jar + +###################################################################### +# IDE + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +nbproject/private/ +build/* +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +###################################################################### +# Others +*.log +*.xml.versionsBackup +*.swp + +!*/build/*.java +!*/build/*.html +!*/build/*.xml diff --git a/bashi-admin/pom.xml b/bashi-admin/pom.xml new file mode 100644 index 0000000..31a5dca --- /dev/null +++ b/bashi-admin/pom.xml @@ -0,0 +1,105 @@ + + + + bashi + com.bashi + 2.4.0 + + 4.0.0 + jar + bashi-admin + + + web服务入口 + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + junit + junit + test + + + + + org.springframework.boot + spring-boot-devtools + true + + + + + mysql + mysql-connector-java + + + + com.bashi + bashi-dk + 2.4.0 + + + + + + + + com.bashi + bashi-generator + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + true + + + + + repackage + + + + + + org.apache.maven.plugins + maven-war-plugin + 3.1.0 + + false + ${project.artifactId} + + + + org.apache.maven.plugins + 2.6 + maven-resources-plugin + + UTF-8 + + xlsx + p12 + + + + + + + diff --git a/bashi-admin/src/main/java/com/bashi/BaShiApplication.java b/bashi-admin/src/main/java/com/bashi/BaShiApplication.java new file mode 100644 index 0000000..e9dd046 --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/BaShiApplication.java @@ -0,0 +1,24 @@ +package com.bashi; + +import org.dromara.x.file.storage.spring.EnableFileStorage; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; + +/** + * 启动程序 + * + * @author duteliang + */ + +@SpringBootApplication +@EnableScheduling +@EnableFileStorage +public class BaShiApplication { + public static void main(String[] args) { + System.setProperty("spring.devtools.restart.enabled", "false"); + System.setProperty("druid.mysql.usePingMethod", "false"); + SpringApplication.run(BaShiApplication.class, args); + System.out.println("(♥◠‿◠)ノ゙ 巴适启动成功 ლ(´ڡ`ლ)゙"); + } +} diff --git a/bashi-admin/src/main/java/com/bashi/BaShiServletInitializer.java b/bashi-admin/src/main/java/com/bashi/BaShiServletInitializer.java new file mode 100644 index 0000000..5e3f3b4 --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/BaShiServletInitializer.java @@ -0,0 +1,18 @@ +package com.bashi; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * web容器中进行部署 + * + * @author duteliang + */ +public class BaShiServletInitializer extends SpringBootServletInitializer +{ + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) + { + return application.sources(BaShiApplication.class); + } +} diff --git a/bashi-admin/src/main/java/com/bashi/com/UserDetailsServiceImpl.java b/bashi-admin/src/main/java/com/bashi/com/UserDetailsServiceImpl.java new file mode 100644 index 0000000..18a171e --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/com/UserDetailsServiceImpl.java @@ -0,0 +1,105 @@ +package com.bashi.com; + +import cn.hutool.core.lang.Validator; +import com.bashi.common.core.domain.entity.Customer; +import com.bashi.common.core.domain.entity.SysUser; +import com.bashi.common.core.domain.model.LoginPhoneBody; +import com.bashi.common.core.domain.model.LoginUser; +import com.bashi.common.enums.UserStatus; +import com.bashi.common.exception.BaseException; +import com.bashi.common.exception.CustomException; +import com.bashi.dk.service.CustomerService; +import com.bashi.framework.security.sms.IDuteUserDetailsService; +import com.bashi.framework.security.sms.LoginTypeEnums; +import com.bashi.framework.web.service.RegisterEventService; +import com.bashi.framework.web.service.SysPermissionService; +import com.bashi.system.service.ISysUserService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; + +/** + * 用户验证处理 + * + * @author duteliang + */ +@Service +public class UserDetailsServiceImpl implements IDuteUserDetailsService { + private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); + + @Autowired + private ISysUserService userService; + @Autowired + private SysPermissionService permissionService; + @Autowired(required = false) + private Map list; + @Autowired + private CustomerService customerService; + @Autowired + private BCryptPasswordEncoder passwordEncoder; + @PostConstruct + public void init(){ + System.out.println("daaaa"); + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + SysUser user = userService.selectUserByUserName(username); + if (Validator.isNull(user)) { + log.info("登录用户:{} 不存在.", username); + throw new UsernameNotFoundException("登录用户:" + username + " 不存在"); + } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { + log.info("登录用户:{} 已被删除.", username); + throw new BaseException("对不起,您的账号:" + username + " 已被删除"); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", username); + throw new BaseException("对不起,您的账号:" + username + " 已停用"); + } + return createLoginUser(user); + } + + public UserDetails createLoginUser(SysUser user) { + return new LoginUser(user, permissionService.getMenuPermission(user)); + } + + public UserDetails createCustomerUser(Customer customer) { + LoginUser loginUser = new LoginUser(null, new HashSet<>()); + loginUser.setCustomer(customer); + loginUser.setType(1); + return loginUser; + } + + @Override + public UserDetails loadUserByMobile(LoginPhoneBody body) throws UsernameNotFoundException { + if(body == null){ + throw new CustomException("账号不存在,登陆失败"); + } + String mobile = body.getMobile(); + Customer customer = null; + if(Objects.equals(body.getLoginRole(), LoginTypeEnums.CUSTOMER_PASSWORD.getCode())){ + customer = customerService.getCustomerByName(mobile); + if(customer == null || !passwordEncoder.matches(body.getPassword(),customer.getPassword())){ + log.info("登录用户:{} 用户名不存在或者密码错误", mobile); + throw new BaseException("对不起,用户名不存在或者密码错误"); + } + } + if (customer == null){ + log.info("登录用户:{} 不存在.", mobile); + throw new UsernameNotFoundException("登录用户:" + mobile + " 不存在"); + } else if (customer.getStatus() == 1) { + log.info("登录用户:{} 已被停用.", mobile); + throw new BaseException("对不起,您的账号:" + mobile + " 已停用"); + } + return createCustomerUser(customer); + } + +} diff --git a/bashi-admin/src/main/java/com/bashi/web/controller/common/CaptchaController.java b/bashi-admin/src/main/java/com/bashi/web/controller/common/CaptchaController.java new file mode 100644 index 0000000..5920b9e --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/web/controller/common/CaptchaController.java @@ -0,0 +1,121 @@ +package com.bashi.web.controller.common; + +import cn.hutool.captcha.AbstractCaptcha; +import cn.hutool.captcha.CircleCaptcha; +import cn.hutool.captcha.LineCaptcha; +import cn.hutool.captcha.ShearCaptcha; +import cn.hutool.captcha.generator.CodeGenerator; +import cn.hutool.captcha.generator.RandomGenerator; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.bashi.common.constant.Constants; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.redis.RedisCache; +import com.bashi.framework.captcha.UnsignedMathGenerator; +import com.bashi.framework.config.properties.CaptchaProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * 验证码操作处理 + * + * @author Lion Li + */ +@RestController +public class CaptchaController { + + // 圆圈干扰验证码 + @Resource(name = "CircleCaptcha") + private CircleCaptcha circleCaptcha; + // 线段干扰的验证码 + @Resource(name = "LineCaptcha") + private LineCaptcha lineCaptcha; + // 扭曲干扰验证码 + @Resource(name = "ShearCaptcha") + private ShearCaptcha shearCaptcha; + + @Autowired + private RedisCache redisCache; + + @Autowired + private CaptchaProperties captchaProperties; + + /** + * 生成验证码 + */ + @GetMapping("/captchaImage") + public AjaxResult getCode() { + Map ajax = new HashMap<>(); + Boolean enabled = captchaProperties.getEnabled(); + ajax.put("enabled", enabled); + if (!enabled) { + return AjaxResult.success(ajax); + } + // 保存验证码信息 + String uuid = IdUtil.simpleUUID(); + String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; + String code = null; + // 生成验证码 + CodeGenerator codeGenerator; + AbstractCaptcha captcha; + switch (captchaProperties.getType()) { + case "math": + codeGenerator = new UnsignedMathGenerator(captchaProperties.getNumberLength()); + break; + case "char": + codeGenerator = new RandomGenerator(captchaProperties.getCharLength()); + break; + default: + throw new IllegalArgumentException("验证码类型异常"); + } + switch (captchaProperties.getCategory()) { + case "line": + captcha = lineCaptcha; + break; + case "circle": + captcha = circleCaptcha; + break; + case "shear": + captcha = shearCaptcha; + break; + default: + throw new IllegalArgumentException("验证码类别异常"); + } + captcha.setGenerator(codeGenerator); + captcha.createCode(); + if ("math".equals(captchaProperties.getType())) { + code = getCodeResult(captcha.getCode()); + } else if ("char".equals(captchaProperties.getType())) { + code = captcha.getCode(); + } + redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); + ajax.put("uuid", uuid); + ajax.put("img", captcha.getImageBase64()); + return AjaxResult.success(ajax); + } + + private String getCodeResult(String capStr) { + int numberLength = captchaProperties.getNumberLength(); + int a = Convert.toInt(StrUtil.sub(capStr, 0, numberLength).trim()); + char operator = capStr.charAt(numberLength); + int b = Convert.toInt(StrUtil.sub(capStr, numberLength + 1, numberLength + 1 + numberLength).trim()); + switch (operator) { + case '*': + return a * b + ""; + case '+': + return a + b + ""; + case '-': + return a - b + ""; + default: + return ""; + } + } + +} diff --git a/bashi-admin/src/main/java/com/bashi/web/controller/common/CommonController.java b/bashi-admin/src/main/java/com/bashi/web/controller/common/CommonController.java new file mode 100644 index 0000000..2ccaaf8 --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/web/controller/common/CommonController.java @@ -0,0 +1,107 @@ +package com.bashi.web.controller.common; + +import cn.hutool.core.util.StrUtil; +import com.bashi.common.config.BsConfig; +import com.bashi.common.constant.Constants; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.utils.file.FileUploadUtils; +import com.bashi.common.utils.file.FileUtils; +import com.bashi.framework.config.ServerConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +/** + * 通用请求处理 + * + * @author duteliang + */ +@RestController +public class CommonController { + private static final Logger log = LoggerFactory.getLogger(CommonController.class); + + @Autowired + private ServerConfig serverConfig; + + /** + * 通用下载请求 + * + * @param fileName 文件名称 + * @param delete 是否删除 + */ + @GetMapping("common/download") + public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) { + try { + if (!FileUtils.checkAllowDownload(fileName)) { + throw new Exception(StrUtil.format("文件名称({})非法,不允许下载。 ", fileName)); + } + String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1); + String filePath = BsConfig.getDownloadPath() + fileName; + File file = new File(filePath); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, realFileName); + FileUtils.writeToStream(file, response.getOutputStream()); + if (delete) { + FileUtils.del(file); + } + } catch (Exception e) { + log.error("下载文件失败", e); + } + } + + /** + * 通用上传请求 + */ + @PostMapping("/common/upload") + public AjaxResult uploadFile(MultipartFile file) throws Exception { + try { + // 上传文件路径 + String filePath = BsConfig.getUploadPath(); + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + String url = serverConfig.getUrl() + fileName; + Map ajax = new HashMap<>(); + ajax.put("fileName", fileName); + ajax.put("url", url); + return AjaxResult.success(ajax); + } catch (Exception e) { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 本地资源通用下载 + */ + @GetMapping("/common/download/resource") + public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response) + throws Exception { + try { + if (!FileUtils.checkAllowDownload(resource)) { + throw new Exception(StrUtil.format("资源文件({})非法,不允许下载。 ", resource)); + } + // 本地资源路径 + String localPath = BsConfig.getProfile(); + // 数据库资源地址 + String downloadPath = localPath + StrUtil.subAfter(resource, Constants.RESOURCE_PREFIX, false); + // 下载名称 + String downloadName = StrUtil.subAfter(downloadPath, "/", true); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + File file = new File(downloadPath); + FileUtils.setAttachmentResponseHeader(response, downloadName); + FileUtils.writeToStream(file, response.getOutputStream()); + } catch (Exception e) { + log.error("下载文件失败", e); + } + } +} diff --git a/bashi-admin/src/main/java/com/bashi/web/controller/monitor/CacheController.java b/bashi-admin/src/main/java/com/bashi/web/controller/monitor/CacheController.java new file mode 100644 index 0000000..f21a502 --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/web/controller/monitor/CacheController.java @@ -0,0 +1,50 @@ +package com.bashi.web.controller.monitor; + +import cn.hutool.core.util.StrUtil; +import com.bashi.common.core.domain.AjaxResult; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisCallback; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.*; + +/** + * 缓存监控 + * + * @author duteliang + */ +@RestController +@RequestMapping("/monitor/cache") +public class CacheController +{ + @Autowired + private RedisTemplate redisTemplate; + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @GetMapping() + public AjaxResult getInfo() throws Exception + { + Properties info = (Properties) redisTemplate.execute((RedisCallback) connection -> connection.info()); + Properties commandStats = (Properties) redisTemplate.execute((RedisCallback) connection -> connection.info("commandstats")); + Object dbSize = redisTemplate.execute((RedisCallback) connection -> connection.dbSize()); + + Map result = new HashMap<>(3); + result.put("info", info); + result.put("dbSize", dbSize); + + List> pieList = new ArrayList<>(); + commandStats.stringPropertyNames().forEach(key -> { + Map data = new HashMap<>(2); + String property = commandStats.getProperty(key); + data.put("name", StrUtil.removePrefix(key, "cmdstat_")); + data.put("value", StrUtil.subBetween(property, "calls=", ",usec")); + pieList.add(data); + }); + result.put("commandStats", pieList); + return AjaxResult.success(result); + } +} diff --git a/bashi-admin/src/main/java/com/bashi/web/controller/monitor/SysLogininforController.java b/bashi-admin/src/main/java/com/bashi/web/controller/monitor/SysLogininforController.java new file mode 100644 index 0000000..bc51e18 --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/web/controller/monitor/SysLogininforController.java @@ -0,0 +1,62 @@ +package com.bashi.web.controller.monitor; + +import com.bashi.common.annotation.Log; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.enums.BusinessType; +import com.bashi.common.utils.poi.ExcelUtil; +import com.bashi.system.domain.SysLogininfor; +import com.bashi.system.service.ISysLogininforService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 系统访问记录 + * + * @author duteliang + */ +@RestController +@RequestMapping("/monitor/logininfor") +public class SysLogininforController extends BaseController +{ + @Autowired + private ISysLogininforService logininforService; + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:list')") + @GetMapping("/list") + public TableDataInfo list(SysLogininfor logininfor) + { + return logininforService.selectPageLogininforList(logininfor); + } + + @Log(title = "登录日志", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('monitor:logininfor:export')") + @GetMapping("/export") + public AjaxResult export(SysLogininfor logininfor) + { + List list = logininforService.selectLogininforList(logininfor); + ExcelUtil util = new ExcelUtil(SysLogininfor.class); + return util.exportExcel(list, "登录日志"); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')") + @Log(title = "登录日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{infoIds}") + public AjaxResult remove(@PathVariable Long[] infoIds) + { + return toAjax(logininforService.deleteLogininforByIds(infoIds)); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')") + @Log(title = "登录日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public AjaxResult clean() + { + logininforService.cleanLogininfor(); + return AjaxResult.success(); + } +} diff --git a/bashi-admin/src/main/java/com/bashi/web/controller/monitor/SysOperlogController.java b/bashi-admin/src/main/java/com/bashi/web/controller/monitor/SysOperlogController.java new file mode 100644 index 0000000..262dad5 --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/web/controller/monitor/SysOperlogController.java @@ -0,0 +1,62 @@ +package com.bashi.web.controller.monitor; + +import com.bashi.common.annotation.Log; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.enums.BusinessType; +import com.bashi.common.utils.poi.ExcelUtil; +import com.bashi.system.domain.SysOperLog; +import com.bashi.system.service.ISysOperLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 操作日志记录 + * + * @author duteliang + */ +@RestController +@RequestMapping("/monitor/operlog") +public class SysOperlogController extends BaseController +{ + @Autowired + private ISysOperLogService operLogService; + + @PreAuthorize("@ss.hasPermi('monitor:operlog:list')") + @GetMapping("/list") + public TableDataInfo list(SysOperLog operLog) + { + return operLogService.selectPageOperLogList(operLog); + } + + @Log(title = "操作日志", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('monitor:operlog:export')") + @GetMapping("/export") + public AjaxResult export(SysOperLog operLog) + { + List list = operLogService.selectOperLogList(operLog); + ExcelUtil util = new ExcelUtil(SysOperLog.class); + return util.exportExcel(list, "操作日志"); + } + + @Log(title = "操作日志", businessType = BusinessType.DELETE) + @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')") + @DeleteMapping("/{operIds}") + public AjaxResult remove(@PathVariable Long[] operIds) + { + return toAjax(operLogService.deleteOperLogByIds(operIds)); + } + + @Log(title = "操作日志", businessType = BusinessType.CLEAN) + @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')") + @DeleteMapping("/clean") + public AjaxResult clean() + { + operLogService.cleanOperLog(); + return AjaxResult.success(); + } +} diff --git a/bashi-admin/src/main/java/com/bashi/web/controller/monitor/SysUserOnlineController.java b/bashi-admin/src/main/java/com/bashi/web/controller/monitor/SysUserOnlineController.java new file mode 100644 index 0000000..02ff0aa --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/web/controller/monitor/SysUserOnlineController.java @@ -0,0 +1,91 @@ +package com.bashi.web.controller.monitor; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import com.bashi.common.annotation.Log; +import com.bashi.common.constant.Constants; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.domain.model.LoginUser; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.core.redis.RedisCache; +import com.bashi.common.enums.BusinessType; +import com.bashi.common.utils.PageUtils; +import com.bashi.system.domain.SysUserOnline; +import com.bashi.system.service.ISysUserOnlineService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * 在线用户监控 + * + * @author duteliang + */ +@RestController +@RequestMapping("/monitor/online") +public class SysUserOnlineController extends BaseController +{ + @Autowired + private ISysUserOnlineService userOnlineService; + + @Autowired + private RedisCache redisCache; + + @PreAuthorize("@ss.hasPermi('monitor:online:list')") + @GetMapping("/list") + public TableDataInfo list(String ipaddr, String userName) + { + Collection keys = redisCache.keys(Constants.LOGIN_TOKEN_KEY + "*"); + List userOnlineList = new ArrayList(); + for (String key : keys) + { + LoginUser user = redisCache.getCacheObject(key); + if (Validator.isNotEmpty(ipaddr) && Validator.isNotEmpty(userName)) + { + if (StrUtil.equals(ipaddr, user.getIpaddr()) && StrUtil.equals(userName, user.getUsername())) + { + userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user)); + } + } + else if (Validator.isNotEmpty(ipaddr)) + { + if (StrUtil.equals(ipaddr, user.getIpaddr())) + { + userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user)); + } + } + else if (Validator.isNotEmpty(userName) && Validator.isNotNull(user.getUser())) + { + if (StrUtil.equals(userName, user.getUsername())) + { + userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user)); + } + } + else + { + userOnlineList.add(userOnlineService.loginUserToUserOnline(user)); + } + } + Collections.reverse(userOnlineList); + userOnlineList.removeAll(Collections.singleton(null)); + return PageUtils.buildDataInfo(userOnlineList); + } + + /** + * 强退用户 + */ + @PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')") + @Log(title = "在线用户", businessType = BusinessType.FORCE) + @DeleteMapping("/{tokenId}") + public AjaxResult forceLogout(@PathVariable String tokenId) + { + redisCache.deleteObject(Constants.LOGIN_TOKEN_KEY + tokenId); + return AjaxResult.success(); + } +} diff --git a/bashi-admin/src/main/java/com/bashi/web/controller/system/SysConfigController.java b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysConfigController.java new file mode 100644 index 0000000..d5fb62c --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysConfigController.java @@ -0,0 +1,128 @@ +package com.bashi.web.controller.system; + +import com.bashi.common.annotation.Log; +import com.bashi.common.annotation.RepeatSubmit; +import com.bashi.common.constant.UserConstants; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.enums.BusinessType; +import com.bashi.common.utils.SecurityUtils; +import com.bashi.common.utils.poi.ExcelUtil; +import com.bashi.system.domain.SysConfig; +import com.bashi.system.service.ISysConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 参数配置 信息操作处理 + * + * @author duteliang + */ +@RestController +@RequestMapping("/system/config") +public class SysConfigController extends BaseController +{ + @Autowired + private ISysConfigService configService; + + /** + * 获取参数配置列表 + */ + @PreAuthorize("@ss.hasPermi('system:config:list')") + @GetMapping("/list") + public TableDataInfo list(SysConfig config) + { + return configService.selectPageConfigList(config); + } + + @Log(title = "参数管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:config:export')") + @GetMapping("/export") + public AjaxResult export(SysConfig config) + { + List list = configService.selectConfigList(config); + ExcelUtil util = new ExcelUtil(SysConfig.class); + return util.exportExcel(list, "参数数据"); + } + + /** + * 根据参数编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:config:query')") + @GetMapping(value = "/{configId}") + public AjaxResult getInfo(@PathVariable Long configId) + { + return AjaxResult.success(configService.selectConfigById(configId)); + } + + /** + * 根据参数键名查询参数值 + */ + @GetMapping(value = "/configKey/{configKey}") + public AjaxResult getConfigKey(@PathVariable String configKey) + { + return AjaxResult.success(configService.selectConfigByKey(configKey)); + } + + /** + * 新增参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:add')") + @Log(title = "参数管理", businessType = BusinessType.INSERT) + @PostMapping + @RepeatSubmit + public AjaxResult add(@Validated @RequestBody SysConfig config) + { + if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config))) + { + return AjaxResult.error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setCreateBy(SecurityUtils.getUsername()); + return toAjax(configService.insertConfig(config)); + } + + /** + * 修改参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:edit')") + @Log(title = "参数管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysConfig config) + { + if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config))) + { + return AjaxResult.error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(configService.updateConfig(config)); + } + + /** + * 删除参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:remove')") + @Log(title = "参数管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{configIds}") + public AjaxResult remove(@PathVariable Long[] configIds) + { + configService.deleteConfigByIds(configIds); + return success(); + } + + /** + * 刷新参数缓存 + */ + @PreAuthorize("@ss.hasPermi('system:config:remove')") + @Log(title = "参数管理", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public AjaxResult refreshCache() + { + configService.resetConfigCache(); + return AjaxResult.success(); + } +} diff --git a/bashi-admin/src/main/java/com/bashi/web/controller/system/SysDeptController.java b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysDeptController.java new file mode 100644 index 0000000..b999686 --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysDeptController.java @@ -0,0 +1,159 @@ +package com.bashi.web.controller.system; + +import cn.hutool.core.util.StrUtil; +import com.bashi.common.annotation.Log; +import com.bashi.common.constant.UserConstants; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.domain.entity.SysDept; +import com.bashi.common.enums.BusinessType; +import com.bashi.common.utils.SecurityUtils; +import com.bashi.system.service.ISysDeptService; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * 部门信息 + * + * @author duteliang + */ +@RestController +@RequestMapping("/system/dept") +public class SysDeptController extends BaseController +{ + @Autowired + private ISysDeptService deptService; + + /** + * 获取部门列表 + */ + @PreAuthorize("@ss.hasPermi('system:dept:list')") + @GetMapping("/list") + public AjaxResult list(SysDept dept) + { + List depts = deptService.selectDeptList(dept); + return AjaxResult.success(depts); + } + + /** + * 查询部门列表(排除节点) + */ + @PreAuthorize("@ss.hasPermi('system:dept:list')") + @GetMapping("/list/exclude/{deptId}") + public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) + { + List depts = deptService.selectDeptList(new SysDept()); + Iterator it = depts.iterator(); + while (it.hasNext()) + { + SysDept d = (SysDept) it.next(); + if (d.getDeptId().intValue() == deptId + || ArrayUtils.contains(StrUtil.splitToArray(d.getAncestors(), ','), deptId + "")) + { + it.remove(); + } + } + return AjaxResult.success(depts); + } + + /** + * 根据部门编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:dept:query')") + @GetMapping(value = "/{deptId}") + public AjaxResult getInfo(@PathVariable Long deptId) + { + return AjaxResult.success(deptService.selectDeptById(deptId)); + } + + /** + * 获取部门下拉树列表 + */ + @GetMapping("/treeselect") + public AjaxResult treeselect(SysDept dept) + { + List depts = deptService.selectDeptList(dept); + return AjaxResult.success(deptService.buildDeptTreeSelect(depts)); + } + + /** + * 加载对应角色部门列表树 + */ + @GetMapping(value = "/roleDeptTreeselect/{roleId}") + public AjaxResult roleDeptTreeselect(@PathVariable("roleId") Long roleId) + { + List depts = deptService.selectDeptList(new SysDept()); + Map ajax = new HashMap<>(); + ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId)); + ajax.put("depts", deptService.buildDeptTreeSelect(depts)); + return AjaxResult.success(ajax); + } + + /** + * 新增部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:add')") + @Log(title = "部门管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDept dept) + { + if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept))) + { + return AjaxResult.error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + dept.setCreateBy(SecurityUtils.getUsername()); + return toAjax(deptService.insertDept(dept)); + } + + /** + * 修改部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:edit')") + @Log(title = "部门管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDept dept) + { + if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept))) + { + return AjaxResult.error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + else if (dept.getParentId().equals(dept.getDeptId())) + { + return AjaxResult.error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己"); + } + else if (StrUtil.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) + && deptService.selectNormalChildrenDeptById(dept.getDeptId()) > 0) + { + return AjaxResult.error("该部门包含未停用的子部门!"); + } + dept.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(deptService.updateDept(dept)); + } + + /** + * 删除部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:remove')") + @Log(title = "部门管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{deptId}") + public AjaxResult remove(@PathVariable Long deptId) + { + if (deptService.hasChildByDeptId(deptId)) + { + return AjaxResult.error("存在下级部门,不允许删除"); + } + if (deptService.checkDeptExistUser(deptId)) + { + return AjaxResult.error("部门存在用户,不允许删除"); + } + return toAjax(deptService.deleteDeptById(deptId)); + } +} diff --git a/bashi-admin/src/main/java/com/bashi/web/controller/system/SysDictDataController.java b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysDictDataController.java new file mode 100644 index 0000000..6f99c53 --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysDictDataController.java @@ -0,0 +1,113 @@ +package com.bashi.web.controller.system; + +import cn.hutool.core.lang.Validator; +import com.bashi.common.annotation.Log; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.domain.entity.SysDictData; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.enums.BusinessType; +import com.bashi.common.utils.SecurityUtils; +import com.bashi.common.utils.poi.ExcelUtil; +import com.bashi.system.service.ISysDictDataService; +import com.bashi.system.service.ISysDictTypeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; + +/** + * 数据字典信息 + * + * @author duteliang + */ +@RestController +@RequestMapping("/system/dict/data") +public class SysDictDataController extends BaseController +{ + @Autowired + private ISysDictDataService dictDataService; + + @Autowired + private ISysDictTypeService dictTypeService; + + @PreAuthorize("@ss.hasPermi('system:dict:list')") + @GetMapping("/list") + public TableDataInfo list(SysDictData dictData) + { + return dictDataService.selectPageDictDataList(dictData); + } + + @Log(title = "字典数据", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:dict:export')") + @GetMapping("/export") + public AjaxResult export(SysDictData dictData) + { + List list = dictDataService.selectDictDataList(dictData); + ExcelUtil util = new ExcelUtil(SysDictData.class); + return util.exportExcel(list, "字典数据"); + } + + /** + * 查询字典数据详细 + */ + @PreAuthorize("@ss.hasPermi('system:dict:query')") + @GetMapping(value = "/{dictCode}") + public AjaxResult getInfo(@PathVariable Long dictCode) + { + return AjaxResult.success(dictDataService.selectDictDataById(dictCode)); + } + + /** + * 根据字典类型查询字典数据信息 + */ + @GetMapping(value = "/type/{dictType}") + public AjaxResult dictType(@PathVariable String dictType) + { + List data = dictTypeService.selectDictDataByType(dictType); + if (Validator.isNull(data)) + { + data = new ArrayList(); + } + return AjaxResult.success(data); + } + + /** + * 新增字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:add')") + @Log(title = "字典数据", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDictData dict) + { + dict.setCreateBy(SecurityUtils.getUsername()); + return toAjax(dictDataService.insertDictData(dict)); + } + + /** + * 修改保存字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:edit')") + @Log(title = "字典数据", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDictData dict) + { + dict.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(dictDataService.updateDictData(dict)); + } + + /** + * 删除字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictCodes}") + public AjaxResult remove(@PathVariable Long[] dictCodes) + { + dictDataService.deleteDictDataByIds(dictCodes); + return success(); + } +} diff --git a/bashi-admin/src/main/java/com/bashi/web/controller/system/SysDictTypeController.java b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysDictTypeController.java new file mode 100644 index 0000000..6b3fed8 --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysDictTypeController.java @@ -0,0 +1,124 @@ +package com.bashi.web.controller.system; + +import com.bashi.common.annotation.Log; +import com.bashi.common.constant.UserConstants; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.domain.entity.SysDictType; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.enums.BusinessType; +import com.bashi.common.utils.SecurityUtils; +import com.bashi.common.utils.poi.ExcelUtil; +import com.bashi.system.service.ISysDictTypeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 数据字典信息 + * + * @author duteliang + */ +@RestController +@RequestMapping("/system/dict/type") +public class SysDictTypeController extends BaseController +{ + @Autowired + private ISysDictTypeService dictTypeService; + + @PreAuthorize("@ss.hasPermi('system:dict:list')") + @GetMapping("/list") + public TableDataInfo list(SysDictType dictType) + { + return dictTypeService.selectPageDictTypeList(dictType); + } + + @Log(title = "字典类型", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:dict:export')") + @GetMapping("/export") + public AjaxResult export(SysDictType dictType) + { + List list = dictTypeService.selectDictTypeList(dictType); + ExcelUtil util = new ExcelUtil(SysDictType.class); + return util.exportExcel(list, "字典类型"); + } + + /** + * 查询字典类型详细 + */ + @PreAuthorize("@ss.hasPermi('system:dict:query')") + @GetMapping(value = "/{dictId}") + public AjaxResult getInfo(@PathVariable Long dictId) + { + return AjaxResult.success(dictTypeService.selectDictTypeById(dictId)); + } + + /** + * 新增字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:add')") + @Log(title = "字典类型", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDictType dict) + { + if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) + { + return AjaxResult.error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setCreateBy(SecurityUtils.getUsername()); + return toAjax(dictTypeService.insertDictType(dict)); + } + + /** + * 修改字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:edit')") + @Log(title = "字典类型", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDictType dict) + { + if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) + { + return AjaxResult.error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(dictTypeService.updateDictType(dict)); + } + + /** + * 删除字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictIds}") + public AjaxResult remove(@PathVariable Long[] dictIds) + { + dictTypeService.deleteDictTypeByIds(dictIds); + return success(); + } + + /** + * 刷新字典缓存 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public AjaxResult refreshCache() + { + dictTypeService.resetDictCache(); + return AjaxResult.success(); + } + + /** + * 获取字典选择框列表 + */ + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + List dictTypes = dictTypeService.selectDictTypeAll(); + return AjaxResult.success(dictTypes); + } +} diff --git a/bashi-admin/src/main/java/com/bashi/web/controller/system/SysLoginController.java b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysLoginController.java new file mode 100644 index 0000000..61c8776 --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysLoginController.java @@ -0,0 +1,93 @@ +package com.bashi.web.controller.system; + +import com.bashi.common.constant.Constants; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.domain.entity.SysMenu; +import com.bashi.common.core.domain.entity.SysUser; +import com.bashi.common.core.domain.model.LoginBody; +import com.bashi.common.core.domain.model.LoginUser; +import com.bashi.common.utils.ServletUtils; +import com.bashi.framework.web.service.SysLoginService; +import com.bashi.framework.web.service.SysPermissionService; +import com.bashi.framework.web.service.TokenService; +import com.bashi.system.service.ISysMenuService; +import org.springframework.beans.factory.annotation.Autowired; +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 java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 登录验证 + * + * @author duteliang + */ +@RestController +public class SysLoginController { + @Autowired + private SysLoginService loginService; + + @Autowired + private ISysMenuService menuService; + + @Autowired + private SysPermissionService permissionService; + + @Autowired + private TokenService tokenService; + + /** + * 登录方法 + * + * @param loginBody 登录信息 + * @return 结果 + */ + @PostMapping("/login") + public AjaxResult login(@RequestBody LoginBody loginBody) { + Map ajax = new HashMap<>(); + // 生成令牌 + String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), + loginBody.getUuid()); + ajax.put(Constants.TOKEN, token); + return AjaxResult.success(ajax); + } + + /** + * 获取用户信息 + * + * @return 用户信息 + */ + @GetMapping("getInfo") + public AjaxResult getInfo() { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + SysUser user = loginUser.getUser(); + // 角色集合 + Set roles = permissionService.getRolePermission(user); + // 权限集合 + Set permissions = permissionService.getMenuPermission(user); + Map ajax = new HashMap<>(); + ajax.put("user", user); + ajax.put("roles", roles); + ajax.put("permissions", permissions); + return AjaxResult.success(ajax); + } + + /** + * 获取路由信息 + * + * @return 路由信息 + */ + @GetMapping("getRouters") + public AjaxResult getRouters() { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + // 用户信息 + SysUser user = loginUser.getUser(); + List menus = menuService.selectMenuTreeByUserId(user.getUserId()); + return AjaxResult.success(menuService.buildMenus(menus)); + } +} diff --git a/bashi-admin/src/main/java/com/bashi/web/controller/system/SysMenuController.java b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysMenuController.java new file mode 100644 index 0000000..4b8f88a --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysMenuController.java @@ -0,0 +1,153 @@ +package com.bashi.web.controller.system; + +import cn.hutool.core.util.StrUtil; +import com.bashi.common.annotation.Log; +import com.bashi.common.constant.Constants; +import com.bashi.common.constant.UserConstants; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.domain.entity.SysMenu; +import com.bashi.common.core.domain.model.LoginUser; +import com.bashi.common.enums.BusinessType; +import com.bashi.common.utils.SecurityUtils; +import com.bashi.common.utils.ServletUtils; +import com.bashi.framework.web.service.TokenService; +import com.bashi.system.service.ISysMenuService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 菜单信息 + * + * @author duteliang + */ +@RestController +@RequestMapping("/system/menu") +public class SysMenuController extends BaseController +{ + @Autowired + private ISysMenuService menuService; + + @Autowired + private TokenService tokenService; + + /** + * 获取菜单列表 + */ + @PreAuthorize("@ss.hasPermi('system:menu:list')") + @GetMapping("/list") + public AjaxResult list(SysMenu menu) + { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + Long userId = loginUser.getUser().getUserId(); + List menus = menuService.selectMenuList(menu, userId); + return AjaxResult.success(menus); + } + + /** + * 根据菜单编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:menu:query')") + @GetMapping(value = "/{menuId}") + public AjaxResult getInfo(@PathVariable Long menuId) + { + return AjaxResult.success(menuService.selectMenuById(menuId)); + } + + /** + * 获取菜单下拉树列表 + */ + @GetMapping("/treeselect") + public AjaxResult treeselect(SysMenu menu) + { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + Long userId = loginUser.getUser().getUserId(); + List menus = menuService.selectMenuList(menu, userId); + return AjaxResult.success(menuService.buildMenuTreeSelect(menus)); + } + + /** + * 加载对应角色菜单列表树 + */ + @GetMapping(value = "/roleMenuTreeselect/{roleId}") + public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId) + { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + List menus = menuService.selectMenuList(loginUser.getUser().getUserId()); + Map ajax = new HashMap<>(); + ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId)); + ajax.put("menus", menuService.buildMenuTreeSelect(menus)); + return AjaxResult.success(ajax); + } + + /** + * 新增菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:add')") + @Log(title = "菜单管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysMenu menu) + { + if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) + { + return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) + && !StrUtil.startWithAny(menu.getPath(), Constants.HTTP, Constants.HTTPS)) + { + return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + menu.setCreateBy(SecurityUtils.getUsername()); + return toAjax(menuService.insertMenu(menu)); + } + + /** + * 修改菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:edit')") + @Log(title = "菜单管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysMenu menu) + { + if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) + { + return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) + && !StrUtil.startWithAny(menu.getPath(), Constants.HTTP, Constants.HTTPS)) + { + return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + else if (menu.getMenuId().equals(menu.getParentId())) + { + return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己"); + } + menu.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(menuService.updateMenu(menu)); + } + + /** + * 删除菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:remove')") + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{menuId}") + public AjaxResult remove(@PathVariable("menuId") Long menuId) + { + if (menuService.hasChildByMenuId(menuId)) + { + return AjaxResult.error("存在子菜单,不允许删除"); + } + if (menuService.checkMenuExistRole(menuId)) + { + return AjaxResult.error("菜单已分配,不允许删除"); + } + return toAjax(menuService.deleteMenuById(menuId)); + } +} diff --git a/bashi-admin/src/main/java/com/bashi/web/controller/system/SysNoticeController.java b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysNoticeController.java new file mode 100644 index 0000000..2d328ef --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysNoticeController.java @@ -0,0 +1,89 @@ +package com.bashi.web.controller.system; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.bashi.common.annotation.Log; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.enums.BusinessType; +import com.bashi.common.utils.SecurityUtils; +import com.bashi.system.domain.SysNotice; +import com.bashi.system.service.ISysNoticeService; + +/** + * 公告 信息操作处理 + * + * @author duteliang + */ +@RestController +@RequestMapping("/system/notice") +public class SysNoticeController extends BaseController +{ + @Autowired + private ISysNoticeService noticeService; + + /** + * 获取通知公告列表 + */ + @PreAuthorize("@ss.hasPermi('system:notice:list')") + @GetMapping("/list") + public TableDataInfo list(SysNotice notice) + { + return noticeService.selectPageNoticeList(notice); + } + + /** + * 根据通知公告编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:notice:query')") + @GetMapping(value = "/{noticeId}") + public AjaxResult getInfo(@PathVariable Long noticeId) + { + return AjaxResult.success(noticeService.selectNoticeById(noticeId)); + } + + /** + * 新增通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:add')") + @Log(title = "通知公告", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysNotice notice) + { + notice.setCreateBy(SecurityUtils.getUsername()); + return toAjax(noticeService.insertNotice(notice)); + } + + /** + * 修改通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:edit')") + @Log(title = "通知公告", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysNotice notice) + { + notice.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(noticeService.updateNotice(notice)); + } + + /** + * 删除通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:remove')") + @Log(title = "通知公告", businessType = BusinessType.DELETE) + @DeleteMapping("/{noticeIds}") + public AjaxResult remove(@PathVariable Long[] noticeIds) + { + return toAjax(noticeService.deleteNoticeByIds(noticeIds)); + } +} diff --git a/bashi-admin/src/main/java/com/bashi/web/controller/system/SysPostController.java b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysPostController.java new file mode 100644 index 0000000..d81ac01 --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysPostController.java @@ -0,0 +1,128 @@ +package com.bashi.web.controller.system; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.bashi.common.annotation.Log; +import com.bashi.common.constant.UserConstants; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.enums.BusinessType; +import com.bashi.common.utils.SecurityUtils; +import com.bashi.common.utils.poi.ExcelUtil; +import com.bashi.system.domain.SysPost; +import com.bashi.system.service.ISysPostService; + +/** + * 岗位信息操作处理 + * + * @author duteliang + */ +@RestController +@RequestMapping("/system/post") +public class SysPostController extends BaseController +{ + @Autowired + private ISysPostService postService; + + /** + * 获取岗位列表 + */ + @PreAuthorize("@ss.hasPermi('system:post:list')") + @GetMapping("/list") + public TableDataInfo list(SysPost post) + { + return postService.selectPagePostList(post); + } + + @Log(title = "岗位管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:post:export')") + @GetMapping("/export") + public AjaxResult export(SysPost post) + { + List list = postService.selectPostList(post); + ExcelUtil util = new ExcelUtil(SysPost.class); + return util.exportExcel(list, "岗位数据"); + } + + /** + * 根据岗位编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:post:query')") + @GetMapping(value = "/{postId}") + public AjaxResult getInfo(@PathVariable Long postId) + { + return AjaxResult.success(postService.selectPostById(postId)); + } + + /** + * 新增岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:add')") + @Log(title = "岗位管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysPost post) + { + if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post))) + { + return AjaxResult.error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } + else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post))) + { + return AjaxResult.error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setCreateBy(SecurityUtils.getUsername()); + return toAjax(postService.insertPost(post)); + } + + /** + * 修改岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:edit')") + @Log(title = "岗位管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysPost post) + { + if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post))) + { + return AjaxResult.error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } + else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post))) + { + return AjaxResult.error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(postService.updatePost(post)); + } + + /** + * 删除岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:remove')") + @Log(title = "岗位管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{postIds}") + public AjaxResult remove(@PathVariable Long[] postIds) + { + return toAjax(postService.deletePostByIds(postIds)); + } + + /** + * 获取岗位选择框列表 + */ + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + List posts = postService.selectPostAll(); + return AjaxResult.success(posts); + } +} diff --git a/bashi-admin/src/main/java/com/bashi/web/controller/system/SysProfileController.java b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysProfileController.java new file mode 100644 index 0000000..df95f50 --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysProfileController.java @@ -0,0 +1,137 @@ +package com.bashi.web.controller.system; + +import cn.hutool.core.util.StrUtil; +import com.bashi.common.annotation.Log; +import com.bashi.common.config.BsConfig; +import com.bashi.common.constant.UserConstants; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.domain.entity.SysUser; +import com.bashi.common.core.domain.model.LoginUser; +import com.bashi.common.enums.BusinessType; +import com.bashi.common.utils.SecurityUtils; +import com.bashi.common.utils.ServletUtils; +import com.bashi.common.utils.file.FileUploadUtils; +import com.bashi.framework.web.service.TokenService; +import com.bashi.system.service.ISysUserService; +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.HashMap; +import java.util.Map; + +/** + * 个人信息 业务处理 + * + * @author duteliang + */ +@RestController +@RequestMapping("/system/user/profile") +public class SysProfileController extends BaseController +{ + @Autowired + private ISysUserService userService; + + @Autowired + private TokenService tokenService; + + /** + * 个人信息 + */ + @GetMapping + public AjaxResult profile() + { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + SysUser user = loginUser.getUser(); + Map ajax = new HashMap<>(); + ajax.put("user", user); + ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername())); + ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername())); + return AjaxResult.success(ajax); + } + + /** + * 修改用户 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult updateProfile(@RequestBody SysUser user) + { + if (StrUtil.isNotEmpty(user.getPhonenumber()) + && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) + { + return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + if (StrUtil.isNotEmpty(user.getEmail()) + && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) + { + return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + if (userService.updateUserProfile(user) > 0) + { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + // 更新缓存用户信息 + loginUser.getUser().setNickName(user.getNickName()); + loginUser.getUser().setPhonenumber(user.getPhonenumber()); + loginUser.getUser().setEmail(user.getEmail()); + loginUser.getUser().setSex(user.getSex()); + tokenService.setLoginUser(loginUser); + return AjaxResult.success(); + } + return AjaxResult.error("修改个人信息异常,请联系管理员"); + } + + /** + * 重置密码 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping("/updatePwd") + public AjaxResult updatePwd(String oldPassword, String newPassword) + { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + String userName = loginUser.getUsername(); + String password = loginUser.getPassword(); + if (!SecurityUtils.matchesPassword(oldPassword, password)) + { + return AjaxResult.error("修改密码失败,旧密码错误"); + } + if (SecurityUtils.matchesPassword(newPassword, password)) + { + return AjaxResult.error("新密码不能与旧密码相同"); + } + if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0) + { + // 更新缓存用户密码 + loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword)); + tokenService.setLoginUser(loginUser); + return AjaxResult.success(); + } + return AjaxResult.error("修改密码异常,请联系管理员"); + } + + /** + * 头像上传 + */ + @Log(title = "用户头像", businessType = BusinessType.UPDATE) + @PostMapping("/avatar") + public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws IOException + { + if (!file.isEmpty()) + { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + String avatar = FileUploadUtils.upload(BsConfig.getAvatarPath(), file); + if (userService.updateUserAvatar(loginUser.getUsername(), avatar)) + { + Map ajax = new HashMap<>(); + ajax.put("imgUrl", avatar); + // 更新缓存用户头像 + loginUser.getUser().setAvatar(avatar); + tokenService.setLoginUser(loginUser); + return AjaxResult.success(ajax); + } + } + return AjaxResult.error("上传图片异常,请联系管理员"); + } +} diff --git a/bashi-admin/src/main/java/com/bashi/web/controller/system/SysRoleController.java b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysRoleController.java new file mode 100644 index 0000000..d5cbd3f --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysRoleController.java @@ -0,0 +1,174 @@ +package com.bashi.web.controller.system; + +import cn.hutool.core.lang.Validator; +import com.bashi.common.annotation.Log; +import com.bashi.common.constant.UserConstants; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.domain.entity.SysRole; +import com.bashi.common.core.domain.model.LoginUser; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.enums.BusinessType; +import com.bashi.common.utils.SecurityUtils; +import com.bashi.common.utils.ServletUtils; +import com.bashi.common.utils.poi.ExcelUtil; +import com.bashi.framework.web.service.SysPermissionService; +import com.bashi.framework.web.service.TokenService; +import com.bashi.system.service.ISysRoleService; +import com.bashi.system.service.ISysUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 角色信息 + * + * @author duteliang + */ +@RestController +@RequestMapping("/system/role") +public class SysRoleController extends BaseController +{ + @Autowired + private ISysRoleService roleService; + + @Autowired + private TokenService tokenService; + + @Autowired + private SysPermissionService permissionService; + + @Autowired + private ISysUserService userService; + + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/list") + public TableDataInfo list(SysRole role) + { + return roleService.selectPageRoleList(role); + } + + @Log(title = "角色管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:role:export')") + @GetMapping("/export") + public AjaxResult export(SysRole role) + { + List list = roleService.selectRoleList(role); + ExcelUtil util = new ExcelUtil(SysRole.class); + return util.exportExcel(list, "角色数据"); + } + + /** + * 根据角色编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping(value = "/{roleId}") + public AjaxResult getInfo(@PathVariable Long roleId) + { + return AjaxResult.success(roleService.selectRoleById(roleId)); + } + + /** + * 新增角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:add')") + @Log(title = "角色管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysRole role) + { + if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role))) + { + return AjaxResult.error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } + else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role))) + { + return AjaxResult.error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setCreateBy(SecurityUtils.getUsername()); + return toAjax(roleService.insertRole(role)); + + } + + /** + * 修改保存角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role))) + { + return AjaxResult.error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } + else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role))) + { + return AjaxResult.error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setUpdateBy(SecurityUtils.getUsername()); + + if (roleService.updateRole(role) > 0) + { + // 更新缓存用户权限 + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + if (Validator.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin()) + { + loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser())); + loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName())); + tokenService.setLoginUser(loginUser); + } + return AjaxResult.success(); + } + return AjaxResult.error("修改角色'" + role.getRoleName() + "'失败,请联系管理员"); + } + + /** + * 修改保存数据权限 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/dataScope") + public AjaxResult dataScope(@RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + return toAjax(roleService.authDataScope(role)); + } + + /** + * 状态修改 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + role.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(roleService.updateRoleStatus(role)); + } + + /** + * 删除角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:remove')") + @Log(title = "角色管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{roleIds}") + public AjaxResult remove(@PathVariable Long[] roleIds) + { + return toAjax(roleService.deleteRoleByIds(roleIds)); + } + + /** + * 获取角色选择框列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + return AjaxResult.success(roleService.selectRoleAll()); + } +} diff --git a/bashi-admin/src/main/java/com/bashi/web/controller/system/SysUserController.java b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysUserController.java new file mode 100644 index 0000000..09cdb84 --- /dev/null +++ b/bashi-admin/src/main/java/com/bashi/web/controller/system/SysUserController.java @@ -0,0 +1,180 @@ +package com.bashi.web.controller.system; + +import cn.hutool.core.lang.Validator; +import com.bashi.common.annotation.Log; +import com.bashi.common.constant.UserConstants; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.domain.entity.SysRole; +import com.bashi.common.core.domain.entity.SysUser; +import com.bashi.common.core.domain.model.LoginUser; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.enums.BusinessType; +import com.bashi.common.utils.SecurityUtils; +import com.bashi.common.utils.ServletUtils; +import com.bashi.common.utils.poi.ExcelUtil; +import com.bashi.framework.web.service.TokenService; +import com.bashi.system.service.ISysPostService; +import com.bashi.system.service.ISysRoleService; +import com.bashi.system.service.ISysUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 用户信息 + * + * @author duteliang + */ +@RestController +@RequestMapping("/system/user") +public class SysUserController extends BaseController { + @Autowired + private ISysUserService userService; + + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysPostService postService; + + @Autowired + private TokenService tokenService; + + /** + * 获取用户列表 + */ + @PreAuthorize("@ss.hasPermi('system:user:list')") + @GetMapping("/list") + public TableDataInfo list(SysUser user) { + return userService.selectPageUserList(user); + } + + @Log(title = "用户管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:user:export')") + @GetMapping("/export") + public AjaxResult export(SysUser user) { + List list = userService.selectUserList(user); + ExcelUtil util = new ExcelUtil(SysUser.class); + return util.exportExcel(list, "用户数据"); + } + + @Log(title = "用户管理", businessType = BusinessType.IMPORT) + @PreAuthorize("@ss.hasPermi('system:user:import')") + @PostMapping("/importData") + public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception { + ExcelUtil util = new ExcelUtil(SysUser.class); + List userList = util.importExcel(file.getInputStream()); + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + String operName = loginUser.getUsername(); + String message = userService.importUser(userList, updateSupport, operName); + return AjaxResult.success(message); + } + + @GetMapping("/importTemplate") + public AjaxResult importTemplate() { + ExcelUtil util = new ExcelUtil(SysUser.class); + return util.importTemplateExcel("用户数据"); + } + + /** + * 根据用户编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:user:query')") + @GetMapping(value = {"/", "/{userId}"}) + public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId) { + Map ajax = new HashMap<>(); + List roles = roleService.selectRoleAll(); + ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); + ajax.put("posts", postService.selectPostAll()); + if (Validator.isNotNull(userId)) { + ajax.put("user", userService.selectUserById(userId)); + ajax.put("postIds", postService.selectPostListByUserId(userId)); + ajax.put("roleIds", roleService.selectRoleListByUserId(userId)); + } + return AjaxResult.success(ajax); + } + + /** + * 新增用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:add')") + @Log(title = "用户管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysUser user) { + if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user.getUserName()))) { + return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); + } else if (Validator.isNotEmpty(user.getPhonenumber()) + && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) { + return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,手机号码已存在"); + } else if (Validator.isNotEmpty(user.getEmail()) + && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) { + return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setCreateBy(SecurityUtils.getUsername()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + return toAjax(userService.insertUser(user)); + } + + + /** + * 修改用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysUser user) { + userService.checkUserAllowed(user); + if (Validator.isNotEmpty(user.getPhonenumber()) + && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) { + return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } else if (Validator.isNotEmpty(user.getEmail()) + && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) { + return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(userService.updateUser(user)); + } + + /** + * 删除用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:remove')") + @Log(title = "用户管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{userIds}") + public AjaxResult remove(@PathVariable Long[] userIds) { + return toAjax(userService.deleteUserByIds(userIds)); + } + + /** + * 重置密码 + */ + @PreAuthorize("@ss.hasPermi('system:user:resetPwd')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/resetPwd") + public AjaxResult resetPwd(@RequestBody SysUser user) { + userService.checkUserAllowed(user); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + user.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(userService.resetPwd(user)); + } + + /** + * 状态修改 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysUser user) { + userService.checkUserAllowed(user); + user.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(userService.updateUserStatus(user)); + } +} diff --git a/bashi-admin/src/main/resources/META-INF/spring-devtools.properties b/bashi-admin/src/main/resources/META-INF/spring-devtools.properties new file mode 100644 index 0000000..2b23f85 --- /dev/null +++ b/bashi-admin/src/main/resources/META-INF/spring-devtools.properties @@ -0,0 +1 @@ +restart.include.json=/com.alibaba.fastjson.*.jar \ No newline at end of file diff --git a/bashi-admin/src/main/resources/application-dev.yml b/bashi-admin/src/main/resources/application-dev.yml new file mode 100644 index 0000000..fbca8c7 --- /dev/null +++ b/bashi-admin/src/main/resources/application-dev.yml @@ -0,0 +1,112 @@ +# 数据源配置 +spring: + autoconfigure: + exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure + datasource: + type: com.alibaba.druid.pool.DruidDataSource + # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content + dynamic: + #设置默认的数据源或者数据源组,默认值即为 master + primary: master + datasource: + # 主库数据源 + master: + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:4306/dk?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true + username: dk + password: dk123.com + # 从库数据源 + slave: + driverClassName: com.mysql.cj.jdbc.Driver + url: + username: + password: + druid: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + # 注意这个值和druid原生不一致,默认启动了stat + filters: stat + webStatFilter: + enabled: false + statViewServlet: + enabled: false + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: duteliang + login-password: 123456 + filter: + stat: + enabled: false + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true + # redis 配置 + redis: + # 地址 + host: localhost + # 端口,默认为6379 + port: 6379 + # 数据库索引 + database: 1 + # 密码 + password: + # 连接超时时间 + timeout: 10s + # 是否开启ssl + ssl: false + +--- # redisson 客户端配置 +redisson: + # 线程池数量 + threads: 16 + # Netty线程池数量 + nettyThreads: 32 + # 传输模式 + transportMode: "NIO" + # 单节点配置 + singleServerConfig: + # 客户端名称 + clientName: ${bashi.name} + # 最小空闲连接数 + connectionMinimumIdleSize: 32 + # 连接池大小 + connectionPoolSize: 64 + # 连接空闲超时,单位:毫秒 + idleConnectionTimeout: 10000 + # 命令等待超时,单位:毫秒 + timeout: 3000 + # 如果尝试在此限制之内发送成功,则开始启用 timeout 计时。 + retryAttempts: 3 + # 命令重试发送时间间隔,单位:毫秒 + retryInterval: 1500 + # 发布和订阅连接的最小空闲连接数 + subscriptionConnectionMinimumIdleSize: 1 + # 发布和订阅连接池大小 + subscriptionConnectionPoolSize: 50 + # 单个连接最大订阅数量 + subscriptionsPerConnection: 5 + # DNS监测时间间隔,单位:毫秒 + dnsMonitoringInterval: 5000 diff --git a/bashi-admin/src/main/resources/application-prod.yml b/bashi-admin/src/main/resources/application-prod.yml new file mode 100644 index 0000000..ccb7da5 --- /dev/null +++ b/bashi-admin/src/main/resources/application-prod.yml @@ -0,0 +1,112 @@ +# 数据源配置 +spring: + autoconfigure: + exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure + datasource: + type: com.alibaba.druid.pool.DruidDataSource + # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content + dynamic: + #设置默认的数据源或者数据源组,默认值即为 master + primary: master + datasource: + # 主库数据源 + master: + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:4306/dk?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true + username: dk + password: dk + # 从库数据源 + slave: + driverClassName: com.mysql.cj.jdbc.Driver + url: + username: + password: + druid: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + # 注意这个值和druid原生不一致,默认启动了stat + filters: stat + webStatFilter: + enabled: false + statViewServlet: + enabled: false + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: duteliang + login-password: 123456 + filter: + stat: + enabled: false + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true + # redis 配置 + redis: + # 地址 + host: localhost + # 端口,默认为 6379 + port: 9379 + # 数据库索引 + database: 3 + # 密码 + password: 383200134 + # 连接超时时间 + timeout: 10s + # 是否开启ssl + ssl: false + +--- # redisson 客户端配置 +redisson: + # 线程池数量 + threads: 16 + # Netty线程池数量 + nettyThreads: 32 + # 传输模式 + transportMode: "NIO" + # 单节点配置 + singleServerConfig: + # 客户端名称 + clientName: ${duteliang.name} + # 最小空闲连接数 + connectionMinimumIdleSize: 32 + # 连接池大小 + connectionPoolSize: 64 + # 连接空闲超时,单位:毫秒 + idleConnectionTimeout: 10000 + # 命令等待超时,单位:毫秒 + timeout: 3000 + # 如果尝试在此限制之内发送成功,则开始启用 timeout 计时。 + retryAttempts: 3 + # 命令重试发送时间间隔,单位:毫秒 + retryInterval: 1500 + # 发布和订阅连接的最小空闲连接数 + subscriptionConnectionMinimumIdleSize: 1 + # 发布和订阅连接池大小 + subscriptionConnectionPoolSize: 50 + # 单个连接最大订阅数量 + subscriptionsPerConnection: 5 + # DNS监测时间间隔,单位:毫秒 + dnsMonitoringInterval: 5000 diff --git a/bashi-admin/src/main/resources/application.yml b/bashi-admin/src/main/resources/application.yml new file mode 100644 index 0000000..28955f5 --- /dev/null +++ b/bashi-admin/src/main/resources/application.yml @@ -0,0 +1,340 @@ +# 项目相关配置 +bashi: + # 名称 + name: dk + # 版本 + version: 2.4.0 + # 版权年份 + copyrightYear: 2021 + # 实例演示开关 + demoEnabled: true + # 文件路径 + profile: ./dk/uploadPath + # 获取ip地址开关 + addressEnabled: true + +captcha: + # 验证码开关 + enabled: true + # 验证码类型 math 数组计算 char 字符验证 + type: math + # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰 + category: circle + # 数字验证码位数 + numberLength: 1 + # 字符验证码长度 + charLength: 4 + +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8080 + port: 8082 + servlet: + # 应用的访问路径 + context-path: / + # undertow 配置 + undertow: + # HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的 + max-http-post-size: -1 + # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理 + # 每块buffer的空间大小,越小的空间被利用越充分 + buffer-size: 512 + # 是否分配的直接内存 + direct-buffers: true + threads: + # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程 + io: 8 + # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载 + worker: 256 +# # tomcat 配置 +# tomcat: +# # tomcat的URI编码 +# uri-encoding: UTF-8 +# # tomcat最大线程数,默认为200 +# max-threads: 500 +# # Tomcat启动初始化的线程数,默认值25 +# min-spare-threads: 30 + +# 日志配置 +logging: + level: + com.bashi: info + org.springframework: warn + config: classpath:logback.xml + +# Spring配置 +spring: + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + profiles: + active: dev + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 40MB + # 设置总上传的文件大小 + max-request-size: 40MB +# location: /tmp/undertow + # 服务模块 + devtools: + restart: + # 热部署开关 + enabled: true + # 与vue整合部署使用 + thymeleaf: + # 将系统模板放置到最前面 否则会与 springboot-admin 页面冲突 + template-resolver-order: 1 + jackson: + # 日期格式化 + date-format: yyyy-MM-dd HH:mm:ss + serialization: + # 格式化输出 + indent_output: false + # 忽略无法转换的对象 + fail_on_empty_beans: false + deserialization: + # 允许对象忽略json中不存在的属性 + fail_on_unknown_properties: false + +# token配置 +token: + # 令牌自定义标识 + header: Authorization + # 令牌密钥 + secret: abcdefghijklmnopqrstuvwxyz + # 令牌有效期(默认30分钟) 1一个月 30*24*60 + expireTime: 43200 + +# MyBatisPlus配置 +# https://baomidou.com/config/ +mybatis-plus: + mapperPackage: com.bashi.**.mapper + # 对应的 XML 文件位置 + mapperLocations: classpath*:mapper/**/*Mapper.xml + # 实体扫描,多个package用逗号或者分号分隔 + typeAliasesPackage: com.bashi.**.domain + # 针对 typeAliasesPackage,如果配置了该属性,则仅仅会扫描路径下以该类作为父类的域对象 + #typeAliasesSuperType: Class + # 如果配置了该属性,SqlSessionFactoryBean 会把该包下面的类注册为对应的 TypeHandler + #typeHandlersPackage: null + # 如果配置了该属性,会将路径下的枚举类进行注入,让实体类字段能够简单快捷的使用枚举属性 + #typeEnumsPackage: null + # 启动时是否检查 MyBatis XML 文件的存在,默认不检查 + checkConfigLocation: false + # 通过该属性可指定 MyBatis 的执行器,MyBatis 的执行器总共有三种: + # SIMPLE:该执行器类型不做特殊的事情,为每个语句的执行创建一个新的预处理语句(PreparedStatement) + # REUSE:该执行器类型会复用预处理语句(PreparedStatement) + # BATCH:该执行器类型会批量执行所有的更新语句 + executorType: SIMPLE + # 指定外部化 MyBatis Properties 配置,通过该配置可以抽离配置,实现不同环境的配置部署 + configurationProperties: null + configuration: + # 自动驼峰命名规则(camel case)映射 + # 如果您的数据库命名符合规则无需使用 @TableField 注解指定数据库字段名 + mapUnderscoreToCamelCase: true + # 默认枚举处理类,如果配置了该属性,枚举将统一使用指定处理器进行处理 + # org.apache.ibatis.type.EnumTypeHandler : 存储枚举的名称 + # org.apache.ibatis.type.EnumOrdinalTypeHandler : 存储枚举的索引 + # com.baomidou.mybatisplus.extension.handlers.MybatisEnumTypeHandler : 枚举类需要实现IEnum接口或字段标记@EnumValue注解. + defaultEnumTypeHandler: org.apache.ibatis.type.EnumTypeHandler + # 当设置为 true 的时候,懒加载的对象可能被任何懒属性全部加载,否则,每个属性都按需加载。需要和 lazyLoadingEnabled 一起使用。 + aggressiveLazyLoading: true + # MyBatis 自动映射策略 + # NONE:不启用自动映射 + # PARTIAL:只对非嵌套的 resultMap 进行自动映射 + # FULL:对所有的 resultMap 都进行自动映射 + autoMappingBehavior: PARTIAL + # MyBatis 自动映射时未知列或未知属性处理策 + # NONE:不做任何处理 (默认值) + # WARNING:以日志的形式打印相关警告信息 + # FAILING:当作映射失败处理,并抛出异常和详细信息 + autoMappingUnknownColumnBehavior: NONE + # Mybatis一级缓存,默认为 SESSION + # SESSION session级别缓存,同一个session相同查询语句不会再次查询数据库 + # STATEMENT 关闭一级缓存 + localCacheScope: SESSION + # 开启Mybatis二级缓存,默认为 true + cacheEnabled: true + global-config: + # 是否打印 Logo banner + banner: true + # 是否初始化 SqlRunner + enableSqlRunner: false + dbConfig: + # 主键类型 + # AUTO 数据库ID自增 + # NONE 空 + # INPUT 用户输入ID + # ASSIGN_ID 全局唯一ID + # ASSIGN_UUID 全局唯一ID UUID + idType: AUTO + # 表名前缀 + tablePrefix: null + # 字段 format,例: %s,(对主键无效) + columnFormat: null + # 表名是否使用驼峰转下划线命名,只对表名生效 + tableUnderline: true + # 大写命名,对表名和字段名均生效 + capitalMode: false + # 全局的entity的逻辑删除字段属性名 + logicDeleteField: null + # 逻辑已删除值 + logicDeleteValue: 2 + # 逻辑未删除值 + logicNotDeleteValue: 0 + # 字段验证策略之 insert,在 insert 的时候的字段验证策略 + # IGNORED 忽略判断 + # NOT_NULL 非NULL判断 + # NOT_EMPTY 非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断) + # DEFAULT 默认的,一般只用于注解里 + # NEVER 不加入 SQL + insertStrategy: NOT_EMPTY + # 字段验证策略之 update,在 update 的时候的字段验证策略 + updateStrategy: NOT_EMPTY + # 字段验证策略之 select,在 select 的时候的字段验证策略既 wrapper 根据内部 entity 生成的 where 条件 + selectStrategy: NOT_EMPTY + +# Swagger配置 +swagger: + # 是否开启swagger + enabled: true + # 请求前缀 + pathMapping: /dev-api + # 标题 + title: '标题:591后台管理系统_接口文档' + # 描述 + description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...' + # 版本 + version: '版本号: 2.4.0' + # 作者信息 + contact: + name: Lion Li + email: crazylionli@163.com + url: https://gitee.com/JavaLionLi/new-591 + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludes: /system/notice/* + # 匹配链接 + urlPatterns: /system/*,/monitor/*,/tool/* + +# 全局线程池相关配置 +thread-pool: + # 是否开启线程池 + enabled: false + # 核心线程池大小 + corePoolSize: 8 + # 最大可创建的线程数 + maxPoolSize: 16 + # 队列最大长度 + queueCapacity: 128 + # 线程池维护线程所允许的空闲时间 + keepAliveSeconds: 300 + # 线程池对拒绝任务(无线程可用)的处理策略 + # CallerRunsPolicy 等待 + # DiscardOldestPolicy 放弃最旧的 + # DiscardPolicy 丢弃 + # AbortPolicy 中止 + rejectedExecutionHandler: CallerRunsPolicy + +# feign 相关配置 +feign: + package: com.bashi.**.feign + # 开启压缩 + compression: + request: + enabled: true + response: + enabled: true + okhttp: + enabled: true + circuitbreaker: + enabled: true + +--- # 分布式锁 lock4j 全局配置 +lock4j: + # 获取分布式锁超时时间,默认为 3000 毫秒 + acquire-timeout: 3000 + # 分布式锁的超时时间,默认为 30 毫秒 + expire: 30000 + +--- # 定时任务配置 +spring: + quartz: + scheduler-name: BsScheduler + startup-delay: 1s + overwrite-existing-jobs: true + auto-startup: true + job-store-type: jdbc + properties: + org: + quartz: + # Scheduler 相关配置 + scheduler: + instanceName: BsScheduler + instanceId: AUTO + # 线程池相关配置 + threadPool: + class: org.quartz.simpl.SimpleThreadPool + threadCount: 20 + threadPriority: 5 + # JobStore 集群配置 + jobStore: + class: org.quartz.impl.jdbcjobstore.JobStoreTX + isClustered: true + clusterCheckinInterval: 15000 + txIsolationLevelSerializable: true + misfireThreshold: 60000 + tablePrefix: QRTZ_ + # sqlserver 启用 + # selectWithLockSQL: SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ? + +--- # 监控配置 +spring: + application: + name: bashi + boot: + admin: + # Spring Boot Admin Client 客户端的相关配置 + client: + # 设置 Spring Boot Admin Server 地址 + url: http://localhost:${server.port}${spring.boot.admin.context-path} + instance: + prefer-ip: true # 注册实例时,优先使用 IP + # Spring Boot Admin Server 服务端的相关配置 + context-path: /admin # 配置 Spring +dromara: + x-file-storage: + default-platform: tencent-cos-1 + aliyun-oss: + - platform: aliyun-oss-1 # 存储平台标识 + enable-storage: false # 启用存储 + access-key: 123 + secret-key: 13 + end-point: oss-cn-qingdao.aliyuncs.com + bucket-name: 123 + domain: https://file.168.com/ + base-path: dk/ + tencent-cos: + - platform: tencent-cos-1 # 存储平台标识 + enable-storage: true # 启用存储 + secret-id: IKIDy0vmjyz5AD7B80jBXnureUHxa6UgiZKs + secret-key: e4Siy8jHkEbqyuhS5b1d8VggUJ2c5PVR + region: ap-nanjing #存仓库所在地域 + bucket-name: an-download-1322700873 + domain: https://baidu.com/ # 访问域名,注意“/”结尾,例如:https://abc.cos.ap-nanjing.myqcloud.com/ + base-path: dk/ # 基础路径 + local-plus: + - platform: local-plus-1 + enable-storage: true + enable-access: true + domain: "http://127.0.0.1:8030/dk-plus/" # 访问域名,例如:“http://127.0.0.1:8030/local-plus/”,注意后面要和 path-patterns 保持一致,“/”结尾,本地存储建议使用相对路径,方便后期更换域名 + base-path: dk-plus/ # 基础路径 + path-patterns: /dk-plus/** # 访问路径 + storage-path: D:/Temp/ # 存储路径 diff --git a/bashi-admin/src/main/resources/banner.txt b/bashi-admin/src/main/resources/banner.txt new file mode 100644 index 0000000..7ad36c1 --- /dev/null +++ b/bashi-admin/src/main/resources/banner.txt @@ -0,0 +1,2 @@ +Application Version: 2.4.0 +Spring Boot Version: ${spring-boot.version} diff --git a/bashi-admin/src/main/resources/i18n/messages.properties b/bashi-admin/src/main/resources/i18n/messages.properties new file mode 100644 index 0000000..d63aa1f --- /dev/null +++ b/bashi-admin/src/main/resources/i18n/messages.properties @@ -0,0 +1,36 @@ +#错误消息 +not.null=* 必须填写 +user.jcaptcha.error=验证码错误 +user.jcaptcha.expire=验证码已失效 +user.not.exists=用户不存在/密码错误 +user.password.not.match=用户不存在/密码错误 +user.password.retry.limit.count=密码输入错误{0}次 +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定10分钟 +user.password.delete=对不起,您的账号已被删除 +user.blocked=用户已封禁,请联系管理员 +role.blocked=角色已封禁,请联系管理员 +user.logout.success=退出成功 + +length.not.valid=长度必须在{min}到{max}个字符之间 + +user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 +user.password.not.valid=* 5-50个字符 + +user.email.not.valid=邮箱格式错误 +user.mobile.phone.number.not.valid=手机号格式错误 +user.login.success=登录成功 +user.notfound=请重新登录 +user.forcelogout=管理员强制退出,请重新登录 +user.unknown.error=未知错误,请重新登录 + +##文件上传消息 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB! +upload.filename.exceed.length=上传的文件名最长{0}个字符 + +##权限 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] diff --git a/bashi-admin/src/main/resources/logback.xml b/bashi-admin/src/main/resources/logback.xml new file mode 100644 index 0000000..8d842f0 --- /dev/null +++ b/bashi-admin/src/main/resources/logback.xml @@ -0,0 +1,71 @@ + + + + + + + + + + ${console.log.pattern} + utf-8 + + + + + + ${log.path}/sys-info.log + + + + ${log.path}/%d{yyyy-MM-dd}/sys-info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + + + ${log.path}/sys-user.log + + + + ${log.path}/%d{yyyy-MM-dd}/sys-user.%d{yyyy-MM-dd}.log + + 20 + + + ${log.pattern} + + + + + + + + + + + + + + + + + + + + + + diff --git a/bashi-admin/src/test/java/com/bashi/BaseTest.java b/bashi-admin/src/test/java/com/bashi/BaseTest.java new file mode 100644 index 0000000..301bbd7 --- /dev/null +++ b/bashi-admin/src/test/java/com/bashi/BaseTest.java @@ -0,0 +1,10 @@ +package com.bashi; + +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class BaseTest { +} diff --git a/bashi-common/pom.xml b/bashi-common/pom.xml new file mode 100644 index 0000000..4176474 --- /dev/null +++ b/bashi-common/pom.xml @@ -0,0 +1,164 @@ + + + + bashi + com.bashi + 2.4.0 + + 4.0.0 + + bashi-common + + + common通用工具 + + + + + + + org.springframework + spring-context-support + + + + + org.springframework + spring-web + + + + + org.springframework.boot + spring-boot-starter-security + + + + + javax.validation + validation-api + + + + + org.apache.commons + commons-lang3 + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + org.apache.poi + poi-ooxml + + + + + org.yaml + snakeyaml + + + + + io.jsonwebtoken + jjwt + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + org.apache.commons + commons-pool2 + + + + + javax.servlet + javax.servlet-api + + + + com.baomidou + mybatis-plus-boot-starter + + + com.baomidou + mybatis-plus-extension + + + cn.hutool + hutool-all + + + org.projectlombok + lombok + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + io.github.openfeign + feign-okhttp + + + + de.codecentric + spring-boot-admin-starter-server + + + de.codecentric + spring-boot-admin-starter-client + + + + com.github.xiaoymin + knife4j-spring-boot-starter + + + + + org.springframework.boot + spring-boot-configuration-processor + + + + + org.redisson + redisson-spring-boot-starter + + + + + com.baomidou + dynamic-datasource-spring-boot-starter + + + + com.baomidou + lock4j-redisson-spring-boot-starter + + + + + com.alibaba + fastjson + + + + + diff --git a/bashi-common/src/main/java/com/bashi/common/annotation/DataScope.java b/bashi-common/src/main/java/com/bashi/common/annotation/DataScope.java new file mode 100644 index 0000000..12572a4 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/annotation/DataScope.java @@ -0,0 +1,33 @@ +package com.bashi.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; + +/** + * 数据权限过滤注解 + * + * @author duteliang + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataScope +{ + /** + * 部门表的别名 + */ + public String deptAlias() default ""; + + /** + * 用户表的别名 + */ + public String userAlias() default ""; + + /** + * 是否过滤用户权限 + */ + public boolean isUser() default false; +} diff --git a/bashi-common/src/main/java/com/bashi/common/annotation/DataSource.java b/bashi-common/src/main/java/com/bashi/common/annotation/DataSource.java new file mode 100644 index 0000000..88b57d9 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/annotation/DataSource.java @@ -0,0 +1,28 @@ +package com.bashi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.bashi.common.enums.DataSourceType; + +/** + * 自定义多数据源切换注解 + * + * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准 + * + * @author duteliang + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface DataSource +{ + /** + * 切换数据源名称 + */ + public DataSourceType value() default DataSourceType.MASTER; +} diff --git a/bashi-common/src/main/java/com/bashi/common/annotation/Excel.java b/bashi-common/src/main/java/com/bashi/common/annotation/Excel.java new file mode 100644 index 0000000..c30fd54 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/annotation/Excel.java @@ -0,0 +1,165 @@ +package com.bashi.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.math.BigDecimal; + +/** + * 自定义导出Excel数据注解 + * + * @author duteliang + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Excel +{ + /** + * 导出时在excel中排序 + */ + public int sort() default Integer.MAX_VALUE; + + /** + * 导出到Excel中的名字. + */ + public String name() default ""; + + /** + * 日期格式, 如: yyyy-MM-dd + */ + public String dateFormat() default ""; + + /** + * 如果是字典类型,请设置字典的type值 (如: sys_user_sex) + */ + public String dictType() default ""; + + /** + * 读取内容转表达式 (如: 0=男,1=女,2=未知) + */ + public String readConverterExp() default ""; + + /** + * 分隔符,读取字符串组内容 + */ + public String separator() default ","; + + /** + * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化) + */ + public int scale() default -1; + + /** + * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN + */ + public int roundingMode() default BigDecimal.ROUND_HALF_EVEN; + + /** + * 导出类型(0数字 1字符串) + */ + public ColumnType cellType() default ColumnType.STRING; + + /** + * 导出时在excel中每个列的高度 单位为字符 + */ + public double height() default 14; + + /** + * 导出时在excel中每个列的宽 单位为字符 + */ + public double width() default 16; + + /** + * 文字后缀,如% 90 变成90% + */ + public String suffix() default ""; + + /** + * 当值为空时,字段的默认值 + */ + public String defaultValue() default ""; + + /** + * 提示信息 + */ + public String prompt() default ""; + + /** + * 设置只能选择不能输入的列内容. + */ + public String[] combo() default {}; + + /** + * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. + */ + public boolean isExport() default true; + + /** + * 另一个类中的属性名称,支持多级获取,以小数点隔开 + */ + public String targetAttr() default ""; + + /** + * 是否自动统计数据,在最后追加一行统计数据总和 + */ + public boolean isStatistics() default false; + + /** + * 导出字段对齐方式(0:默认;1:靠左;2:居中;3:靠右) + */ + Align align() default Align.AUTO; + + public enum Align + { + AUTO(0), LEFT(1), CENTER(2), RIGHT(3); + private final int value; + + Align(int value) + { + this.value = value; + } + + public int value() + { + return this.value; + } + } + + /** + * 字段类型(0:导出导入;1:仅导出;2:仅导入) + */ + Type type() default Type.ALL; + + public enum Type + { + ALL(0), EXPORT(1), IMPORT(2); + private final int value; + + Type(int value) + { + this.value = value; + } + + public int value() + { + return this.value; + } + } + + public enum ColumnType + { + NUMERIC(0), STRING(1), IMAGE(2); + private final int value; + + ColumnType(int value) + { + this.value = value; + } + + public int value() + { + return this.value; + } + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/annotation/Excels.java b/bashi-common/src/main/java/com/bashi/common/annotation/Excels.java new file mode 100644 index 0000000..50ce99c --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/annotation/Excels.java @@ -0,0 +1,18 @@ +package com.bashi.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Excel注解集 + * + * @author duteliang + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Excels +{ + Excel[] value(); +} diff --git a/bashi-common/src/main/java/com/bashi/common/annotation/Log.java b/bashi-common/src/main/java/com/bashi/common/annotation/Log.java new file mode 100644 index 0000000..43f1799 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/annotation/Log.java @@ -0,0 +1,52 @@ +package com.bashi.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; + +import com.bashi.common.enums.BusinessType; +import com.bashi.common.enums.OperatorType; + +/** + * 自定义操作日志记录注解 + * + * @author duteliang + */ +@Target({ElementType.PARAMETER, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Log { + /** + * 模块 + */ + String title() default ""; + + /** + * 功能 + */ + BusinessType businessType() default BusinessType.OTHER; + + /** + * 操作人类别 + */ + OperatorType operatorType() default OperatorType.MANAGE; + + boolean isSaveDb() default true; + + /** + * 是否打印数据 + */ + boolean isPrint() default true; + + /** + * 是否保存请求的参数 + */ + boolean isSaveRequestData() default true; + + /** + * 是否保存响应的参数 + */ + boolean isSaveResponseData() default true; +} diff --git a/bashi-common/src/main/java/com/bashi/common/annotation/RepeatSubmit.java b/bashi-common/src/main/java/com/bashi/common/annotation/RepeatSubmit.java new file mode 100644 index 0000000..b3a8ec4 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/annotation/RepeatSubmit.java @@ -0,0 +1,23 @@ +package com.bashi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义注解防止表单重复提交 + * + * @author duteliang + * + */ +@Inherited +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RepeatSubmit +{ + +} diff --git a/bashi-common/src/main/java/com/bashi/common/com/Condition.java b/bashi-common/src/main/java/com/bashi/common/com/Condition.java new file mode 100644 index 0000000..d01dec0 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/com/Condition.java @@ -0,0 +1,32 @@ +package com.bashi.common.com; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; + +/** + *

created on 2021/7/7

+ * + * @author zhangliang + */ +public class Condition { + + public Condition() { + } + + public static IPage getPage(PageParams query) { + query.check(); + Page page = new Page(query.getPageNum(), query.getPageSize()); + if(query.getOrderByColumn() != null){ + OrderItem orderItem = null; + if("desc".equals(query.getIsAsc())){ + orderItem = OrderItem.desc(query.getOrderByColumn()); + }else{ + orderItem = OrderItem.asc(query.getOrderByColumn()); + } + page.addOrder(orderItem); + } + return page; + } + +} diff --git a/bashi-common/src/main/java/com/bashi/common/com/PageParams.java b/bashi-common/src/main/java/com/bashi/common/com/PageParams.java new file mode 100644 index 0000000..a272532 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/com/PageParams.java @@ -0,0 +1,39 @@ +package com.bashi.common.com; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + *

created on 2021/7/7

+ * + * @author zhangliang + */ +@Data +@ApiModel("分页参数") +public class PageParams { + /** 分页大小 */ + @ApiModelProperty("分页大小") + private Integer pageSize; + /** 当前页数 */ + @ApiModelProperty("当前页数") + private Integer pageNum; + /** 排序列 */ + @ApiModelProperty("排序列") + @Deprecated + private String orderByColumn; + /** 排序的方向desc或者asc */ + @ApiModelProperty(value = "排序的方向", example = "asc,desc") + @Deprecated + private String isAsc; + + public void check(){ + if(pageSize == null){ + pageSize = 10; + } + if(pageNum == null){ + pageNum = 1; + } + } + +} diff --git a/bashi-common/src/main/java/com/bashi/common/config/BsConfig.java b/bashi-common/src/main/java/com/bashi/common/config/BsConfig.java new file mode 100644 index 0000000..5b8598f --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/config/BsConfig.java @@ -0,0 +1,76 @@ +package com.bashi.common.config; + +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 读取项目相关配置 + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@Component +@ConfigurationProperties(prefix = "bashi") +public class BsConfig +{ + /** 项目名称 */ + private String name; + + /** 版本 */ + private String version; + + /** 版权年份 */ + private String copyrightYear; + + /** 实例演示开关 */ + private boolean demoEnabled; + + /** 上传路径 */ + @Getter + private static String profile; + + /** 获取地址开关 */ + @Getter + private static boolean addressEnabled; + + public void setProfile(String profile) + { + BsConfig.profile = profile; + } + + public void setAddressEnabled(boolean addressEnabled) + { + BsConfig.addressEnabled = addressEnabled; + } + + /** + * 获取头像上传路径 + */ + public static String getAvatarPath() + { + return getProfile() + "/avatar"; + } + + /** + * 获取下载路径 + */ + public static String getDownloadPath() + { + return getProfile() + "/download/"; + } + + /** + * 获取上传路径 + */ + public static String getUploadPath() + { + return getProfile() + "/upload"; + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/constant/Constants.java b/bashi-common/src/main/java/com/bashi/common/constant/Constants.java new file mode 100644 index 0000000..5707be8 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/constant/Constants.java @@ -0,0 +1,139 @@ +package com.bashi.common.constant; + +/** + * 通用常量信息 + * + * @author duteliang + */ +public class Constants +{ + /** + * UTF-8 字符集 + */ + public static final String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + public static final String GBK = "GBK"; + + /** + * http请求 + */ + public static final String HTTP = "http://"; + + /** + * https请求 + */ + public static final String HTTPS = "https://"; + + /** + * 通用成功标识 + */ + public static final String SUCCESS = "0"; + + /** + * 通用失败标识 + */ + public static final String FAIL = "1"; + + /** + * 登录成功 + */ + public static final String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + public static final String LOGOUT = "Logout"; + + /** + * 登录失败 + */ + public static final String LOGIN_FAIL = "Error"; + + /** + * 验证码 redis key + */ + public static final String CAPTCHA_CODE_KEY = "captcha_codes:"; + + /** + * 登录用户 redis key + */ + public static final String LOGIN_TOKEN_KEY = "login_tokens:"; + + /** + * 防重提交 redis key + */ + public static final String REPEAT_SUBMIT_KEY = "repeat_submit:"; + + /** + * 验证码有效期(分钟) + */ + public static final Integer CAPTCHA_EXPIRATION = 2; + + /** + * 令牌 + */ + public static final String TOKEN = "token"; + + /** + * 令牌前缀 + */ + public static final String TOKEN_PREFIX = "Bearer "; + + /** + * 令牌前缀 + */ + public static final String LOGIN_USER_KEY = "login_user_key"; + + /** + * 用户ID + */ + public static final String JWT_USERID = "userid"; + + /** + * 用户名称 + */ + public static final String JWT_USERNAME = "sub"; + + /** + * 用户头像 + */ + public static final String JWT_AVATAR = "avatar"; + + /** + * 创建时间 + */ + public static final String JWT_CREATED = "created"; + + /** + * 用户权限 + */ + public static final String JWT_AUTHORITIES = "authorities"; + + /** + * 参数管理 cache key + */ + public static final String SYS_CONFIG_KEY = "sys_config:"; + + /** + * 字典管理 cache key + */ + public static final String SYS_DICT_KEY = "sys_dict:"; + + /** + * 资源映射路径 前缀 + */ + public static final String RESOURCE_PREFIX = "/profile"; + + /** + * RMI 远程方法调用 + */ + public static final String LOOKUP_RMI = "rmi://"; + + /** + * 资源映射路径 前缀 + */ + public static final String REDIS_LOCK_KEY = "redis_lock:"; +} diff --git a/bashi-common/src/main/java/com/bashi/common/constant/DateConstant.java b/bashi-common/src/main/java/com/bashi/common/constant/DateConstant.java new file mode 100644 index 0000000..4538054 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/constant/DateConstant.java @@ -0,0 +1,13 @@ +package com.bashi.common.constant; + +/** + *

created on 2021/7/15

+ * + * @author zhangliang + */ +public class DateConstant { + + public final static String PATTERN_DATETIME = "yyyy-MM-dd HH:mm:ss"; + + public final static String PATTERN_DATE = "yyyy-MM-dd"; +} diff --git a/bashi-common/src/main/java/com/bashi/common/constant/GenConstants.java b/bashi-common/src/main/java/com/bashi/common/constant/GenConstants.java new file mode 100644 index 0000000..5078c70 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/constant/GenConstants.java @@ -0,0 +1,114 @@ +package com.bashi.common.constant; + +/** + * 代码生成通用常量 + * + * @author duteliang + */ +public class GenConstants +{ + /** 单表(增删改查) */ + public static final String TPL_CRUD = "crud"; + + /** 树表(增删改查) */ + public static final String TPL_TREE = "tree"; + + /** 主子表(增删改查) */ + public static final String TPL_SUB = "sub"; + + /** 树编码字段 */ + public static final String TREE_CODE = "treeCode"; + + /** 树父编码字段 */ + public static final String TREE_PARENT_CODE = "treeParentCode"; + + /** 树名称字段 */ + public static final String TREE_NAME = "treeName"; + + /** 上级菜单ID字段 */ + public static final String PARENT_MENU_ID = "parentMenuId"; + + /** 上级菜单名称字段 */ + public static final String PARENT_MENU_NAME = "parentMenuName"; + + /** 数据库字符串类型 */ + public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" }; + + /** 数据库文本类型 */ + public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" }; + + /** 数据库时间类型 */ + public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" }; + + /** 数据库数字类型 */ + public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer", + "bit", "bigint", "float", "double", "decimal" }; + + /** 页面不需要编辑字段 */ + public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" }; + + /** 页面不需要显示的列表字段 */ + public static final String[] COLUMNNAME_NOT_LIST = { "id", "create_by", "create_time", "del_flag", "update_by", + "update_time" }; + + /** 页面不需要查询字段 */ + public static final String[] COLUMNNAME_NOT_QUERY = { "id", "create_by", "create_time", "del_flag", "update_by", + "update_time", "remark" }; + + /** Entity基类字段 */ + public static final String[] BASE_ENTITY = { "createBy", "createTime", "updateBy", "updateTime", "remark" }; + + /** Tree基类字段 */ + public static final String[] TREE_ENTITY = { "parentName", "parentId", "orderNum", "ancestors", "children" }; + + /** 文本框 */ + public static final String HTML_INPUT = "input"; + + /** 文本域 */ + public static final String HTML_TEXTAREA = "textarea"; + + /** 下拉框 */ + public static final String HTML_SELECT = "select"; + + /** 单选框 */ + public static final String HTML_RADIO = "radio"; + + /** 复选框 */ + public static final String HTML_CHECKBOX = "checkbox"; + + /** 日期控件 */ + public static final String HTML_DATETIME = "datetime"; + + /** 图片上传控件 */ + public static final String HTML_IMAGE_UPLOAD = "imageUpload"; + + /** 文件上传控件 */ + public static final String HTML_FILE_UPLOAD = "fileUpload"; + + /** 富文本控件 */ + public static final String HTML_EDITOR = "editor"; + + /** 字符串类型 */ + public static final String TYPE_STRING = "String"; + + /** 整型 */ + public static final String TYPE_INTEGER = "Integer"; + + /** 长整型 */ + public static final String TYPE_LONG = "Long"; + + /** 浮点型 */ + public static final String TYPE_DOUBLE = "Double"; + + /** 高精度计算类型 */ + public static final String TYPE_BIGDECIMAL = "BigDecimal"; + + /** 时间类型 */ + public static final String TYPE_DATE = "Date"; + + /** 模糊查询 */ + public static final String QUERY_LIKE = "LIKE"; + + /** 需要 */ + public static final String REQUIRE = "1"; +} diff --git a/bashi-common/src/main/java/com/bashi/common/constant/ScheduleConstants.java b/bashi-common/src/main/java/com/bashi/common/constant/ScheduleConstants.java new file mode 100644 index 0000000..e9c7bd2 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/constant/ScheduleConstants.java @@ -0,0 +1,50 @@ +package com.bashi.common.constant; + +/** + * 任务调度通用常量 + * + * @author duteliang + */ +public class ScheduleConstants +{ + public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME"; + + /** 执行目标key */ + public static final String TASK_PROPERTIES = "TASK_PROPERTIES"; + + /** 默认 */ + public static final String MISFIRE_DEFAULT = "0"; + + /** 立即触发执行 */ + public static final String MISFIRE_IGNORE_MISFIRES = "1"; + + /** 触发一次执行 */ + public static final String MISFIRE_FIRE_AND_PROCEED = "2"; + + /** 不触发立即执行 */ + public static final String MISFIRE_DO_NOTHING = "3"; + + public enum Status + { + /** + * 正常 + */ + NORMAL("0"), + /** + * 暂停 + */ + PAUSE("1"); + + private String value; + + private Status(String value) + { + this.value = value; + } + + public String getValue() + { + return value; + } + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/constant/UserConstants.java b/bashi-common/src/main/java/com/bashi/common/constant/UserConstants.java new file mode 100644 index 0000000..42ac007 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/constant/UserConstants.java @@ -0,0 +1,63 @@ +package com.bashi.common.constant; + +/** + * 用户常量信息 + * + * @author duteliang + */ +public class UserConstants +{ + /** + * 平台内系统用户的唯一标志 + */ + public static final String SYS_USER = "SYS_USER"; + + /** 正常状态 */ + public static final String NORMAL = "0"; + + /** 异常状态 */ + public static final String EXCEPTION = "1"; + + /** 用户封禁状态 */ + public static final String USER_DISABLE = "1"; + + /** 角色封禁状态 */ + public static final String ROLE_DISABLE = "1"; + + /** 部门正常状态 */ + public static final String DEPT_NORMAL = "0"; + + /** 部门停用状态 */ + public static final String DEPT_DISABLE = "1"; + + /** 字典正常状态 */ + public static final String DICT_NORMAL = "0"; + + /** 是否为系统默认(是) */ + public static final String YES = "Y"; + + /** 是否菜单外链(是) */ + public static final String YES_FRAME = "0"; + + /** 是否菜单外链(否) */ + public static final String NO_FRAME = "1"; + + /** 菜单类型(目录) */ + public static final String TYPE_DIR = "M"; + + /** 菜单类型(菜单) */ + public static final String TYPE_MENU = "C"; + + /** 菜单类型(按钮) */ + public static final String TYPE_BUTTON = "F"; + + /** Layout组件标识 */ + public final static String LAYOUT = "Layout"; + + /** ParentView组件标识 */ + public final static String PARENT_VIEW = "ParentView"; + + /** 校验返回结果码 */ + public final static String UNIQUE = "0"; + public final static String NOT_UNIQUE = "1"; +} diff --git a/bashi-common/src/main/java/com/bashi/common/core/controller/BaseController.java b/bashi-common/src/main/java/com/bashi/common/core/controller/BaseController.java new file mode 100644 index 0000000..234b60a --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/controller/BaseController.java @@ -0,0 +1,70 @@ +package com.bashi.common.core.controller; + +import cn.hutool.core.util.StrUtil; +import com.bashi.common.core.domain.AjaxResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * web层通用数据处理 + * + * @author duteliang + */ +public class BaseController { + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * 响应返回结果 + * + * @param rows 影响行数 + * @return 操作结果 + */ + protected AjaxResult toAjax(int rows) { + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); + } + + /** + * 响应返回结果 + * + * @param result 结果 + * @return 操作结果 + */ + protected AjaxResult toAjax(boolean result) { + return result ? success() : error(); + } + + /** + * 返回成功 + */ + public AjaxResult success() { + return AjaxResult.success(); + } + + /** + * 返回失败消息 + */ + public AjaxResult error() { + return AjaxResult.error(); + } + + /** + * 返回成功消息 + */ + public AjaxResult success(String message) { + return AjaxResult.success(message); + } + + /** + * 返回失败消息 + */ + public AjaxResult error(String message) { + return AjaxResult.error(message); + } + + /** + * 页面跳转 + */ + public String redirect(String url) { + return StrUtil.format("redirect:{}", url); + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/core/domain/AjaxResult.java b/bashi-common/src/main/java/com/bashi/common/core/domain/AjaxResult.java new file mode 100644 index 0000000..f99e36d --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/domain/AjaxResult.java @@ -0,0 +1,134 @@ +package com.bashi.common.core.domain; + +import cn.hutool.http.HttpStatus; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * 操作消息提醒 + * + * @author Lion Li + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@ApiModel("请求响应对象") +public class AjaxResult { + + private static final long serialVersionUID = 1L; + + /** + * 状态码 + */ + @ApiModelProperty("消息状态码") + private int code; + + /** + * 返回内容 + */ + @ApiModelProperty("消息内容") + private String msg; + + /** + * 数据对象 + */ + @ApiModelProperty("数据对象") + private T data; + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + */ + public AjaxResult(int code, String msg) { + this.code = code; + this.msg = msg; + } + + /** + * 返回成功消息 + * + * @return 成功消息 + */ + public static AjaxResult success() { + return AjaxResult.success("操作成功"); + } + + /** + * 返回成功数据 + * + * @return 成功消息 + */ + public static AjaxResult success(T data) { + return AjaxResult.success("操作成功", data); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @return 成功消息 + */ + public static AjaxResult success(String msg) { + return AjaxResult.success(msg, null); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 成功消息 + */ + public static AjaxResult success(String msg, T data) { + return new AjaxResult<>(HttpStatus.HTTP_OK, msg, data); + } + + /** + * 返回错误消息 + * + * @return + */ + public static AjaxResult error() { + return AjaxResult.error("操作失败"); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult error(String msg) { + return AjaxResult.error(msg, null); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static AjaxResult error(String msg, T data) { + return new AjaxResult<>(HttpStatus.HTTP_BAD_REQUEST, msg, data); + } + + /** + * 返回错误消息 + * + * @param code 状态码 + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult error(int code, String msg) { + return new AjaxResult<>(code, msg, null); + } + +} diff --git a/bashi-common/src/main/java/com/bashi/common/core/domain/BaseEntity.java b/bashi-common/src/main/java/com/bashi/common/core/domain/BaseEntity.java new file mode 100644 index 0000000..be66f50 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/domain/BaseEntity.java @@ -0,0 +1,46 @@ +package com.bashi.common.core.domain; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * Entity基类 + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class BaseEntity implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 搜索值 */ + private String searchValue; + + /** 创建者 */ + private String createBy; + + /** 创建时间 */ + private Date createTime; + + /** 更新者 */ + private String updateBy; + + /** 更新时间 */ + private Date updateTime; + + /** 备注 */ + private String remark; + + /** 请求参数 */ + private Map params = new HashMap<>(); + +} diff --git a/bashi-common/src/main/java/com/bashi/common/core/domain/TreeEntity.java b/bashi-common/src/main/java/com/bashi/common/core/domain/TreeEntity.java new file mode 100644 index 0000000..d6db02d --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/domain/TreeEntity.java @@ -0,0 +1,38 @@ +package com.bashi.common.core.domain; + +import lombok.*; +import lombok.experimental.Accessors; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tree基类 + * + * @author duteliang + */ + +@EqualsAndHashCode(callSuper = true) +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class TreeEntity extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 父菜单名称 */ + private String parentName; + + /** 父菜单ID */ + private Long parentId; + + /** 显示顺序 */ + private Integer orderNum; + + /** 祖级列表 */ + private String ancestors; + + /** 子部门 */ + private List children = new ArrayList<>(); + +} diff --git a/bashi-common/src/main/java/com/bashi/common/core/domain/TreeSelect.java b/bashi-common/src/main/java/com/bashi/common/core/domain/TreeSelect.java new file mode 100644 index 0000000..64ea2e0 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/domain/TreeSelect.java @@ -0,0 +1,50 @@ +package com.bashi.common.core.domain; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.bashi.common.core.domain.entity.SysDept; +import com.bashi.common.core.domain.entity.SysMenu; +import lombok.*; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Treeselect树结构实体类 + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class TreeSelect implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 节点ID */ + private Long id; + + /** 节点名称 */ + private String label; + + /** 子节点 */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List children; + + public TreeSelect(SysDept dept) + { + this.id = dept.getDeptId(); + this.label = dept.getDeptName(); + this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + public TreeSelect(SysMenu menu) + { + this.id = menu.getMenuId(); + this.label = menu.getMenuName(); + this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + +} diff --git a/bashi-common/src/main/java/com/bashi/common/core/domain/entity/Customer.java b/bashi-common/src/main/java/com/bashi/common/core/domain/entity/Customer.java new file mode 100644 index 0000000..d64ba8b --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/domain/entity/Customer.java @@ -0,0 +1,115 @@ +package com.bashi.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.bashi.common.constant.DateConstant; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 客户 + * @TableName dk_customer + */ +@TableName(value ="dk_customer") +@Data +@ApiModel("客户模型") +public class Customer implements Serializable { + /** + * ID + */ + @TableId(type = IdType.AUTO) + private Long id; + + /** + * 手机 + */ + @ApiModelProperty("手机") + private String phoneNumber; + + /** + * 用户名称 + */ + @ApiModelProperty("用户名称") + private String nickName; + + /** + * 用户密码 + */ + @ApiModelProperty("用户密码") + private String password; + + /** + * 是否实名 + */ + @ApiModelProperty("是否实名") + private Integer realNameAuth; + + /** + * 是否贷款 + */ + @ApiModelProperty("是否贷款") + private Integer loansFlag; + + @ApiModelProperty("允许提现") + private Boolean allowWithdrawFlag; + + /** + * 是否提现 + */ + @ApiModelProperty("是否提现") + private Integer withdrawFlag; + + /** + * 余额 + */ + @ApiModelProperty("余额") + private BigDecimal account; + + @ApiModelProperty("贷款金额") + private BigDecimal borrowAccount; + + @ApiModelProperty("还款金额") + private BigDecimal repaymentAccount; + + /** + * 状态 0-正常 1-封禁 + */ + @ApiModelProperty("状态 0-正常 1-封禁") + private Integer status; + + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonFormat(pattern = DateConstant.PATTERN_DATETIME) + @ApiModelProperty("最后登陆时间") + private LocalDateTime lastLoginTime; + + @ApiModelProperty("最后登陆IP") + private String lastLoginIp; + + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonFormat(pattern = DateConstant.PATTERN_DATETIME) + @ApiModelProperty("修改时间") + private LocalDateTime updateTime; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @JsonIgnore + public String getPassword() { + return password; + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/core/domain/entity/SysDept.java b/bashi-common/src/main/java/com/bashi/common/core/domain/entity/SysDept.java new file mode 100644 index 0000000..851562b --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/domain/entity/SysDept.java @@ -0,0 +1,99 @@ +package com.bashi.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.*; + +/** + * 部门表 sys_dept + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("sys_dept") +public class SysDept implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 部门ID */ + @TableId(value = "dept_id",type = IdType.AUTO) + private Long deptId; + + /** 父部门ID */ + private Long parentId; + + /** 祖级列表 */ + private String ancestors; + + /** 部门名称 */ + @NotBlank(message = "部门名称不能为空") + @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符") + private String deptName; + + /** 显示顺序 */ + @NotBlank(message = "显示顺序不能为空") + private String orderNum; + + /** 负责人 */ + private String leader; + + /** 联系电话 */ + @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符") + private String phone; + + /** 邮箱 */ + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + private String email; + + /** 部门状态:0正常,1停用 */ + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + @TableLogic + private String delFlag; + + /** 父部门名称 */ + @TableField(exist = false) + private String parentName; + + /** 创建者 */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** 创建时间 */ + @TableField(fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 更新者 */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** 更新时间 */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** 子部门 */ + @TableField(exist = false) + private List children = new ArrayList(); + + /** + * 请求参数 + */ + @TableField(exist = false) + private Map params = new HashMap<>(); + +} diff --git a/bashi-common/src/main/java/com/bashi/common/core/domain/entity/SysDictData.java b/bashi-common/src/main/java/com/bashi/common/core/domain/entity/SysDictData.java new file mode 100644 index 0000000..35d212f --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/domain/entity/SysDictData.java @@ -0,0 +1,107 @@ +package com.bashi.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.bashi.common.annotation.Excel; +import com.bashi.common.annotation.Excel.ColumnType; +import com.bashi.common.constant.UserConstants; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 字典数据表 sys_dict_data + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("sys_dict_data") +public class SysDictData implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 字典编码 */ + @Excel(name = "字典编码", cellType = ColumnType.NUMERIC) + @TableId(value = "dict_code",type = IdType.AUTO) + private Long dictCode; + + /** 字典排序 */ + @Excel(name = "字典排序", cellType = ColumnType.NUMERIC) + private Long dictSort; + + /** 字典标签 */ + @Excel(name = "字典标签") + @NotBlank(message = "字典标签不能为空") + @Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符") + private String dictLabel; + + /** 字典键值 */ + @Excel(name = "字典键值") + @NotBlank(message = "字典键值不能为空") + @Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符") + private String dictValue; + + /** 字典类型 */ + @Excel(name = "字典类型") + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符") + private String dictType; + + /** 样式属性(其他样式扩展) */ + @Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符") + private String cssClass; + + /** 表格字典样式 */ + private String listClass; + + /** 是否默认(Y是 N否) */ + @Excel(name = "是否默认", readConverterExp = "Y=是,N=否") + private String isDefault; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 创建者 */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** 创建时间 */ + @TableField(fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 更新者 */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** 更新时间 */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** 备注 */ + private String remark; + + /** + * 请求参数 + */ + @TableField(exist = false) + private Map params = new HashMap<>(); + + public boolean getDefault() + { + return UserConstants.YES.equals(this.isDefault) ? true : false; + } + +} diff --git a/bashi-common/src/main/java/com/bashi/common/core/domain/entity/SysDictType.java b/bashi-common/src/main/java/com/bashi/common/core/domain/entity/SysDictType.java new file mode 100644 index 0000000..0885da9 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/domain/entity/SysDictType.java @@ -0,0 +1,80 @@ +package com.bashi.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.bashi.common.annotation.Excel; +import com.bashi.common.annotation.Excel.ColumnType; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 字典类型表 sys_dict_type + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("sys_dict_type") +public class SysDictType implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 字典主键 */ + @Excel(name = "字典主键", cellType = ColumnType.NUMERIC) + @TableId(value = "dict_id",type = IdType.AUTO) + private Long dictId; + + /** 字典名称 */ + @Excel(name = "字典名称") + @NotBlank(message = "字典名称不能为空") + @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符") + private String dictName; + + /** 字典类型 */ + @Excel(name = "字典类型") + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符") + private String dictType; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 创建者 */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** 创建时间 */ + @TableField(fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 更新者 */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** 更新时间 */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** 备注 */ + private String remark; + + /** + * 请求参数 + */ + @TableField(exist = false) + private Map params = new HashMap<>(); + +} diff --git a/bashi-common/src/main/java/com/bashi/common/core/domain/entity/SysMenu.java b/bashi-common/src/main/java/com/bashi/common/core/domain/entity/SysMenu.java new file mode 100644 index 0000000..04ca021 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/domain/entity/SysMenu.java @@ -0,0 +1,108 @@ +package com.bashi.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.*; + +/** + * 菜单权限表 sys_menu + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("sys_menu") +public class SysMenu implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 菜单ID */ + @TableId(value = "menu_id",type = IdType.AUTO) + private Long menuId; + + /** 菜单名称 */ + @NotBlank(message = "菜单名称不能为空") + @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符") + private String menuName; + + /** 父菜单名称 */ + @TableField(exist = false) + private String parentName; + + /** 父菜单ID */ + private Long parentId; + + /** 显示顺序 */ + @NotBlank(message = "显示顺序不能为空") + private String orderNum; + + /** 路由地址 */ + @Size(min = 0, max = 200, message = "路由地址不能超过200个字符") + private String path; + + /** 组件路径 */ + @Size(min = 0, max = 200, message = "组件路径不能超过255个字符") + private String component; + + /** 是否为外链(0是 1否) */ + private String isFrame; + + /** 是否缓存(0缓存 1不缓存) */ + private String isCache; + + /** 类型(M目录 C菜单 F按钮) */ + @NotBlank(message = "菜单类型不能为空") + private String menuType; + + /** 显示状态(0显示 1隐藏) */ + private String visible; + + /** 菜单状态(0显示 1隐藏) */ + private String status; + + /** 权限字符串 */ + @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符") + private String perms; + + /** 菜单图标 */ + private String icon; + + /** 创建者 */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** 创建时间 */ + @TableField(fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 更新者 */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** 更新时间 */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** 备注 */ + private String remark; + + /** 请求参数 */ + @TableField(exist = false) + private Map params = new HashMap<>(); + + /** 子菜单 */ + @TableField(exist = false) + private List children = new ArrayList(); + +} diff --git a/bashi-common/src/main/java/com/bashi/common/core/domain/entity/SysRole.java b/bashi-common/src/main/java/com/bashi/common/core/domain/entity/SysRole.java new file mode 100644 index 0000000..a1e0f52 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/domain/entity/SysRole.java @@ -0,0 +1,126 @@ +package com.bashi.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.bashi.common.annotation.Excel; +import com.bashi.common.annotation.Excel.ColumnType; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 角色表 sys_role + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("sys_role") +public class SysRole implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 角色ID */ + @Excel(name = "角色序号", cellType = ColumnType.NUMERIC) + @TableId(value = "role_id",type = IdType.AUTO) + private Long roleId; + + /** 角色名称 */ + @Excel(name = "角色名称") + @NotBlank(message = "角色名称不能为空") + @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符") + private String roleName; + + /** 角色权限 */ + @Excel(name = "角色权限") + @NotBlank(message = "权限字符不能为空") + @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符") + private String roleKey; + + /** 角色排序 */ + @Excel(name = "角色排序") + @NotBlank(message = "显示顺序不能为空") + private String roleSort; + + /** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */ + @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限") + private String dataScope; + + /** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */ + private boolean menuCheckStrictly; + + /** 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */ + private boolean deptCheckStrictly; + + /** 角色状态(0正常 1停用) */ + @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + @TableLogic + private String delFlag; + + /** 创建者 */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** 创建时间 */ + @TableField(fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 更新者 */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** 更新时间 */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** 备注 */ + private String remark; + + /** + * 请求参数 + */ + @TableField(exist = false) + private Map params = new HashMap<>(); + + /** 用户是否存在此角色标识 默认不存在 */ + @TableField(exist = false) + private boolean flag = false; + + /** 菜单组 */ + @TableField(exist = false) + private Long[] menuIds; + + /** 部门组(数据权限) */ + @TableField(exist = false) + private Long[] deptIds; + + public SysRole(Long roleId) + { + this.roleId = roleId; + } + + public boolean isAdmin() + { + return isAdmin(this.roleId); + } + + public static boolean isAdmin(Long roleId) + { + return roleId != null && 1L == roleId; + } + +} diff --git a/bashi-common/src/main/java/com/bashi/common/core/domain/entity/SysUser.java b/bashi-common/src/main/java/com/bashi/common/core/domain/entity/SysUser.java new file mode 100644 index 0000000..3de13a3 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/domain/entity/SysUser.java @@ -0,0 +1,168 @@ +package com.bashi.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.bashi.common.annotation.Excel; +import com.bashi.common.annotation.Excel.ColumnType; +import com.bashi.common.annotation.Excel.Type; +import com.bashi.common.annotation.Excels; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 用户对象 sys_user + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("sys_user") +public class SysUser implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 用户ID */ + @Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号") + @TableId(value = "user_id",type = IdType.AUTO) + private Long userId; + + /** 部门ID */ + @Excel(name = "部门编号", type = Type.IMPORT) + private Long deptId; + + /** 用户账号 */ + @NotBlank(message = "用户账号不能为空") + @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符") + @Excel(name = "登录名称") + private String userName; + + /** 用户昵称 */ + @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符") + @Excel(name = "用户名称") + private String nickName; + + /** 用户邮箱 */ + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + @Excel(name = "用户邮箱") + private String email; + + /** 手机号码 */ + @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符") + @Excel(name = "手机号码") + private String phonenumber; + + /** 用户性别 */ + @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知") + private String sex; + + /** 用户头像 */ + private String avatar; + + private String openId; + + /** 密码 */ + private String password; + + @JsonIgnore + @JsonProperty + public String getPassword() { + return password; + } + + /** 盐加密 */ + private String salt; + + /** 帐号状态(0正常 1停用) */ + @Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + @TableLogic + private String delFlag; + + /** 最后登录IP */ + @Excel(name = "最后登录IP", type = Type.EXPORT) + private String loginIp; + + /** 最后登录时间 */ + @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT) + private Date loginDate; + + /** 创建者 */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** 创建时间 */ + @TableField(fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 更新者 */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** 更新时间 */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** 备注 */ + private String remark; + + /** + * 请求参数 + */ + @TableField(exist = false) + private Map params = new HashMap<>(); + + /** 部门对象 */ + @Excels({ + @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT), + @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT) + }) + @TableField(exist = false) + private SysDept dept; + + /** 角色对象 */ + @TableField(exist = false) + private List roles; + + /** 角色组 */ + @TableField(exist = false) + private Long[] roleIds; + + /** 岗位组 */ + @TableField(exist = false) + private Long[] postIds; + + public SysUser(Long userId) + { + this.userId = userId; + } + + public boolean isAdmin() + { + return isAdmin(this.userId); + } + + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } + +} diff --git a/bashi-common/src/main/java/com/bashi/common/core/domain/model/LoginBody.java b/bashi-common/src/main/java/com/bashi/common/core/domain/model/LoginBody.java new file mode 100644 index 0000000..e394a12 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/domain/model/LoginBody.java @@ -0,0 +1,37 @@ +package com.bashi.common.core.domain.model; + +import lombok.*; +import lombok.experimental.Accessors; + +/** + * 用户登录对象 + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class LoginBody +{ + /** + * 用户名 + */ + private String username; + + /** + * 用户密码 + */ + private String password; + + /** + * 验证码 + */ + private String code; + + /** + * 唯一标识 + */ + private String uuid = ""; + +} diff --git a/bashi-common/src/main/java/com/bashi/common/core/domain/model/LoginPhoneBody.java b/bashi-common/src/main/java/com/bashi/common/core/domain/model/LoginPhoneBody.java new file mode 100644 index 0000000..17275f1 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/domain/model/LoginPhoneBody.java @@ -0,0 +1,39 @@ +package com.bashi.common.core.domain.model; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + *

created on 2021/7/13

+ * + * @author zhangliang + */ +@Data +@NoArgsConstructor +@Accessors(chain = true) +@ApiModel("登录模型") +public class LoginPhoneBody implements Serializable { + + private static final long serialVersionUID=1L; + + @ApiModelProperty("公众号登录必填") + private String openId; + + @ApiModelProperty("手机号") + private String mobile; + + @ApiModelProperty("密码") + private String password; + + @ApiModelProperty("验证码") + private String mobileCode; + + @ApiModelProperty("用户-1") + private Integer loginRole; + +} diff --git a/bashi-common/src/main/java/com/bashi/common/core/domain/model/LoginUser.java b/bashi-common/src/main/java/com/bashi/common/core/domain/model/LoginUser.java new file mode 100644 index 0000000..ae3489f --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/domain/model/LoginUser.java @@ -0,0 +1,142 @@ +package com.bashi.common.core.domain.model; + +import com.bashi.common.core.domain.entity.Customer; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.bashi.common.core.domain.entity.SysUser; +import lombok.*; +import lombok.experimental.Accessors; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.Set; + +/** + * 登录用户身份权限 + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class LoginUser implements UserDetails { + private static final long serialVersionUID = 1L; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 权限列表 + */ + private Set permissions; + + /** + * 用户信息 + */ + private SysUser user; + + private Customer customer; + /** + * 0-user + * 1-sms + */ + private Integer type = 0; + + public LoginUser(SysUser user, Set permissions) { + this.user = user; + this.permissions = permissions; + } + + @JsonIgnore + @Override + public String getPassword() { + return user.getPassword(); + } + + @JsonIgnore + @Override + public String getUsername() { + return type == 1 ? customer.getPhoneNumber() : user.getUserName(); + } + + /** + * 账户是否未过期,过期无法验证 + */ + @JsonIgnore + @Override + public boolean isAccountNonExpired() { + return true; + } + + /** + * 指定用户是否解锁,锁定的用户无法进行身份验证 + * + * @return + */ + @JsonIgnore + @Override + public boolean isAccountNonLocked() { + return true; + } + + /** + * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 + * + * @return + */ + @JsonIgnore + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + /** + * 是否可用 ,禁用的用户不能身份验证 + * + * @return + */ + @JsonIgnore + @Override + public boolean isEnabled() { + return true; + } + + @JsonIgnore + @Override + public Collection getAuthorities() { + return null; + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/core/mybatisplus/cache/MybatisPlusRedisCache.java b/bashi-common/src/main/java/com/bashi/common/core/mybatisplus/cache/MybatisPlusRedisCache.java new file mode 100644 index 0000000..4abd5c5 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/mybatisplus/cache/MybatisPlusRedisCache.java @@ -0,0 +1,102 @@ +package com.bashi.common.core.mybatisplus.cache; + +import cn.hutool.extra.spring.SpringUtil; +import com.bashi.common.core.redis.RedisCache; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.cache.Cache; +import org.springframework.data.redis.connection.RedisServerCommands; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.util.CollectionUtils; + +import java.util.Collection; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * mybatis-redis 二级缓存 + * + * @author Lion Li + */ +@Slf4j +public class MybatisPlusRedisCache implements Cache { + + private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true); + + private RedisCache redisCache; + + private String id; + + public MybatisPlusRedisCache(final String id) { + if (id == null) { + throw new IllegalArgumentException("Cache instances require an ID"); + } + this.id = id; + } + + @Override + public String getId() { + return this.id; + } + + @Override + public void putObject(Object key, Object value) { + if (redisCache == null) { + redisCache = SpringUtil.getBean(RedisCache.class); + } + if (value != null) { + redisCache.setCacheObject(key.toString(), value); + } + } + + @Override + public Object getObject(Object key) { + if (redisCache == null) { + //由于启动期间注入失败,只能运行期间注入,这段代码可以删除 + redisCache = SpringUtil.getBean(RedisCache.class); + } + try { + if (key != null) { + return redisCache.getCacheObject(key.toString()); + } + } catch (Exception e) { + e.printStackTrace(); + log.error("缓存出错"); + } + return null; + } + + @Override + public Object removeObject(Object key) { + if (redisCache == null) { + redisCache = SpringUtil.getBean(RedisCache.class); + } + if (key != null) { + redisCache.deleteObject(key.toString()); + } + return null; + } + + @Override + public void clear() { + log.debug("清空缓存"); + if (redisCache == null) { + redisCache = SpringUtil.getBean(RedisCache.class); + } + Collection keys = redisCache.keys("*:" + this.id + "*"); + if (!CollectionUtils.isEmpty(keys)) { + redisCache.deleteObject(keys); + } + } + + @Override + public int getSize() { + RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate"); + Long size = redisTemplate.execute(RedisServerCommands::dbSize); + return size.intValue(); + } + + @Override + public ReadWriteLock getReadWriteLock() { + return this.readWriteLock; + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/core/mybatisplus/core/BaseMapperPlus.java b/bashi-common/src/main/java/com/bashi/common/core/mybatisplus/core/BaseMapperPlus.java new file mode 100644 index 0000000..27de8e3 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/mybatisplus/core/BaseMapperPlus.java @@ -0,0 +1,18 @@ +package com.bashi.common.core.mybatisplus.core; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Collection; + +/** + * 自定义 Mapper 接口, 实现 自定义扩展 + * + * @author Lion Li + * @since 2021-05-13 + */ +public interface BaseMapperPlus extends BaseMapper { + + int insertAll(@Param("list") Collection batchList); + +} diff --git a/bashi-common/src/main/java/com/bashi/common/core/mybatisplus/methods/InsertAll.java b/bashi-common/src/main/java/com/bashi/common/core/mybatisplus/methods/InsertAll.java new file mode 100644 index 0000000..1975ec9 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/mybatisplus/methods/InsertAll.java @@ -0,0 +1,52 @@ +package com.bashi.common.core.mybatisplus.methods; + +import cn.hutool.core.util.StrUtil; +import org.apache.ibatis.executor.keygen.NoKeyGenerator; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlSource; + +import com.baomidou.mybatisplus.core.injector.AbstractMethod; +import com.baomidou.mybatisplus.core.metadata.TableInfo; + +/** + * 单sql批量插入 + * + * @author Lion Li + */ +public class InsertAll extends AbstractMethod { + + @Override + public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { + final String sql = ""; + final String fieldSql = prepareFieldSql(tableInfo); + final String valueSql = prepareValuesSqlForMysqlBatch(tableInfo); + final String sqlResult = String.format(sql, tableInfo.getTableName(), fieldSql, valueSql); + SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass); + return this.addInsertMappedStatement(mapperClass, modelClass, "insertAll", sqlSource, new NoKeyGenerator(), null, null); + } + + private String prepareFieldSql(TableInfo tableInfo) { + StringBuilder fieldSql = new StringBuilder(); + if (StrUtil.isNotBlank(tableInfo.getKeyColumn())) { + fieldSql.append(tableInfo.getKeyColumn()).append(","); + } + tableInfo.getFieldList().forEach(x -> fieldSql.append(x.getColumn()).append(",")); + fieldSql.delete(fieldSql.length() - 1, fieldSql.length()); + fieldSql.insert(0, "("); + fieldSql.append(")"); + return fieldSql.toString(); + } + + private String prepareValuesSqlForMysqlBatch(TableInfo tableInfo) { + final StringBuilder valueSql = new StringBuilder(); + valueSql.append(""); + if (StrUtil.isNotBlank(tableInfo.getKeyColumn())) { + valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},"); + } + tableInfo.getFieldList().forEach(x -> valueSql.append("#{item.").append(x.getProperty()).append("},")); + valueSql.delete(valueSql.length() - 1, valueSql.length()); + valueSql.append(""); + return valueSql.toString(); + } +} + diff --git a/bashi-common/src/main/java/com/bashi/common/core/page/PagePlus.java b/bashi-common/src/main/java/com/bashi/common/core/page/PagePlus.java new file mode 100644 index 0000000..ec0f6cd --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/page/PagePlus.java @@ -0,0 +1,156 @@ +package com.bashi.common.core.page; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * 分页 Page 增强对象 + * + * @param 数据库实体 + * @param vo实体 + * @author Lion Li + */ +@Data +@Accessors(chain = true) +public class PagePlus implements IPage { + + /** + * domain实体列表 + */ + private List records = Collections.emptyList(); + + /** + * vo实体列表 + */ + private List recordsVo = Collections.emptyList(); + + /** + * 总数 + */ + private long total = 0L; + + /** + * 页长度 + */ + private long size = 10L; + + /** + * 当前页 + */ + private long current = 1L; + + /** + * 排序字段信息 + */ + private List orders = new ArrayList<>(); + + /** + * 自动优化 COUNT SQL + */ + private boolean optimizeCountSql = true; + + /** + * 是否进行 count 查询 + */ + private boolean isSearchCount = true; + + /** + * 是否命中count缓存 + */ + private boolean hitCount = false; + + /** + * countId + */ + private String countId; + + /** + * 最大limit + */ + private Long maxLimit; + + public PagePlus() { + } + + public PagePlus(long current, long size) { + this(current, size, 0L); + } + + public PagePlus(long current, long size, long total) { + this(current, size, total, true); + } + + public PagePlus(long current, long size, boolean isSearchCount) { + this(current, size, 0L, isSearchCount); + } + + public PagePlus(long current, long size, long total, boolean isSearchCount) { + if (current > 1L) { + this.current = current; + } + this.size = size; + this.total = total; + this.isSearchCount = isSearchCount; + } + + @Override + public String countId() { + return this.getCountId(); + } + + @Override + public Long maxLimit() { + return this.getMaxLimit(); + } + + public PagePlus addOrder(OrderItem... items) { + this.orders.addAll(Arrays.asList(items)); + return this; + } + + public PagePlus addOrder(List items) { + this.orders.addAll(items); + return this; + } + + @Override + public List orders() { + return this.getOrders(); + } + + @Override + public boolean optimizeCountSql() { + return this.optimizeCountSql; + } + + @Override + public long getPages() { + // 解决 github issues/3208 + return IPage.super.getPages(); + } + + public static PagePlus of(long current, long size) { + return of(current, size, 0); + } + + public static PagePlus of(long current, long size, long total) { + return of(current, size, total, true); + } + + public static PagePlus of(long current, long size, boolean searchCount) { + return of(current, size, 0, searchCount); + } + + public static PagePlus of(long current, long size, long total, boolean searchCount) { + return new PagePlus<>(current, size, total, searchCount); + } + +} + diff --git a/bashi-common/src/main/java/com/bashi/common/core/page/TableDataInfo.java b/bashi-common/src/main/java/com/bashi/common/core/page/TableDataInfo.java new file mode 100644 index 0000000..4c380d7 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/page/TableDataInfo.java @@ -0,0 +1,60 @@ +package com.bashi.common.core.page; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; + +/** + * 表格分页数据对象 + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@ApiModel("分页响应对象") +public class TableDataInfo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 总记录数 + */ + @ApiModelProperty("总记录数") + private long total; + + /** + * 列表数据 + */ + @ApiModelProperty("列表数据") + private List rows; + + /** + * 消息状态码 + */ + @ApiModelProperty("消息状态码") + private int code; + + /** + * 消息内容 + */ + @ApiModelProperty("消息内容") + private String msg; + + /** + * 分页 + * + * @param list 列表数据 + * @param total 总记录数 + */ + public TableDataInfo(List list, long total) { + this.rows = list; + this.total = total; + } + +} diff --git a/bashi-common/src/main/java/com/bashi/common/core/redis/RedisCache.java b/bashi-common/src/main/java/com/bashi/common/core/redis/RedisCache.java new file mode 100644 index 0000000..cea542c --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/core/redis/RedisCache.java @@ -0,0 +1,223 @@ +package com.bashi.common.core.redis; + +import com.google.common.collect.Lists; +import org.redisson.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * spring redis 工具类 + * + * @author shenxinquan + **/ +@SuppressWarnings(value = {"unchecked", "rawtypes"}) +@Component +public class RedisCache { + + @Autowired + private RedissonClient redissonClient; + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public void setCacheObject(final String key, final T value) { + redissonClient.getBucket(key).set(value); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + */ + public void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) { + RBucket result = redissonClient.getBucket(key); + result.set(value); + result.expire(timeout, timeUnit); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout) { + return expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @param unit 时间单位 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout, final TimeUnit unit) { + RBucket rBucket = redissonClient.getBucket(key); + return rBucket.expire(timeout, unit); + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public T getCacheObject(final String key) { + RBucket rBucket = redissonClient.getBucket(key); + return rBucket.get(); + } + + /** + * 删除单个对象 + * + * @param key + */ + public boolean deleteObject(final String key) { + return redissonClient.getBucket(key).delete(); + } + + /* */ + + /** + * 删除集合对象 + * + * @param collection 多个对象 + * @return + */ + public void deleteObject(final Collection collection) { + RBatch batch = redissonClient.createBatch(); + collection.forEach(t->{ + batch.getBucket(t.toString()).deleteAsync(); + }); + batch.execute(); + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + */ + public boolean setCacheList(final String key, final List dataList) { + RList rList = redissonClient.getList(key); + return rList.addAll(dataList); + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public List getCacheList(final String key) { + RList rList = redissonClient.getList(key); + return rList.readAll(); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + */ + public boolean setCacheSet(final String key, final Set dataSet) { + RSet rSet = redissonClient.getSet(key); + return rSet.addAll(dataSet); + } + + /** + * 获得缓存的set + * + * @param key + * @return + */ + public Set getCacheSet(final String key) { + RSet rSet = redissonClient.getSet(key); + return rSet.readAll(); + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + */ + public void setCacheMap(final String key, final Map dataMap) { + if (dataMap != null) { + RMap rMap = redissonClient.getMap(key); + rMap.putAll(dataMap); + } + } + + /** + * 获得缓存的Map + * + * @param key + * @return + */ + public Map getCacheMap(final String key) { + RMap rMap = redissonClient.getMap(key); + return rMap.getAll(rMap.keySet()); + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public void setCacheMapValue(final String key, final String hKey, final T value) { + RMap rMap = redissonClient.getMap(key); + rMap.put(hKey, value); + } + + /** + * 获取Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return Hash中的对象 + */ + public T getCacheMapValue(final String key, final String hKey) { + RMap rMap = redissonClient.getMap(key); + return rMap.get(hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * @return Hash对象集合 + */ + public List getMultiCacheMapValue(final String key, final Collection hKeys) { + RListMultimap rListMultimap = redissonClient.getListMultimap(key); + return rListMultimap.getAll(hKeys); + } + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * @return 对象列表 + */ + public Collection keys(final String pattern) { + Iterable iterable = redissonClient.getKeys().getKeysByPattern(pattern); + return Lists.newArrayList(iterable); + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/enums/BusinessStatus.java b/bashi-common/src/main/java/com/bashi/common/enums/BusinessStatus.java new file mode 100644 index 0000000..e4817e5 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/enums/BusinessStatus.java @@ -0,0 +1,20 @@ +package com.bashi.common.enums; + +/** + * 操作状态 + * + * @author duteliang + * + */ +public enum BusinessStatus +{ + /** + * 成功 + */ + SUCCESS, + + /** + * 失败 + */ + FAIL, +} diff --git a/bashi-common/src/main/java/com/bashi/common/enums/BusinessType.java b/bashi-common/src/main/java/com/bashi/common/enums/BusinessType.java new file mode 100644 index 0000000..c5086ff --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/enums/BusinessType.java @@ -0,0 +1,59 @@ +package com.bashi.common.enums; + +/** + * 业务操作类型 + * + * @author duteliang + */ +public enum BusinessType +{ + /** + * 其它 + */ + OTHER, + + /** + * 新增 + */ + INSERT, + + /** + * 修改 + */ + UPDATE, + + /** + * 删除 + */ + DELETE, + + /** + * 授权 + */ + GRANT, + + /** + * 导出 + */ + EXPORT, + + /** + * 导入 + */ + IMPORT, + + /** + * 强退 + */ + FORCE, + + /** + * 生成代码 + */ + GENCODE, + + /** + * 清空数据 + */ + CLEAN, +} diff --git a/bashi-common/src/main/java/com/bashi/common/enums/DataSourceType.java b/bashi-common/src/main/java/com/bashi/common/enums/DataSourceType.java new file mode 100644 index 0000000..bcac247 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/enums/DataSourceType.java @@ -0,0 +1,25 @@ +package com.bashi.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据源 + * + * @author Lion Li + */ +@AllArgsConstructor +public enum DataSourceType { + /** + * 主库 + */ + MASTER("master"), + + /** + * 从库 + */ + SLAVE("slave"); + + @Getter + private final String source; +} diff --git a/bashi-common/src/main/java/com/bashi/common/enums/HttpMethod.java b/bashi-common/src/main/java/com/bashi/common/enums/HttpMethod.java new file mode 100644 index 0000000..04738f4 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/enums/HttpMethod.java @@ -0,0 +1,36 @@ +package com.bashi.common.enums; + +import java.util.HashMap; +import java.util.Map; +import org.springframework.lang.Nullable; + +/** + * 请求方式 + * + * @author duteliang + */ +public enum HttpMethod +{ + GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; + + private static final Map mappings = new HashMap<>(16); + + static + { + for (HttpMethod httpMethod : values()) + { + mappings.put(httpMethod.name(), httpMethod); + } + } + + @Nullable + public static HttpMethod resolve(@Nullable String method) + { + return (method != null ? mappings.get(method) : null); + } + + public boolean matches(String method) + { + return (this == resolve(method)); + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/enums/OperatorType.java b/bashi-common/src/main/java/com/bashi/common/enums/OperatorType.java new file mode 100644 index 0000000..c9b2436 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/enums/OperatorType.java @@ -0,0 +1,24 @@ +package com.bashi.common.enums; + +/** + * 操作人类别 + * + * @author duteliang + */ +public enum OperatorType +{ + /** + * 其它 + */ + OTHER, + + /** + * 后台用户 + */ + MANAGE, + + /** + * 手机端用户 + */ + MOBILE +} diff --git a/bashi-common/src/main/java/com/bashi/common/enums/UserStatus.java b/bashi-common/src/main/java/com/bashi/common/enums/UserStatus.java new file mode 100644 index 0000000..c9839d2 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/enums/UserStatus.java @@ -0,0 +1,30 @@ +package com.bashi.common.enums; + +/** + * 用户状态 + * + * @author duteliang + */ +public enum UserStatus +{ + OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除"); + + private final String code; + private final String info; + + UserStatus(String code, String info) + { + this.code = code; + this.info = info; + } + + public String getCode() + { + return code; + } + + public String getInfo() + { + return info; + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/exception/BaseException.java b/bashi-common/src/main/java/com/bashi/common/exception/BaseException.java new file mode 100644 index 0000000..1c12498 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/exception/BaseException.java @@ -0,0 +1,97 @@ +package com.bashi.common.exception; + +import cn.hutool.core.lang.Validator; +import com.bashi.common.utils.MessageUtils; + +/** + * 基础异常 + * + * @author duteliang + */ +public class BaseException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 所属模块 + */ + private String module; + + /** + * 错误码 + */ + private String code; + + /** + * 错误码对应的参数 + */ + private Object[] args; + + /** + * 错误消息 + */ + private String defaultMessage; + + public BaseException(String module, String code, Object[] args, String defaultMessage) + { + this.module = module; + this.code = code; + this.args = args; + this.defaultMessage = defaultMessage; + } + + public BaseException(String module, String code, Object[] args) + { + this(module, code, args, null); + } + + public BaseException(String module, String defaultMessage) + { + this(module, null, null, defaultMessage); + } + + public BaseException(String code, Object[] args) + { + this(null, code, args, null); + } + + public BaseException(String defaultMessage) + { + this(null, null, null, defaultMessage); + } + + @Override + public String getMessage() + { + String message = null; + if (!Validator.isEmpty(code)) + { + message = MessageUtils.message(code, args); + } + if (message == null) + { + message = defaultMessage; + } + return message; + } + + public String getModule() + { + return module; + } + + public String getCode() + { + return code; + } + + public Object[] getArgs() + { + return args; + } + + public String getDefaultMessage() + { + return defaultMessage; + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/exception/CustomException.java b/bashi-common/src/main/java/com/bashi/common/exception/CustomException.java new file mode 100644 index 0000000..db71731 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/exception/CustomException.java @@ -0,0 +1,45 @@ +package com.bashi.common.exception; + +import cn.hutool.http.HttpStatus; + +/** + * 自定义异常 + * + * @author duteliang + */ +public class CustomException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + private Integer code = HttpStatus.HTTP_BAD_REQUEST; + + private String message; + + public CustomException(String message) + { + this.message = message; + } + + public CustomException(String message, Integer code) + { + this.message = message; + this.code = code; + } + + public CustomException(String message, Throwable e) + { + super(message, e); + this.message = message; + } + + @Override + public String getMessage() + { + return message; + } + + public Integer getCode() + { + return code; + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/exception/DemoModeException.java b/bashi-common/src/main/java/com/bashi/common/exception/DemoModeException.java new file mode 100644 index 0000000..a533977 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/exception/DemoModeException.java @@ -0,0 +1,15 @@ +package com.bashi.common.exception; + +/** + * 演示模式异常 + * + * @author duteliang + */ +public class DemoModeException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public DemoModeException() + { + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/exception/UtilException.java b/bashi-common/src/main/java/com/bashi/common/exception/UtilException.java new file mode 100644 index 0000000..4814fa1 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/exception/UtilException.java @@ -0,0 +1,26 @@ +package com.bashi.common.exception; + +/** + * 工具类异常 + * + * @author duteliang + */ +public class UtilException extends RuntimeException +{ + private static final long serialVersionUID = 8247610319171014183L; + + public UtilException(Throwable e) + { + super(e.getMessage(), e); + } + + public UtilException(String message) + { + super(message); + } + + public UtilException(String message, Throwable throwable) + { + super(message, throwable); + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/exception/file/FileException.java b/bashi-common/src/main/java/com/bashi/common/exception/file/FileException.java new file mode 100644 index 0000000..479c8ef --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/exception/file/FileException.java @@ -0,0 +1,19 @@ +package com.bashi.common.exception.file; + +import com.bashi.common.exception.BaseException; + +/** + * 文件信息异常类 + * + * @author duteliang + */ +public class FileException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public FileException(String code, Object[] args) + { + super("file", code, args, null); + } + +} diff --git a/bashi-common/src/main/java/com/bashi/common/exception/file/FileNameLengthLimitExceededException.java b/bashi-common/src/main/java/com/bashi/common/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 0000000..2958da1 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,16 @@ +package com.bashi.common.exception.file; + +/** + * 文件名称超长限制异常类 + * + * @author duteliang + */ +public class FileNameLengthLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException(int defaultFileNameLength) + { + super("upload.filename.exceed.length", new Object[] { defaultFileNameLength }); + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/exception/file/FileSizeLimitExceededException.java b/bashi-common/src/main/java/com/bashi/common/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 0000000..2e22798 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,16 @@ +package com.bashi.common.exception.file; + +/** + * 文件名大小限制异常类 + * + * @author duteliang + */ +public class FileSizeLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException(long defaultMaxSize) + { + super("upload.exceed.maxSize", new Object[] { defaultMaxSize }); + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/exception/file/InvalidExtensionException.java b/bashi-common/src/main/java/com/bashi/common/exception/file/InvalidExtensionException.java new file mode 100644 index 0000000..1867e4c --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/exception/file/InvalidExtensionException.java @@ -0,0 +1,81 @@ +package com.bashi.common.exception.file; + +import java.util.Arrays; +import org.apache.commons.fileupload.FileUploadException; + +/** + * 文件上传 误异常类 + * + * @author duteliang + */ +public class InvalidExtensionException extends FileUploadException +{ + private static final long serialVersionUID = 1L; + + private String[] allowedExtension; + private String extension; + private String filename; + + public InvalidExtensionException(String[] allowedExtension, String extension, String filename) + { + super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : [" + Arrays.toString(allowedExtension) + "]"); + this.allowedExtension = allowedExtension; + this.extension = extension; + this.filename = filename; + } + + public String[] getAllowedExtension() + { + return allowedExtension; + } + + public String getExtension() + { + return extension; + } + + public String getFilename() + { + return filename; + } + + public static class InvalidImageExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidFlashExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidMediaExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidVideoExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/exception/job/TaskException.java b/bashi-common/src/main/java/com/bashi/common/exception/job/TaskException.java new file mode 100644 index 0000000..2718f06 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/exception/job/TaskException.java @@ -0,0 +1,34 @@ +package com.bashi.common.exception.job; + +/** + * 计划策略异常 + * + * @author duteliang + */ +public class TaskException extends Exception +{ + private static final long serialVersionUID = 1L; + + private Code code; + + public TaskException(String msg, Code code) + { + this(msg, code, null); + } + + public TaskException(String msg, Code code, Exception nestedEx) + { + super(msg, nestedEx); + this.code = code; + } + + public Code getCode() + { + return code; + } + + public enum Code + { + TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/exception/user/CaptchaException.java b/bashi-common/src/main/java/com/bashi/common/exception/user/CaptchaException.java new file mode 100644 index 0000000..d5fe5ba --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/exception/user/CaptchaException.java @@ -0,0 +1,16 @@ +package com.bashi.common.exception.user; + +/** + * 验证码错误异常类 + * + * @author duteliang + */ +public class CaptchaException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaException() + { + super("user.jcaptcha.error", null); + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/exception/user/CaptchaExpireException.java b/bashi-common/src/main/java/com/bashi/common/exception/user/CaptchaExpireException.java new file mode 100644 index 0000000..eb81054 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/exception/user/CaptchaExpireException.java @@ -0,0 +1,16 @@ +package com.bashi.common.exception.user; + +/** + * 验证码失效异常类 + * + * @author duteliang + */ +public class CaptchaExpireException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaExpireException() + { + super("user.jcaptcha.expire", null); + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/exception/user/UserException.java b/bashi-common/src/main/java/com/bashi/common/exception/user/UserException.java new file mode 100644 index 0000000..94ba5f7 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/exception/user/UserException.java @@ -0,0 +1,18 @@ +package com.bashi.common.exception.user; + +import com.bashi.common.exception.BaseException; + +/** + * 用户信息异常类 + * + * @author duteliang + */ +public class UserException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public UserException(String code, Object[] args) + { + super("user", code, args, null); + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/exception/user/UserPasswordNotMatchException.java b/bashi-common/src/main/java/com/bashi/common/exception/user/UserPasswordNotMatchException.java new file mode 100644 index 0000000..040134d --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/exception/user/UserPasswordNotMatchException.java @@ -0,0 +1,16 @@ +package com.bashi.common.exception.user; + +/** + * 用户密码不正确或不符合规范异常类 + * + * @author duteliang + */ +public class UserPasswordNotMatchException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserPasswordNotMatchException() + { + super("user.password.not.match", null); + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/filter/RepeatableFilter.java b/bashi-common/src/main/java/com/bashi/common/filter/RepeatableFilter.java new file mode 100644 index 0000000..4bad342 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/filter/RepeatableFilter.java @@ -0,0 +1,48 @@ +package com.bashi.common.filter; + +import cn.hutool.core.util.StrUtil; +import org.springframework.http.MediaType; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +/** + * Repeatable 过滤器 + * + * @author duteliang + */ +public class RepeatableFilter implements Filter +{ + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + ServletRequest requestWrapper = null; + if (request instanceof HttpServletRequest + && StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) + { + requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response); + } + if (null == requestWrapper) + { + chain.doFilter(request, response); + } + else + { + chain.doFilter(requestWrapper, response); + } + } + + @Override + public void destroy() + { + + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/filter/RepeatedlyRequestWrapper.java b/bashi-common/src/main/java/com/bashi/common/filter/RepeatedlyRequestWrapper.java new file mode 100644 index 0000000..495a3f1 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/filter/RepeatedlyRequestWrapper.java @@ -0,0 +1,77 @@ +package com.bashi.common.filter; + +import cn.hutool.core.io.IoUtil; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +/** + * 构建可重复读取inputStream的request + * + * @author duteliang + */ +public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper +{ + private final byte[] body; + + public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException + { + super(request); + request.setCharacterEncoding("UTF-8"); + response.setCharacterEncoding("UTF-8"); + + body = IoUtil.readUtf8(request.getInputStream()).getBytes(StandardCharsets.UTF_8); + } + + @Override + public BufferedReader getReader() throws IOException + { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() + { + @Override + public int read() throws IOException + { + return bais.read(); + } + + @Override + public int available() throws IOException + { + return body.length; + } + + @Override + public boolean isFinished() + { + return false; + } + + @Override + public boolean isReady() + { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) + { + + } + }; + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/filter/XssFilter.java b/bashi-common/src/main/java/com/bashi/common/filter/XssFilter.java new file mode 100644 index 0000000..d612f98 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/filter/XssFilter.java @@ -0,0 +1,93 @@ +package com.bashi.common.filter; + +import cn.hutool.core.util.StrUtil; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 防止XSS攻击的过滤器 + * + * @author duteliang + */ +public class XssFilter implements Filter +{ + /** + * 排除链接 + */ + public List excludes = new ArrayList<>(); + + /** + * xss过滤开关 + */ + public boolean enabled = false; + + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + String tempExcludes = filterConfig.getInitParameter("excludes"); + String tempEnabled = filterConfig.getInitParameter("enabled"); + if (StrUtil.isNotEmpty(tempExcludes)) + { + String[] url = tempExcludes.split(","); + for (int i = 0; url != null && i < url.length; i++) + { + excludes.add(url[i]); + } + } + if (StrUtil.isNotEmpty(tempEnabled)) + { + enabled = Boolean.valueOf(tempEnabled); + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + if (handleExcludeURL(req, resp)) + { + chain.doFilter(request, response); + return; + } + XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); + chain.doFilter(xssRequest, response); + } + + private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) + { + if (!enabled) + { + return true; + } + if (excludes == null || excludes.isEmpty()) + { + return false; + } + String url = request.getServletPath(); + for (String pattern : excludes) + { + Pattern p = Pattern.compile("^" + pattern); + Matcher m = p.matcher(url); + if (m.find()) + { + return true; + } + } + return false; + } + + @Override + public void destroy() + { + + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/filter/XssHttpServletRequestWrapper.java b/bashi-common/src/main/java/com/bashi/common/filter/XssHttpServletRequestWrapper.java new file mode 100644 index 0000000..53d9253 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/filter/XssHttpServletRequestWrapper.java @@ -0,0 +1,108 @@ +package com.bashi.common.filter; + +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HtmlUtil; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +/** + * XSS过滤处理 + * + * @author duteliang + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper +{ + /** + * @param request + */ + public XssHttpServletRequestWrapper(HttpServletRequest request) + { + super(request); + } + + @Override + public String[] getParameterValues(String name) + { + String[] values = super.getParameterValues(name); + if (values != null) + { + int length = values.length; + String[] escapseValues = new String[length]; + for (int i = 0; i < length; i++) + { + // 防xss攻击和过滤前后空格 + escapseValues[i] = HtmlUtil.cleanHtmlTag(values[i]).trim(); + } + return escapseValues; + } + return super.getParameterValues(name); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + // 非json类型,直接返回 + if (!isJsonRequest()) + { + return super.getInputStream(); + } + + // 为空,直接返回 + String json = IoUtil.read(super.getInputStream(), StandardCharsets.UTF_8); + if (Validator.isEmpty(json)) + { + return super.getInputStream(); + } + + // xss过滤 + json = HtmlUtil.cleanHtmlTag(json).trim(); + + final ByteArrayInputStream bis = IoUtil.toStream(json, StandardCharsets.UTF_8); + return new ServletInputStream() + { + @Override + public boolean isFinished() + { + return true; + } + + @Override + public boolean isReady() + { + return true; + } + + @Override + public void setReadListener(ReadListener readListener) + { + } + + @Override + public int read() throws IOException + { + return bis.read(); + } + }; + } + + /** + * 是否是Json请求 + * + * @param request + */ + public boolean isJsonRequest() + { + String header = super.getHeader(HttpHeaders.CONTENT_TYPE); + return StrUtil.startWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/BeanContextUtils.java b/bashi-common/src/main/java/com/bashi/common/utils/BeanContextUtils.java new file mode 100644 index 0000000..034d2f6 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/BeanContextUtils.java @@ -0,0 +1,29 @@ +package com.bashi.common.utils; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * spring bean工具类 + */ +@Component +public class BeanContextUtils implements ApplicationContextAware { + + private static ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext app) { + if (applicationContext == null) { + applicationContext = app; + } + } + + public static T getBean(Class tClass) { + return applicationContext.getBean(tClass); + } + + public static Object getBean(String className) { + return applicationContext.getBean(className); + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/BeanConvertUtil.java b/bashi-common/src/main/java/com/bashi/common/utils/BeanConvertUtil.java new file mode 100644 index 0000000..1869a76 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/BeanConvertUtil.java @@ -0,0 +1,102 @@ +package com.bashi.common.utils; + +import com.google.common.collect.Lists; +import org.springframework.beans.BeanUtils; + +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; + +/** + * bean工具 + * @Author: 004795 + * @Date: 2022/5/24 11:11 + */ +public class BeanConvertUtil extends BeanUtils { + + private BeanConvertUtil(){} + + @FunctionalInterface + public interface CallBack { + /** + * 回调方法 + * + * @param s 源对象 + * @param t 目标对象 + */ + void callBack(S s, T t); + } + + /** + * 转换list 对象 + * + * @param sources 源对象 + * @param targetSupplier 目标对象供应方 + * @param callBack 回调方法 + * @param 源对象类型 + * @param 目标对象类型 + * @Return 转换对象 + */ + public static List convertListTo(List sources, Supplier targetSupplier, CallBack callBack) { + if (null == sources || null == targetSupplier) { + return Collections.emptyList(); + } + List list = Lists.newArrayListWithCapacity(sources.size()); + for (S source : sources) { + T target = targetSupplier.get(); + copyProperties(source, target); + if (callBack != null) { + callBack.callBack(source, target); + } + list.add(target); + } + return list; + } + + /** + * 转换list 对象 + * + * @param sources 源对象 + * @param targetSupplier 目标对象供应方 + * @param 源对象类型 + * @param 目标对象类型 + */ + public static List convertListTo(List sources, Supplier targetSupplier) { + return convertListTo(sources, targetSupplier, null); + } + + /** + * 转换对象 + * + * @param source 源对象 + * @param targetSupplier 目标对象供应方 + * @param 源对象类型 + * @param 目标对象类型 + * @return 目标对象 + */ + public static T convertTo(S source, Supplier targetSupplier) { + return convertTo(source, targetSupplier, null); + } + + /** + * 转换对象 + * + * @param source 源对象 + * @param targetSupplier 目标对象供应方 + * @param callBack 回调方法 + * @param 源对象类型 + * @param 目标对象类型 + * @return 目标对象 + */ + public static T convertTo(S source, Supplier targetSupplier, CallBack callBack) { + if (null == source || null == targetSupplier) { + return null; + } + T target = targetSupplier.get(); + copyProperties(source, target); + if (callBack != null) { + callBack.callBack(source, target); + } + return target; + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/DateUtils.java b/bashi-common/src/main/java/com/bashi/common/utils/DateUtils.java new file mode 100644 index 0000000..d203417 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/DateUtils.java @@ -0,0 +1,155 @@ +package com.bashi.common.utils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import org.apache.commons.lang3.time.DateFormatUtils; + +/** + * 时间工具类 + * + * @author duteliang + */ +public class DateUtils extends org.apache.commons.lang3.time.DateUtils +{ + public static String YYYY = "yyyy"; + + public static String YYYY_MM = "yyyy-MM"; + + public static String YYYY_MM_DD = "yyyy-MM-dd"; + + public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static String[] parsePatterns = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate() + { + return new Date(); + } + + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate() + { + return dateTimeNow(YYYY_MM_DD); + } + + public static final String getTime() + { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static final String dateTimeNow() + { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static final String dateTimeNow(final String format) + { + return parseDateToStr(format, new Date()); + } + + public static final String dateTime(final Date date) + { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static final String parseDateToStr(final String format, final Date date) + { + return new SimpleDateFormat(format).format(date); + } + + public static final Date dateTime(final String format, final String ts) + { + try + { + return new SimpleDateFormat(format).parse(ts); + } + catch (ParseException e) + { + throw new RuntimeException(e); + } + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static final String datePath() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static final String dateTime() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) + { + if (str == null) + { + return null; + } + try + { + return parseDate(str.toString(), parsePatterns); + } + catch (ParseException e) + { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() + { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算两个时间差 + */ + public static String getDatePoor(Date endDate, Date nowDate) + { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - nowDate.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/DictUtils.java b/bashi-common/src/main/java/com/bashi/common/utils/DictUtils.java new file mode 100644 index 0000000..acfac19 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/DictUtils.java @@ -0,0 +1,187 @@ +package com.bashi.common.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import com.bashi.common.constant.Constants; +import com.bashi.common.core.domain.entity.SysDictData; +import com.bashi.common.core.redis.RedisCache; +import com.bashi.common.utils.spring.SpringUtils; + +import java.util.Collection; +import java.util.List; + +/** + * 字典工具类 + * + * @author duteliang + */ +public class DictUtils +{ + /** + * 分隔符 + */ + public static final String SEPARATOR = ","; + + /** + * 设置字典缓存 + * + * @param key 参数键 + * @param dictDatas 字典数据列表 + */ + public static void setDictCache(String key, List dictDatas) + { + SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas); + } + + /** + * 获取字典缓存 + * + * @param key 参数键 + * @return dictDatas 字典数据列表 + */ + public static List getDictCache(String key) + { + Object cacheObj = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key)); + if (Validator.isNotNull(cacheObj)) + { + List dictDatas = (List)cacheObj; + return dictDatas; + } + return null; + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @return 字典标签 + */ + public static String getDictLabel(String dictType, String dictValue) + { + return getDictLabel(dictType, dictValue, SEPARATOR); + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @return 字典值 + */ + public static String getDictValue(String dictType, String dictLabel) + { + return getDictValue(dictType, dictLabel, SEPARATOR); + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @param separator 分隔符 + * @return 字典标签 + */ + public static String getDictLabel(String dictType, String dictValue, String separator) + { + StringBuilder propertyString = new StringBuilder(); + List datas = getDictCache(dictType); + + if (StrUtil.containsAny(separator, dictValue) && CollUtil.isNotEmpty(datas)) + { + for (SysDictData dict : datas) + { + for (String value : dictValue.split(separator)) + { + if (value.equals(dict.getDictValue())) + { + propertyString.append(dict.getDictLabel() + separator); + break; + } + } + } + } + else + { + for (SysDictData dict : datas) + { + if (dictValue.equals(dict.getDictValue())) + { + return dict.getDictLabel(); + } + } + } + return StrUtil.strip(propertyString.toString(), null, separator); + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @param separator 分隔符 + * @return 字典值 + */ + public static String getDictValue(String dictType, String dictLabel, String separator) + { + StringBuilder propertyString = new StringBuilder(); + List datas = getDictCache(dictType); + + if (StrUtil.containsAny(separator, dictLabel) && CollUtil.isNotEmpty(datas)) + { + for (SysDictData dict : datas) + { + for (String label : dictLabel.split(separator)) + { + if (label.equals(dict.getDictLabel())) + { + propertyString.append(dict.getDictValue() + separator); + break; + } + } + } + } + else + { + for (SysDictData dict : datas) + { + if (dictLabel.equals(dict.getDictLabel())) + { + return dict.getDictValue(); + } + } + } + return StrUtil.strip(propertyString.toString(), null, separator); + } + + /** + * 删除指定字典缓存 + * + * @param key 字典键 + */ + public static void removeDictCache(String key) + { + SpringUtils.getBean(RedisCache.class).deleteObject(getCacheKey(key)); + } + + /** + * 清空字典缓存 + */ + public static void clearDictCache() + { + Collection keys = SpringUtils.getBean(RedisCache.class).keys(Constants.SYS_DICT_KEY + "*"); + SpringUtils.getBean(RedisCache.class).deleteObject(keys); + } + + /** + * 设置cache key + * + * @param configKey 参数键 + * @return 缓存键key + */ + public static String getCacheKey(String configKey) + { + return Constants.SYS_DICT_KEY + configKey; + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/JsonUtils.java b/bashi-common/src/main/java/com/bashi/common/utils/JsonUtils.java new file mode 100644 index 0000000..64f9ab5 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/JsonUtils.java @@ -0,0 +1,101 @@ +package com.bashi.common.utils; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * JSON 工具类 + * + * @author 芋道源码 + */ +public class JsonUtils { + + private static ObjectMapper objectMapper = new ObjectMapper(); + + /** + * 初始化 objectMapper 属性 + *

+ * 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean + * + * @param objectMapper ObjectMapper 对象 + */ + public static void init(ObjectMapper objectMapper) { + JsonUtils.objectMapper = objectMapper; + } + + public static String toJsonString(Object object) { + if (Validator.isEmpty(object)) { + return null; + } + try { + return objectMapper.writeValueAsString(object); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + public static T parseObject(String text, Class clazz) { + if (StrUtil.isEmpty(text)) { + return null; + } + try { + return objectMapper.readValue(text, clazz); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static T parseObject(byte[] bytes, Class clazz) { + if (ArrayUtil.isEmpty(bytes)) { + return null; + } + try { + return objectMapper.readValue(bytes, clazz); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static T parseObject(String text, TypeReference typeReference) { + if (StrUtil.isBlank(text)) { + return null; + } + try { + return objectMapper.readValue(text, typeReference); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static Map parseMap(String text) { + if (StrUtil.isBlank(text)) { + return null; + } + try { + return objectMapper.readValue(text, new TypeReference>() {}); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static List parseArray(String text, Class clazz) { + if (StrUtil.isEmpty(text)) { + return new ArrayList<>(); + } + try { + return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/MessageUtils.java b/bashi-common/src/main/java/com/bashi/common/utils/MessageUtils.java new file mode 100644 index 0000000..76afdbc --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/MessageUtils.java @@ -0,0 +1,26 @@ +package com.bashi.common.utils; + +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import com.bashi.common.utils.spring.SpringUtils; + +/** + * 获取i18n资源文件 + * + * @author duteliang + */ +public class MessageUtils +{ + /** + * 根据消息键和参数 获取消息 委托给spring messageSource + * + * @param code 消息键 + * @param args 参数 + * @return 获取国际化翻译值 + */ + public static String message(String code, Object... args) + { + MessageSource messageSource = SpringUtils.getBean(MessageSource.class); + return messageSource.getMessage(code, args, LocaleContextHolder.getLocale()); + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/PageUtils.java b/bashi-common/src/main/java/com/bashi/common/utils/PageUtils.java new file mode 100644 index 0000000..1edb748 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/PageUtils.java @@ -0,0 +1,155 @@ +package com.bashi.common.utils; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpStatus; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bashi.common.core.page.PagePlus; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.utils.sql.SqlUtil; +import org.springframework.beans.BeanUtils; +import org.springframework.context.annotation.Bean; + +import javax.naming.Name; +import java.util.List; +import java.util.function.Supplier; + +/** + * 分页工具 + * + * @author Lion Li + */ +public class PageUtils { + + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 当前记录起始索引 默认值 + */ + public static final int DEFAULT_PAGE_NUM = 1; + + /** + * 每页显示记录数 默认值 默认查全部 + */ + public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE; + + /** + * 构建 plus 分页对象 + * @param domain 实体 + * @param vo 实体 + * @return 分页对象 + */ + public static PagePlus buildPagePlus() { + Integer pageNum = ServletUtils.getParameterToInt(PAGE_NUM, DEFAULT_PAGE_NUM); + Integer pageSize = ServletUtils.getParameterToInt(PAGE_SIZE, DEFAULT_PAGE_SIZE); + String orderByColumn = ServletUtils.getParameter(ORDER_BY_COLUMN); + String isAsc = ServletUtils.getParameter(IS_ASC); + PagePlus page = new PagePlus<>(pageNum, pageSize); + if (StrUtil.isNotBlank(orderByColumn)) { + String orderBy = SqlUtil.escapeOrderBySql(orderByColumn); + if ("asc".equals(isAsc)) { + page.addOrder(OrderItem.asc(orderBy)); + } else if ("desc".equals(isAsc)) { + page.addOrder(OrderItem.desc(orderBy)); + } + } + return page; + } + + public static Page buildPage() { + return buildPage(null, null); + } + + /** + * 构建 MP 普通分页对象 + * @param domain 实体 + * @return 分页对象 + */ + public static Page buildPage(String defaultOrderByColumn, String defaultIsAsc) { + Integer pageNum = ServletUtils.getParameterToInt(PAGE_NUM, DEFAULT_PAGE_NUM); + Integer pageSize = ServletUtils.getParameterToInt(PAGE_SIZE, DEFAULT_PAGE_SIZE); + String orderByColumn = ServletUtils.getParameter(ORDER_BY_COLUMN, defaultOrderByColumn); + String isAsc = ServletUtils.getParameter(IS_ASC, defaultIsAsc); + // 兼容前端排序类型 + if ("ascending".equals(isAsc)) { + isAsc = "asc"; + } else if ("descending".equals(isAsc)) { + isAsc = "desc"; + } + Page page = new Page<>(pageNum, pageSize); + if (StrUtil.isNotBlank(orderByColumn)) { + String orderBy = SqlUtil.escapeOrderBySql(orderByColumn); + orderBy = StrUtil.toUnderlineCase(orderBy); + if ("asc".equals(isAsc)) { + page.addOrder(OrderItem.asc(orderBy)); + } else if ("desc".equals(isAsc)) { + page.addOrder(OrderItem.desc(orderBy)); + } + } + return page; + } + + public static TableDataInfo buildDataInfo(PagePlus page) { + TableDataInfo rspData = new TableDataInfo<>(); + rspData.setCode(HttpStatus.HTTP_OK); + rspData.setMsg("查询成功"); + rspData.setRows(page.getRecordsVo()); + rspData.setTotal(page.getTotal()); + return rspData; + } + + public static TableDataInfo buildDataInfo(IPage page) { + TableDataInfo rspData = new TableDataInfo<>(); + rspData.setCode(HttpStatus.HTTP_OK); + rspData.setMsg("查询成功"); + rspData.setRows(page.getRecords()); + rspData.setTotal(page.getTotal()); + return rspData; + } + + public static IPage transPage(IPage page, Supplier targetSupplier) { + IPage rspData = new Page<>(page.getCurrent(),page.getSize(),page.getTotal()); + List listTo = BeanConvertUtil.convertListTo(page.getRecords(), targetSupplier); + rspData.setRecords(listTo); + return rspData; + } + + public static TableDataInfo buildDataInfo(List list) { + TableDataInfo rspData = new TableDataInfo<>(); + rspData.setCode(HttpStatus.HTTP_OK); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal(list.size()); + return rspData; + } + + public static TableDataInfo buildDataInfo(IPage page, Supplier targetSupplier) { + TableDataInfo rspData = new TableDataInfo<>(); + rspData.setCode(HttpStatus.HTTP_OK); + rspData.setMsg("查询成功"); + List listTo = BeanConvertUtil.convertListTo(page.getRecords(), targetSupplier); + rspData.setRows(listTo); + rspData.setTotal(page.getTotal()); + return rspData; + } + +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/SecurityUtils.java b/bashi-common/src/main/java/com/bashi/common/utils/SecurityUtils.java new file mode 100644 index 0000000..7692b0b --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/SecurityUtils.java @@ -0,0 +1,96 @@ +package com.bashi.common.utils; + +import cn.hutool.http.HttpStatus; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import com.bashi.common.core.domain.model.LoginUser; +import com.bashi.common.exception.CustomException; + +/** + * 安全服务工具类 + * + * @author duteliang + */ +public class SecurityUtils { + /** + * 获取用户账户 + **/ + public static String getUsername() { + try { + return getLoginUser().getUsername(); + } catch (Exception e) { + throw new CustomException("获取用户账户异常", HttpStatus.HTTP_UNAUTHORIZED); + } + } + + /** + * 获取用户账户 + **/ + public static Long getUserId() { + try { + return getLoginUser().getUser().getUserId(); + } catch (Exception e) { + throw new CustomException("获取用户账户异常", HttpStatus.HTTP_UNAUTHORIZED); + } + } + + /** + * 获取用户 + **/ + public static LoginUser getLoginUser() { + try { + return (LoginUser) getAuthentication().getPrincipal(); + } catch (Exception e) { + throw new CustomException("获取用户信息异常", HttpStatus.HTTP_UNAUTHORIZED); + } + } + + public static LoginUser getLoginUserNoException() { + try { + return (LoginUser) getAuthentication().getPrincipal(); + } catch (Exception e) { + return null; + } + } + + /** + * 获取Authentication + */ + public static Authentication getAuthentication() { + return SecurityContextHolder.getContext().getAuthentication(); + } + + /** + * 生成BCryptPasswordEncoder密码 + * + * @param password 密码 + * @return 加密字符串 + */ + public static String encryptPassword(String password) { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } + + /** + * 判断密码是否相同 + * + * @param rawPassword 真实密码 + * @param encodedPassword 加密后字符 + * @return 结果 + */ + public static boolean matchesPassword(String rawPassword, String encodedPassword) { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.matches(rawPassword, encodedPassword); + } + + /** + * 是否为管理员 + * + * @param userId 用户ID + * @return 结果 + */ + public static boolean isAdmin(Long userId) { + return userId != null && 1L == userId; + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/ServletUtils.java b/bashi-common/src/main/java/com/bashi/common/utils/ServletUtils.java new file mode 100644 index 0000000..d68279b --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/ServletUtils.java @@ -0,0 +1,130 @@ +package com.bashi.common.utils; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.servlet.ServletUtil; +import cn.hutool.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.context.request.RequestAttributes; +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 javax.servlet.http.HttpSession; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +/** + * 客户端工具类 + * + * @author duteliang + */ +public class ServletUtils extends ServletUtil { + /** + * 获取String参数 + */ + public static String getParameter(String name) { + return getRequest().getParameter(name); + } + + /** + * 获取String参数 + */ + public static String getParameter(String name, String defaultValue) { + return Convert.toStr(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name) { + return Convert.toInt(getRequest().getParameter(name)); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name, Integer defaultValue) { + return Convert.toInt(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest() { + return getRequestAttributes().getRequest(); + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse() { + return getRequestAttributes().getResponse(); + } + + /** + * 获取session + */ + public static HttpSession getSession() { + return getRequest().getSession(); + } + + public static ServletRequestAttributes getRequestAttributes() { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } + + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + * @return null + */ + public static String renderString(HttpServletResponse response, String string) { + try { + response.setStatus(HttpStatus.HTTP_OK); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setCharacterEncoding(StandardCharsets.UTF_8.toString()); + response.getWriter().print(string); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 是否是Ajax异步请求 + * + * @param request + */ + public static boolean isAjaxRequest(HttpServletRequest request) { + + String accept = request.getHeader("accept"); + if (accept != null && accept.indexOf("application/json") != -1) { + return true; + } + + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1) { + return true; + } + + String uri = request.getRequestURI(); + if (StrUtil.equalsAnyIgnoreCase(uri, ".json", ".xml")) { + return true; + } + + String ajax = request.getParameter("__ajax"); + if (StrUtil.equalsAnyIgnoreCase(ajax, "json", "xml")) { + return true; + } + return false; + } + + public static String getClientIP() { + return getClientIP(getRequest()); + } + +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/Threads.java b/bashi-common/src/main/java/com/bashi/common/utils/Threads.java new file mode 100644 index 0000000..a2aa09e --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/Threads.java @@ -0,0 +1,99 @@ +package com.bashi.common.utils; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 线程相关工具类. + * + * @author duteliang + */ +public class Threads +{ + private static final Logger logger = LoggerFactory.getLogger(Threads.class); + + /** + * sleep等待,单位为毫秒 + */ + public static void sleep(long milliseconds) + { + try + { + Thread.sleep(milliseconds); + } + catch (InterruptedException e) + { + return; + } + } + + /** + * 停止线程池 + * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. + * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. + * 如果仍人超時,則強制退出. + * 另对在shutdown时线程本身被调用中断做了处理. + */ + public static void shutdownAndAwaitTermination(ExecutorService pool) + { + if (pool != null && !pool.isShutdown()) + { + pool.shutdown(); + try + { + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) + { + pool.shutdownNow(); + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) + { + logger.info("Pool did not terminate"); + } + } + } + catch (InterruptedException ie) + { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } + + /** + * 打印线程异常信息 + */ + public static void printException(Runnable r, Throwable t) + { + if (t == null && r instanceof Future) + { + try + { + Future future = (Future) r; + if (future.isDone()) + { + future.get(); + } + } + catch (CancellationException ce) + { + t = ce; + } + catch (ExecutionException ee) + { + t = ee.getCause(); + } + catch (InterruptedException ie) + { + Thread.currentThread().interrupt(); + } + } + if (t != null) + { + logger.error(t.getMessage(), t); + } + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/file/FileTypeUtils.java b/bashi-common/src/main/java/com/bashi/common/utils/file/FileTypeUtils.java new file mode 100644 index 0000000..8da3452 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/file/FileTypeUtils.java @@ -0,0 +1,76 @@ +package com.bashi.common.utils.file; + +import java.io.File; +import org.apache.commons.lang3.StringUtils; + +/** + * 文件类型工具类 + * + * @author duteliang + */ +public class FileTypeUtils +{ + /** + * 获取文件类型 + *

+ * 例如: duteliang.txt, 返回: txt + * + * @param file 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(File file) + { + if (null == file) + { + return StringUtils.EMPTY; + } + return getFileType(file.getName()); + } + + /** + * 获取文件类型 + *

+ * 例如: duteliang.txt, 返回: txt + * + * @param fileName 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(String fileName) + { + int separatorIndex = fileName.lastIndexOf("."); + if (separatorIndex < 0) + { + return ""; + } + return fileName.substring(separatorIndex + 1).toLowerCase(); + } + + /** + * 获取文件类型 + * + * @param photoByte 文件字节码 + * @return 后缀(不含".") + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "JPG"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "GIF"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "JPG"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "BMP"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "PNG"; + } + return strFileExtendName; + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/file/FileUploadUtils.java b/bashi-common/src/main/java/com/bashi/common/utils/file/FileUploadUtils.java new file mode 100644 index 0000000..657c2c4 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/file/FileUploadUtils.java @@ -0,0 +1,239 @@ +package com.bashi.common.utils.file; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.bashi.common.config.BsConfig; +import com.bashi.common.constant.Constants; +import com.bashi.common.exception.file.FileNameLengthLimitExceededException; +import com.bashi.common.exception.file.FileSizeLimitExceededException; +import com.bashi.common.exception.file.InvalidExtensionException; +import com.bashi.common.utils.DateUtils; +import org.apache.commons.io.FilenameUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; + +/** + * 文件上传工具类 + * + * @author duteliang + */ +public class FileUploadUtils +{ + /** + * 默认大小 50M + */ + public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024; + + /** + * 默认的文件名最大长度 100 + */ + public static final int DEFAULT_FILE_NAME_LENGTH = 100; + + /** + * 默认上传的地址 + */ + private static String defaultBaseDir = BsConfig.getProfile(); + + public static void setDefaultBaseDir(String defaultBaseDir) + { + FileUploadUtils.defaultBaseDir = defaultBaseDir; + } + + public static String getDefaultBaseDir() + { + return defaultBaseDir; + } + + /** + * 以默认配置进行文件上传 + * + * @param file 上传的文件 + * @return 文件名称 + * @throws Exception + */ + public static final String upload(MultipartFile file) throws IOException + { + try + { + return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 根据文件路径上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @return 文件名称 + * @throws IOException + */ + public static final String upload(String baseDir, MultipartFile file) throws IOException + { + try + { + return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 文件上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @param allowedExtension 上传文件类型 + * @return 返回上传成功的文件名 + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws FileNameLengthLimitExceededException 文件名太长 + * @throws IOException 比如读写文件出错时 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException + { + int fileNamelength = file.getOriginalFilename().length(); + if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) + { + throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); + } + + assertAllowed(file, allowedExtension); + + String fileName = extractFilename(file); + + File desc = getAbsoluteFile(baseDir, fileName); + desc = FileUtil.touch(desc); + FileUtil.writeFromStream(file.getInputStream(), desc); + String pathFileName = getPathFileName(baseDir, fileName); + return pathFileName; + } + + /** + * 编码文件名 + */ + public static final String extractFilename(MultipartFile file) + { + String fileName = file.getOriginalFilename(); + String extension = getExtension(file); + fileName = DateUtils.datePath() + "/" + IdUtil.fastUUID() + "." + extension; + return fileName; + } + + private static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException + { + File desc = new File(uploadDir + File.separator + fileName); + + if (!desc.exists()) + { + if (!desc.getParentFile().exists()) + { + desc.getParentFile().mkdirs(); + } + } + return desc; + } + + private static final String getPathFileName(String uploadDir, String fileName) throws IOException + { + int dirLastIndex = BsConfig.getProfile().length() + 1; + String currentDir = StrUtil.subSuf(uploadDir, dirLastIndex); + String pathFileName = Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName; + return pathFileName; + } + + /** + * 文件大小校验 + * + * @param file 上传的文件 + * @return + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws InvalidExtensionException + */ + public static final void assertAllowed(MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, InvalidExtensionException + { + long size = file.getSize(); + if (DEFAULT_MAX_SIZE != -1 && size > DEFAULT_MAX_SIZE) + { + throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); + } + + String fileName = file.getOriginalFilename(); + String extension = getExtension(file); + if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) + { + if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) + { + throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) + { + throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) + { + throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) + { + throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, + fileName); + } + else + { + throw new InvalidExtensionException(allowedExtension, extension, fileName); + } + } + + } + + /** + * 判断MIME类型是否是允许的MIME类型 + * + * @param extension + * @param allowedExtension + * @return + */ + public static final boolean isAllowedExtension(String extension, String[] allowedExtension) + { + for (String str : allowedExtension) + { + if (str.equalsIgnoreCase(extension)) + { + return true; + } + } + return false; + } + + /** + * 获取文件名的后缀 + * + * @param file 表单文件 + * @return 后缀名 + */ + public static final String getExtension(MultipartFile file) + { + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + if (Validator.isEmpty(extension)) + { + extension = MimeTypeUtils.getExtension(file.getContentType()); + } + return extension; + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/file/FileUtils.java b/bashi-common/src/main/java/com/bashi/common/utils/file/FileUtils.java new file mode 100644 index 0000000..82b82e4 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/file/FileUtils.java @@ -0,0 +1,125 @@ +package com.bashi.common.utils.file; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +/** + * 文件处理工具类 + * + * @author duteliang + */ +public class FileUtils extends FileUtil +{ + public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; + + /** + * 文件名称验证 + * + * @param filename 文件名称 + * @return true 正常 false 非法 + */ + public static boolean isValidFilename(String filename) + { + return filename.matches(FILENAME_PATTERN); + } + + /** + * 检查文件是否可下载 + * + * @param resource 需要下载的文件 + * @return true 正常 false 非法 + */ + public static boolean checkAllowDownload(String resource) + { + // 禁止目录上跳级别 + if (StrUtil.contains(resource, "..")) + { + return false; + } + + // 检查允许下载的文件规则 + if (ArrayUtil.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) + { + return true; + } + + // 不在允许下载的文件规则 + return false; + } + + /** + * 下载文件名重新编码 + * + * @param request 请求对象 + * @param fileName 文件名 + * @return 编码后的文件名 + */ + public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException + { + final String agent = request.getHeader("USER-AGENT"); + String filename = fileName; + if (agent.contains("MSIE")) + { + // IE浏览器 + filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString()); + filename = filename.replace("+", " "); + } + else if (agent.contains("Firefox")) + { + // 火狐浏览器 + filename = new String(fileName.getBytes(), "ISO8859-1"); + } + else if (agent.contains("Chrome")) + { + // google浏览器 + filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString()); + } + else + { + // 其它浏览器 + filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString()); + } + return filename; + } + + /** + * 下载文件名重新编码 + * + * @param response 响应对象 + * @param realFileName 真实文件名 + * @return + */ + public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException + { + String percentEncodedFileName = percentEncode(realFileName); + + StringBuilder contentDispositionValue = new StringBuilder(); + contentDispositionValue.append("attachment; filename=") + .append(percentEncodedFileName) + .append(";") + .append("filename*=") + .append("utf-8''") + .append(percentEncodedFileName); + + response.setHeader("Content-disposition", contentDispositionValue.toString()); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * @return 百分号编码后的字符串 + */ + public static String percentEncode(String s) throws UnsupportedEncodingException + { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); + return encode.replaceAll("\\+", "%20"); + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/file/ImageUtils.java b/bashi-common/src/main/java/com/bashi/common/utils/file/ImageUtils.java new file mode 100644 index 0000000..79d7948 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/file/ImageUtils.java @@ -0,0 +1,102 @@ +package com.bashi.common.utils.file; + +import cn.hutool.core.util.StrUtil; +import com.bashi.common.config.BsConfig; +import com.bashi.common.constant.Constants; +import org.apache.poi.util.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.Arrays; + +/** + * 图片处理工具类 + * + * @author duteliang + */ +public class ImageUtils +{ + private static final Logger log = LoggerFactory.getLogger(ImageUtils.class); + + public static byte[] getImage(String imagePath) + { + InputStream is = getFile(imagePath); + try + { + return IOUtils.toByteArray(is); + } + catch (Exception e) + { + log.error("图片加载异常 {}", e); + return null; + } + finally + { + IOUtils.closeQuietly(is); + } + } + + public static InputStream getFile(String imagePath) + { + try + { + byte[] result = readFile(imagePath); + result = Arrays.copyOf(result, result.length); + return new ByteArrayInputStream(result); + } + catch (Exception e) + { + log.error("获取图片异常 {}", e); + } + return null; + } + + /** + * 读取文件为字节数据 + * + * @param key 地址 + * @return 字节数据 + */ + public static byte[] readFile(String url) + { + InputStream in = null; + ByteArrayOutputStream baos = null; + try + { + if (url.startsWith("http")) + { + // 网络地址 + URL urlObj = new URL(url); + URLConnection urlConnection = urlObj.openConnection(); + urlConnection.setConnectTimeout(30 * 1000); + urlConnection.setReadTimeout(60 * 1000); + urlConnection.setDoInput(true); + in = urlConnection.getInputStream(); + } + else + { + // 本机地址 + String localPath = BsConfig.getProfile(); + String downloadPath = localPath + StrUtil.subAfter(url, Constants.RESOURCE_PREFIX,false); + in = new FileInputStream(downloadPath); + } + return IOUtils.toByteArray(in); + } + catch (Exception e) + { + log.error("获取文件路径异常 {}", e); + return null; + } + finally + { + IOUtils.closeQuietly(in); + IOUtils.closeQuietly(baos); + } + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/file/MimeTypeUtils.java b/bashi-common/src/main/java/com/bashi/common/utils/file/MimeTypeUtils.java new file mode 100644 index 0000000..b5211c0 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/file/MimeTypeUtils.java @@ -0,0 +1,59 @@ +package com.bashi.common.utils.file; + +/** + * 媒体类型工具类 + * + * @author duteliang + */ +public class MimeTypeUtils +{ + public static final String IMAGE_PNG = "image/png"; + + public static final String IMAGE_JPG = "image/jpg"; + + public static final String IMAGE_JPEG = "image/jpeg"; + + public static final String IMAGE_BMP = "image/bmp"; + + public static final String IMAGE_GIF = "image/gif"; + + public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" }; + + public static final String[] FLASH_EXTENSION = { "swf", "flv" }; + + public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", + "asf", "rm", "rmvb" }; + + public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" }; + + public static final String[] DEFAULT_ALLOWED_EXTENSION = { + // 图片 + "bmp", "gif", "jpg", "jpeg", "png", + // word excel powerpoint + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", + // 压缩文件 + "rar", "zip", "gz", "bz2", + // 视频格式 + "mp4", "avi", "rmvb", + // pdf + "pdf" }; + + public static String getExtension(String prefix) + { + switch (prefix) + { + case IMAGE_PNG: + return "png"; + case IMAGE_JPG: + return "jpg"; + case IMAGE_JPEG: + return "jpeg"; + case IMAGE_BMP: + return "bmp"; + case IMAGE_GIF: + return "gif"; + default: + return ""; + } + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/ip/AddressUtils.java b/bashi-common/src/main/java/com/bashi/common/utils/ip/AddressUtils.java new file mode 100644 index 0000000..0779324 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/ip/AddressUtils.java @@ -0,0 +1,55 @@ +package com.bashi.common.utils.ip; + +import cn.hutool.core.net.NetUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HtmlUtil; +import cn.hutool.http.HttpUtil; +import com.bashi.common.config.BsConfig; +import com.bashi.common.constant.Constants; +import com.bashi.common.utils.JsonUtils; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; + +/** + * 获取地址类 + * + * @author duteliang + */ +@Slf4j +public class AddressUtils { + + // IP地址查询 + public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp"; + + // 未知地址 + public static final String UNKNOWN = "XX XX"; + + public static String getRealAddressByIP(String ip) { + String address = UNKNOWN; + // 内网不查询 + ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip); + if (NetUtil.isInnerIP(ip)) { + return "内网IP"; + } + if (BsConfig.isAddressEnabled()) { + try { + String rspStr = HttpUtil.createGet(IP_URL) + .body("ip=" + ip + "&json=true", Constants.GBK) + .execute() + .body(); + if (StrUtil.isEmpty(rspStr)) { + log.error("获取地理位置异常 {}", ip); + return UNKNOWN; + } + Map obj = JsonUtils.parseMap(rspStr); + String region = obj.get("pro"); + String city = obj.get("city"); + return String.format("%s %s", region, city); + } catch (Exception e) { + log.error("获取地理位置异常 {}", ip); + } + } + return address; + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/poi/ExcelUtil.java b/bashi-common/src/main/java/com/bashi/common/utils/poi/ExcelUtil.java new file mode 100644 index 0000000..eac8ce0 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/poi/ExcelUtil.java @@ -0,0 +1,1072 @@ +package com.bashi.common.utils.poi; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import com.bashi.common.annotation.Excel; +import com.bashi.common.annotation.Excel.ColumnType; +import com.bashi.common.annotation.Excel.Type; +import com.bashi.common.annotation.Excels; +import com.bashi.common.config.BsConfig; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.exception.CustomException; +import com.bashi.common.utils.DateUtils; +import com.bashi.common.utils.DictUtils; +import com.bashi.common.utils.file.FileTypeUtils; +import com.bashi.common.utils.file.ImageUtils; +import com.bashi.common.utils.reflect.ReflectUtils; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDataValidation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.lang.reflect.Field; +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Excel相关处理 + * + * @author duteliang + */ +public class ExcelUtil +{ + private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class); + + /** + * Excel sheet最大行数,默认65536 + */ + public static final int sheetSize = 65536; + + /** + * 工作表名称 + */ + private String sheetName; + + /** + * 导出类型(EXPORT:导出数据;IMPORT:导入模板) + */ + private Type type; + + /** + * 工作薄对象 + */ + private Workbook wb; + + /** + * 工作表对象 + */ + private Sheet sheet; + + /** + * 样式列表 + */ + private Map styles; + + /** + * 导入导出数据列表 + */ + private List list; + + /** + * 注解列表 + */ + private List fields; + + /** + * 最大高度 + */ + private short maxHeight; + + /** + * 统计列表 + */ + private Map statistics = new HashMap(); + + /** + * 数字格式 + */ + private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00"); + + /** + * 实体对象 + */ + public Class clazz; + + public ExcelUtil(Class clazz) + { + this.clazz = clazz; + } + + public void init(List list, String sheetName, Type type) + { + if (list == null) + { + list = new ArrayList(); + } + this.list = list; + this.sheetName = sheetName; + this.type = type; + createExcelField(); + createWorkbook(); + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(InputStream is) throws Exception + { + return importExcel(StrUtil.EMPTY, is); + } + + /** + * 对excel表单指定表格索引名转换成list + * + * @param sheetName 表格索引名 + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(String sheetName, InputStream is) throws Exception + { + this.type = Type.IMPORT; + this.wb = WorkbookFactory.create(is); + List list = new ArrayList(); + Sheet sheet = null; + if (Validator.isNotEmpty(sheetName)) + { + // 如果指定sheet名,则取指定sheet中的内容. + sheet = wb.getSheet(sheetName); + } + else + { + // 如果传入的sheet名不存在则默认指向第1个sheet. + sheet = wb.getSheetAt(0); + } + + if (sheet == null) + { + throw new IOException("文件sheet不存在"); + } + + int rows = sheet.getPhysicalNumberOfRows(); + + if (rows > 0) + { + // 定义一个map用于存放excel列的序号和field. + Map cellMap = new HashMap(); + // 获取表头 + Row heard = sheet.getRow(0); + for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) + { + Cell cell = heard.getCell(i); + if (Validator.isNotNull(cell)) + { + String value = this.getCellValue(heard, i).toString(); + cellMap.put(value, i); + } + else + { + cellMap.put(null, i); + } + } + // 有数据时才处理 得到类的所有field. + Field[] allFields = clazz.getDeclaredFields(); + // 定义一个map用于存放列的序号和field. + Map fieldsMap = new HashMap(); + for (int col = 0; col < allFields.length; col++) + { + Field field = allFields[col]; + Excel attr = field.getAnnotation(Excel.class); + if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) + { + // 设置类的私有字段属性可访问. + field.setAccessible(true); + Integer column = cellMap.get(attr.name()); + if (column != null) + { + fieldsMap.put(column, field); + } + } + } + for (int i = 1; i < rows; i++) + { + // 从第2行开始取数据,默认第一行是表头. + Row row = sheet.getRow(i); + if(row == null) + { + continue; + } + T entity = null; + for (Map.Entry entry : fieldsMap.entrySet()) + { + Object val = this.getCellValue(row, entry.getKey()); + + // 如果不存在实例则新建. + entity = (entity == null ? clazz.newInstance() : entity); + // 从map中得到对应列的field. + Field field = fieldsMap.get(entry.getKey()); + // 取得类型,并根据对象类型设置值. + Class fieldType = field.getType(); + if (String.class == fieldType) + { + String s = Convert.toStr(val); + if (StrUtil.endWith(s, ".0")) + { + val = StrUtil.subBefore(s, ".0",false); + } + else + { + String dateFormat = field.getAnnotation(Excel.class).dateFormat(); + if (Validator.isNotEmpty(dateFormat)) + { + val = DateUtils.parseDateToStr(dateFormat, (Date) val); + } + else + { + val = Convert.toStr(val); + } + } + } + else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && Validator.isNumber(Convert.toStr(val))) + { + val = Convert.toInt(val); + } + else if (Long.TYPE == fieldType || Long.class == fieldType) + { + val = Convert.toLong(val); + } + else if (Double.TYPE == fieldType || Double.class == fieldType) + { + val = Convert.toDouble(val); + } + else if (Float.TYPE == fieldType || Float.class == fieldType) + { + val = Convert.toFloat(val); + } + else if (BigDecimal.class == fieldType) + { + val = Convert.toBigDecimal(val); + } + else if (Date.class == fieldType) + { + if (val instanceof String) + { + val = DateUtils.parseDate(val); + } + else if (val instanceof Double) + { + val = DateUtil.getJavaDate((Double) val); + } + } + else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) + { + val = Convert.toBool(val, false); + } + if (Validator.isNotNull(fieldType)) + { + Excel attr = field.getAnnotation(Excel.class); + String propertyName = field.getName(); + if (Validator.isNotEmpty(attr.targetAttr())) + { + propertyName = field.getName() + "." + attr.targetAttr(); + } + else if (Validator.isNotEmpty(attr.readConverterExp())) + { + val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator()); + } + else if (Validator.isNotEmpty(attr.dictType())) + { + val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator()); + } + ReflectUtils.invokeSetter(entity, propertyName, val); + } + } + list.add(entity); + } + } + return list; + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @return 结果 + */ + public AjaxResult exportExcel(List list, String sheetName) + { + this.init(list, sheetName, Type.EXPORT); + return exportExcel(); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @return 结果 + */ + public AjaxResult importTemplateExcel(String sheetName) + { + this.init(null, sheetName, Type.IMPORT); + return exportExcel(); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public AjaxResult exportExcel() + { + OutputStream out = null; + try + { + // 取出一共有多少个sheet. + double sheetNo = Math.ceil(list.size() / sheetSize); + for (int index = 0; index <= sheetNo; index++) + { + createSheet(sheetNo, index); + + // 产生一行 + Row row = sheet.createRow(0); + int column = 0; + // 写入各个字段的列头名称 + for (Object[] os : fields) + { + Excel excel = (Excel) os[1]; + this.createCell(excel, row, column++); + } + if (Type.EXPORT.equals(type)) + { + fillExcelData(index, row); + addStatisticsRow(); + } + } + String filename = encodingFilename(sheetName); + out = new FileOutputStream(getAbsoluteFile(filename)); + wb.write(out); + return AjaxResult.success(filename); + } + catch (Exception e) + { + log.error("导出Excel异常{}", e.getMessage()); + throw new CustomException("导出Excel失败,请联系网站管理员!"); + } + finally + { + if (wb != null) + { + try + { + wb.close(); + } + catch (IOException e1) + { + e1.printStackTrace(); + } + } + if (out != null) + { + try + { + out.close(); + } + catch (IOException e1) + { + e1.printStackTrace(); + } + } + } + } + + /** + * 填充excel数据 + * + * @param index 序号 + * @param row 单元格行 + */ + public void fillExcelData(int index, Row row) + { + int startNo = index * sheetSize; + int endNo = Math.min(startNo + sheetSize, list.size()); + for (int i = startNo; i < endNo; i++) + { + row = sheet.createRow(i + 1 - startNo); + // 得到导出对象. + T vo = (T) list.get(i); + int column = 0; + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + // 设置实体类私有属性可访问 + field.setAccessible(true); + this.addCell(excel, row, vo, field, column++); + } + } + } + + /** + * 创建表格样式 + * + * @param wb 工作薄对象 + * @return 样式列表 + */ + private Map createStyles(Workbook wb) + { + // 写入各条记录,每条记录对应excel表中的一行 + Map styles = new HashMap(); + CellStyle style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + style.setFont(dataFont); + styles.put("data", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + Font headerFont = wb.createFont(); + headerFont.setFontName("Arial"); + headerFont.setFontHeightInPoints((short) 10); + headerFont.setBold(true); + headerFont.setColor(IndexedColors.WHITE.getIndex()); + style.setFont(headerFont); + styles.put("header", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font totalFont = wb.createFont(); + totalFont.setFontName("Arial"); + totalFont.setFontHeightInPoints((short) 10); + style.setFont(totalFont); + styles.put("total", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.LEFT); + styles.put("data1", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.CENTER); + styles.put("data2", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.RIGHT); + styles.put("data3", style); + + return styles; + } + + /** + * 创建单元格 + */ + public Cell createCell(Excel attr, Row row, int column) + { + // 创建列 + Cell cell = row.createCell(column); + // 写入列信息 + cell.setCellValue(attr.name()); + setDataValidation(attr, row, column); + cell.setCellStyle(styles.get("header")); + return cell; + } + + /** + * 设置单元格信息 + * + * @param value 单元格值 + * @param attr 注解相关 + * @param cell 单元格信息 + */ + public void setCellVo(Object value, Excel attr, Cell cell) + { + if (ColumnType.STRING == attr.cellType()) + { + cell.setCellValue(Validator.isNull(value) ? attr.defaultValue() : value + attr.suffix()); + } + else if (ColumnType.NUMERIC == attr.cellType()) + { + if (Validator.isNotNull(value)) + { + cell.setCellValue(StrUtil.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value)); + } + } + else if (ColumnType.IMAGE == attr.cellType()) + { + ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), + cell.getRow().getRowNum() + 1); + String imagePath = Convert.toStr(value); + if (Validator.isNotEmpty(imagePath)) + { + byte[] data = ImageUtils.getImage(imagePath); + getDrawingPatriarch(cell.getSheet()).createPicture(anchor, + cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); + } + } + } + + /** + * 获取画布 + */ + public static Drawing getDrawingPatriarch(Sheet sheet) + { + if (sheet.getDrawingPatriarch() == null) + { + sheet.createDrawingPatriarch(); + } + return sheet.getDrawingPatriarch(); + } + + /** + * 获取图片类型,设置图片插入类型 + */ + public int getImageType(byte[] value) + { + String type = FileTypeUtils.getFileExtendName(value); + if ("JPG".equalsIgnoreCase(type)) + { + return Workbook.PICTURE_TYPE_JPEG; + } + else if ("PNG".equalsIgnoreCase(type)) + { + return Workbook.PICTURE_TYPE_PNG; + } + return Workbook.PICTURE_TYPE_JPEG; + } + + /** + * 创建表格样式 + */ + public void setDataValidation(Excel attr, Row row, int column) + { + if (attr.name().indexOf("注:") >= 0) + { + sheet.setColumnWidth(column, 6000); + } + else + { + // 设置列宽 + sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); + } + // 如果设置了提示信息则鼠标放上去提示. + if (Validator.isNotEmpty(attr.prompt())) + { + // 这里默认设了2-101列提示. + setXSSFPrompt(sheet, "", attr.prompt(), 1, 100, column, column); + } + // 如果设置了combo属性则本列只能选择不能输入 + if (attr.combo().length > 0) + { + // 这里默认设了2-101列只能选择不能输入. + setXSSFValidation(sheet, attr.combo(), 1, 100, column, column); + } + } + + /** + * 添加单元格 + */ + public Cell addCell(Excel attr, Row row, T vo, Field field, int column) + { + Cell cell = null; + try + { + // 设置行高 + row.setHeight(maxHeight); + // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. + if (attr.isExport()) + { + // 创建cell + cell = row.createCell(column); + int align = attr.align().value(); + cell.setCellStyle(styles.get("data" + (align >= 1 && align <= 3 ? align : ""))); + + // 用于读取对象中的属性 + Object value = getTargetValue(vo, field, attr); + String dateFormat = attr.dateFormat(); + String readConverterExp = attr.readConverterExp(); + String separator = attr.separator(); + String dictType = attr.dictType(); + if (Validator.isNotEmpty(dateFormat) && Validator.isNotNull(value)) + { + cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) value)); + } + else if (Validator.isNotEmpty(readConverterExp) && Validator.isNotNull(value)) + { + cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator)); + } + else if (Validator.isNotEmpty(dictType) && Validator.isNotNull(value)) + { + cell.setCellValue(convertDictByExp(Convert.toStr(value), dictType, separator)); + } + else if (value instanceof BigDecimal && -1 != attr.scale()) + { + cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).toString()); + } + else + { + // 设置列类型 + setCellVo(value, attr, cell); + } + addStatisticsData(column, Convert.toStr(value), attr); + } + } + catch (Exception e) + { + log.error("导出Excel失败{}", e); + } + return cell; + } + + /** + * 设置 POI XSSFSheet 单元格提示 + * + * @param sheet 表单 + * @param promptTitle 提示标题 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setXSSFPrompt(Sheet sheet, String promptTitle, String promptContent, int firstRow, int endRow, + int firstCol, int endCol) + { + DataValidationHelper helper = sheet.getDataValidationHelper(); + DataValidationConstraint constraint = helper.createCustomConstraint("DD1"); + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + DataValidation dataValidation = helper.createValidation(constraint, regions); + dataValidation.createPromptBox(promptTitle, promptContent); + dataValidation.setShowPromptBox(true); + sheet.addValidationData(dataValidation); + } + + /** + * 设置某些列的值只能输入预制的数据,显示下拉框. + * + * @param sheet 要设置的sheet. + * @param textlist 下拉框显示的内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + * @return 设置好的sheet. + */ + public void setXSSFValidation(Sheet sheet, String[] textlist, int firstRow, int endRow, int firstCol, int endCol) + { + DataValidationHelper helper = sheet.getDataValidationHelper(); + // 加载下拉列表内容 + DataValidationConstraint constraint = helper.createExplicitListConstraint(textlist); + // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + // 数据有效性对象 + DataValidation dataValidation = helper.createValidation(constraint, regions); + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) + { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } + else + { + dataValidation.setSuppressDropDownArrow(false); + } + + sheet.addValidationData(dataValidation); + } + + /** + * 解析导出值 0=男,1=女,2=未知 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String convertByExp(String propertyValue, String converterExp, String separator) + { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) + { + String[] itemArray = item.split("="); + if (StrUtil.containsAny(separator, propertyValue)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[0].equals(value)) + { + propertyString.append(itemArray[1] + separator); + break; + } + } + } + else + { + if (itemArray[0].equals(propertyValue)) + { + return itemArray[1]; + } + } + } + return StrUtil.strip(propertyString.toString(), null,separator); + } + + /** + * 反向解析值 男=0,女=1,未知=2 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String reverseByExp(String propertyValue, String converterExp, String separator) + { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) + { + String[] itemArray = item.split("="); + if (StrUtil.containsAny(separator, propertyValue)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[1].equals(value)) + { + propertyString.append(itemArray[0] + separator); + break; + } + } + } + else + { + if (itemArray[1].equals(propertyValue)) + { + return itemArray[0]; + } + } + } + return StrUtil.strip(propertyString.toString(), null,separator); + } + + /** + * 解析字典值 + * + * @param dictValue 字典值 + * @param dictType 字典类型 + * @param separator 分隔符 + * @return 字典标签 + */ + public static String convertDictByExp(String dictValue, String dictType, String separator) + { + return DictUtils.getDictLabel(dictType, dictValue, separator); + } + + /** + * 反向解析值字典值 + * + * @param dictLabel 字典标签 + * @param dictType 字典类型 + * @param separator 分隔符 + * @return 字典值 + */ + public static String reverseDictByExp(String dictLabel, String dictType, String separator) + { + return DictUtils.getDictValue(dictType, dictLabel, separator); + } + + /** + * 合计统计信息 + */ + private void addStatisticsData(Integer index, String text, Excel entity) + { + if (entity != null && entity.isStatistics()) + { + Double temp = 0D; + if (!statistics.containsKey(index)) + { + statistics.put(index, temp); + } + try + { + temp = Double.valueOf(text); + } + catch (NumberFormatException e) + { + } + statistics.put(index, statistics.get(index) + temp); + } + } + + /** + * 创建统计行 + */ + public void addStatisticsRow() + { + if (statistics.size() > 0) + { + Cell cell = null; + Row row = sheet.createRow(sheet.getLastRowNum() + 1); + Set keys = statistics.keySet(); + cell = row.createCell(0); + cell.setCellStyle(styles.get("total")); + cell.setCellValue("合计"); + + for (Integer key : keys) + { + cell = row.createCell(key); + cell.setCellStyle(styles.get("total")); + cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key))); + } + statistics.clear(); + } + } + + /** + * 编码文件名 + */ + public String encodingFilename(String filename) + { + filename = UUID.randomUUID().toString() + "_" + filename + ".xlsx"; + return filename; + } + + /** + * 获取下载路径 + * + * @param filename 文件名称 + */ + public String getAbsoluteFile(String filename) + { + String downloadPath = BsConfig.getDownloadPath() + filename; + File desc = new File(downloadPath); + if (!desc.getParentFile().exists()) + { + desc.getParentFile().mkdirs(); + } + return downloadPath; + } + + /** + * 获取bean中的属性值 + * + * @param vo 实体对象 + * @param field 字段 + * @param excel 注解 + * @return 最终的属性值 + * @throws Exception + */ + private Object getTargetValue(T vo, Field field, Excel excel) throws Exception + { + Object o = field.get(vo); + if (Validator.isNotEmpty(excel.targetAttr())) + { + String target = excel.targetAttr(); + if (target.contains(".")) + { + String[] targets = target.split("[.]"); + for (String name : targets) + { + o = getValue(o, name); + } + } + else + { + o = getValue(o, target); + } + } + return o; + } + + /** + * 以类的属性的get方法方法形式获取值 + * + * @param o + * @param name + * @return value + * @throws Exception + */ + private Object getValue(Object o, String name) throws Exception + { + if (Validator.isNotNull(o) && Validator.isNotEmpty(name)) + { + Class clazz = o.getClass(); + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + o = field.get(o); + } + return o; + } + + /** + * 得到所有定义字段 + */ + private void createExcelField() + { + this.fields = new ArrayList(); + List tempFields = new ArrayList<>(); + tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); + tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); + for (Field field : tempFields) + { + // 单注解 + if (field.isAnnotationPresent(Excel.class)) + { + putToField(field, field.getAnnotation(Excel.class)); + } + + // 多注解 + if (field.isAnnotationPresent(Excels.class)) + { + Excels attrs = field.getAnnotation(Excels.class); + Excel[] excels = attrs.value(); + for (Excel excel : excels) + { + putToField(field, excel); + } + } + } + this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList()); + this.maxHeight = getRowHeight(); + } + + /** + * 根据注解获取最大行高 + */ + public short getRowHeight() + { + double maxHeight = 0; + for (Object[] os : this.fields) + { + Excel excel = (Excel) os[1]; + maxHeight = maxHeight > excel.height() ? maxHeight : excel.height(); + } + return (short) (maxHeight * 20); + } + + /** + * 放到字段集合中 + */ + private void putToField(Field field, Excel attr) + { + if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) + { + this.fields.add(new Object[] { field, attr }); + } + } + + /** + * 创建一个工作簿 + */ + public void createWorkbook() + { + this.wb = new SXSSFWorkbook(500); + } + + /** + * 创建工作表 + * + * @param sheetNo sheet数量 + * @param index 序号 + */ + public void createSheet(double sheetNo, int index) + { + this.sheet = wb.createSheet(); + this.styles = createStyles(wb); + // 设置工作表的名称. + if (sheetNo == 0) + { + wb.setSheetName(index, sheetName); + } + else + { + wb.setSheetName(index, sheetName + index); + } + } + + /** + * 获取单元格值 + * + * @param row 获取的行 + * @param column 获取单元格列号 + * @return 单元格值 + */ + public Object getCellValue(Row row, int column) + { + if (row == null) + { + return row; + } + Object val = ""; + try + { + Cell cell = row.getCell(column); + if (Validator.isNotNull(cell)) + { + if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA) + { + val = cell.getNumericCellValue(); + if (DateUtil.isCellDateFormatted(cell)) + { + val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换 + } + else + { + if ((Double) val % 1 != 0) + { + val = new BigDecimal(val.toString()); + } + else + { + val = new DecimalFormat("0").format(val); + } + } + } + else if (cell.getCellType() == CellType.STRING) + { + val = cell.getStringCellValue(); + } + else if (cell.getCellType() == CellType.BOOLEAN) + { + val = cell.getBooleanCellValue(); + } + else if (cell.getCellType() == CellType.ERROR) + { + val = cell.getErrorCellValue(); + } + + } + } + catch (Exception e) + { + return val; + } + return val; + } +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/reflect/ReflectUtils.java b/bashi-common/src/main/java/com/bashi/common/utils/reflect/ReflectUtils.java new file mode 100644 index 0000000..c686f8f --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/reflect/ReflectUtils.java @@ -0,0 +1,54 @@ +package com.bashi.common.utils.reflect; + +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; + +import java.lang.reflect.Method; +import java.util.List; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author Lion Li + */ +@SuppressWarnings("rawtypes") +public class ReflectUtils extends ReflectUtil { + + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + @SuppressWarnings("unchecked") + public static E invokeGetter(Object obj, String propertyName) { + Object object = obj; + for (String name : StrUtil.split(propertyName, ".")) { + String getterMethodName = GETTER_PREFIX + StrUtil.upperFirst(name); + object = invoke(object, getterMethodName); + } + return (E) object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter(Object obj, String propertyName, E value) { + Object object = obj; + List names = StrUtil.split(propertyName, "."); + for (int i = 0; i < names.size(); i++) { + if (i < names.size() - 1) { + String getterMethodName = GETTER_PREFIX + StrUtil.upperFirst(names.get(i)); + object = invoke(object, getterMethodName); + } else { + String setterMethodName = SETTER_PREFIX + StrUtil.upperFirst(names.get(i)); + Method method = getMethodByName(object.getClass(), setterMethodName); + invoke(object, method, value); + } + } + } + +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/spring/SpringUtils.java b/bashi-common/src/main/java/com/bashi/common/utils/spring/SpringUtils.java new file mode 100644 index 0000000..9c0cc37 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/spring/SpringUtils.java @@ -0,0 +1,65 @@ +package com.bashi.common.utils.spring; + +import cn.hutool.extra.spring.SpringUtil; +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.stereotype.Component; + +/** + * spring工具类 + * + * @author Lion Li + */ +@Component +public final class SpringUtils extends SpringUtil { + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) { + return getBeanFactory().containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 + * 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * @return boolean + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { + return getBeanFactory().isSingleton(name); + } + + /** + * @param name + * @return Class 注册对象的类型 + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException { + return getBeanFactory().getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { + return getBeanFactory().getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) { + return (T) AopContext.currentProxy(); + } + +} diff --git a/bashi-common/src/main/java/com/bashi/common/utils/sql/SqlUtil.java b/bashi-common/src/main/java/com/bashi/common/utils/sql/SqlUtil.java new file mode 100644 index 0000000..52603a1 --- /dev/null +++ b/bashi-common/src/main/java/com/bashi/common/utils/sql/SqlUtil.java @@ -0,0 +1,37 @@ +package com.bashi.common.utils.sql; + +import cn.hutool.core.lang.Validator; +import com.bashi.common.exception.BaseException; + +/** + * sql操作工具类 + * + * @author duteliang + */ +public class SqlUtil +{ + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) + { + if (Validator.isNotEmpty(value) && !isValidOrderBySql(value)) + { + throw new BaseException("参数不符合规范,不能进行查询"); + } + return value; + } + + /** + * 验证 order by 语法是否符合规范 + */ + public static boolean isValidOrderBySql(String value) + { + return value.matches(SQL_PATTERN); + } +} diff --git a/bashi-dk/pom.xml b/bashi-dk/pom.xml new file mode 100644 index 0000000..f5a74c8 --- /dev/null +++ b/bashi-dk/pom.xml @@ -0,0 +1,51 @@ + + + + bashi + com.bashi + 2.4.0 + + 4.0.0 + + bashi-dk + + + 8 + 8 + UTF-8 + + + + + + com.bashi + bashi-common + + + com.bashi + bashi-framework + + + org.dromara.x-file-storage + x-file-storage-spring + 2.0.0 + + + + com.aliyun.oss + aliyun-sdk-oss + 3.16.1 + + + com.qcloud + cos_api + 5.6.137 + + + org.freemarker + freemarker + + + diff --git a/bashi-dk/src/main/java/com/bashi/dk/controller/BorrowStatusController.java b/bashi-dk/src/main/java/com/bashi/dk/controller/BorrowStatusController.java new file mode 100644 index 0000000..7c9289d --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/controller/BorrowStatusController.java @@ -0,0 +1,103 @@ +package com.bashi.dk.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.bashi.common.annotation.Log; +import com.bashi.common.annotation.RepeatSubmit; +import com.bashi.common.com.Condition; +import com.bashi.common.com.PageParams; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.enums.BusinessType; +import com.bashi.common.utils.PageUtils; +import com.bashi.dk.domain.BorrowStatus; +import com.bashi.dk.enums.BankTypeEnums; +import com.bashi.dk.service.BorrowStatusService; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@RestController +@RequestMapping("/dk/BorrowStatus") +public class BorrowStatusController extends BaseController { + + private final BorrowStatusService borrowStatusService; + + /** + * 查询借款状态列表 + */ + @PreAuthorize("@ss.hasPermi('dk:BorrowStatus:list')") + @GetMapping("/list") + public TableDataInfo list(PageParams pageParams, @Validated BorrowStatus bo) { + IPage page = borrowStatusService.page(Condition.getPage(pageParams), Wrappers.query(bo)); + return PageUtils.buildDataInfo(page); + } + + @GetMapping("/bankType") + public AjaxResult> bankType(){ + List list = Arrays.stream(BankTypeEnums.values()).map(BankTypeEnums::getName).collect(Collectors.toList()); + return AjaxResult.success(list); + } + + @GetMapping("/all") + public AjaxResult> all(){ + List list = borrowStatusService.list(); + return AjaxResult.success(list); + } + + /** + * 获取借款状态详细信息 + */ + @PreAuthorize("@ss.hasPermi('dk:BorrowStatus:query')") + @GetMapping("/{id}") + public AjaxResult getInfo(@NotNull(message = "主键不能为空") + @PathVariable("id") Long id) { + return AjaxResult.success(borrowStatusService.getById(id)); + } + + /** + * 新增借款状态 + */ + @PreAuthorize("@ss.hasPermi('dk:BorrowStatus:add')") + @Log(title = "借款状态", businessType = BusinessType.INSERT) + @RepeatSubmit + @PostMapping() + public AjaxResult add(@Validated @RequestBody BorrowStatus bo) { + return toAjax(borrowStatusService.save(bo) ? 1 : 0); + } + + /** + * 修改借款状态 + */ + @PreAuthorize("@ss.hasPermi('dk:BorrowStatus:edit')") + @Log(title = "借款状态", businessType = BusinessType.UPDATE) + @RepeatSubmit + @PutMapping() + public AjaxResult edit(@Validated @RequestBody BorrowStatus bo) { + return toAjax(borrowStatusService.updateById(bo) ? 1 : 0); + } + + /** + * 删除借款状态 + */ + @PreAuthorize("@ss.hasPermi('dk:BorrowStatus:remove')") + @Log(title = "借款状态" , businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@NotEmpty(message = "主键不能为空") + @PathVariable String ids) { + List idList = Stream.of(ids.split(",")).collect(Collectors.toList()); + return toAjax(borrowStatusService.removeByIds(idList) ? 1 : 0); + } +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/controller/DkAgreementSettingController.java b/bashi-dk/src/main/java/com/bashi/dk/controller/DkAgreementSettingController.java new file mode 100644 index 0000000..faca27e --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/controller/DkAgreementSettingController.java @@ -0,0 +1,43 @@ +package com.bashi.dk.controller; + +import com.bashi.common.annotation.Log; +import com.bashi.common.annotation.RepeatSubmit; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.enums.BusinessType; +import com.bashi.dk.domain.AgreementSetting; +import com.bashi.dk.service.AgreementSettingService; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@RestController +@RequestMapping("/dk/AgreementSetting") +public class DkAgreementSettingController extends BaseController { + + private final AgreementSettingService agreementSettingService; + + /** + * 获取协议设置详细信息 + */ + @PreAuthorize("@ss.hasPermi('dk:AgreementSetting:query')") + @GetMapping("/info") + public AjaxResult getInfo() { + return AjaxResult.success(agreementSettingService.getAgreementSetting()); + } + + /** + * 修改协议设置 + */ + @PreAuthorize("@ss.hasPermi('dk:AgreementSetting:edit')") + @Log(title = "协议设置", businessType = BusinessType.UPDATE) + @RepeatSubmit + @PutMapping() + public AjaxResult edit(@Validated @RequestBody AgreementSetting bo) { + return toAjax(agreementSettingService.updateById(bo) ? 1 : 0); + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/controller/DkBorrowController.java b/bashi-dk/src/main/java/com/bashi/dk/controller/DkBorrowController.java new file mode 100644 index 0000000..e566849 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/controller/DkBorrowController.java @@ -0,0 +1,124 @@ +package com.bashi.dk.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.bashi.common.annotation.Log; +import com.bashi.common.annotation.RepeatSubmit; +import com.bashi.common.com.Condition; +import com.bashi.common.com.PageParams; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.enums.BusinessType; +import com.bashi.common.utils.PageUtils; +import com.bashi.dk.domain.Borrow; +import com.bashi.dk.dto.admin.req.BorrowUpdateStatusReq; +import com.bashi.dk.dto.admin.resp.BorrowResp; +import com.bashi.dk.service.BorrowService; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@RestController +@RequestMapping("/dk/borrow") +public class DkBorrowController extends BaseController { + + private final BorrowService borrowService; + + /** + * 查询借款计划列表 + */ + @PreAuthorize("@ss.hasPermi('dk:borrow:list')") + @GetMapping("/list") + public TableDataInfo list(PageParams pageParams, @Validated Borrow bo) { + IPage page = borrowService.pageAdmin(pageParams, bo); + return PageUtils.buildDataInfo(page); + } + + + /** + * 获取借款计划详细信息 + */ + @PreAuthorize("@ss.hasPermi('dk:borrow:query')") + @GetMapping("/{id}") + public AjaxResult getInfo(@NotNull(message = "主键不能为空") + @PathVariable("id") Long id) { + return AjaxResult.success(borrowService.getById(id)); + } + + /** + * 新增借款计划 + */ + @PreAuthorize("@ss.hasPermi('dk:borrow:add')") + @Log(title = "借款计划", businessType = BusinessType.INSERT) + @RepeatSubmit + @PostMapping() + public AjaxResult add(@Validated @RequestBody Borrow bo) { + return toAjax(borrowService.save(bo) ? 1 : 0); + } + + /** + * 修改借款计划 + */ + @PreAuthorize("@ss.hasPermi('dk:borrow:edit')") + @Log(title = "借款计划", businessType = BusinessType.UPDATE) + @RepeatSubmit + @PutMapping() + public AjaxResult edit(@Validated @RequestBody Borrow bo) { + return toAjax(borrowService.updateById(bo) ? 1 : 0); + } + + @Log(title = "修改接口银行卡", businessType = BusinessType.UPDATE) + @PostMapping("updateBank") + public AjaxResult updateBank(@RequestBody Borrow bo) { + return toAjax(borrowService.updateBank(bo)); + } + + @PreAuthorize("@ss.hasPermi('dk:borrow:edit')") + @Log(title = "修改借款", businessType = BusinessType.UPDATE) + @RepeatSubmit + @PostMapping("/updateLoan") + public AjaxResult updateLoan(@Validated @RequestBody Borrow bo) { + return toAjax(borrowService.updateLoan(bo)); + } + + + @PreAuthorize("@ss.hasPermi('dk:borrow:edit')") + @Log(title = "修改借款状态", businessType = BusinessType.UPDATE) + @RepeatSubmit + @PostMapping("/updateStatus") + public AjaxResult updateStatus(@Validated @RequestBody BorrowUpdateStatusReq bo) { + return toAjax(borrowService.updateStatus(bo)); + } + + + /** + * 删除借款计划 + */ + @PreAuthorize("@ss.hasPermi('dk:borrow:remove')") + @Log(title = "借款计划" , businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@NotEmpty(message = "主键不能为空") + @PathVariable String ids) { + List idList = Stream.of(ids.split(",")).collect(Collectors.toList()); + return toAjax(borrowService.removeByIds(idList) ? 1 : 0); + } + + @GetMapping("/getContract") + public AjaxResult getContract(String tradeNo){ + String contract = borrowService.getContract(tradeNo); + AjaxResult success = AjaxResult.success(); + success.setData(contract); + return success; + } +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/controller/DkCustomerController.java b/bashi-dk/src/main/java/com/bashi/dk/controller/DkCustomerController.java new file mode 100644 index 0000000..b4cc42d --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/controller/DkCustomerController.java @@ -0,0 +1,88 @@ +package com.bashi.dk.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.bashi.common.annotation.Log; +import com.bashi.common.annotation.RepeatSubmit; +import com.bashi.common.com.PageParams; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.domain.entity.Customer; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.enums.BusinessType; +import com.bashi.common.utils.PageUtils; +import com.bashi.common.utils.poi.ExcelUtil; +import com.bashi.dk.dto.admin.req.UpdatePwdCustomerReq; +import com.bashi.dk.dto.admin.resp.CustomerAdminResp; +import com.bashi.dk.dto.admin.resp.CustomerExportVo; +import com.bashi.dk.mapper.CustomerMapper; +import com.bashi.dk.service.CustomerService; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@RestController +@RequestMapping("/dk/dkCustomer") +public class DkCustomerController extends BaseController { + + private final CustomerService customerService; + @Resource + private CustomerMapper customerMapper; + + @PreAuthorize("@ss.hasPermi('dk:dkCustomer:list')") + @GetMapping("/list") + public TableDataInfo list(PageParams pageParams, @Validated CustomerAdminResp bo) { + IPage page = customerService.pageAdmin(pageParams, bo); + return PageUtils.buildDataInfo(page); + } + + @ApiOperation("导出客户列表") + @PreAuthorize("@ss.hasPermi('dk:dkCustomer:export')") + @Log(title = "客户", businessType = BusinessType.EXPORT) + @GetMapping("/export") + public AjaxResult export(@Validated CustomerAdminResp bo) { + List list = customerMapper.exportAdmin(bo); + ExcelUtil util = new ExcelUtil<>(CustomerExportVo.class); + return util.exportExcel(list, "客户"); + } + + @PreAuthorize("@ss.hasPermi('dk:dkCustomer:query')") + @GetMapping("/{id}") + public AjaxResult getInfo(@NotNull(message = "主键不能为空") + @PathVariable("id") Long id) { + return AjaxResult.success(customerService.getById(id)); + } + + + @PreAuthorize("@ss.hasPermi('dk:dkCustomer:edit')") + @Log(title = "客户", businessType = BusinessType.UPDATE) + @RepeatSubmit + @PutMapping() + public AjaxResult edit(@Validated @RequestBody Customer bo) { + return toAjax(customerService.updateById(bo) ? 1 : 0); + } + + @DeleteMapping("/{ids}") + public AjaxResult remove(@NotEmpty(message = "主键不能为空") + @PathVariable String ids) { + List idList = Stream.of(ids.split(",")).collect(Collectors.toList()); + return toAjax(customerService.removeByIds(idList) ? 1 : 0); + } + + + @Log(title = "修改密码" , businessType = BusinessType.DELETE) + @PostMapping("/resetPwd") + public AjaxResult resetPwd(@RequestBody UpdatePwdCustomerReq customer) { + return toAjax(customerService.updatePwd(customer.getCustomerId(),customer.getPassword())); + } +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/controller/DkCustomerInfoController.java b/bashi-dk/src/main/java/com/bashi/dk/controller/DkCustomerInfoController.java new file mode 100644 index 0000000..bd3a18a --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/controller/DkCustomerInfoController.java @@ -0,0 +1,64 @@ +package com.bashi.dk.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.bashi.common.annotation.Log; +import com.bashi.common.annotation.RepeatSubmit; +import com.bashi.common.com.Condition; +import com.bashi.common.com.PageParams; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.enums.BusinessType; +import com.bashi.common.utils.PageUtils; +import com.bashi.dk.domain.CustomerInfo; +import com.bashi.dk.service.CustomerInfoService; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; + +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@RestController +@RequestMapping("/dk/dkCustomerInfo") +public class DkCustomerInfoController extends BaseController { + + private final CustomerInfoService customerInfoService; + + @PreAuthorize("@ss.hasPermi('dk:dkCustomerInfo:list')") + @GetMapping("/list") + public TableDataInfo list(PageParams pageParams, @Validated CustomerInfo bo) { + IPage page = customerInfoService.page(Condition.getPage(pageParams), Wrappers.query(bo)); + return PageUtils.buildDataInfo(page); + } + + @GetMapping("/getInfoByCustomerId") + public AjaxResult getInfoByCustomer(Long customerId) { + return AjaxResult.success(customerInfoService.getByCustomerId(customerId)); + } + + @PostMapping("/updateAllowSignature") + public AjaxResult updateAllowSignature(@RequestBody CustomerInfo bo) { + customerInfoService.updateAllowSignature(bo); + return AjaxResult.success(); + } + + @PreAuthorize("@ss.hasPermi('dk:dkCustomerInfo:query')") + @GetMapping("/{id}") + public AjaxResult getInfo(@NotNull(message = "主键不能为空") + @PathVariable("id") Long id) { + return AjaxResult.success(customerInfoService.getById(id)); + } + + @PreAuthorize("@ss.hasPermi('dk:dkCustomerInfo:edit')") + @Log(title = "客户资料", businessType = BusinessType.UPDATE) + @RepeatSubmit + @PutMapping() + public AjaxResult edit(@Validated @RequestBody CustomerInfo bo) { + return toAjax(customerInfoService.updateById(bo) ? 1 : 0); + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/controller/DkHomeSettingController.java b/bashi-dk/src/main/java/com/bashi/dk/controller/DkHomeSettingController.java new file mode 100644 index 0000000..e3a1df0 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/controller/DkHomeSettingController.java @@ -0,0 +1,51 @@ +package com.bashi.dk.controller; + +import com.bashi.common.annotation.Log; +import com.bashi.common.annotation.RepeatSubmit; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.enums.BusinessType; +import com.bashi.dk.domain.HomeSetting; +import com.bashi.dk.service.HomeSettingService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * 常规设置Controller + * + * @author duteliang + * @date 2023-11-29 + */ +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@RestController +@RequestMapping("/dk/HomeSetting") +public class DkHomeSettingController extends BaseController { + + private final HomeSettingService homeSettingService; + + + /** + * 获取常规设置详细信息 + */ + @PreAuthorize("@ss.hasPermi('dk:HomeSetting:query')") + @GetMapping("/info") + public AjaxResult getInfo() { + return AjaxResult.success(homeSettingService.getHomeSetting()); + } + + /** + * 修改常规设置 + */ + @PreAuthorize("@ss.hasPermi('dk:HomeSetting:edit')") + @Log(title = "常规设置", businessType = BusinessType.UPDATE) + @RepeatSubmit + @PutMapping() + public AjaxResult edit(@Validated @RequestBody HomeSetting bo) { + return toAjax(homeSettingService.updateById(bo) ? 1 : 0); + } +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/controller/DkLoansSettingController.java b/bashi-dk/src/main/java/com/bashi/dk/controller/DkLoansSettingController.java new file mode 100644 index 0000000..83c5434 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/controller/DkLoansSettingController.java @@ -0,0 +1,50 @@ +package com.bashi.dk.controller; + +import com.bashi.common.annotation.Log; +import com.bashi.common.annotation.RepeatSubmit; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.enums.BusinessType; +import com.bashi.dk.domain.LoansSetting; +import com.bashi.dk.service.LoansSettingService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * 贷款设置Controller + * + * @author duteliang + * @date 2023-11-29 + */ +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@RestController +@RequestMapping("/dk/LoansSetting") +public class DkLoansSettingController extends BaseController { + + private final LoansSettingService loansSettingService; + + /** + * 获取贷款设置详细信息 + */ + @PreAuthorize("@ss.hasPermi('dk:LoansSetting:query')") + @GetMapping("/info") + public AjaxResult getInfo() { + return AjaxResult.success(loansSettingService.getLoansSetting()); + } + + /** + * 修改贷款设置 + */ + @PreAuthorize("@ss.hasPermi('dk:LoansSetting:edit')") + @Log(title = "贷款设置", businessType = BusinessType.UPDATE) + @RepeatSubmit + @PutMapping() + public AjaxResult edit(@Validated @RequestBody LoansSetting bo) { + return toAjax(loansSettingService.updateById(bo) ? 1 : 0); + } +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/controller/app/AppBorrowController.java b/bashi-dk/src/main/java/com/bashi/dk/controller/app/AppBorrowController.java new file mode 100644 index 0000000..e5d267d --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/controller/app/AppBorrowController.java @@ -0,0 +1,88 @@ +package com.bashi.dk.controller.app; + + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.bashi.common.com.Condition; +import com.bashi.common.com.PageParams; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.utils.BeanConvertUtil; +import com.bashi.common.utils.PageUtils; +import com.bashi.common.utils.SecurityUtils; +import com.bashi.dk.domain.Borrow; +import com.bashi.dk.dto.app.req.BorrowStartReq; +import com.bashi.dk.dto.app.resp.BorrowInfo; +import com.bashi.dk.dto.app.resp.LoanProcessResp; +import com.bashi.dk.service.BorrowService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/app/borrow") +@Api(value = "贷款相关的接口", tags = {"贷款相关的接口"}) +public class AppBorrowController { + + @Autowired + private BorrowService borrowService; + + @PostMapping("/start") + @ApiOperation(value = "发起贷款") + public AjaxResult start(@RequestBody BorrowStartReq req){ + req.setCustomerId(SecurityUtils.getLoginUser().getCustomer().getId()); + Borrow borrow = borrowService.borrow(req); + return AjaxResult.success(borrow); + } + + @GetMapping("/withdraw") + @ApiOperation(value = "提现") + public AjaxResult withdraw(@ApiParam("提现金额") Double withdrawAmount){ + Long customerId = SecurityUtils.getLoginUser().getCustomer().getId(); + borrowService.withdraw(withdrawAmount,customerId); + return AjaxResult.success(); + } + + + @GetMapping("/getStepBorrow") + @ApiOperation(value = "获取贷款进度") + public AjaxResult getStepBorrow(){ + Long customerId = SecurityUtils.getLoginUser().getCustomer().getId(); + LoanProcessResp stepBorrow = borrowService.getStepBorrow(customerId); + return AjaxResult.success(stepBorrow); + } + + + @GetMapping("/info") + @ApiOperation(value = "查看贷款详情") + public AjaxResult info(String tradeNo){ + Borrow borrow = borrowService.getByTradeNo(tradeNo); + BorrowInfo borrowInfo = BeanConvertUtil.convertTo(borrow, BorrowInfo::new); + LoanProcessResp stepBorrow = borrowService.parseStepBorrow(borrow); + borrowInfo.setLoanProcessResp(stepBorrow); + return AjaxResult.success(borrowInfo); + } + + @GetMapping("/page") + @ApiOperation(value = "分页查询贷款我的贷款") + public TableDataInfo page(PageParams pageParams){ + Long customerId = SecurityUtils.getLoginUser().getCustomer().getId(); + LambdaQueryWrapper query = Wrappers.lambdaQuery(Borrow.class) + .eq(Borrow::getCustomerId,customerId) + .orderByDesc(Borrow::getCreateTime); + IPage page = borrowService.page(Condition.getPage(pageParams), query); + return PageUtils.buildDataInfo(page); + } + + + @GetMapping("/getContract") + public AjaxResult getContract(String tradeNo){ + String contract = borrowService.getContract(tradeNo); + AjaxResult success = AjaxResult.success(); + success.setData(contract); + return success; + } +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/controller/app/AppCustomerController.java b/bashi-dk/src/main/java/com/bashi/dk/controller/app/AppCustomerController.java new file mode 100644 index 0000000..acdb3c6 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/controller/app/AppCustomerController.java @@ -0,0 +1,50 @@ +package com.bashi.dk.controller.app; + +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.domain.entity.Customer; +import com.bashi.common.utils.SecurityUtils; +import com.bashi.dk.domain.CustomerInfo; +import com.bashi.dk.service.CustomerInfoService; +import com.bashi.dk.service.CustomerService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/app/customer") +@Api(value = "客户接口", tags = {"客户接口"}) +public class AppCustomerController { + + @Autowired + private CustomerInfoService customerInfoService; + @Autowired + private CustomerService customerService; + + @GetMapping("/info") + @ApiOperation(value = "客户信息") + public AjaxResult info(){ + Customer customer = SecurityUtils.getLoginUser().getCustomer(); + customer = customerService.getById(customer.getId()); + return AjaxResult.success(customer); + } + + @GetMapping("/card/info") + @ApiOperation(value = "客户资料信息") + public AjaxResult cardInfo(){ + Customer customer = SecurityUtils.getLoginUser().getCustomer(); + CustomerInfo customerInfo = customerInfoService.getByCustomerId(customer.getId()); + return AjaxResult.success(customerInfo); + } + + @PostMapping("/updateCustomerCard") + @ApiOperation(value = "修改客户资料信息") + public AjaxResult updateCustomerCard(@RequestBody CustomerInfo customerInfo){ + Customer customer = SecurityUtils.getLoginUser().getCustomer(); + customerInfo.setCustomerId(customer.getId()); + customerInfoService.updateCustomerInfo(customerInfo); + return AjaxResult.success(); + } + + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/controller/app/AppCustomerOpenController.java b/bashi-dk/src/main/java/com/bashi/dk/controller/app/AppCustomerOpenController.java new file mode 100644 index 0000000..f32081a --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/controller/app/AppCustomerOpenController.java @@ -0,0 +1,89 @@ +package com.bashi.dk.controller.app; + +import cn.hutool.core.lang.UUID; +import cn.hutool.core.util.RandomUtil; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.domain.entity.Customer; +import com.bashi.common.core.redis.RedisCache; +import com.bashi.common.exception.CustomException; +import com.bashi.dk.dto.app.req.CustomerRegisterReq; +import com.bashi.dk.dto.app.req.UpdatePwdOpenReq; +import com.bashi.dk.service.CustomerService; +import com.bashi.framework.constant.CodeType; +import com.bashi.framework.web.service.CodeService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +@RequestMapping("/customer/open") +@RestController +@Api(value = "用户开放接口", tags = {"用户开放接口"}) +public class AppCustomerOpenController { + @Autowired + private CodeService codeService; + @Autowired + private CustomerService customerService; + @Autowired + private RedisCache redisCache; + + @GetMapping("/sms/register") + @ApiOperation("用户注册-验证码") + public AjaxResult customerRegister(String phoneNumber) { + String numbers = RandomUtil.randomNumbers(6); + codeService.put(phoneNumber, CodeType.CUSTOMER_REGISTER,numbers); + AjaxResult success = AjaxResult.success(); + success.setData(numbers); + return success; + } + + @PostMapping("/register") + @ApiOperation("用户注册") + public AjaxResult customerRegister(@RequestBody CustomerRegisterReq register) { + if(!codeService.check(register.getPhoneNumber(), CodeType.CUSTOMER_REGISTER,register.getCode())){ + throw new CustomException("验证码错误"); + } + customerService.register(register); + return AjaxResult.success(); + } + + @GetMapping("/sms/forget") + @ApiOperation("忘记密码-验证码") + public AjaxResult customerForgetPassword(String phoneNumber) { + String numbers = RandomUtil.randomNumbers(6); + codeService.put(phoneNumber, CodeType.CUSTOMER_FORGET_PASSWORD,numbers); + AjaxResult success = AjaxResult.success(); + success.setData(numbers); + return success; + } + + @GetMapping("/sms/forget/check") + @ApiOperation("忘记密码-验证码-校验") + public AjaxResult customerForgetPasswordCheck(String phoneNumber,String code) { + if(!codeService.check(phoneNumber, CodeType.CUSTOMER_REGISTER,code)){ + throw new CustomException("验证码错误"); + } + Customer customer = customerService.getCustomerByName(phoneNumber); + String uuid = UUID.randomUUID().toString(); + redisCache.setCacheObject(uuid,customer); + AjaxResult success = AjaxResult.success(); + success.setData(uuid); + return success; + } + + @PostMapping("/updatePwd") + @ApiOperation("修改密码") + public AjaxResult customerForgetPasswordCheck(@RequestBody UpdatePwdOpenReq updatePwdOpenReq) { + Customer customer = redisCache.getCacheObject(updatePwdOpenReq.getCheckCode()); + if(customer == null){ + throw new CustomException("密码修改失败,请重新获取验证码操作"); + } + if(!updatePwdOpenReq.getPassword().equals(updatePwdOpenReq.getConfirmPassword())){ + throw new CustomException("密码不一致"); + } + customerService.updatePwd(customer.getId(),updatePwdOpenReq.getPassword()); + AjaxResult success = AjaxResult.success(); + return success; + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/controller/app/AppHomeController.java b/bashi-dk/src/main/java/com/bashi/dk/controller/app/AppHomeController.java new file mode 100644 index 0000000..50f6d49 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/controller/app/AppHomeController.java @@ -0,0 +1,65 @@ + +package com.bashi.dk.controller.app; + +import cn.hutool.core.util.RandomUtil; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.utils.BeanConvertUtil; +import com.bashi.dk.domain.LoansSetting; +import com.bashi.dk.dto.app.req.CalLoanReq; +import com.bashi.dk.dto.app.resp.CalLoanResp; +import com.bashi.dk.dto.app.resp.LoanUser; +import com.bashi.dk.kit.CalLoanManager; +import com.bashi.dk.service.LoansSettingService; +import com.bashi.dk.util.Loan; +import com.bashi.dk.util.PhoneRandomUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.aspectj.weaver.loadtime.Aj; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Random; + +@RestController +@RequestMapping("/app/home/loans") +@Api(value = "首页开放", tags = {"首页开放"}) +public class AppHomeController { + + @Autowired + private CalLoanManager calLoanManager; + @Autowired + private LoansSettingService loansSettingService; + + @PostMapping("/calLoan") + @ApiOperation(value = "计算每月还款") + public AjaxResult calLoan(@RequestBody CalLoanReq calLoanReq) { + if(calLoanReq.getTotalLoanMoney() == null || calLoanReq.getTotalMonth() == null){ + LoansSetting loansSetting = loansSettingService.getLoansSetting(); + if(calLoanReq.getTotalLoanMoney() == null){ + calLoanReq.setTotalLoanMoney(loansSetting.getLoansInitAccount()); + } + if(calLoanReq.getTotalMonth() == null){ + calLoanReq.setTotalMonth(Integer.valueOf(loansSetting.getLoansInitMonth())); + } + } + Loan loan = calLoanManager.calLoan(calLoanReq); + CalLoanResp calLoanResp = BeanConvertUtil.convertTo(loan, CalLoanResp::new); + return AjaxResult.success(calLoanResp); + } + + + @GetMapping("/loansUser") + @ApiOperation(value = "获取贷款用户") + public AjaxResult loansUser() { + Random random = new Random(); + LoanUser loanUser = new LoanUser(); + loanUser.setPhone(PhoneRandomUtil.gen()); + loanUser.setAmount(RandomUtil.randomInt(10,100)+"000"); + loanUser.setTime(LocalDate.now().plusDays(-random.nextInt(7)).format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))); + return AjaxResult.success(loanUser); + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/controller/app/AppSettingController.java b/bashi-dk/src/main/java/com/bashi/dk/controller/app/AppSettingController.java new file mode 100644 index 0000000..19f214c --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/controller/app/AppSettingController.java @@ -0,0 +1,74 @@ +package com.bashi.dk.controller.app; + +import cn.hutool.core.util.NumberUtil; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.utils.BeanConvertUtil; +import com.bashi.dk.domain.AgreementSetting; +import com.bashi.dk.domain.HomeSetting; +import com.bashi.dk.domain.LoansSetting; +import com.bashi.dk.dto.app.resp.LoansSettingVO; +import com.bashi.dk.enums.BankTypeEnums; +import com.bashi.dk.service.AgreementSettingService; +import com.bashi.dk.service.HomeSettingService; +import com.bashi.dk.service.LoansSettingService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/app/home/setting") +@Api(value = "设置信息", tags = {"设置信息"}) +public class AppSettingController { + + @Autowired + private AgreementSettingService agreementSettingService; + @Autowired + private HomeSettingService homeSettingService; + @Autowired + private LoansSettingService loansSettingService; + + @GetMapping("/agreement") + @ApiOperation(value = "协议内容") + public AjaxResult agreement() { + AgreementSetting setting = agreementSettingService.getAgreementSetting(); + return AjaxResult.success(setting); + } + + @GetMapping("/home") + @ApiOperation(value = "常规内容") + public AjaxResult home() { + HomeSetting setting = homeSettingService.getHomeSetting(); + return AjaxResult.success(setting); + } + + @GetMapping("/bankType") + @ApiOperation(value = "获取银行列表") + public AjaxResult> bankType(){ + List list = Arrays.stream(BankTypeEnums.values()).map(BankTypeEnums::getName).collect(Collectors.toList()); + return AjaxResult.success(list); + } + + @GetMapping("/loans") + @ApiOperation(value = "贷款信息") + public AjaxResult loans() { + LoansSetting setting = loansSettingService.getLoansSetting(); + LoansSettingVO vo = BeanConvertUtil.convertTo(setting, LoansSettingVO::new); + Double minDayServiceRate = Arrays.stream(setting.getServiceRate().split(",")) + .map(Double::valueOf).min(Double::compareTo).orElse(null); + if(minDayServiceRate != null){ + minDayServiceRate = NumberUtil.div(minDayServiceRate, new Double(30D), 4); + }else{ + minDayServiceRate = 0D; + } + vo.setMinDayServiceRate(minDayServiceRate); + return AjaxResult.success(setting); + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/controller/app/LoginV2Controller.java b/bashi-dk/src/main/java/com/bashi/dk/controller/app/LoginV2Controller.java new file mode 100644 index 0000000..4adccfb --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/controller/app/LoginV2Controller.java @@ -0,0 +1,33 @@ +package com.bashi.dk.controller.app; + +import com.bashi.common.constant.Constants; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.domain.model.LoginPhoneBody; +import com.bashi.dk.kit.DkLoginKit; +import com.bashi.framework.security.sms.LoginTypeEnums; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + +@RestController +public class LoginV2Controller { + @Autowired + private DkLoginKit dkLoginKit; + + @PostMapping("/customer/login") + @ApiOperation("用户登陆") + public AjaxResult loginCustomer(@RequestBody LoginPhoneBody loginBody) { + Map ajax = new HashMap<>(); + loginBody.setLoginRole(LoginTypeEnums.CUSTOMER_PASSWORD.getCode()); + // 生成令牌 + String token = dkLoginKit.login(loginBody); + ajax.put(Constants.TOKEN, token); + return AjaxResult.success(ajax); + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/controller/app/V2CommonController.java b/bashi-dk/src/main/java/com/bashi/dk/controller/app/V2CommonController.java new file mode 100644 index 0000000..aaea4bf --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/controller/app/V2CommonController.java @@ -0,0 +1,44 @@ +package com.bashi.dk.controller.app; + +import com.alibaba.fastjson.JSON; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.dk.manager.FileUploadManager; +import com.bashi.dk.manager.FileUploadRes; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.dromara.x.file.storage.core.FileInfo; +import org.dromara.x.file.storage.core.FileStorageService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +/** + *

created on 2021/7/13

+ * + * @author zhangliang + */ +@RestController +@Api(value = "通用接口", tags = {"通用接口"}) +@Slf4j +public class V2CommonController { + @Autowired + private FileStorageService fileStorageService; + + @PostMapping("/v2/common/upload") + @ApiOperation("文件上传") + public AjaxResult uploadFile(MultipartFile file) throws Exception { + try { + FileInfo upload = fileStorageService.of(file).setPath("upload/").upload(); + FileUploadRes fileUploadRes = new FileUploadRes(); + fileUploadRes.setUrl(upload.getUrl()); + fileUploadRes.setFileName(upload.getOriginalFilename()); + log.info("sss={}", JSON.toJSONString(upload)); + return AjaxResult.success(fileUploadRes); + } catch (Exception e) { + return AjaxResult.error(e.getMessage()); + } + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/domain/AgreementSetting.java b/bashi-dk/src/main/java/com/bashi/dk/domain/AgreementSetting.java new file mode 100644 index 0000000..a493f0c --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/domain/AgreementSetting.java @@ -0,0 +1,55 @@ +package com.bashi.dk.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 协议设置 + * @TableName dk_agreement_setting + */ +@TableName(value ="dk_agreement_setting") +@Data +@ApiModel("协议设置对象") +public class AgreementSetting implements Serializable { + /** + * ID + */ + @TableId(type = IdType.AUTO) + private Long id; + + /** + * 借款协议 + */ + @ApiModelProperty("借款协议") + private String loansAgreement; + + /** + * 服务协议 + */ + @ApiModelProperty("服务协议") + private String serviceAgreement; + + /** + * 授权协议 + */ + @ApiModelProperty("授权协议") + private String authAgreement; + + /** + * 法律责任 + */ + @ApiModelProperty("法律责任") + private String lawAgreement; + + private String contractTemplate; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/domain/Borrow.java b/bashi-dk/src/main/java/com/bashi/dk/domain/Borrow.java new file mode 100644 index 0000000..10c4c12 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/domain/Borrow.java @@ -0,0 +1,210 @@ +package com.bashi.dk.domain; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 借款计划对象 dk_borrow + * + * @author duteliang + * @date 2023-11-29 + */ +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("dk_borrow") +@ApiModel("借款计划") +public class Borrow implements Serializable { + + private static final long serialVersionUID=1L; + + + /** ID */ + @TableId(value = "id") + @ApiModelProperty("ID") + private Long id; + + @ApiModelProperty("用途说明") + private String noteRemark; + + + private Boolean auditFlag; + + /** 订单编号 */ + @ApiModelProperty("订单编号") + private String tradeNo; + + /** 总贷款额 */ + @ApiModelProperty("总贷款额") + private BigDecimal totalLoanMoney; + + /** 还款月数 */ + @ApiModelProperty("还款月数") + private Integer totalMonth; + + /** 月利率 */ + @ApiModelProperty("月利率") + private Double loanMonthRate; + + /** 年利率 */ + @ApiModelProperty("年利率") + private Double loanYearRate; + + /** 总利息数 */ + @ApiModelProperty("总利息数") + private BigDecimal totalInterest; + + /** 还款总额 */ + @ApiModelProperty("还款总额") + private BigDecimal totalRepayment; + + /** 首月还款额 */ + @ApiModelProperty("首月还款额") + private BigDecimal firstRepayment; + + /** 每月还款额 */ + @ApiModelProperty("每月还款额") + private BigDecimal avgRepayment; + + /** 每月还款日 */ + @ApiModelProperty("每月还款日") + private Integer dueDate; + + /** 是否打款 */ + @ApiModelProperty("是否打款") + private Integer remitFlag; + + @ApiModelProperty("客户电话") + private String customerPhone; + + /** 借款状态 */ + @ApiModelProperty("借款状态") + private String borrowName; + + @ApiModelProperty("借款状态样式") + private String borrowNameStyle; + + /** 借款说明 */ + @ApiModelProperty("借款说明") + private String borrowRemark; + + /** 还款说明 */ + @ApiModelProperty("还款说明") + private String repayRemark; + + /** 计划 */ + @ApiModelProperty("计划") + private String infoJson; + + /** 客户ID */ + @ApiModelProperty("客户ID") + private Long customerId; + + /** 真实姓名 */ + @ApiModelProperty("真实姓名") + private String realName; + + /** 身份证照片 */ + @ApiModelProperty("身份证照片") + private String cardNum; + + /** 身份证正面 */ + @ApiModelProperty("身份证正面") + private String cardFrontPicture; + + private String handCardPicture; + + /** 身份证背面 */ + @ApiModelProperty("身份证背面") + private String cardBackPicture; + + /** 单位名称 */ + @ApiModelProperty("单位名称") + private String companyName; + + /** 职位 */ + @ApiModelProperty("职位") + private String companyTitle; + + /** 单位电话 */ + @ApiModelProperty("单位电话") + private String companyPhone; + + /** 工作年龄 */ + @ApiModelProperty("工作年龄") + private String companyYear; + + /** 单位地址 */ + @ApiModelProperty("单位地址") + private String companyAddress; + + /** 详细地址 */ + @ApiModelProperty("详细地址") + private String companyAddressInfo; + + /** 现居住地址 */ + @ApiModelProperty("现居住地址") + private String customerAddress; + + /** 详细地址 */ + @ApiModelProperty("详细地址") + private String customerAddressInfo; + + /** 亲属姓名 */ + @ApiModelProperty("亲属姓名") + private String kinsfolkName; + + /** 亲属电话 */ + @ApiModelProperty("亲属电话") + private String kinsfolkPhone; + + /** 亲属关系 1-父母、2-配偶、3-子女,4-祖父母 */ + @ApiModelProperty("亲属关系 1-父母、2-配偶、3-子女,4-祖父母") + private String kinsfolkRef; + + @ApiModelProperty("转账备注") + private String transRemark; + + /** $column.columnComment */ + @ApiModelProperty("$column.columnComment") + private String bankType; + + /** $column.columnComment */ + @ApiModelProperty("$column.columnComment") + private String backCardNum; + + /** $column.columnComment */ + @ApiModelProperty("$column.columnComment") + private String firstBankType; + + /** $column.columnComment */ + @ApiModelProperty("$column.columnComment") + private String firstBackCardNum; + + /** 修改银行卡次数 */ + @ApiModelProperty("修改银行卡次数") + private Integer updateBackNum; + + @ApiModelProperty("收入(万)") + private BigDecimal incomeWan; + + /** 创建时间 */ + @ApiModelProperty("创建时间") + private LocalDateTime createTime; + + /** 修改时间 */ + @ApiModelProperty("修改时间") + private LocalDateTime updateTime; + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/domain/BorrowLog.java b/bashi-dk/src/main/java/com/bashi/dk/domain/BorrowLog.java new file mode 100644 index 0000000..2deb549 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/domain/BorrowLog.java @@ -0,0 +1,21 @@ +package com.bashi.dk.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("dk_borrow_log") +public class BorrowLog { + + /** ID */ + @TableId(value = "id") + private Long id; + private Double withdrawAccount; + private Long customerId; + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/domain/BorrowStatus.java b/bashi-dk/src/main/java/com/bashi/dk/domain/BorrowStatus.java new file mode 100644 index 0000000..d0bbf68 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/domain/BorrowStatus.java @@ -0,0 +1,59 @@ +package com.bashi.dk.domain; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import java.io.Serializable; +import java.util.Date; +import java.math.BigDecimal; + +/** + * 借款状态对象 dk_borrow_status + * + * @author duteliang + * @date 2023-11-29 + */ +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("dk_borrow_status") +@ApiModel("借款状态添加对象") +public class BorrowStatus implements Serializable { + + private static final long serialVersionUID=1L; + + + /** ID */ + @TableId(value = "id") + @ApiModelProperty("ID") + private Long id; + + /** 是否可以打款 */ + @ApiModelProperty("是否可以打款") + private Integer usedRemit; + + /** 借款状态 */ + @ApiModelProperty("借款状态") + private String borrowName; + + /** 借款说明 */ + @ApiModelProperty("借款说明") + private String borrowRemark; + + @ApiModelProperty("借款样式") + private String borrowNameStyle; + + /** 创建时间 */ + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty("创建时间") + private Date createTime; + + /** 修改时间 */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @ApiModelProperty("修改时间") + private Date updateTime; + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/domain/CustomerInfo.java b/bashi-dk/src/main/java/com/bashi/dk/domain/CustomerInfo.java new file mode 100644 index 0000000..6bd3fa8 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/domain/CustomerInfo.java @@ -0,0 +1,162 @@ +package com.bashi.dk.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serializable; +import java.math.BigDecimal; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 客户资料 + * @TableName dk_customer_info + */ +@TableName(value ="dk_customer_info") +@Data +@ApiModel("客户资料") +public class CustomerInfo implements Serializable { + /** + * ID + */ + @TableId(type = IdType.AUTO) + private Long id; + + @ApiModelProperty("身份信息完整度") + private Boolean cardFlag = false; + @ApiModelProperty("资料信息完整度") + private Boolean infoFlag = false; + @ApiModelProperty("银行卡信息完整度") + private Boolean bankFlag = false; + + /** + * 客户ID + */ + @ApiModelProperty("客户ID") + private Long customerId; + + @ApiModelProperty("客户电话") + private String customerPhone; + + /** + * 真实姓名 + */ + @ApiModelProperty("真实姓名") + private String realName; + + /** + * 身份证号码 + */ + @ApiModelProperty("身份证号码") + private String cardNum; + + /** + * 身份证正面 + */ + @ApiModelProperty("身份证正面") + private String cardFrontPicture; + + /** + * 手持身份证照片 + */ + @ApiModelProperty("手持身份证照片") + private String handCardPicture; + + /** + * 身份证背面 + */ + @ApiModelProperty("身份证背面") + private String cardBackPicture; + + /** + * 单位名称 + */ + @ApiModelProperty("单位名称") + private String companyName; + + /** + * 职位 + */ + @ApiModelProperty("职位") + private String companyTitle; + + /** + * 单位电话 + */ + @ApiModelProperty("单位电话") + private String companyPhone; + + /** + * 工作年龄 + */ + @ApiModelProperty("工作年龄") + private String companyYear; + + /** + * 单位地址 + */ + @ApiModelProperty("单位地址") + private String companyAddress; + + /** + * 详细地址 + */ + @ApiModelProperty("详细地址") + private String companyAddressInfo; + + /** + * 现居住地址 + */ + @ApiModelProperty("现居住地址") + private String customerAddress; + + /** + * 详细地址 + */ + @ApiModelProperty("详细地址") + private String customerAddressInfo; + + @ApiModelProperty("收入(万)") + private BigDecimal incomeWan; + + /** + * 亲属姓名 + */ + @ApiModelProperty("亲属姓名") + private String kinsfolkName; + + /** + * 亲属电话 + */ + @ApiModelProperty("亲属电话") + private String kinsfolkPhone; + + /** + * 亲属关系 1-父母、2-配偶、3-子女,4-祖父母 + */ + @ApiModelProperty("亲属关系 1-父母、2-配偶、3-子女,4-祖父母") + private String kinsfolkRef; + + /** + * 开户银行 + */ + @ApiModelProperty("开户银行") + private String bankType; + + /** + * 银行卡号 + */ + @ApiModelProperty("银行卡号") + private String backCardNum; + + @ApiModelProperty("签名") + private String signature; + + private Boolean allowSignature = true; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/domain/HomeSetting.java b/bashi-dk/src/main/java/com/bashi/dk/domain/HomeSetting.java new file mode 100644 index 0000000..8ab015d --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/domain/HomeSetting.java @@ -0,0 +1,42 @@ +package com.bashi.dk.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serializable; +import lombok.Data; + +/** + * 常规设置 + * @TableName dk_home_setting + */ +@TableName(value ="dk_home_setting") +@Data +public class HomeSetting implements Serializable { + /** + * ID + */ + @TableId(type = IdType.AUTO) + private Long id; + + /** + * 站点标题 + */ + private String homeTitle; + + /** + * banner图 + */ + private String bannerOne; + + /** + * 公章 + */ + private String commonSeal; + + + private String chatUrl; + @TableField(exist = false) + private static final long serialVersionUID = 1L; +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/domain/LoansSetting.java b/bashi-dk/src/main/java/com/bashi/dk/domain/LoansSetting.java new file mode 100644 index 0000000..66e71db --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/domain/LoansSetting.java @@ -0,0 +1,61 @@ +package com.bashi.dk.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serializable; +import java.math.BigDecimal; +import lombok.Data; + +/** + * 贷款设置 + * @TableName dk_loans_setting + */ +@TableName(value ="dk_loans_setting") +@Data +public class LoansSetting implements Serializable { + /** + * ID + */ + @TableId(type = IdType.AUTO) + private Long id; + + /** + * 贷款最小金额(元) + */ + private BigDecimal loansMinAccount; + + /** + * 贷款最大金额(元) + */ + private BigDecimal loansMaxAccount; + + /** + * 贷款初始金额(元) + */ + private BigDecimal loansInitAccount; + + /** + * 允许选择月份 + */ + private String loansMonth; + + /** + * 初始选择月份 + */ + private String loansInitMonth; + + /** + * 每月还款日 + */ + private Integer dueDate; + + /** + * 服务费率 + */ + private String serviceRate; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/dto/admin/req/BorrowUpdateStatusReq.java b/bashi-dk/src/main/java/com/bashi/dk/dto/admin/req/BorrowUpdateStatusReq.java new file mode 100644 index 0000000..310efa1 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/dto/admin/req/BorrowUpdateStatusReq.java @@ -0,0 +1,13 @@ +package com.bashi.dk.dto.admin.req; + +import lombok.Data; + +@Data +public class BorrowUpdateStatusReq { + private Long id; + private Integer usedRemit; + private String borrowName; + private String borrowRemark; + private String borrowNameStyle; + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/dto/admin/req/UpdatePwdCustomerReq.java b/bashi-dk/src/main/java/com/bashi/dk/dto/admin/req/UpdatePwdCustomerReq.java new file mode 100644 index 0000000..75e2910 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/dto/admin/req/UpdatePwdCustomerReq.java @@ -0,0 +1,9 @@ +package com.bashi.dk.dto.admin.req; + +import lombok.Data; + +@Data +public class UpdatePwdCustomerReq { + private Long customerId; + private String password; +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/dto/admin/resp/BorrowResp.java b/bashi-dk/src/main/java/com/bashi/dk/dto/admin/resp/BorrowResp.java new file mode 100644 index 0000000..f63a567 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/dto/admin/resp/BorrowResp.java @@ -0,0 +1,10 @@ +package com.bashi.dk.dto.admin.resp; + +import com.bashi.dk.domain.Borrow; +import lombok.Data; + +@Data +public class BorrowResp extends Borrow { + + private String customerLoginName; +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/dto/admin/resp/CustomerAdminResp.java b/bashi-dk/src/main/java/com/bashi/dk/dto/admin/resp/CustomerAdminResp.java new file mode 100644 index 0000000..5b14d7e --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/dto/admin/resp/CustomerAdminResp.java @@ -0,0 +1,11 @@ +package com.bashi.dk.dto.admin.resp; + +import com.bashi.common.core.domain.entity.Customer; +import lombok.Data; + +@Data +public class CustomerAdminResp extends Customer { + private String realName; + + private Boolean allowSignature = true; +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/dto/admin/resp/CustomerExportVo.java b/bashi-dk/src/main/java/com/bashi/dk/dto/admin/resp/CustomerExportVo.java new file mode 100644 index 0000000..38f5a9d --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/dto/admin/resp/CustomerExportVo.java @@ -0,0 +1,116 @@ +package com.bashi.dk.dto.admin.resp; + +import com.bashi.common.annotation.Excel; +import lombok.Data; + +@Data +public class CustomerExportVo { + + @Excel(name = "登陆手机号") + private String phoneNumber; + + /** + * 用户名称 + */ + @Excel(name = "登陆手机号") + private String nickName; + + /** + * 是否实名 + */ + @Excel(name = "是否实名",readConverterExp = "0=否,1=是") + private Integer realNameAuth; + + /** + * 是否贷款 + */ + @Excel(name = "是否贷款",readConverterExp = "0=否,1=是") + private Integer loansFlag; + + /** + * 是否提现 + */ + @Excel(name = "是否提现",readConverterExp = "0=否,1=是") + private Integer withdrawFlag; + + @Excel(name = "真实姓名") + private String realName; + + @Excel(name = "身份证号码") + private String cardNum; + + /** + * 单位名称 + */ + @Excel(name = "单位名称") + private String companyName; + + /** + * 职位 + */ + @Excel(name = "职位") + private String companyTitle; + + /** + * 单位电话 + */ + @Excel(name = "单位电话") + private String companyPhone; + + /** + * 工作年龄 + */ + @Excel(name = "工作年龄") + private String companyYear; + + /** + * 单位地址 + */ + @Excel(name = "单位地址") + private String companyAddress; + + /** + * 详细地址 + */ + @Excel(name = "详细地址") + private String companyAddressInfo; + + /** + * 现居住地址 + */ + @Excel(name = "现居住地址") + private String customerAddress; + + /** + * 详细地址 + */ + @Excel(name = "详细地址") + private String customerAddressInfo; + + @Excel(name = "亲属关系",readConverterExp = "1=父母,2=配偶,3=子女,4=祖父母") + private String kinsfolkRef; + /** + * 亲属姓名 + */ + @Excel(name = "亲属姓名") + private String kinsfolkName; + + /** + * 亲属电话 + */ + @Excel(name = "亲属电话") + private String kinsfolkPhone; + + + /** + * 开户银行 + */ + @Excel(name = "开户银行") + private String bankType; + + /** + * 银行卡号 + */ + @Excel(name = "银行卡号") + private String backCardNum; +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/dto/admin/resp/CustomerInfo.java b/bashi-dk/src/main/java/com/bashi/dk/dto/admin/resp/CustomerInfo.java new file mode 100644 index 0000000..ae570c1 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/dto/admin/resp/CustomerInfo.java @@ -0,0 +1,77 @@ +package com.bashi.dk.dto.admin.resp; + +import com.bashi.common.constant.DateConstant; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +public class CustomerInfo { + + /** + * ID + */ + private Long id; + + /** + * 手机 + */ + private String phoneNumber; + + /** + * 用户名称 + */ + private String nickName; + + /** + * 用户密码 + */ + private String password; + + /** + * 是否实名 + */ + private Integer realNameAuth; + + /** + * 是否贷款 + */ + private Integer loansFlag; + + /** + * 是否提现 + */ + private Integer withdrawFlag; + + /** + * 余额 + */ + private Long account; + + /** + * 可提现金额 + */ + private Long withdrawAccount; + + /** + * 状态 0-正常 1-封禁 + */ + private Integer status; + + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonFormat(pattern = DateConstant.PATTERN_DATETIME) + private LocalDateTime lastLoginTime; + + private String lastLoginIp; + + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonFormat(pattern = DateConstant.PATTERN_DATETIME) + private LocalDateTime updateTime; +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/dto/app/req/BorrowStartReq.java b/bashi-dk/src/main/java/com/bashi/dk/dto/app/req/BorrowStartReq.java new file mode 100644 index 0000000..9c2ea00 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/dto/app/req/BorrowStartReq.java @@ -0,0 +1,25 @@ +package com.bashi.dk.dto.app.req; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@ApiModel("发起贷款入参") +public class BorrowStartReq { + + @ApiModelProperty("客户ID") + private Long customerId; + + @ApiModelProperty("用途说明") + private String noteRemark; + + @ApiModelProperty("总贷款额") + private BigDecimal totalLoanMoney; + + @ApiModelProperty("还款月数") + private Integer totalMonth; + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/dto/app/req/CalLoanReq.java b/bashi-dk/src/main/java/com/bashi/dk/dto/app/req/CalLoanReq.java new file mode 100644 index 0000000..43aac31 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/dto/app/req/CalLoanReq.java @@ -0,0 +1,20 @@ +package com.bashi.dk.dto.app.req; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@ApiModel("贷款计算器") +public class CalLoanReq { + + @ApiModelProperty("总贷款额") + private BigDecimal totalLoanMoney; + + @ApiModelProperty("还款月数") + private Integer totalMonth; + + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/dto/app/req/CustomerRegisterReq.java b/bashi-dk/src/main/java/com/bashi/dk/dto/app/req/CustomerRegisterReq.java new file mode 100644 index 0000000..f160ce1 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/dto/app/req/CustomerRegisterReq.java @@ -0,0 +1,29 @@ +package com.bashi.dk.dto.app.req; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel("用户注册-入参") +public class CustomerRegisterReq { + + /** + * 手机 + */ + @ApiModelProperty("手机号") + private String phoneNumber; + + /** + * 用户密码 + */ + @ApiModelProperty("密码") + private String password; + + /** + * code + */ + @ApiModelProperty("验证码") + private String code; + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/dto/app/req/UpdatePwdOpenReq.java b/bashi-dk/src/main/java/com/bashi/dk/dto/app/req/UpdatePwdOpenReq.java new file mode 100644 index 0000000..6a14bac --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/dto/app/req/UpdatePwdOpenReq.java @@ -0,0 +1,17 @@ +package com.bashi.dk.dto.app.req; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel("用户忘记密码-修改密码入参") +public class UpdatePwdOpenReq { + + @ApiModelProperty("校验码") + private String checkCode; + @ApiModelProperty("密码") + private String password; + @ApiModelProperty("确认密码") + private String confirmPassword; +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/dto/app/resp/BorrowInfo.java b/bashi-dk/src/main/java/com/bashi/dk/dto/app/resp/BorrowInfo.java new file mode 100644 index 0000000..6f8361e --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/dto/app/resp/BorrowInfo.java @@ -0,0 +1,14 @@ +package com.bashi.dk.dto.app.resp; + +import com.bashi.dk.domain.Borrow; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel("贷款详情DTO") +public class BorrowInfo extends Borrow { + + @ApiModelProperty("贷款进度条") + private LoanProcessResp loanProcessResp; +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/dto/app/resp/BorrowStepResp.java b/bashi-dk/src/main/java/com/bashi/dk/dto/app/resp/BorrowStepResp.java new file mode 100644 index 0000000..7c70a79 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/dto/app/resp/BorrowStepResp.java @@ -0,0 +1,24 @@ +package com.bashi.dk.dto.app.resp; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@ApiModel("贷款进度") +@Data +public class BorrowStepResp { + + @ApiModelProperty("名称") + private String name; + + @ApiModelProperty("是否完成") + private boolean over = false; + + public BorrowStepResp(String name, boolean over) { + this.name = name; + this.over = over; + } + + public BorrowStepResp() { + } +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/dto/app/resp/CalLoanResp.java b/bashi-dk/src/main/java/com/bashi/dk/dto/app/resp/CalLoanResp.java new file mode 100644 index 0000000..0e0c9bc --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/dto/app/resp/CalLoanResp.java @@ -0,0 +1,28 @@ +package com.bashi.dk.dto.app.resp; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@ApiModel("计算利息返回数据") +public class CalLoanResp { + + @ApiModelProperty("贷款总额") + private BigDecimal totalLoanMoney; // + @ApiModelProperty("还款月份") + private int totalMonth; // + @ApiModelProperty("贷款年利率") + private double loanRate; // + + @ApiModelProperty("总利息数") + private BigDecimal totalInterest; // + @ApiModelProperty("还款总额") + private BigDecimal totalRepayment; // + @ApiModelProperty("首月还款额") + private BigDecimal firstRepayment; // + @ApiModelProperty("月均还款额") + private BigDecimal avgRepayment; // +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/dto/app/resp/LoanProcessResp.java b/bashi-dk/src/main/java/com/bashi/dk/dto/app/resp/LoanProcessResp.java new file mode 100644 index 0000000..555d9c2 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/dto/app/resp/LoanProcessResp.java @@ -0,0 +1,22 @@ +package com.bashi.dk.dto.app.resp; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +@Data +@ApiModel("贷款进度说明") +public class LoanProcessResp { + + @ApiModelProperty("贷款进度条") + private List borrowStep; + + @ApiModelProperty("借款状态样式") + private String borrowNameStyle; + + /** 借款说明 */ + @ApiModelProperty("借款说明") + private String borrowRemark; +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/dto/app/resp/LoanUser.java b/bashi-dk/src/main/java/com/bashi/dk/dto/app/resp/LoanUser.java new file mode 100644 index 0000000..bad405a --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/dto/app/resp/LoanUser.java @@ -0,0 +1,26 @@ +package com.bashi.dk.dto.app.resp; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel("贷款用户") +public class LoanUser { + /** + * 手机 + */ + @ApiModelProperty("手机号") + private String phone; + /** + * 金额 + */ + @ApiModelProperty("金额") + private String amount; + + /** + * 时间 + */ + @ApiModelProperty("贷款时间") + private String time; +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/dto/app/resp/LoansSettingVO.java b/bashi-dk/src/main/java/com/bashi/dk/dto/app/resp/LoansSettingVO.java new file mode 100644 index 0000000..7307154 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/dto/app/resp/LoansSettingVO.java @@ -0,0 +1,53 @@ +package com.bashi.dk.dto.app.resp; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@ApiModel("费率设置-出参") +public class LoansSettingVO { + /** + * 贷款最小金额(元) + */ + @ApiModelProperty("贷款最小金额(元)") + private BigDecimal loansMinAccount; + + /** + * 贷款最大金额(元) + */ + @ApiModelProperty("贷款最大金额(元)") + private BigDecimal loansMaxAccount; + + /** + * 贷款初始金额(元) + */ + @ApiModelProperty("贷款初始金额(元)") + private BigDecimal loansInitAccount; + + /** + * 允许选择月份 + */ + @ApiModelProperty("允许选择月份") + private String loansMonth; + + /** + * 初始选择月份 + */ + @ApiModelProperty("初始选择月份") + private String loansInitMonth; + + /** + * 每月还款日 + */ + @ApiModelProperty("每月还款日") + private Integer dueDate; + + /** + * 最小 日息 + */ + @ApiModelProperty("最小 日息") + private Double minDayServiceRate; +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/enums/BankTypeEnums.java b/bashi-dk/src/main/java/com/bashi/dk/enums/BankTypeEnums.java new file mode 100644 index 0000000..29cf991 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/enums/BankTypeEnums.java @@ -0,0 +1,25 @@ +package com.bashi.dk.enums; + +public enum BankTypeEnums { + PBC("中国人民银行"), + ICBC("中国工商银行"), + CCB("中国建设银行"), + HSBC("汇丰银行"), + BOC("中国银行"), + ABC("中国农业银行"), + BC("交通银行"), + ZS_CMB("招商银行"), + CMB("中国民生银行"), + CITIC("中信银行"), + ; + + private final String name; + + BankTypeEnums(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/kit/CalLoanManager.java b/bashi-dk/src/main/java/com/bashi/dk/kit/CalLoanManager.java new file mode 100644 index 0000000..2916cc6 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/kit/CalLoanManager.java @@ -0,0 +1,42 @@ +package com.bashi.dk.kit; + +import com.bashi.dk.domain.LoansSetting; +import com.bashi.dk.dto.app.req.CalLoanReq; +import com.bashi.dk.service.LoansSettingService; +import com.bashi.dk.util.ACPIMLoanCalculator; +import com.bashi.dk.util.Loan; +import com.bashi.dk.util.LoanUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; + +@Component +public class CalLoanManager { + @Autowired + private LoansSettingService loansSettingService; + + public Loan calLoan(BigDecimal totalLoanMoney,Integer totalMonth, Double loanRate){ + ACPIMLoanCalculator calculator = new ACPIMLoanCalculator(); + Loan loan = calculator.calLoan(totalLoanMoney, totalMonth, loanRate, LoanUtil.RATE_TYPE_MONTH); + loan.setLoanRateMonth(loanRate); + return loan; + } + + public Loan calLoan(CalLoanReq calLoanReq){ + Double loanRate = getLoanRate(calLoanReq.getTotalMonth()); + return this.calLoan(calLoanReq.getTotalLoanMoney(), calLoanReq.getTotalMonth(), loanRate); + } + + public Double getLoanRate(Integer mouth){ + LoansSetting loansSetting = loansSettingService.getLoansSetting(); + String serviceRate = loansSetting.getServiceRate(); + String[] split = serviceRate.split(","); + if(split.length < mouth){ + return Double.valueOf(split[split.length-1]); + }else{ + return Double.valueOf(split[mouth-1]); + } + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/kit/DkLoginKit.java b/bashi-dk/src/main/java/com/bashi/dk/kit/DkLoginKit.java new file mode 100644 index 0000000..43b8c27 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/kit/DkLoginKit.java @@ -0,0 +1,80 @@ +package com.bashi.dk.kit; + +import com.bashi.common.constant.Constants; +import com.bashi.common.core.domain.entity.Customer; +import com.bashi.common.core.domain.entity.SysUser; +import com.bashi.common.core.domain.model.LoginPhoneBody; +import com.bashi.common.core.domain.model.LoginUser; +import com.bashi.common.exception.CustomException; +import com.bashi.common.utils.DateUtils; +import com.bashi.common.utils.ServletUtils; +import com.bashi.dk.service.CustomerService; +import com.bashi.framework.security.sms.LoginTypeEnums; +import com.bashi.framework.security.sms.SmsAuthenticationToken; +import com.bashi.framework.web.service.AsyncService; +import com.bashi.framework.web.service.TokenService; +import com.bashi.system.service.ISysUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.time.LocalDateTime; + +@Component +public class DkLoginKit { + + @Resource + private AuthenticationManager authenticationManager; + @Autowired + private AsyncService asyncService; + @Autowired + private TokenService tokenService; + @Autowired + private ISysUserService userService; + @Autowired + private CustomerService customerService; + + public String login(LoginPhoneBody mobile) { + HttpServletRequest request = ServletUtils.getRequest(); + // 用户验证 + Authentication authentication; + try { + authentication = authenticationManager + .authenticate(new SmsAuthenticationToken(mobile)); + } catch (Exception e) { + asyncService.recordLogininfor(mobile.getMobile(), Constants.LOGIN_FAIL, e.getMessage(), request); + throw new CustomException(e.getMessage()); + } + asyncService.recordLogininfor(mobile.getMobile(), Constants.LOGIN_SUCCESS, LoginTypeEnums.getMsgByCode(mobile.getLoginRole()) + "登陆成功", request); + LoginUser loginUser = (LoginUser) authentication.getPrincipal(); + recordLoginInfo(loginUser); + // 生成token + return tokenService.createToken(loginUser); + } + + + /** + * 记录登录信息 + */ + public void recordLoginInfo(LoginUser user) { + if(user.getType() == 1){ + Customer customer = user.getCustomer(); + Customer update = new Customer(); + update.setId(customer.getId()); + update.setLastLoginIp(ServletUtils.getClientIP()); + update.setLastLoginTime(LocalDateTime.now()); + customerService.updateById(update); + }else{ + SysUser sysUser = user.getUser(); + sysUser.setLoginIp(ServletUtils.getClientIP()); + sysUser.setLoginDate(DateUtils.getNowDate()); + sysUser.setUpdateBy(sysUser.getUserName()); + userService.updateUserProfile(sysUser); + } + + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/manager/FileUploadManager.java b/bashi-dk/src/main/java/com/bashi/dk/manager/FileUploadManager.java new file mode 100644 index 0000000..d006fa9 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/manager/FileUploadManager.java @@ -0,0 +1,55 @@ +package com.bashi.dk.manager; + +import cn.hutool.core.lang.UUID; +import com.bashi.common.config.BsConfig; +import com.bashi.common.utils.file.FileUploadUtils; +import com.bashi.dk.oss.ali.AliOssKit; +import com.bashi.dk.oss.ali.CosKit; +import com.bashi.framework.config.ServerConfig; +import org.apache.commons.io.FilenameUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +@Component +public class FileUploadManager { + @Autowired + private AliOssKit aliOssKit; + @Autowired + private CosKit cosKit; + @Autowired + private ServerConfig serverConfig; + + + public FileUploadRes uploadFile(MultipartFile file) throws Exception { + if(aliOssKit.getOssClient() != null){ + String filename = file.getOriginalFilename(); + String extension = FilenameUtils.getExtension(filename); + String uuid = UUID.fastUUID().toString(); + String ossUrl = aliOssKit.upload(file.getInputStream(), AliOssKit.OSS_KEY_COMMON + uuid + "." + extension); + FileUploadRes fileUploadRes = new FileUploadRes(); + fileUploadRes.setUrl(ossUrl); + fileUploadRes.setFileName(filename); + return fileUploadRes; + }else if(cosKit.isEnable()){ + String filename = file.getOriginalFilename(); + String extension = FilenameUtils.getExtension(filename); + String uuid = UUID.fastUUID().toString(); + String ossUrl = cosKit.upload(file, AliOssKit.OSS_KEY_COMMON + uuid + "." + extension); + FileUploadRes fileUploadRes = new FileUploadRes(); + fileUploadRes.setUrl(ossUrl); + fileUploadRes.setFileName(filename); + return fileUploadRes; + }else { + // 上传文件路径 + String filePath = BsConfig.getUploadPath(); + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + String url = serverConfig.getUrl() + "/api/" + fileName; + FileUploadRes fileUploadRes = new FileUploadRes(); + fileUploadRes.setUrl(url); + fileUploadRes.setFileName(fileName); + return fileUploadRes; + } + } +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/manager/FileUploadRes.java b/bashi-dk/src/main/java/com/bashi/dk/manager/FileUploadRes.java new file mode 100644 index 0000000..1d39618 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/manager/FileUploadRes.java @@ -0,0 +1,10 @@ +package com.bashi.dk.manager; + +import lombok.Data; + +@Data +public class FileUploadRes { + + private String url; + private String fileName; +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/mapper/AgreementSettingMapper.java b/bashi-dk/src/main/java/com/bashi/dk/mapper/AgreementSettingMapper.java new file mode 100644 index 0000000..ad05c2d --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/mapper/AgreementSettingMapper.java @@ -0,0 +1,7 @@ +package com.bashi.dk.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.bashi.dk.domain.AgreementSetting; + +public interface AgreementSettingMapper extends BaseMapper { +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/mapper/BorrowLogMapper.java b/bashi-dk/src/main/java/com/bashi/dk/mapper/BorrowLogMapper.java new file mode 100644 index 0000000..6155099 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/mapper/BorrowLogMapper.java @@ -0,0 +1,7 @@ +package com.bashi.dk.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.bashi.dk.domain.BorrowLog; + +public interface BorrowLogMapper extends BaseMapper { +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/mapper/BorrowMapper.java b/bashi-dk/src/main/java/com/bashi/dk/mapper/BorrowMapper.java new file mode 100644 index 0000000..55ffdfb --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/mapper/BorrowMapper.java @@ -0,0 +1,11 @@ +package com.bashi.dk.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.bashi.dk.domain.Borrow; +import com.bashi.dk.dto.admin.resp.BorrowResp; +import org.apache.ibatis.annotations.Param; + +public interface BorrowMapper extends BaseMapper { + IPage pageAdmin(@Param("page") IPage page, @Param("bo") Borrow bo); +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/mapper/BorrowStatusMapper.java b/bashi-dk/src/main/java/com/bashi/dk/mapper/BorrowStatusMapper.java new file mode 100644 index 0000000..f814816 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/mapper/BorrowStatusMapper.java @@ -0,0 +1,7 @@ +package com.bashi.dk.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.bashi.dk.domain.BorrowStatus; + +public interface BorrowStatusMapper extends BaseMapper { +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/mapper/CustomerInfoMapper.java b/bashi-dk/src/main/java/com/bashi/dk/mapper/CustomerInfoMapper.java new file mode 100644 index 0000000..269e21b --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/mapper/CustomerInfoMapper.java @@ -0,0 +1,7 @@ +package com.bashi.dk.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.bashi.dk.domain.CustomerInfo; + +public interface CustomerInfoMapper extends BaseMapper { +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/mapper/CustomerMapper.java b/bashi-dk/src/main/java/com/bashi/dk/mapper/CustomerMapper.java new file mode 100644 index 0000000..ddc1b60 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/mapper/CustomerMapper.java @@ -0,0 +1,23 @@ +package com.bashi.dk.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.bashi.common.core.domain.entity.Customer; +import com.bashi.dk.dto.admin.resp.CustomerAdminResp; +import com.bashi.dk.dto.admin.resp.CustomerExportVo; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; +import java.util.List; + +public interface CustomerMapper extends BaseMapper { + + void incsAmount(@Param("customerId") Long customerId, @Param("totalLoanMoney") BigDecimal totalLoanMoney, + @Param("totalRepayment") BigDecimal totalRepayment); + + void withdraw(@Param("customerId") Long customerId, @Param("withdrawAmount") Double withdrawAmount); + + IPage pageAdmin(@Param("page") IPage page, @Param("bo") CustomerAdminResp bo); + + List exportAdmin(@Param("bo") CustomerAdminResp bo); +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/mapper/HomeSettingMapper.java b/bashi-dk/src/main/java/com/bashi/dk/mapper/HomeSettingMapper.java new file mode 100644 index 0000000..c77bffd --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/mapper/HomeSettingMapper.java @@ -0,0 +1,7 @@ +package com.bashi.dk.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.bashi.dk.domain.HomeSetting; + +public interface HomeSettingMapper extends BaseMapper { +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/mapper/LoansSettingMapper.java b/bashi-dk/src/main/java/com/bashi/dk/mapper/LoansSettingMapper.java new file mode 100644 index 0000000..1dd3dca --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/mapper/LoansSettingMapper.java @@ -0,0 +1,7 @@ +package com.bashi.dk.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.bashi.dk.domain.LoansSetting; + +public interface LoansSettingMapper extends BaseMapper { +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/oss/ali/AliOssKit.java b/bashi-dk/src/main/java/com/bashi/dk/oss/ali/AliOssKit.java new file mode 100644 index 0000000..2ca1404 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/oss/ali/AliOssKit.java @@ -0,0 +1,79 @@ +package com.bashi.dk.oss.ali; + +import com.aliyun.oss.ClientConfiguration; +import com.aliyun.oss.OSSClient; +import com.aliyun.oss.common.auth.CredentialsProvider; +import com.aliyun.oss.common.auth.DefaultCredentialProvider; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.io.InputStream; + +/** + *

created on 2021/3/3

+ * + * @author zhangliang + */ +@Component +@Slf4j +public class AliOssKit { + + @Autowired + @Setter + @Getter + private OssConfig ossConfig; + + @Getter + public OSSClient ossClient = null; + + public final static String OSS_KEY_COMMON = "dk/common/"; + + @PostConstruct + public void init(){ + if(!ossConfig.isEnable()){ + log.error("未开启阿里云OSS配置"); + return; + } + // 创建ClientConfiguration。ClientConfiguration是OSSClient的配置类,可配置代理、连接超时、最大连接数等参数。 + ClientConfiguration conf = new ClientConfiguration(); + // 设置OSSClient允许打开的最大HTTP连接数,默认为1024个。 + conf.setMaxConnections(1024); + // 设置Socket层传输数据的超时时间,默认为50000毫秒。 + conf.setSocketTimeout(50000); + // 设置建立连接的超时时间,默认为50000毫秒。 + conf.setConnectionTimeout(50000); + // 设置从连接池中获取连接的超时时间(单位:毫秒),默认不超时。 + conf.setConnectionRequestTimeout(1000); + // 设置连接空闲超时时间。超时则关闭连接,默认为60000毫秒。 + conf.setIdleConnectionTime(60000); + // 设置失败请求重试次数,默认为3次。 + conf.setMaxErrorRetry(5); + CredentialsProvider credentialsProvider = new DefaultCredentialProvider(ossConfig.getAccessKeyId(), ossConfig.getAccessKeySecret()); + // 创建客户端 + ossClient = new OSSClient(ossConfig.getEndpoint(), credentialsProvider, conf); + } + + public String upload(InputStream inputStream,String fileName){ + ossClient.putObject(ossConfig.getBucketName(), fileName, inputStream); + return getUrl(fileName); + } + + /** + * 上传单个文件 公开文件 + * @param inputStream + * @param key + */ + public String uploadByKey(InputStream inputStream,String key){ + ossClient.putObject(ossConfig.getBucketName(), key, inputStream); + return getUrl(key); + } + + public String getUrl(String key){ + return ossConfig.getCdnDomain()+"/"+key; + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/oss/ali/CosConfig.java b/bashi-dk/src/main/java/com/bashi/dk/oss/ali/CosConfig.java new file mode 100644 index 0000000..666e4e9 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/oss/ali/CosConfig.java @@ -0,0 +1,23 @@ +package com.bashi.dk.oss.ali; + + +import lombok.Data; +import lombok.ToString; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@ToString(exclude={"secretId","secretKey"}) +@Configuration +@ConfigurationProperties(prefix = "tencent.cos") +public class CosConfig { + + private boolean enable = false; + private String appId; + private String region; // 连接区域地址 + private String cdnDomain; // cdn 域名 + private String secretId; // 连接keyId + private String secretKey; // 连接秘钥 + private String bucketName; // 需要存储的bucketName + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/oss/ali/CosKit.java b/bashi-dk/src/main/java/com/bashi/dk/oss/ali/CosKit.java new file mode 100644 index 0000000..0226225 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/oss/ali/CosKit.java @@ -0,0 +1,105 @@ +package com.bashi.dk.oss.ali; + +import com.qcloud.cos.COSClient; +import com.qcloud.cos.ClientConfig; +import com.qcloud.cos.auth.BasicCOSCredentials; +import com.qcloud.cos.auth.COSCredentials; +import com.qcloud.cos.http.HttpProtocol; +import com.qcloud.cos.model.ObjectMetadata; +import com.qcloud.cos.model.PutObjectRequest; +import com.qcloud.cos.model.UploadResult; +import com.qcloud.cos.region.Region; +import com.qcloud.cos.transfer.TransferManager; +import com.qcloud.cos.transfer.TransferManagerConfiguration; +import com.qcloud.cos.transfer.Upload; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.PostConstruct; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Component +@Slf4j +public class CosKit { + + @Autowired + private CosConfig cosConfig; + private COSClient cosClient = null; + + private TransferManager transferManager = null; + + + public TransferManager createTransferManager(COSClient cosClient){ + // 自定义线程池大小,建议在客户端与 COS 网络充足(例如使用腾讯云的 CVM,同地域上传 COS)的情况下,设置成16或32即可,可较充分的利用网络资源 + // 对于使用公网传输且网络带宽质量不高的情况,建议减小该值,避免因网速过慢,造成请求超时。 + ExecutorService threadPool = Executors.newFixedThreadPool(4); + // 传入一个 threadpool, 若不传入线程池,默认 TransferManager 中会生成一个单线程的线程池。 + TransferManager transferManager = new TransferManager(cosClient, threadPool); + // 设置高级接口的配置项 + // 分块上传阈值和分块大小分别为 5MB 和 1MB + TransferManagerConfiguration transferManagerConfiguration = new TransferManagerConfiguration(); + transferManagerConfiguration.setMultipartUploadThreshold(5*1024*1024); + transferManagerConfiguration.setMinimumUploadPartSize(1*1024*1024); + transferManager.setConfiguration(transferManagerConfiguration); + return transferManager; + } + + public COSClient createCosClient(){ + // SECRETID 和 SECRETKEY 请登录访问管理控制台 https://console.cloud.tencent.com/cam/capi 进行查看和管理 + String secretId = cosConfig.getSecretId(); + String secretKey = cosConfig.getSecretKey(); + COSCredentials cred = new BasicCOSCredentials(secretId, secretKey); + // ClientConfig 中包含了后续请求 COS 的客户端设置: + ClientConfig clientConfig = new ClientConfig(); + // 设置 bucket 的地域 + // COS_REGION 请参照 https://cloud.tencent.com/document/product/436/6224 + clientConfig.setRegion(new Region(cosConfig.getRegion())); + // 设置请求协议, http 或者 https + // 5.6.53 及更低的版本,建议设置使用 https 协议 + // 5.6.54 及更高版本,默认使用了 https + clientConfig.setHttpProtocol(HttpProtocol.https); + // 以下的设置,是可选的: + // 设置 socket 读取超时,默认 30s + clientConfig.setSocketTimeout(30*1000); + // 设置建立连接超时,默认 30s + clientConfig.setConnectionTimeout(30*1000); + // 如果需要的话,设置 http 代理,ip 以及 port +// clientConfig.setHttpProxyIp("httpProxyIp"); +// clientConfig.setHttpProxyPort(80); + // 生成 cos 客户端。 + return new COSClient(cred, clientConfig); + } + + public boolean isEnable(){ + return cosConfig.isEnable(); + } + + @PostConstruct + public void init(){ + if(!cosConfig.isEnable()){ + log.error("未开启腾讯云COS配置"); + return; + } + cosClient = createCosClient(); + transferManager = createTransferManager(cosClient); + } + + public String upload(MultipartFile file, String fileName) throws Exception { + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentLength(file.getSize()); + PutObjectRequest putObjectRequest = new PutObjectRequest(cosConfig.getBucketName(), fileName, file.getInputStream(),metadata); + Upload upload = transferManager.upload(putObjectRequest); + UploadResult uploadResult = upload.waitForUploadResult(); + return getUrl(uploadResult.getKey()); + } + + + + public String getUrl(String key){ + return cosConfig.getCdnDomain()+"/"+key; + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/oss/ali/OssConfig.java b/bashi-dk/src/main/java/com/bashi/dk/oss/ali/OssConfig.java new file mode 100644 index 0000000..89635f2 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/oss/ali/OssConfig.java @@ -0,0 +1,24 @@ +package com.bashi.dk.oss.ali; + +import lombok.Data; +import lombok.ToString; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + *

created on 2021/3/3

+ * + * @author zhangliang + */ +@Data +@ToString(exclude={"accessKeyId","accessKeySecret"}) +@Configuration +@ConfigurationProperties(prefix = "ali.oss") +public class OssConfig { + private boolean enable = false; + private String endpoint; // 连接区域地址 + private String cdnDomain; // cdn 域名 + private String accessKeyId; // 连接keyId + private String accessKeySecret; // 连接秘钥 + private String bucketName; // 需要存储的bucketName +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/service/AgreementSettingService.java b/bashi-dk/src/main/java/com/bashi/dk/service/AgreementSettingService.java new file mode 100644 index 0000000..88b57d8 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/service/AgreementSettingService.java @@ -0,0 +1,8 @@ +package com.bashi.dk.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.dk.domain.AgreementSetting; + +public interface AgreementSettingService extends IService { + AgreementSetting getAgreementSetting(); +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/service/BorrowService.java b/bashi-dk/src/main/java/com/bashi/dk/service/BorrowService.java new file mode 100644 index 0000000..ae0b00f --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/service/BorrowService.java @@ -0,0 +1,32 @@ +package com.bashi.dk.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.common.com.PageParams; +import com.bashi.dk.domain.Borrow; +import com.bashi.dk.dto.admin.req.BorrowUpdateStatusReq; +import com.bashi.dk.dto.admin.resp.BorrowResp; +import com.bashi.dk.dto.app.req.BorrowStartReq; +import com.bashi.dk.dto.app.resp.LoanProcessResp; + +public interface BorrowService extends IService { + Borrow borrow(BorrowStartReq req); + + Borrow getByTradeNo(String tradeNo); + + boolean updateLoan(Borrow bo); + + boolean updateStatus(BorrowUpdateStatusReq bo); + + boolean updateBank(Borrow bo); + + void withdraw(Double withdrawAmount, Long customerId); + + LoanProcessResp getStepBorrow(Long customerId); + + LoanProcessResp parseStepBorrow(Borrow one); + + IPage pageAdmin(PageParams pageParams, Borrow bo); + + String getContract(String tradeNo); +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/service/BorrowStatusService.java b/bashi-dk/src/main/java/com/bashi/dk/service/BorrowStatusService.java new file mode 100644 index 0000000..c956922 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/service/BorrowStatusService.java @@ -0,0 +1,7 @@ +package com.bashi.dk.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.dk.domain.BorrowStatus; + +public interface BorrowStatusService extends IService { +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/service/CustomerInfoService.java b/bashi-dk/src/main/java/com/bashi/dk/service/CustomerInfoService.java new file mode 100644 index 0000000..a9b77e2 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/service/CustomerInfoService.java @@ -0,0 +1,12 @@ +package com.bashi.dk.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.dk.domain.CustomerInfo; + +public interface CustomerInfoService extends IService { + void updateCustomerInfo(CustomerInfo customerInfo); + + CustomerInfo getByCustomerId(Long customerId); + + boolean updateAllowSignature(CustomerInfo bo); +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/service/CustomerService.java b/bashi-dk/src/main/java/com/bashi/dk/service/CustomerService.java new file mode 100644 index 0000000..912d970 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/service/CustomerService.java @@ -0,0 +1,26 @@ +package com.bashi.dk.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.common.com.PageParams; +import com.bashi.common.core.domain.entity.Customer; +import com.bashi.dk.dto.admin.resp.CustomerAdminResp; +import com.bashi.dk.dto.app.req.CustomerRegisterReq; + +import java.math.BigDecimal; + +public interface CustomerService extends IService { + Customer getCustomerByName(String mobile); + + void register(CustomerRegisterReq register); + + boolean updatePwd(Long id, String password); + + void borrowAmount(Long customerId, BigDecimal totalLoanMoney,BigDecimal totalRepayment); + + boolean withdraw(Long customerId, Double withdrawAmount); + + void dk(Long customerId); + + IPage pageAdmin(PageParams pageParams, CustomerAdminResp bo); +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/service/HomeSettingService.java b/bashi-dk/src/main/java/com/bashi/dk/service/HomeSettingService.java new file mode 100644 index 0000000..a869d8b --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/service/HomeSettingService.java @@ -0,0 +1,8 @@ +package com.bashi.dk.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.dk.domain.HomeSetting; + +public interface HomeSettingService extends IService { + HomeSetting getHomeSetting(); +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/service/LoansSettingService.java b/bashi-dk/src/main/java/com/bashi/dk/service/LoansSettingService.java new file mode 100644 index 0000000..c2573ca --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/service/LoansSettingService.java @@ -0,0 +1,8 @@ +package com.bashi.dk.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.dk.domain.LoansSetting; + +public interface LoansSettingService extends IService { + LoansSetting getLoansSetting(); +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/service/impl/AgreementSettingServiceImpl.java b/bashi-dk/src/main/java/com/bashi/dk/service/impl/AgreementSettingServiceImpl.java new file mode 100644 index 0000000..c9b38cb --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/service/impl/AgreementSettingServiceImpl.java @@ -0,0 +1,18 @@ +package com.bashi.dk.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.dk.domain.AgreementSetting; +import com.bashi.dk.mapper.AgreementSettingMapper; +import com.bashi.dk.service.AgreementSettingService; +import org.springframework.stereotype.Service; + +@Service +public class AgreementSettingServiceImpl extends ServiceImpl implements AgreementSettingService { + + @Override + public AgreementSetting getAgreementSetting(){ + return this.getOne(Wrappers.lambdaQuery(AgreementSetting.class).last("limit 1")); + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/service/impl/BorrowServiceImpl.java b/bashi-dk/src/main/java/com/bashi/dk/service/impl/BorrowServiceImpl.java new file mode 100644 index 0000000..5fec6b0 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/service/impl/BorrowServiceImpl.java @@ -0,0 +1,268 @@ +package com.bashi.dk.service.impl; + +import cn.hutool.core.lang.hash.Hash; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.common.com.Condition; +import com.bashi.common.com.PageParams; +import com.bashi.common.core.domain.entity.Customer; +import com.bashi.common.exception.CustomException; +import com.bashi.common.utils.BeanConvertUtil; +import com.bashi.common.utils.JsonUtils; +import com.bashi.dk.domain.AgreementSetting; +import com.bashi.dk.domain.Borrow; +import com.bashi.dk.domain.CustomerInfo; +import com.bashi.dk.domain.LoansSetting; +import com.bashi.dk.dto.admin.req.BorrowUpdateStatusReq; +import com.bashi.dk.dto.admin.resp.BorrowResp; +import com.bashi.dk.dto.app.req.BorrowStartReq; +import com.bashi.dk.dto.app.req.CalLoanReq; +import com.bashi.dk.dto.app.resp.BorrowStepResp; +import com.bashi.dk.dto.app.resp.LoanProcessResp; +import com.bashi.dk.kit.CalLoanManager; +import com.bashi.dk.mapper.BorrowMapper; +import com.bashi.dk.service.*; +import com.bashi.dk.util.ContentReplaceUtil; +import com.bashi.dk.util.Loan; +import com.bashi.dk.util.MoneyUtil; +import com.bashi.dk.util.OrderTradeNoUtil; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class BorrowServiceImpl extends ServiceImpl implements BorrowService { + + @Autowired + private CustomerService customerService; + @Autowired + private CustomerInfoService customerInfoService; + @Autowired + private CalLoanManager calLoanManager; + @Autowired + private LoansSettingService loansSettingService; + + + + @Override + public Borrow borrow(BorrowStartReq req) { + Customer customer = customerService.getById(req.getCustomerId()); + CustomerInfo customerInfo = customerInfoService.getByCustomerId(req.getCustomerId()); + if(customerInfo == null || customer == null){ + throw new CustomException("用户不存在"); + } + if(customer.getLoansFlag() == 1){ + throw new CustomException("请等待上一次贷款完结后在发起贷款"); + } + if(customer.getRealNameAuth() != 1){ + throw new CustomException("请补全个人资料后在发起贷款"); + } + LoansSetting loansSetting = loansSettingService.getLoansSetting(); + CalLoanReq calLoanReq = new CalLoanReq(); + calLoanReq.setTotalLoanMoney(req.getTotalLoanMoney()); + calLoanReq.setTotalMonth(req.getTotalMonth()); + Loan loan = calLoanManager.calLoan(calLoanReq); + Borrow borrow = BeanConvertUtil.convertTo(customerInfo, Borrow::new); + borrow.setId(null); + borrow.setTradeNo(OrderTradeNoUtil.createOrder(OrderTradeNoUtil.BORROW)); + borrow.setTotalLoanMoney(loan.getTotalLoanMoney()); + borrow.setTotalMonth(loan.getTotalMonth()); + borrow.setLoanMonthRate(loan.getLoanRateMonth()); + borrow.setLoanYearRate(loan.getLoanRate()); + borrow.setTotalInterest(loan.getTotalInterest()); + borrow.setTotalRepayment(loan.getTotalRepayment()); + borrow.setFirstRepayment(loan.getFirstRepayment()); + borrow.setAvgRepayment(loan.getAvgRepayment()); + borrow.setDueDate(loansSetting.getDueDate()); + borrow.setNoteRemark(req.getNoteRemark()); + borrow.setBorrowName("审核中"); + borrow.setBorrowNameStyle("black"); + borrow.setBorrowRemark("审核中..."); + borrow.setInfoJson(JsonUtils.toJsonString(loan)); + borrow.setFirstBankType(borrow.getBankType()); + borrow.setFirstBackCardNum(borrow.getBackCardNum()); + this.save(borrow); + customerService.dk(customer.getId()); + return borrow; + } + + @Override + public Borrow getByTradeNo(String tradeNo) { + return this.getOne(Wrappers.lambdaQuery(Borrow.class) + .eq(Borrow::getTradeNo,tradeNo).last("limit 1")); + } + + @Override + public boolean updateLoan(Borrow bo) { + Loan loan = calLoanManager.calLoan(bo.getTotalLoanMoney(),bo.getTotalMonth(),bo.getLoanMonthRate()); + Borrow update = new Borrow(); + update.setId(bo.getId()); + update.setTotalLoanMoney(loan.getTotalLoanMoney()); + update.setTotalMonth(loan.getTotalMonth()); + update.setLoanMonthRate(loan.getLoanRateMonth()); + update.setLoanYearRate(loan.getLoanRate()); + update.setTotalInterest(loan.getTotalInterest()); + update.setTotalRepayment(loan.getTotalRepayment()); + update.setFirstRepayment(loan.getFirstRepayment()); + update.setAvgRepayment(loan.getAvgRepayment()); + update.setInfoJson(JsonUtils.toJsonString(loan)); + return this.updateById(update); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateStatus(BorrowUpdateStatusReq bo) { + Borrow borrow = this.getById(bo.getId()); + boolean remit = false; + if(borrow.getRemitFlag() == 0 && bo.getUsedRemit() != null && bo.getUsedRemit() == 1){ + remit = true; + } + Borrow update = new Borrow(); + update.setId(bo.getId()); + update.setBorrowName(bo.getBorrowName()); + update.setBorrowRemark(bo.getBorrowRemark()); + update.setBorrowNameStyle(bo.getBorrowNameStyle()); + update.setAuditFlag(true); + if(remit){ + update.setRemitFlag(1); + } + boolean bool = this.updateById(update); + if(bool && remit){ + customerService.borrowAmount(borrow.getCustomerId(), borrow.getTotalLoanMoney(),borrow.getTotalRepayment()); + return true; + } + return bool; + } + + @Override + public boolean updateBank(Borrow bo) { + this.updateById(bo); + Borrow borrow = this.getById(bo.getId()); + LambdaUpdateWrapper update = Wrappers.lambdaUpdate(CustomerInfo.class).eq(CustomerInfo::getCustomerId, borrow.getCustomerId()); + boolean updateFlag = false; + if(StringUtils.isNotEmpty(bo.getBankType())){ + update.set(CustomerInfo::getBankType,bo.getBankType()); + updateFlag = true; + } + if(StringUtils.isNotEmpty(bo.getBackCardNum())){ + update.set(CustomerInfo::getBackCardNum,bo.getBackCardNum()); + updateFlag = true; + } + if(updateFlag){ + customerInfoService.update(update); + } + return true; + } + + @Override + public void withdraw(Double withdrawAmount, Long customerId) { + Customer customer = customerService.getById(customerId); + if(BooleanUtils.isNotTrue(customer.getAllowWithdrawFlag())){ + throw new CustomException("提现失败,账号异常"); + } + if(customer.getAccount().doubleValue() < withdrawAmount){ + throw new CustomException("余额不足"); + } + Borrow one = this.getOne(Wrappers.lambdaQuery(Borrow.class).eq(Borrow::getCustomerId,customerId)); + if(one == null){ + throw new CustomException("提现失败"); + } + if(!"审核通过".equals(one.getBorrowName())){ + throw new CustomException(one.getBorrowName()); + } + boolean result = customerService.withdraw(customerId,withdrawAmount); + Borrow update = new Borrow(); + update.setId(one.getId()); + update.setBorrowName("提现中"); + update.setBorrowRemark("提现中..."); + update.setBorrowNameStyle("red"); + this.updateById(update); + } + + @Override + public LoanProcessResp getStepBorrow(Long customerId){ + Borrow one = this.getOne(Wrappers.lambdaQuery(Borrow.class).eq(Borrow::getCustomerId,customerId)); + return parseStepBorrow(one); + } + + @Override + public LoanProcessResp parseStepBorrow(Borrow one){ + LoanProcessResp resp = new LoanProcessResp(); + List borrowStep = new ArrayList<>(); + if(one == null){ + borrowStep.add(new BorrowStepResp("提交成功",false)); + borrowStep.add(new BorrowStepResp("正在审核",false)); + borrowStep.add(new BorrowStepResp("到账成功",false)); + resp.setBorrowStep(borrowStep); + resp.setBorrowNameStyle("red"); + resp.setBorrowRemark("您的个人信用贷款正在审核,请留意您的审核状态!如有疑问,请联系业务员咨询…"); + return resp; + } + borrowStep.add(new BorrowStepResp("提交成功",true)); + if("审核通过".equals(one.getBorrowName())){ + borrowStep.add(new BorrowStepResp(one.getBorrowName(),true)); + borrowStep.add(new BorrowStepResp("到账成功",true)); + }else{ + borrowStep.add(new BorrowStepResp(one.getBorrowName(),true)); + borrowStep.add(new BorrowStepResp("到账成功",false)); + } + resp.setBorrowStep(borrowStep); + resp.setBorrowNameStyle(one.getBorrowNameStyle()); + resp.setBorrowRemark(one.getBorrowRemark()); + return resp; + } + + @Override + public IPage pageAdmin(PageParams pageParams, Borrow bo) { + return baseMapper.pageAdmin(Condition.getPage(pageParams),bo); + } + + @Autowired + private AgreementSettingService agreementSettingService; + + @Override + public String getContract(String tradeNo) { + Borrow borrow = this.getByTradeNo(tradeNo); + if(borrow == null){ + throw new CustomException("借款不存在"); + } + CustomerInfo customerInfo = customerInfoService.getByCustomerId(borrow.getCustomerId()); + Customer customer = customerService.getById(borrow.getCustomerId()); + LocalDate startDate = borrow.getCreateTime().toLocalDate(); + Map map = new HashMap<>(); + map.put("合同编号",borrow.getTradeNo()); + map.put("签订日期",startDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + map.put("借款人用户名",StringUtils.isEmpty(customer.getPhoneNumber())?"":customer.getPhoneNumber()); + map.put("借款人身份证号",StringUtils.isEmpty(borrow.getCardNum())?"":borrow.getCardNum()); + map.put("借款人手机号",StringUtils.isEmpty(customer.getPhoneNumber())?"":customer.getPhoneNumber()); + String signatureImage = "
签名:
"; + if(StringUtils.isNotEmpty(customerInfo.getSignature())){ +// signatureImage = "\"无效\""; + signatureImage = "
签名:\"无效\"
"; + } + map.put("借款人签名",signatureImage); + map.put("借款金额大写", MoneyUtil.toChinese(borrow.getTotalLoanMoney().toString())); + map.put("借款金额小写",borrow.getTotalLoanMoney().toString()); + map.put("借款期限",borrow.getTotalMonth()); + map.put("借款开始日",startDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + map.put("借款结束日",startDate.plusMonths(borrow.getTotalMonth()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + map.put("借款人名称",StringUtils.isEmpty(borrow.getRealName())?"":borrow.getRealName()); + AgreementSetting agreementSetting = agreementSettingService.getAgreementSetting(); + String contractTemplate = agreementSetting.getContractTemplate(); + return ContentReplaceUtil.replaceWord(contractTemplate, map); + } + + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/service/impl/BorrowStatusServiceImpl.java b/bashi-dk/src/main/java/com/bashi/dk/service/impl/BorrowStatusServiceImpl.java new file mode 100644 index 0000000..c8a1bf9 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/service/impl/BorrowStatusServiceImpl.java @@ -0,0 +1,11 @@ +package com.bashi.dk.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.dk.domain.BorrowStatus; +import com.bashi.dk.mapper.BorrowStatusMapper; +import com.bashi.dk.service.BorrowStatusService; +import org.springframework.stereotype.Service; + +@Service +public class BorrowStatusServiceImpl extends ServiceImpl implements BorrowStatusService { +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/service/impl/CustomerInfoServiceImpl.java b/bashi-dk/src/main/java/com/bashi/dk/service/impl/CustomerInfoServiceImpl.java new file mode 100644 index 0000000..337c0ea --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/service/impl/CustomerInfoServiceImpl.java @@ -0,0 +1,86 @@ +package com.bashi.dk.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.common.core.domain.entity.Customer; +import com.bashi.dk.domain.CustomerInfo; +import com.bashi.dk.mapper.CustomerInfoMapper; +import com.bashi.dk.service.CustomerInfoService; +import com.bashi.dk.service.CustomerService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class CustomerInfoServiceImpl extends ServiceImpl implements CustomerInfoService { + @Autowired + private CustomerService customerService; + @Override + public void updateCustomerInfo(CustomerInfo customerInfo) { + Long customerId = customerInfo.getCustomerId(); + this.update(customerInfo,Wrappers.lambdaQuery(CustomerInfo.class) + .eq(CustomerInfo::getCustomerId, customerId)); + CustomerInfo one = this.getOne(Wrappers.lambdaQuery(CustomerInfo.class) + .eq(CustomerInfo::getCustomerId, customerId)); + checkCustomerInfoFlag(one); + } + + private void checkCustomerInfoFlag(CustomerInfo one){ + Long customerId = one.getCustomerId(); + int realNameAuth = 0; + boolean infoFlag = false; + boolean cardFlag = false; + boolean bankFlag = false; + boolean signFlag = false; + // 身份信息 + if(StringUtils.isNotEmpty(one.getRealName()) && StringUtils.isNotEmpty(one.getCardNum()) && + StringUtils.isNotEmpty(one.getCardFrontPicture()) && StringUtils.isNotEmpty(one.getCardBackPicture()) && + StringUtils.isNotEmpty(one.getHandCardPicture())){ + cardFlag = true; + } + // 资料信息 + if(StringUtils.isNotEmpty(one.getCompanyName()) && StringUtils.isNotEmpty(one.getCompanyTitle()) && + StringUtils.isNotEmpty(one.getCompanyPhone()) && StringUtils.isNotEmpty(one.getCompanyYear()) && + StringUtils.isNotEmpty(one.getCompanyAddress()) && StringUtils.isNotEmpty(one.getCompanyAddressInfo()) && + StringUtils.isNotEmpty(one.getKinsfolkName()) && StringUtils.isNotEmpty(one.getKinsfolkRef()) && + StringUtils.isNotEmpty(one.getKinsfolkPhone()) && one.getIncomeWan() != null){ + infoFlag = true; + } + // 收款银行卡 + if(StringUtils.isNotEmpty(one.getBankType()) && StringUtils.isNotEmpty(one.getBackCardNum())){ + bankFlag = true; + } + if(!one.getAllowSignature()){ + signFlag = true; + } else { + signFlag = StringUtils.isNotEmpty(one.getSignature()); + } + if(infoFlag && cardFlag && bankFlag && signFlag){ + realNameAuth = 1; + } + this.update(Wrappers.lambdaUpdate(CustomerInfo.class) + .eq(CustomerInfo::getId, customerId) + .set(CustomerInfo::getInfoFlag,infoFlag) + .set(CustomerInfo::getCardFlag,cardFlag) + .set(CustomerInfo::getBankFlag,bankFlag)); + customerService.update(Wrappers.lambdaUpdate(Customer.class) + .eq(Customer::getId, customerId) + .set(Customer::getRealNameAuth,realNameAuth)); + } + + @Override + public CustomerInfo getByCustomerId(Long customerId) { + return this.getOne(Wrappers.lambdaQuery(CustomerInfo.class) + .eq(CustomerInfo::getCustomerId,customerId)); + } + + @Override + public boolean updateAllowSignature(CustomerInfo bo) { + this.update(Wrappers.lambdaUpdate(CustomerInfo.class) + .eq(CustomerInfo::getCustomerId,bo.getCustomerId()) + .set(CustomerInfo::getAllowSignature,bo.getAllowSignature())); + CustomerInfo customerInfo = this.getByCustomerId(bo.getCustomerId()); + checkCustomerInfoFlag(customerInfo); + return false; + } +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/service/impl/CustomerServiceImpl.java b/bashi-dk/src/main/java/com/bashi/dk/service/impl/CustomerServiceImpl.java new file mode 100644 index 0000000..5d57c63 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/service/impl/CustomerServiceImpl.java @@ -0,0 +1,93 @@ +package com.bashi.dk.service.impl; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.common.com.Condition; +import com.bashi.common.com.PageParams; +import com.bashi.common.core.domain.entity.Customer; +import com.bashi.common.exception.CustomException; +import com.bashi.dk.domain.BorrowLog; +import com.bashi.dk.domain.CustomerInfo; +import com.bashi.dk.dto.admin.resp.CustomerAdminResp; +import com.bashi.dk.dto.app.req.CustomerRegisterReq; +import com.bashi.dk.mapper.BorrowLogMapper; +import com.bashi.dk.mapper.CustomerMapper; +import com.bashi.dk.service.CustomerInfoService; +import com.bashi.dk.service.CustomerService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.math.BigDecimal; + +@Service +public class CustomerServiceImpl extends ServiceImpl implements CustomerService { + @Autowired + private BCryptPasswordEncoder passwordEncoder; + @Autowired + private CustomerInfoService customerInfoService; + @Resource + private BorrowLogMapper borrowLogMapper; + @Override + public Customer getCustomerByName(String mobile) { + return this.getOne(Wrappers.lambdaQuery(Customer.class) + .eq(Customer::getPhoneNumber,mobile) + .last("limit 1")); + } + + @Override + public void register(CustomerRegisterReq register) { + String phoneNumber = register.getPhoneNumber(); + Customer customer = this.getCustomerByName(phoneNumber); + if(customer != null){ + throw new CustomException("用户已存在"); + } + customer = new Customer(); + customer.setPhoneNumber(phoneNumber); + customer.setNickName("VIP用户"+phoneNumber.substring(phoneNumber.length() - 4)); + customer.setPassword(passwordEncoder.encode(register.getPassword())); + this.save(customer); + CustomerInfo customerInfo = new CustomerInfo(); + customerInfo.setCustomerId(customer.getId()); + customerInfoService.save(customerInfo); + } + + @Override + public boolean updatePwd(Long id, String password) { + return this.update(Wrappers.lambdaUpdate(Customer.class) + .eq(Customer::getId,id) + .set(Customer::getPassword,passwordEncoder.encode(password))); + } + + @Override + public void borrowAmount(Long customerId, BigDecimal totalLoanMoney,BigDecimal totalRepayment) { + baseMapper.incsAmount(customerId,totalLoanMoney,totalRepayment); + } + + @Override + public boolean withdraw(Long customerId, Double withdrawAmount) { + baseMapper.withdraw(customerId,withdrawAmount); + BorrowLog borrowLog = new BorrowLog(); + borrowLog.setWithdrawAccount(withdrawAmount); + borrowLog.setCustomerId(customerId); + borrowLogMapper.insert(borrowLog); + this.update(Wrappers.lambdaUpdate(Customer.class) + .eq(Customer::getId,customerId) + .set(Customer::getWithdrawFlag,1)); + return true; + } + + @Override + public void dk(Long customerId){ + this.update(Wrappers.lambdaUpdate(Customer.class) + .eq(Customer::getId,customerId) + .set(Customer::getLoansFlag,1)); + } + + @Override + public IPage pageAdmin(PageParams pageParams, CustomerAdminResp bo) { + return baseMapper.pageAdmin(Condition.getPage(pageParams),bo); + } +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/service/impl/HomeSettingServiceImpl.java b/bashi-dk/src/main/java/com/bashi/dk/service/impl/HomeSettingServiceImpl.java new file mode 100644 index 0000000..55b30b1 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/service/impl/HomeSettingServiceImpl.java @@ -0,0 +1,16 @@ +package com.bashi.dk.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.dk.domain.HomeSetting; +import com.bashi.dk.mapper.HomeSettingMapper; +import com.bashi.dk.service.HomeSettingService; +import org.springframework.stereotype.Service; + +@Service +public class HomeSettingServiceImpl extends ServiceImpl implements HomeSettingService { + @Override + public HomeSetting getHomeSetting(){ + return this.getOne(Wrappers.lambdaQuery(HomeSetting.class).last("limit 1")); + } +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/service/impl/LoansSettingServiceImpl.java b/bashi-dk/src/main/java/com/bashi/dk/service/impl/LoansSettingServiceImpl.java new file mode 100644 index 0000000..dba4be3 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/service/impl/LoansSettingServiceImpl.java @@ -0,0 +1,16 @@ +package com.bashi.dk.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.dk.domain.LoansSetting; +import com.bashi.dk.mapper.LoansSettingMapper; +import com.bashi.dk.service.LoansSettingService; +import org.springframework.stereotype.Service; + +@Service +public class LoansSettingServiceImpl extends ServiceImpl implements LoansSettingService { + @Override + public LoansSetting getLoansSetting(){ + return this.getOne(Wrappers.lambdaQuery(LoansSetting.class).last("limit 1")); + } +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/util/ACMLoanCalculator.java b/bashi-dk/src/main/java/com/bashi/dk/util/ACMLoanCalculator.java new file mode 100644 index 0000000..cb4c834 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/util/ACMLoanCalculator.java @@ -0,0 +1,62 @@ +package com.bashi.dk.util; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +/** + * 等额本金还款法 + * + * Created by WangGenshen on 1/23/16. + */ +public class ACMLoanCalculator extends LoanCalculatorAdapter { + + @Override + public Loan calLoan(BigDecimal totalLoanMoney, int totalMonth, double loanRate, int rateType) { + Loan loan = new Loan(); + BigDecimal loanRateMonth = rateType == LoanUtil.RATE_TYPE_YEAR ? new BigDecimal(loanRate / 100 / 12) : new BigDecimal(loanRate / 100); + loan.setTotalMonth(totalMonth); + loan.setTotalLoanMoney(totalLoanMoney); + BigDecimal payPrincipal = totalLoanMoney.divide(new BigDecimal(totalMonth), 2, BigDecimal.ROUND_HALF_UP); + + BigDecimal totalPayedPrincipal = new BigDecimal(0);//累积所还本金 + BigDecimal totalInterest = new BigDecimal(0); //总利息 + BigDecimal totalRepayment = new BigDecimal(0); // 已还款总数 + List loanByMonthList = new ArrayList<>(); + int year = 0; + int monthInYear = 0; + for (int i = 0; i < totalMonth; i++) { + LoanByMonth loanByMonth = new LoanByMonth(); + loanByMonth.setMonth(i + 1); + loanByMonth.setYear(year + 1); + loanByMonth.setMonthInYear(++monthInYear); + if ((i + 1) % 12 == 0) { + year++; + monthInYear = 0; + } + totalPayedPrincipal = totalPayedPrincipal.add(payPrincipal); + loanByMonth.setPayPrincipal(payPrincipal); + BigDecimal interest = totalLoanMoney.subtract(totalPayedPrincipal).multiply(loanRateMonth).setScale(2, BigDecimal.ROUND_HALF_UP); + loanByMonth.setInterest(interest); + totalInterest = totalInterest.add(interest); + loanByMonth.setRepayment(payPrincipal.add(interest)); + if (i == 0) { + loan.setFirstRepayment(loanByMonth.getRepayment()); + } + totalRepayment = totalRepayment.add(loanByMonth.getRepayment()); + loanByMonth.setRemainPrincipal(totalLoanMoney.subtract(totalPayedPrincipal)); + loanByMonthList.add(loanByMonth); + } + loan.setTotalRepayment(totalRepayment); + loan.setAvgRepayment(totalRepayment.divide(new BigDecimal(totalMonth), 2, BigDecimal.ROUND_HALF_UP)); + loan.setTotalInterest(totalInterest); + BigDecimal totalPayedRepayment = new BigDecimal(0); + for (LoanByMonth loanByMonth : loanByMonthList) { + totalPayedRepayment = totalPayedRepayment.add(loanByMonth.getRepayment()); + loanByMonth.setRemainTotal(totalRepayment.subtract(totalPayedRepayment)); + } + loan.setAllLoans(loanByMonthList); + return loan; + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/util/ACPIMLoanCalculator.java b/bashi-dk/src/main/java/com/bashi/dk/util/ACPIMLoanCalculator.java new file mode 100644 index 0000000..bf6bdb1 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/util/ACPIMLoanCalculator.java @@ -0,0 +1,59 @@ +package com.bashi.dk.util; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +/** + * 等额本息还款法 + * Created by WangGenshen on 1/23/16. + */ +public class ACPIMLoanCalculator extends LoanCalculatorAdapter { + + @Override + public Loan calLoan(BigDecimal totalLoanMoney, int totalMonth, double loanRate, int rateType) { + Loan loan = new Loan(); + BigDecimal loanRateMonth = rateType == LoanUtil.RATE_TYPE_YEAR ? new BigDecimal(loanRate / 100 / 12) : new BigDecimal(loanRate / 100); + BigDecimal factor = new BigDecimal(Math.pow(1 + loanRateMonth.doubleValue(), totalMonth)); + BigDecimal avgRepayment = totalLoanMoney.multiply(loanRateMonth).multiply(factor).divide(factor.subtract(new BigDecimal(1)), 2, BigDecimal.ROUND_HALF_UP); + loan.setLoanRate(loanRate); + loan.setTotalLoanMoney(totalLoanMoney); + loan.setTotalMonth(totalMonth); + loan.setAvgRepayment(avgRepayment); + loan.setTotalRepayment(avgRepayment.multiply(new BigDecimal(totalMonth))); + loan.setFirstRepayment(avgRepayment); + + BigDecimal totalPayedPrincipal = new BigDecimal(0);//累积所还本金 + BigDecimal totalInterest = new BigDecimal(0); //总利息 + BigDecimal totalRepayment = new BigDecimal(0); // 已还款总数 + List loanByMonthList = new ArrayList<>(); + int year = 0; + int monthInYear = 0; + for (int i = 0; i < totalMonth; i++) { + LoanByMonth loanByMonth = new LoanByMonth(); + BigDecimal remainPrincipal = totalLoanMoney.subtract(totalPayedPrincipal); + BigDecimal interest = remainPrincipal.multiply(loanRateMonth).setScale(2, BigDecimal.ROUND_HALF_UP); + totalInterest = totalInterest.add(interest); + BigDecimal principal = loan.getAvgRepayment().subtract(interest); + totalPayedPrincipal = totalPayedPrincipal.add(principal); + loanByMonth.setMonth(i + 1); + loanByMonth.setYear(year + 1); + loanByMonth.setMonthInYear(++monthInYear); + if ((i + 1) % 12 == 0) { + year++; + monthInYear = 0; + } + loanByMonth.setInterest(interest); + loanByMonth.setPayPrincipal(principal); + loanByMonth.setRepayment(loan.getAvgRepayment()); + totalRepayment = totalRepayment.add(loanByMonth.getRepayment()); + loanByMonth.setRemainPrincipal(remainPrincipal); + loanByMonth.setRemainTotal(loan.getTotalRepayment().subtract(totalRepayment)); + loanByMonthList.add(loanByMonth); + } + loan.setTotalInterest(totalInterest); + loan.setAllLoans(loanByMonthList); + return loan; + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/util/ContentReplaceUtil.java b/bashi-dk/src/main/java/com/bashi/dk/util/ContentReplaceUtil.java new file mode 100644 index 0000000..a383088 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/util/ContentReplaceUtil.java @@ -0,0 +1,46 @@ +package com.bashi.dk.util; + +import cn.hutool.core.util.StrUtil; +import freemarker.cache.StringTemplateLoader; +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateException; +import lombok.SneakyThrows; +import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; + +import java.io.IOException; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 使用说明 ${name!12312312} 代表默认值 + * 支持自定义日期格式newDate为约定 ${newDate?string("yyyy-MM-dd HH:mm:ss") + * 普通字符直接替换${code} + * @author zlf + * @date 2023/6/19 6:56 PM + * @desc + */ +public class ContentReplaceUtil { + + public static String NOW_DATE = "newDate"; + + + @SneakyThrows + public static String replaceWord(String content, Map params) { + if (StrUtil.isEmpty(content)){ + return null; + } + if (content.contains(NOW_DATE)){ + params.put("newDate",new Date()); + } + StringTemplateLoader stringTemplateLoader = new StringTemplateLoader(); + stringTemplateLoader.putTemplate("test.ftl", content); + Configuration cfg = new Configuration(Configuration.VERSION_2_3_22); + cfg.setTemplateLoader(stringTemplateLoader); + Template template = cfg.getTemplate("test.ftl"); + String finalContent = FreeMarkerTemplateUtils.processTemplateIntoString(template, params); + return finalContent; + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/util/ILoanCalculator.java b/bashi-dk/src/main/java/com/bashi/dk/util/ILoanCalculator.java new file mode 100644 index 0000000..dbe076b --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/util/ILoanCalculator.java @@ -0,0 +1,21 @@ +package com.bashi.dk.util; + +import java.math.BigDecimal; + +/** + * Created by WangGenshen on 1/14/16. + */ +public interface ILoanCalculator { + + /** + * 贷款计算 + * + * @param totalLoanMoney 总贷款额 + * @param totalMonth 还款月数 + * @param loanRate 贷款利率 + * @param rateType 可选择年利率或月利率 + * @return + */ + public Loan calLoan(BigDecimal totalLoanMoney, int totalMonth, double loanRate, int rateType); + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/util/ImageUtil.java b/bashi-dk/src/main/java/com/bashi/dk/util/ImageUtil.java new file mode 100644 index 0000000..7d2a7de --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/util/ImageUtil.java @@ -0,0 +1,154 @@ +/* +package com.bashi.dk.util; + +import com.sun.image.codec.jpeg.JPEGCodec; +import com.sun.image.codec.jpeg.JPEGEncodeParam; +import com.sun.image.codec.jpeg.JPEGImageEncoder; +import lombok.extern.slf4j.Slf4j; +import sun.font.FontDesignMetrics; + +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; + +@Slf4j +public class ImageUtil { + + public static void main(String[] args) { + String filePath = "d:\\Users\\004796\\桌面\\123\\hd_bg.png"; + Map content = new LinkedHashMap<>(); + content.put(" 转账批次号","542838541"); + content.put("  转出单位","招商银行股份有限公司"); + content.put("  转出账户","2361293892173872198"); + content.put("转出账号地区","深圳市福田区深南大道708"); + content.put(" 收款人姓名","马中华"); + content.put("  收款账户","1273891273897123718"); + content.put("    币种","人民币元"); + content.put("  转出金额","100000.00"); + content.put("  转出时间","2023-11-27 09:30:12"); + content.put("  转账类型","签约金融企业--网贷放款预约到账"); + content.put("  执行方式","点击选择执行方式"); + content.put("    状态","点击选择状态"); + content.put("  银行备注","点击选择银行备注"); + content.put("  处理结果","点击选择处理结果"); + content.put("  用户备注","点击选择用户备注"); + String putPath = "d:\\Users\\004796\\桌面\\123\\1233.jpg"; + createStringMark(filePath,content,putPath); + } + + + //给jpg添加文字 + public static boolean createStringMark(String filePath, Map content, String outPath) { + ImageIcon imgIcon = new ImageIcon(filePath); + Image theImg = imgIcon.getImage(); + int width = theImg.getWidth(null) == -1 ? 200 : theImg.getWidth(null); + int height = theImg.getHeight(null) == -1 ? 200 : theImg.getHeight(null); + int fontSize = 16; + BufferedImage bimage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + Graphics2D g = bimage.createGraphics(); + Color mycolor = Color.black; + g.setColor(mycolor); + g.setBackground(Color.black); + g.drawImage(theImg, 0, 0, null); + g.setFont(new Font("宋体", Font.PLAIN, fontSize)); //字体、字型、字号 + RenderingHints hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g.setRenderingHints(hints); + Graphics2D g1 = bimage.createGraphics(); + g1.setColor(mycolor); + g1.setBackground(Color.black); + g1.drawImage(theImg, 0, 0, null); + g1.setFont(new Font("黑体", Font.PLAIN, fontSize)); //字体、字型、字号 + g1.setRenderingHints(hints); + int widthFlag = 500; + int heightFlag = 335; + for (Map.Entry entry : content.entrySet()) { + g1.drawString(entry.getKey()+": "+entry.getValue(), widthFlag, heightFlag); //画文字 + heightFlag += 29; + } + g1.dispose(); + g.dispose(); + FileOutputStream out = null; + try { + out = new FileOutputStream(outPath); //先用一个特定的输出文件名 + JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); + JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bimage); + param.setQuality(100, true); // + encoder.encode(bimage, param); + } catch (Exception e) { + log.error("图片生成失败", e); + return false; + } finally { + try { + if(out != null) out.close(); + } catch (IOException e) { + log.error(e.getMessage(), e); + return false; + } + } + return true; + } + + + public static void gogo(){ + int fontSize = 14; + // 获取当前系统所有字体 + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + String[] fontNames = ge.getAvailableFontFamilyNames(); + String str = "转账批次号: 1231231"; + int height = 0; + int width = 0; + // 根据字体获取需要生成的图片的宽和高 + for (String fontName : fontNames) { + Font font = new Font(fontName, Font.PLAIN, fontSize); + FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font); + height += metrics.getHeight(); + int tmpWidth = metrics.stringWidth(fontName + " : " + str); + if (tmpWidth > width) { + width = tmpWidth; + } + } + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); + Graphics2D g2d = null; + try { + //创建画笔 + g2d = image.createGraphics(); + //设置背景颜色为白色 + g2d.setColor(Color.WHITE); + g2d.fillRect(0, 0, image.getWidth(), image.getHeight()); + //设置画笔颜色为黑色 + g2d.setColor(Color.BLACK); + RenderingHints hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + // 开启文字抗锯齿 + hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g2d.setRenderingHints(hints); + int startY = 0; + for (String fontName : fontNames) { + Font font = new Font(fontName, Font.PLAIN, fontSize); + System.out.println(fontName); + g2d.setFont(font); + g2d.drawString(fontName + " : " + str, 0, startY); + // 下一行文字的左上角纵坐标 + FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font); + startY += metrics.getHeight(); + } + String savePath = "d:\\Users\\004796\\桌面\\123\\1111.jpg"; + ImageIO.write(image, "PNG", new File(savePath)); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (g2d != null) { + g2d.dispose(); + } + } + } + + +} +*/ diff --git a/bashi-dk/src/main/java/com/bashi/dk/util/Loan.java b/bashi-dk/src/main/java/com/bashi/dk/util/Loan.java new file mode 100644 index 0000000..3c1aab4 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/util/Loan.java @@ -0,0 +1,115 @@ +package com.bashi.dk.util; + +import lombok.Getter; +import lombok.Setter; + +import java.math.BigDecimal; +import java.util.List; + +/** + * Created by WangGenshen on 1/14/16. + */ +public class Loan { + + private BigDecimal totalLoanMoney; //贷款总额 + private int totalMonth; //还款月份 + private double loanRate; //贷款年利率 + + @Getter + @Setter + private double loanRateMonth; + + private BigDecimal totalInterest; // 总利息数 + private BigDecimal totalRepayment; // 还款总额 + private BigDecimal firstRepayment; // 首月还款额 + private BigDecimal avgRepayment; // 月均还款额 + + private List allLoans; // 所有月份的还款情况 + + + + + public BigDecimal getTotalLoanMoney() { + return totalLoanMoney; + } + + public void setTotalLoanMoney(BigDecimal totalLoanMoney) { + this.totalLoanMoney = totalLoanMoney; + } + + public int getTotalMonth() { + return totalMonth; + } + + public void setTotalMonth(int totalMonth) { + this.totalMonth = totalMonth; + } + + public double getLoanRate() { + return loanRate; + } + + public void setLoanRate(double loanRate) { + this.loanRate = loanRate; + } + + public BigDecimal getTotalInterest() { + return totalInterest; + } + + public void setTotalInterest(BigDecimal totalInterest) { + this.totalInterest = totalInterest; + } + + public BigDecimal getTotalRepayment() { + return totalRepayment; + } + + public void setTotalRepayment(BigDecimal totalRepayment) { + this.totalRepayment = totalRepayment; + } + + public BigDecimal getFirstRepayment() { + return firstRepayment; + } + + public void setFirstRepayment(BigDecimal firstRepayment) { + this.firstRepayment = firstRepayment; + } + + public BigDecimal getAvgRepayment() { + return avgRepayment; + } + + public void setAvgRepayment(BigDecimal avgRepayment) { + this.avgRepayment = avgRepayment; + } + + public List getAllLoans() { + return allLoans; + } + + public void setAllLoans(List allLoans) { + this.allLoans = allLoans; + } + + @Override + public String toString() { + String allLoansStr = ""; + if (allLoans != null) { + for (LoanByMonth loanByMonth : allLoans) { + String lbmStr = "月份: " + loanByMonth.getMonth() + "\t第" + loanByMonth.getYear() + "年\t第" + + loanByMonth.getMonthInYear() + "月\t" + "月供: " + loanByMonth.getRepayment() + + "\t本金: " + loanByMonth.getPayPrincipal() + "\t利息: " + loanByMonth.getInterest() + + "\t剩余贷款: " + loanByMonth.getRemainTotal(); + if (allLoansStr.equals("")) { + allLoansStr = lbmStr; + } else { + allLoansStr += "\n" + lbmStr; + } + } + } + return "每月还款: " + getAvgRepayment() + "\t总利息: " + getTotalInterest() + + "\t还款总额:" + getTotalRepayment() + "\t首月还款: " + getFirstRepayment() + "\n" + allLoansStr; + } +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/util/LoanByMonth.java b/bashi-dk/src/main/java/com/bashi/dk/util/LoanByMonth.java new file mode 100644 index 0000000..c553b31 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/util/LoanByMonth.java @@ -0,0 +1,83 @@ +package com.bashi.dk.util; + +import java.math.BigDecimal; + +/** + * Created by WangGenshen on 1/14/16. + */ +public class LoanByMonth { + + private int month; // 第几个月份 + private BigDecimal repayment; // 该月还款额 + private BigDecimal payPrincipal; // 该月所还本金 + private BigDecimal interest; // 该月利息 + private BigDecimal remainTotal; // 剩余贷款 + private BigDecimal remainPrincipal; // 剩余总本金 + + private int year; // 第几年 + private int monthInYear; // 年里的第几月 + + public int getMonth() { + return month; + } + + public void setMonth(int month) { + this.month = month; + } + + public BigDecimal getRepayment() { + return repayment; + } + + public void setRepayment(BigDecimal repayment) { + this.repayment = repayment; + } + + public BigDecimal getPayPrincipal() { + return payPrincipal; + } + + public void setPayPrincipal(BigDecimal payPrincipal) { + this.payPrincipal = payPrincipal; + } + + public BigDecimal getInterest() { + return interest; + } + + public void setInterest(BigDecimal interest) { + this.interest = interest; + } + + public BigDecimal getRemainTotal() { + return remainTotal; + } + + public void setRemainTotal(BigDecimal remainTotal) { + this.remainTotal = remainTotal; + } + + public BigDecimal getRemainPrincipal() { + return remainPrincipal; + } + + public void setRemainPrincipal(BigDecimal remainPrincipal) { + this.remainPrincipal = remainPrincipal; + } + + public int getYear() { + return year; + } + + public void setYear(int year) { + this.year = year; + } + + public int getMonthInYear() { + return monthInYear; + } + + public void setMonthInYear(int monthInYear) { + this.monthInYear = monthInYear; + } +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/util/LoanCalculatorAdapter.java b/bashi-dk/src/main/java/com/bashi/dk/util/LoanCalculatorAdapter.java new file mode 100644 index 0000000..6a76cca --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/util/LoanCalculatorAdapter.java @@ -0,0 +1,17 @@ +package com.bashi.dk.util; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by WangGenshen on 1/14/16. + */ +public class LoanCalculatorAdapter implements ILoanCalculator { + + @Override + public Loan calLoan(BigDecimal totalLoanMoney, int totalMonth, double loanRate, int rateType) { + return null; + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/util/LoanCalculatorTest.java b/bashi-dk/src/main/java/com/bashi/dk/util/LoanCalculatorTest.java new file mode 100644 index 0000000..5275cbb --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/util/LoanCalculatorTest.java @@ -0,0 +1,57 @@ +package com.bashi.dk.util; + +import org.xnio.channels.SuspendableAcceptChannel; + +import java.math.BigDecimal; + +/** + * Created by WangGenshen on 1/14/16. + */ +public class LoanCalculatorTest { + + private int totalMonth; + private BigDecimal totalMoney; + private double percent; + private double rate; + private double rateDiscount; + + protected void setUp() throws Exception { + totalMonth = 36; + totalMoney = new BigDecimal(50000); + percent = 0; + rate = 10.8; + rateDiscount = 1; + } + + public static void main(String[] args) { + ACPIMLoanCalculator calculator = new ACPIMLoanCalculator(); + Loan loan = calculator.calLoan( + LoanUtil.totalLoanMoney(new BigDecimal(50000), 2), + 36, + 0.6, + LoanUtil.RATE_TYPE_MONTH); + System.out.println("asd"); + } + + + public void testACPIMCalculate() { + ACPIMLoanCalculator calculator = new ACPIMLoanCalculator(); + Loan loan = calculator.calLoan( + LoanUtil.totalLoanMoney(totalMoney, percent), + totalMonth, + LoanUtil.rate(rate, rateDiscount), + LoanUtil.RATE_TYPE_YEAR); + System.out.println(loan); + } + + public void testACMCalculate() { + ACMLoanCalculator calculator = new ACMLoanCalculator(); + Loan loan = calculator.calLoan( + LoanUtil.totalLoanMoney(totalMoney, percent), + totalMonth, + LoanUtil.rate(rate, rateDiscount), + LoanUtil.RATE_TYPE_YEAR); + System.out.println(loan); + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/util/LoanUtil.java b/bashi-dk/src/main/java/com/bashi/dk/util/LoanUtil.java new file mode 100644 index 0000000..d84470e --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/util/LoanUtil.java @@ -0,0 +1,33 @@ +package com.bashi.dk.util; + +import java.math.BigDecimal; + +/** + * Created by WangGenshen on 1/23/16. + */ +public class LoanUtil { + + public static final int RATE_TYPE_YEAR = 10; + public static final int RATE_TYPE_MONTH = 11; + + public static BigDecimal totalMoney(double area, BigDecimal price, double discount) { + return price.multiply(new BigDecimal(area)).multiply(new BigDecimal(discount)).setScale(2, BigDecimal.ROUND_HALF_UP); + } + + public static BigDecimal totalLoanMoney(BigDecimal totalMoney, double percent) { + return totalMoney.multiply(new BigDecimal(1 - percent)).setScale(2, BigDecimal.ROUND_HALF_UP); + } + + public static BigDecimal totalLoanMoney(double area, BigDecimal price, double discount, double percent) { + return totalLoanMoney(totalMoney(area, price, discount), percent); + } + + public static double rate(double rate, double discount) { + return rate * discount; + } + + public static int totalMonth(int year) { + return 12 * year; + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/util/MoneyUtil.java b/bashi-dk/src/main/java/com/bashi/dk/util/MoneyUtil.java new file mode 100644 index 0000000..e02aff5 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/util/MoneyUtil.java @@ -0,0 +1,141 @@ +package com.bashi.dk.util; + +public class MoneyUtil { + + /** 大写数字 */ + private static final String[] NUMBERS = { "零", "壹", "贰", "叁", "肆", "伍", "陆", + "柒", "捌", "玖" }; + /** 整数部分的单位 */ + private static final String[] IUNIT = { "元", "拾", "佰", "仟", "万", "拾", "佰", + "仟", "亿", "拾", "佰", "仟", "万", "拾", "佰", "仟" }; + /** 小数部分的单位 */ + private static final String[] DUNIT = { "角", "分", "厘" }; + + /** + * 得到大写金额。 + */ + public static String toChinese(String str) { + str = str.replaceAll(",", "");// 去掉"," + String integerStr;// 整数部分数字 + String decimalStr;// 小数部分数字 + + // 初始化:分离整数部分和小数部分 + if (str.indexOf(".") > 0) { + integerStr = str.substring(0, str.indexOf(".")); + decimalStr = str.substring(str.indexOf(".") + 1); + } else if (str.indexOf(".") == 0) { + integerStr = ""; + decimalStr = str.substring(1); + } else { + integerStr = str; + decimalStr = ""; + } + // integerStr去掉首0,不必去掉decimalStr的尾0(超出部分舍去) + if (!integerStr.equals("")) { + integerStr = Long.toString(Long.parseLong(integerStr)); + if (integerStr.equals("0")) { + integerStr = ""; + } + } + // overflow超出处理能力,直接返回 + if (integerStr.length() > IUNIT.length) { + System.out.println(str + ":超出处理能力"); + return str; + } + + int[] integers = toArray(integerStr);// 整数部分数字 + boolean isMust5 = isMust5(integerStr);// 设置万单位 + int[] decimals = toArray(decimalStr);// 小数部分数字 + return getChineseInteger(integers, isMust5) + getChineseDecimal(decimals); + } + + /** + * 整数部分和小数部分转换为数组,从高位至低位 + */ + private static int[] toArray(String number) { + int[] array = new int[number.length()]; + for (int i = 0; i < number.length(); i++) { + array[i] = Integer.parseInt(number.substring(i, i + 1)); + } + return array; + } + + /** + * 得到中文金额的整数部分。 + */ + private static String getChineseInteger(int[] integers, boolean isMust5) { + StringBuffer chineseInteger = new StringBuffer(""); + int length = integers.length; + for (int i = 0; i < length; i++) { + // 0出现在关键位置:1234(万)5678(亿)9012(万)3456(元) + // 特殊情况:10(拾元、壹拾元、壹拾万元、拾万元) + String key = ""; + if (integers[i] == 0) { + if ((length - i) == 13)// 万(亿)(必填) + key = IUNIT[4]; + else if ((length - i) == 9)// 亿(必填) + key = IUNIT[8]; + else if ((length - i) == 5 && isMust5)// 万(不必填) + key = IUNIT[4]; + else if ((length - i) == 1)// 元(必填) + key = IUNIT[0]; + // 0遇非0时补零,不包含最后一位 + if ((length - i) > 1 && integers[i + 1] != 0) + key += NUMBERS[0]; + } + chineseInteger.append(integers[i] == 0 ? key + : (NUMBERS[integers[i]] + IUNIT[length - i - 1])); + } + return chineseInteger.toString(); + } + + /** + * 得到中文金额的小数部分。 + */ + private static String getChineseDecimal(int[] decimals) { + StringBuffer chineseDecimal = new StringBuffer(""); + for (int i = 0; i < decimals.length; i++) { + // 舍去3位小数之后的 + if (i == 3) + break; + chineseDecimal.append(decimals[i] == 0 ? "" + : (NUMBERS[decimals[i]] + DUNIT[i])); + } + return chineseDecimal.toString(); + } + + /** + * 判断第5位数字的单位"万"是否应加。 + */ + private static boolean isMust5(String integerStr) { + int length = integerStr.length(); + if (length > 4) { + String subInteger = ""; + if (length > 8) { + // 取得从低位数,第5到第8位的字串 + subInteger = integerStr.substring(length - 8, length - 4); + } else { + subInteger = integerStr.substring(0, length - 4); + } + return Integer.parseInt(subInteger) > 0; + } else { + return false; + } + } + + public static void main(String[] args) { + String number = "1.23"; + System.out.println(number + " " + MoneyUtil.toChinese(number)); + number = "1234567890123456.123"; + System.out.println(number + " " + MoneyUtil.toChinese(number)); + number = "0.0798"; + System.out.println(number + " " + MoneyUtil.toChinese(number)); + number = "10,001,000.09"; + System.out.println(number + " " + MoneyUtil.toChinese(number)); + number = "01.107700"; + System.out.println(number + " " + MoneyUtil.toChinese(number)); + number = "01.107700"; + System.out.println(number + " " + MoneyUtil.toChinese(number)); + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/util/OrderTradeNoUtil.java b/bashi-dk/src/main/java/com/bashi/dk/util/OrderTradeNoUtil.java new file mode 100644 index 0000000..1dca309 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/util/OrderTradeNoUtil.java @@ -0,0 +1,27 @@ +package com.bashi.dk.util; + +import com.baomidou.mybatisplus.core.toolkit.StringUtils; + +/** + *

created on 2021/8/23

+ * + * @author zhangliang + */ +public class OrderTradeNoUtil { + + public static final String BORROW = "B"; + + private static final SnowFlake snowFlake = new SnowFlake(2, 3); + + public static String createOrder(String orderType){ + return orderType + snowFlake.nextId(); + } + + public static String getOrderType(String tradeNo){ + if(StringUtils.isBlank(tradeNo)){ + return null; + } + return String.valueOf(tradeNo.charAt(0)); + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/util/PhoneRandomUtil.java b/bashi-dk/src/main/java/com/bashi/dk/util/PhoneRandomUtil.java new file mode 100644 index 0000000..8bdf4ca --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/util/PhoneRandomUtil.java @@ -0,0 +1,21 @@ +package com.bashi.dk.util; + +import java.util.Random; + +public class PhoneRandomUtil { + + public static String gen() { + Random random = new Random(); + StringBuilder phoneNumber = new StringBuilder(); + phoneNumber.append("1"); + for (int i = 0; i < 2; i++) { + phoneNumber.append(random.nextInt(10)); + } + phoneNumber.append("****"); + for (int i = 0; i < 4; i++) { + phoneNumber.append(random.nextInt(10)); + } + return phoneNumber.toString(); + } + +} diff --git a/bashi-dk/src/main/java/com/bashi/dk/util/SnowFlake.java b/bashi-dk/src/main/java/com/bashi/dk/util/SnowFlake.java new file mode 100644 index 0000000..8e393e9 --- /dev/null +++ b/bashi-dk/src/main/java/com/bashi/dk/util/SnowFlake.java @@ -0,0 +1,100 @@ +package com.bashi.dk.util; + +/** + * 描述: Twitter的分布式自增ID雪花算法snowflake (Java版) + * + * @author yanpenglei + * @create 2018-03-13 12:37 + **/ +public class SnowFlake { + + /** + * 起始的时间戳 + */ + private final static long START_STMP = 1480166465631L; + + /** + * 每一部分占用的位数 + */ + private final static long SEQUENCE_BIT = 12; //序列号占用的位数 + private final static long MACHINE_BIT = 5; //机器标识占用的位数 + private final static long DATACENTER_BIT = 5;//数据中心占用的位数 + + /** + * 每一部分的最大值 + */ + private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT); + private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT); + private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT); + + /** + * 每一部分向左的位移 + */ + private final static long MACHINE_LEFT = SEQUENCE_BIT; + private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; + private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT; + + private long datacenterId; //数据中心 + private long machineId; //机器标识 + private long sequence = 0L; //序列号 + private long lastStmp = -1L;//上一次时间戳 + + public SnowFlake(long datacenterId, long machineId) { + if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) { + throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0"); + } + if (machineId > MAX_MACHINE_NUM || machineId < 0) { + throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0"); + } + this.datacenterId = datacenterId; + this.machineId = machineId; + } + + /** + * 产生下一个ID + * + * @return + */ + public synchronized long nextId() { + long currStmp = getNewstmp(); + if (currStmp < lastStmp) { + throw new RuntimeException("Clock moved backwards. Refusing to generate id"); + } + + if (currStmp == lastStmp) { + //相同毫秒内,序列号自增 + sequence = (sequence + 1) & MAX_SEQUENCE; + //同一毫秒的序列数已经达到最大 + if (sequence == 0L) { + currStmp = getNextMill(); + } + } else { + //不同毫秒内,序列号置为0 + sequence = 0L; + } + + lastStmp = currStmp; + + return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分 + | datacenterId << DATACENTER_LEFT //数据中心部分 + | machineId << MACHINE_LEFT //机器标识部分 + | sequence; //序列号部分 + } + + private long getNextMill() { + long mill = getNewstmp(); + while (mill <= lastStmp) { + mill = getNewstmp(); + } + return mill; + } + + private long getNewstmp() { + return System.currentTimeMillis(); + } + + public static void main(String[] args) { + + + } +} diff --git a/bashi-dk/src/main/resources/mapper/BorrowMapper.xml b/bashi-dk/src/main/resources/mapper/BorrowMapper.xml new file mode 100644 index 0000000..9fbb415 --- /dev/null +++ b/bashi-dk/src/main/resources/mapper/BorrowMapper.xml @@ -0,0 +1,18 @@ + + + + + diff --git a/bashi-dk/src/main/resources/mapper/CustomerMapper.xml b/bashi-dk/src/main/resources/mapper/CustomerMapper.xml new file mode 100644 index 0000000..164983e --- /dev/null +++ b/bashi-dk/src/main/resources/mapper/CustomerMapper.xml @@ -0,0 +1,51 @@ + + + + + update dk_customer + set account = account + #{totalLoanMoney}, + borrow_account = #{totalLoanMoney}, + repayment_account = repayment_account + #{totalRepayment} + where id = #{customerId} + + + update dk_customer + set account = account - #{withdrawAmount} + where id = #{customerId} + + + + diff --git a/bashi-framework/pom.xml b/bashi-framework/pom.xml new file mode 100644 index 0000000..66d6f1c --- /dev/null +++ b/bashi-framework/pom.xml @@ -0,0 +1,91 @@ + + + + bashi + com.bashi + 2.4.0 + + 4.0.0 + + bashi-framework + + + framework框架核心 + + + + + + + + org.springframework.boot + spring-boot-starter-web + + + spring-boot-starter-tomcat + org.springframework.boot + + + + + + org.springframework.boot + spring-boot-starter-websocket + + + + org.springframework.boot + spring-boot-starter-undertow + + + + + + + + + + + + + org.springframework.boot + spring-boot-starter-aop + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + com.alibaba + druid-spring-boot-starter + + + com.sun + jconsole + + + com.sun + tools + + + + + + + com.bashi + bashi-system + + + com.bashi + bashi-common + + + + + diff --git a/bashi-framework/src/main/java/com/bashi/framework/app/AppTypeEnums.java b/bashi-framework/src/main/java/com/bashi/framework/app/AppTypeEnums.java new file mode 100644 index 0000000..438303a --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/app/AppTypeEnums.java @@ -0,0 +1,37 @@ +package com.bashi.framework.app; + +public enum AppTypeEnums { + + TBS("TBS","泰巴适"), + TATA("TATA","她他"), + MIYU("MIYU","秘约"), + APP591("591","591"), + MUSPAN("MUSPAN","幕Span"), + QIANZHI("QIANZHI","仟指"); + + private final String code; + private final String name; + + AppTypeEnums(String code, String name) { + this.code = code; + this.name = name; + } + + public String getName() { + return name; + } + + public String getCode() { + return code; + } + + public static AppTypeEnums getByCode(String code){ + AppTypeEnums[] values = AppTypeEnums.values(); + for (AppTypeEnums value : values) { + if(value.getCode().equals(code)){ + return value; + } + } + return AppTypeEnums.TBS; + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/app/AppTypeFactory.java b/bashi-framework/src/main/java/com/bashi/framework/app/AppTypeFactory.java new file mode 100644 index 0000000..189a4a9 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/app/AppTypeFactory.java @@ -0,0 +1,24 @@ +package com.bashi.framework.app; + +import com.bashi.common.utils.ServletUtils; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; + +@Component +public class AppTypeFactory { + + private static final String TYPE = "TYPE"; + + public String getAppType(){ + String type = getHeaderType(); + AppTypeEnums appTypeEnums = AppTypeEnums.getByCode(type); + return appTypeEnums.getCode(); + } + + public String getHeaderType(){ + HttpServletRequest request = ServletUtils.getRequest(); + return request.getHeader(TYPE); + } + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/aspectj/DataScopeAspect.java b/bashi-framework/src/main/java/com/bashi/framework/aspectj/DataScopeAspect.java new file mode 100644 index 0000000..9d5e8e4 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/aspectj/DataScopeAspect.java @@ -0,0 +1,175 @@ +package com.bashi.framework.aspectj; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import com.bashi.common.annotation.DataScope; +import com.bashi.common.core.domain.BaseEntity; +import com.bashi.common.core.domain.entity.SysRole; +import com.bashi.common.core.domain.entity.SysUser; +import com.bashi.common.core.domain.model.LoginUser; +import com.bashi.common.utils.ServletUtils; +import com.bashi.common.utils.reflect.ReflectUtils; +import com.bashi.common.utils.spring.SpringUtils; +import com.bashi.framework.web.service.TokenService; +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.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; +import java.util.Map; + +/** + * 数据过滤处理 + * + * @author Lion Li + */ +@Aspect +@Component +public class DataScopeAspect { + + /** + * 全部数据权限 + */ + public static final String DATA_SCOPE_ALL = "1"; + + /** + * 自定数据权限 + */ + public static final String DATA_SCOPE_CUSTOM = "2"; + + /** + * 部门数据权限 + */ + public static final String DATA_SCOPE_DEPT = "3"; + + /** + * 部门及以下数据权限 + */ + public static final String DATA_SCOPE_DEPT_AND_CHILD = "4"; + + /** + * 仅本人数据权限 + */ + public static final String DATA_SCOPE_SELF = "5"; + + /** + * 数据权限过滤关键字 + */ + public static final String DATA_SCOPE = "dataScope"; + + // 配置织入点 + @Pointcut("@annotation(com.bashi.common.annotation.DataScope)") + public void dataScopePointCut() { + } + + @Before("dataScopePointCut()") + public void doBefore(JoinPoint point) throws Throwable { + clearDataScope(point); + handleDataScope(point); + } + + protected void handleDataScope(final JoinPoint joinPoint) { + // 获得注解 + DataScope controllerDataScope = getAnnotationLog(joinPoint); + if (controllerDataScope == null) { + return; + } + // 获取当前的用户 + LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest()); + if (Validator.isNotNull(loginUser)) { + SysUser currentUser = loginUser.getUser(); + // 如果是超级管理员,则不过滤数据 + if (Validator.isNotNull(currentUser) && !currentUser.isAdmin()) { + dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), + controllerDataScope.userAlias(), controllerDataScope.isUser()); + } + } + } + + /** + * 数据范围过滤 + * + * @param joinPoint 切点 + * @param user 用户 + * @param userAlias 别名 + */ + public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, boolean isUser) { + StringBuilder sqlString = new StringBuilder(); + + // 将 "." 提取出,不写别名为单表查询,写别名为多表查询 + deptAlias = StrUtil.isNotBlank(deptAlias) ? deptAlias + "." : ""; + userAlias = StrUtil.isNotBlank(userAlias) ? userAlias + "." : ""; + + for (SysRole role : user.getRoles()) { + String dataScope = role.getDataScope(); + if (DATA_SCOPE_ALL.equals(dataScope)) { + sqlString = new StringBuilder(); + break; + } else if (DATA_SCOPE_CUSTOM.equals(dataScope)) { + sqlString.append(StrUtil.format( + " OR {}dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", + deptAlias, role.getRoleId())); + } else if (DATA_SCOPE_DEPT.equals(dataScope)) { + sqlString.append(StrUtil.format(" OR {}dept_id = {} ", + deptAlias, user.getDeptId())); + } else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) { + sqlString.append(StrUtil.format( + " OR {}dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", + deptAlias, user.getDeptId(), user.getDeptId())); + } else if (DATA_SCOPE_SELF.equals(dataScope)) { + if (isUser) { + sqlString.append(StrUtil.format(" OR {}user_id = {} ", + userAlias, user.getUserId())); + } else { + // 数据权限为仅本人且没有userAlias别名不查询任何数据 + sqlString.append(" OR 1=0 "); + } + } + } + + if (StrUtil.isNotBlank(sqlString.toString())) { + putDataScope(joinPoint, sqlString.substring(4)); + } + } + + /** + * 是否存在注解,如果存在就获取 + */ + private DataScope getAnnotationLog(JoinPoint joinPoint) { + Signature signature = joinPoint.getSignature(); + MethodSignature methodSignature = (MethodSignature) signature; + Method method = methodSignature.getMethod(); + + if (method != null) { + return method.getAnnotation(DataScope.class); + } + return null; + } + + /** + * 拼接权限sql前先清空params.dataScope参数防止注入 + */ + private void clearDataScope(final JoinPoint joinPoint) { + Object params = joinPoint.getArgs()[0]; + if (Validator.isNotNull(params)) { + putDataScope(joinPoint, ""); + } + } + + private static void putDataScope(JoinPoint joinPoint, String sql) { + Object params = joinPoint.getArgs()[0]; + if (Validator.isNotNull(params)) { + if (params instanceof BaseEntity) { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, sql); + } else { + Map invoke = ReflectUtils.invokeGetter(params, "params"); + invoke.put(DATA_SCOPE, sql); + } + } + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/aspectj/DataSourceAspect.java b/bashi-framework/src/main/java/com/bashi/framework/aspectj/DataSourceAspect.java new file mode 100644 index 0000000..9caba2a --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/aspectj/DataSourceAspect.java @@ -0,0 +1,62 @@ +package com.bashi.framework.aspectj; + +import cn.hutool.core.lang.Validator; +import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder; +import com.bashi.common.annotation.DataSource; +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.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.util.Objects; + +/** + * 多数据源处理 + * + * @author duteliang + */ +@Aspect +@Order(-500) +@Component +public class DataSourceAspect { + + @Pointcut("@annotation(com.bashi.common.annotation.DataSource)" + + "|| @within(com.bashi.common.annotation.DataSource)") + public void dsPointCut() { + } + + @Around("dsPointCut()") + public Object around(ProceedingJoinPoint point) throws Throwable { + DataSource dataSource = getDataSource(point); + + if (Validator.isNotNull(dataSource)) { + DynamicDataSourceContextHolder.poll(); + String source = dataSource.value().getSource(); + DynamicDataSourceContextHolder.push(source); + } + + try { + return point.proceed(); + } finally { + // 销毁数据源 在执行方法之后 + DynamicDataSourceContextHolder.clear(); + } + } + + /** + * 获取需要切换的数据源 + */ + public DataSource getDataSource(ProceedingJoinPoint point) { + MethodSignature signature = (MethodSignature) point.getSignature(); + DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class); + if (Objects.nonNull(dataSource)) { + return dataSource; + } + + return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class); + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/aspectj/LogAspect.java b/bashi-framework/src/main/java/com/bashi/framework/aspectj/LogAspect.java new file mode 100644 index 0000000..95e3ec5 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/aspectj/LogAspect.java @@ -0,0 +1,240 @@ +package com.bashi.framework.aspectj; + +import com.alibaba.fastjson.JSON; +import com.bashi.common.annotation.Log; +import com.bashi.common.core.domain.model.LoginUser; +import com.bashi.common.enums.BusinessStatus; +import com.bashi.common.utils.SecurityUtils; +import com.bashi.common.utils.ServletUtils; +import com.bashi.common.utils.spring.SpringUtils; +import com.bashi.framework.app.AppTypeFactory; +import com.bashi.framework.util.AgentUtils; +import com.bashi.framework.web.service.AsyncService; +import com.bashi.system.domain.SysOperLog; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpHeaders; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; +import java.util.Map; +import java.util.Objects; + +/** + * 操作日志记录处理 + * + * @author nohi + */ +@Aspect +@Component +@Slf4j +public class LogAspect { + + @Autowired + private AppTypeFactory appTypeFactory; + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) { + handleLog(joinPoint, controllerLog, null, jsonResult); + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) { + handleLog(joinPoint, controllerLog, e, null); + } + + protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) { + try { + long startTime = System.currentTimeMillis(); + StringBuilder logString = new StringBuilder(); + logString.append("record logs:"); + // 获得注解 + if (controllerLog == null || (!controllerLog.isPrint() && !controllerLog.isSaveDb())) { + return; + } + // 获取当前的用户 + HttpServletRequest request = ServletUtils.getRequest(); + LoginUser loginUser = SecurityUtils.getLoginUserNoException(); + // *========数据库日志=========*// + SysOperLog operLog = new SysOperLog(); + operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); + // 请求的地址 + String ip = ServletUtils.getClientIP(request); + operLog.setOperIp(ip); + operLog.setOperUrl(request.getRequestURI()); + // 设置请求方式 + operLog.setRequestMethod(request.getMethod()); + logString.append(String.format("ip=%s;",ip)); + if (loginUser != null) { + logString.append(String.format("userName=%s;",loginUser.getUsername())); + operLog.setOperName(loginUser.getUsername()); + } + String userAgent = request.getHeader(HttpHeaders.USER_AGENT); + logString.append(String.format("url=%s;method=%s;title=%s;",operLog.getOperUrl(),operLog.getRequestMethod(),controllerLog.title())); + logString.append(String.format("userAgent=%s;agentCode=%s;appHeader=%s;", + userAgent, + AgentUtils.getAgentCode(request),appTypeFactory.getHeaderType())); + if (e != null) { + operLog.setStatus(BusinessStatus.FAIL.ordinal()); + operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000)); + logString.append(String.format("exception=%s;",e.getMessage())); + } + // 设置方法名称 + String className = joinPoint.getTarget().getClass().getName(); + String methodName = joinPoint.getSignature().getName(); + operLog.setMethod(className + "." + methodName + "()"); + // 处理设置注解上的参数 + getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult,logString); + + DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + logString.append("StartTime:").append(dateFormat.format(new Date(startTime))); + long endTime = System.currentTimeMillis(); + logString.append(";EndTime:").append(dateFormat.format(new Date(endTime))); + logString.append(";CostTime:").append(endTime - startTime).append("ms"); + AsyncService bean = SpringUtils.getBean(AsyncService.class); + if(controllerLog.isPrint()){ + bean.recordLog(logString); + } + if(controllerLog.isSaveDb()){ + bean.recordOper(operLog); + } + } catch (Exception exp) { + // 记录本地异常日志 + log.error("==前置通知异常=="); + log.error("异常信息:{}", exp.getMessage()); + exp.printStackTrace(); + } + } + + /** + * 获取注解中对方法的描述信息 用于Controller层注解 + * + * @param log 日志 + * @param operLog 操作日志 + * @throws Exception + */ + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult, + StringBuilder logString) throws Exception { + // 设置action动作 + operLog.setBusinessType(log.businessType().ordinal()); + // 设置标题 + operLog.setTitle(log.title()); + // 设置操作人类别 + operLog.setOperatorType(log.operatorType().ordinal()); + // 是否需要保存request,参数和值 + if (log.isSaveRequestData()) { + // 获取参数的信息,传入到数据库中。 + setRequestValue(joinPoint, operLog,logString); + } + // 是否需要保存response,参数和值 + if (log.isSaveResponseData() && jsonResult != null) { + String jsonResultString = JSON.toJSONString(jsonResult); + operLog.setJsonResult(StringUtils.substring(jsonResultString, 0, 2000)); + logString.append(String.format("result=%s;",jsonResultString)); + } + } + + /** + * 获取请求的参数,放到log中 + * + * @param operLog 操作日志 + * @throws Exception 异常 + */ + private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog,StringBuilder logString) throws Exception { + String requestMethod = operLog.getRequestMethod(); + if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) { + String params = argsArrayToString(joinPoint.getArgs()); + operLog.setOperParam(StringUtils.substring(params, 0, 2000)); + logString.append(String.format("params=%s;",params)); + } else { + Map paramsMap = ServletUtils.getRequest().getParameterMap(); + String params = toMapString(paramsMap); + operLog.setOperParam(StringUtils.substring(params, 0, 2000)); + logString.append(String.format("params=%s;",params)); + } + } + + private String toMapString(Map paramsMap){ + StringBuilder paramString = new StringBuilder(); + paramString.append("{"); + for(Map.Entry paramEntry : paramsMap.entrySet()) { + paramString.append(paramEntry.getKey()).append(":"); + if (Objects.nonNull(paramEntry.getValue())) { + paramString.append(org.apache.commons.lang3.StringUtils.join(paramEntry.getValue(),",")); + } + paramString.append(";"); + } + paramString.append("}"); + return paramString.toString(); + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray) { + String params = ""; + if (paramsArray != null && paramsArray.length > 0) { + for (Object o : paramsArray) { + if (o != null && !isFilterObject(o)) { + try { + Object jsonObj = JSON.toJSONString(o); + params += jsonObj + " "; + } catch (Exception e) { + } + } + } + } + return params.trim(); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) { + Class clazz = o.getClass(); + if (clazz.isArray()) { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } else if (Collection.class.isAssignableFrom(clazz)) { + Collection collection = (Collection) o; + for (Object value : collection) { + return value instanceof MultipartFile; + } + } else if (Map.class.isAssignableFrom(clazz)) { + Map map = (Map) o; + for (Object value : map.entrySet()) { + Map.Entry entry = (Map.Entry) value; + return entry.getValue() instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/captcha/UnsignedMathGenerator.java b/bashi-framework/src/main/java/com/bashi/framework/captcha/UnsignedMathGenerator.java new file mode 100644 index 0000000..190c448 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/captcha/UnsignedMathGenerator.java @@ -0,0 +1,85 @@ +package com.bashi.framework.captcha; + +import cn.hutool.captcha.generator.CodeGenerator; +import cn.hutool.core.math.Calculator; +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; + +/** + * 无符号计算生成器 + * + * @author Lion Li + */ +public class UnsignedMathGenerator implements CodeGenerator { + + private static final long serialVersionUID = -5514819971774091076L; + + private static final String operators = "+-*"; + + /** + * 参与计算数字最大长度 + */ + private final int numberLength; + + /** + * 构造 + */ + public UnsignedMathGenerator() { + this(2); + } + + /** + * 构造 + * + * @param numberLength 参与计算最大数字位数 + */ + public UnsignedMathGenerator(int numberLength) { + this.numberLength = numberLength; + } + + @Override + public String generate() { + final int limit = getLimit(); + int min = RandomUtil.randomInt(limit); + int max = RandomUtil.randomInt(min, limit); + String number1 = Integer.toString(max); + String number2 = Integer.toString(min); + number1 = StrUtil.padAfter(number1, this.numberLength, CharUtil.SPACE); + number2 = StrUtil.padAfter(number2, this.numberLength, CharUtil.SPACE); + + return number1 + RandomUtil.randomChar(operators) + number2 + '='; + } + + @Override + public boolean verify(String code, String userInputCode) { + int result; + try { + result = Integer.parseInt(userInputCode); + } catch (NumberFormatException e) { + // 用户输入非数字 + return false; + } + + final int calculateResult = (int) Calculator.conversion(code); + return result == calculateResult; + } + + /** + * 获取验证码长度 + * + * @return 验证码长度 + */ + public int getLength() { + return this.numberLength * 2 + 2; + } + + /** + * 根据长度获取参与计算数字最大值 + * + * @return 最大值 + */ + private int getLimit() { + return Integer.parseInt("1" + StrUtil.repeat('0', this.numberLength)); + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/AdminServerConfig.java b/bashi-framework/src/main/java/com/bashi/framework/config/AdminServerConfig.java new file mode 100644 index 0000000..638fb46 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/AdminServerConfig.java @@ -0,0 +1,63 @@ +package com.bashi.framework.config; + +import de.codecentric.boot.admin.server.config.EnableAdminServer; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; +import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties; +import org.springframework.boot.task.TaskExecutorBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.thymeleaf.dialect.IDialect; +import org.thymeleaf.spring5.ISpringTemplateEngine; +import org.thymeleaf.spring5.SpringTemplateEngine; +import org.thymeleaf.templateresolver.ITemplateResolver; + +import java.util.Comparator; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.stream.Collectors; + +/** + * springboot-admin server配置类 + * + * @author Lion Li + */ +@Configuration +@EnableAdminServer +public class AdminServerConfig { + + @Lazy + @Bean(name = TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME) + @ConditionalOnMissingBean(Executor.class) + public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) { + return builder.build(); + } + + /** + * 解决 admin 与 项目 页面的交叉引用 将 admin 的路由放到最后 + * @param properties + * @param templateResolvers + * @param dialects + * @return + */ + @Bean + @ConditionalOnMissingBean(ISpringTemplateEngine.class) + SpringTemplateEngine templateEngine(ThymeleafProperties properties, + ObjectProvider templateResolvers, ObjectProvider dialects) { + SpringTemplateEngine engine = new SpringTemplateEngine(); + engine.setEnableSpringELCompiler(properties.isEnableSpringElCompiler()); + engine.setRenderHiddenMarkersBeforeCheckboxes(properties.isRenderHiddenMarkersBeforeCheckboxes()); + templateResolvers.orderedStream().forEach(engine::addTemplateResolver); + dialects.orderedStream().forEach(engine::addDialect); + Set templateResolvers1 = engine.getTemplateResolvers(); + templateResolvers1 = templateResolvers1.stream() + .sorted(Comparator.comparing(ITemplateResolver::getOrder)) + .collect(Collectors.toCollection(LinkedHashSet::new)); + engine.setTemplateResolvers(templateResolvers1); + return engine; + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/ApplicationConfig.java b/bashi-framework/src/main/java/com/bashi/framework/config/ApplicationConfig.java new file mode 100644 index 0000000..54e4759 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/ApplicationConfig.java @@ -0,0 +1,26 @@ +package com.bashi.framework.config; + +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +import java.util.TimeZone; + +/** + * 程序注解配置 + * + * @author Lion Li + */ +@Configuration +// 表示通过aop框架暴露该代理对象,AopContext能够访问 +@EnableAspectJAutoProxy(exposeProxy = true) +public class ApplicationConfig { + /** + * 时区配置 + */ + @Bean + public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() { + return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/AsyncConfig.java b/bashi-framework/src/main/java/com/bashi/framework/config/AsyncConfig.java new file mode 100644 index 0000000..30b8dc7 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/AsyncConfig.java @@ -0,0 +1,51 @@ +package com.bashi.framework.config; + +import com.bashi.common.exception.CustomException; +import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.AsyncConfigurerSupport; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService; + +import java.util.Arrays; +import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledExecutorService; + +/** + * 异步配置 + * + * @author Lion Li + */ +@EnableAsync +@Configuration +public class AsyncConfig extends AsyncConfigurerSupport { + + @Autowired + @Qualifier("scheduledExecutorService") + private ScheduledExecutorService scheduledExecutorService; + + /** + * 异步执行需要使用权限框架自带的包装线程池 保证权限信息的传递 + */ + @Override + public Executor getAsyncExecutor() { + return new DelegatingSecurityContextExecutorService(scheduledExecutorService); + } + + /** + * 异步执行异常处理 + */ + @Override + public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { + return (throwable, method, objects) -> { + throwable.printStackTrace(); + throw new CustomException( + "Exception message - " + throwable.getMessage() + + ", Method name - " + method.getName() + + ", Parameter value - " + Arrays.toString(objects)); + }; + } + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/CaptchaConfig.java b/bashi-framework/src/main/java/com/bashi/framework/config/CaptchaConfig.java new file mode 100644 index 0000000..e2c48ee --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/CaptchaConfig.java @@ -0,0 +1,55 @@ +package com.bashi.framework.config; + +import java.awt.*; + +import cn.hutool.captcha.*; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 验证码配置 + * + * @author Lion Li + */ +@Configuration +public class CaptchaConfig { + + private final int width = 160; + private final int height = 60; + private final Color background = Color.PINK; + private final Font font = new Font("Arial", Font.BOLD, 48); + + /** + * 圆圈干扰验证码 + */ + @Bean(name = "CircleCaptcha") + public CircleCaptcha getCircleCaptcha() { + CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(width, height); + captcha.setBackground(background); + captcha.setFont(font); + return captcha; + } + + /** + * 线段干扰的验证码 + */ + @Bean(name = "LineCaptcha") + public LineCaptcha getLineCaptcha() { + LineCaptcha captcha = CaptchaUtil.createLineCaptcha(width, height); + captcha.setBackground(background); + captcha.setFont(font); + return captcha; + } + + /** + * 扭曲干扰验证码 + */ + @Bean(name = "ShearCaptcha") + public ShearCaptcha getShearCaptcha() { + ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(width, height); + captcha.setBackground(background); + captcha.setFont(font); + return captcha; + } + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/DruidConfig.java b/bashi-framework/src/main/java/com/bashi/framework/config/DruidConfig.java new file mode 100644 index 0000000..3528625 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/DruidConfig.java @@ -0,0 +1,66 @@ +package com.bashi.framework.config; + +import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; +import com.alibaba.druid.util.Utils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.servlet.*; +import java.io.IOException; + +/** + * druid 配置多数据源 + * + * @author duteliang + */ +@Configuration +public class DruidConfig { + + /** + * 去除监控页面底部的广告 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true") + public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) + { + // 获取web监控页面的参数 + DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); + // 提取common.js的配置路径 + String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; + String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); + final String filePath = "support/http/resources/js/common.js"; + // 创建filter进行过滤 + Filter filter = new Filter() + { + @Override + public void init(javax.servlet.FilterConfig filterConfig) throws ServletException + { + } + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + chain.doFilter(request, response); + // 重置缓冲区,响应头不会被重置 +// response.resetBuffer(); + // 获取common.js + String text = Utils.readFromResource(filePath); + // 正则替换banner, 除去底部的广告信息 + text = text.replaceAll("
", ""); + text = text.replaceAll("powered.*?shrek.wang", ""); + response.getWriter().write(text); + } + @Override + public void destroy() + { + } + }; + FilterRegistrationBean registrationBean = new FilterRegistrationBean(); + registrationBean.setFilter(filter); + registrationBean.addUrlPatterns(commonJsPattern); + return registrationBean; + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/FeignConfig.java b/bashi-framework/src/main/java/com/bashi/framework/config/FeignConfig.java new file mode 100644 index 0000000..83c2342 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/FeignConfig.java @@ -0,0 +1,57 @@ +package com.bashi.framework.config; + +import feign.*; +import okhttp3.ConnectionPool; +import okhttp3.OkHttpClient; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.cloud.openfeign.FeignAutoConfiguration; +import org.springframework.cloud.openfeign.support.SpringMvcContract; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.concurrent.TimeUnit; + +/** + * openfeign配置类 + * + * @author Lion Li + */ +@EnableFeignClients("${feign.package}") +@Configuration +@ConditionalOnClass(Feign.class) +@AutoConfigureBefore(FeignAutoConfiguration.class) +public class FeignConfig { + + @Bean + public OkHttpClient okHttpClient(){ + return new OkHttpClient.Builder() + .readTimeout(60, TimeUnit.SECONDS) + .connectTimeout(60, TimeUnit.SECONDS) + .writeTimeout(120, TimeUnit.SECONDS) + .connectionPool(new ConnectionPool()) + .build(); + } + + @Bean + public Contract feignContract() { + return new SpringMvcContract(); + } + + @Bean + public Logger.Level feignLoggerLevel() { + return Logger.Level.BASIC; + } + + @Bean + public Request.Options feignRequestOptions() { + return new Request.Options(10, TimeUnit.SECONDS, 60,TimeUnit.SECONDS,true); + } + + @Bean + public Retryer feignRetry() { + return new Retryer.Default(); + } + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/FilterConfig.java b/bashi-framework/src/main/java/com/bashi/framework/config/FilterConfig.java new file mode 100644 index 0000000..c6388bf --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/FilterConfig.java @@ -0,0 +1,54 @@ +package com.bashi.framework.config; + +import cn.hutool.core.util.StrUtil; +import com.bashi.common.filter.RepeatableFilter; +import com.bashi.common.filter.XssFilter; +import com.bashi.framework.config.properties.XssProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.servlet.DispatcherType; +import java.util.HashMap; +import java.util.Map; + +/** + * Filter配置 + * + * @author Lion Li + */ +@Configuration +public class FilterConfig { + + @Autowired + private XssProperties xssProperties; + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Bean + public FilterRegistrationBean xssFilterRegistration() { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter()); + registration.addUrlPatterns(StrUtil.splitToArray(xssProperties.getUrlPatterns(), ",")); + registration.setName("xssFilter"); + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); + Map initParameters = new HashMap(); + initParameters.put("excludes", xssProperties.getExcludes()); + initParameters.put("enabled", xssProperties.getEnabled()); + registration.setInitParameters(initParameters); + return registration; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Bean + public FilterRegistrationBean someFilterRegistration() { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(new RepeatableFilter()); + registration.addUrlPatterns("/*"); + registration.setName("repeatableFilter"); + registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); + return registration; + } + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/JacksonConfig.java b/bashi-framework/src/main/java/com/bashi/framework/config/JacksonConfig.java new file mode 100644 index 0000000..5543d0b --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/JacksonConfig.java @@ -0,0 +1,50 @@ +package com.bashi.framework.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.bashi.common.utils.JsonUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.time.LocalDateTime; +import java.util.TimeZone; + +/** + * jackson 配置 + * + * @author Lion Li + */ +@Slf4j +@Configuration +public class JacksonConfig { + + @Bean + public BeanPostProcessor objectMapperBeanPostProcessor() { + return new BeanPostProcessor() { + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (!(bean instanceof ObjectMapper)) { + return bean; + } + ObjectMapper objectMapper = (ObjectMapper) bean; + // 全局配置序列化返回 JSON 处理 + SimpleModule simpleModule = new SimpleModule(); + simpleModule.addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE); + simpleModule.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE); + simpleModule.addSerializer(Long.class, ToStringSerializer.instance); + objectMapper.registerModule(simpleModule); + objectMapper.setTimeZone(TimeZone.getDefault()); + JsonUtils.init(objectMapper); + log.info("初始化 jackson 配置"); + return bean; + } + }; + } + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/MybatisPlusConfig.java b/bashi-framework/src/main/java/com/bashi/framework/config/MybatisPlusConfig.java new file mode 100644 index 0000000..0e50736 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/MybatisPlusConfig.java @@ -0,0 +1,108 @@ +package com.bashi.framework.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.baomidou.mybatisplus.core.injector.AbstractMethod; +import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector; +import com.baomidou.mybatisplus.core.injector.ISqlInjector; +import com.baomidou.mybatisplus.core.metadata.TableInfo; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import com.bashi.common.core.mybatisplus.methods.InsertAll; +import com.bashi.framework.mybatisplus.CreateAndUpdateMetaObjectHandler; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import java.util.List; + +/** + * mybatis-plus配置类 + * + * @author Lion Li + */ +@EnableTransactionManagement(proxyTargetClass = true) +@Configuration +// 指定要扫描的Mapper类的包的路径 +@MapperScan("${mybatis-plus.mapperPackage}") +public class MybatisPlusConfig { + + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 分页插件 + interceptor.addInnerInterceptor(paginationInnerInterceptor()); + // 乐观锁插件 + interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor()); + // 阻断插件 +// interceptor.addInnerInterceptor(blockAttackInnerInterceptor()); + return interceptor; + } + + /** + * 分页插件,自动识别数据库类型 + * https://baomidou.com/guide/interceptor-pagination.html + */ + public PaginationInnerInterceptor paginationInnerInterceptor() { + PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); + // 设置数据库类型为mysql + paginationInnerInterceptor.setDbType(DbType.MYSQL); + // 设置最大单页限制数量,默认 500 条,-1 不受限制 + paginationInnerInterceptor.setMaxLimit(-1L); + return paginationInnerInterceptor; + } + + /** + * 乐观锁插件 + * https://baomidou.com/guide/interceptor-optimistic-locker.html + */ + public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() { + return new OptimisticLockerInnerInterceptor(); + } + + /** + * 如果是对全表的删除或更新操作,就会终止该操作 + * https://baomidou.com/guide/interceptor-block-attack.html + */ +// public BlockAttackInnerInterceptor blockAttackInnerInterceptor() { +// return new BlockAttackInnerInterceptor(); +// } + + /** + * sql性能规范插件(垃圾SQL拦截) + * 如有需要可以启用 + */ +// public IllegalSQLInnerInterceptor illegalSQLInnerInterceptor() { +// return new IllegalSQLInnerInterceptor(); +// } + + + /** + * 元对象字段填充控制器 + * https://baomidou.com/guide/auto-fill-metainfo.html + */ + @Bean + public MetaObjectHandler metaObjectHandler() { + return new CreateAndUpdateMetaObjectHandler(); + } + + /** + * sql注入器配置 + * https://baomidou.com/guide/sql-injector.html + */ + @Bean + public ISqlInjector sqlInjector() { + return new DefaultSqlInjector() { + @Override + public List getMethodList(Class mapperClass, TableInfo tableInfo) { + List methodList = super.getMethodList(mapperClass,tableInfo); + methodList.add(new InsertAll()); + return methodList; + } + }; + } + + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/RedisConfig.java b/bashi-framework/src/main/java/com/bashi/framework/config/RedisConfig.java new file mode 100644 index 0000000..f2733f3 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/RedisConfig.java @@ -0,0 +1,110 @@ +package com.bashi.framework.config; + +import cn.hutool.core.util.StrUtil; +import com.bashi.framework.config.properties.RedissonProperties; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.client.codec.Codec; +import org.redisson.codec.JsonJacksonCodec; +import org.redisson.config.Config; +import org.redisson.spring.cache.CacheConfig; +import org.redisson.spring.cache.RedissonSpringCacheManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.io.IOException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.Map; + +/** + * redis配置 + * + * @author Lion Li + */ +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport { + + private static final String REDIS_PROTOCOL_PREFIX = "redis://"; + private static final String REDISS_PROTOCOL_PREFIX = "rediss://"; + + @Autowired + private RedisProperties redisProperties; + + @Autowired + private RedissonProperties redissonProperties; + + @Bean(destroyMethod = "shutdown") + @ConditionalOnMissingBean(RedissonClient.class) + public RedissonClient redisson() throws IOException { + String prefix = REDIS_PROTOCOL_PREFIX; + if (redisProperties.isSsl()) { + prefix = REDISS_PROTOCOL_PREFIX; + } + Config config = new Config(); + config.setThreads(redissonProperties.getThreads()) + .setNettyThreads(redissonProperties.getNettyThreads()) + .setCodec(JsonJacksonCodec.INSTANCE) + .setTransportMode(redissonProperties.getTransportMode()); + + RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig(); + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + // 解决jackson2无法反序列化LocalDateTime的问题 + om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + JavaTimeModule timeModule = new JavaTimeModule(); + timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + timeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + om.registerModule(timeModule); + config.setCodec(new JsonJacksonCodec(om)); + // 使用单机模式 + config.useSingleServer() + .setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort()) + .setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue()) + .setDatabase(redisProperties.getDatabase()) + .setPassword(StrUtil.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null) + .setTimeout(singleServerConfig.getTimeout()) + .setRetryAttempts(singleServerConfig.getRetryAttempts()) + .setRetryInterval(singleServerConfig.getRetryInterval()) + .setSubscriptionsPerConnection(singleServerConfig.getSubscriptionsPerConnection()) + .setClientName(singleServerConfig.getClientName()) + .setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout()) + .setSubscriptionConnectionMinimumIdleSize(singleServerConfig.getSubscriptionConnectionMinimumIdleSize()) + .setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize()) + .setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize()) + .setConnectionPoolSize(singleServerConfig.getConnectionPoolSize()) + .setDnsMonitoringInterval(singleServerConfig.getDnsMonitoringInterval()); + return Redisson.create(config); + } + + /** + * 整合spring-cache + */ + @Bean + public CacheManager cacheManager(RedissonClient redissonClient) { + Map config = new HashMap<>(); + config.put("redissonCacheMap", new CacheConfig(30*60*1000, 10*60*1000)); + return new RedissonSpringCacheManager(redissonClient, config, JsonJacksonCodec.INSTANCE); + } + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/ResourcesConfig.java b/bashi-framework/src/main/java/com/bashi/framework/config/ResourcesConfig.java new file mode 100644 index 0000000..62af1b3 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/ResourcesConfig.java @@ -0,0 +1,62 @@ +package com.bashi.framework.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import com.bashi.common.config.BsConfig; +import com.bashi.common.constant.Constants; +import com.bashi.framework.interceptor.RepeatSubmitInterceptor; + +/** + * 通用配置 + * + * @author duteliang + */ +@Configuration +public class ResourcesConfig implements WebMvcConfigurer +{ + @Autowired + private RepeatSubmitInterceptor repeatSubmitInterceptor; + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) + { + /** 本地文件上传路径 */ + registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**").addResourceLocations("file:" + BsConfig.getProfile() + "/"); + } + + /** + * 自定义拦截规则 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) + { + registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**"); + } + + /** + * 跨域配置 + */ + @Bean + public CorsFilter corsFilter() + { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + // 设置访问源地址 + config.addAllowedOriginPattern("*"); + // 设置访问源请求头 + config.addAllowedHeader("*"); + // 设置访问源请求方法 + config.addAllowedMethod("*"); + // 对接口配置跨域设置 + source.registerCorsConfiguration("/**", config); + return new CorsFilter(source); + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/SecurityConfig.java b/bashi-framework/src/main/java/com/bashi/framework/config/SecurityConfig.java new file mode 100644 index 0000000..6c60c1a --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/SecurityConfig.java @@ -0,0 +1,172 @@ +package com.bashi.framework.config; + +import com.bashi.framework.security.filter.JwtAuthenticationTokenFilter; +import com.bashi.framework.security.handle.AuthenticationEntryPointImpl; +import com.bashi.framework.security.handle.LogoutSuccessHandlerImpl; +import de.codecentric.boot.admin.server.config.AdminServerProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.web.filter.CorsFilter; + +import javax.annotation.Resource; + +/** + * spring security配置 + * + * @author duteliang + */ +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) +@EnableWebSecurity(debug = false) +public class SecurityConfig extends WebSecurityConfigurerAdapter +{ + /** + * 自定义用户认证逻辑 + */ + @Resource + private UserDetailsService userDetailsService; + + /** + * 认证失败处理类 + */ + @Autowired + private AuthenticationEntryPointImpl unauthorizedHandler; + + /** + * 退出处理类 + */ + @Autowired + private LogoutSuccessHandlerImpl logoutSuccessHandler; + + /** + * token认证过滤器 + */ + @Autowired + private JwtAuthenticationTokenFilter authenticationTokenFilter; + + /** + * 跨域过滤器 + */ + @Autowired + private CorsFilter corsFilter; + + @Autowired + private AdminServerProperties adminServerProperties; + + @Autowired + private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig; + + /** + * 解决 无法直接注入 AuthenticationManager + * + * @return + * @throws Exception + */ + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception + { + return super.authenticationManagerBean(); + } + + /** + * anyRequest | 匹配所有请求路径 + * access | SpringEl表达式结果为true时可以访问 + * anonymous | 匿名可以访问 + * denyAll | 用户不能访问 + * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) + * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 + * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 + * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 + * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 + * hasRole | 如果有参数,参数表示角色,则其角色可以访问 + * permitAll | 用户可以任意访问 + * rememberMe | 允许通过remember-me登录的用户访问 + * authenticated | 用户登录后可访问 + */ + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception + { + httpSecurity + // CSRF禁用,因为不使用session + .csrf().disable() + // 认证失败处理类 + .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() + // 基于token,所以不需要session + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() + // 过滤请求 + .authorizeRequests() + // 对于登录login 验证码captchaImage 允许匿名访问 + .antMatchers("/login", "/captchaImage", + "/customer/login","/customer/open/**" + ).anonymous() + .antMatchers( + HttpMethod.GET, + "/*.html", + "/**/*.html", + "/**/*.css", + "/download/**", + "/**/*.js" + ).permitAll() + .antMatchers("/ws/**").permitAll() + .antMatchers("/open/common/upload").permitAll() + .antMatchers("/app/home/**").permitAll() + .antMatchers("/pay/notify/**").permitAll() + .antMatchers("/profile/**").anonymous() + .antMatchers("/common/download**").anonymous() + .antMatchers("/common/download/resource**").anonymous() + .antMatchers("/doc.html").anonymous() + .antMatchers("/swagger-resources/**").anonymous() + .antMatchers("/webjars/**").anonymous() + .antMatchers("/*/api-docs").anonymous() +// .antMatchers("/druid/**").anonymous() + // Spring Boot Admin Server 的安全配置 + .antMatchers(adminServerProperties.getContextPath()).anonymous() + .antMatchers(adminServerProperties.getContextPath() + "/**").anonymous() + // Spring Boot Actuator 的安全配置 +// .antMatchers("/actuator").anonymous() +// .antMatchers("/actuator/**").anonymous() + // 除上面外的所有请求全部需要鉴权认证 + .anyRequest().authenticated() + .and() + .headers().frameOptions().disable() + .and() + .apply(smsCodeAuthenticationSecurityConfig); + httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); + // 添加JWT filter + httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); + // 添加CORS filter + httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); + httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); + } + + + /** + * 强散列哈希加密实现 + */ + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder() + { + return new BCryptPasswordEncoder(); + } + + /** + * 身份认证接口 + */ + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception + { + auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/ServerConfig.java b/bashi-framework/src/main/java/com/bashi/framework/config/ServerConfig.java new file mode 100644 index 0000000..e0f115d --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/ServerConfig.java @@ -0,0 +1,33 @@ +package com.bashi.framework.config; + +import javax.servlet.http.HttpServletRequest; + +import com.bashi.common.utils.ServletUtils; +import org.springframework.stereotype.Component; + +/** + * 服务相关配置 + * + * @author duteliang + */ +@Component +public class ServerConfig +{ + /** + * 获取完整的请求路径,包括:域名,端口,上下文访问路径 + * + * @return 服务地址 + */ + public String getUrl() + { + HttpServletRequest request = ServletUtils.getRequest(); + return getDomain(request); + } + + public static String getDomain(HttpServletRequest request) + { + StringBuffer url = request.getRequestURL(); + String contextPath = request.getServletContext().getContextPath(); + return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString(); + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/SmsCodeAuthenticationSecurityConfig.java b/bashi-framework/src/main/java/com/bashi/framework/config/SmsCodeAuthenticationSecurityConfig.java new file mode 100644 index 0000000..70ec9a4 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/SmsCodeAuthenticationSecurityConfig.java @@ -0,0 +1,47 @@ +package com.bashi.framework.config; + +import com.bashi.framework.security.sms.*; +import com.bashi.framework.web.service.CodeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.SecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.DefaultSecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.stereotype.Component; + +/** + *

created on 2021/7/13

+ * + * @author zhangliang + */ +@Component +public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter { + + @Autowired + private IDuteUserDetailsService userDetailsService; + @Autowired + private CodeService codeService; + @Autowired + private SmsAuthenticationSuccessHandler smsAuthenticationSuccessHandler; + @Autowired + private SmsAuthenticationFailureHandler smsAuthenticationFailureHandler; + + @Override + public void configure(HttpSecurity http) { + // 手机号登陆 + SmsAuthenticationFilter smsAuthenticationFilter = new SmsAuthenticationFilter(); + smsAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class)); + smsAuthenticationFilter.setAuthenticationSuccessHandler(smsAuthenticationSuccessHandler); + smsAuthenticationFilter.setAuthenticationFailureHandler(smsAuthenticationFailureHandler); + smsAuthenticationFilter.setCodeService(codeService); + + SmsAuthenticationProvider smsAuthenticationProvider = new SmsAuthenticationProvider(); + smsAuthenticationProvider.setUserDetailsService(userDetailsService); + smsAuthenticationProvider.setCodeService(codeService); + + http.authenticationProvider(smsAuthenticationProvider) + .addFilterAfter(smsAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + } + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/SwaggerConfig.java b/bashi-framework/src/main/java/com/bashi/framework/config/SwaggerConfig.java new file mode 100644 index 0000000..a5f60eb --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/SwaggerConfig.java @@ -0,0 +1,108 @@ +package com.bashi.framework.config; + +import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; +import com.bashi.framework.config.properties.SwaggerProperties; +import io.swagger.annotations.Api; +import io.swagger.models.auth.In; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.*; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.service.contexts.SecurityContext; +import springfox.documentation.spring.web.plugins.Docket; + +import java.util.ArrayList; +import java.util.List; + +/** + * Swagger 文档配置 + * + * @author Lion Li + */ +@Configuration +@EnableKnife4j +public class SwaggerConfig { + + @Autowired + private SwaggerProperties swaggerProperties; + + /** + * 创建API + */ + @Bean + public Docket createRestApi() { + return new Docket(DocumentationType.OAS_30) + .enable(swaggerProperties.getEnabled()) + // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息) + .apiInfo(apiInfo()) + // 设置哪些接口暴露给Swagger展示 + .select() + // 扫描所有有注解的api,用这种方式更灵活 + .apis(RequestHandlerSelectors.withClassAnnotation(Api.class)) + // 扫描指定包中的swagger注解 + // .apis(RequestHandlerSelectors.basePackage("com.bashi.project.tool.swagger")) + // 扫描所有 .apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.any()) + .build() + /* 设置安全模式,swagger可以设置访问token */ + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()) + .pathMapping(swaggerProperties.getPathMapping()); + } + + /** + * 安全模式,这里指定token通过Authorization头请求头传递 + */ + private List securitySchemes() { + List apiKeyList = new ArrayList(); + apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue())); + return apiKeyList; + } + + /** + * 安全上下文 + */ + private List securityContexts() { + List securityContexts = new ArrayList<>(); + securityContexts.add( + SecurityContext.builder() + .securityReferences(defaultAuth()) + .operationSelector(o -> o.requestMappingPattern().matches("/.*")) + .build()); + return securityContexts; + } + + /** + * 默认的安全上引用 + */ + private List defaultAuth() { + AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); + AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; + authorizationScopes[0] = authorizationScope; + List securityReferences = new ArrayList<>(); + securityReferences.add(new SecurityReference("Authorization", authorizationScopes)); + return securityReferences; + } + + /** + * 添加摘要信息 + */ + private ApiInfo apiInfo() { + // 用ApiInfoBuilder进行定制 + SwaggerProperties.Contact contact = swaggerProperties.getContact(); + return new ApiInfoBuilder() + // 设置标题 + .title(swaggerProperties.getTitle()) + // 描述 + .description(swaggerProperties.getDescription()) + // 作者信息 + .contact(new Contact(contact.getName(), contact.getUrl(), contact.getEmail())) + // 版本 + .version(swaggerProperties.getVersion()) + .build(); + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/ThreadPoolConfig.java b/bashi-framework/src/main/java/com/bashi/framework/config/ThreadPoolConfig.java new file mode 100644 index 0000000..765576b --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/ThreadPoolConfig.java @@ -0,0 +1,70 @@ +package com.bashi.framework.config; + +import com.bashi.common.utils.Threads; +import com.bashi.framework.config.properties.ThreadPoolProperties; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 线程池配置 + * + * @author Lion Li + **/ +@Configuration +public class ThreadPoolConfig { + + @Autowired + private ThreadPoolProperties threadPoolProperties; + + @Bean("threadPoolTaskExecutor") + @ConditionalOnProperty(prefix = "threadPoolTaskExecutor", name = "enabled", havingValue = "true") + public ThreadPoolTaskExecutor threadPoolTaskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setMaxPoolSize(threadPoolProperties.getMaxPoolSize()); + executor.setCorePoolSize(threadPoolProperties.getCorePoolSize()); + executor.setQueueCapacity(threadPoolProperties.getQueueCapacity()); + executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds()); + executor.setThreadNamePrefix("new-591-"); + RejectedExecutionHandler handler; + switch (threadPoolProperties.getRejectedExecutionHandler()) { + case "CallerRunsPolicy": + handler = new ThreadPoolExecutor.CallerRunsPolicy(); + break; + case "DiscardOldestPolicy": + handler = new ThreadPoolExecutor.DiscardOldestPolicy(); + break; + case "DiscardPolicy": + handler = new ThreadPoolExecutor.DiscardPolicy(); + break; + default: + handler = new ThreadPoolExecutor.AbortPolicy(); + break; + } + executor.setRejectedExecutionHandler(handler); + return executor; + } + + /** + * 执行周期性或定时任务 + */ + @Bean(name = "scheduledExecutorService") + protected ScheduledExecutorService scheduledExecutorService() { + return new ScheduledThreadPoolExecutor(threadPoolProperties.getCorePoolSize(), + new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build()) { + @Override + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + Threads.printException(r, t); + } + }; + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/ValidatorConfig.java b/bashi-framework/src/main/java/com/bashi/framework/config/ValidatorConfig.java new file mode 100644 index 0000000..8573cca --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/ValidatorConfig.java @@ -0,0 +1,31 @@ +package com.bashi.framework.config; + +import org.hibernate.validator.HibernateValidator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; + +/** + * 校验框架配置类 + * + * @author Lion Li + */ +@Configuration +public class ValidatorConfig { + + /** + * 配置校验框架 快速返回模式 + */ + @Bean + public Validator validator() { + ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class) + .configure() + .failFast(true) + .buildValidatorFactory(); + return validatorFactory.getValidator(); + } + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/WebSocketConfig.java b/bashi-framework/src/main/java/com/bashi/framework/config/WebSocketConfig.java new file mode 100644 index 0000000..6ebcd31 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/WebSocketConfig.java @@ -0,0 +1,14 @@ +package com.bashi.framework.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +@Configuration +public class WebSocketConfig { + + @Bean + public ServerEndpointExporter serverEndpointExporter(){ + return new ServerEndpointExporter(); + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/WebSocketServer.java b/bashi-framework/src/main/java/com/bashi/framework/config/WebSocketServer.java new file mode 100644 index 0000000..4a2b895 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/WebSocketServer.java @@ -0,0 +1,146 @@ +package com.bashi.framework.config; + +import com.bashi.common.utils.JsonUtils; +import com.bashi.framework.dto.WebSocketMessageDTO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.websocket.OnClose; +import javax.websocket.OnError; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; +import java.io.IOException; +import java.util.concurrent.ConcurrentHashMap; + +@ServerEndpoint("/ws/{uuid}") +@Component +@Slf4j +public class WebSocketServer { + + /**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/ + private static int onlineCount = 0; + /**concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。*/ + private static ConcurrentHashMap webSocketMap = new ConcurrentHashMap<>(); + /**与某个客户端的连接会话,需要通过它来给客户端发送数据*/ + private Session session; + /**接收userId*/ + private String uuidKey =""; + + /** + * 连接建立成功调用的方法*/ + @OnOpen + public void onOpen(Session session,@PathParam("uuid") String uuid) { + this.session = session; + this.uuidKey =uuid; + if(webSocketMap.containsKey(uuid)){ + webSocketMap.remove(uuid); + webSocketMap.put(uuid,this); + //加入set中 + }else{ + webSocketMap.put(uuid,this); + //加入set中 + addOnlineCount(); + //在线数加1 + } + + log.info("用户连接:"+uuid+",当前在线人数为:" + getOnlineCount()); + + try { + sendMessage("连接成功"); + } catch (IOException e) { + log.error("用户:"+uuid+",网络异常!!!!!!"); + } + } + + /** + * 连接关闭调用的方法 + */ + @OnClose + public void onClose() { + if(webSocketMap.containsKey(uuidKey)){ + webSocketMap.remove(uuidKey); + //从set中删除 + subOnlineCount(); + } + log.info("用户退出:"+ uuidKey +",当前在线人数为:" + getOnlineCount()); + } + +// /** +// * 收到客户端消息后调用的方法 +// * +// * @param message 客户端发送过来的消息*/ +// @OnMessage +// public void onMessage(String message, Session session) { +// log.info("用户消息:"+userId+",报文:"+message); +// //可以群发消息 +// //消息保存到数据库、redis +// if(StringUtils.isNotBlank(message)){ +// try { +// //解析发送的报文 +// JSONObject jsonObject = JSON.parseObject(message); +// //追加发送人(防止串改) +// jsonObject.put("fromUserId",this.userId); +// String toUserId=jsonObject.getString("toUserId"); +// //传送给对应toUserId用户的websocket +// if(StringUtils.isNotBlank(toUserId)&&webSocketMap.containsKey(toUserId)){ +// webSocketMap.get(toUserId).sendMessage(jsonObject.toJSONString()); +// }else{ +// log.error("请求的userId:"+toUserId+"不在该服务器上"); +// //否则不在这个服务器上,发送到mysql或者redis +// } +// }catch (Exception e){ +// e.printStackTrace(); +// } +// } +// } + + /** + * + * @param session + * @param error + */ + @OnError + public void onError(Session session, Throwable error) { + log.error("用户错误:"+this.uuidKey +",原因:"+error.getMessage()); + error.printStackTrace(); + } + + /** + * 实现服务器主动推送 + */ + public void sendMessage(String message) throws IOException { + this.session.getBasicRemote().sendText(message); + } + + /** + * 发送自定义消息 + * */ + /*public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException { + log.info("发送消息到:"+userId+",报文:"+message); + if(StringUtils.isNotBlank(userId)&&webSocketMap.containsKey(userId)){ + webSocketMap.get(userId).sendMessage(message); + }else{ + log.error("用户"+userId+",不在线!"); + } + }*/ + + public static void sendInfoAll(WebSocketMessageDTO message) throws IOException { + for (WebSocketServer value : webSocketMap.values()) { + value.sendMessage(JsonUtils.toJsonString(message)); + } + } + + public static synchronized int getOnlineCount() { + return onlineCount; + } + + public static synchronized void addOnlineCount() { + WebSocketServer.onlineCount++; + } + + public static synchronized void subOnlineCount() { + WebSocketServer.onlineCount--; + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/WebSocketServerDemo.java b/bashi-framework/src/main/java/com/bashi/framework/config/WebSocketServerDemo.java new file mode 100644 index 0000000..5093ce2 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/WebSocketServerDemo.java @@ -0,0 +1,143 @@ +package com.bashi.framework.config; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import javax.websocket.OnClose; +import javax.websocket.OnError; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import java.io.IOException; +import java.util.concurrent.ConcurrentHashMap; + +//@ServerEndpoint("/ws/{userId}") +//@Component +@Slf4j +public class WebSocketServerDemo { + + /**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/ + private static int onlineCount = 0; + /**concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。*/ + private static ConcurrentHashMap webSocketMap = new ConcurrentHashMap<>(); + /**与某个客户端的连接会话,需要通过它来给客户端发送数据*/ + private Session session; + /**接收userId*/ + private String userId=""; + + /** + * 连接建立成功调用的方法*/ + @OnOpen + public void onOpen(Session session,@PathParam("userId") String userId) { + this.session = session; + this.userId=userId; + if(webSocketMap.containsKey(userId)){ + webSocketMap.remove(userId); + webSocketMap.put(userId,this); + //加入set中 + }else{ + webSocketMap.put(userId,this); + //加入set中 + addOnlineCount(); + //在线数加1 + } + + log.info("用户连接:"+userId+",当前在线人数为:" + getOnlineCount()); + + try { + sendMessage("连接成功"); + } catch (IOException e) { + log.error("用户:"+userId+",网络异常!!!!!!"); + } + } + + /** + * 连接关闭调用的方法 + */ + @OnClose + public void onClose() { + if(webSocketMap.containsKey(userId)){ + webSocketMap.remove(userId); + //从set中删除 + subOnlineCount(); + } + log.info("用户退出:"+userId+",当前在线人数为:" + getOnlineCount()); + } + +// /** +// * 收到客户端消息后调用的方法 +// * +// * @param message 客户端发送过来的消息*/ +// @OnMessage +// public void onMessage(String message, Session session) { +// log.info("用户消息:"+userId+",报文:"+message); +// //可以群发消息 +// //消息保存到数据库、redis +// if(StringUtils.isNotBlank(message)){ +// try { +// //解析发送的报文 +// JSONObject jsonObject = JSON.parseObject(message); +// //追加发送人(防止串改) +// jsonObject.put("fromUserId",this.userId); +// String toUserId=jsonObject.getString("toUserId"); +// //传送给对应toUserId用户的websocket +// if(StringUtils.isNotBlank(toUserId)&&webSocketMap.containsKey(toUserId)){ +// webSocketMap.get(toUserId).sendMessage(jsonObject.toJSONString()); +// }else{ +// log.error("请求的userId:"+toUserId+"不在该服务器上"); +// //否则不在这个服务器上,发送到mysql或者redis +// } +// }catch (Exception e){ +// e.printStackTrace(); +// } +// } +// } + + /** + * + * @param session + * @param error + */ + @OnError + public void onError(Session session, Throwable error) { + log.error("用户错误:"+this.userId+",原因:"+error.getMessage()); + error.printStackTrace(); + } + + /** + * 实现服务器主动推送 + */ + public void sendMessage(String message) throws IOException { + this.session.getBasicRemote().sendText(message); + } + + /** + * 发送自定义消息 + * */ + public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException { + log.info("发送消息到:"+userId+",报文:"+message); + if(StringUtils.isNotBlank(userId)&&webSocketMap.containsKey(userId)){ + webSocketMap.get(userId).sendMessage(message); + }else{ + log.error("用户"+userId+",不在线!"); + } + } + + public static void sendInfoAll(String message) throws IOException { + for (WebSocketServerDemo value : webSocketMap.values()) { + value.sendMessage(message); + } + } + + public static synchronized int getOnlineCount() { + return onlineCount; + } + + public static synchronized void addOnlineCount() { + WebSocketServerDemo.onlineCount++; + } + + public static synchronized void subOnlineCount() { + WebSocketServerDemo.onlineCount--; + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/properties/CaptchaProperties.java b/bashi-framework/src/main/java/com/bashi/framework/config/properties/CaptchaProperties.java new file mode 100644 index 0000000..404b657 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/properties/CaptchaProperties.java @@ -0,0 +1,41 @@ +package com.bashi.framework.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 验证码 配置属性 + * + * @author Lion Li + */ +@Data +@Component +@ConfigurationProperties(prefix = "captcha") +public class CaptchaProperties { + + /** + * 验证码开关 + */ + private Boolean enabled; + + /** + * 验证码类型 + */ + private String type; + + /** + * 验证码类别 + */ + private String category; + + /** + * 数字验证码位数 + */ + private Integer numberLength; + + /** + * 字符验证码长度 + */ + private Integer charLength; +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/properties/RedissonProperties.java b/bashi-framework/src/main/java/com/bashi/framework/config/properties/RedissonProperties.java new file mode 100644 index 0000000..484017c --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/properties/RedissonProperties.java @@ -0,0 +1,100 @@ +package com.bashi.framework.config.properties; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.redisson.config.TransportMode; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * Redisson 配置属性 + * + * @author Lion Li + */ +@Data +@Component +@ConfigurationProperties(prefix = "redisson") +public class RedissonProperties { + + /** + * 线程池数量,默认值 = 当前处理核数量 * 2 + */ + private int threads; + + /** + * Netty线程池数量,默认值 = 当前处理核数量 * 2 + */ + private int nettyThreads; + + /** + * 传输模式 + */ + private TransportMode transportMode; + + /** + * 单机服务配置 + */ + private SingleServerConfig singleServerConfig; + + @Data + @NoArgsConstructor + public static class SingleServerConfig { + + /** + * 客户端名称 + */ + private String clientName; + + /** + * 最小空闲连接数 + */ + private int connectionMinimumIdleSize; + + /** + * 连接池大小 + */ + private int connectionPoolSize; + + /** + * 连接空闲超时,单位:毫秒 + */ + private int idleConnectionTimeout; + + /** + * 命令等待超时,单位:毫秒 + */ + private int timeout; + + /** + * 如果尝试在此限制之内发送成功,则开始启用 timeout 计时。 + */ + private int retryAttempts; + + /** + * 命令重试发送时间间隔,单位:毫秒 + */ + private int retryInterval; + + /** + * 发布和订阅连接的最小空闲连接数 + */ + private int subscriptionConnectionMinimumIdleSize; + + /** + * 发布和订阅连接池大小 + */ + private int subscriptionConnectionPoolSize; + + /** + * 单个连接最大订阅数量 + */ + private int subscriptionsPerConnection; + + /** + * DNS监测时间间隔,单位:毫秒 + */ + private int dnsMonitoringInterval; + + } + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/properties/SwaggerProperties.java b/bashi-framework/src/main/java/com/bashi/framework/config/properties/SwaggerProperties.java new file mode 100644 index 0000000..32c3266 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/properties/SwaggerProperties.java @@ -0,0 +1,63 @@ +package com.bashi.framework.config.properties; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * swagger 配置属性 + * + * @author Lion Li + */ +@Data +@Component +@ConfigurationProperties(prefix = "swagger") +public class SwaggerProperties { + + /** + * 验证码类型 + */ + private Boolean enabled; + /** + * 设置请求的统一前缀 + */ + private String pathMapping; + /** + * 验证码类别 + */ + private String title; + /** + * 数字验证码位数 + */ + private String description; + /** + * 字符验证码长度 + */ + private String version; + + /** + * 联系方式 + */ + private Contact contact; + + @Data + @NoArgsConstructor + public static class Contact{ + + /** + * 联系人 + **/ + private String name; + /** + * 联系人url + **/ + private String url; + /** + * 联系人email + **/ + private String email; + + } + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/properties/ThreadPoolProperties.java b/bashi-framework/src/main/java/com/bashi/framework/config/properties/ThreadPoolProperties.java new file mode 100644 index 0000000..3a5e885 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/properties/ThreadPoolProperties.java @@ -0,0 +1,47 @@ +package com.bashi.framework.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 线程池 配置属性 + * + * @author Lion Li + */ +@Data +@Component +@ConfigurationProperties(prefix = "thread-pool") +public class ThreadPoolProperties { + + /** + * 是否开启线程池 + */ + private boolean enabled; + + /** + * 核心线程池大小 + */ + private int corePoolSize; + + /** + * 最大可创建的线程数 + */ + private int maxPoolSize; + + /** + * 队列最大长度 + */ + private int queueCapacity; + + /** + * 线程池维护线程所允许的空闲时间 + */ + private int keepAliveSeconds; + + /** + * 线程池对拒绝任务(无线程可用)的处理策略 + */ + private String rejectedExecutionHandler; + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/properties/TokenProperties.java b/bashi-framework/src/main/java/com/bashi/framework/config/properties/TokenProperties.java new file mode 100644 index 0000000..ab22b42 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/properties/TokenProperties.java @@ -0,0 +1,31 @@ +package com.bashi.framework.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * token 配置属性 + * + * @author Lion Li + */ +@Data +@Component +@ConfigurationProperties(prefix = "token") +public class TokenProperties { + + /** + * 令牌自定义标识 + */ + private String header; + + /** + * 令牌秘钥 + */ + private String secret; + + /** + * 令牌有效期(默认30分钟) + */ + private int expireTime; +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/config/properties/XssProperties.java b/bashi-framework/src/main/java/com/bashi/framework/config/properties/XssProperties.java new file mode 100644 index 0000000..ab321ca --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/config/properties/XssProperties.java @@ -0,0 +1,32 @@ +package com.bashi.framework.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * xss过滤 配置属性 + * + * @author Lion Li + */ +@Data +@Component +@ConfigurationProperties(prefix = "xss") +public class XssProperties { + + /** + * 过滤开关 + */ + private String enabled; + + /** + * 排除链接(多个用逗号分隔) + */ + private String excludes; + + /** + * 匹配链接 + */ + private String urlPatterns; + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/constant/CodeType.java b/bashi-framework/src/main/java/com/bashi/framework/constant/CodeType.java new file mode 100644 index 0000000..5db6ddd --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/constant/CodeType.java @@ -0,0 +1,17 @@ +package com.bashi.framework.constant; + +/** + *

created on 2021/7/15

+ * + * @author zhangliang + */ +public enum CodeType { + + LOGIN, + JOIN_MECHANIC, + JOIN_SHOP, + CUSTOMER_REGISTER, + CUSTOMER_FORGET_PASSWORD, + + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/dto/WebSocketMessageDTO.java b/bashi-framework/src/main/java/com/bashi/framework/dto/WebSocketMessageDTO.java new file mode 100644 index 0000000..22babb0 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/dto/WebSocketMessageDTO.java @@ -0,0 +1,10 @@ +package com.bashi.framework.dto; + +import lombok.Data; + +@Data +public class WebSocketMessageDTO { + + private WebSocketMessageType type = WebSocketMessageType.ORDER_NOTIFY; + private String city; +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/dto/WebSocketMessageType.java b/bashi-framework/src/main/java/com/bashi/framework/dto/WebSocketMessageType.java new file mode 100644 index 0000000..7003624 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/dto/WebSocketMessageType.java @@ -0,0 +1,5 @@ +package com.bashi.framework.dto; + +public enum WebSocketMessageType { + ORDER_NOTIFY +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/interceptor/RepeatSubmitInterceptor.java b/bashi-framework/src/main/java/com/bashi/framework/interceptor/RepeatSubmitInterceptor.java new file mode 100644 index 0000000..8d9d797 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/interceptor/RepeatSubmitInterceptor.java @@ -0,0 +1,56 @@ +package com.bashi.framework.interceptor; + +import com.bashi.common.annotation.RepeatSubmit; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.utils.JsonUtils; +import com.bashi.common.utils.ServletUtils; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.lang.reflect.Method; + +/** + * 防止重复提交拦截器 + * + * @author duteliang + */ +@Component +public abstract class RepeatSubmitInterceptor extends HandlerInterceptorAdapter +{ + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception + { + if (handler instanceof HandlerMethod) + { + HandlerMethod handlerMethod = (HandlerMethod) handler; + Method method = handlerMethod.getMethod(); + RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); + if (annotation != null) + { + if (this.isRepeatSubmit(request)) + { + AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试"); + ServletUtils.renderString(response, JsonUtils.toJsonString(ajaxResult)); + return false; + } + } + return true; + } + else + { + return super.preHandle(request, response, handler); + } + } + + /** + * 验证是否重复提交由子类实现具体的防重复提交的规则 + * + * @param request + * @return + * @throws Exception + */ + public abstract boolean isRepeatSubmit(HttpServletRequest request); +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/interceptor/impl/SameUrlDataInterceptor.java b/bashi-framework/src/main/java/com/bashi/framework/interceptor/impl/SameUrlDataInterceptor.java new file mode 100644 index 0000000..f1c29f3 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/interceptor/impl/SameUrlDataInterceptor.java @@ -0,0 +1,133 @@ +package com.bashi.framework.interceptor.impl; + +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.lang.Validator; +import com.bashi.common.constant.Constants; +import com.bashi.common.core.redis.RedisCache; +import com.bashi.common.filter.RepeatedlyRequestWrapper; +import com.bashi.common.utils.JsonUtils; +import com.bashi.framework.interceptor.RepeatSubmitInterceptor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * 判断请求url和数据是否和上一次相同, + * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。 + * + * @author duteliang + */ +@Slf4j +@Component +public class SameUrlDataInterceptor extends RepeatSubmitInterceptor +{ + public final String REPEAT_PARAMS = "repeatParams"; + + public final String REPEAT_TIME = "repeatTime"; + + // 令牌自定义标识 + @Value("${token.header}") + private String header; + + @Autowired + private RedisCache redisCache; + + /** + * 间隔时间,单位:秒 默认10秒 + * + * 两次相同参数的请求,如果间隔时间大于该参数,系统不会认定为重复提交的数据 + */ + private int intervalTime = 10; + + public void setIntervalTime(int intervalTime) + { + this.intervalTime = intervalTime; + } + + @SuppressWarnings("unchecked") + @Override + public boolean isRepeatSubmit(HttpServletRequest request) + { + String nowParams = ""; + if (request instanceof RepeatedlyRequestWrapper) + { + RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; + try { + nowParams = IoUtil.readUtf8(repeatedlyRequest.getInputStream()); + } catch (IOException e) { + log.warn("读取流出现问题!"); + } + } + + // body参数为空,获取Parameter的数据 + if (Validator.isEmpty(nowParams)) + { + nowParams = JsonUtils.toJsonString(request.getParameterMap()); + } + Map nowDataMap = new HashMap(); + nowDataMap.put(REPEAT_PARAMS, nowParams); + nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); + + // 请求地址(作为存放cache的key值) + String url = request.getRequestURI(); + + // 唯一值(没有消息头则使用请求地址) + String submitKey = request.getHeader(header); + if (Validator.isEmpty(submitKey)) + { + submitKey = url; + } + + // 唯一标识(指定key + 消息头) + String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey; + + Object sessionObj = redisCache.getCacheObject(cacheRepeatKey); + if (sessionObj != null) + { + Map sessionMap = (Map) sessionObj; + if (sessionMap.containsKey(url)) + { + Map preDataMap = (Map) sessionMap.get(url); + if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap)) + { + return true; + } + } + } + Map cacheMap = new HashMap(); + cacheMap.put(url, nowDataMap); + redisCache.setCacheObject(cacheRepeatKey, cacheMap, intervalTime, TimeUnit.SECONDS); + return false; + } + + /** + * 判断参数是否相同 + */ + private boolean compareParams(Map nowMap, Map preMap) + { + String nowParams = (String) nowMap.get(REPEAT_PARAMS); + String preParams = (String) preMap.get(REPEAT_PARAMS); + return nowParams.equals(preParams); + } + + /** + * 判断两次间隔时间 + */ + private boolean compareTime(Map nowMap, Map preMap) + { + long time1 = (Long) nowMap.get(REPEAT_TIME); + long time2 = (Long) preMap.get(REPEAT_TIME); + if ((time1 - time2) < (this.intervalTime * 1000)) + { + return true; + } + return false; + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/manager/ShutdownManager.java b/bashi-framework/src/main/java/com/bashi/framework/manager/ShutdownManager.java new file mode 100644 index 0000000..effc78d --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/manager/ShutdownManager.java @@ -0,0 +1,41 @@ +package com.bashi.framework.manager; + +import com.bashi.common.utils.Threads; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +import javax.annotation.PreDestroy; +import java.util.concurrent.ScheduledExecutorService; + +/** + * 确保应用退出时能关闭后台线程 + * + * @author Lion Li + */ +@Slf4j(topic = "sys-user") +@Component +public class ShutdownManager { + + @Autowired + @Qualifier("scheduledExecutorService") + private ScheduledExecutorService scheduledExecutorService; + + @PreDestroy + public void destroy() { + shutdownAsyncManager(); + } + + /** + * 停止异步执行任务 + */ + private void shutdownAsyncManager() { + try { + log.info("====关闭后台任务任务线程池===="); + Threads.shutdownAndAwaitTermination(scheduledExecutorService); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/mybatisplus/CreateAndUpdateMetaObjectHandler.java b/bashi-framework/src/main/java/com/bashi/framework/mybatisplus/CreateAndUpdateMetaObjectHandler.java new file mode 100644 index 0000000..b67f3e1 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/mybatisplus/CreateAndUpdateMetaObjectHandler.java @@ -0,0 +1,45 @@ +package com.bashi.framework.mybatisplus; + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.bashi.common.utils.SecurityUtils; +import org.apache.ibatis.reflection.MetaObject; + +import java.util.Date; + +/** + * MP注入处理器 + * @author Lion Li + * @date 2021/4/25 + */ +public class CreateAndUpdateMetaObjectHandler implements MetaObjectHandler { + + @Override + public void insertFill(MetaObject metaObject) { + //根据属性名字设置要填充的值 + if (metaObject.hasGetter("createTime")) { + if (metaObject.getValue("createTime") == null) { + this.setFieldValByName("createTime", new Date(), metaObject); + } + } + if (metaObject.hasGetter("createBy")) { + if (metaObject.getValue("createBy") == null) { + this.setFieldValByName("createBy", SecurityUtils.getUsername(), metaObject); + } + } + } + + @Override + public void updateFill(MetaObject metaObject) { + if (metaObject.hasGetter("updateBy")) { + if (metaObject.getValue("updateBy") == null) { + this.setFieldValByName("updateBy", SecurityUtils.getUsername(), metaObject); + } + } + if (metaObject.hasGetter("updateTime")) { + if (metaObject.getValue("updateTime") == null) { + this.setFieldValByName("updateTime", new Date(), metaObject); + } + } + } + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/security/filter/JwtAuthenticationTokenFilter.java b/bashi-framework/src/main/java/com/bashi/framework/security/filter/JwtAuthenticationTokenFilter.java new file mode 100644 index 0000000..ec050df --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/security/filter/JwtAuthenticationTokenFilter.java @@ -0,0 +1,52 @@ +package com.bashi.framework.security.filter; + +import cn.hutool.core.lang.Validator; +import com.bashi.common.core.domain.model.LoginUser; +import com.bashi.common.utils.SecurityUtils; +import com.bashi.framework.web.service.TokenService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * token过滤器 验证token有效性 + * + * @author duteliang + */ +@Component +public class JwtAuthenticationTokenFilter extends OncePerRequestFilter +{ + @Autowired + private TokenService tokenService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException + { + LoginUser loginUser = tokenService.getLoginUser(request); + if (Validator.isNotNull(loginUser) && Validator.isNull(SecurityUtils.getAuthentication())) { + tokenService.verifyToken(loginUser); + if(loginUser.getUser() != null){ + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); + authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + }else if(loginUser.getCustomer() != null){ + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); + authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + } + + + } + chain.doFilter(request, response); + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/security/handle/AuthenticationEntryPointImpl.java b/bashi-framework/src/main/java/com/bashi/framework/security/handle/AuthenticationEntryPointImpl.java new file mode 100644 index 0000000..6b42c2c --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/security/handle/AuthenticationEntryPointImpl.java @@ -0,0 +1,35 @@ +package com.bashi.framework.security.handle; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpStatus; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.utils.JsonUtils; +import com.bashi.common.utils.ServletUtils; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.Serializable; + +/** + * 认证失败处理类 返回未授权 + * + * @author duteliang + */ +@Component +public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable +{ + private static final long serialVersionUID = -8970718410437077606L; + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) + throws IOException + { + int code = HttpStatus.HTTP_UNAUTHORIZED; + String msg = StrUtil.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI()); + ServletUtils.renderString(response, JsonUtils.toJsonString(AjaxResult.error(code, msg))); + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/security/handle/LogoutSuccessHandlerImpl.java b/bashi-framework/src/main/java/com/bashi/framework/security/handle/LogoutSuccessHandlerImpl.java new file mode 100644 index 0000000..3b314fb --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/security/handle/LogoutSuccessHandlerImpl.java @@ -0,0 +1,53 @@ +package com.bashi.framework.security.handle; + +import cn.hutool.core.lang.Validator; +import cn.hutool.http.HttpStatus; +import com.bashi.common.constant.Constants; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.domain.model.LoginUser; +import com.bashi.common.utils.JsonUtils; +import com.bashi.common.utils.ServletUtils; +import com.bashi.framework.web.service.AsyncService; +import com.bashi.framework.web.service.TokenService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * 自定义退出处理类 返回成功 + * + * @author duteliang + */ +@Configuration +public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { + + @Autowired + private TokenService tokenService; + + @Autowired + private AsyncService asyncService; + + /** + * 退出处理 + */ + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException { + LoginUser loginUser = tokenService.getLoginUser(request); + if (Validator.isNotNull(loginUser)) { + String userName = loginUser.getUsername(); + // 删除用户缓存记录 + tokenService.delLoginUser(loginUser.getToken()); + // 记录用户退出日志 + asyncService.recordLogininfor(userName, Constants.LOGOUT, "退出成功", request); + } + ServletUtils.renderString(response, JsonUtils.toJsonString(AjaxResult.error(HttpStatus.HTTP_OK, "退出成功"))); + } + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/security/sms/IDuteUserDetailsService.java b/bashi-framework/src/main/java/com/bashi/framework/security/sms/IDuteUserDetailsService.java new file mode 100644 index 0000000..369a8c2 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/security/sms/IDuteUserDetailsService.java @@ -0,0 +1,17 @@ +package com.bashi.framework.security.sms; + +import com.bashi.common.core.domain.model.LoginPhoneBody; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +/** + *

created on 2021/7/15

+ * + * @author zhangliang + */ +public interface IDuteUserDetailsService extends UserDetailsService { + + UserDetails loadUserByMobile(LoginPhoneBody username) throws UsernameNotFoundException; + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/security/sms/LoginTypeEnums.java b/bashi-framework/src/main/java/com/bashi/framework/security/sms/LoginTypeEnums.java new file mode 100644 index 0000000..4a5d05c --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/security/sms/LoginTypeEnums.java @@ -0,0 +1,37 @@ +package com.bashi.framework.security.sms; + +public enum LoginTypeEnums { + USER(1,"用户"), + MECHANIC(2,"服务人员"), + SHOP(3,"商家"), + MP_OPEN_ID(4,"公众号openId登录"), + MP_PHONE(5,"公众号手机登录"), + CUSTOMER_PASSWORD(6,"客户密码登陆"), + ; + + private final Integer code; + private final String msg; + + LoginTypeEnums(Integer code, String msg) { + this.code = code; + this.msg = msg; + } + + public Integer getCode() { + return code; + } + + public String getMsg() { + return msg; + } + + public static String getMsgByCode(Integer code){ + LoginTypeEnums[] values = LoginTypeEnums.values(); + for (LoginTypeEnums value : values) { + if(value.getCode().equals(code)){ + return value.getMsg(); + } + } + return "用户"; + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/security/sms/SmsAuthenticationFailureHandler.java b/bashi-framework/src/main/java/com/bashi/framework/security/sms/SmsAuthenticationFailureHandler.java new file mode 100644 index 0000000..a31fbd9 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/security/sms/SmsAuthenticationFailureHandler.java @@ -0,0 +1,41 @@ +package com.bashi.framework.security.sms; + +import com.bashi.common.constant.Constants; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.utils.JsonUtils; +import com.bashi.common.utils.MessageUtils; +import com.bashi.framework.web.service.AsyncService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + *

created on 2021/7/14

+ * + * @author zhangliang + */ +@Component +@Slf4j +public class SmsAuthenticationFailureHandler implements AuthenticationFailureHandler { + @Autowired + private AsyncService asyncService; + public static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile"; + + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { + AjaxResult error = AjaxResult.error(exception.getMessage(), null); + String mobile = request.getParameter(SPRING_SECURITY_FORM_MOBILE_KEY); + asyncService.recordLogininfor(mobile, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"), request); + response.setStatus(HttpStatus.OK.value()); + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write(JsonUtils.toJsonString(error)); + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/security/sms/SmsAuthenticationFilter.java b/bashi-framework/src/main/java/com/bashi/framework/security/sms/SmsAuthenticationFilter.java new file mode 100644 index 0000000..2c5a7e1 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/security/sms/SmsAuthenticationFilter.java @@ -0,0 +1,94 @@ +package com.bashi.framework.security.sms; + +import com.bashi.framework.web.service.CodeService; +import org.springframework.lang.Nullable; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.util.Assert; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + *

created on 2021/7/13

+ * + * @author zhangliang + */ +public class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFilter { + + public static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile"; + private String mobileParameter = SPRING_SECURITY_FORM_MOBILE_KEY; + private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/sms/login", "POST"); + private boolean postOnly = true; + + private CodeService codeService; + + public CodeService getCodeService() { + return codeService; + } + + public void setCodeService(CodeService codeService) { + this.codeService = codeService; + } + + public SmsAuthenticationFilter() { + super(DEFAULT_ANT_PATH_REQUEST_MATCHER); + } + + public SmsAuthenticationFilter(AuthenticationManager authenticationManager) { + super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager); + } + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { + if (this.postOnly && !"POST".equals(request.getMethod())) { + throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); + } else { + String mobile = this.obtainMobile(request); + mobile = mobile != null ? mobile : ""; + mobile = mobile.trim(); + /*String mobileCode = this.obtainMobileCode(request); + mobileCode = mobileCode != null ? mobileCode : ""; + mobileCode = mobileCode.trim(); + if(!codeService.check(mobile, CodeType.LOGIN,mobileCode)){ + throw new AuthenticationServiceException("验证码错误"); + }*/ + SmsAuthenticationToken authRequest = new SmsAuthenticationToken(mobile); + this.setDetails(request, authRequest); + return this.getAuthenticationManager().authenticate(authRequest); + } + } + + @Nullable + protected String obtainMobile(HttpServletRequest request) { + return request.getParameter(this.mobileParameter); + } + + @Nullable + protected String obtainMobileCode(HttpServletRequest request) { + return request.getParameter("mobileCode"); + } + + protected void setDetails(HttpServletRequest request, SmsAuthenticationToken authRequest) { + authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request)); + } + + public void setMobileParameter(String mobileParameter) { + Assert.hasText(mobileParameter, "Mobile parameter must not be empty or null"); + this.mobileParameter = mobileParameter; + } + + public String getMobileParameter() { + return mobileParameter; + } + + + public void setPostOnly(boolean postOnly) { + this.postOnly = postOnly; + } + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/security/sms/SmsAuthenticationProvider.java b/bashi-framework/src/main/java/com/bashi/framework/security/sms/SmsAuthenticationProvider.java new file mode 100644 index 0000000..16f2662 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/security/sms/SmsAuthenticationProvider.java @@ -0,0 +1,60 @@ +package com.bashi.framework.security.sms; + +import com.bashi.common.core.domain.model.LoginPhoneBody; +import com.bashi.common.utils.JsonUtils; +import com.bashi.framework.web.service.CodeService; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; + +/** + *

created on 2021/7/13

+ * + * @author zhangliang + */ +public class SmsAuthenticationProvider implements AuthenticationProvider { + + private IDuteUserDetailsService userDetailsService; + + private CodeService codeService; + + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + SmsAuthenticationToken authenticationToken = (SmsAuthenticationToken) authentication; + + LoginPhoneBody mobile = (LoginPhoneBody) authenticationToken.getPrincipal(); + + UserDetails userDetails = userDetailsService.loadUserByMobile(mobile); + + // 此时鉴权成功后,应当重新 new 一个拥有鉴权的 authenticationResult 返回 + SmsAuthenticationToken authenticationResult = new SmsAuthenticationToken(userDetails, userDetails.getAuthorities()); + + authenticationResult.setDetails(authenticationToken.getDetails()); + + return authenticationResult; + } + + @Override + public boolean supports(Class authentication) { + // 判断 authentication 是不是 SmsCodeAuthenticationToken 的子类或子接口 + return SmsAuthenticationToken.class.isAssignableFrom(authentication); + } + + public IDuteUserDetailsService getUserDetailsService() { + return userDetailsService; + } + + public void setUserDetailsService(IDuteUserDetailsService userDetailsService) { + this.userDetailsService = userDetailsService; + } + + public CodeService getCodeService() { + return codeService; + } + + public void setCodeService(CodeService codeService) { + this.codeService = codeService; + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/security/sms/SmsAuthenticationSuccessHandler.java b/bashi-framework/src/main/java/com/bashi/framework/security/sms/SmsAuthenticationSuccessHandler.java new file mode 100644 index 0000000..aa63610 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/security/sms/SmsAuthenticationSuccessHandler.java @@ -0,0 +1,69 @@ +package com.bashi.framework.security.sms; + +import com.bashi.common.constant.Constants; +import com.bashi.common.core.domain.entity.SysUser; +import com.bashi.common.core.domain.model.LoginUser; +import com.bashi.common.utils.DateUtils; +import com.bashi.common.utils.MessageUtils; +import com.bashi.common.utils.ServletUtils; +import com.bashi.framework.web.service.AsyncService; +import com.bashi.framework.web.service.TokenService; +import com.bashi.system.service.ISysUserService; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + *

created on 2021/7/14

+ * + * @author zhangliang + */ +@Component +@Slf4j +public class SmsAuthenticationSuccessHandler implements AuthenticationSuccessHandler { + + @Autowired + private ObjectMapper objectMapper; + @Autowired + private AsyncService asyncService; + @Autowired + private ISysUserService userService; + @Autowired + private TokenService tokenService; + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException { + LoginUser loginUser = (LoginUser) authentication.getPrincipal(); + asyncService.recordLogininfor(loginUser.getUsername(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request); + recordLoginInfo(loginUser.getUser()); + // 生成token + String token = tokenService.createToken(loginUser); + Map ajax = new HashMap<>(); + ajax.put("token",token); + ajax.put("code",200); +// ajax.put("data",loginUser); + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write(objectMapper.writeValueAsString(ajax)); + } + + /** + * 记录登录信息 + */ + public void recordLoginInfo(SysUser user) { + user.setLoginIp(ServletUtils.getClientIP()); + user.setLoginDate(DateUtils.getNowDate()); + user.setUpdateBy(user.getUserName()); + userService.updateUserProfile(user); + } + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/security/sms/SmsAuthenticationToken.java b/bashi-framework/src/main/java/com/bashi/framework/security/sms/SmsAuthenticationToken.java new file mode 100644 index 0000000..58f90b7 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/security/sms/SmsAuthenticationToken.java @@ -0,0 +1,74 @@ +package com.bashi.framework.security.sms; + +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.SpringSecurityCoreVersion; +import org.springframework.util.Assert; + +import java.util.Collection; + +/** + *

created on 2021/7/13

+ * + * @author zhangliang + */ +public class SmsAuthenticationToken extends AbstractAuthenticationToken { + + private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; + + /** + * 手机号 + */ + private final Object principal; + + /** + * This constructor can be safely used by any code that wishes to create a + * UsernamePasswordAuthenticationToken, as the {@link #isAuthenticated()} + * will return false. + * + */ + public SmsAuthenticationToken(Object principal) { + super(null); + this.principal = principal; + setAuthenticated(false); + } + + /** + * This constructor should only be used by AuthenticationManager or + * AuthenticationProvider implementations that are satisfied with + * producing a trusted (i.e. {@link #isAuthenticated()} = true) + * authentication token. + * @param principal + * @param authorities + */ + public SmsAuthenticationToken(Object principal, + Collection authorities) { + super(authorities); + this.principal = principal; + super.setAuthenticated(true); // must use super, as we override + } + + + @Override + public Object getCredentials() { + return null; + } + + @Override + public Object getPrincipal() { + return this.principal; + } + + @Override + public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { + Assert.isTrue(!isAuthenticated, + "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead"); + super.setAuthenticated(false); + } + + @Override + public void eraseCredentials() { + super.eraseCredentials(); + } + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/util/AgentUtils.java b/bashi-framework/src/main/java/com/bashi/framework/util/AgentUtils.java new file mode 100644 index 0000000..5cf74c9 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/util/AgentUtils.java @@ -0,0 +1,83 @@ +package com.bashi.framework.util; + +import cn.hutool.extra.servlet.ServletUtil; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import org.apache.http.HttpHeaders; + +import javax.servlet.http.HttpServletRequest; + +public class AgentUtils { + + public enum MarketEnum { + none("none","未知"), + iPhone("iPhone","苹果"), + MP("MP","公众号"), + huawei("huawei","华为"), + xiaomi("xiaomi","小米"), + a360("a360","360"), + vivo("vivo","vivo"), + baidu("baidu591","百度"), + share("share","分享"), + ; + + private String code; + private String name; + + MarketEnum(String code, String name) { + this.code = code; + this.name = name; + } + + public String getCode() { + return code; + } + + public String getName() { + return name; + } + + public static String getMarketValue(String key){ + MarketEnum[] values = MarketEnum.values(); + for (MarketEnum value : values) { + if(value.getCode().equals(key)){ + return value.getName(); + } + } + return key; + } + } + + + private final static String APP_MARKET = "channelId"; + + private final static String VERSION_NAME = "versionName"; + + public static String getVersion(HttpServletRequest request){ + return request.getHeader(VERSION_NAME); + } + + public static String getAgentCode(HttpServletRequest request){ + return request.getHeader(APP_MARKET); + } + + public static boolean checkIos(HttpServletRequest request){ + String agent = request.getHeader(HttpHeaders.USER_AGENT); + if(agent.contains("iPhone")|| agent.contains("iPod") || agent.contains("iPad")){ + return true; + } + return false; + } + + public static String getAgent(HttpServletRequest request){ + String market = request.getHeader(APP_MARKET); + if(StringUtils.isBlank(market)){ // 可能是IOS,也可能谁都不是 + String agent = request.getHeader(HttpHeaders.USER_AGENT); + if(agent.contains("iPhone")|| agent.contains("iPod") || agent.contains("iPad")){ + return MarketEnum.iPhone.getName(); + }else{ + return MarketEnum.none.getName(); + } + } + return MarketEnum.getMarketValue(market); + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/web/exception/GlobalExceptionHandler.java b/bashi-framework/src/main/java/com/bashi/framework/web/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..caf8537 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/web/exception/GlobalExceptionHandler.java @@ -0,0 +1,117 @@ +package com.bashi.framework.web.exception; + +import cn.hutool.core.lang.Validator; +import cn.hutool.http.HttpStatus; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.exception.BaseException; +import com.bashi.common.exception.CustomException; +import com.bashi.common.exception.DemoModeException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authentication.AccountExpiredException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.validation.BindException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.NoHandlerFoundException; + +import javax.validation.ConstraintViolationException; + +/** + * 全局异常处理器 + * + * @author duteliang + */ +@RestControllerAdvice +public class GlobalExceptionHandler { + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + /** + * 基础异常 + */ + @ExceptionHandler(BaseException.class) + public AjaxResult baseException(BaseException e) { + return AjaxResult.error(e.getMessage()); + } + + /** + * 业务异常 + */ + @ExceptionHandler(CustomException.class) + public AjaxResult businessException(CustomException e) { + if (Validator.isNull(e.getCode())) { + return AjaxResult.error(e.getMessage()); + } + return AjaxResult.error(e.getCode(), e.getMessage()); + } + + @ExceptionHandler(NoHandlerFoundException.class) + public AjaxResult handlerNoFoundException(Exception e) { + log.error(e.getMessage(), e); + return AjaxResult.error(HttpStatus.HTTP_NOT_FOUND, "路径不存在,请检查路径是否正确"); + } + + @ExceptionHandler(AccessDeniedException.class) + public AjaxResult handleAuthorizationException(AccessDeniedException e) { + log.error(e.getMessage()); + return AjaxResult.error(HttpStatus.HTTP_FORBIDDEN, "没有权限,请联系管理员授权"); + } + + @ExceptionHandler(AccountExpiredException.class) + public AjaxResult handleAccountExpiredException(AccountExpiredException e) { + log.error(e.getMessage(), e); + return AjaxResult.error(e.getMessage()); + } + + @ExceptionHandler(UsernameNotFoundException.class) + public AjaxResult handleUsernameNotFoundException(UsernameNotFoundException e) { + log.error(e.getMessage(), e); + return AjaxResult.error(e.getMessage()); + } + + @ExceptionHandler(Exception.class) + public AjaxResult handleException(Exception e) { + log.error(e.getMessage(), e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(BindException.class) + public AjaxResult validatedBindException(BindException e) { + log.error(e.getMessage(), e); + String message = e.getAllErrors().get(0).getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(ConstraintViolationException.class) + public AjaxResult constraintViolationException(ConstraintViolationException e) { + log.error(e.getMessage(), e); + String message = e.getConstraintViolations().iterator().next().getMessage(); + return AjaxResult.error(message); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Object validExceptionHandler(MethodArgumentNotValidException e) { + log.error(e.getMessage(), e); + String message = e.getBindingResult().getFieldError().getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 演示模式异常 + */ + @ExceptionHandler(DemoModeException.class) + public AjaxResult demoModeException(DemoModeException e) { + return AjaxResult.error("演示模式,不允许操作"); + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/web/service/AsyncService.java b/bashi-framework/src/main/java/com/bashi/framework/web/service/AsyncService.java new file mode 100644 index 0000000..7b8c8d1 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/web/service/AsyncService.java @@ -0,0 +1,100 @@ +package com.bashi.framework.web.service; + +import cn.hutool.http.useragent.UserAgent; +import cn.hutool.http.useragent.UserAgentUtil; +import com.bashi.common.constant.Constants; +import com.bashi.common.utils.ServletUtils; +import com.bashi.common.utils.ip.AddressUtils; +import com.bashi.system.domain.SysLogininfor; +import com.bashi.system.domain.SysOperLog; +import com.bashi.system.service.ISysLogininforService; +import com.bashi.system.service.ISysOperLogService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; + +/** + * 异步工厂(产生任务用) + * + * @author Lion Li + */ +@Slf4j(topic = "sys-user") +@Async +@Component +public class AsyncService { + + @Autowired + private ISysLogininforService iSysLogininforService; + + @Autowired + private ISysOperLogService iSysOperLogService; + + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息 + * @param args 列表 + */ + public void recordLogininfor(final String username, final String status, final String message, + HttpServletRequest request, final Object... args) { + final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent")); + final String ip = ServletUtils.getClientIP(request); + + String address = AddressUtils.getRealAddressByIP(ip); + StringBuilder s = new StringBuilder(); + s.append(getBlock(ip)); + s.append(address); + s.append(getBlock(username)); + s.append(getBlock(status)); + s.append(getBlock(message)); + // 打印信息到日志 + log.info(s.toString(), args); + // 获取客户端操作系统 + String os = userAgent.getOs().getName(); + // 获取客户端浏览器 + String browser = userAgent.getBrowser().getName(); + // 封装对象 + SysLogininfor logininfor = new SysLogininfor(); + logininfor.setUserName(username); + logininfor.setIpaddr(ip); + logininfor.setLoginLocation(address); + logininfor.setBrowser(browser); + logininfor.setOs(os); + logininfor.setMsg(message); + // 日志状态 + if (Constants.LOGIN_SUCCESS.equals(status) || Constants.LOGOUT.equals(status)) { + logininfor.setStatus(Constants.SUCCESS); + } else if (Constants.LOGIN_FAIL.equals(status)) { + logininfor.setStatus(Constants.FAIL); + } + // 插入数据 + iSysLogininforService.insertLogininfor(logininfor); + } + + /** + * 操作日志记录 + * + * @param operLog 操作日志信息 + */ + public void recordOper(final SysOperLog operLog) { + // 远程查询操作地点 + operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); + iSysOperLogService.insertOperlog(operLog); + } + + private String getBlock(Object msg) { + if (msg == null) { + msg = ""; + } + return "[" + msg.toString() + "]"; + } + + public void recordLog(final StringBuilder logBuild) { + log.info(logBuild.toString()); + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/web/service/CodeService.java b/bashi-framework/src/main/java/com/bashi/framework/web/service/CodeService.java new file mode 100644 index 0000000..965ce72 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/web/service/CodeService.java @@ -0,0 +1,56 @@ +package com.bashi.framework.web.service; + +import com.bashi.framework.constant.CodeType; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + +import java.time.Duration; + +/** + *

created on 2021/7/15

+ * + * @author zhangliang + */ +@Component +public class CodeService { + + @Autowired + private StringRedisTemplate redisTemplate; + + private static final String AUTH_CODE = "AUTH:CODE"; + + private static final String CODE = "DUTE:CODE:"; + + private static final String TEST_ACCOUNT = "15312531253"; + + public void put(String mobile,CodeType type,String code){ + redisTemplate.opsForValue().set(CODE + type.name() + ":" + mobile,code, Duration.ofMinutes(11)); + } + + public boolean check(String mobile, CodeType type, String code){ + if(TEST_ACCOUNT.equals(mobile)){ + return true; + } + if(StringUtils.isBlank(code)){ + return false; + } + String authCode = redisTemplate.opsForValue().get(AUTH_CODE); + if(StringUtils.isNotEmpty(authCode)){ + String[] allCodeArray = authCode.split(","); + for (String allCode : allCodeArray) { + if(allCode.equals(code)){ + return true; + } + } + } + String s = redisTemplate.opsForValue().get(CODE + type.name() + ":" + mobile); + return code.equals(s); + } + + public void putAuthCode(String code){ + redisTemplate.opsForValue().set(AUTH_CODE,code); + } + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/web/service/CustomerService.java b/bashi-framework/src/main/java/com/bashi/framework/web/service/CustomerService.java new file mode 100644 index 0000000..9924c7c --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/web/service/CustomerService.java @@ -0,0 +1,10 @@ +package com.bashi.framework.web.service; + +/** + *

created on 2021/7/30

+ * + * @author dute7liang + */ +public interface CustomerService { + +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/web/service/PermissionService.java b/bashi-framework/src/main/java/com/bashi/framework/web/service/PermissionService.java new file mode 100644 index 0000000..369496c --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/web/service/PermissionService.java @@ -0,0 +1,170 @@ +package com.bashi.framework.web.service; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import com.bashi.common.core.domain.entity.SysRole; +import com.bashi.common.core.domain.model.LoginUser; +import com.bashi.common.utils.ServletUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Set; + +/** + * 自定义权限实现,ss取自SpringSecurity首字母 + * + * @author duteliang + */ +@Service("ss") +public class PermissionService +{ + /** 所有权限标识 */ + private static final String ALL_PERMISSION = "*:*:*"; + + /** 管理员角色权限标识 */ + private static final String SUPER_ADMIN = "admin"; + + private static final String ROLE_DELIMETER = ","; + + private static final String PERMISSION_DELIMETER = ","; + + @Autowired + private TokenService tokenService; + + /** + * 验证用户是否具备某权限 + * + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public boolean hasPermi(String permission) + { + if (Validator.isEmpty(permission)) + { + return false; + } + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + if (Validator.isNull(loginUser) || Validator.isEmpty(loginUser.getPermissions())) + { + return false; + } + return hasPermissions(loginUser.getPermissions(), permission); + } + + /** + * 验证用户是否不具备某权限,与 hasPermi逻辑相反 + * + * @param permission 权限字符串 + * @return 用户是否不具备某权限 + */ + public boolean lacksPermi(String permission) + { + return hasPermi(permission) != true; + } + + /** + * 验证用户是否具有以下任意一个权限 + * + * @param permissions 以 PERMISSION_NAMES_DELIMETER 为分隔符的权限列表 + * @return 用户是否具有以下任意一个权限 + */ + public boolean hasAnyPermi(String permissions) + { + if (Validator.isEmpty(permissions)) + { + return false; + } + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + if (Validator.isNull(loginUser) || Validator.isEmpty(loginUser.getPermissions())) + { + return false; + } + Set authorities = loginUser.getPermissions(); + for (String permission : permissions.split(PERMISSION_DELIMETER)) + { + if (permission != null && hasPermissions(authorities, permission)) + { + return true; + } + } + return false; + } + + /** + * 判断用户是否拥有某个角色 + * + * @param role 角色字符串 + * @return 用户是否具备某角色 + */ + public boolean hasRole(String role) + { + if (Validator.isEmpty(role)) + { + return false; + } + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + if (Validator.isNull(loginUser) || Validator.isEmpty(loginUser.getUser().getRoles())) + { + return false; + } + for (SysRole sysRole : loginUser.getUser().getRoles()) + { + String roleKey = sysRole.getRoleKey(); + if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StrUtil.trim(role))) + { + return true; + } + } + return false; + } + + /** + * 验证用户是否不具备某角色,与 isRole逻辑相反。 + * + * @param role 角色名称 + * @return 用户是否不具备某角色 + */ + public boolean lacksRole(String role) + { + return hasRole(role) != true; + } + + /** + * 验证用户是否具有以下任意一个角色 + * + * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表 + * @return 用户是否具有以下任意一个角色 + */ + public boolean hasAnyRoles(String roles) + { + if (Validator.isEmpty(roles)) + { + return false; + } + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + if (Validator.isNull(loginUser) || Validator.isEmpty(loginUser.getUser().getRoles())) + { + return false; + } + for (String role : roles.split(ROLE_DELIMETER)) + { + if (hasRole(role)) + { + return true; + } + } + return false; + } + + /** + * 判断是否包含权限 + * + * @param permissions 权限列表 + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + private boolean hasPermissions(Set permissions, String permission) + { + return permissions.contains(ALL_PERMISSION) || permissions.contains(StrUtil.trim(permission)); + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/web/service/RegisterEventService.java b/bashi-framework/src/main/java/com/bashi/framework/web/service/RegisterEventService.java new file mode 100644 index 0000000..9cc59aa --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/web/service/RegisterEventService.java @@ -0,0 +1,13 @@ +package com.bashi.framework.web.service; + +import com.bashi.common.core.domain.entity.SysUser; + +/** + *

created on 2021/7/15

+ * + * @author zhangliang + */ +public interface RegisterEventService { + + void event(SysUser user); +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/web/service/SysLoginService.java b/bashi-framework/src/main/java/com/bashi/framework/web/service/SysLoginService.java new file mode 100644 index 0000000..a5c6242 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/web/service/SysLoginService.java @@ -0,0 +1,106 @@ +package com.bashi.framework.web.service; + +import com.bashi.common.constant.Constants; +import com.bashi.common.core.domain.entity.SysUser; +import com.bashi.common.core.domain.model.LoginUser; +import com.bashi.common.core.redis.RedisCache; +import com.bashi.common.exception.CustomException; +import com.bashi.common.exception.user.CaptchaException; +import com.bashi.common.exception.user.CaptchaExpireException; +import com.bashi.common.exception.user.UserPasswordNotMatchException; +import com.bashi.common.utils.DateUtils; +import com.bashi.common.utils.MessageUtils; +import com.bashi.common.utils.ServletUtils; +import com.bashi.framework.config.properties.CaptchaProperties; +import com.bashi.system.service.ISysUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; + +/** + * 登录校验方法 + * + * @author duteliang + */ +@Component +public class SysLoginService { + @Autowired + private TokenService tokenService; + + @Resource + private AuthenticationManager authenticationManager; + + @Autowired + private RedisCache redisCache; + + @Autowired + private CaptchaProperties captchaProperties; + + @Autowired + private ISysUserService userService; + + @Autowired + private AsyncService asyncService; + + /** + * 登录验证 + * + * @param username 用户名 + * @param password 密码 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public String login(String username, String password, String code, String uuid) { + HttpServletRequest request = ServletUtils.getRequest(); + if (captchaProperties.getEnabled()) { + String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; + String captcha = redisCache.getCacheObject(verifyKey); + redisCache.deleteObject(verifyKey); + if (captcha == null) { + asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"), request); + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) { + asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"), request); + throw new CaptchaException(); + } + } + // 用户验证 + Authentication authentication = null; + try { + // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername + authentication = authenticationManager + .authenticate(new UsernamePasswordAuthenticationToken(username, password)); + } catch (Exception e) { + if (e instanceof BadCredentialsException) { + asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"), request); + throw new UserPasswordNotMatchException(); + } else { + asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage(), request); + throw new CustomException(e.getMessage()); + } + } + asyncService.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request); + LoginUser loginUser = (LoginUser) authentication.getPrincipal(); + recordLoginInfo(loginUser.getUser()); + // 生成token + return tokenService.createToken(loginUser); + } + + /** + * 记录登录信息 + */ + public void recordLoginInfo(SysUser user) { + user.setLoginIp(ServletUtils.getClientIP()); + user.setLoginDate(DateUtils.getNowDate()); + user.setUpdateBy(user.getUserName()); + userService.updateUserProfile(user); + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/web/service/SysPermissionService.java b/bashi-framework/src/main/java/com/bashi/framework/web/service/SysPermissionService.java new file mode 100644 index 0000000..1912f44 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/web/service/SysPermissionService.java @@ -0,0 +1,66 @@ +package com.bashi.framework.web.service; + +import java.util.HashSet; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import com.bashi.common.core.domain.entity.SysUser; +import com.bashi.system.service.ISysMenuService; +import com.bashi.system.service.ISysRoleService; + +/** + * 用户权限处理 + * + * @author duteliang + */ +@Component +public class SysPermissionService +{ + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysMenuService menuService; + + /** + * 获取角色数据权限 + * + * @param user 用户信息 + * @return 角色权限信息 + */ + public Set getRolePermission(SysUser user) + { + Set roles = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + roles.add("admin"); + } + else + { + roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); + } + return roles; + } + + /** + * 获取菜单数据权限 + * + * @param user 用户信息 + * @return 菜单权限信息 + */ + public Set getMenuPermission(SysUser user) + { + Set perms = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + perms.add("*:*:*"); + } + else + { + perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); + } + return perms; + } +} diff --git a/bashi-framework/src/main/java/com/bashi/framework/web/service/TokenService.java b/bashi-framework/src/main/java/com/bashi/framework/web/service/TokenService.java new file mode 100644 index 0000000..a786d68 --- /dev/null +++ b/bashi-framework/src/main/java/com/bashi/framework/web/service/TokenService.java @@ -0,0 +1,194 @@ +package com.bashi.framework.web.service; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.IdUtil; +import cn.hutool.http.useragent.UserAgent; +import cn.hutool.http.useragent.UserAgentUtil; +import com.bashi.common.constant.Constants; +import com.bashi.common.core.domain.model.LoginUser; +import com.bashi.common.core.redis.RedisCache; +import com.bashi.common.utils.ServletUtils; +import com.bashi.common.utils.ip.AddressUtils; +import com.bashi.framework.config.properties.TokenProperties; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * token验证处理 + * + * @author Lion Li + */ +@Component +public class TokenService { + + protected static final long MILLIS_SECOND = 1000; + + protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; + + private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L; + + @Autowired + private RedisCache redisCache; + + @Autowired + private TokenProperties tokenProperties; + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser(HttpServletRequest request) { + // 获取请求携带的令牌 + String token = getToken(request); + if (Validator.isNotEmpty(token)) { + Claims claims = parseToken(token); + // 解析对应的权限以及用户信息 + String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); + String userKey = getTokenKey(uuid); + LoginUser user = redisCache.getCacheObject(userKey); + return user; + } + return null; + } + + /** + * 设置用户身份信息 + */ + public void setLoginUser(LoginUser loginUser) { + if (Validator.isNotNull(loginUser) && Validator.isNotEmpty(loginUser.getToken())) { + refreshToken(loginUser); + } + } + + /** + * 删除用户身份信息 + */ + public void delLoginUser(String token) { + if (Validator.isNotEmpty(token)) { + String userKey = getTokenKey(token); + redisCache.deleteObject(userKey); + } + } + + /** + * 创建令牌 + * + * @param loginUser 用户信息 + * @return 令牌 + */ + public String createToken(LoginUser loginUser) { + String token = IdUtil.fastUUID(); + loginUser.setToken(token); + setUserAgent(loginUser); + refreshToken(loginUser); + + Map claims = new HashMap<>(); + claims.put(Constants.LOGIN_USER_KEY, token); + return createToken(claims); + } + + /** + * 验证令牌有效期,相差不足20分钟,自动刷新缓存 + * + * @param loginUser + * @return 令牌 + */ + public void verifyToken(LoginUser loginUser) { + long expireTime = loginUser.getExpireTime(); + long currentTime = System.currentTimeMillis(); + if (expireTime - currentTime <= MILLIS_MINUTE_TEN) { + refreshToken(loginUser); + } + } + + /** + * 刷新令牌有效期 + * + * @param loginUser 登录信息 + */ + public void refreshToken(LoginUser loginUser) { + loginUser.setLoginTime(System.currentTimeMillis()); + loginUser.setExpireTime(loginUser.getLoginTime() + tokenProperties.getExpireTime() * MILLIS_MINUTE); + // 根据uuid将loginUser缓存 + String userKey = getTokenKey(loginUser.getToken()); + redisCache.setCacheObject(userKey, loginUser, tokenProperties.getExpireTime(), TimeUnit.MINUTES); + } + + /** + * 设置用户代理信息 + * + * @param loginUser 登录信息 + */ + public void setUserAgent(LoginUser loginUser) { + UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); + String ip = ServletUtils.getClientIP(); + loginUser.setIpaddr(ip); + loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); + loginUser.setBrowser(userAgent.getBrowser().getName()); + loginUser.setOs(userAgent.getOs().getName()); + } + + /** + * 从数据声明生成令牌 + * + * @param claims 数据声明 + * @return 令牌 + */ + private String createToken(Map claims) { + String token = Jwts.builder() + .setClaims(claims) + .signWith(SignatureAlgorithm.HS512, tokenProperties.getSecret()).compact(); + return token; + } + + /** + * 从令牌中获取数据声明 + * + * @param token 令牌 + * @return 数据声明 + */ + private Claims parseToken(String token) { + return Jwts.parser() + .setSigningKey(tokenProperties.getSecret()) + .parseClaimsJws(token) + .getBody(); + } + + /** + * 从令牌中获取用户名 + * + * @param token 令牌 + * @return 用户名 + */ + public String getUsernameFromToken(String token) { + Claims claims = parseToken(token); + return claims.getSubject(); + } + + /** + * 获取请求token + * + * @param request + * @return token + */ + private String getToken(HttpServletRequest request) { + String token = request.getHeader(tokenProperties.getHeader()); + if (Validator.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) { + token = token.replace(Constants.TOKEN_PREFIX, ""); + } + return token; + } + + private String getTokenKey(String uuid) { + return Constants.LOGIN_TOKEN_KEY + uuid; + } +} diff --git a/bashi-generator/pom.xml b/bashi-generator/pom.xml new file mode 100644 index 0000000..b7a2f4a --- /dev/null +++ b/bashi-generator/pom.xml @@ -0,0 +1,34 @@ + + + + bashi + com.bashi + 2.4.0 + + 4.0.0 + + bashi-generator + + + generator代码生成 + + + + + + + org.apache.velocity + velocity + + + + + com.bashi + bashi-common + + + + + diff --git a/bashi-generator/src/main/java/com/bashi/generator/config/GenConfig.java b/bashi-generator/src/main/java/com/bashi/generator/config/GenConfig.java new file mode 100644 index 0000000..89fb057 --- /dev/null +++ b/bashi-generator/src/main/java/com/bashi/generator/config/GenConfig.java @@ -0,0 +1,73 @@ +package com.bashi.generator.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; + +/** + * 读取代码生成相关配置 + * + * @author duteliang + */ +@Component +@ConfigurationProperties(prefix = "gen") +@PropertySource(value = { "classpath:generator.yml" }) +public class GenConfig +{ + /** 作者 */ + public static String author; + + /** 生成包路径 */ + public static String packageName; + + /** 自动去除表前缀,默认是false */ + public static boolean autoRemovePre; + + /** 表前缀(类名不会包含表前缀) */ + public static String tablePrefix; + + public static String getAuthor() + { + return author; + } + + @Value("${author}") + public void setAuthor(String author) + { + GenConfig.author = author; + } + + public static String getPackageName() + { + return packageName; + } + + @Value("${packageName}") + public void setPackageName(String packageName) + { + GenConfig.packageName = packageName; + } + + public static boolean getAutoRemovePre() + { + return autoRemovePre; + } + + @Value("${autoRemovePre}") + public void setAutoRemovePre(boolean autoRemovePre) + { + GenConfig.autoRemovePre = autoRemovePre; + } + + public static String getTablePrefix() + { + return tablePrefix; + } + + @Value("${tablePrefix}") + public void setTablePrefix(String tablePrefix) + { + GenConfig.tablePrefix = tablePrefix; + } +} diff --git a/bashi-generator/src/main/java/com/bashi/generator/controller/GenController.java b/bashi-generator/src/main/java/com/bashi/generator/controller/GenController.java new file mode 100644 index 0000000..d0a10fc --- /dev/null +++ b/bashi-generator/src/main/java/com/bashi/generator/controller/GenController.java @@ -0,0 +1,204 @@ +package com.bashi.generator.controller; + +import cn.hutool.core.convert.Convert; +import com.bashi.common.annotation.Log; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.enums.BusinessType; +import com.bashi.generator.domain.GenTable; +import com.bashi.generator.domain.GenTableColumn; +import com.bashi.generator.service.IGenTableColumnService; +import com.bashi.generator.service.IGenTableService; +import org.apache.commons.io.IOUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 代码生成 操作处理 + * + * @author duteliang + */ +@RestController +@RequestMapping("/tool/gen") +public class GenController extends BaseController +{ + @Autowired + private IGenTableService genTableService; + + @Autowired + private IGenTableColumnService genTableColumnService; + + /** + * 查询代码生成列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping("/list") + public TableDataInfo genList(GenTable genTable) + { + return genTableService.selectPageGenTableList(genTable); + } + + /** + * 修改代码生成业务 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:query')") + @GetMapping(value = "/{talbleId}") + public AjaxResult getInfo(@PathVariable Long talbleId) + { + GenTable table = genTableService.selectGenTableById(talbleId); + List tables = genTableService.selectGenTableAll(); + List list = genTableColumnService.selectGenTableColumnListByTableId(talbleId); + Map map = new HashMap(); + map.put("info", table); + map.put("rows", list); + map.put("tables", tables); + return AjaxResult.success(map); + } + + /** + * 查询数据库列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping("/db/list") + public TableDataInfo dataList(GenTable genTable) + { + return genTableService.selectPageDbTableList(genTable); + } + + /** + * 查询数据表字段列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping(value = "/column/{talbleId}") + public TableDataInfo columnList(Long tableId) + { + TableDataInfo dataInfo = new TableDataInfo(); + List list = genTableColumnService.selectGenTableColumnListByTableId(tableId); + dataInfo.setRows(list); + dataInfo.setTotal(list.size()); + return dataInfo; + } + + /** + * 导入表结构(保存) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:import')") + @Log(title = "代码生成", businessType = BusinessType.IMPORT) + @PostMapping("/importTable") + public AjaxResult importTableSave(String tables) + { + String[] tableNames = Convert.toStrArray(tables); + // 查询表信息 + List tableList = genTableService.selectDbTableListByNames(tableNames); + genTableService.importGenTable(tableList); + return AjaxResult.success(); + } + + /** + * 修改保存代码生成业务 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:edit')") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult editSave(@Validated @RequestBody GenTable genTable) + { + genTableService.validateEdit(genTable); + genTableService.updateGenTable(genTable); + return AjaxResult.success(); + } + + /** + * 删除代码生成 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:remove')") + @Log(title = "代码生成", businessType = BusinessType.DELETE) + @DeleteMapping("/{tableIds}") + public AjaxResult remove(@PathVariable Long[] tableIds) + { + genTableService.deleteGenTableByIds(tableIds); + return AjaxResult.success(); + } + + /** + * 预览代码 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:preview')") + @GetMapping("/preview/{tableId}") + public AjaxResult preview(@PathVariable("tableId") Long tableId) throws IOException + { + Map dataMap = genTableService.previewCode(tableId); + return AjaxResult.success(dataMap); + } + + /** + * 生成代码(下载方式) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/download/{tableName}") + public void download(HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException + { + byte[] data = genTableService.downloadCode(tableName); + genCode(response, data); + } + + /** + * 生成代码(自定义路径) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/genCode/{tableName}") + public AjaxResult genCode(@PathVariable("tableName") String tableName) + { + genTableService.generatorCode(tableName); + return AjaxResult.success(); + } + + /** + * 同步数据库 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:edit')") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @GetMapping("/synchDb/{tableName}") + public AjaxResult synchDb(@PathVariable("tableName") String tableName) + { + genTableService.synchDb(tableName); + return AjaxResult.success(); + } + + /** + * 批量生成代码 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/batchGenCode") + public void batchGenCode(HttpServletResponse response, String tables) throws IOException + { + String[] tableNames = Convert.toStrArray(tables); + byte[] data = genTableService.downloadCode(tableNames); + genCode(response, data); + } + + /** + * 生成zip文件 + */ + private void genCode(HttpServletResponse response, byte[] data) throws IOException + { + response.reset(); + response.addHeader("Access-Control-Allow-Origin", "*"); + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition"); + response.setHeader("Content-Disposition", "attachment; filename=\"caocao.zip\""); + response.addHeader("Content-Length", "" + data.length); + response.setContentType("application/octet-stream; charset=UTF-8"); + IOUtils.write(data, response.getOutputStream()); + } +} diff --git a/bashi-generator/src/main/java/com/bashi/generator/domain/GenTable.java b/bashi-generator/src/main/java/com/bashi/generator/domain/GenTable.java new file mode 100644 index 0000000..3a2a7f4 --- /dev/null +++ b/bashi-generator/src/main/java/com/bashi/generator/domain/GenTable.java @@ -0,0 +1,237 @@ +package com.bashi.generator.domain; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.bashi.common.constant.GenConstants; +import lombok.*; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.ArrayUtils; + +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 业务表 gen_table + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("gen_table") +public class GenTable implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 编号 + */ + @TableId(value = "table_id", type = IdType.AUTO) + private Long tableId; + + /** + * 表名称 + */ + @NotBlank(message = "表名称不能为空") + private String tableName; + + /** + * 表描述 + */ + @NotBlank(message = "表描述不能为空") + private String tableComment; + + /** + * 关联父表的表名 + */ + private String subTableName; + + /** + * 本表关联父表的外键名 + */ + private String subTableFkName; + + /** + * 实体类名称(首字母大写) + */ + @NotBlank(message = "实体类名称不能为空") + private String className; + + /** + * 使用的模板(crud单表操作 tree树表操作 sub主子表操作) + */ + private String tplCategory; + + /** + * 生成包路径 + */ + @NotBlank(message = "生成包路径不能为空") + private String packageName; + + /** + * 生成模块名 + */ + @NotBlank(message = "生成模块名不能为空") + private String moduleName; + + /** + * 生成业务名 + */ + @NotBlank(message = "生成业务名不能为空") + private String businessName; + + /** + * 生成功能名 + */ + @NotBlank(message = "生成功能名不能为空") + private String functionName; + + /** + * 生成作者 + */ + @NotBlank(message = "作者不能为空") + private String functionAuthor; + + /** + * 生成代码方式(0zip压缩包 1自定义路径) + */ + private String genType; + + /** + * 生成路径(不填默认项目路径) + */ + private String genPath; + + /** + * 主键信息 + */ + @TableField(exist = false) + private GenTableColumn pkColumn; + + /** + * 子表信息 + */ + @TableField(exist = false) + private GenTable subTable; + + /** + * 表列信息 + */ + @Valid + @TableField(exist = false) + private List columns; + + /** + * 其它生成选项 + */ + private String options; + + /** + * 创建者 + */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** + * 备注 + */ + private String remark; + + /** + * 请求参数 + */ + @TableField(exist = false) + private Map params = new HashMap<>(); + + /** + * 树编码字段 + */ + @TableField(exist = false) + private String treeCode; + + /** + * 树父编码字段 + */ + @TableField(exist = false) + private String treeParentCode; + + /** + * 树名称字段 + */ + @TableField(exist = false) + private String treeName; + + /** + * 上级菜单ID字段 + */ + @TableField(exist = false) + private String parentMenuId; + + /** + * 上级菜单名称字段 + */ + @TableField(exist = false) + private String parentMenuName; + + public boolean isSub() { + return isSub(this.tplCategory); + } + + public static boolean isSub(String tplCategory) { + return tplCategory != null && StrUtil.equals(GenConstants.TPL_SUB, tplCategory); + } + + public boolean isTree() { + return isTree(this.tplCategory); + } + + public static boolean isTree(String tplCategory) { + return tplCategory != null && StrUtil.equals(GenConstants.TPL_TREE, tplCategory); + } + + public boolean isCrud() { + return isCrud(this.tplCategory); + } + + public static boolean isCrud(String tplCategory) { + return tplCategory != null && StrUtil.equals(GenConstants.TPL_CRUD, tplCategory); + } + + public boolean isSuperColumn(String javaField) { + return isSuperColumn(this.tplCategory, javaField); + } + + public static boolean isSuperColumn(String tplCategory, String javaField) { + if (isTree(tplCategory)) { + return StrUtil.equalsAnyIgnoreCase(javaField, + ArrayUtils.addAll(GenConstants.TREE_ENTITY, GenConstants.BASE_ENTITY)); + } + return StrUtil.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY); + } +} diff --git a/bashi-generator/src/main/java/com/bashi/generator/domain/GenTableColumn.java b/bashi-generator/src/main/java/com/bashi/generator/domain/GenTableColumn.java new file mode 100644 index 0000000..45a44ca --- /dev/null +++ b/bashi-generator/src/main/java/com/bashi/generator/domain/GenTableColumn.java @@ -0,0 +1,249 @@ +package com.bashi.generator.domain; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.*; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotBlank; +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 代码生成业务字段表 gen_table_column + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("gen_table_column") +public class GenTableColumn implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 编号 + */ + @TableId(value = "column_id", type = IdType.AUTO) + private Long columnId; + + /** + * 归属表编号 + */ + private Long tableId; + + /** + * 列名称 + */ + private String columnName; + + /** + * 列描述 + */ + private String columnComment; + + /** + * 列类型 + */ + private String columnType; + + /** + * JAVA类型 + */ + private String javaType; + + /** + * JAVA字段名 + */ + @NotBlank(message = "Java属性不能为空") + private String javaField; + + /** + * 是否主键(1是) + */ + private String isPk; + + /** + * 是否自增(1是) + */ + private String isIncrement; + + /** + * 是否必填(1是) + */ + private String isRequired; + + /** + * 是否为插入字段(1是) + */ + private String isInsert; + + /** + * 是否编辑字段(1是) + */ + private String isEdit; + + /** + * 是否列表字段(1是) + */ + private String isList; + + /** + * 是否查询字段(1是) + */ + private String isQuery; + + /** + * 查询方式(EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围) + */ + private String queryType; + + /** + * 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件) + */ + private String htmlType; + + /** + * 字典类型 + */ + private String dictType; + + /** + * 排序 + */ + private Integer sort; + + /** + * 创建者 + */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** + * 请求参数 + */ + @TableField(exist = false) + private Map params = new HashMap<>(); + + public String getCapJavaField() { + return StrUtil.upperFirst(javaField); + } + + public boolean isPk() { + return isPk(this.isPk); + } + + public boolean isPk(String isPk) { + return isPk != null && StrUtil.equals("1", isPk); + } + + public boolean isIncrement() { + return isIncrement(this.isIncrement); + } + + public boolean isIncrement(String isIncrement) { + return isIncrement != null && StrUtil.equals("1", isIncrement); + } + + public boolean isRequired() { + return isRequired(this.isRequired); + } + + public boolean isRequired(String isRequired) { + return isRequired != null && StrUtil.equals("1", isRequired); + } + + public boolean isInsert() { + return isInsert(this.isInsert); + } + + public boolean isInsert(String isInsert) { + return isInsert != null && StrUtil.equals("1", isInsert); + } + + public boolean isEdit() { + return isInsert(this.isEdit); + } + + public boolean isEdit(String isEdit) { + return isEdit != null && StrUtil.equals("1", isEdit); + } + + public boolean isList() { + return isList(this.isList); + } + + public boolean isList(String isList) { + return isList != null && StrUtil.equals("1", isList); + } + + public boolean isQuery() { + return isQuery(this.isQuery); + } + + public boolean isQuery(String isQuery) { + return isQuery != null && StrUtil.equals("1", isQuery); + } + + public boolean isSuperColumn() { + return isSuperColumn(this.javaField); + } + + public static boolean isSuperColumn(String javaField) { + return StrUtil.equalsAnyIgnoreCase(javaField, + // BaseEntity + "createBy", "createTime", "updateBy", "updateTime", "remark", + // TreeEntity + "parentName", "parentId", "orderNum", "ancestors"); + } + + public boolean isUsableColumn() { + return isUsableColumn(javaField); + } + + public static boolean isUsableColumn(String javaField) { + // isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单 + return StrUtil.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark"); + } + + public String readConverterExp() { + String remarks = StrUtil.subBetween(this.columnComment, "(", ")"); + StringBuffer sb = new StringBuffer(); + if (StrUtil.isNotEmpty(remarks)) { + for (String value : remarks.split(" ")) { + if (StrUtil.isNotEmpty(value)) { + Object startStr = value.subSequence(0, 1); + String endStr = value.substring(1); + sb.append("").append(startStr).append("=").append(endStr).append(","); + } + } + return sb.deleteCharAt(sb.length() - 1).toString(); + } else { + return this.columnComment; + } + } +} diff --git a/bashi-generator/src/main/java/com/bashi/generator/mapper/GenTableColumnMapper.java b/bashi-generator/src/main/java/com/bashi/generator/mapper/GenTableColumnMapper.java new file mode 100644 index 0000000..d257f20 --- /dev/null +++ b/bashi-generator/src/main/java/com/bashi/generator/mapper/GenTableColumnMapper.java @@ -0,0 +1,22 @@ +package com.bashi.generator.mapper; + +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; +import com.bashi.generator.domain.GenTableColumn; + +import java.util.List; + +/** + * 业务字段 数据层 + * + * @author duteliang + */ +public interface GenTableColumnMapper extends BaseMapperPlus { + /** + * 根据表名称查询列信息 + * + * @param tableName 表名称 + * @return 列信息 + */ + public List selectDbTableColumnsByName(String tableName); + +} diff --git a/bashi-generator/src/main/java/com/bashi/generator/mapper/GenTableMapper.java b/bashi-generator/src/main/java/com/bashi/generator/mapper/GenTableMapper.java new file mode 100644 index 0000000..304dc02 --- /dev/null +++ b/bashi-generator/src/main/java/com/bashi/generator/mapper/GenTableMapper.java @@ -0,0 +1,69 @@ +package com.bashi.generator.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; +import com.bashi.generator.domain.GenTable; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 业务 数据层 + * + * @author duteliang + */ +public interface GenTableMapper extends BaseMapperPlus { + + + Page selectPageGenTableList(@Param("page") Page page, @Param("genTable") GenTable genTable); + + Page selectPageDbTableList(@Param("page") Page page, @Param("genTable") GenTable genTable); + + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + public List selectGenTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + public List selectDbTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + public List selectDbTableListByNames(String[] tableNames); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + public List selectGenTableAll(); + + /** + * 查询表ID业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + public GenTable selectGenTableById(Long id); + + /** + * 查询表名称业务信息 + * + * @param tableName 表名称 + * @return 业务信息 + */ + public GenTable selectGenTableByName(String tableName); + +} diff --git a/bashi-generator/src/main/java/com/bashi/generator/service/GenTableColumnServiceImpl.java b/bashi-generator/src/main/java/com/bashi/generator/service/GenTableColumnServiceImpl.java new file mode 100644 index 0000000..0350b15 --- /dev/null +++ b/bashi-generator/src/main/java/com/bashi/generator/service/GenTableColumnServiceImpl.java @@ -0,0 +1,65 @@ +package com.bashi.generator.service; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.generator.domain.GenTableColumn; +import com.bashi.generator.mapper.GenTableColumnMapper; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; + +/** + * 业务字段 服务层实现 + * + * @author duteliang + */ +@Service +public class GenTableColumnServiceImpl extends ServiceImpl implements IGenTableColumnService { + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + @Override + public List selectGenTableColumnListByTableId(Long tableId) { + return list(new LambdaQueryWrapper() + .eq(GenTableColumn::getTableId,tableId) + .orderByAsc(GenTableColumn::getSort)); + } + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + @Override + public int insertGenTableColumn(GenTableColumn genTableColumn) { + return baseMapper.insert(genTableColumn); + } + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + @Override + public int updateGenTableColumn(GenTableColumn genTableColumn) { + return baseMapper.updateById(genTableColumn); + } + + /** + * 删除业务字段对象 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteGenTableColumnByIds(String ids) { + return baseMapper.deleteBatchIds(Arrays.asList(ids.split(","))); + } +} diff --git a/bashi-generator/src/main/java/com/bashi/generator/service/GenTableServiceImpl.java b/bashi-generator/src/main/java/com/bashi/generator/service/GenTableServiceImpl.java new file mode 100644 index 0000000..e3e65ee --- /dev/null +++ b/bashi-generator/src/main/java/com/bashi/generator/service/GenTableServiceImpl.java @@ -0,0 +1,457 @@ +package com.bashi.generator.service; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.common.constant.Constants; +import com.bashi.common.constant.GenConstants; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.exception.CustomException; +import com.bashi.common.utils.JsonUtils; +import com.bashi.common.utils.PageUtils; +import com.bashi.common.utils.SecurityUtils; +import com.bashi.common.utils.file.FileUtils; +import com.bashi.generator.domain.GenTable; +import com.bashi.generator.domain.GenTableColumn; +import com.bashi.generator.mapper.GenTableColumnMapper; +import com.bashi.generator.mapper.GenTableMapper; +import com.bashi.generator.util.GenUtils; +import com.bashi.generator.util.VelocityInitializer; +import com.bashi.generator.util.VelocityUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * 业务 服务层实现 + * + * @author duteliang + */ +@Slf4j +@Service +public class GenTableServiceImpl extends ServiceImpl implements IGenTableService { + + @Autowired + private GenTableColumnMapper genTableColumnMapper; + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + @Override + public GenTable selectGenTableById(Long id) { + GenTable genTable = baseMapper.selectGenTableById(id); + setTableFromOptions(genTable); + return genTable; + } + + @Override + public TableDataInfo selectPageGenTableList(GenTable genTable) { + return PageUtils.buildDataInfo(baseMapper.selectPageGenTableList(PageUtils.buildPage(), genTable)); + } + + @Override + public TableDataInfo selectPageDbTableList(GenTable genTable) { + return PageUtils.buildDataInfo(baseMapper.selectPageDbTableList(PageUtils.buildPage(), genTable)); + } + + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + @Override + public List selectGenTableList(GenTable genTable) { + return baseMapper.selectGenTableList(genTable); + } + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + @Override + public List selectDbTableList(GenTable genTable) { + return baseMapper.selectDbTableList(genTable); + } + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + @Override + public List selectDbTableListByNames(String[] tableNames) { + return baseMapper.selectDbTableListByNames(tableNames); + } + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + @Override + public List selectGenTableAll() { + return baseMapper.selectGenTableAll(); + } + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + @Override + @Transactional + public void updateGenTable(GenTable genTable) { + String options = JsonUtils.toJsonString(genTable.getParams()); + genTable.setOptions(options); + int row = baseMapper.updateById(genTable); + if (row > 0) { + for (GenTableColumn cenTableColumn : genTable.getColumns()) { + genTableColumnMapper.update(cenTableColumn, + new LambdaUpdateWrapper() + .set(cenTableColumn.getIsPk() == null, GenTableColumn::getIsPk, null) + .set(cenTableColumn.getIsIncrement() == null, GenTableColumn::getIsIncrement, null) + .set(cenTableColumn.getIsInsert() == null, GenTableColumn::getIsInsert, null) + .set(cenTableColumn.getIsEdit() == null, GenTableColumn::getIsEdit, null) + .set(cenTableColumn.getIsList() == null, GenTableColumn::getIsList, null) + .set(cenTableColumn.getIsQuery() == null, GenTableColumn::getIsQuery, null) + .set(cenTableColumn.getIsRequired() == null, GenTableColumn::getIsRequired, null) + .eq(GenTableColumn::getColumnId,cenTableColumn.getColumnId())); + } + } + } + + /** + * 删除业务对象 + * + * @param tableIds 需要删除的数据ID + * @return 结果 + */ + @Override + @Transactional + public void deleteGenTableByIds(Long[] tableIds) { + List ids = Arrays.asList(tableIds); + removeByIds(ids); + genTableColumnMapper.delete(new LambdaQueryWrapper().in(GenTableColumn::getTableId, ids)); + } + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + */ + @Override + @Transactional + public void importGenTable(List tableList) { + String operName = SecurityUtils.getUsername(); + try { + for (GenTable table : tableList) { + String tableName = table.getTableName(); + GenUtils.initTable(table, operName); + int row = baseMapper.insert(table); + if (row > 0) { + // 保存列信息 + List genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + for (GenTableColumn column : genTableColumns) { + GenUtils.initColumnField(column, table); + genTableColumnMapper.insert(column); + } + } + } + } catch (Exception e) { + throw new CustomException("导入失败:" + e.getMessage()); + } + } + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + @Override + public Map previewCode(Long tableId) { + Map dataMap = new LinkedHashMap<>(); + // 查询表信息 + GenTable table = baseMapper.selectGenTableById(tableId); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + dataMap.put(template, sw.toString()); + } + return dataMap; + } + + /** + * 生成代码(下载方式) + * + * @param tableName 表名称 + * @return 数据 + */ + @Override + public byte[] downloadCode(String tableName) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(outputStream); + generatorCode(tableName, zip); + IOUtils.closeQuietly(zip); + return outputStream.toByteArray(); + } + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + */ + @Override + public void generatorCode(String tableName) { + // 查询表信息 + GenTable table = baseMapper.selectGenTableByName(tableName); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) { + if (!StrUtil.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm")) { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + String path = getGenPath(table, template); + FileUtils.writeUtf8String(sw.toString(), path); + } + } + } + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + @Override + @Transactional + public void synchDb(String tableName) { + GenTable table = baseMapper.selectGenTableByName(tableName); + List tableColumns = table.getColumns(); + List tableColumnNames = tableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList()); + + List dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + if (Validator.isEmpty(dbTableColumns)) { + throw new CustomException("同步数据失败,原表结构不存在"); + } + List dbTableColumnNames = dbTableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList()); + + dbTableColumns.forEach(column -> { + if (!tableColumnNames.contains(column.getColumnName())) { + GenUtils.initColumnField(column, table); + genTableColumnMapper.insert(column); + } + }); + + List delColumns = tableColumns.stream().filter(column -> !dbTableColumnNames.contains(column.getColumnName())).collect(Collectors.toList()); + if (CollUtil.isNotEmpty(delColumns)) { + List ids = delColumns.stream().map(GenTableColumn::getColumnId).collect(Collectors.toList()); + genTableColumnMapper.deleteBatchIds(ids); + } + } + + /** + * 批量生成代码(下载方式) + * + * @param tableNames 表数组 + * @return 数据 + */ + @Override + public byte[] downloadCode(String[] tableNames) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(outputStream); + for (String tableName : tableNames) { + generatorCode(tableName, zip); + } + IOUtils.closeQuietly(zip); + return outputStream.toByteArray(); + } + + /** + * 查询表信息并生成代码 + */ + private void generatorCode(String tableName, ZipOutputStream zip) { + // 查询表信息 + GenTable table = baseMapper.selectGenTableByName(tableName); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + try { + // 添加到zip + zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table))); + IOUtils.write(sw.toString(), zip, Constants.UTF8); + IOUtils.closeQuietly(sw); + zip.flush(); + zip.closeEntry(); + } catch (IOException e) { + log.error("渲染模板失败,表名:" + table.getTableName(), e); + } + } + } + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + @Override + public void validateEdit(GenTable genTable) { + if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) { + Map paramsObj = genTable.getParams(); + if (Validator.isEmpty(paramsObj.get(GenConstants.TREE_CODE))) { + throw new CustomException("树编码字段不能为空"); + } else if (Validator.isEmpty(paramsObj.get(GenConstants.TREE_PARENT_CODE))) { + throw new CustomException("树父编码字段不能为空"); + } else if (Validator.isEmpty(paramsObj.get(GenConstants.TREE_NAME))) { + throw new CustomException("树名称字段不能为空"); + } else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) { + if (Validator.isEmpty(genTable.getSubTableName())) { + throw new CustomException("关联子表的表名不能为空"); + } else if (Validator.isEmpty(genTable.getSubTableFkName())) { + throw new CustomException("子表关联的外键名不能为空"); + } + } + } + } + + /** + * 设置主键列信息 + * + * @param table 业务表信息 + */ + public void setPkColumn(GenTable table) { + for (GenTableColumn column : table.getColumns()) { + if (column.isPk()) { + table.setPkColumn(column); + break; + } + } + if (Validator.isNull(table.getPkColumn())) { + table.setPkColumn(table.getColumns().get(0)); + } + if (GenConstants.TPL_SUB.equals(table.getTplCategory())) { + for (GenTableColumn column : table.getSubTable().getColumns()) { + if (column.isPk()) { + table.getSubTable().setPkColumn(column); + break; + } + } + if (Validator.isNull(table.getSubTable().getPkColumn())) { + table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0)); + } + } + } + + /** + * 设置主子表信息 + * + * @param table 业务表信息 + */ + public void setSubTable(GenTable table) { + String subTableName = table.getSubTableName(); + if (Validator.isNotEmpty(subTableName)) { + table.setSubTable(baseMapper.selectGenTableByName(subTableName)); + } + } + + /** + * 设置代码生成其他选项值 + * + * @param genTable 设置后的生成对象 + */ + public void setTableFromOptions(GenTable genTable) { + Map paramsObj = JsonUtils.parseMap(genTable.getOptions()); + if (Validator.isNotNull(paramsObj)) { + String treeCode = Convert.toStr(paramsObj.get(GenConstants.TREE_CODE)); + String treeParentCode = Convert.toStr(paramsObj.get(GenConstants.TREE_PARENT_CODE)); + String treeName = Convert.toStr(paramsObj.get(GenConstants.TREE_NAME)); + String parentMenuId = Convert.toStr(paramsObj.get(GenConstants.PARENT_MENU_ID)); + String parentMenuName = Convert.toStr(paramsObj.get(GenConstants.PARENT_MENU_NAME)); + + genTable.setTreeCode(treeCode); + genTable.setTreeParentCode(treeParentCode); + genTable.setTreeName(treeName); + genTable.setParentMenuId(parentMenuId); + genTable.setParentMenuName(parentMenuName); + } + } + + /** + * 获取代码生成地址 + * + * @param table 业务表信息 + * @param template 模板文件路径 + * @return 生成地址 + */ + public static String getGenPath(GenTable table, String template) { + String genPath = table.getGenPath(); + if (StrUtil.equals(genPath, "/")) { + return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table); + } + return genPath + File.separator + VelocityUtils.getFileName(template, table); + } +} diff --git a/bashi-generator/src/main/java/com/bashi/generator/service/IGenTableColumnService.java b/bashi-generator/src/main/java/com/bashi/generator/service/IGenTableColumnService.java new file mode 100644 index 0000000..6339370 --- /dev/null +++ b/bashi-generator/src/main/java/com/bashi/generator/service/IGenTableColumnService.java @@ -0,0 +1,45 @@ +package com.bashi.generator.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.generator.domain.GenTableColumn; + +import java.util.List; + +/** + * 业务字段 服务层 + * + * @author duteliang + */ +public interface IGenTableColumnService extends IService { + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + public List selectGenTableColumnListByTableId(Long tableId); + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int insertGenTableColumn(GenTableColumn genTableColumn); + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int updateGenTableColumn(GenTableColumn genTableColumn); + + /** + * 删除业务字段信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteGenTableColumnByIds(String ids); +} diff --git a/bashi-generator/src/main/java/com/bashi/generator/service/IGenTableService.java b/bashi-generator/src/main/java/com/bashi/generator/service/IGenTableService.java new file mode 100644 index 0000000..a9cf235 --- /dev/null +++ b/bashi-generator/src/main/java/com/bashi/generator/service/IGenTableService.java @@ -0,0 +1,130 @@ +package com.bashi.generator.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.generator.domain.GenTable; + +import java.util.List; +import java.util.Map; + +/** + * 业务 服务层 + * + * @author duteliang + */ +public interface IGenTableService extends IService { + + + TableDataInfo selectPageGenTableList(GenTable genTable); + + + TableDataInfo selectPageDbTableList(GenTable genTable); + + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + public List selectGenTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + public List selectDbTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + public List selectDbTableListByNames(String[] tableNames); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + public List selectGenTableAll(); + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + public GenTable selectGenTableById(Long id); + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + public void updateGenTable(GenTable genTable); + + /** + * 删除业务信息 + * + * @param tableIds 需要删除的表数据ID + * @return 结果 + */ + public void deleteGenTableByIds(Long[] tableIds); + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + */ + public void importGenTable(List tableList); + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + public Map previewCode(Long tableId); + + /** + * 生成代码(下载方式) + * + * @param tableName 表名称 + * @return 数据 + */ + public byte[] downloadCode(String tableName); + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + * @return 数据 + */ + public void generatorCode(String tableName); + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + public void synchDb(String tableName); + + /** + * 批量生成代码(下载方式) + * + * @param tableNames 表数组 + * @return 数据 + */ + public byte[] downloadCode(String[] tableNames); + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + public void validateEdit(GenTable genTable); +} diff --git a/bashi-generator/src/main/java/com/bashi/generator/util/GenUtils.java b/bashi-generator/src/main/java/com/bashi/generator/util/GenUtils.java new file mode 100644 index 0000000..ba00be8 --- /dev/null +++ b/bashi-generator/src/main/java/com/bashi/generator/util/GenUtils.java @@ -0,0 +1,259 @@ +package com.bashi.generator.util; + +import cn.hutool.core.util.StrUtil; +import com.bashi.common.constant.GenConstants; +import com.bashi.generator.config.GenConfig; +import com.bashi.generator.domain.GenTable; +import com.bashi.generator.domain.GenTableColumn; +import org.apache.commons.lang3.RegExUtils; + +import java.util.Arrays; + +/** + * 代码生成器 工具类 + * + * @author duteliang + */ +public class GenUtils +{ + /** + * 初始化表信息 + */ + public static void initTable(GenTable genTable, String operName) + { + genTable.setClassName(convertClassName(genTable.getTableName())); + genTable.setPackageName(GenConfig.getPackageName()); + genTable.setModuleName(getModuleName(GenConfig.getPackageName())); + genTable.setBusinessName(getBusinessName(genTable.getTableName())); + genTable.setFunctionName(replaceText(genTable.getTableComment())); + genTable.setFunctionAuthor(GenConfig.getAuthor()); + genTable.setCreateBy(operName); + } + + /** + * 初始化列属性字段 + */ + public static void initColumnField(GenTableColumn column, GenTable table) + { + String dataType = getDbType(column.getColumnType()); + String columnName = column.getColumnName(); + column.setTableId(table.getTableId()); + column.setCreateBy(table.getCreateBy()); + // 设置java字段名 + column.setJavaField(StrUtil.toCamelCase(columnName)); + // 设置默认类型 + column.setJavaType(GenConstants.TYPE_STRING); + + if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) + { + // 字符串长度超过500设置为文本域 + Integer columnLength = getColumnLength(column.getColumnType()); + String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT; + column.setHtmlType(htmlType); + } + else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) + { + column.setJavaType(GenConstants.TYPE_DATE); + column.setHtmlType(GenConstants.HTML_DATETIME); + } + else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) + { + column.setHtmlType(GenConstants.HTML_INPUT); + + // 如果是浮点型 统一用BigDecimal + String[] str = StrUtil.splitToArray(StrUtil.subBetween(column.getColumnType(), "(", ")"), ","); + if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) + { + column.setJavaType(GenConstants.TYPE_BIGDECIMAL); + } + // 如果是整形 + else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10) + { + column.setJavaType(GenConstants.TYPE_INTEGER); + } + // 长整形 + else + { + column.setJavaType(GenConstants.TYPE_LONG); + } + } + + // 插入字段(默认所有字段都需要插入) + column.setIsInsert(GenConstants.REQUIRE); + + // 编辑字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName) && !column.isPk()) + { + column.setIsEdit(GenConstants.REQUIRE); + } + // 列表字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName) && !column.isPk()) + { + column.setIsList(GenConstants.REQUIRE); + } + // 查询字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()) + { + column.setIsQuery(GenConstants.REQUIRE); + } + + // 查询字段类型 + if (StrUtil.endWithIgnoreCase(columnName, "name")) + { + column.setQueryType(GenConstants.QUERY_LIKE); + } + // 状态字段设置单选框 + if (StrUtil.endWithIgnoreCase(columnName, "status")) + { + column.setHtmlType(GenConstants.HTML_RADIO); + } + // 类型&性别字段设置下拉框 + else if (StrUtil.endWithIgnoreCase(columnName, "type") + || StrUtil.endWithIgnoreCase(columnName, "sex")) + { + column.setHtmlType(GenConstants.HTML_SELECT); + } + // 图片字段设置图片上传控件 + else if (StrUtil.endWithIgnoreCase(columnName, "image")) + { + column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD); + } + // 文件字段设置文件上传控件 + else if (StrUtil.endWithIgnoreCase(columnName, "file")) + { + column.setHtmlType(GenConstants.HTML_FILE_UPLOAD); + } + // 内容字段设置富文本控件 + else if (StrUtil.endWithIgnoreCase(columnName, "content")) + { + column.setHtmlType(GenConstants.HTML_EDITOR); + } + } + + /** + * 校验数组是否包含指定值 + * + * @param arr 数组 + * @param targetValue 值 + * @return 是否包含 + */ + public static boolean arraysContains(String[] arr, String targetValue) + { + return Arrays.asList(arr).contains(targetValue); + } + + /** + * 获取模块名 + * + * @param packageName 包名 + * @return 模块名 + */ + public static String getModuleName(String packageName) + { + int lastIndex = packageName.lastIndexOf("."); + int nameLength = packageName.length(); + String moduleName = StrUtil.sub(packageName, lastIndex + 1, nameLength); + return moduleName; + } + + /** + * 获取业务名 + * + * @param tableName 表名 + * @return 业务名 + */ + public static String getBusinessName(String tableName) + { + int lastIndex = tableName.lastIndexOf("_"); + int nameLength = tableName.length(); + String businessName = StrUtil.sub(tableName, lastIndex + 1, nameLength); + return businessName; + } + + /** + * 表名转换成Java类名 + * + * @param tableName 表名称 + * @return 类名 + */ + public static String convertClassName(String tableName) + { + boolean autoRemovePre = GenConfig.getAutoRemovePre(); + String tablePrefix = GenConfig.getTablePrefix(); + if (autoRemovePre && StrUtil.isNotEmpty(tablePrefix)) + { + String[] searchList = StrUtil.splitToArray(tablePrefix, ","); + tableName = replaceFirst(tableName, searchList); + } + return StrUtil.upperFirst(StrUtil.toCamelCase(tableName)); + } + + /** + * 批量替换前缀 + * + * @param replacementm 替换值 + * @param searchList 替换列表 + * @return + */ + public static String replaceFirst(String replacementm, String[] searchList) + { + String text = replacementm; + for (String searchString : searchList) + { + if (replacementm.startsWith(searchString)) + { + text = replacementm.replaceFirst(searchString, ""); + break; + } + } + return text; + } + + /** + * 关键字替换 + * + * @param text 需要被替换的名字 + * @return 替换后的名字 + */ + public static String replaceText(String text) + { + return RegExUtils.replaceAll(text, "(?:表|若依)", ""); + } + + /** + * 获取数据库类型字段 + * + * @param columnType 列类型 + * @return 截取后的列类型 + */ + public static String getDbType(String columnType) + { + if (StrUtil.indexOf(columnType, '(') > 0) + { + return StrUtil.subBefore(columnType, "(",false); + } + else + { + return columnType; + } + } + + /** + * 获取字段长度 + * + * @param columnType 列类型 + * @return 截取后的列类型 + */ + public static Integer getColumnLength(String columnType) + { + if (StrUtil.indexOf(columnType, '(') > 0) + { + String length = StrUtil.subBetween(columnType, "(", ")"); + return Integer.valueOf(length); + } + else + { + return 0; + } + } +} diff --git a/bashi-generator/src/main/java/com/bashi/generator/util/VelocityInitializer.java b/bashi-generator/src/main/java/com/bashi/generator/util/VelocityInitializer.java new file mode 100644 index 0000000..5a0f96b --- /dev/null +++ b/bashi-generator/src/main/java/com/bashi/generator/util/VelocityInitializer.java @@ -0,0 +1,35 @@ +package com.bashi.generator.util; + +import java.util.Properties; +import org.apache.velocity.app.Velocity; +import com.bashi.common.constant.Constants; + +/** + * VelocityEngine工厂 + * + * @author duteliang + */ +public class VelocityInitializer +{ + /** + * 初始化vm方法 + */ + public static void initVelocity() + { + Properties p = new Properties(); + try + { + // 加载classpath目录下的vm文件 + p.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + // 定义字符集 + p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8); + p.setProperty(Velocity.OUTPUT_ENCODING, Constants.UTF8); + // 初始化Velocity引擎,指定配置Properties + Velocity.init(p); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } +} diff --git a/bashi-generator/src/main/java/com/bashi/generator/util/VelocityUtils.java b/bashi-generator/src/main/java/com/bashi/generator/util/VelocityUtils.java new file mode 100644 index 0000000..4c0f138 --- /dev/null +++ b/bashi-generator/src/main/java/com/bashi/generator/util/VelocityUtils.java @@ -0,0 +1,385 @@ +package com.bashi.generator.util; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import com.bashi.common.constant.GenConstants; +import com.bashi.common.utils.DateUtils; +import com.bashi.common.utils.JsonUtils; +import com.bashi.generator.domain.GenTable; +import com.bashi.generator.domain.GenTableColumn; +import org.apache.velocity.VelocityContext; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +/** + * 模板处理工具类 + * + * @author duteliang + */ +public class VelocityUtils +{ + /** 项目空间路径 */ + private static final String PROJECT_PATH = "main/java"; + + /** mybatis空间路径 */ + private static final String MYBATIS_PATH = "main/resources/mapper"; + + /** 默认上级菜单,系统工具 */ + private static final String DEFAULT_PARENT_MENU_ID = "3"; + + /** + * 设置模板变量信息 + * + * @return 模板列表 + */ + public static VelocityContext prepareContext(GenTable genTable) + { + String moduleName = genTable.getModuleName(); + String businessName = genTable.getBusinessName(); + String packageName = genTable.getPackageName(); + String tplCategory = genTable.getTplCategory(); + String functionName = genTable.getFunctionName(); + + VelocityContext velocityContext = new VelocityContext(); + velocityContext.put("tplCategory", genTable.getTplCategory()); + velocityContext.put("tableName", genTable.getTableName()); + velocityContext.put("functionName", StrUtil.isNotEmpty(functionName) ? functionName : "【请填写功能名称】"); + velocityContext.put("ClassName", genTable.getClassName()); + velocityContext.put("className", StrUtil.lowerFirst(genTable.getClassName())); + velocityContext.put("moduleName", genTable.getModuleName()); + velocityContext.put("BusinessName", StrUtil.upperFirst(genTable.getBusinessName())); + velocityContext.put("businessName", genTable.getBusinessName()); + velocityContext.put("basePackage", getPackagePrefix(packageName)); + velocityContext.put("packageName", packageName); + velocityContext.put("author", genTable.getFunctionAuthor()); + velocityContext.put("datetime", DateUtils.getDate()); + velocityContext.put("pkColumn", genTable.getPkColumn()); + velocityContext.put("importList", getImportList(genTable)); + velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName)); + velocityContext.put("columns", genTable.getColumns()); + velocityContext.put("table", genTable); + setMenuVelocityContext(velocityContext, genTable); + if (GenConstants.TPL_TREE.equals(tplCategory)) + { + setTreeVelocityContext(velocityContext, genTable); + } + if (GenConstants.TPL_SUB.equals(tplCategory)) + { + setSubVelocityContext(velocityContext, genTable); + } + return velocityContext; + } + + public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) + { + String options = genTable.getOptions(); + Map paramsObj = JsonUtils.parseMap(options); + String parentMenuId = getParentMenuId(paramsObj); + context.put("parentMenuId", parentMenuId); + } + + public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) + { + String options = genTable.getOptions(); + Map paramsObj = JsonUtils.parseMap(options); + String treeCode = getTreecode(paramsObj); + String treeParentCode = getTreeParentCode(paramsObj); + String treeName = getTreeName(paramsObj); + + context.put("treeCode", treeCode); + context.put("treeParentCode", treeParentCode); + context.put("treeName", treeName); + context.put("expandColumn", getExpandColumn(genTable)); + if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) + { + context.put("tree_parent_code", paramsObj.get(GenConstants.TREE_PARENT_CODE)); + } + if (paramsObj.containsKey(GenConstants.TREE_NAME)) + { + context.put("tree_name", paramsObj.get(GenConstants.TREE_NAME)); + } + } + + public static void setSubVelocityContext(VelocityContext context, GenTable genTable) + { + GenTable subTable = genTable.getSubTable(); + String subTableName = genTable.getSubTableName(); + String subTableFkName = genTable.getSubTableFkName(); + String subClassName = genTable.getSubTable().getClassName(); + String subTableFkClassName = StrUtil.toCamelCase(subTableFkName); + + context.put("subTable", subTable); + context.put("subTableName", subTableName); + context.put("subTableFkName", subTableFkName); + context.put("subTableFkClassName", subTableFkClassName); + context.put("subTableFkclassName", StrUtil.lowerFirst(subTableFkClassName)); + context.put("subClassName", subClassName); + context.put("subclassName", StrUtil.lowerFirst(subClassName)); + context.put("subImportList", getImportList(genTable.getSubTable())); + } + + /** + * 获取模板信息 + * + * @return 模板列表 + */ + public static List getTemplateList(String tplCategory) + { + List templates = new ArrayList(); + templates.add("vm/java/domain.java.vm"); + templates.add("vm/java/vo.java.vm"); + templates.add("vm/java/queryBo.java.vm"); + templates.add("vm/java/addBo.java.vm"); + templates.add("vm/java/editBo.java.vm"); + templates.add("vm/java/mapper.java.vm"); + templates.add("vm/java/service.java.vm"); + templates.add("vm/java/serviceImpl.java.vm"); + templates.add("vm/java/controller.java.vm"); + templates.add("vm/xml/mapper.xml.vm"); + templates.add("vm/sql/sql.vm"); + templates.add("vm/js/api.js.vm"); + if (GenConstants.TPL_CRUD.equals(tplCategory)) + { + templates.add("vm/vue/index.vue.vm"); + } + else if (GenConstants.TPL_TREE.equals(tplCategory)) + { + templates.add("vm/vue/index-tree.vue.vm"); + } + else if (GenConstants.TPL_SUB.equals(tplCategory)) + { + templates.add("vm/vue/index.vue.vm"); + templates.add("vm/java/sub-domain.java.vm"); + } + return templates; + } + + /** + * 获取文件名 + */ + public static String getFileName(String template, GenTable genTable) + { + // 文件名称 + String fileName = ""; + // 包路径 + String packageName = genTable.getPackageName(); + // 模块名 + String moduleName = genTable.getModuleName(); + // 大写类名 + String className = genTable.getClassName(); + // 业务名称 + String businessName = genTable.getBusinessName(); + + String javaPath = PROJECT_PATH + "/" + StrUtil.replace(packageName, ".", "/"); + String mybatisPath = MYBATIS_PATH + "/" + moduleName; + String vuePath = "vue"; + + if (template.contains("domain.java.vm")) + { + fileName = StrUtil.format("{}/domain/{}.java", javaPath, className); + } + if (template.contains("vo.java.vm")) + { + fileName = StrUtil.format("{}/vo/{}Vo.java", javaPath, className); + } + if (template.contains("queryBo.java.vm")) + { + fileName = StrUtil.format("{}/bo/{}QueryBo.java", javaPath, className); + } + if (template.contains("addBo.java.vm")) + { + fileName = StrUtil.format("{}/bo/{}AddBo.java", javaPath, className); + } + if (template.contains("editBo.java.vm")) + { + fileName = StrUtil.format("{}/bo/{}EditBo.java", javaPath, className); + } + if (template.contains("sub-domain.java.vm") && StrUtil.equals(GenConstants.TPL_SUB, genTable.getTplCategory())) + { + fileName = StrUtil.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName()); + } + else if (template.contains("mapper.java.vm")) + { + fileName = StrUtil.format("{}/mapper/{}Mapper.java", javaPath, className); + } + else if (template.contains("service.java.vm")) + { + fileName = StrUtil.format("{}/service/I{}Service.java", javaPath, className); + } + else if (template.contains("serviceImpl.java.vm")) + { + fileName = StrUtil.format("{}/service/impl/{}ServiceImpl.java", javaPath, className); + } + else if (template.contains("controller.java.vm")) + { + fileName = StrUtil.format("{}/controller/{}Controller.java", javaPath, className); + } + else if (template.contains("mapper.xml.vm")) + { + fileName = StrUtil.format("{}/{}Mapper.xml", mybatisPath, className); + } + else if (template.contains("sql.vm")) + { + fileName = businessName + "Menu.sql"; + } + else if (template.contains("api.js.vm")) + { + fileName = StrUtil.format("{}/api/{}/{}.js", vuePath, moduleName, businessName); + } + else if (template.contains("index.vue.vm")) + { + fileName = StrUtil.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } + else if (template.contains("index-tree.vue.vm")) + { + fileName = StrUtil.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } + return fileName; + } + + /** + * 获取包前缀 + * + * @param packageName 包名称 + * @return 包前缀名称 + */ + public static String getPackagePrefix(String packageName) + { + int lastIndex = packageName.lastIndexOf("."); + String basePackage = StrUtil.sub(packageName, 0, lastIndex); + return basePackage; + } + + /** + * 根据列类型获取导入包 + * + * @param genTable 业务表对象 + * @return 返回需要导入的包列表 + */ + public static HashSet getImportList(GenTable genTable) + { + List columns = genTable.getColumns(); + GenTable subGenTable = genTable.getSubTable(); + HashSet importList = new HashSet(); + if (Validator.isNotNull(subGenTable)) + { + importList.add("java.util.List"); + } + for (GenTableColumn column : columns) + { + if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) + { + importList.add("java.util.Date"); + importList.add("com.fasterxml.jackson.annotation.JsonFormat"); + } + else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) + { + importList.add("java.math.BigDecimal"); + } + } + return importList; + } + + /** + * 获取权限前缀 + * + * @param moduleName 模块名称 + * @param businessName 业务名称 + * @return 返回权限前缀 + */ + public static String getPermissionPrefix(String moduleName, String businessName) + { + return StrUtil.format("{}:{}", moduleName, businessName); + } + + /** + * 获取上级菜单ID字段 + * + * @param paramsObj 生成其他选项 + * @return 上级菜单ID字段 + */ + public static String getParentMenuId(Map paramsObj) + { + if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID)) + { + return Convert.toStr(paramsObj.get(GenConstants.PARENT_MENU_ID)); + } + return DEFAULT_PARENT_MENU_ID; + } + + /** + * 获取树编码 + * + * @param paramsObj 生成其他选项 + * @return 树编码 + */ + public static String getTreecode(Map paramsObj) + { + if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_CODE)) + { + return StrUtil.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_CODE))); + } + return StrUtil.EMPTY; + } + + /** + * 获取树父编码 + * + * @param paramsObj 生成其他选项 + * @return 树父编码 + */ + public static String getTreeParentCode(Map paramsObj) + { + if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) + { + return StrUtil.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_PARENT_CODE))); + } + return StrUtil.EMPTY; + } + + /** + * 获取树名称 + * + * @param paramsObj 生成其他选项 + * @return 树名称 + */ + public static String getTreeName(Map paramsObj) + { + if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_NAME)) + { + return StrUtil.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_NAME))); + } + return StrUtil.EMPTY; + } + + /** + * 获取需要在哪一列上面显示展开按钮 + * + * @param genTable 业务表对象 + * @return 展开按钮列序号 + */ + public static int getExpandColumn(GenTable genTable) + { + String options = genTable.getOptions(); + Map paramsObj = JsonUtils.parseMap(options); + String treeName = Convert.toStr(paramsObj.get(GenConstants.TREE_NAME)); + int num = 0; + for (GenTableColumn column : genTable.getColumns()) + { + if (column.isList()) + { + num++; + String columnName = column.getColumnName(); + if (columnName.equals(treeName)) + { + break; + } + } + } + return num; + } +} diff --git a/bashi-generator/src/main/resources/generator.yml b/bashi-generator/src/main/resources/generator.yml new file mode 100644 index 0000000..70cf03e --- /dev/null +++ b/bashi-generator/src/main/resources/generator.yml @@ -0,0 +1,10 @@ +# 代码生成 +gen: + # 作者 + author: duteliang + # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool + packageName: com.bashi.system + # 自动去除表前缀,默认是false + autoRemovePre: false + # 表前缀(生成类名不会包含表前缀,多个用逗号分隔) + tablePrefix: sys_ diff --git a/bashi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml b/bashi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml new file mode 100644 index 0000000..d663fec --- /dev/null +++ b/bashi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bashi-generator/src/main/resources/mapper/generator/GenTableMapper.xml b/bashi-generator/src/main/resources/mapper/generator/GenTableMapper.xml new file mode 100644 index 0000000..efe91b2 --- /dev/null +++ b/bashi-generator/src/main/resources/mapper/generator/GenTableMapper.xml @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select table_id, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table + + + + + + + + + + + + + + + + + + + + + + diff --git a/bashi-generator/src/main/resources/vm/java/addBo.java.vm b/bashi-generator/src/main/resources/vm/java/addBo.java.vm new file mode 100644 index 0000000..82d0862 --- /dev/null +++ b/bashi-generator/src/main/resources/vm/java/addBo.java.vm @@ -0,0 +1,45 @@ +package ${packageName}.bo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import java.util.Date; +import javax.validation.constraints.*; + + +#foreach ($import in $importList) +import ${import}; +#end + +/** + * ${functionName}添加对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +@Data +@ApiModel("${functionName}添加对象") +public class ${ClassName}AddBo { + +#foreach ($column in $columns) +#if($column.isInsert && $column.isPk!=1) + + /** $column.columnComment */ + @ApiModelProperty("$column.columnComment") +#if($column.isRequired==1) +#if($column.javaType == 'String') + @NotBlank(message = "$column.columnComment不能为空") +#else + @NotNull(message = "$column.columnComment不能为空") +#end +#end + private $column.javaType $column.javaField; +#end +#end +#if($table.sub) + + /** $table.subTable.functionName信息 */ + @ApiModelProperty("$table.subTable.functionName") + private List<${subClassName}> ${subclassName}List; +#end +} diff --git a/bashi-generator/src/main/resources/vm/java/controller.java.vm b/bashi-generator/src/main/resources/vm/java/controller.java.vm new file mode 100644 index 0000000..497ba4c --- /dev/null +++ b/bashi-generator/src/main/resources/vm/java/controller.java.vm @@ -0,0 +1,121 @@ +package ${packageName}.controller; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import javax.validation.constraints.*; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import com.bashi.common.annotation.RepeatSubmit; +import com.bashi.common.annotation.Log; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.enums.BusinessType; +import ${packageName}.vo.${ClassName}Vo; +import ${packageName}.bo.${ClassName}QueryBo; +import ${packageName}.bo.${ClassName}AddBo; +import ${packageName}.bo.${ClassName}EditBo; +import ${packageName}.service.I${ClassName}Service; +import com.bashi.common.utils.poi.ExcelUtil; +#if($table.crud || $table.sub) +import com.bashi.common.core.page.TableDataInfo; +#elseif($table.tree) +#end +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; + +/** + * ${functionName}Controller + * + * @author ${author} + * @date ${datetime} + */ +@Api(value = "${functionName}控制器", tags = {"${functionName}管理"}) +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@RestController +@RequestMapping("/${moduleName}/${businessName}") +public class ${ClassName}Controller extends BaseController { + + private final I${ClassName}Service i${ClassName}Service; + + /** + * 查询${functionName}列表 + */ + @ApiOperation("查询${functionName}列表") + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')") + @GetMapping("/list") +#if($table.crud || $table.sub) + public TableDataInfo<${ClassName}> list(PageParams pageParams, @Validated ${ClassName} bo) { + IPage<${ClassName}> page = i${ClassName}Service.page(Condition.getPage(pageParams), Wrappers.query(bo)); + return PageUtils.buildDataInfo(page); + } +#elseif($table.tree) + public AjaxResult> list(@Validated ${ClassName}QueryBo bo) { + List<${ClassName}Vo> list = i${ClassName}Service.queryList(bo); + return AjaxResult.success(list); + } +#end + + /** + * 导出${functionName}列表 + */ + @ApiOperation("导出${functionName}列表") + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')") + @Log(title = "${functionName}", businessType = BusinessType.EXPORT) + @GetMapping("/export") + public AjaxResult<${ClassName}Vo> export(@Validated ${ClassName}QueryBo bo) { + List<${ClassName}Vo> list = i${ClassName}Service.queryList(bo); + ExcelUtil<${ClassName}Vo> util = new ExcelUtil<${ClassName}Vo>(${ClassName}Vo.class); + return util.exportExcel(list, "${functionName}"); + } + + /** + * 获取${functionName}详细信息 + */ + @ApiOperation("获取${functionName}详细信息") + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')") + @GetMapping("/{${pkColumn.javaField}}") + public AjaxResult<${ClassName}> getInfo(@NotNull(message = "主键不能为空") + @PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}) { + return AjaxResult.success(i${ClassName}Service.getById(${pkColumn.javaField})); + } + + /** + * 新增${functionName} + */ + @ApiOperation("新增${functionName}") + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')") + @Log(title = "${functionName}", businessType = BusinessType.INSERT) + @RepeatSubmit + @PostMapping() + public AjaxResult add(@Validated @RequestBody ${ClassName} bo) { + return toAjax(i${ClassName}Service.save(bo) ? 1 : 0); + } + + /** + * 修改${functionName} + */ + @ApiOperation("修改${functionName}") + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')") + @Log(title = "${functionName}", businessType = BusinessType.UPDATE) + @RepeatSubmit + @PutMapping() + public AjaxResult edit(@Validated @RequestBody ${ClassName} bo) { + return toAjax(i${ClassName}Service.updateById(bo) ? 1 : 0); + } + + /** + * 删除${functionName} + */ + @ApiOperation("删除${functionName}") + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')") + @Log(title = "${functionName}" , businessType = BusinessType.DELETE) + @DeleteMapping("/{${pkColumn.javaField}s}") + public AjaxResult remove(@NotEmpty(message = "主键不能为空") + @PathVariable ${pkColumn.javaType} ${pkColumn.javaField}s) { + List idList = Stream.of(ids.split(",")).collect(Collectors.toList()); + return toAjax(i${ClassName}Service.removeByIds(idList) ? 1 : 0); + } +} diff --git a/bashi-generator/src/main/resources/vm/java/domain.java.vm b/bashi-generator/src/main/resources/vm/java/domain.java.vm new file mode 100644 index 0000000..fa5889d --- /dev/null +++ b/bashi-generator/src/main/resources/vm/java/domain.java.vm @@ -0,0 +1,50 @@ +package ${packageName}.domain; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import java.io.Serializable; +import java.util.Date; +import java.math.BigDecimal; + +/** + * ${functionName}对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("${tableName}") +@ApiModel("${functionName}添加对象") +public class ${ClassName} implements Serializable { + + private static final long serialVersionUID=1L; + +#foreach ($column in $columns) + + /** $column.columnComment */ +#if($column.javaField=="createBy"||$column.javaField=="createTime") + @TableField(fill = FieldFill.INSERT) +#end +#if($column.javaField=="updateBy"||$column.javaField=="updateTime") + @TableField(fill = FieldFill.INSERT_UPDATE) +#end +#if($column.javaField=='delFlag') + @TableLogic +#end +#if($column.javaField=='version') + @Version +#end +#if($column.isPk==1) + @TableId(value = "$column.columnName") +#end + @ApiModelProperty("$column.columnComment") + private $column.javaType $column.javaField; +#end + +} diff --git a/bashi-generator/src/main/resources/vm/java/editBo.java.vm b/bashi-generator/src/main/resources/vm/java/editBo.java.vm new file mode 100644 index 0000000..3533a91 --- /dev/null +++ b/bashi-generator/src/main/resources/vm/java/editBo.java.vm @@ -0,0 +1,44 @@ +package ${packageName}.bo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import java.util.Date; +import javax.validation.constraints.*; + +#foreach ($import in $importList) +import ${import}; +#end + +/** + * ${functionName}编辑对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +@Data +@ApiModel("${functionName}编辑对象") +public class ${ClassName}EditBo { + +#foreach ($column in $columns) +#if($column.isEdit || $column.isPk==1) + + /** $column.columnComment */ + @ApiModelProperty("$column.columnComment") +#if($column.isRequired==1) +#if($column.javaType == 'String') + @NotBlank(message = "$column.columnComment不能为空") +#else + @NotNull(message = "$column.columnComment不能为空") +#end +#end + private $column.javaType $column.javaField; +#end +#end +#if($table.sub) + + /** $table.subTable.functionName信息 */ + @ApiModelProperty("$table.subTable.functionName") + private List<${subClassName}> ${subclassName}List; +#end +} diff --git a/bashi-generator/src/main/resources/vm/java/mapper.java.vm b/bashi-generator/src/main/resources/vm/java/mapper.java.vm new file mode 100644 index 0000000..80825ef --- /dev/null +++ b/bashi-generator/src/main/resources/vm/java/mapper.java.vm @@ -0,0 +1,16 @@ +package ${packageName}.mapper; + +import ${packageName}.domain.${ClassName}; +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; + +/** + * ${functionName}Mapper接口 + * + * @author ${author} + * @date ${datetime} + */ +// 如使需切换数据源 请勿使用缓存 会造成数据不一致现象 +// @CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class) +public interface ${ClassName}Mapper extends BaseMapperPlus<${ClassName}> { + +} diff --git a/bashi-generator/src/main/resources/vm/java/queryBo.java.vm b/bashi-generator/src/main/resources/vm/java/queryBo.java.vm new file mode 100644 index 0000000..26632d3 --- /dev/null +++ b/bashi-generator/src/main/resources/vm/java/queryBo.java.vm @@ -0,0 +1,54 @@ +package ${packageName}.bo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +#foreach ($import in $importList) +import ${import}; +#end +#if($table.crud || $table.sub) +#elseif($table.tree) +#end + +/** + * ${functionName}分页查询对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +#if($table.crud || $table.sub) +#set($Entity="BaseEntity") +#elseif($table.tree) +#set($Entity="TreeEntity") +#end + +@Data +@EqualsAndHashCode(callSuper = true) +@ApiModel("${functionName}分页查询对象") +public class ${ClassName}QueryBo extends ${Entity} { + + /** 分页大小 */ + @ApiModelProperty("分页大小") + private Integer pageSize; + /** 当前页数 */ + @ApiModelProperty("当前页数") + private Integer pageNum; + /** 排序列 */ + @ApiModelProperty("排序列") + private String orderByColumn; + /** 排序的方向desc或者asc */ + @ApiModelProperty(value = "排序的方向", example = "asc,desc") + private String isAsc; + + +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField) && $column.query) + /** $column.columnComment */ + @ApiModelProperty("$column.columnComment") + private $column.javaType $column.javaField; +#end +#end + +} diff --git a/bashi-generator/src/main/resources/vm/java/service.java.vm b/bashi-generator/src/main/resources/vm/java/service.java.vm new file mode 100644 index 0000000..279e1c2 --- /dev/null +++ b/bashi-generator/src/main/resources/vm/java/service.java.vm @@ -0,0 +1,13 @@ +package ${packageName}.service; + +import ${packageName}.domain.${ClassName}; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * ${functionName}Service接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface I${ClassName}Service extends IService<${ClassName}> { +} diff --git a/bashi-generator/src/main/resources/vm/java/serviceImpl.java.vm b/bashi-generator/src/main/resources/vm/java/serviceImpl.java.vm new file mode 100644 index 0000000..bc65b06 --- /dev/null +++ b/bashi-generator/src/main/resources/vm/java/serviceImpl.java.vm @@ -0,0 +1,18 @@ +package ${packageName}.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import ${packageName}.domain.${ClassName}; +import ${packageName}.mapper.${ClassName}Mapper; +import ${packageName}.service.I${ClassName}Service; +import org.springframework.stereotype.Service; + +/** + * ${functionName}Service业务层处理 + * + * @author ${author} + * @date ${datetime} + */ +@Service +public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service { + +} diff --git a/bashi-generator/src/main/resources/vm/java/sub-domain.java.vm b/bashi-generator/src/main/resources/vm/java/sub-domain.java.vm new file mode 100644 index 0000000..c431f92 --- /dev/null +++ b/bashi-generator/src/main/resources/vm/java/sub-domain.java.vm @@ -0,0 +1,73 @@ +package ${packageName}.domain; + +#foreach ($import in $subImportList) +import ${import}; +#end +import com.bashi.common.annotation.Excel; + +/** + * ${subTable.functionName}对象 ${subTableName} + * + * @author ${author} + * @date ${datetime} + */ +public class ${subClassName} extends BaseEntity +{ + private static final long serialVersionUID = 1L; + +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end +#end + private $column.javaType $column.javaField; + +#end +#end +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + public void set${AttrName}($column.javaType $column.javaField) + { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() + { + return $column.javaField; + } +#end +#end + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) +#foreach ($column in $subTable.columns) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + .append("${column.javaField}", get${AttrName}()) +#end + .toString(); + } +} diff --git a/bashi-generator/src/main/resources/vm/java/vo.java.vm b/bashi-generator/src/main/resources/vm/java/vo.java.vm new file mode 100644 index 0000000..0be4edf --- /dev/null +++ b/bashi-generator/src/main/resources/vm/java/vo.java.vm @@ -0,0 +1,50 @@ +package ${packageName}.vo; + +import com.bashi.common.annotation.Excel; +#foreach ($import in $importList) +import ${import}; +#end +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + + +/** + * ${functionName}视图对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +@Data +@ApiModel("${functionName}视图对象") +public class ${ClassName}Vo { + + private static final long serialVersionUID = 1L; + + /** $pkColumn.columnComment */ + @ApiModelProperty("$pkColumn.columnComment") + private ${pkColumn.javaType} ${pkColumn.javaField}; + +#foreach ($column in $columns) +#if($column.isList && $column.isPk!=1) + /** $column.columnComment */ +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}" , readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @Excel(name = "${comment}" , width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end + @ApiModelProperty("$column.columnComment") + private $column.javaType $column.javaField; + +#end +#end + +} diff --git a/bashi-generator/src/main/resources/vm/js/api.js.vm b/bashi-generator/src/main/resources/vm/js/api.js.vm new file mode 100644 index 0000000..296d41a --- /dev/null +++ b/bashi-generator/src/main/resources/vm/js/api.js.vm @@ -0,0 +1,53 @@ +import request from '@/utils/request' + +// 查询${functionName}列表 +export function list${BusinessName}(query) { + return request({ + url: '/${moduleName}/${businessName}/list', + method: 'get', + params: query + }) +} + +// 查询${functionName}详细 +export function get${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'get' + }) +} + +// 新增${functionName} +export function add${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'post', + data: data + }) +} + +// 修改${functionName} +export function update${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'put', + data: data + }) +} + +// 删除${functionName} +export function del${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'delete' + }) +} + +// 导出${functionName} +export function export${BusinessName}(query) { + return request({ + url: '/${moduleName}/${businessName}/export', + method: 'get', + params: query + }) +} \ No newline at end of file diff --git a/bashi-generator/src/main/resources/vm/sql/sql.vm b/bashi-generator/src/main/resources/vm/sql/sql.vm new file mode 100644 index 0000000..0575583 --- /dev/null +++ b/bashi-generator/src/main/resources/vm/sql/sql.vm @@ -0,0 +1,22 @@ +-- 菜单 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate(), '', null, '${functionName}菜单'); + +-- 按钮父菜单ID +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}查询', @parentId, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}新增', @parentId, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}修改', @parentId, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}删除', @parentId, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}导出', @parentId, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', sysdate(), '', null, ''); \ No newline at end of file diff --git a/bashi-generator/src/main/resources/vm/vue/index-tree.vue.vm b/bashi-generator/src/main/resources/vm/vue/index-tree.vue.vm new file mode 100644 index 0000000..d43c2ce --- /dev/null +++ b/bashi-generator/src/main/resources/vm/vue/index-tree.vue.vm @@ -0,0 +1,546 @@ + + + diff --git a/bashi-generator/src/main/resources/vm/vue/index.vue.vm b/bashi-generator/src/main/resources/vm/vue/index.vue.vm new file mode 100644 index 0000000..abc799e --- /dev/null +++ b/bashi-generator/src/main/resources/vm/vue/index.vue.vm @@ -0,0 +1,653 @@ + + + diff --git a/bashi-generator/src/main/resources/vm/xml/mapper.xml.vm b/bashi-generator/src/main/resources/vm/xml/mapper.xml.vm new file mode 100644 index 0000000..d053009 --- /dev/null +++ b/bashi-generator/src/main/resources/vm/xml/mapper.xml.vm @@ -0,0 +1,14 @@ + + + + + +#foreach ($column in $columns) + +#end + + + + \ No newline at end of file diff --git a/bashi-quartz/pom.xml b/bashi-quartz/pom.xml new file mode 100644 index 0000000..5c1c8f9 --- /dev/null +++ b/bashi-quartz/pom.xml @@ -0,0 +1,40 @@ + + + + bashi + com.bashi + 2.4.0 + + 4.0.0 + + bashi-quartz + + + quartz定时任务 + + + + + + + org.springframework.boot + spring-boot-starter-quartz + + + com.mchange + c3p0 + + + + + + + com.bashi + bashi-common + + + + + diff --git a/bashi-quartz/src/main/java/com/bashi/quartz/config/ScheduleConfig.java b/bashi-quartz/src/main/java/com/bashi/quartz/config/ScheduleConfig.java new file mode 100644 index 0000000..2311662 --- /dev/null +++ b/bashi-quartz/src/main/java/com/bashi/quartz/config/ScheduleConfig.java @@ -0,0 +1,13 @@ +package com.bashi.quartz.config; + +import org.springframework.context.annotation.Configuration; + +/** + * 定时任务配置 + * + * @author Lion Li + */ +@Configuration +public class ScheduleConfig { + +} diff --git a/bashi-quartz/src/main/java/com/bashi/quartz/controller/SysJobController.java b/bashi-quartz/src/main/java/com/bashi/quartz/controller/SysJobController.java new file mode 100644 index 0000000..a12f0d0 --- /dev/null +++ b/bashi-quartz/src/main/java/com/bashi/quartz/controller/SysJobController.java @@ -0,0 +1,144 @@ +package com.bashi.quartz.controller; + +import cn.hutool.core.util.StrUtil; +import com.bashi.common.annotation.Log; +import com.bashi.common.constant.Constants; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.enums.BusinessType; +import com.bashi.common.exception.job.TaskException; +import com.bashi.common.utils.SecurityUtils; +import com.bashi.common.utils.poi.ExcelUtil; +import com.bashi.quartz.domain.SysJob; +import com.bashi.quartz.service.ISysJobService; +import com.bashi.quartz.util.CronUtils; +import org.quartz.SchedulerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 调度任务信息操作处理 + * + * @author duteliang + */ +@RestController +@RequestMapping("/monitor/job") +public class SysJobController extends BaseController +{ + @Autowired + private ISysJobService jobService; + + /** + * 查询定时任务列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:list')") + @GetMapping("/list") + public TableDataInfo list(SysJob sysJob) + { + return jobService.selectPageJobList(sysJob); + } + + /** + * 导出定时任务列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:export')") + @Log(title = "定时任务", businessType = BusinessType.EXPORT) + @GetMapping("/export") + public AjaxResult export(SysJob sysJob) + { + List list = jobService.selectJobList(sysJob); + ExcelUtil util = new ExcelUtil(SysJob.class); + return util.exportExcel(list, "定时任务"); + } + + /** + * 获取定时任务详细信息 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:query')") + @GetMapping(value = "/{jobId}") + public AjaxResult getInfo(@PathVariable("jobId") Long jobId) + { + return AjaxResult.success(jobService.selectJobById(jobId)); + } + + /** + * 新增定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:add')") + @Log(title = "定时任务", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysJob sysJob) throws SchedulerException, TaskException + { + if (!CronUtils.isValid(sysJob.getCronExpression())) + { + return AjaxResult.error("新增任务'" + sysJob.getJobName() + "'失败,Cron表达式不正确"); + } + else if (StrUtil.containsIgnoreCase(sysJob.getInvokeTarget(), Constants.LOOKUP_RMI)) + { + return AjaxResult.error("新增任务'" + sysJob.getJobName() + "'失败,目标字符串不允许'rmi://'调用"); + } + sysJob.setCreateBy(SecurityUtils.getUsername()); + return toAjax(jobService.insertJob(sysJob)); + } + + /** + * 修改定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:edit')") + @Log(title = "定时任务", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysJob sysJob) throws SchedulerException, TaskException + { + if (!CronUtils.isValid(sysJob.getCronExpression())) + { + return AjaxResult.error("修改任务'" + sysJob.getJobName() + "'失败,Cron表达式不正确"); + } + else if (StrUtil.containsIgnoreCase(sysJob.getInvokeTarget(), Constants.LOOKUP_RMI)) + { + return AjaxResult.error("修改任务'" + sysJob.getJobName() + "'失败,目标字符串不允许'rmi://'调用"); + } + sysJob.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(jobService.updateJob(sysJob)); + } + + /** + * 定时任务状态修改 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')") + @Log(title = "定时任务", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException + { + SysJob newJob = jobService.selectJobById(job.getJobId()); + newJob.setStatus(job.getStatus()); + return toAjax(jobService.changeStatus(newJob)); + } + + /** + * 定时任务立即执行一次 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')") + @Log(title = "定时任务", businessType = BusinessType.UPDATE) + @PutMapping("/run") + public AjaxResult run(@RequestBody SysJob job) throws SchedulerException + { + jobService.run(job); + return AjaxResult.success(); + } + + /** + * 删除定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:remove')") + @Log(title = "定时任务", businessType = BusinessType.DELETE) + @DeleteMapping("/{jobIds}") + public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException + { + jobService.deleteJobByIds(jobIds); + return AjaxResult.success(); + } +} diff --git a/bashi-quartz/src/main/java/com/bashi/quartz/controller/SysJobLogController.java b/bashi-quartz/src/main/java/com/bashi/quartz/controller/SysJobLogController.java new file mode 100644 index 0000000..c1916ae --- /dev/null +++ b/bashi-quartz/src/main/java/com/bashi/quartz/controller/SysJobLogController.java @@ -0,0 +1,85 @@ +package com.bashi.quartz.controller; + +import com.bashi.common.annotation.Log; +import com.bashi.common.core.controller.BaseController; +import com.bashi.common.core.domain.AjaxResult; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.enums.BusinessType; +import com.bashi.common.utils.poi.ExcelUtil; +import com.bashi.quartz.domain.SysJobLog; +import com.bashi.quartz.service.ISysJobLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 调度日志操作处理 + * + * @author duteliang + */ +@RestController +@RequestMapping("/monitor/jobLog") +public class SysJobLogController extends BaseController +{ + @Autowired + private ISysJobLogService jobLogService; + + /** + * 查询定时任务调度日志列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:list')") + @GetMapping("/list") + public TableDataInfo list(SysJobLog sysJobLog) + { + return jobLogService.selectPageJobLogList(sysJobLog); + } + + /** + * 导出定时任务调度日志列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:export')") + @Log(title = "任务调度日志", businessType = BusinessType.EXPORT) + @GetMapping("/export") + public AjaxResult export(SysJobLog sysJobLog) + { + List list = jobLogService.selectJobLogList(sysJobLog); + ExcelUtil util = new ExcelUtil(SysJobLog.class); + return util.exportExcel(list, "调度日志"); + } + + /** + * 根据调度编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:query')") + @GetMapping(value = "/{configId}") + public AjaxResult getInfo(@PathVariable Long jobLogId) + { + return AjaxResult.success(jobLogService.selectJobLogById(jobLogId)); + } + + + /** + * 删除定时任务调度日志 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:remove')") + @Log(title = "定时任务调度日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{jobLogIds}") + public AjaxResult remove(@PathVariable Long[] jobLogIds) + { + return toAjax(jobLogService.deleteJobLogByIds(jobLogIds)); + } + + /** + * 清空定时任务调度日志 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:remove')") + @Log(title = "调度日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public AjaxResult clean() + { + jobLogService.cleanJobLog(); + return AjaxResult.success(); + } +} diff --git a/bashi-quartz/src/main/java/com/bashi/quartz/domain/SysJob.java b/bashi-quartz/src/main/java/com/bashi/quartz/domain/SysJob.java new file mode 100644 index 0000000..5b6b9be --- /dev/null +++ b/bashi-quartz/src/main/java/com/bashi/quartz/domain/SysJob.java @@ -0,0 +1,134 @@ +package com.bashi.quartz.domain; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.bashi.common.annotation.Excel; +import com.bashi.common.annotation.Excel.ColumnType; +import com.bashi.common.constant.ScheduleConstants; +import com.bashi.quartz.util.CronUtils; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 定时任务调度表 sys_job + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("sys_job") +public class SysJob implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 任务ID + */ + @Excel(name = "任务序号", cellType = ColumnType.NUMERIC) + @TableId(value = "job_id", type = IdType.AUTO) + private Long jobId; + + /** + * 任务名称 + */ + @NotBlank(message = "任务名称不能为空") + @Size(min = 0, max = 64, message = "任务名称不能超过64个字符") + @Excel(name = "任务名称") + private String jobName; + + /** + * 任务组名 + */ + @Excel(name = "任务组名") + private String jobGroup; + + /** + * 调用目标字符串 + */ + @NotBlank(message = "调用目标字符串不能为空") + @Size(min = 0, max = 500, message = "调用目标字符串长度不能超过500个字符") + @Excel(name = "调用目标字符串") + private String invokeTarget; + + /** + * cron执行表达式 + */ + @NotBlank(message = "Cron执行表达式不能为空") + @Size(min = 0, max = 255, message = "Cron执行表达式不能超过255个字符") + @Excel(name = "执行表达式 ") + private String cronExpression; + + /** + * cron计划策略 + */ + @Excel(name = "计划策略 ", readConverterExp = "0=默认,1=立即触发执行,2=触发一次执行,3=不触发立即执行") + private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT; + + /** + * 是否并发执行(0允许 1禁止) + */ + @Excel(name = "并发执行", readConverterExp = "0=允许,1=禁止") + private String concurrent; + + /** + * 任务状态(0正常 1暂停) + */ + @Excel(name = "任务状态", readConverterExp = "0=正常,1=暂停") + private String status; + + /** + * 创建者 + */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** + * 备注 + */ + private String remark; + + /** + * 请求参数 + */ + @TableField(exist = false) + private Map params = new HashMap<>(); + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + public Date getNextValidTime() { + if (StrUtil.isNotEmpty(cronExpression)) { + return CronUtils.getNextExecution(cronExpression); + } + return null; + } + +} diff --git a/bashi-quartz/src/main/java/com/bashi/quartz/domain/SysJobLog.java b/bashi-quartz/src/main/java/com/bashi/quartz/domain/SysJobLog.java new file mode 100644 index 0000000..e523b70 --- /dev/null +++ b/bashi-quartz/src/main/java/com/bashi/quartz/domain/SysJobLog.java @@ -0,0 +1,78 @@ +package com.bashi.quartz.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.bashi.common.annotation.Excel; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 定时任务调度日志表 sys_job_log + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("sys_job_log") +public class SysJobLog +{ + private static final long serialVersionUID = 1L; + + /** ID */ + @Excel(name = "日志序号") + @TableId(value = "job_log_id", type = IdType.AUTO) + private Long jobLogId; + + /** 任务名称 */ + @Excel(name = "任务名称") + private String jobName; + + /** 任务组名 */ + @Excel(name = "任务组名") + private String jobGroup; + + /** 调用目标字符串 */ + @Excel(name = "调用目标字符串") + private String invokeTarget; + + /** 日志信息 */ + @Excel(name = "日志信息") + private String jobMessage; + + /** 执行状态(0正常 1失败) */ + @Excel(name = "执行状态", readConverterExp = "0=正常,1=失败") + private String status; + + /** 异常信息 */ + @Excel(name = "异常信息") + private String exceptionInfo; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 请求参数 + */ + @TableField(exist = false) + private Map params = new HashMap<>(); + + /** 开始时间 */ + @TableField(exist = false) + private Date startTime; + + /** 停止时间 */ + @TableField(exist = false) + private Date stopTime; + +} diff --git a/bashi-quartz/src/main/java/com/bashi/quartz/mapper/SysJobLogMapper.java b/bashi-quartz/src/main/java/com/bashi/quartz/mapper/SysJobLogMapper.java new file mode 100644 index 0000000..b1772f7 --- /dev/null +++ b/bashi-quartz/src/main/java/com/bashi/quartz/mapper/SysJobLogMapper.java @@ -0,0 +1,13 @@ +package com.bashi.quartz.mapper; + +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; +import com.bashi.quartz.domain.SysJobLog; + +/** + * 调度任务日志信息 数据层 + * + * @author duteliang + */ +public interface SysJobLogMapper extends BaseMapperPlus { + +} diff --git a/bashi-quartz/src/main/java/com/bashi/quartz/mapper/SysJobMapper.java b/bashi-quartz/src/main/java/com/bashi/quartz/mapper/SysJobMapper.java new file mode 100644 index 0000000..8a78eea --- /dev/null +++ b/bashi-quartz/src/main/java/com/bashi/quartz/mapper/SysJobMapper.java @@ -0,0 +1,13 @@ +package com.bashi.quartz.mapper; + +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; +import com.bashi.quartz.domain.SysJob; + +/** + * 调度任务信息 数据层 + * + * @author duteliang + */ +public interface SysJobMapper extends BaseMapperPlus { + +} diff --git a/bashi-quartz/src/main/java/com/bashi/quartz/service/ISysJobLogService.java b/bashi-quartz/src/main/java/com/bashi/quartz/service/ISysJobLogService.java new file mode 100644 index 0000000..aed8ca7 --- /dev/null +++ b/bashi-quartz/src/main/java/com/bashi/quartz/service/ISysJobLogService.java @@ -0,0 +1,62 @@ +package com.bashi.quartz.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.quartz.domain.SysJobLog; + +import java.util.List; + +/** + * 定时任务调度日志信息信息 服务层 + * + * @author duteliang + */ +public interface ISysJobLogService extends IService { + + + TableDataInfo selectPageJobLogList(SysJobLog jobLog); + + /** + * 获取quartz调度器日志的计划任务 + * + * @param jobLog 调度日志信息 + * @return 调度任务日志集合 + */ + public List selectJobLogList(SysJobLog jobLog); + + /** + * 通过调度任务日志ID查询调度信息 + * + * @param jobLogId 调度任务日志ID + * @return 调度任务日志对象信息 + */ + public SysJobLog selectJobLogById(Long jobLogId); + + /** + * 新增任务日志 + * + * @param jobLog 调度日志信息 + */ + public void addJobLog(SysJobLog jobLog); + + /** + * 批量删除调度日志信息 + * + * @param logIds 需要删除的日志ID + * @return 结果 + */ + public int deleteJobLogByIds(Long[] logIds); + + /** + * 删除任务日志 + * + * @param jobId 调度日志ID + * @return 结果 + */ + public int deleteJobLogById(Long jobId); + + /** + * 清空任务日志 + */ + public void cleanJobLog(); +} diff --git a/bashi-quartz/src/main/java/com/bashi/quartz/service/ISysJobService.java b/bashi-quartz/src/main/java/com/bashi/quartz/service/ISysJobService.java new file mode 100644 index 0000000..8f47f12 --- /dev/null +++ b/bashi-quartz/src/main/java/com/bashi/quartz/service/ISysJobService.java @@ -0,0 +1,106 @@ +package com.bashi.quartz.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.exception.job.TaskException; +import com.bashi.quartz.domain.SysJob; +import org.quartz.SchedulerException; + +import java.util.List; + +/** + * 定时任务调度信息信息 服务层 + * + * @author duteliang + */ +public interface ISysJobService extends IService { + /** + * 获取quartz调度器的计划任务 + * + * @param job 调度信息 + * @return 调度任务集合 + */ + public TableDataInfo selectPageJobList(SysJob job); + + public List selectJobList(SysJob job); + + /** + * 通过调度任务ID查询调度信息 + * + * @param jobId 调度任务ID + * @return 调度任务对象信息 + */ + public SysJob selectJobById(Long jobId); + + /** + * 暂停任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int pauseJob(SysJob job) throws SchedulerException; + + /** + * 恢复任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int resumeJob(SysJob job) throws SchedulerException; + + /** + * 删除任务后,所对应的trigger也将被删除 + * + * @param job 调度信息 + * @return 结果 + */ + public int deleteJob(SysJob job) throws SchedulerException; + + /** + * 批量删除调度信息 + * + * @param jobIds 需要删除的任务ID + * @return 结果 + */ + public void deleteJobByIds(Long[] jobIds) throws SchedulerException; + + /** + * 任务调度状态修改 + * + * @param job 调度信息 + * @return 结果 + */ + public int changeStatus(SysJob job) throws SchedulerException; + + /** + * 立即运行任务 + * + * @param job 调度信息 + * @return 结果 + */ + public void run(SysJob job) throws SchedulerException; + + /** + * 新增任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int insertJob(SysJob job) throws SchedulerException, TaskException; + + /** + * 更新任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int updateJob(SysJob job) throws SchedulerException, TaskException; + + /** + * 校验cron表达式是否有效 + * + * @param cronExpression 表达式 + * @return 结果 + */ + public boolean checkCronExpressionIsValid(String cronExpression); +} diff --git a/bashi-quartz/src/main/java/com/bashi/quartz/service/impl/SysJobLogServiceImpl.java b/bashi-quartz/src/main/java/com/bashi/quartz/service/impl/SysJobLogServiceImpl.java new file mode 100644 index 0000000..c33b3ce --- /dev/null +++ b/bashi-quartz/src/main/java/com/bashi/quartz/service/impl/SysJobLogServiceImpl.java @@ -0,0 +1,114 @@ +package com.bashi.quartz.service.impl; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.utils.PageUtils; +import com.bashi.quartz.domain.SysJobLog; +import com.bashi.quartz.mapper.SysJobLogMapper; +import com.bashi.quartz.service.ISysJobLogService; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * 定时任务调度日志信息 服务层 + * + * @author duteliang + */ +@Service +public class SysJobLogServiceImpl extends ServiceImpl implements ISysJobLogService { + + @Override + public TableDataInfo selectPageJobLogList(SysJobLog jobLog) { + Map params = jobLog.getParams(); + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .like(StrUtil.isNotBlank(jobLog.getJobName()), SysJobLog::getJobName, jobLog.getJobName()) + .eq(StrUtil.isNotBlank(jobLog.getJobGroup()), SysJobLog::getJobGroup, jobLog.getJobGroup()) + .eq(StrUtil.isNotBlank(jobLog.getStatus()), SysJobLog::getStatus, jobLog.getStatus()) + .like(StrUtil.isNotBlank(jobLog.getInvokeTarget()), SysJobLog::getInvokeTarget, jobLog.getInvokeTarget()) + .apply(Validator.isNotEmpty(params.get("beginTime")), + "date_format(create_time,'%y%m%d') >= date_format({0},'%y%m%d')", + params.get("beginTime")) + .apply(Validator.isNotEmpty(params.get("endTime")), + "date_format(create_time,'%y%m%d') <= date_format({0},'%y%m%d')", + params.get("endTime")); + return PageUtils.buildDataInfo(page(PageUtils.buildPage(), lqw)); + } + + /** + * 获取quartz调度器日志的计划任务 + * + * @param jobLog 调度日志信息 + * @return 调度任务日志集合 + */ + @Override + public List selectJobLogList(SysJobLog jobLog) { + Map params = jobLog.getParams(); + return list(new LambdaQueryWrapper() + .like(StrUtil.isNotBlank(jobLog.getJobName()), SysJobLog::getJobName, jobLog.getJobName()) + .eq(StrUtil.isNotBlank(jobLog.getJobGroup()), SysJobLog::getJobGroup, jobLog.getJobGroup()) + .eq(StrUtil.isNotBlank(jobLog.getStatus()), SysJobLog::getStatus, jobLog.getStatus()) + .like(StrUtil.isNotBlank(jobLog.getInvokeTarget()), SysJobLog::getInvokeTarget, jobLog.getInvokeTarget()) + .apply(Validator.isNotEmpty(params.get("beginTime")), + "date_format(create_time,'%y%m%d') >= date_format({0},'%y%m%d')", + params.get("beginTime")) + .apply(Validator.isNotEmpty(params.get("endTime")), + "date_format(create_time,'%y%m%d') <= date_format({0},'%y%m%d')", + params.get("endTime"))); + } + + /** + * 通过调度任务日志ID查询调度信息 + * + * @param jobLogId 调度任务日志ID + * @return 调度任务日志对象信息 + */ + @Override + public SysJobLog selectJobLogById(Long jobLogId) { + return getById(jobLogId); + } + + /** + * 新增任务日志 + * + * @param jobLog 调度日志信息 + */ + @Override + public void addJobLog(SysJobLog jobLog) { + baseMapper.insert(jobLog); + } + + /** + * 批量删除调度日志信息 + * + * @param logIds 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteJobLogByIds(Long[] logIds) { + return baseMapper.deleteBatchIds(Arrays.asList(logIds)); + } + + /** + * 删除任务日志 + * + * @param jobId 调度日志ID + */ + @Override + public int deleteJobLogById(Long jobId) { + return baseMapper.deleteById(jobId); + } + + /** + * 清空任务日志 + */ + @Override + public void cleanJobLog() { + remove(new LambdaQueryWrapper<>()); + } +} diff --git a/bashi-quartz/src/main/java/com/bashi/quartz/service/impl/SysJobServiceImpl.java b/bashi-quartz/src/main/java/com/bashi/quartz/service/impl/SysJobServiceImpl.java new file mode 100644 index 0000000..5d3e481 --- /dev/null +++ b/bashi-quartz/src/main/java/com/bashi/quartz/service/impl/SysJobServiceImpl.java @@ -0,0 +1,246 @@ +package com.bashi.quartz.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.common.constant.ScheduleConstants; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.exception.job.TaskException; +import com.bashi.common.utils.PageUtils; +import com.bashi.quartz.domain.SysJob; +import com.bashi.quartz.mapper.SysJobMapper; +import com.bashi.quartz.service.ISysJobService; +import com.bashi.quartz.util.CronUtils; +import com.bashi.quartz.util.ScheduleUtils; +import org.quartz.JobDataMap; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.PostConstruct; +import java.util.List; + +/** + * 定时任务调度信息 服务层 + * + * @author duteliang + */ +@Service +public class SysJobServiceImpl extends ServiceImpl implements ISysJobService { + @Autowired + private Scheduler scheduler; + + /** + * 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据) + */ + @PostConstruct + public void init() throws SchedulerException, TaskException { + scheduler.clear(); + List jobList = list(); + for (SysJob job : jobList) { + ScheduleUtils.createScheduleJob(scheduler, job); + } + } + + @Override + public TableDataInfo selectPageJobList(SysJob job) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .like(StrUtil.isNotBlank(job.getJobName()), SysJob::getJobName, job.getJobName()) + .eq(StrUtil.isNotBlank(job.getJobGroup()), SysJob::getJobGroup, job.getJobGroup()) + .eq(StrUtil.isNotBlank(job.getStatus()), SysJob::getStatus, job.getStatus()) + .like(StrUtil.isNotBlank(job.getInvokeTarget()), SysJob::getInvokeTarget, job.getInvokeTarget()); + return PageUtils.buildDataInfo(page(PageUtils.buildPage(), lqw)); + } + + /** + * 获取quartz调度器的计划任务列表 + * + * @param job 调度信息 + * @return + */ + @Override + public List selectJobList(SysJob job) { + return list(new LambdaQueryWrapper() + .like(StrUtil.isNotBlank(job.getJobName()), SysJob::getJobName, job.getJobName()) + .eq(StrUtil.isNotBlank(job.getJobGroup()), SysJob::getJobGroup, job.getJobGroup()) + .eq(StrUtil.isNotBlank(job.getStatus()), SysJob::getStatus, job.getStatus()) + .like(StrUtil.isNotBlank(job.getInvokeTarget()), SysJob::getInvokeTarget, job.getInvokeTarget())); + } + + /** + * 通过调度任务ID查询调度信息 + * + * @param jobId 调度任务ID + * @return 调度任务对象信息 + */ + @Override + public SysJob selectJobById(Long jobId) { + return getById(jobId); + } + + /** + * 暂停任务 + * + * @param job 调度信息 + */ + @Override + @Transactional + public int pauseJob(SysJob job) throws SchedulerException { + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + job.setStatus(ScheduleConstants.Status.PAUSE.getValue()); + int rows = baseMapper.updateById(job); + if (rows > 0) { + scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + return rows; + } + + /** + * 恢复任务 + * + * @param job 调度信息 + */ + @Override + @Transactional + public int resumeJob(SysJob job) throws SchedulerException { + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + job.setStatus(ScheduleConstants.Status.NORMAL.getValue()); + int rows = baseMapper.updateById(job); + if (rows > 0) { + scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + return rows; + } + + /** + * 删除任务后,所对应的trigger也将被删除 + * + * @param job 调度信息 + */ + @Override + @Transactional + public int deleteJob(SysJob job) throws SchedulerException { + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + int rows = baseMapper.deleteById(jobId); + if (rows > 0) { + scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + return rows; + } + + /** + * 批量删除调度信息 + * + * @param jobIds 需要删除的任务ID + * @return 结果 + */ + @Override + @Transactional + public void deleteJobByIds(Long[] jobIds) throws SchedulerException { + for (Long jobId : jobIds) { + SysJob job = getById(jobId); + deleteJob(job); + } + } + + /** + * 任务调度状态修改 + * + * @param job 调度信息 + */ + @Override + @Transactional + public int changeStatus(SysJob job) throws SchedulerException { + int rows = 0; + String status = job.getStatus(); + if (ScheduleConstants.Status.NORMAL.getValue().equals(status)) { + rows = resumeJob(job); + } else if (ScheduleConstants.Status.PAUSE.getValue().equals(status)) { + rows = pauseJob(job); + } + return rows; + } + + /** + * 立即运行任务 + * + * @param job 调度信息 + */ + @Override + @Transactional + public void run(SysJob job) throws SchedulerException { + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + SysJob properties = selectJobById(job.getJobId()); + // 参数 + JobDataMap dataMap = new JobDataMap(); + dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties); + scheduler.triggerJob(ScheduleUtils.getJobKey(jobId, jobGroup), dataMap); + } + + /** + * 新增任务 + * + * @param job 调度信息 调度信息 + */ + @Override + @Transactional + public int insertJob(SysJob job) throws SchedulerException, TaskException { + job.setStatus(ScheduleConstants.Status.PAUSE.getValue()); + int rows = baseMapper.insert(job); + if (rows > 0) { + ScheduleUtils.createScheduleJob(scheduler, job); + } + return rows; + } + + /** + * 更新任务的时间表达式 + * + * @param job 调度信息 + */ + @Override + @Transactional + public int updateJob(SysJob job) throws SchedulerException, TaskException { + SysJob properties = selectJobById(job.getJobId()); + int rows = baseMapper.updateById(job); + if (rows > 0) { + updateSchedulerJob(job, properties.getJobGroup()); + } + return rows; + } + + /** + * 更新任务 + * + * @param job 任务对象 + * @param jobGroup 任务组名 + */ + public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException { + Long jobId = job.getJobId(); + // 判断是否存在 + JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup); + if (scheduler.checkExists(jobKey)) { + // 防止创建时存在数据问题 先移除,然后在执行创建操作 + scheduler.deleteJob(jobKey); + } + ScheduleUtils.createScheduleJob(scheduler, job); + } + + /** + * 校验cron表达式是否有效 + * + * @param cronExpression 表达式 + * @return 结果 + */ + @Override + public boolean checkCronExpressionIsValid(String cronExpression) { + return CronUtils.isValid(cronExpression); + } +} diff --git a/bashi-quartz/src/main/java/com/bashi/quartz/task/RyTask.java b/bashi-quartz/src/main/java/com/bashi/quartz/task/RyTask.java new file mode 100644 index 0000000..48243fb --- /dev/null +++ b/bashi-quartz/src/main/java/com/bashi/quartz/task/RyTask.java @@ -0,0 +1,29 @@ +package com.bashi.quartz.task; + +import cn.hutool.core.lang.Console; +import cn.hutool.core.util.StrUtil; +import org.springframework.stereotype.Component; + +/** + * 定时任务调度测试 + * + * @author duteliang + */ +@Component("ryTask") +public class RyTask +{ + public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i) + { + Console.log(StrUtil.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i)); + } + + public void ryParams(String params) + { + Console.log("执行有参方法:" + params); + } + + public void ryNoParams() + { + Console.log("执行无参方法"); + } +} diff --git a/bashi-quartz/src/main/java/com/bashi/quartz/util/AbstractQuartzJob.java b/bashi-quartz/src/main/java/com/bashi/quartz/util/AbstractQuartzJob.java new file mode 100644 index 0000000..e204432 --- /dev/null +++ b/bashi-quartz/src/main/java/com/bashi/quartz/util/AbstractQuartzJob.java @@ -0,0 +1,109 @@ +package com.bashi.quartz.util; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.exceptions.ExceptionUtil; +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import com.bashi.common.constant.Constants; +import com.bashi.common.constant.ScheduleConstants; +import com.bashi.common.utils.spring.SpringUtils; +import com.bashi.quartz.domain.SysJob; +import com.bashi.quartz.domain.SysJobLog; +import com.bashi.quartz.service.ISysJobLogService; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Date; + +/** + * 抽象quartz调用 + * + * @author duteliang + */ +public abstract class AbstractQuartzJob implements Job +{ + private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class); + + /** + * 线程本地变量 + */ + private static ThreadLocal threadLocal = new ThreadLocal<>(); + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException + { + SysJob sysJob = new SysJob(); + BeanUtil.copyProperties(context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES),sysJob); + try + { + before(context, sysJob); + if (Validator.isNotNull(sysJob)) + { + doExecute(context, sysJob); + } + after(context, sysJob, null); + } + catch (Exception e) + { + log.error("任务执行异常 - :", e); + after(context, sysJob, e); + } + } + + /** + * 执行前 + * + * @param context 工作执行上下文对象 + * @param sysJob 系统计划任务 + */ + protected void before(JobExecutionContext context, SysJob sysJob) + { + threadLocal.set(new Date()); + } + + /** + * 执行后 + * + * @param context 工作执行上下文对象 + * @param sysJob 系统计划任务 + */ + protected void after(JobExecutionContext context, SysJob sysJob, Exception e) + { + Date startTime = threadLocal.get(); + threadLocal.remove(); + + final SysJobLog sysJobLog = new SysJobLog(); + sysJobLog.setJobName(sysJob.getJobName()); + sysJobLog.setJobGroup(sysJob.getJobGroup()); + sysJobLog.setInvokeTarget(sysJob.getInvokeTarget()); + sysJobLog.setStartTime(startTime); + sysJobLog.setStopTime(new Date()); + long runMs = sysJobLog.getStopTime().getTime() - sysJobLog.getStartTime().getTime(); + sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时:" + runMs + "毫秒"); + if (e != null) + { + sysJobLog.setStatus(Constants.FAIL); + String errorMsg = StrUtil.sub(ExceptionUtil.stacktraceToString(e), 0, 2000); + sysJobLog.setExceptionInfo(errorMsg); + } + else + { + sysJobLog.setStatus(Constants.SUCCESS); + } + + // 写入数据库当中 + SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog); + } + + /** + * 执行方法,由子类重载 + * + * @param context 工作执行上下文对象 + * @param sysJob 系统计划任务 + * @throws Exception 执行过程中的异常 + */ + protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception; +} diff --git a/bashi-quartz/src/main/java/com/bashi/quartz/util/CronUtils.java b/bashi-quartz/src/main/java/com/bashi/quartz/util/CronUtils.java new file mode 100644 index 0000000..48ea060 --- /dev/null +++ b/bashi-quartz/src/main/java/com/bashi/quartz/util/CronUtils.java @@ -0,0 +1,63 @@ +package com.bashi.quartz.util; + +import java.text.ParseException; +import java.util.Date; +import org.quartz.CronExpression; + +/** + * cron表达式工具类 + * + * @author duteliang + * + */ +public class CronUtils +{ + /** + * 返回一个布尔值代表一个给定的Cron表达式的有效性 + * + * @param cronExpression Cron表达式 + * @return boolean 表达式是否有效 + */ + public static boolean isValid(String cronExpression) + { + return CronExpression.isValidExpression(cronExpression); + } + + /** + * 返回一个字符串值,表示该消息无效Cron表达式给出有效性 + * + * @param cronExpression Cron表达式 + * @return String 无效时返回表达式错误描述,如果有效返回null + */ + public static String getInvalidMessage(String cronExpression) + { + try + { + new CronExpression(cronExpression); + return null; + } + catch (ParseException pe) + { + return pe.getMessage(); + } + } + + /** + * 返回下一个执行时间根据给定的Cron表达式 + * + * @param cronExpression Cron表达式 + * @return Date 下次Cron表达式执行时间 + */ + public static Date getNextExecution(String cronExpression) + { + try + { + CronExpression cron = new CronExpression(cronExpression); + return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis())); + } + catch (ParseException e) + { + throw new IllegalArgumentException(e.getMessage()); + } + } +} diff --git a/bashi-quartz/src/main/java/com/bashi/quartz/util/JobInvokeUtil.java b/bashi-quartz/src/main/java/com/bashi/quartz/util/JobInvokeUtil.java new file mode 100644 index 0000000..8d64591 --- /dev/null +++ b/bashi-quartz/src/main/java/com/bashi/quartz/util/JobInvokeUtil.java @@ -0,0 +1,184 @@ +package com.bashi.quartz.util; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import com.bashi.common.utils.spring.SpringUtils; +import com.bashi.quartz.domain.SysJob; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.LinkedList; +import java.util.List; + +/** + * 任务执行工具 + * + * @author duteliang + */ +public class JobInvokeUtil +{ + /** + * 执行方法 + * + * @param sysJob 系统任务 + */ + public static void invokeMethod(SysJob sysJob) throws Exception + { + String invokeTarget = sysJob.getInvokeTarget(); + String beanName = getBeanName(invokeTarget); + String methodName = getMethodName(invokeTarget); + List methodParams = getMethodParams(invokeTarget); + + if (!isValidClassName(beanName)) + { + Object bean = SpringUtils.getBean(beanName); + invokeMethod(bean, methodName, methodParams); + } + else + { + Object bean = Class.forName(beanName).newInstance(); + invokeMethod(bean, methodName, methodParams); + } + } + + /** + * 调用任务方法 + * + * @param bean 目标对象 + * @param methodName 方法名称 + * @param methodParams 方法参数 + */ + private static void invokeMethod(Object bean, String methodName, List methodParams) + throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, + InvocationTargetException + { + if (Validator.isNotNull(methodParams) && methodParams.size() > 0) + { + Method method = bean.getClass().getDeclaredMethod(methodName, getMethodParamsType(methodParams)); + method.invoke(bean, getMethodParamsValue(methodParams)); + } + else + { + Method method = bean.getClass().getDeclaredMethod(methodName); + method.invoke(bean); + } + } + + /** + * 校验是否为为class包名 + * + * @param str 名称 + * @return true是 false否 + */ + public static boolean isValidClassName(String invokeTarget) + { + return StrUtil.count(invokeTarget, ".") > 1; + } + + /** + * 获取bean名称 + * + * @param invokeTarget 目标字符串 + * @return bean名称 + */ + public static String getBeanName(String invokeTarget) + { + String beanName = StrUtil.subBefore(invokeTarget, "(",false); + return StrUtil.subBefore(beanName, ".",true); + } + + /** + * 获取bean方法 + * + * @param invokeTarget 目标字符串 + * @return method方法 + */ + public static String getMethodName(String invokeTarget) + { + String methodName = StrUtil.subBefore(invokeTarget, "(",false); + return StrUtil.subAfter(methodName, ".",true); + } + + /** + * 获取method方法参数相关列表 + * + * @param invokeTarget 目标字符串 + * @return method方法相关参数列表 + */ + public static List getMethodParams(String invokeTarget) + { + String methodStr = StrUtil.subBetween(invokeTarget, "(", ")"); + if (StrUtil.isEmpty(methodStr)) + { + return null; + } + String[] methodParams = methodStr.split(","); + List classs = new LinkedList<>(); + for (int i = 0; i < methodParams.length; i++) + { + String str = StrUtil.trimToEmpty(methodParams[i]); + // String字符串类型,包含' + if (StrUtil.contains(str, "'")) + { + classs.add(new Object[] { StrUtil.replace(str, "'", ""), String.class }); + } + // boolean布尔类型,等于true或者false + else if (StrUtil.equals(str, "true") || StrUtil.equalsIgnoreCase(str, "false")) + { + classs.add(new Object[] { Boolean.valueOf(str), Boolean.class }); + } + // long长整形,包含L + else if (StrUtil.containsIgnoreCase(str, "L")) + { + classs.add(new Object[] { Long.valueOf(StrUtil.replaceIgnoreCase(str, "L", "")), Long.class }); + } + // double浮点类型,包含D + else if (StrUtil.containsIgnoreCase(str, "D")) + { + classs.add(new Object[] { Double.valueOf(StrUtil.replaceIgnoreCase(str, "D", "")), Double.class }); + } + // 其他类型归类为整形 + else + { + classs.add(new Object[] { Integer.valueOf(str), Integer.class }); + } + } + return classs; + } + + /** + * 获取参数类型 + * + * @param methodParams 参数相关列表 + * @return 参数类型列表 + */ + public static Class[] getMethodParamsType(List methodParams) + { + Class[] classs = new Class[methodParams.size()]; + int index = 0; + for (Object[] os : methodParams) + { + classs[index] = (Class) os[1]; + index++; + } + return classs; + } + + /** + * 获取参数值 + * + * @param methodParams 参数相关列表 + * @return 参数值列表 + */ + public static Object[] getMethodParamsValue(List methodParams) + { + Object[] classs = new Object[methodParams.size()]; + int index = 0; + for (Object[] os : methodParams) + { + classs[index] = (Object) os[0]; + index++; + } + return classs; + } +} diff --git a/bashi-quartz/src/main/java/com/bashi/quartz/util/QuartzDisallowConcurrentExecution.java b/bashi-quartz/src/main/java/com/bashi/quartz/util/QuartzDisallowConcurrentExecution.java new file mode 100644 index 0000000..10599e8 --- /dev/null +++ b/bashi-quartz/src/main/java/com/bashi/quartz/util/QuartzDisallowConcurrentExecution.java @@ -0,0 +1,21 @@ +package com.bashi.quartz.util; + +import org.quartz.DisallowConcurrentExecution; +import org.quartz.JobExecutionContext; +import com.bashi.quartz.domain.SysJob; + +/** + * 定时任务处理(禁止并发执行) + * + * @author duteliang + * + */ +@DisallowConcurrentExecution +public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob +{ + @Override + protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception + { + JobInvokeUtil.invokeMethod(sysJob); + } +} diff --git a/bashi-quartz/src/main/java/com/bashi/quartz/util/QuartzJobExecution.java b/bashi-quartz/src/main/java/com/bashi/quartz/util/QuartzJobExecution.java new file mode 100644 index 0000000..28efa11 --- /dev/null +++ b/bashi-quartz/src/main/java/com/bashi/quartz/util/QuartzJobExecution.java @@ -0,0 +1,19 @@ +package com.bashi.quartz.util; + +import org.quartz.JobExecutionContext; +import com.bashi.quartz.domain.SysJob; + +/** + * 定时任务处理(允许并发执行) + * + * @author duteliang + * + */ +public class QuartzJobExecution extends AbstractQuartzJob +{ + @Override + protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception + { + JobInvokeUtil.invokeMethod(sysJob); + } +} diff --git a/bashi-quartz/src/main/java/com/bashi/quartz/util/ScheduleUtils.java b/bashi-quartz/src/main/java/com/bashi/quartz/util/ScheduleUtils.java new file mode 100644 index 0000000..0410607 --- /dev/null +++ b/bashi-quartz/src/main/java/com/bashi/quartz/util/ScheduleUtils.java @@ -0,0 +1,113 @@ +package com.bashi.quartz.util; + +import org.quartz.CronScheduleBuilder; +import org.quartz.CronTrigger; +import org.quartz.Job; +import org.quartz.JobBuilder; +import org.quartz.JobDetail; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.TriggerBuilder; +import org.quartz.TriggerKey; +import com.bashi.common.constant.ScheduleConstants; +import com.bashi.common.exception.job.TaskException; +import com.bashi.common.exception.job.TaskException.Code; +import com.bashi.quartz.domain.SysJob; + +/** + * 定时任务工具类 + * + * @author duteliang + * + */ +public class ScheduleUtils +{ + /** + * 得到quartz任务类 + * + * @param sysJob 执行计划 + * @return 具体执行任务类 + */ + private static Class getQuartzJobClass(SysJob sysJob) + { + boolean isConcurrent = "0".equals(sysJob.getConcurrent()); + return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class; + } + + /** + * 构建任务触发对象 + */ + public static TriggerKey getTriggerKey(Long jobId, String jobGroup) + { + return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup); + } + + /** + * 构建任务键对象 + */ + public static JobKey getJobKey(Long jobId, String jobGroup) + { + return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup); + } + + /** + * 创建定时任务 + */ + public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException + { + Class jobClass = getQuartzJobClass(job); + // 构建job信息 + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build(); + + // 表达式调度构建器 + CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); + cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder); + + // 按新的cronExpression表达式构建一个新的trigger + CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup)) + .withSchedule(cronScheduleBuilder).build(); + + // 放入参数,运行时的方法可以获取 + jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job); + + // 判断是否存在 + if (scheduler.checkExists(getJobKey(jobId, jobGroup))) + { + // 防止创建时存在数据问题 先移除,然后在执行创建操作 + scheduler.deleteJob(getJobKey(jobId, jobGroup)); + } + + scheduler.scheduleJob(jobDetail, trigger); + + // 暂停任务 + if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) + { + scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + } + + /** + * 设置定时任务策略 + */ + public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb) + throws TaskException + { + switch (job.getMisfirePolicy()) + { + case ScheduleConstants.MISFIRE_DEFAULT: + return cb; + case ScheduleConstants.MISFIRE_IGNORE_MISFIRES: + return cb.withMisfireHandlingInstructionIgnoreMisfires(); + case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED: + return cb.withMisfireHandlingInstructionFireAndProceed(); + case ScheduleConstants.MISFIRE_DO_NOTHING: + return cb.withMisfireHandlingInstructionDoNothing(); + default: + throw new TaskException("The task misfire policy '" + job.getMisfirePolicy() + + "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR); + } + } +} diff --git a/bashi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml b/bashi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml new file mode 100644 index 0000000..e6cdc57 --- /dev/null +++ b/bashi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + diff --git a/bashi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml b/bashi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml new file mode 100644 index 0000000..430bb93 --- /dev/null +++ b/bashi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/bashi-system/pom.xml b/bashi-system/pom.xml new file mode 100644 index 0000000..6781728 --- /dev/null +++ b/bashi-system/pom.xml @@ -0,0 +1,28 @@ + + + + bashi + com.bashi + 2.4.0 + + 4.0.0 + + bashi-system + + + system系统模块 + + + + + + + com.bashi + bashi-common + + + + + diff --git a/bashi-system/src/main/java/com/bashi/system/domain/SysConfig.java b/bashi-system/src/main/java/com/bashi/system/domain/SysConfig.java new file mode 100644 index 0000000..c29c087 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/domain/SysConfig.java @@ -0,0 +1,105 @@ +package com.bashi.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.bashi.common.annotation.Excel; +import com.bashi.common.annotation.Excel.ColumnType; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 参数配置表 sys_config + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("sys_config") +public class SysConfig implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 参数主键 + */ + @Excel(name = "参数主键", cellType = ColumnType.NUMERIC) + @TableId(value = "config_id", type = IdType.AUTO) + private Long configId; + + /** + * 参数名称 + */ + @Excel(name = "参数名称") + @NotBlank(message = "参数名称不能为空") + @Size(min = 0, max = 100, message = "参数名称不能超过100个字符") + private String configName; + + /** + * 参数键名 + */ + @Excel(name = "参数键名") + @NotBlank(message = "参数键名长度不能为空") + @Size(min = 0, max = 100, message = "参数键名长度不能超过100个字符") + private String configKey; + + /** + * 参数键值 + */ + @Excel(name = "参数键值") + @NotBlank(message = "参数键值不能为空") + @Size(min = 0, max = 500, message = "参数键值长度不能超过500个字符") + private String configValue; + + /** + * 系统内置(Y是 N否) + */ + @Excel(name = "系统内置", readConverterExp = "Y=是,N=否") + private String configType; + + /** + * 创建者 + */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** + * 备注 + */ + private String remark; + + /** + * 请求参数 + */ + @TableField(exist = false) + private Map params = new HashMap<>(); + +} diff --git a/bashi-system/src/main/java/com/bashi/system/domain/SysLogininfor.java b/bashi-system/src/main/java/com/bashi/system/domain/SysLogininfor.java new file mode 100644 index 0000000..87c2e77 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/domain/SysLogininfor.java @@ -0,0 +1,94 @@ +package com.bashi.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.bashi.common.annotation.Excel; +import com.bashi.common.annotation.Excel.ColumnType; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 系统访问记录表 sys_logininfor + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("sys_logininfor") +public class SysLogininfor implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Excel(name = "序号", cellType = ColumnType.NUMERIC) + @TableId(value = "info_id", type = IdType.AUTO) + private Long infoId; + + /** + * 用户账号 + */ + @Excel(name = "用户账号") + private String userName; + + /** + * 登录状态 0成功 1失败 + */ + @Excel(name = "登录状态", readConverterExp = "0=成功,1=失败") + private String status; + + /** + * 登录IP地址 + */ + @Excel(name = "登录地址") + private String ipaddr; + + /** + * 登录地点 + */ + @Excel(name = "登录地点") + private String loginLocation; + + /** + * 浏览器类型 + */ + @Excel(name = "浏览器") + private String browser; + + /** + * 操作系统 + */ + @Excel(name = "操作系统") + private String os; + + /** + * 提示消息 + */ + @Excel(name = "提示消息") + private String msg; + + /** + * 访问时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "访问时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date loginTime; + + /** + * 请求参数 + */ + @TableField(exist = false) + private Map params = new HashMap<>(); + +} diff --git a/bashi-system/src/main/java/com/bashi/system/domain/SysNotice.java b/bashi-system/src/main/java/com/bashi/system/domain/SysNotice.java new file mode 100644 index 0000000..769fae0 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/domain/SysNotice.java @@ -0,0 +1,93 @@ +package com.bashi.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 通知公告表 sys_notice + * + * @author duteliang + */ +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("sys_notice") +public class SysNotice implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 公告ID + */ + @TableId(value = "notice_id", type = IdType.AUTO) + private Long noticeId; + + /** + * 公告标题 + */ + @NotBlank(message = "公告标题不能为空") + @Size(min = 0, max = 50, message = "公告标题不能超过50个字符") + private String noticeTitle; + + /** + * 公告类型(1通知 2公告) + */ + private String noticeType; + + /** + * 公告内容 + */ + private String noticeContent; + + /** + * 公告状态(0正常 1关闭) + */ + private String status; + + /** + * 创建者 + */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** + * 备注 + */ + private String remark; + + /** + * 请求参数 + */ + @TableField(exist = false) + private Map params = new HashMap<>(); + +} diff --git a/bashi-system/src/main/java/com/bashi/system/domain/SysOperLog.java b/bashi-system/src/main/java/com/bashi/system/domain/SysOperLog.java new file mode 100644 index 0000000..026fb8e --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/domain/SysOperLog.java @@ -0,0 +1,142 @@ +package com.bashi.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.bashi.common.annotation.Excel; +import com.bashi.common.annotation.Excel.ColumnType; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 操作日志记录表 oper_log + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("sys_oper_log") +public class SysOperLog implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 日志主键 + */ + @Excel(name = "操作序号", cellType = ColumnType.NUMERIC) + @TableId(value = "oper_id", type = IdType.AUTO) + private Long operId; + + /** + * 操作模块 + */ + @Excel(name = "操作模块") + private String title; + + /** + * 业务类型(0其它 1新增 2修改 3删除) + */ + @Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据") + private Integer businessType; + + /** + * 业务类型数组 + */ + @TableField(exist = false) + private Integer[] businessTypes; + + /** + * 请求方法 + */ + @Excel(name = "请求方法") + private String method; + + /** + * 请求方式 + */ + @Excel(name = "请求方式") + private String requestMethod; + + /** + * 操作类别(0其它 1后台用户 2手机端用户) + */ + @Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户") + private Integer operatorType; + + /** + * 操作人员 + */ + @Excel(name = "操作人员") + private String operName; + + /** + * 部门名称 + */ + @Excel(name = "部门名称") + private String deptName; + + /** + * 请求url + */ + @Excel(name = "请求地址") + private String operUrl; + + /** + * 操作地址 + */ + @Excel(name = "操作地址") + private String operIp; + + /** + * 操作地点 + */ + @Excel(name = "操作地点") + private String operLocation; + + /** + * 请求参数 + */ + @Excel(name = "请求参数") + private String operParam; + + /** + * 返回参数 + */ + @Excel(name = "返回参数") + private String jsonResult; + + /** + * 操作状态(0正常 1异常) + */ + @Excel(name = "状态", readConverterExp = "0=正常,1=异常") + private Integer status; + + /** + * 错误消息 + */ + @Excel(name = "错误消息") + private String errorMsg; + + /** + * 操作时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date operTime; + + /** + * 请求参数 + */ + @TableField(exist = false) + private Map params = new HashMap<>(); + +} diff --git a/bashi-system/src/main/java/com/bashi/system/domain/SysPost.java b/bashi-system/src/main/java/com/bashi/system/domain/SysPost.java new file mode 100644 index 0000000..2db7908 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/domain/SysPost.java @@ -0,0 +1,110 @@ +package com.bashi.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.bashi.common.annotation.Excel; +import com.bashi.common.annotation.Excel.ColumnType; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 岗位表 sys_post + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("sys_post") +public class SysPost implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 岗位序号 + */ + @Excel(name = "岗位序号", cellType = ColumnType.NUMERIC) + @TableId(value = "post_id", type = IdType.AUTO) + private Long postId; + + /** + * 岗位编码 + */ + @Excel(name = "岗位编码") + @NotBlank(message = "岗位编码不能为空") + @Size(min = 0, max = 64, message = "岗位编码长度不能超过64个字符") + private String postCode; + + /** + * 岗位名称 + */ + @Excel(name = "岗位名称") + @NotBlank(message = "岗位名称不能为空") + @Size(min = 0, max = 50, message = "岗位名称长度不能超过50个字符") + private String postName; + + /** + * 岗位排序 + */ + @Excel(name = "岗位排序") + @NotBlank(message = "显示顺序不能为空") + private String postSort; + + /** + * 状态(0正常 1停用) + */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** + * 创建者 + */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** + * 备注 + */ + private String remark; + + /** + * 请求参数 + */ + @TableField(exist = false) + private Map params = new HashMap<>(); + + /** + * 用户是否存在此岗位标识 默认不存在 + */ + @TableField(exist = false) + private boolean flag = false; + +} diff --git a/bashi-system/src/main/java/com/bashi/system/domain/SysRoleDept.java b/bashi-system/src/main/java/com/bashi/system/domain/SysRoleDept.java new file mode 100644 index 0000000..c391293 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/domain/SysRoleDept.java @@ -0,0 +1,29 @@ +package com.bashi.system.domain; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * 角色和部门关联 sys_role_dept + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("sys_role_dept") +public class SysRoleDept { + /** + * 角色ID + */ + private Long roleId; + + /** + * 部门ID + */ + private Long deptId; + +} diff --git a/bashi-system/src/main/java/com/bashi/system/domain/SysRoleMenu.java b/bashi-system/src/main/java/com/bashi/system/domain/SysRoleMenu.java new file mode 100644 index 0000000..c845c0b --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/domain/SysRoleMenu.java @@ -0,0 +1,29 @@ +package com.bashi.system.domain; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * 角色和菜单关联 sys_role_menu + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("sys_role_menu") +public class SysRoleMenu { + /** + * 角色ID + */ + private Long roleId; + + /** + * 菜单ID + */ + private Long menuId; + +} diff --git a/bashi-system/src/main/java/com/bashi/system/domain/SysUserOnline.java b/bashi-system/src/main/java/com/bashi/system/domain/SysUserOnline.java new file mode 100644 index 0000000..33b6ddf --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/domain/SysUserOnline.java @@ -0,0 +1,56 @@ +package com.bashi.system.domain; + +import lombok.*; +import lombok.experimental.Accessors; + +/** + * 当前在线会话 + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class SysUserOnline { + /** + * 会话编号 + */ + private String tokenId; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 用户名称 + */ + private String userName; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地址 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 登录时间 + */ + private Long loginTime; + +} diff --git a/bashi-system/src/main/java/com/bashi/system/domain/SysUserPost.java b/bashi-system/src/main/java/com/bashi/system/domain/SysUserPost.java new file mode 100644 index 0000000..27ed827 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/domain/SysUserPost.java @@ -0,0 +1,29 @@ +package com.bashi.system.domain; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * 用户和岗位关联 sys_user_post + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("sys_user_post") +public class SysUserPost { + /** + * 用户ID + */ + private Long userId; + + /** + * 岗位ID + */ + private Long postId; + +} diff --git a/bashi-system/src/main/java/com/bashi/system/domain/SysUserRole.java b/bashi-system/src/main/java/com/bashi/system/domain/SysUserRole.java new file mode 100644 index 0000000..92b3355 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/domain/SysUserRole.java @@ -0,0 +1,29 @@ +package com.bashi.system.domain; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * 用户和角色关联 sys_user_role + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +@TableName("sys_user_role") +public class SysUserRole { + /** + * 用户ID + */ + private Long userId; + + /** + * 角色ID + */ + private Long roleId; + +} diff --git a/bashi-system/src/main/java/com/bashi/system/domain/vo/MetaVo.java b/bashi-system/src/main/java/com/bashi/system/domain/vo/MetaVo.java new file mode 100644 index 0000000..a678fa0 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/domain/vo/MetaVo.java @@ -0,0 +1,42 @@ +package com.bashi.system.domain.vo; + +import lombok.*; +import lombok.experimental.Accessors; + +/** + * 路由显示信息 + * + * @author duteliang + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class MetaVo { + /** + * 设置该路由在侧边栏和面包屑中展示的名字 + */ + private String title; + + /** + * 设置该路由的图标,对应路径src/assets/icons/svg + */ + private String icon; + + /** + * 设置为true,则不会被 缓存 + */ + private boolean noCache; + + public MetaVo(String title, String icon) { + this.title = title; + this.icon = icon; + } + + public MetaVo(String title, String icon, boolean noCache) { + this.title = title; + this.icon = icon; + this.noCache = noCache; + } + +} diff --git a/bashi-system/src/main/java/com/bashi/system/domain/vo/RouterVo.java b/bashi-system/src/main/java/com/bashi/system/domain/vo/RouterVo.java new file mode 100644 index 0000000..682ff0c --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/domain/vo/RouterVo.java @@ -0,0 +1,59 @@ +package com.bashi.system.domain.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.*; +import lombok.experimental.Accessors; + +import java.util.List; + +/** + * 路由配置信息 + * + * @author duteliang + */ +@Data +@NoArgsConstructor +@Accessors(chain = true) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class RouterVo { + /** + * 路由名字 + */ + private String name; + + /** + * 路由地址 + */ + private String path; + + /** + * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 + */ + private boolean hidden; + + /** + * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + */ + private String redirect; + + /** + * 组件地址 + */ + private String component; + + /** + * 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + */ + private Boolean alwaysShow; + + /** + * 其他元素 + */ + private MetaVo meta; + + /** + * 子路由 + */ + private List children; + +} diff --git a/bashi-system/src/main/java/com/bashi/system/mapper/SysConfigMapper.java b/bashi-system/src/main/java/com/bashi/system/mapper/SysConfigMapper.java new file mode 100644 index 0000000..9870f04 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/mapper/SysConfigMapper.java @@ -0,0 +1,13 @@ +package com.bashi.system.mapper; + +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; +import com.bashi.system.domain.SysConfig; + +/** + * 参数配置 数据层 + * + * @author duteliang + */ +public interface SysConfigMapper extends BaseMapperPlus { + +} diff --git a/bashi-system/src/main/java/com/bashi/system/mapper/SysDeptMapper.java b/bashi-system/src/main/java/com/bashi/system/mapper/SysDeptMapper.java new file mode 100644 index 0000000..ff5d61d --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/mapper/SysDeptMapper.java @@ -0,0 +1,41 @@ +package com.bashi.system.mapper; + +import com.bashi.common.core.domain.entity.SysDept; +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 部门管理 数据层 + * + * @author duteliang + */ +public interface SysDeptMapper extends BaseMapperPlus { + + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + public List selectDeptList(SysDept dept); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @param deptCheckStrictly 部门树选择项是否关联显示 + * @return 选中部门列表 + */ + public List selectDeptListByRoleId(@Param("roleId") Long roleId, @Param("deptCheckStrictly") boolean deptCheckStrictly); + + /** + * 修改子元素关系 + * + * @param depts 子元素 + * @return 结果 + */ + public int updateDeptChildren(@Param("depts") List depts); + +} diff --git a/bashi-system/src/main/java/com/bashi/system/mapper/SysDictDataMapper.java b/bashi-system/src/main/java/com/bashi/system/mapper/SysDictDataMapper.java new file mode 100644 index 0000000..ccb78a0 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/mapper/SysDictDataMapper.java @@ -0,0 +1,23 @@ +package com.bashi.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.bashi.common.core.domain.entity.SysDictData; +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; + +import java.util.List; + +/** + * 字典表 数据层 + * + * @author duteliang + */ +public interface SysDictDataMapper extends BaseMapperPlus { + + default List selectDictDataByType(String dictType) { + return selectList( + new LambdaQueryWrapper() + .eq(SysDictData::getStatus, "0") + .eq(SysDictData::getDictType, dictType) + .orderByAsc(SysDictData::getDictSort)); + } +} diff --git a/bashi-system/src/main/java/com/bashi/system/mapper/SysDictTypeMapper.java b/bashi-system/src/main/java/com/bashi/system/mapper/SysDictTypeMapper.java new file mode 100644 index 0000000..94680ae --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/mapper/SysDictTypeMapper.java @@ -0,0 +1,13 @@ +package com.bashi.system.mapper; + +import com.bashi.common.core.domain.entity.SysDictType; +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; + +/** + * 字典表 数据层 + * + * @author duteliang + */ +public interface SysDictTypeMapper extends BaseMapperPlus { + +} diff --git a/bashi-system/src/main/java/com/bashi/system/mapper/SysLogininforMapper.java b/bashi-system/src/main/java/com/bashi/system/mapper/SysLogininforMapper.java new file mode 100644 index 0000000..d143035 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/mapper/SysLogininforMapper.java @@ -0,0 +1,13 @@ +package com.bashi.system.mapper; + +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; +import com.bashi.system.domain.SysLogininfor; + +/** + * 系统访问日志情况信息 数据层 + * + * @author duteliang + */ +public interface SysLogininforMapper extends BaseMapperPlus { + +} diff --git a/bashi-system/src/main/java/com/bashi/system/mapper/SysMenuMapper.java b/bashi-system/src/main/java/com/bashi/system/mapper/SysMenuMapper.java new file mode 100644 index 0000000..bed7790 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/mapper/SysMenuMapper.java @@ -0,0 +1,63 @@ +package com.bashi.system.mapper; + +import com.bashi.common.core.domain.entity.SysMenu; +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 菜单表 数据层 + * + * @author duteliang + */ +public interface SysMenuMapper extends BaseMapperPlus { + + /** + * 根据用户所有权限 + * + * @return 权限列表 + */ + public List selectMenuPerms(); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + public List selectMenuListByUserId(SysMenu menu); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public List selectMenuPermsByUserId(Long userId); + + /** + * 根据用户ID查询菜单 + * + * @return 菜单列表 + */ + public List selectMenuTreeAll(); + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @param menuCheckStrictly 菜单树选择项是否关联显示 + * @return 选中菜单列表 + */ + public List selectMenuListByRoleId(@Param("roleId") Long roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly); + +} diff --git a/bashi-system/src/main/java/com/bashi/system/mapper/SysNoticeMapper.java b/bashi-system/src/main/java/com/bashi/system/mapper/SysNoticeMapper.java new file mode 100644 index 0000000..c546fbd --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/mapper/SysNoticeMapper.java @@ -0,0 +1,13 @@ +package com.bashi.system.mapper; + +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; +import com.bashi.system.domain.SysNotice; + +/** + * 通知公告表 数据层 + * + * @author duteliang + */ +public interface SysNoticeMapper extends BaseMapperPlus { + +} diff --git a/bashi-system/src/main/java/com/bashi/system/mapper/SysOperLogMapper.java b/bashi-system/src/main/java/com/bashi/system/mapper/SysOperLogMapper.java new file mode 100644 index 0000000..53488e8 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/mapper/SysOperLogMapper.java @@ -0,0 +1,13 @@ +package com.bashi.system.mapper; + +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; +import com.bashi.system.domain.SysOperLog; + +/** + * 操作日志 数据层 + * + * @author duteliang + */ +public interface SysOperLogMapper extends BaseMapperPlus { + +} diff --git a/bashi-system/src/main/java/com/bashi/system/mapper/SysPostMapper.java b/bashi-system/src/main/java/com/bashi/system/mapper/SysPostMapper.java new file mode 100644 index 0000000..d7e5aa9 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/mapper/SysPostMapper.java @@ -0,0 +1,31 @@ +package com.bashi.system.mapper; + +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; +import com.bashi.system.domain.SysPost; + +import java.util.List; + +/** + * 岗位信息 数据层 + * + * @author duteliang + */ +public interface SysPostMapper extends BaseMapperPlus { + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + public List selectPostListByUserId(Long userId); + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + public List selectPostsByUserName(String userName); + +} diff --git a/bashi-system/src/main/java/com/bashi/system/mapper/SysRoleDeptMapper.java b/bashi-system/src/main/java/com/bashi/system/mapper/SysRoleDeptMapper.java new file mode 100644 index 0000000..4bba4d6 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/mapper/SysRoleDeptMapper.java @@ -0,0 +1,13 @@ +package com.bashi.system.mapper; + +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; +import com.bashi.system.domain.SysRoleDept; + +/** + * 角色与部门关联表 数据层 + * + * @author duteliang + */ +public interface SysRoleDeptMapper extends BaseMapperPlus { + +} diff --git a/bashi-system/src/main/java/com/bashi/system/mapper/SysRoleMapper.java b/bashi-system/src/main/java/com/bashi/system/mapper/SysRoleMapper.java new file mode 100644 index 0000000..5e2df34 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/mapper/SysRoleMapper.java @@ -0,0 +1,52 @@ +package com.bashi.system.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bashi.common.core.domain.entity.SysRole; +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 角色表 数据层 + * + * @author duteliang + */ +public interface SysRoleMapper extends BaseMapperPlus { + + Page selectPageRoleList(@Param("page") Page page, @Param("role") SysRole role); + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + public List selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + public List selectRolePermissionByUserId(Long userId); + + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + public List selectRoleListByUserId(Long userId); + + /** + * 根据用户ID查询角色 + * + * @param userName 用户名 + * @return 角色列表 + */ + public List selectRolesByUserName(String userName); + +} diff --git a/bashi-system/src/main/java/com/bashi/system/mapper/SysRoleMenuMapper.java b/bashi-system/src/main/java/com/bashi/system/mapper/SysRoleMenuMapper.java new file mode 100644 index 0000000..3bb645f --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/mapper/SysRoleMenuMapper.java @@ -0,0 +1,13 @@ +package com.bashi.system.mapper; + +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; +import com.bashi.system.domain.SysRoleMenu; + +/** + * 角色与菜单关联表 数据层 + * + * @author duteliang + */ +public interface SysRoleMenuMapper extends BaseMapperPlus { + +} diff --git a/bashi-system/src/main/java/com/bashi/system/mapper/SysUserMapper.java b/bashi-system/src/main/java/com/bashi/system/mapper/SysUserMapper.java new file mode 100644 index 0000000..127183f --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/mapper/SysUserMapper.java @@ -0,0 +1,46 @@ +package com.bashi.system.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bashi.common.core.domain.entity.SysUser; +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 用户表 数据层 + * + * @author duteliang + */ +public interface SysUserMapper extends BaseMapperPlus { + + Page selectPageUserList(@Param("page") Page page, @Param("user") SysUser user); + + /** + * 根据条件分页查询用户列表 + * + * @param sysUser 用户信息 + * @return 用户信息集合信息 + */ + public List selectUserList(SysUser sysUser); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public SysUser selectUserByUserName(String userName); + + public SysUser selectUserByMobile(String mobile); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public SysUser selectUserById(Long userId); + + SysUser selectUserByOpenId(String openId); +} diff --git a/bashi-system/src/main/java/com/bashi/system/mapper/SysUserPostMapper.java b/bashi-system/src/main/java/com/bashi/system/mapper/SysUserPostMapper.java new file mode 100644 index 0000000..22be36b --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/mapper/SysUserPostMapper.java @@ -0,0 +1,13 @@ +package com.bashi.system.mapper; + +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; +import com.bashi.system.domain.SysUserPost; + +/** + * 用户与岗位关联表 数据层 + * + * @author duteliang + */ +public interface SysUserPostMapper extends BaseMapperPlus { + +} diff --git a/bashi-system/src/main/java/com/bashi/system/mapper/SysUserRoleMapper.java b/bashi-system/src/main/java/com/bashi/system/mapper/SysUserRoleMapper.java new file mode 100644 index 0000000..24c7f99 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/mapper/SysUserRoleMapper.java @@ -0,0 +1,13 @@ +package com.bashi.system.mapper; + +import com.bashi.common.core.mybatisplus.core.BaseMapperPlus; +import com.bashi.system.domain.SysUserRole; + +/** + * 用户与角色关联表 数据层 + * + * @author duteliang + */ +public interface SysUserRoleMapper extends BaseMapperPlus { + +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/ISysConfigService.java b/bashi-system/src/main/java/com/bashi/system/service/ISysConfigService.java new file mode 100644 index 0000000..7e0b33b --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/ISysConfigService.java @@ -0,0 +1,89 @@ +package com.bashi.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.system.domain.SysConfig; + +import java.util.List; + +/** + * 参数配置 服务层 + * + * @author duteliang + */ +public interface ISysConfigService extends IService { + + + TableDataInfo selectPageConfigList(SysConfig config); + + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + public SysConfig selectConfigById(Long configId); + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数键名 + * @return 参数键值 + */ + public String selectConfigByKey(String configKey); + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + public List selectConfigList(SysConfig config); + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int insertConfig(SysConfig config); + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int updateConfig(SysConfig config); + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + * @return 结果 + */ + public void deleteConfigByIds(Long[] configIds); + + /** + * 加载参数缓存数据 + */ + public void loadingConfigCache(); + + /** + * 清空参数缓存数据 + */ + public void clearConfigCache(); + + /** + * 重置参数缓存数据 + */ + public void resetConfigCache(); + + /** + * 校验参数键名是否唯一 + * + * @param config 参数信息 + * @return 结果 + */ + public String checkConfigKeyUnique(SysConfig config); +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/ISysDeptService.java b/bashi-system/src/main/java/com/bashi/system/service/ISysDeptService.java new file mode 100644 index 0000000..eed8b02 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/ISysDeptService.java @@ -0,0 +1,110 @@ +package com.bashi.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.common.core.domain.TreeSelect; +import com.bashi.common.core.domain.entity.SysDept; + +import java.util.List; + +/** + * 部门管理 服务层 + * + * @author duteliang + */ +public interface ISysDeptService extends IService { + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + public List selectDeptList(SysDept dept); + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * @return 树结构列表 + */ + public List buildDeptTree(List depts); + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + public List buildDeptTreeSelect(List depts); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + public List selectDeptListByRoleId(Long roleId); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + public SysDept selectDeptById(Long deptId); + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + public long selectNormalChildrenDeptById(Long deptId); + + /** + * 是否存在部门子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + public boolean hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + public boolean checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + public String checkDeptNameUnique(SysDept dept); + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int insertDept(SysDept dept); + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int updateDept(SysDept dept); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + public int deleteDeptById(Long deptId); +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/ISysDictDataService.java b/bashi-system/src/main/java/com/bashi/system/service/ISysDictDataService.java new file mode 100644 index 0000000..131cffc --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/ISysDictDataService.java @@ -0,0 +1,67 @@ +package com.bashi.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.common.core.domain.entity.SysDictData; +import com.bashi.common.core.page.TableDataInfo; + +import java.util.List; + +/** + * 字典 业务层 + * + * @author duteliang + */ +public interface ISysDictDataService extends IService { + + + TableDataInfo selectPageDictDataList(SysDictData dictData); + + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + public List selectDictDataList(SysDictData dictData); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + public String selectDictLabel(String dictType, String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + public SysDictData selectDictDataById(Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + * @return 结果 + */ + public void deleteDictDataByIds(Long[] dictCodes); + + /** + * 新增保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int insertDictData(SysDictData dictData); + + /** + * 修改保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int updateDictData(SysDictData dictData); +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/ISysDictTypeService.java b/bashi-system/src/main/java/com/bashi/system/service/ISysDictTypeService.java new file mode 100644 index 0000000..b519984 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/ISysDictTypeService.java @@ -0,0 +1,105 @@ +package com.bashi.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.common.core.domain.entity.SysDictData; +import com.bashi.common.core.domain.entity.SysDictType; +import com.bashi.common.core.page.TableDataInfo; + +import java.util.List; + +/** + * 字典 业务层 + * + * @author duteliang + */ +public interface ISysDictTypeService extends IService { + + + TableDataInfo selectPageDictTypeList(SysDictType dictType); + + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + public List selectDictTypeList(SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + public List selectDictTypeAll(); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + public List selectDictDataByType(String dictType); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + public SysDictType selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + public SysDictType selectDictTypeByType(String dictType); + + /** + * 批量删除字典信息 + * + * @param dictIds 需要删除的字典ID + * @return 结果 + */ + public void deleteDictTypeByIds(Long[] dictIds); + + /** + * 加载字典缓存数据 + */ + public void loadingDictCache(); + + /** + * 清空字典缓存数据 + */ + public void clearDictCache(); + + /** + * 重置字典缓存数据 + */ + public void resetDictCache(); + + /** + * 新增保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int insertDictType(SysDictType dictType); + + /** + * 修改保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int updateDictType(SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + public String checkDictTypeUnique(SysDictType dictType); +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/ISysLogininforService.java b/bashi-system/src/main/java/com/bashi/system/service/ISysLogininforService.java new file mode 100644 index 0000000..cd84ca5 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/ISysLogininforService.java @@ -0,0 +1,46 @@ +package com.bashi.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.system.domain.SysLogininfor; + +import java.util.List; + +/** + * 系统访问日志情况信息 服务层 + * + * @author duteliang + */ +public interface ISysLogininforService extends IService { + + + TableDataInfo selectPageLogininforList(SysLogininfor logininfor); + + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public void insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + public List selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return + */ + public int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + */ + public void cleanLogininfor(); +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/ISysMenuService.java b/bashi-system/src/main/java/com/bashi/system/service/ISysMenuService.java new file mode 100644 index 0000000..c919a85 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/ISysMenuService.java @@ -0,0 +1,137 @@ +package com.bashi.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.common.core.domain.TreeSelect; +import com.bashi.common.core.domain.entity.SysMenu; +import com.bashi.system.domain.vo.RouterVo; + +import java.util.List; +import java.util.Set; + +/** + * 菜单 业务层 + * + * @author duteliang + */ +public interface ISysMenuService extends IService { + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuList(Long userId); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuList(SysMenu menu, Long userId); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public Set selectMenuPermsByUserId(Long userId); + + /** + * 根据用户ID查询菜单树信息 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + public List selectMenuListByRoleId(Long roleId); + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + public List buildMenus(List menus); + + /** + * 构建前端所需要树结构 + * + * @param menus 菜单列表 + * @return 树结构列表 + */ + public List buildMenuTree(List menus); + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + public List buildMenuTreeSelect(List menus); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + public SysMenu selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + public boolean hasChildByMenuId(Long menuId); + + /** + * 查询菜单是否存在角色 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + public boolean checkMenuExistRole(Long menuId); + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int insertMenu(SysMenu menu); + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int updateMenu(SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int deleteMenuById(Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + public String checkMenuNameUnique(SysMenu menu); +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/ISysNoticeService.java b/bashi-system/src/main/java/com/bashi/system/service/ISysNoticeService.java new file mode 100644 index 0000000..11647ce --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/ISysNoticeService.java @@ -0,0 +1,66 @@ +package com.bashi.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.system.domain.SysNotice; + +import java.util.List; + +/** + * 公告 服务层 + * + * @author duteliang + */ +public interface ISysNoticeService extends IService { + + + TableDataInfo selectPageNoticeList(SysNotice notice); + + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + public SysNotice selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + public List selectNoticeList(SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int insertNotice(SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int updateNotice(SysNotice notice); + + /** + * 删除公告信息 + * + * @param noticeId 公告ID + * @return 结果 + */ + public int deleteNoticeById(Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + public int deleteNoticeByIds(Long[] noticeIds); +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/ISysOperLogService.java b/bashi-system/src/main/java/com/bashi/system/service/ISysOperLogService.java new file mode 100644 index 0000000..cfe858d --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/ISysOperLogService.java @@ -0,0 +1,53 @@ +package com.bashi.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.system.domain.SysOperLog; + +import java.util.List; + +/** + * 操作日志 服务层 + * + * @author duteliang + */ +public interface ISysOperLogService extends IService { + + TableDataInfo selectPageOperLogList(SysOperLog operLog); + + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + public void insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + public List selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + public int deleteOperLogByIds(Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + public SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + public void cleanOperLog(); +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/ISysPostService.java b/bashi-system/src/main/java/com/bashi/system/service/ISysPostService.java new file mode 100644 index 0000000..10c5355 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/ISysPostService.java @@ -0,0 +1,106 @@ +package com.bashi.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.system.domain.SysPost; + +import java.util.List; + +/** + * 岗位信息 服务层 + * + * @author duteliang + */ +public interface ISysPostService extends IService { + + + TableDataInfo selectPagePostList(SysPost post); + + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位列表 + */ + public List selectPostList(SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + public List selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + public SysPost selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + public List selectPostListByUserId(Long userId); + + /** + * 校验岗位名称 + * + * @param post 岗位信息 + * @return 结果 + */ + public String checkPostNameUnique(SysPost post); + + /** + * 校验岗位编码 + * + * @param post 岗位信息 + * @return 结果 + */ + public String checkPostCodeUnique(SysPost post); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + public long countUserPostById(Long postId); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + public int deletePostById(Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + * @throws Exception 异常 + */ + public int deletePostByIds(Long[] postIds); + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int insertPost(SysPost post); + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int updatePost(SysPost post); +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/ISysRoleService.java b/bashi-system/src/main/java/com/bashi/system/service/ISysRoleService.java new file mode 100644 index 0000000..2604943 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/ISysRoleService.java @@ -0,0 +1,137 @@ +package com.bashi.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.common.core.domain.entity.SysRole; +import com.bashi.common.core.page.TableDataInfo; + +import java.util.List; +import java.util.Set; + +/** + * 角色业务层 + * + * @author duteliang + */ +public interface ISysRoleService extends IService { + + + TableDataInfo selectPageRoleList(SysRole role); + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + public List selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 权限列表 + */ + public Set selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + public List selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + public SysRole selectRoleById(Long roleId); + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + public String checkRoleNameUnique(SysRole role); + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + public String checkRoleKeyUnique(SysRole role); + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + public void checkRoleAllowed(SysRole role); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + public long countUserRoleByRoleId(Long roleId); + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int insertRole(SysRole role); + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRole(SysRole role); + + /** + * 修改角色状态 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRoleStatus(SysRole role); + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int authDataScope(SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleById(Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + public int deleteRoleByIds(Long[] roleIds); +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/ISysUserOnlineService.java b/bashi-system/src/main/java/com/bashi/system/service/ISysUserOnlineService.java new file mode 100644 index 0000000..7369a53 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/ISysUserOnlineService.java @@ -0,0 +1,47 @@ +package com.bashi.system.service; + +import com.bashi.common.core.domain.model.LoginUser; +import com.bashi.system.domain.SysUserOnline; + +/** + * 在线用户 服务层 + * + * @author duteliang + */ +public interface ISysUserOnlineService { + /** + * 通过登录地址查询信息 + * + * @param ipaddr 登录地址 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user); + + /** + * 通过用户名称查询信息 + * + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByUserName(String userName, LoginUser user); + + /** + * 通过登录地址/用户名称查询信息 + * + * @param ipaddr 登录地址 + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user); + + /** + * 设置在线用户信息 + * + * @param user 用户信息 + * @return 在线用户 + */ + public SysUserOnline loginUserToUserOnline(LoginUser user); +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/ISysUserService.java b/bashi-system/src/main/java/com/bashi/system/service/ISysUserService.java new file mode 100644 index 0000000..6d6623e --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/ISysUserService.java @@ -0,0 +1,178 @@ +package com.bashi.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bashi.common.core.domain.entity.SysUser; +import com.bashi.common.core.page.TableDataInfo; + +import java.util.List; + +/** + * 用户 业务层 + * + * @author duteliang + */ +public interface ISysUserService extends IService { + + + TableDataInfo selectPageUserList(SysUser user); + + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectUserList(SysUser user); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public SysUser selectUserByUserName(String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public SysUser selectUserById(Long userId); + + /** + * 根据用户ID查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + public String selectUserRoleGroup(String userName); + + /** + * 根据用户ID查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + public String selectUserPostGroup(String userName); + + /** + * 校验用户名称是否唯一 + * + * @param userName 用户名称 + * @return 结果 + */ + public String checkUserNameUnique(String userName); + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public String checkPhoneUnique(SysUser user); + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public String checkEmailUnique(SysUser user); + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + public void checkUserAllowed(SysUser user); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int insertUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUser(SysUser user); + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserStatus(SysUser user); + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserProfile(SysUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + boolean updateUserAvatar(String userName, String avatar); + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + int resetPwd(SysUser user); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + int resetUserPwd(String userName, String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + int deleteUserById(Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + int deleteUserByIds(Long[] userIds); + + /** + * 导入用户数据 + * + * @param userList 用户数据列表 + * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 + * @param operName 操作用户 + * @return 结果 + */ + String importUser(List userList, Boolean isUpdateSupport, String operName); + + SysUser selectUserByMobile(String mobile); + + SysUser selectUserByOpenId(String openId); + +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/impl/SysConfigServiceImpl.java b/bashi-system/src/main/java/com/bashi/system/service/impl/SysConfigServiceImpl.java new file mode 100644 index 0000000..5e4de45 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/impl/SysConfigServiceImpl.java @@ -0,0 +1,220 @@ +package com.bashi.system.service.impl; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.common.annotation.DataSource; +import com.bashi.common.constant.Constants; +import com.bashi.common.constant.UserConstants; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.core.redis.RedisCache; +import com.bashi.common.enums.DataSourceType; +import com.bashi.common.exception.CustomException; +import com.bashi.common.utils.PageUtils; +import com.bashi.system.domain.SysConfig; +import com.bashi.system.mapper.SysConfigMapper; +import com.bashi.system.service.ISysConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 参数配置 服务层实现 + * + * @author duteliang + */ +@Service +public class SysConfigServiceImpl extends ServiceImpl implements ISysConfigService { + + @Autowired + private RedisCache redisCache; + + /** + * 项目启动时,初始化参数到缓存 + */ + @PostConstruct + public void init() { + loadingConfigCache(); + } + + @Override + public TableDataInfo selectPageConfigList(SysConfig config) { + Map params = config.getParams(); + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .like(StrUtil.isNotBlank(config.getConfigName()), SysConfig::getConfigName, config.getConfigName()) + .eq(StrUtil.isNotBlank(config.getConfigType()), SysConfig::getConfigType, config.getConfigType()) + .like(StrUtil.isNotBlank(config.getConfigKey()), SysConfig::getConfigKey, config.getConfigKey()) + .apply(Validator.isNotEmpty(params.get("beginTime")), + "date_format(create_time,'%y%m%d') >= date_format({0},'%y%m%d')", + params.get("beginTime")) + .apply(Validator.isNotEmpty(params.get("endTime")), + "date_format(create_time,'%y%m%d') <= date_format({0},'%y%m%d')", + params.get("endTime")); + return PageUtils.buildDataInfo(page(PageUtils.buildPage(), lqw)); + } + + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + @Override + @DataSource(DataSourceType.MASTER) + public SysConfig selectConfigById(Long configId) { + return baseMapper.selectById(configId); + } + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数key + * @return 参数键值 + */ + @Override + public String selectConfigByKey(String configKey) { + String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey))); + if (Validator.isNotEmpty(configValue)) { + return configValue; + } + SysConfig retConfig = baseMapper.selectOne(new LambdaQueryWrapper() + .eq(SysConfig::getConfigKey, configKey)); + if (Validator.isNotNull(retConfig)) { + redisCache.setCacheObject(getCacheKey(configKey), retConfig.getConfigValue()); + return retConfig.getConfigValue(); + } + return StrUtil.EMPTY; + } + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + @Override + public List selectConfigList(SysConfig config) { + Map params = config.getParams(); + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .like(StrUtil.isNotBlank(config.getConfigName()), SysConfig::getConfigName, config.getConfigName()) + .eq(StrUtil.isNotBlank(config.getConfigType()), SysConfig::getConfigType, config.getConfigType()) + .like(StrUtil.isNotBlank(config.getConfigKey()), SysConfig::getConfigKey, config.getConfigKey()) + .apply(Validator.isNotEmpty(params.get("beginTime")), + "date_format(create_time,'%y%m%d') >= date_format({0},'%y%m%d')", + params.get("beginTime")) + .apply(Validator.isNotEmpty(params.get("endTime")), + "date_format(create_time,'%y%m%d') <= date_format({0},'%y%m%d')", + params.get("endTime")); + return baseMapper.selectList(lqw); + } + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public int insertConfig(SysConfig config) { + int row = baseMapper.insert(config); + if (row > 0) { + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + return row; + } + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public int updateConfig(SysConfig config) { + int row = baseMapper.updateById(config); + if (row > 0) { + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + return row; + } + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + * @return 结果 + */ + @Override + public void deleteConfigByIds(Long[] configIds) { + for (Long configId : configIds) { + SysConfig config = selectConfigById(configId); + if (StrUtil.equals(UserConstants.YES, config.getConfigType())) { + throw new CustomException(String.format("内置参数【%1$s】不能删除 ", config.getConfigKey())); + } + redisCache.deleteObject(getCacheKey(config.getConfigKey())); + } + baseMapper.deleteBatchIds(Arrays.asList(configIds)); + } + + /** + * 加载参数缓存数据 + */ + @Override + public void loadingConfigCache() { + List configsList = selectConfigList(new SysConfig()); + for (SysConfig config : configsList) { + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + } + + /** + * 清空参数缓存数据 + */ + @Override + public void clearConfigCache() { + Collection keys = redisCache.keys(Constants.SYS_CONFIG_KEY + "*"); + redisCache.deleteObject(keys); + } + + /** + * 重置参数缓存数据 + */ + @Override + public void resetConfigCache() { + clearConfigCache(); + loadingConfigCache(); + } + + /** + * 校验参数键名是否唯一 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public String checkConfigKeyUnique(SysConfig config) { + Long configId = Validator.isNull(config.getConfigId()) ? -1L : config.getConfigId(); + SysConfig info = baseMapper.selectOne(new LambdaQueryWrapper().eq(SysConfig::getConfigKey, config.getConfigKey())); + if (Validator.isNotNull(info) && info.getConfigId().longValue() != configId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 设置cache key + * + * @param configKey 参数键 + * @return 缓存键key + */ + private String getCacheKey(String configKey) { + return Constants.SYS_CONFIG_KEY + configKey; + } +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/impl/SysDeptServiceImpl.java b/bashi-system/src/main/java/com/bashi/system/service/impl/SysDeptServiceImpl.java new file mode 100644 index 0000000..d20ba5e --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/impl/SysDeptServiceImpl.java @@ -0,0 +1,289 @@ +package com.bashi.system.service.impl; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Validator; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.common.annotation.DataScope; +import com.bashi.common.constant.UserConstants; +import com.bashi.common.core.domain.TreeSelect; +import com.bashi.common.core.domain.entity.SysDept; +import com.bashi.common.core.domain.entity.SysRole; +import com.bashi.common.core.domain.entity.SysUser; +import com.bashi.common.exception.CustomException; +import com.bashi.system.mapper.SysDeptMapper; +import com.bashi.system.mapper.SysRoleMapper; +import com.bashi.system.mapper.SysUserMapper; +import com.bashi.system.service.ISysDeptService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 部门管理 服务实现 + * + * @author duteliang + */ +@Service +public class SysDeptServiceImpl extends ServiceImpl implements ISysDeptService { + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysUserMapper userMapper; + + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + @Override + @DataScope(deptAlias = "d") + public List selectDeptList(SysDept dept) { + return baseMapper.selectDeptList(dept); + } + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * @return 树结构列表 + */ + @Override + public List buildDeptTree(List depts) { + List returnList = new ArrayList(); + List tempList = new ArrayList(); + for (SysDept dept : depts) { + tempList.add(dept.getDeptId()); + } + for (SysDept dept : depts) { + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.contains(dept.getParentId())) { + recursionFn(depts, dept); + returnList.add(dept); + } + } + if (returnList.isEmpty()) { + returnList = depts; + } + return returnList; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + @Override + public List buildDeptTreeSelect(List depts) { + List deptTrees = buildDeptTree(depts); + return deptTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + @Override + public List selectDeptListByRoleId(Long roleId) { + SysRole role = roleMapper.selectById(roleId); + return baseMapper.selectDeptListByRoleId(roleId, role.isDeptCheckStrictly()); + } + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + @Override + public SysDept selectDeptById(Long deptId) { + return getById(deptId); + } + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + @Override + public long selectNormalChildrenDeptById(Long deptId) { + return count(new LambdaQueryWrapper() + .eq(SysDept::getStatus, 0) + .apply("find_in_set({0}, ancestors)", deptId)); + } + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public boolean hasChildByDeptId(Long deptId) { + long result = count(new LambdaQueryWrapper() + .eq(SysDept::getParentId, deptId) + .last("limit 1")); + return result > 0; + } + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + @Override + public boolean checkDeptExistUser(Long deptId) { + long result = userMapper.selectCount(new LambdaQueryWrapper() + .eq(SysUser::getDeptId, deptId)); + return result > 0; + } + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public String checkDeptNameUnique(SysDept dept) { + Long deptId = Validator.isNull(dept.getDeptId()) ? -1L : dept.getDeptId(); + SysDept info = getOne(new LambdaQueryWrapper() + .eq(SysDept::getDeptName, dept.getDeptName()) + .eq(SysDept::getParentId, dept.getParentId()) + .last("limit 1")); + if (Validator.isNotNull(info) && info.getDeptId().longValue() != deptId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public int insertDept(SysDept dept) { + SysDept info = getById(dept.getParentId()); + // 如果父节点不为正常状态,则不允许新增子节点 + if (!UserConstants.DEPT_NORMAL.equals(info.getStatus())) { + throw new CustomException("部门停用,不允许新增"); + } + dept.setAncestors(info.getAncestors() + "," + dept.getParentId()); + return baseMapper.insert(dept); + } + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public int updateDept(SysDept dept) { + SysDept newParentDept = getById(dept.getParentId()); + SysDept oldDept = getById(dept.getDeptId()); + if (Validator.isNotNull(newParentDept) && Validator.isNotNull(oldDept)) { + String newAncestors = newParentDept.getAncestors() + "," + newParentDept.getDeptId(); + String oldAncestors = oldDept.getAncestors(); + dept.setAncestors(newAncestors); + updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors); + } + int result = baseMapper.updateById(dept); + if (UserConstants.DEPT_NORMAL.equals(dept.getStatus())) { + // 如果该部门是启用状态,则启用该部门的所有上级部门 + updateParentDeptStatusNormal(dept); + } + return result; + } + + /** + * 修改该部门的父级部门状态 + * + * @param dept 当前部门 + */ + private void updateParentDeptStatusNormal(SysDept dept) { + String ancestors = dept.getAncestors(); + Long[] deptIds = Convert.toLongArray(ancestors); + update(null, new LambdaUpdateWrapper() + .set(SysDept::getStatus, "0") + .in(SysDept::getDeptId, Arrays.asList(deptIds))); + } + + /** + * 修改子元素关系 + * + * @param deptId 被修改的部门ID + * @param newAncestors 新的父ID集合 + * @param oldAncestors 旧的父ID集合 + */ + public void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors) { + List children = list(new LambdaQueryWrapper() + .apply("find_in_set({0},ancestors)",deptId)); + for (SysDept child : children) { + child.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors)); + } + if (children.size() > 0) { + baseMapper.updateDeptChildren(children); + } + } + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public int deleteDeptById(Long deptId) { + return baseMapper.deleteById(deptId); + } + + /** + * 递归列表 + */ + private void recursionFn(List list, SysDept t) { + // 得到子节点列表 + List childList = getChildList(list, t); + t.setChildren(childList); + for (SysDept tChild : childList) { + if (hasChild(list, tChild)) { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + private List getChildList(List list, SysDept t) { + List tlist = new ArrayList(); + for (SysDept n : list) { + if (Validator.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getDeptId().longValue()) { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List list, SysDept t) { + return getChildList(list, t).size() > 0; + } +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/impl/SysDictDataServiceImpl.java b/bashi-system/src/main/java/com/bashi/system/service/impl/SysDictDataServiceImpl.java new file mode 100644 index 0000000..0fe1369 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/impl/SysDictDataServiceImpl.java @@ -0,0 +1,124 @@ +package com.bashi.system.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.common.core.domain.entity.SysDictData; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.utils.DictUtils; +import com.bashi.common.utils.PageUtils; +import com.bashi.system.mapper.SysDictDataMapper; +import com.bashi.system.service.ISysDictDataService; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; + +/** + * 字典 业务层处理 + * + * @author duteliang + */ +@Service +public class SysDictDataServiceImpl extends ServiceImpl implements ISysDictDataService { + + @Override + public TableDataInfo selectPageDictDataList(SysDictData dictData) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .eq(StrUtil.isNotBlank(dictData.getDictType()), SysDictData::getDictType, dictData.getDictType()) + .like(StrUtil.isNotBlank(dictData.getDictLabel()), SysDictData::getDictLabel, dictData.getDictLabel()) + .eq(StrUtil.isNotBlank(dictData.getStatus()), SysDictData::getStatus, dictData.getStatus()) + .orderByAsc(SysDictData::getDictSort); + return PageUtils.buildDataInfo(page(PageUtils.buildPage(), lqw)); + } + + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + @Override + public List selectDictDataList(SysDictData dictData) { + return list(new LambdaQueryWrapper() + .eq(StrUtil.isNotBlank(dictData.getDictType()), SysDictData::getDictType, dictData.getDictType()) + .like(StrUtil.isNotBlank(dictData.getDictLabel()), SysDictData::getDictLabel, dictData.getDictLabel()) + .eq(StrUtil.isNotBlank(dictData.getStatus()), SysDictData::getStatus, dictData.getStatus()) + .orderByAsc(SysDictData::getDictSort)); + } + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + @Override + public String selectDictLabel(String dictType, String dictValue) { + return getOne(new LambdaQueryWrapper() + .select(SysDictData::getDictLabel) + .eq(SysDictData::getDictType, dictType) + .eq(SysDictData::getDictValue, dictValue)) + .getDictLabel(); + } + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + @Override + public SysDictData selectDictDataById(Long dictCode) { + return getById(dictCode); + } + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + * @return 结果 + */ + @Override + public void deleteDictDataByIds(Long[] dictCodes) { + for (Long dictCode : dictCodes) { + SysDictData data = selectDictDataById(dictCode); + List dictDatas = baseMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + baseMapper.deleteBatchIds(Arrays.asList(dictCodes)); + } + + /** + * 新增保存字典数据信息 + * + * @param data 字典数据信息 + * @return 结果 + */ + @Override + public int insertDictData(SysDictData data) { + int row = baseMapper.insert(data); + if (row > 0) { + List dictDatas = baseMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + return row; + } + + /** + * 修改保存字典数据信息 + * + * @param data 字典数据信息 + * @return 结果 + */ + @Override + public int updateDictData(SysDictData data) { + int row = baseMapper.updateById(data); + if (row > 0) { + List dictDatas = baseMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + return row; + } +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/impl/SysDictTypeServiceImpl.java b/bashi-system/src/main/java/com/bashi/system/service/impl/SysDictTypeServiceImpl.java new file mode 100644 index 0000000..0790fce --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/impl/SysDictTypeServiceImpl.java @@ -0,0 +1,237 @@ +package com.bashi.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.common.constant.UserConstants; +import com.bashi.common.core.domain.entity.SysDictData; +import com.bashi.common.core.domain.entity.SysDictType; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.exception.CustomException; +import com.bashi.common.utils.DictUtils; +import com.bashi.common.utils.PageUtils; +import com.bashi.system.mapper.SysDictDataMapper; +import com.bashi.system.mapper.SysDictTypeMapper; +import com.bashi.system.service.ISysDictTypeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.PostConstruct; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * 字典 业务层处理 + * + * @author duteliang + */ +@Service +public class SysDictTypeServiceImpl extends ServiceImpl implements ISysDictTypeService { + + @Autowired + private SysDictDataMapper dictDataMapper; + + /** + * 项目启动时,初始化字典到缓存 + */ + @PostConstruct + public void init() { + loadingDictCache(); + } + + @Override + public TableDataInfo selectPageDictTypeList(SysDictType dictType) { + Map params = dictType.getParams(); + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .like(StrUtil.isNotBlank(dictType.getDictName()), SysDictType::getDictName, dictType.getDictName()) + .eq(StrUtil.isNotBlank(dictType.getStatus()), SysDictType::getStatus, dictType.getStatus()) + .like(StrUtil.isNotBlank(dictType.getDictType()), SysDictType::getDictType, dictType.getDictType()) + .apply(Validator.isNotEmpty(params.get("beginTime")), + "date_format(create_time,'%y%m%d') >= date_format({0},'%y%m%d')", + params.get("beginTime")) + .apply(Validator.isNotEmpty(params.get("endTime")), + "date_format(create_time,'%y%m%d') <= date_format({0},'%y%m%d')", + params.get("endTime")); + return PageUtils.buildDataInfo(page(PageUtils.buildPage(), lqw)); + } + + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeList(SysDictType dictType) { + Map params = dictType.getParams(); + return list(new LambdaQueryWrapper() + .like(StrUtil.isNotBlank(dictType.getDictName()), SysDictType::getDictName, dictType.getDictName()) + .eq(StrUtil.isNotBlank(dictType.getStatus()), SysDictType::getStatus, dictType.getStatus()) + .like(StrUtil.isNotBlank(dictType.getDictType()), SysDictType::getDictType, dictType.getDictType()) + .apply(Validator.isNotEmpty(params.get("beginTime")), + "date_format(create_time,'%y%m%d') >= date_format({0},'%y%m%d')", + params.get("beginTime")) + .apply(Validator.isNotEmpty(params.get("endTime")), + "date_format(create_time,'%y%m%d') <= date_format({0},'%y%m%d')", + params.get("endTime"))); + } + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeAll() { + return list(); + } + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + @Override + public List selectDictDataByType(String dictType) { + List dictDatas = DictUtils.getDictCache(dictType); + if (CollUtil.isNotEmpty(dictDatas)) { + return dictDatas; + } + dictDatas = dictDataMapper.selectDictDataByType(dictType); + if (CollUtil.isNotEmpty(dictDatas)) { + DictUtils.setDictCache(dictType, dictDatas); + return dictDatas; + } + return null; + } + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + @Override + public SysDictType selectDictTypeById(Long dictId) { + return getById(dictId); + } + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + @Override + public SysDictType selectDictTypeByType(String dictType) { + return getOne(new LambdaQueryWrapper().eq(SysDictType::getDictType, dictType)); + } + + /** + * 批量删除字典类型信息 + * + * @param dictIds 需要删除的字典ID + * @return 结果 + */ + @Override + public void deleteDictTypeByIds(Long[] dictIds) { + for (Long dictId : dictIds) { + SysDictType dictType = selectDictTypeById(dictId); + if (dictDataMapper.selectCount(new LambdaQueryWrapper() + .eq(SysDictData::getDictType, dictType.getDictType())) > 0) { + throw new CustomException(String.format("%1$s已分配,不能删除", dictType.getDictName())); + } + DictUtils.removeDictCache(dictType.getDictType()); + } + baseMapper.deleteBatchIds(Arrays.asList(dictIds)); + } + + /** + * 加载字典缓存数据 + */ + @Override + public void loadingDictCache() { + List dictTypeList = list(); + for (SysDictType dictType : dictTypeList) { + List dictDatas = dictDataMapper.selectDictDataByType(dictType.getDictType()); + DictUtils.setDictCache(dictType.getDictType(), dictDatas); + } + } + + /** + * 清空字典缓存数据 + */ + @Override + public void clearDictCache() { + DictUtils.clearDictCache(); + } + + /** + * 重置字典缓存数据 + */ + @Override + public void resetDictCache() { + clearDictCache(); + loadingDictCache(); + } + + /** + * 新增保存字典类型信息 + * + * @param dict 字典类型信息 + * @return 结果 + */ + @Override + public int insertDictType(SysDictType dict) { + int row = baseMapper.insert(dict); + if (row > 0) { + DictUtils.setDictCache(dict.getDictType(), null); + } + return row; + } + + /** + * 修改保存字典类型信息 + * + * @param dict 字典类型信息 + * @return 结果 + */ + @Override + @Transactional + public int updateDictType(SysDictType dict) { + SysDictType oldDict = getById(dict.getDictId()); + dictDataMapper.update(null, new LambdaUpdateWrapper() + .set(SysDictData::getDictType, dict.getDictType()) + .eq(SysDictData::getDictType, oldDict.getDictType())); + int row = baseMapper.updateById(dict); + if (row > 0) { + List dictDatas = dictDataMapper.selectDictDataByType(dict.getDictType()); + DictUtils.setDictCache(dict.getDictType(), dictDatas); + } + return row; + } + + /** + * 校验字典类型称是否唯一 + * + * @param dict 字典类型 + * @return 结果 + */ + @Override + public String checkDictTypeUnique(SysDictType dict) { + Long dictId = Validator.isNull(dict.getDictId()) ? -1L : dict.getDictId(); + SysDictType dictType = getOne(new LambdaQueryWrapper() + .eq(SysDictType::getDictType, dict.getDictType()) + .last("limit 1")); + if (Validator.isNotNull(dictType) && dictType.getDictId().longValue() != dictId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/impl/SysLogininforServiceImpl.java b/bashi-system/src/main/java/com/bashi/system/service/impl/SysLogininforServiceImpl.java new file mode 100644 index 0000000..18d7e8c --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/impl/SysLogininforServiceImpl.java @@ -0,0 +1,94 @@ +package com.bashi.system.service.impl; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.utils.PageUtils; +import com.bashi.system.domain.SysLogininfor; +import com.bashi.system.mapper.SysLogininforMapper; +import com.bashi.system.service.ISysLogininforService; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 系统访问日志情况信息 服务层处理 + * + * @author duteliang + */ +@Service +public class SysLogininforServiceImpl extends ServiceImpl implements ISysLogininforService { + + @Override + public TableDataInfo selectPageLogininforList(SysLogininfor logininfor) { + Map params = logininfor.getParams(); + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .like(StrUtil.isNotBlank(logininfor.getIpaddr()), SysLogininfor::getIpaddr, logininfor.getIpaddr()) + .eq(StrUtil.isNotBlank(logininfor.getStatus()), SysLogininfor::getStatus, logininfor.getStatus()) + .like(StrUtil.isNotBlank(logininfor.getUserName()), SysLogininfor::getUserName, logininfor.getUserName()) + .apply(Validator.isNotEmpty(params.get("beginTime")), + "date_format(login_time,'%y%m%d') >= date_format({0},'%y%m%d')", + params.get("beginTime")) + .apply(Validator.isNotEmpty(params.get("endTime")), + "date_format(login_time,'%y%m%d') <= date_format({0},'%y%m%d')", + params.get("endTime")); + return PageUtils.buildDataInfo(page(PageUtils.buildPage("info_id","desc"), lqw)); + } + + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + @Override + public void insertLogininfor(SysLogininfor logininfor) { + logininfor.setLoginTime(new Date()); + save(logininfor); + } + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + @Override + public List selectLogininforList(SysLogininfor logininfor) { + Map params = logininfor.getParams(); + return list(new LambdaQueryWrapper() + .like(StrUtil.isNotBlank(logininfor.getIpaddr()),SysLogininfor::getIpaddr,logininfor.getIpaddr()) + .eq(StrUtil.isNotBlank(logininfor.getStatus()),SysLogininfor::getStatus,logininfor.getStatus()) + .like(StrUtil.isNotBlank(logininfor.getUserName()),SysLogininfor::getUserName,logininfor.getUserName()) + .apply(Validator.isNotEmpty(params.get("beginTime")), + "date_format(login_time,'%y%m%d') >= date_format({0},'%y%m%d')", + params.get("beginTime")) + .apply(Validator.isNotEmpty(params.get("endTime")), + "date_format(login_time,'%y%m%d') <= date_format({0},'%y%m%d')", + params.get("endTime")) + .orderByDesc(SysLogininfor::getInfoId)); + } + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return + */ + @Override + public int deleteLogininforByIds(Long[] infoIds) { + return baseMapper.deleteBatchIds(Arrays.asList(infoIds)); + } + + /** + * 清空系统登录日志 + */ + @Override + public void cleanLogininfor() { + remove(new LambdaQueryWrapper<>()); + } +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/impl/SysMenuServiceImpl.java b/bashi-system/src/main/java/com/bashi/system/service/impl/SysMenuServiceImpl.java new file mode 100644 index 0000000..873ccda --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,412 @@ +package com.bashi.system.service.impl; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.common.constant.UserConstants; +import com.bashi.common.core.domain.TreeSelect; +import com.bashi.common.core.domain.entity.SysMenu; +import com.bashi.common.core.domain.entity.SysRole; +import com.bashi.common.core.domain.entity.SysUser; +import com.bashi.common.utils.SecurityUtils; +import com.bashi.system.domain.SysRoleMenu; +import com.bashi.system.domain.vo.MetaVo; +import com.bashi.system.domain.vo.RouterVo; +import com.bashi.system.mapper.SysMenuMapper; +import com.bashi.system.mapper.SysRoleMapper; +import com.bashi.system.mapper.SysRoleMenuMapper; +import com.bashi.system.service.ISysMenuService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 菜单 业务层处理 + * + * @author duteliang + */ +@Service +public class SysMenuServiceImpl extends ServiceImpl implements ISysMenuService { + public static final String PREMISSION_STRING = "perms[\"{0}\"]"; + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + @Override + public List selectMenuList(Long userId) { + return selectMenuList(new SysMenu(), userId); + } + + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + @Override + public List selectMenuList(SysMenu menu, Long userId) { + List menuList = null; + // 管理员显示所有菜单信息 + if (SysUser.isAdmin(userId)) { + menuList = list(new LambdaQueryWrapper() + .like(StrUtil.isNotBlank(menu.getMenuName()),SysMenu::getMenuName,menu.getMenuName()) + .eq(StrUtil.isNotBlank(menu.getVisible()),SysMenu::getVisible,menu.getVisible()) + .eq(StrUtil.isNotBlank(menu.getStatus()),SysMenu::getStatus,menu.getStatus()) + .orderByAsc(SysMenu::getParentId) + .orderByAsc(SysMenu::getOrderNum)); + } else { + menu.getParams().put("userId", userId); + menuList = baseMapper.selectMenuListByUserId(menu); + } + return menuList; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByUserId(Long userId) { + List perms = baseMapper.selectMenuPermsByUserId(userId); + Set permsSet = new HashSet<>(); + for (String perm : perms) { + if (Validator.isNotEmpty(perm)) { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户名称 + * @return 菜单列表 + */ + @Override + public List selectMenuTreeByUserId(Long userId) { + List menus = null; + if (SecurityUtils.isAdmin(userId)) { + menus = baseMapper.selectMenuTreeAll(); + } else { + menus = baseMapper.selectMenuTreeByUserId(userId); + } + return getChildPerms(menus, 0); + } + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + @Override + public List selectMenuListByRoleId(Long roleId) { + SysRole role = roleMapper.selectById(roleId); + return baseMapper.selectMenuListByRoleId(roleId, role.isMenuCheckStrictly()); + } + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + @Override + public List buildMenus(List menus) { + List routers = new LinkedList(); + for (SysMenu menu : menus) { + RouterVo router = new RouterVo(); + router.setHidden("1".equals(menu.getVisible())); + router.setName(getRouteName(menu)); + router.setPath(getRouterPath(menu)); + router.setComponent(getComponent(menu)); + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StrUtil.equals("1", menu.getIsCache()))); + List cMenus = menu.getChildren(); + if (!cMenus.isEmpty() && UserConstants.TYPE_DIR.equals(menu.getMenuType())) { + router.setAlwaysShow(true); + router.setRedirect("noRedirect"); + router.setChildren(buildMenus(cMenus)); + } else if (isMenuFrame(menu)) { + router.setMeta(null); + List childrenList = new ArrayList(); + RouterVo children = new RouterVo(); + children.setPath(menu.getPath()); + children.setComponent(menu.getComponent()); + children.setName(StrUtil.upperFirst(menu.getPath())); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StrUtil.equals("1", menu.getIsCache()))); + childrenList.add(children); + router.setChildren(childrenList); + } + routers.add(router); + } + return routers; + } + + /** + * 构建前端所需要树结构 + * + * @param menus 菜单列表 + * @return 树结构列表 + */ + @Override + public List buildMenuTree(List menus) { + List returnList = new ArrayList(); + List tempList = new ArrayList(); + for (SysMenu dept : menus) { + tempList.add(dept.getMenuId()); + } + for (SysMenu menu : menus) { + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.contains(menu.getParentId())) { + recursionFn(menus, menu); + returnList.add(menu); + } + } + if (returnList.isEmpty()) { + returnList = menus; + } + return returnList; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + @Override + public List buildMenuTreeSelect(List menus) { + List menuTrees = buildMenuTree(menus); + return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + @Override + public SysMenu selectMenuById(Long menuId) { + return getById(menuId); + } + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean hasChildByMenuId(Long menuId) { + long result = count(new LambdaQueryWrapper().eq(SysMenu::getParentId,menuId)); + return result > 0; + } + + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean checkMenuExistRole(Long menuId) { + long result = roleMenuMapper.selectCount(new LambdaQueryWrapper().eq(SysRoleMenu::getMenuId,menuId)); + return result > 0; + } + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public int insertMenu(SysMenu menu) { + return baseMapper.insert(menu); + } + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public int updateMenu(SysMenu menu) { + return baseMapper.updateById(menu); + } + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public int deleteMenuById(Long menuId) { + return baseMapper.deleteById(menuId); + } + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public String checkMenuNameUnique(SysMenu menu) { + Long menuId = Validator.isNull(menu.getMenuId()) ? -1L : menu.getMenuId(); + SysMenu info = getOne(new LambdaQueryWrapper() + .eq(SysMenu::getMenuName,menu.getMenuName()) + .eq(SysMenu::getParentId,menu.getParentId()) + .last("limit 1")); + if (Validator.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 获取路由名称 + * + * @param menu 菜单信息 + * @return 路由名称 + */ + public String getRouteName(SysMenu menu) { + String routerName = StrUtil.upperFirst(menu.getPath()); + // 非外链并且是一级目录(类型为目录) + if (isMenuFrame(menu)) { + routerName = StrUtil.EMPTY; + } + return routerName; + } + + /** + * 获取路由地址 + * + * @param menu 菜单信息 + * @return 路由地址 + */ + public String getRouterPath(SysMenu menu) { + String routerPath = menu.getPath(); + // 非外链并且是一级目录(类型为目录) + if (0 == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType()) + && UserConstants.NO_FRAME.equals(menu.getIsFrame())) { + routerPath = "/" + menu.getPath(); + } + // 非外链并且是一级目录(类型为菜单) + else if (isMenuFrame(menu)) { + routerPath = "/"; + } + return routerPath; + } + + /** + * 获取组件信息 + * + * @param menu 菜单信息 + * @return 组件信息 + */ + public String getComponent(SysMenu menu) { + String component = UserConstants.LAYOUT; + if (StrUtil.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) { + component = menu.getComponent(); + } else if (StrUtil.isEmpty(menu.getComponent()) && isParentView(menu)) { + component = UserConstants.PARENT_VIEW; + } + return component; + } + + /** + * 是否为菜单内部跳转 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isMenuFrame(SysMenu menu) { + return menu.getParentId().intValue() == 0 && UserConstants.TYPE_MENU.equals(menu.getMenuType()) + && menu.getIsFrame().equals(UserConstants.NO_FRAME); + } + + /** + * 是否为parent_view组件 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isParentView(SysMenu menu) { + return menu.getParentId().intValue() != 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType()); + } + + /** + * 根据父节点的ID获取所有子节点 + * + * @param list 分类表 + * @param parentId 传入的父节点ID + * @return String + */ + public List getChildPerms(List list, int parentId) { + List returnList = new ArrayList(); + for (SysMenu t : list) { + // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点 + if (t.getParentId() == parentId) { + recursionFn(list, t); + returnList.add(t); + } + } + return returnList; + } + + /** + * 递归列表 + * + * @param list + * @param t + */ + private void recursionFn(List list, SysMenu t) { + // 得到子节点列表 + List childList = getChildList(list, t); + t.setChildren(childList); + for (SysMenu tChild : childList) { + if (hasChild(list, tChild)) { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + private List getChildList(List list, SysMenu t) { + List tlist = new ArrayList(); + for (SysMenu n : list) { + if (n.getParentId().longValue() == t.getMenuId().longValue()) { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List list, SysMenu t) { + return getChildList(list, t).size() > 0; + } +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/impl/SysNoticeServiceImpl.java b/bashi-system/src/main/java/com/bashi/system/service/impl/SysNoticeServiceImpl.java new file mode 100644 index 0000000..ea128a0 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/impl/SysNoticeServiceImpl.java @@ -0,0 +1,101 @@ +package com.bashi.system.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.utils.PageUtils; +import com.bashi.system.domain.SysNotice; +import com.bashi.system.mapper.SysNoticeMapper; +import com.bashi.system.service.ISysNoticeService; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; + +/** + * 公告 服务层实现 + * + * @author duteliang + */ +@Service +public class SysNoticeServiceImpl extends ServiceImpl implements ISysNoticeService { + + @Override + public TableDataInfo selectPageNoticeList(SysNotice notice) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .like(StrUtil.isNotBlank(notice.getNoticeTitle()), SysNotice::getNoticeTitle, notice.getNoticeTitle()) + .eq(StrUtil.isNotBlank(notice.getNoticeType()), SysNotice::getNoticeType, notice.getNoticeType()) + .like(StrUtil.isNotBlank(notice.getCreateBy()), SysNotice::getCreateBy, notice.getCreateBy()); + return PageUtils.buildDataInfo(page(PageUtils.buildPage(),lqw)); + } + + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + @Override + public SysNotice selectNoticeById(Long noticeId) { + return getById(noticeId); + } + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + @Override + public List selectNoticeList(SysNotice notice) { + return list(new LambdaQueryWrapper() + .like(StrUtil.isNotBlank(notice.getNoticeTitle()),SysNotice::getNoticeTitle,notice.getNoticeTitle()) + .eq(StrUtil.isNotBlank(notice.getNoticeType()),SysNotice::getNoticeType,notice.getNoticeType()) + .like(StrUtil.isNotBlank(notice.getCreateBy()),SysNotice::getCreateBy,notice.getCreateBy())); + } + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + @Override + public int insertNotice(SysNotice notice) { + return baseMapper.insert(notice); + } + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + @Override + public int updateNotice(SysNotice notice) { + return baseMapper.updateById(notice); + } + + /** + * 删除公告对象 + * + * @param noticeId 公告ID + * @return 结果 + */ + @Override + public int deleteNoticeById(Long noticeId) { + return baseMapper.deleteById(noticeId); + } + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + @Override + public int deleteNoticeByIds(Long[] noticeIds) { + return baseMapper.deleteBatchIds(Arrays.asList(noticeIds)); + } +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/impl/SysOperLogServiceImpl.java b/bashi-system/src/main/java/com/bashi/system/service/impl/SysOperLogServiceImpl.java new file mode 100644 index 0000000..7c0cdfb --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/impl/SysOperLogServiceImpl.java @@ -0,0 +1,122 @@ +package com.bashi.system.service.impl; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.utils.PageUtils; +import com.bashi.system.domain.SysOperLog; +import com.bashi.system.mapper.SysOperLogMapper; +import com.bashi.system.service.ISysOperLogService; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 操作日志 服务层处理 + * + * @author duteliang + */ +@Service +public class SysOperLogServiceImpl extends ServiceImpl implements ISysOperLogService { + + @Override + public TableDataInfo selectPageOperLogList(SysOperLog operLog) { + Map params = operLog.getParams(); + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .like(StrUtil.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle()) + .eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0, + SysOperLog::getBusinessType, operLog.getBusinessType()) + .func(f -> { + if (ArrayUtil.isNotEmpty(operLog.getBusinessTypes())) { + f.in(SysOperLog::getBusinessType, Arrays.asList(operLog.getBusinessTypes())); + } + }) + .eq(operLog.getStatus() != null && operLog.getStatus() > 0, + SysOperLog::getStatus, operLog.getStatus()) + .like(StrUtil.isNotBlank(operLog.getOperName()), SysOperLog::getOperName, operLog.getOperName()) + .apply(Validator.isNotEmpty(params.get("beginTime")), + "date_format(oper_time,'%y%m%d') >= date_format({0},'%y%m%d')", + params.get("beginTime")) + .apply(Validator.isNotEmpty(params.get("endTime")), + "date_format(oper_time,'%y%m%d') <= date_format({0},'%y%m%d')", + params.get("endTime")); + return PageUtils.buildDataInfo(page(PageUtils.buildPage("oper_id","desc"), lqw)); + } + + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + @Override + public void insertOperlog(SysOperLog operLog) { + operLog.setOperTime(new Date()); + save(operLog); + } + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + @Override + public List selectOperLogList(SysOperLog operLog) { + Map params = operLog.getParams(); + return list(new LambdaQueryWrapper() + .like(StrUtil.isNotBlank(operLog.getTitle()),SysOperLog::getTitle,operLog.getTitle()) + .eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0, + SysOperLog::getBusinessType,operLog.getBusinessType()) + .func(f -> { + if (ArrayUtil.isNotEmpty(operLog.getBusinessTypes())){ + f.in(SysOperLog::getBusinessType, Arrays.asList(operLog.getBusinessTypes())); + } + }) + .eq(operLog.getStatus() != null && operLog.getStatus() > 0, + SysOperLog::getStatus,operLog.getStatus()) + .like(StrUtil.isNotBlank(operLog.getOperName()),SysOperLog::getOperName,operLog.getOperName()) + .apply(Validator.isNotEmpty(params.get("beginTime")), + "date_format(oper_time,'%y%m%d') >= date_format({0},'%y%m%d')", + params.get("beginTime")) + .apply(Validator.isNotEmpty(params.get("endTime")), + "date_format(oper_time,'%y%m%d') <= date_format({0},'%y%m%d')", + params.get("endTime")) + .orderByDesc(SysOperLog::getOperId)); + } + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + @Override + public int deleteOperLogByIds(Long[] operIds) { + return baseMapper.deleteBatchIds(Arrays.asList(operIds)); + } + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + @Override + public SysOperLog selectOperLogById(Long operId) { + return getById(operId); + } + + /** + * 清空操作日志 + */ + @Override + public void cleanOperLog() { + remove(new LambdaQueryWrapper<>()); + } +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/impl/SysPostServiceImpl.java b/bashi-system/src/main/java/com/bashi/system/service/impl/SysPostServiceImpl.java new file mode 100644 index 0000000..a7d869b --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/impl/SysPostServiceImpl.java @@ -0,0 +1,183 @@ +package com.bashi.system.service.impl; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.common.constant.UserConstants; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.exception.CustomException; +import com.bashi.common.utils.PageUtils; +import com.bashi.system.domain.SysPost; +import com.bashi.system.domain.SysUserPost; +import com.bashi.system.mapper.SysPostMapper; +import com.bashi.system.mapper.SysUserPostMapper; +import com.bashi.system.service.ISysPostService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; + +/** + * 岗位信息 服务层处理 + * + * @author duteliang + */ +@Service +public class SysPostServiceImpl extends ServiceImpl implements ISysPostService { + + @Autowired + private SysUserPostMapper userPostMapper; + + @Override + public TableDataInfo selectPagePostList(SysPost post) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .like(StrUtil.isNotBlank(post.getPostCode()), SysPost::getPostCode, post.getPostCode()) + .eq(StrUtil.isNotBlank(post.getStatus()), SysPost::getStatus, post.getStatus()) + .like(StrUtil.isNotBlank(post.getPostName()), SysPost::getPostName, post.getPostName()); + return PageUtils.buildDataInfo(page(PageUtils.buildPage(),lqw)); + } + + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位信息集合 + */ + @Override + public List selectPostList(SysPost post) { + return list(new LambdaQueryWrapper() + .like(StrUtil.isNotBlank(post.getPostCode()), SysPost::getPostCode, post.getPostCode()) + .eq(StrUtil.isNotBlank(post.getStatus()), SysPost::getStatus, post.getStatus()) + .like(StrUtil.isNotBlank(post.getPostName()), SysPost::getPostName, post.getPostName())); + } + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + @Override + public List selectPostAll() { + return list(); + } + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + @Override + public SysPost selectPostById(Long postId) { + return getById(postId); + } + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + @Override + public List selectPostListByUserId(Long userId) { + return baseMapper.selectPostListByUserId(userId); + } + + /** + * 校验岗位名称是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public String checkPostNameUnique(SysPost post) { + Long postId = Validator.isNull(post.getPostId()) ? -1L : post.getPostId(); + SysPost info = getOne(new LambdaQueryWrapper() + .eq(SysPost::getPostName, post.getPostName()).last("limit 1")); + if (Validator.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验岗位编码是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public String checkPostCodeUnique(SysPost post) { + Long postId = Validator.isNull(post.getPostId()) ? -1L : post.getPostId(); + SysPost info = getOne(new LambdaQueryWrapper() + .eq(SysPost::getPostCode, post.getPostCode()).last("limit 1")); + if (Validator.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public long countUserPostById(Long postId) { + return userPostMapper.selectCount(new LambdaQueryWrapper().eq(SysUserPost::getPostId,postId)); + } + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public int deletePostById(Long postId) { + return baseMapper.deleteById(postId); + } + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + * @throws Exception 异常 + */ + @Override + public int deletePostByIds(Long[] postIds) { + for (Long postId : postIds) { + SysPost post = selectPostById(postId); + if (countUserPostById(postId) > 0) { + throw new CustomException(String.format("%1$s已分配,不能删除", post.getPostName())); + } + } + return baseMapper.deleteBatchIds(Arrays.asList(postIds)); + } + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public int insertPost(SysPost post) { + return baseMapper.insert(post); + } + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public int updatePost(SysPost post) { + return baseMapper.updateById(post); + } +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/impl/SysRoleServiceImpl.java b/bashi-system/src/main/java/com/bashi/system/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..6035a96 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,308 @@ +package com.bashi.system.service.impl; + +import cn.hutool.core.lang.Validator; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.common.annotation.DataScope; +import com.bashi.common.constant.UserConstants; +import com.bashi.common.core.domain.entity.SysRole; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.exception.CustomException; +import com.bashi.common.utils.PageUtils; +import com.bashi.common.utils.spring.SpringUtils; +import com.bashi.system.domain.SysRoleDept; +import com.bashi.system.domain.SysRoleMenu; +import com.bashi.system.domain.SysUserRole; +import com.bashi.system.mapper.SysRoleDeptMapper; +import com.bashi.system.mapper.SysRoleMapper; +import com.bashi.system.mapper.SysRoleMenuMapper; +import com.bashi.system.mapper.SysUserRoleMapper; +import com.bashi.system.service.ISysRoleService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +/** + * 角色 业务层处理 + * + * @author duteliang + */ +@Service +public class SysRoleServiceImpl extends ServiceImpl implements ISysRoleService { + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + @Autowired + private SysRoleDeptMapper roleDeptMapper; + + @Override + @DataScope(deptAlias = "d") + public TableDataInfo selectPageRoleList(SysRole role) { + return PageUtils.buildDataInfo(baseMapper.selectPageRoleList(PageUtils.buildPage(), role)); + } + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + @Override + @DataScope(deptAlias = "d") + public List selectRoleList(SysRole role) { + return baseMapper.selectRoleList(role); + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set selectRolePermissionByUserId(Long userId) { + List perms = baseMapper.selectRolePermissionByUserId(userId); + Set permsSet = new HashSet<>(); + for (SysRole perm : perms) { + if (Validator.isNotNull(perm)) { + permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(","))); + } + } + return permsSet; + } + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + @Override + public List selectRoleAll() { + return SpringUtils.getAopProxy(this).selectRoleList(new SysRole()); + } + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + @Override + public List selectRoleListByUserId(Long userId) { + return baseMapper.selectRoleListByUserId(userId); + } + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + @Override + public SysRole selectRoleById(Long roleId) { + return getById(roleId); + } + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public String checkRoleNameUnique(SysRole role) { + Long roleId = Validator.isNull(role.getRoleId()) ? -1L : role.getRoleId(); + SysRole info = getOne(new LambdaQueryWrapper() + .eq(SysRole::getRoleName, role.getRoleName()).last("limit 1")); + if (Validator.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public String checkRoleKeyUnique(SysRole role) { + Long roleId = Validator.isNull(role.getRoleId()) ? -1L : role.getRoleId(); + SysRole info = getOne(new LambdaQueryWrapper() + .eq(SysRole::getRoleKey, role.getRoleKey()).last("limit 1")); + if (Validator.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + @Override + public void checkRoleAllowed(SysRole role) { + if (Validator.isNotNull(role.getRoleId()) && role.isAdmin()) { + throw new CustomException("不允许操作超级管理员角色"); + } + } + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + public long countUserRoleByRoleId(Long roleId) { + return userRoleMapper.selectCount(new LambdaQueryWrapper().eq(SysUserRole::getRoleId, roleId)); + } + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int insertRole(SysRole role) { + // 新增角色信息 + baseMapper.insert(role); + return insertRoleMenu(role); + } + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int updateRole(SysRole role) { + // 修改角色信息 + baseMapper.updateById(role); + // 删除角色与菜单关联 + roleMenuMapper.delete(new LambdaQueryWrapper().eq(SysRoleMenu::getRoleId, role.getRoleId())); + return insertRoleMenu(role); + } + + /** + * 修改角色状态 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public int updateRoleStatus(SysRole role) { + return baseMapper.updateById(role); + } + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int authDataScope(SysRole role) { + // 修改角色信息 + baseMapper.updateById(role); + // 删除角色与部门关联 + roleDeptMapper.delete(new LambdaQueryWrapper().eq(SysRoleDept::getRoleId, role.getRoleId())); + // 新增角色和部门信息(数据权限) + return insertRoleDept(role); + } + + /** + * 新增角色菜单信息 + * + * @param role 角色对象 + */ + public int insertRoleMenu(SysRole role) { + int rows = 1; + // 新增用户与角色管理 + List list = new ArrayList(); + for (Long menuId : role.getMenuIds()) { + SysRoleMenu rm = new SysRoleMenu(); + rm.setRoleId(role.getRoleId()); + rm.setMenuId(menuId); + list.add(rm); + } + if (list.size() > 0) { + rows = roleMenuMapper.insertAll(list); + } + return rows; + } + + /** + * 新增角色部门信息(数据权限) + * + * @param role 角色对象 + */ + public int insertRoleDept(SysRole role) { + int rows = 1; + // 新增角色与部门(数据权限)管理 + List list = new ArrayList(); + for (Long deptId : role.getDeptIds()) { + SysRoleDept rd = new SysRoleDept(); + rd.setRoleId(role.getRoleId()); + rd.setDeptId(deptId); + list.add(rd); + } + if (list.size() > 0) { + rows = roleDeptMapper.insertAll(list); + } + return rows; + } + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + @Transactional + public int deleteRoleById(Long roleId) { + // 删除角色与菜单关联 + roleMenuMapper.delete(new LambdaQueryWrapper().eq(SysRoleMenu::getRoleId, roleId)); + // 删除角色与部门关联 + roleDeptMapper.delete(new LambdaQueryWrapper().eq(SysRoleDept::getRoleId, roleId)); + return baseMapper.deleteById(roleId); + } + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + @Override + @Transactional + public int deleteRoleByIds(Long[] roleIds) { + for (Long roleId : roleIds) { + checkRoleAllowed(new SysRole(roleId)); + SysRole role = selectRoleById(roleId); + if (countUserRoleByRoleId(roleId) > 0) { + throw new CustomException(String.format("%1$s已分配,不能删除", role.getRoleName())); + } + } + List ids = Arrays.asList(roleIds); + // 删除角色与菜单关联 + roleMenuMapper.delete(new LambdaQueryWrapper().in(SysRoleMenu::getRoleId, ids)); + // 删除角色与部门关联 + roleDeptMapper.delete(new LambdaQueryWrapper().in(SysRoleDept::getRoleId, ids)); + return baseMapper.deleteBatchIds(ids); + } +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/impl/SysUserOnlineServiceImpl.java b/bashi-system/src/main/java/com/bashi/system/service/impl/SysUserOnlineServiceImpl.java new file mode 100644 index 0000000..2cc7ee9 --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/impl/SysUserOnlineServiceImpl.java @@ -0,0 +1,87 @@ +package com.bashi.system.service.impl; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import com.bashi.common.core.domain.model.LoginUser; +import com.bashi.system.domain.SysUserOnline; +import com.bashi.system.service.ISysUserOnlineService; +import org.springframework.stereotype.Service; + +/** + * 在线用户 服务层处理 + * + * @author duteliang + */ +@Service +public class SysUserOnlineServiceImpl implements ISysUserOnlineService { + /** + * 通过登录地址查询信息 + * + * @param ipaddr 登录地址 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user) { + if (StrUtil.equals(ipaddr, user.getIpaddr())) { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 通过用户名称查询信息 + * + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByUserName(String userName, LoginUser user) { + if (StrUtil.equals(userName, user.getUsername())) { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 通过登录地址/用户名称查询信息 + * + * @param ipaddr 登录地址 + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user) { + if (StrUtil.equals(ipaddr, user.getIpaddr()) && StrUtil.equals(userName, user.getUsername())) { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 设置在线用户信息 + * + * @param user 用户信息 + * @return 在线用户 + */ + @Override + public SysUserOnline loginUserToUserOnline(LoginUser user) { + if (Validator.isNull(user) || Validator.isNull(user.getUser())) { + return null; + } + SysUserOnline sysUserOnline = new SysUserOnline(); + sysUserOnline.setTokenId(user.getToken()); + sysUserOnline.setUserName(user.getUsername()); + sysUserOnline.setIpaddr(user.getIpaddr()); + sysUserOnline.setLoginLocation(user.getLoginLocation()); + sysUserOnline.setBrowser(user.getBrowser()); + sysUserOnline.setOs(user.getOs()); + sysUserOnline.setLoginTime(user.getLoginTime()); + if (Validator.isNotNull(user.getUser().getDept())) { + sysUserOnline.setDeptName(user.getUser().getDept().getDeptName()); + } + return sysUserOnline; + } +} diff --git a/bashi-system/src/main/java/com/bashi/system/service/impl/SysUserServiceImpl.java b/bashi-system/src/main/java/com/bashi/system/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..0584c8e --- /dev/null +++ b/bashi-system/src/main/java/com/bashi/system/service/impl/SysUserServiceImpl.java @@ -0,0 +1,442 @@ +package com.bashi.system.service.impl; + +import cn.hutool.core.lang.Validator; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bashi.common.annotation.DataScope; +import com.bashi.common.constant.UserConstants; +import com.bashi.common.core.domain.entity.SysRole; +import com.bashi.common.core.domain.entity.SysUser; +import com.bashi.common.core.page.TableDataInfo; +import com.bashi.common.exception.CustomException; +import com.bashi.common.utils.PageUtils; +import com.bashi.common.utils.SecurityUtils; +import com.bashi.system.domain.SysPost; +import com.bashi.system.domain.SysUserPost; +import com.bashi.system.domain.SysUserRole; +import com.bashi.system.mapper.*; +import com.bashi.system.service.ISysConfigService; +import com.bashi.system.service.ISysUserService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 用户 业务层处理 + * + * @author duteliang + */ +@Slf4j +@Service +public class SysUserServiceImpl extends ServiceImpl implements ISysUserService { + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysPostMapper postMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + @Autowired + private SysUserPostMapper userPostMapper; + + @Autowired + private ISysConfigService configService; + + @Override + @DataScope(deptAlias = "d", userAlias = "u", isUser = true) + public TableDataInfo selectPageUserList(SysUser user) { + return PageUtils.buildDataInfo(baseMapper.selectPageUserList(PageUtils.buildPage(), user)); + } + + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u", isUser = true) + public List selectUserList(SysUser user) { + return baseMapper.selectUserList(user); + } + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + @Override + public SysUser selectUserByUserName(String userName) { + return baseMapper.selectUserByUserName(userName); + } + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + @Override + public SysUser selectUserById(Long userId) { + return baseMapper.selectUserById(userId); + } + + /** + * 查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserRoleGroup(String userName) { + List list = roleMapper.selectRolesByUserName(userName); + StringBuilder idsStr = new StringBuilder(); + for (SysRole role : list) { + idsStr.append(role.getRoleName()).append(","); + } + if (Validator.isNotEmpty(idsStr.toString())) { + return idsStr.substring(0, idsStr.length() - 1); + } + return idsStr.toString(); + } + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserPostGroup(String userName) { + List list = postMapper.selectPostsByUserName(userName); + StringBuilder idsStr = new StringBuilder(); + for (SysPost post : list) { + idsStr.append(post.getPostName()).append(","); + } + if (Validator.isNotEmpty(idsStr.toString())) { + return idsStr.substring(0, idsStr.length() - 1); + } + return idsStr.toString(); + } + + /** + * 校验用户名称是否唯一 + * + * @param userName 用户名称 + * @return 结果 + */ + @Override + public String checkUserNameUnique(String userName) { + long count = count(new LambdaQueryWrapper().eq(SysUser::getUserName, userName).last("limit 1")); + if (count > 0) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public String checkPhoneUnique(SysUser user) { + Long userId = Validator.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = getOne(new LambdaQueryWrapper() + .select(SysUser::getUserId, SysUser::getPhonenumber) + .eq(SysUser::getPhonenumber, user.getPhonenumber()).last("limit 1")); + if (Validator.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public String checkEmailUnique(SysUser user) { + Long userId = Validator.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = getOne(new LambdaQueryWrapper() + .select(SysUser::getUserId, SysUser::getEmail) + .eq(SysUser::getEmail, user.getEmail()).last("limit 1")); + if (Validator.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + @Override + public void checkUserAllowed(SysUser user) { + if (Validator.isNotNull(user.getUserId()) && user.isAdmin()) { + throw new CustomException("不允许操作超级管理员用户"); + } + } + + /** + * 新增保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional + public int insertUser(SysUser user) { + // 新增用户信息 + int rows = baseMapper.insert(user); + // 新增用户岗位关联 + insertUserPost(user); + // 新增用户与角色管理 + insertUserRole(user); + return rows; + } + + /** + * 修改保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional + public int updateUser(SysUser user) { + Long userId = user.getUserId(); + // 删除用户与角色关联 + userRoleMapper.delete(new LambdaQueryWrapper().eq(SysUserRole::getUserId,userId)); + // 新增用户与角色管理 + insertUserRole(user); + // 删除用户与岗位关联 + userPostMapper.delete(new LambdaQueryWrapper().eq(SysUserPost::getUserId,userId)); + // 新增用户与岗位管理 + insertUserPost(user); + return baseMapper.updateById(user); + } + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserStatus(SysUser user) { + return baseMapper.updateById(user); + } + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserProfile(SysUser user) { + return baseMapper.updateById(user); + } + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + @Override + public boolean updateUserAvatar(String userName, String avatar) { + return baseMapper.update(null, + new LambdaUpdateWrapper() + .set(SysUser::getAvatar,avatar) + .eq(SysUser::getUserName,userName)) > 0; + } + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int resetPwd(SysUser user) { + return baseMapper.updateById(user); + } + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + @Override + public int resetUserPwd(String userName, String password) { + return baseMapper.update(null, + new LambdaUpdateWrapper() + .set(SysUser::getPassword,password) + .eq(SysUser::getUserName,userName)); + } + + /** + * 新增用户角色信息 + * + * @param user 用户对象 + */ + public void insertUserRole(SysUser user) { + Long[] roles = user.getRoleIds(); + if (Validator.isNotNull(roles)) { + // 新增用户与角色管理 + List list = new ArrayList(); + for (Long roleId : roles) { + SysUserRole ur = new SysUserRole(); + ur.setUserId(user.getUserId()); + ur.setRoleId(roleId); + list.add(ur); + } + if (list.size() > 0) { + userRoleMapper.insertAll(list); + } + } + } + + /** + * 新增用户岗位信息 + * + * @param user 用户对象 + */ + public void insertUserPost(SysUser user) { + Long[] posts = user.getPostIds(); + if (Validator.isNotNull(posts)) { + // 新增用户与岗位管理 + List list = new ArrayList(); + for (Long postId : posts) { + SysUserPost up = new SysUserPost(); + up.setUserId(user.getUserId()); + up.setPostId(postId); + list.add(up); + } + if (list.size() > 0) { + userPostMapper.insertAll(list); + } + } + } + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + @Override + @Transactional + public int deleteUserById(Long userId) { + // 删除用户与角色关联 + userRoleMapper.delete(new LambdaQueryWrapper().eq(SysUserRole::getUserId,userId)); + // 删除用户与岗位表 + userPostMapper.delete(new LambdaQueryWrapper().eq(SysUserPost::getUserId,userId)); + return baseMapper.deleteById(userId); + } + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + @Override + @Transactional + public int deleteUserByIds(Long[] userIds) { + for (Long userId : userIds) { + checkUserAllowed(new SysUser(userId)); + } + List ids = Arrays.asList(userIds); + // 删除用户与角色关联 + userRoleMapper.delete(new LambdaQueryWrapper().in(SysUserRole::getUserId,ids)); + // 删除用户与岗位表 + userPostMapper.delete(new LambdaQueryWrapper().in(SysUserPost::getUserId,ids)); + return baseMapper.deleteBatchIds(ids); + } + + /** + * 导入用户数据 + * + * @param userList 用户数据列表 + * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 + * @param operName 操作用户 + * @return 结果 + */ + @Override + public String importUser(List userList, Boolean isUpdateSupport, String operName) { + if (Validator.isNull(userList) || userList.size() == 0) { + throw new CustomException("导入用户数据不能为空!"); + } + int successNum = 0; + int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + String password = configService.selectConfigByKey("sys.user.initPassword"); + for (SysUser user : userList) { + try { + // 验证是否存在这个用户 + SysUser u = baseMapper.selectUserByUserName(user.getUserName()); + if (Validator.isNull(u)) { + user.setPassword(SecurityUtils.encryptPassword(password)); + user.setCreateBy(operName); + this.insertUser(user); + successNum++; + successMsg.append("
" + successNum + "、账号 " + user.getUserName() + " 导入成功"); + } else if (isUpdateSupport) { + user.setUpdateBy(operName); + this.updateUser(user); + successNum++; + successMsg.append("
" + successNum + "、账号 " + user.getUserName() + " 更新成功"); + } else { + failureNum++; + failureMsg.append("
" + failureNum + "、账号 " + user.getUserName() + " 已存在"); + } + } catch (Exception e) { + failureNum++; + String msg = "
" + failureNum + "、账号 " + user.getUserName() + " 导入失败:"; + failureMsg.append(msg + e.getMessage()); + log.error(msg, e); + } + } + if (failureNum > 0) { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new CustomException(failureMsg.toString()); + } else { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } + + @Override + public SysUser selectUserByMobile(String mobile) { + return baseMapper.selectUserByMobile(mobile); + } + + @Override + public SysUser selectUserByOpenId(String openId) { + return baseMapper.selectUserByOpenId(openId); + } + +} diff --git a/bashi-system/src/main/resources/mapper/system/SysConfigMapper.xml b/bashi-system/src/main/resources/mapper/system/SysConfigMapper.xml new file mode 100644 index 0000000..b0fd545 --- /dev/null +++ b/bashi-system/src/main/resources/mapper/system/SysConfigMapper.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + diff --git a/bashi-system/src/main/resources/mapper/system/SysDeptMapper.xml b/bashi-system/src/main/resources/mapper/system/SysDeptMapper.xml new file mode 100644 index 0000000..dfbdf17 --- /dev/null +++ b/bashi-system/src/main/resources/mapper/system/SysDeptMapper.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time + from sys_dept d + + + + + + + + update sys_dept set ancestors = + + when #{item.deptId} then #{item.ancestors} + + where dept_id in + + #{item.deptId} + + + + diff --git a/bashi-system/src/main/resources/mapper/system/SysDictDataMapper.xml b/bashi-system/src/main/resources/mapper/system/SysDictDataMapper.xml new file mode 100644 index 0000000..c41b4f7 --- /dev/null +++ b/bashi-system/src/main/resources/mapper/system/SysDictDataMapper.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/bashi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml b/bashi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml new file mode 100644 index 0000000..432c805 --- /dev/null +++ b/bashi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + diff --git a/bashi-system/src/main/resources/mapper/system/SysLogininforMapper.xml b/bashi-system/src/main/resources/mapper/system/SysLogininforMapper.xml new file mode 100644 index 0000000..9e8ddd7 --- /dev/null +++ b/bashi-system/src/main/resources/mapper/system/SysLogininforMapper.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + diff --git a/bashi-system/src/main/resources/mapper/system/SysMenuMapper.xml b/bashi-system/src/main/resources/mapper/system/SysMenuMapper.xml new file mode 100644 index 0000000..5354270 --- /dev/null +++ b/bashi-system/src/main/resources/mapper/system/SysMenuMapper.xml @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + select menu_id, + menu_name, + parent_id, + order_num, + path, + component, + is_frame, + is_cache, + menu_type, + visible, + status, + ifnull(perms, '') as perms, + icon, + create_time + from sys_menu + + + + + + + + + + + + + + + + diff --git a/bashi-system/src/main/resources/mapper/system/SysNoticeMapper.xml b/bashi-system/src/main/resources/mapper/system/SysNoticeMapper.xml new file mode 100644 index 0000000..0e71033 --- /dev/null +++ b/bashi-system/src/main/resources/mapper/system/SysNoticeMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/bashi-system/src/main/resources/mapper/system/SysOperLogMapper.xml b/bashi-system/src/main/resources/mapper/system/SysOperLogMapper.xml new file mode 100644 index 0000000..17f205b --- /dev/null +++ b/bashi-system/src/main/resources/mapper/system/SysOperLogMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bashi-system/src/main/resources/mapper/system/SysPostMapper.xml b/bashi-system/src/main/resources/mapper/system/SysPostMapper.xml new file mode 100644 index 0000000..0bff5fe --- /dev/null +++ b/bashi-system/src/main/resources/mapper/system/SysPostMapper.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + select post_id, + post_code, + post_name, + post_sort, + status, + create_by, + create_time, + remark + from sys_post + + + + + + + diff --git a/bashi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml b/bashi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml new file mode 100644 index 0000000..62cbe81 --- /dev/null +++ b/bashi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/bashi-system/src/main/resources/mapper/system/SysRoleMapper.xml b/bashi-system/src/main/resources/mapper/system/SysRoleMapper.xml new file mode 100644 index 0000000..05ef675 --- /dev/null +++ b/bashi-system/src/main/resources/mapper/system/SysRoleMapper.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + select distinct r.role_id, + r.role_name, + r.role_key, + r.role_sort, + r.data_scope, + r.menu_check_strictly, + r.dept_check_strictly, + r.status, + r.del_flag, + r.create_time, + r.remark + from sys_role r + left join sys_user_role ur on ur.role_id = r.role_id + left join sys_user u on u.user_id = ur.user_id + left join sys_dept d on u.dept_id = d.dept_id + + + + + + + + + + + + + diff --git a/bashi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml b/bashi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml new file mode 100644 index 0000000..1a72add --- /dev/null +++ b/bashi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/bashi-system/src/main/resources/mapper/system/SysUserMapper.xml b/bashi-system/src/main/resources/mapper/system/SysUserMapper.xml new file mode 100644 index 0000000..3ac99bc --- /dev/null +++ b/bashi-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select u.user_id, + u.dept_id, + u.user_name, + u.nick_name, + u.email, + u.avatar, + u.phonenumber, + u.password, + u.sex, + u.status, + u.del_flag, + u.login_ip, + u.login_date, + u.create_by, + u.create_time, + u.remark, + d.dept_id, + d.parent_id, + d.dept_name, + d.order_num, + d.leader, + d.status as dept_status, + r.role_id, + r.role_name, + r.role_key, + r.role_sort, + r.data_scope, + r.status as role_status + from sys_user u + left join sys_dept d on u.dept_id = d.dept_id + left join sys_user_role ur on u.user_id = ur.user_id + left join sys_role r on r.role_id = ur.role_id + + + + + + + + + + + + + + + diff --git a/bashi-system/src/main/resources/mapper/system/SysUserPostMapper.xml b/bashi-system/src/main/resources/mapper/system/SysUserPostMapper.xml new file mode 100644 index 0000000..fb130c5 --- /dev/null +++ b/bashi-system/src/main/resources/mapper/system/SysUserPostMapper.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/bashi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml b/bashi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml new file mode 100644 index 0000000..f2ba2b7 --- /dev/null +++ b/bashi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/hd_bg.png b/hd_bg.png new file mode 100644 index 0000000..7e9e92d Binary files /dev/null and b/hd_bg.png differ diff --git a/hd_bg1.png b/hd_bg1.png new file mode 100644 index 0000000..d859058 Binary files /dev/null and b/hd_bg1.png differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..4a34406 --- /dev/null +++ b/pom.xml @@ -0,0 +1,292 @@ + + + 4.0.0 + + com.bashi + bashi + 2.4.0 + + bashi + + + 2.4.0 + 2.4.7 + UTF-8 + UTF-8 + 1.8 + 3.1.1 + 1.2.6 + 3.0.2 + 4.1.2 + 1.7 + 0.9.1 + 3.4.3.4 + 5.8.22 + 3.0.3 + 11.0 + 2.4.1 + 3.15.2 + 2.2.1 + 3.4.0 + 1.2.79 + + + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + com.alibaba + druid-spring-boot-starter + ${druid.version} + + + + com.github.xiaoymin + knife4j-spring-boot-starter + ${knife4j.version} + + + + + org.apache.poi + poi-ooxml + ${poi.version} + + + + + org.apache.velocity + velocity + ${velocity.version} + + + + + io.jsonwebtoken + jjwt + ${jwt.version} + + + + + com.baomidou + dynamic-datasource-spring-boot-starter + ${datasource.version} + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + com.baomidou + mybatis-plus-extension + ${mybatis-plus.version} + + + + cn.hutool + hutool-all + ${hutool.version} + + + + org.springframework.cloud + spring-cloud-starter-openfeign + ${feign.version} + + + + io.github.openfeign + feign-okhttp + ${feign-okhttp.version} + + + + de.codecentric + spring-boot-admin-starter-server + ${spring-boot-admin.version} + + + de.codecentric + spring-boot-admin-starter-client + ${spring-boot-admin.version} + + + + + org.redisson + redisson-spring-boot-starter + ${redisson.version} + + + com.baomidou + lock4j-redisson-spring-boot-starter + ${lock4j.version} + + + + com.bashi + bashi-quartz + 2.4.0 + + + + + com.bashi + bashi-generator + 2.4.0 + + + + + com.bashi + bashi-framework + 2.4.0 + + + + + com.bashi + bashi-system + 2.4.0 + + + + + com.bashi + bashi-common + 2.4.0 + + + + + com.alibaba + fastjson + ${fastjson.version} + + + + + + + bashi-admin + bashi-framework + bashi-system + bashi-quartz + bashi-generator + bashi-common + bashi-dk + + pom + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + + + + src/main/resources + + true + + + + + + + public + aliyun nexus + http://maven.aliyun.com/nexus/content/groups/public/ + + true + + + + + + + public + aliyun nexus + http://maven.aliyun.com/nexus/content/groups/public/ + + true + + + false + + + + + + + local + + + local + debug + + + + dev + + + dev + debug + + + + true + + + + prod + + prod + info + + + + + + jdk8 + + true + 1.8 + + + 1.8 + + + + + diff --git a/sql/bus.sql b/sql/bus.sql new file mode 100644 index 0000000..35b9731 --- /dev/null +++ b/sql/bus.sql @@ -0,0 +1,351 @@ +CREATE TABLE `bus_order_count` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT comment 'id', + `count_date` date not NULL comment '订单金额', + `order_amount` bigint(11) not NULL DEFAULT 0 comment '订单金额', + `vip_amount` bigint(11) not NULL DEFAULT 0 comment '会员金额', + `qualification_amount` bigint(11) not NULL DEFAULT 0 comment '证书办理金额', + `order_num` bigint(11) not null DEFAULT 0 comment '订单销售量', + `vip_num` bigint(11) not null DEFAULT 0 comment 'vip销售量', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC comment '订单统计接口'; + +CREATE TABLE `bus_shop_amount_logs` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT comment 'id', + `shop_id` bigint(20) NOT NULL comment '商家ID', + `incs_amount` bigint(20) NOT NULL comment '修改金额', + `operator_id` bigint(20) NOT NULL comment '操作人ID', + `operator_name` varchar(200) NOT NULL comment '操作人', + `operator_remark` varchar(255) comment '操作说明', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC comment '商家修改初始化金额操作记录'; + + +CREATE TABLE `bus_order_logs` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT comment 'id', + `order_id` bigint(20) NOT NULL comment '订单id', + `user_id` bigint(20) NOT NULL comment '用户id', + `user_name` varchar(200) NOT NULL comment '操作人', + `operator_step` varchar(255) comment '操作说明', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC comment '订单操作说明'; + +CREATE TABLE `bus_system_config` ( + `id` bigint(20) NOT NULL, + `vip_auth` bit(1) DEFAULT 1 comment '是否开启Vip权限-关闭vip权限,则视频和图像可直接观看', + `default_order_commit` int(2) DEFAULT 10 COMMENT '默认的平台抽佣比例 1-100', + `platform_phone` varchar(36) comment '平台电话', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC comment '系统设置'; + +CREATE TABLE `bus_evaluation` ( + `id` bigint(20) NOT NULL, + `order_id` bigint(20) DEFAULT NULL comment '订单id', + `order_no` varchar(64) DEFAULT NULL comment '订单编号', + `service_item_id` bigint(20) default null comment '服务ID', + `content` varchar(500) DEFAULT NULL comment '评价内容-不超过500字符', + `status` int(11) NOT NULL DEFAULT 1 COMMENT '状态 0-停用 1-启用', + `customer_id` bigint(20) DEFAULT NULL COMMENT '客户ID', + `customer_avatar` varchar(255) default null comment '客户头像', + `customer_nick_name` varchar(255) default null comment '客户昵称', + `shop_id` bigint(20) DEFAULT NULL comment '商家ID', + `mechanic_id` bigint(20) DEFAULT NULL comment '技师ID', + `mechanic_nick_name` bigint(20) DEFAULT NULL comment '技师昵称', + `score` int(1) DEFAULT NULL comment '分数 1-5', + `create_time` datetime not null default now(), + `virtual` bit(1) not NULL default 0 comment '是否为虚拟评价', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC comment '评价页面'; + +CREATE TABLE `bus_coupon` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT comment 'id', + `name` varchar(255) DEFAULT NULL comment '优惠券名称', + `image` varchar(1024) DEFAULT NULL comment '优惠券图片', + `note` varchar(255) DEFAULT NULL comment '优惠券说明', + `value` bigint(11) not null comment '优惠券金额', + `use_condition` bigint(20) not null comment '优惠券使用条件', + `status` int(2) not null default 1 comment '状态 0-停用 1-启用', + `time_start` date not null comment '优惠券生效时间', + `time_end` date not null comment '优惠券结束时间', + `count` int(11) not null comment '优惠券发放数量', + `got_count` int(11) not NULL comment '优惠券已领数量', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC comment '优惠券中心'; + +CREATE TABLE `bus_customer` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT comment '客户id', + `user_id` bigint(20) NOT NULL comment '用户id', + `vip_end` date DEFAULT NULL comment 'vip到期时间', + `vip` bit(1) default 0 comment '是否为vip', + `avatar` varchar(255) comment '客户头像', + `nick_name` varchar(255) comment '客户昵称', + `phone` varchar(13) comment '客户电话', + `sex` tinyint(2) DEFAULT NULL comment '性别 0 男 1 女', + `merchant` bit(1) default 0 comment '是否商家入驻', + `mechanic` bit(1) default 0 comment '是否技师入驻', + PRIMARY KEY (`id`), + KEY `user_idx` (`user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC comment '客户管理'; + + +CREATE TABLE `bus_customer_star` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT comment 'id', + `user_id` bigint(20) NOT NULL comment '客户id', + `star_mechanic_id` bigint(20) NOT NULL comment '关注的技师ID', + PRIMARY KEY (`id`), + KEY `user_idx` (`user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC comment '客户关注技师表'; + +CREATE TABLE `bus_customer_address` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT comment 'id', + `user_id` bigint(20) NOT NULL comment '用户id', + `customer_id` bigint(20) NOT NULL comment '客户id', + `address` varchar(1024) DEFAULT NULL comment '地址', + `house_number` varchar(64) DEFAULT NULL comment '门牌号', + `longitude` decimal(30,8) DEFAULT NULL COMMENT '经度', + `latitude` decimal(30,8) DEFAULT NULL COMMENT '纬度', + `name` varchar(255) NOT NULL comment '用户名称', + `phone` varchar(64) NOT NULL comment '用户手机', + `be_default` bit(1) NOT NULL default 0 comment '是否默认地址 0-不默认 1-默认', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC comment '客户地址管理'; + +CREATE TABLE `bus_customer_coupon` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT comment '客户id', + `name` varchar(255) DEFAULT NULL comment '优惠券名称', + `image` varchar(1024) DEFAULT NULL comment '优惠券图片', + `note` varchar(255) DEFAULT NULL comment '优惠券说明', + `value` bigint(11) not null comment '优惠券金额', + `use_condition` bigint(20) not null comment '优惠券使用条件', + `time_start` date not null comment '优惠券生效时间', + `time_end` date not null comment '优惠券结束时间', + `used` bit(1) DEFAULT 0 not null comment '是否使用', + `user_id` bigint(20) not NULL comment '用户id', + `sys_coupon_id` bigint(20) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `user_idx` (`user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC comment '客户优惠券'; + + +CREATE TABLE `bus_mechanic` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT comment '技师id', + `user_id` bigint(20) NOT NULL comment '用户id', + `income` bigint(20) NOT NULL DEFAULT 0 comment '收入', + `balance` bigint(20) NOT NULL DEFAULT 0 comment '余额', + `work_time_start` varchar(64) comment '开始接单时间', + `work_time_end` varchar(64) comment '结束接单时间', + `qualification` varchar(2048) comment '资格证书图片', + `identity_body` varchar(2048) comment '实名信息', + `status` int(2) DEFAULT NULL comment '状态 0-未审核 1-审核通过 3-暂停服务', + `service_items` varchar(255) DEFAULT NULL, + `service_year` int(3) comment '服务年限', + `phone_auth` bit(1) default 1 comment '手机认证', + `avatar_auth` bit(1) default 1 comment '头像认证', + `identity_auth` bit(1) default 1 comment '实名认证', + `health_auth` bit(1) default 1 comment '健康档案', + `qua_auth` bit(1) default 1 comment '资质档案', + `shop_id` bigint(20) comment '所属店铺', + `similarity` int(3) comment '头像相识度', + `order_count` int(11) not null default 0 comment '接单数量', + `applause_rate` int(11) not null default 100 comment '好评率', + `sex` int(2) comment '性别 0 男 1 女', + `avatar` varchar(255) comment '技师头像', + `real_avatar` varchar(255) comment '真实头像', + `read_name` varchar(64) comment '技师真实名称', + `nick_name` varchar(64) comment '技师昵称', + `address` varchar(255) DEFAULT NULL comment '技师地址', + `technical_title` varchar(1024) DEFAULT NULL, + `favorite_count` int(11) comment '关注数量' DEFAULT NULL, + `service_status` int(2) NOT NULL default 1 comment '接单状态 0 下线 1 在线', + `phone` varchar(64) NOT NULL comment '技师手机号码', + `remark` varchar(512) DEFAULT NULL comment '自我介绍', + `city` varchar(255) DEFAULT NULL, + `sign_time` datetime default null comment '签约时间', + `join_time` datetime default null comment '注册时间', + `app_type` varchar(10) DEFAULT NULL comment '所属APP', + `red_card` bit(1) DEFAULT b'0' COMMENT '红牌技师', + `longitude` decimal(30,8) DEFAULT NULL COMMENT '经度', + `latitude` decimal(30,8) DEFAULT NULL COMMENT '纬度', + `audit_avatar` varchar(255) DEFAULT NULL COMMENT '待审核的头像 - 空表示没有', + `audit_avatar_status` int(2) DEFAULT NULL COMMENT '头像审核状态 - 0-无审核 1-审核中 -1-审核失败', + `audit_avatar_message` varchar(255) DEFAULT NULL COMMENT '头像原因', + `limit_distance` int(5) NOT NULL DEFAULT '0' COMMENT '最大接单距离', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC comment '技师管理'; + +CREATE TABLE `bus_mechanic_info` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT comment 'id', + `user_id` bigint(20) NOT NULL comment '用户id', + `mechanic_id` bigint(20) NOT NULL DEFAULT 0 comment '技师id', + `info_type` int(2) NOT NULL comment '1-照片 2-视频', + `info_url` varchar(125) comment '内容', + `status` int(2) not null comment '状态 0-正常 1-下架', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC comment '技师照片-视频管理'; + +CREATE TABLE `bus_shop` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `user_id` bigint(20) NOT NULL comment '用户id', + + `shop_avatar` varchar(1024) DEFAULT NULL comment '店铺头像', + `nick_name` varchar(255) DEFAULT NULL comment '企业昵称', + `id_front_url` varchar(255) DEFAULT NULL comment '身份证正面', + `id_back_url` varchar(255) DEFAULT NULL comment '身份证背面', + `id_hand_url` varchar(255) DEFAULT NULL comment '手持身份证照片', + `license_url` varchar(255) DEFAULT NULL comment '营业执照图片', + `capital` varchar(255) DEFAULT NULL comment '注册资本', + `shop_name` varchar(255) DEFAULT NULL comment '企业名称', + `address` varchar(512) DEFAULT NULL comment '店铺地址', + `shop_code` varchar(128) DEFAULT NULL comment '统一社会信用代码', + `invite_code` varchar(16) DEFAULT NULL comment '邀请码', + `introduction` varchar(500) COMMENT '店铺简介(不超过500字符)', + `legal_person` varchar(50) comment '店铺法人', + + `mechanic_count` int(11) DEFAULT 0 comment '技师数量', + `order_count` int(11) DEFAULT 0 comment '订单数量', + `amount` bigint(20) DEFAULT 0 comment '余额', + `total_income` bigint(20) DEFAULT 0 comment '累计收入', + `register_date` datetime default now() comment '注册时间', + `in_time` datetime comment '审核时间', + `commission_rate` int(3) not null default 10 comment '平台提成', + + + `stop_status` int(2) default 0 not null comment '暂停状态', + `audit_status` int(2) default 0 not null comment '审核状态 0-待审核 1-审核通过 -1-审核失败', + + `wx_open_id` varchar(64) DEFAULT NULL comment 'wxOpenId', + `wx_nick_name` varchar(64) DEFAULT NULL comment '微信昵称', + `wx_avatar` varchar(64) DEFAULT NULL comment '微信头像', + `bank_card_no` varchar(128) DEFAULT NULL comment '银行卡号', + `bank_card_name` varchar(128) DEFAULT NULL comment '银行卡姓名', + `bank` varchar(512) DEFAULT NULL comment '银行名称', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC comment '商家管理'; + +CREATE TABLE `bus_service_item` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `name` varchar(255) DEFAULT NULL comment '服务名称', + `service_time` int(11) DEFAULT NULL comment '服务时间', + `offline_price` bigint(20) DEFAULT NULL comment '线下价格', + `online_price` bigint(10) DEFAULT NULL comment '线上价格', + `level` int(11) DEFAULT NULL comment '服务星级(0,1,2,3,4,5)', + `image` varchar(2048) DEFAULT NULL comment '项目头像', + `status` int(2) default 0 not null comment '是否上线(0下线 1上线)', +# `part` varchar(2048) comment '', + `shop_id` bigint(20) comment '所属店铺', + `order_count` int(11) DEFAULT 0 comment '下单数量', + `audit_status` int(2) NOT NULL DEFAULT '0' COMMENT '审核状态 0-待审核 1-审核通过 -1-审核失败', + `audit_message` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC comment '服务管理'; + +CREATE TABLE `bus_item_assign` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `mechanic_id` bigint(20) DEFAULT NULL, + `item_id` bigint(11) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC comment '技师-服务'; + +CREATE TABLE `bus_banner` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `banner_url` varchar(255) DEFAULT NULL comment 'banner图', + `status` int(2) default 1 not null comment '状态 0-停用 1-启用', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC comment 'banner图'; + +CREATE TABLE `bus_order` ( + `id` bigint(20) NOT NULL, + `trade_no` varchar(128) DEFAULT NULL, + + `customer_id` bigint(20) NOT NULL comment '客户ID', + `customer_phone` varchar(64) DEFAULT NULL comment '客户预约手机', + `customer_name` varchar(64) DEFAULT NULL comment '客户预约名称', + `customer_address` varchar(1024) DEFAULT NULL comment '客户地址', + `customer_address_c` varchar(1024) DEFAULT NULL comment '客户坐标', + + `mechanic_id` bigint(20) NOT NULL comment '技师id', + `mechanic_address` varchar(1024) DEFAULT NULL comment '技师地址', + `mechanic_address_c` varchar(1024) DEFAULT NULL comment '技师坐标', + `mechanic_avatar` varchar(255) comment '技师头像', + `mechanic_nick_name` varchar(255) comment '技师昵称', + + `shop_id` bigint(11) NOT NULL comment '商家id', + `service_item_id` bigint(20) DEFAULT NULL comment '服务id', + `coupon_id` bigint(20) default null comment '优惠券id', + `coupon` bigint(20) default null comment '优惠券抵扣', + + + `fare` bigint(20) default 0 comment '车费', + `price` bigint(20) default 0 NOT NULL comment '商品价格', + `amount` bigint(20) default 0 not NULL comment '最终实付金额', + `boss_amount` int(11) DEFAULT 0 comment '平台收入', + `commission_rate` int(3) DEFAULT 0 comment '平台利润', + + `notes` varchar(255) DEFAULT NULL comment '备注', + + `pay_status` int(11) not null default 0 comment '支付状态 0-未支付 1-已支付 2-已退款', + `service_status` int(11) not NULL default 0 comment '服务状态 0-未接单 1-已接单 2-已完成', + `pay_type` int(11) DEFAULT NULL comment '支付类型 1-支付宝 2-微信', + `appraise` bit(1) not NULL default 0 comment '是否评价', + + `create_time` datetime not NULL default now() comment '创建时间', + `pay_time` datetime comment '支付时间', + `order_time` datetime comment '预约时间', + `service_time` datetime comment '接单时间', + `complete_time` datetime comment '完成时间', + `distance` varchar(255) comment '距离', + + `agent` varchar(10) DEFAULT NULL, + `delete_flag` int(2) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC comment '订单管理'; + +CREATE TABLE `bus_order` ( + `id` bigint(20) NOT NULL, + `trade_no` varchar(128) DEFAULT NULL, + + `customer_id` bigint(20) NOT NULL comment '客户ID', + `customer_phone` varchar(64) DEFAULT NULL comment '客户预约手机', + `customer_name` varchar(64) DEFAULT NULL comment '客户预约名称', + `customer_address` varchar(1024) DEFAULT NULL comment '客户地址', + `customer_address_c` varchar(1024) DEFAULT NULL comment '客户坐标', + + `mechanic_id` bigint(20) NOT NULL comment '技师id', + `mechanic_address` varchar(1024) DEFAULT NULL comment '技师地址', + `mechanic_address_c` varchar(1024) DEFAULT NULL comment '技师坐标', + `mechanic_avatar` varchar(255) comment '技师头像', + `mechanic_nick_name` varchar(255) comment '技师昵称', + + `shop_id` bigint(11) NOT NULL comment '商家id', + `service_item_id` bigint(20) DEFAULT NULL comment '服务id', + `coupon_id` bigint(20) default null comment '优惠券id', + `coupon` bigint(20) default null comment '优惠券抵扣', + + + `fare` bigint(20) default 0 comment '车费', + `price` bigint(20) default 0 NOT NULL comment '商品价格', + `amount` bigint(20) default 0 not NULL comment '最终实付金额', + `boss_amount` int(11) DEFAULT 0 comment '平台收入', + `commission_rate` int(3) DEFAULT 0 comment '平台利润', + + `notes` varchar(255) DEFAULT NULL comment '备注', + + `pay_status` int(11) not null default 0 comment '支付状态 0-未支付 1-已支付 2-已退款', + `service_status` int(11) not NULL default 0 comment '服务状态 0-未接单 1-已接单 2-已完成', + `pay_type` int(11) DEFAULT NULL comment '支付类型 1-支付宝 2-微信', + `appraise` bit(1) not NULL default 0 comment '是否评价', + + `create_time` datetime not NULL default now() comment '创建时间', + `pay_time` datetime comment '支付时间', + `order_time` datetime comment '预约时间', + `service_time` datetime comment '接单时间', + `complete_time` datetime comment '完成时间', + `distance` int(20) comment '距离-单位米', + + `agent` varchar(10) DEFAULT NULL, + `delete_flag` int(2) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC comment '订单管理'; + + + + diff --git a/sql/dk.sql b/sql/dk.sql new file mode 100644 index 0000000..6b7bdf2 --- /dev/null +++ b/sql/dk.sql @@ -0,0 +1,2 @@ +ALTER TABLE `dk`.`sys_user` + ADD COLUMN `open_id` varchar(255) NULL COMMENT 'open-id' AFTER `remark`; diff --git a/sql/quartz.sql b/sql/quartz.sql new file mode 100644 index 0000000..55665e2 --- /dev/null +++ b/sql/quartz.sql @@ -0,0 +1,170 @@ +-- ---------------------------- +-- 1、存储每一个已配置的 jobDetail 的详细信息 +-- ---------------------------- +drop table if exists QRTZ_JOB_DETAILS; +create table QRTZ_JOB_DETAILS ( + sched_name varchar(120) not null, + job_name varchar(200) not null, + job_group varchar(200) not null, + description varchar(250) null, + job_class_name varchar(250) not null, + is_durable varchar(1) not null, + is_nonconcurrent varchar(1) not null, + is_update_data varchar(1) not null, + requests_recovery varchar(1) not null, + job_data blob null, + primary key (sched_name,job_name,job_group) +) engine=innodb; + +-- ---------------------------- +-- 2、 存储已配置的 Trigger 的信息 +-- ---------------------------- +drop table if exists QRTZ_TRIGGERS; +create table QRTZ_TRIGGERS ( + sched_name varchar(120) not null, + trigger_name varchar(200) not null, + trigger_group varchar(200) not null, + job_name varchar(200) not null, + job_group varchar(200) not null, + description varchar(250) null, + next_fire_time bigint(13) null, + prev_fire_time bigint(13) null, + priority integer null, + trigger_state varchar(16) not null, + trigger_type varchar(8) not null, + start_time bigint(13) not null, + end_time bigint(13) null, + calendar_name varchar(200) null, + misfire_instr smallint(2) null, + job_data blob null, + primary key (sched_name,trigger_name,trigger_group), + foreign key (sched_name,job_name,job_group) references QRTZ_JOB_DETAILS(sched_name,job_name,job_group) +) engine=innodb; + +-- ---------------------------- +-- 3、 存储简单的 Trigger,包括重复次数,间隔,以及已触发的次数 +-- ---------------------------- +drop table if exists QRTZ_SIMPLE_TRIGGERS; +create table QRTZ_SIMPLE_TRIGGERS ( + sched_name varchar(120) not null, + trigger_name varchar(200) not null, + trigger_group varchar(200) not null, + repeat_count bigint(7) not null, + repeat_interval bigint(12) not null, + times_triggered bigint(10) not null, + primary key (sched_name,trigger_name,trigger_group), + foreign key (sched_name,trigger_name,trigger_group) references QRTZ_TRIGGERS(sched_name,trigger_name,trigger_group) +) engine=innodb; + +-- ---------------------------- +-- 4、 存储 Cron Trigger,包括 Cron 表达式和时区信息 +-- ---------------------------- +drop table if exists QRTZ_CRON_TRIGGERS; +create table QRTZ_CRON_TRIGGERS ( + sched_name varchar(120) not null, + trigger_name varchar(200) not null, + trigger_group varchar(200) not null, + cron_expression varchar(200) not null, + time_zone_id varchar(80), + primary key (sched_name,trigger_name,trigger_group), + foreign key (sched_name,trigger_name,trigger_group) references QRTZ_TRIGGERS(sched_name,trigger_name,trigger_group) +) engine=innodb; + +-- ---------------------------- +-- 5、 Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型,JobStore 并不知道如何存储实例的时候) +-- ---------------------------- +drop table if exists QRTZ_BLOB_TRIGGERS; +create table QRTZ_BLOB_TRIGGERS ( + sched_name varchar(120) not null, + trigger_name varchar(200) not null, + trigger_group varchar(200) not null, + blob_data blob null, + primary key (sched_name,trigger_name,trigger_group), + foreign key (sched_name,trigger_name,trigger_group) references QRTZ_TRIGGERS(sched_name,trigger_name,trigger_group) +) engine=innodb; + +-- ---------------------------- +-- 6、 以 Blob 类型存储存放日历信息, quartz可配置一个日历来指定一个时间范围 +-- ---------------------------- +drop table if exists QRTZ_CALENDARS; +create table QRTZ_CALENDARS ( + sched_name varchar(120) not null, + calendar_name varchar(200) not null, + calendar blob not null, + primary key (sched_name,calendar_name) +) engine=innodb; + +-- ---------------------------- +-- 7、 存储已暂停的 Trigger 组的信息 +-- ---------------------------- +drop table if exists QRTZ_PAUSED_TRIGGER_GRPS; +create table QRTZ_PAUSED_TRIGGER_GRPS ( + sched_name varchar(120) not null, + trigger_group varchar(200) not null, + primary key (sched_name,trigger_group) +) engine=innodb; + +-- ---------------------------- +-- 8、 存储与已触发的 Trigger 相关的状态信息,以及相联 Job 的执行信息 +-- ---------------------------- +drop table if exists QRTZ_FIRED_TRIGGERS; +create table QRTZ_FIRED_TRIGGERS ( + sched_name varchar(120) not null, + entry_id varchar(95) not null, + trigger_name varchar(200) not null, + trigger_group varchar(200) not null, + instance_name varchar(200) not null, + fired_time bigint(13) not null, + sched_time bigint(13) not null, + priority integer not null, + state varchar(16) not null, + job_name varchar(200) null, + job_group varchar(200) null, + is_nonconcurrent varchar(1) null, + requests_recovery varchar(1) null, + primary key (sched_name,entry_id) +) engine=innodb; + +-- ---------------------------- +-- 9、 存储少量的有关 Scheduler 的状态信息,假如是用于集群中,可以看到其他的 Scheduler 实例 +-- ---------------------------- +drop table if exists QRTZ_SCHEDULER_STATE; +create table QRTZ_SCHEDULER_STATE ( + sched_name varchar(120) not null, + instance_name varchar(200) not null, + last_checkin_time bigint(13) not null, + checkin_interval bigint(13) not null, + primary key (sched_name,instance_name) +) engine=innodb; + +-- ---------------------------- +-- 10、 存储程序的悲观锁的信息(假如使用了悲观锁) +-- ---------------------------- +drop table if exists QRTZ_LOCKS; +create table QRTZ_LOCKS ( + sched_name varchar(120) not null, + lock_name varchar(40) not null, + primary key (sched_name,lock_name) +) engine=innodb; + +drop table if exists QRTZ_SIMPROP_TRIGGERS; +create table QRTZ_SIMPROP_TRIGGERS ( + sched_name varchar(120) not null, + trigger_name varchar(200) not null, + trigger_group varchar(200) not null, + str_prop_1 varchar(512) null, + str_prop_2 varchar(512) null, + str_prop_3 varchar(512) null, + int_prop_1 int null, + int_prop_2 int null, + long_prop_1 bigint null, + long_prop_2 bigint null, + dec_prop_1 numeric(13,4) null, + dec_prop_2 numeric(13,4) null, + bool_prop_1 varchar(1) null, + bool_prop_2 varchar(1) null, + primary key (sched_name,trigger_name,trigger_group), + foreign key (sched_name,trigger_name,trigger_group) references QRTZ_TRIGGERS(sched_name,trigger_name,trigger_group) +) engine=innodb; + +commit; \ No newline at end of file diff --git a/sql/refresh.sql b/sql/refresh.sql new file mode 100644 index 0000000..4b566c3 --- /dev/null +++ b/sql/refresh.sql @@ -0,0 +1,33 @@ +## 1. 清空用户表 +## 排除 admin,ry,591管理员 +delete from sys_user where user_id not in(1,2,109); + +## 2. 清空业务表 +truncate table bus_customer; +truncate table bus_customer_address; +truncate table bus_customer_coupon; +truncate table bus_customer_star; + +truncate table bus_mechanic; +truncate table bus_mechanic_info; +truncate table bus_join_mechanic; +truncate table bus_health_record; + +truncate table bus_shop; +truncate table bus_shop_amount_logs; +truncate table bus_shop_in_logs; +truncate table bus_shop_withdraw; +truncate table bus_join_shop; +truncate table bus_item_assign; +truncate table bus_service_item; + +truncate table bus_order; +truncate table bus_order_delete_bak; +truncate table bus_order_logs; +truncate table bus_vip_order; +truncate table bus_evaluation; + +truncate table bus_qualification_apply; + + + diff --git a/sql/ry_20210210.sql b/sql/ry_20210210.sql new file mode 100644 index 0000000..5dc16c1 --- /dev/null +++ b/sql/ry_20210210.sql @@ -0,0 +1,688 @@ +-- ---------------------------- +-- 1、部门表 +-- ---------------------------- +drop table if exists sys_dept; +create table sys_dept ( + dept_id bigint(20) not null auto_increment comment '部门id', + parent_id bigint(20) default 0 comment '父部门id', + ancestors varchar(50) default '' comment '祖级列表', + dept_name varchar(30) default '' comment '部门名称', + order_num int(4) default 0 comment '显示顺序', + leader varchar(20) default null comment '负责人', + phone varchar(11) default null comment '联系电话', + email varchar(50) default null comment '邮箱', + status char(1) default '0' comment '部门状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + primary key (dept_id) +) engine=innodb auto_increment=200 comment = '部门表'; + +-- ---------------------------- +-- 初始化-部门表数据 +-- ---------------------------- +insert into sys_dept values(100, 0, '0', '若依科技', 0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(101, 100, '0,100', '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(102, 100, '0,100', '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(103, 101, '0,100,101', '研发部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(104, 101, '0,100,101', '市场部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(105, 101, '0,100,101', '测试部门', 3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(106, 101, '0,100,101', '财务部门', 4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(107, 101, '0,100,101', '运维部门', 5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(108, 102, '0,100,102', '市场部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(109, 102, '0,100,102', '财务部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); + + +-- ---------------------------- +-- 2、用户信息表 +-- ---------------------------- +drop table if exists sys_user; +create table sys_user ( + user_id bigint(20) not null auto_increment comment '用户ID', + dept_id bigint(20) default null comment '部门ID', + user_name varchar(30) not null comment '用户账号', + nick_name varchar(30) not null comment '用户昵称', + user_type varchar(2) default '00' comment '用户类型(00系统用户)', + email varchar(50) default '' comment '用户邮箱', + phonenumber varchar(11) default '' comment '手机号码', + sex char(1) default '0' comment '用户性别(0男 1女 2未知)', + avatar varchar(100) default '' comment '头像地址', + password varchar(100) default '' comment '密码', + status char(1) default '0' comment '帐号状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + login_ip varchar(128) default '' comment '最后登录IP', + login_date datetime comment '最后登录时间', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (user_id) +) engine=innodb auto_increment=100 comment = '用户信息表'; + +-- ---------------------------- +-- 初始化-用户信息表数据 +-- ---------------------------- +insert into sys_user values(1, 103, 'admin', '若依', '00', 'ry@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), '', null, '管理员'); +insert into sys_user values(2, 105, 'ry', '若依', '00', 'ry@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), '', null, '测试员'); + + +-- ---------------------------- +-- 3、岗位信息表 +-- ---------------------------- +drop table if exists sys_post; +create table sys_post +( + post_id bigint(20) not null auto_increment comment '岗位ID', + post_code varchar(64) not null comment '岗位编码', + post_name varchar(50) not null comment '岗位名称', + post_sort int(4) not null comment '显示顺序', + status char(1) not null comment '状态(0正常 1停用)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (post_id) +) engine=innodb comment = '岗位信息表'; + +-- ---------------------------- +-- 初始化-岗位信息表数据 +-- ---------------------------- +insert into sys_post values(1, 'ceo', '董事长', 1, '0', 'admin', sysdate(), '', null, ''); +insert into sys_post values(2, 'se', '项目经理', 2, '0', 'admin', sysdate(), '', null, ''); +insert into sys_post values(3, 'hr', '人力资源', 3, '0', 'admin', sysdate(), '', null, ''); +insert into sys_post values(4, 'user', '普通员工', 4, '0', 'admin', sysdate(), '', null, ''); + + +-- ---------------------------- +-- 4、角色信息表 +-- ---------------------------- +drop table if exists sys_role; +create table sys_role ( + role_id bigint(20) not null auto_increment comment '角色ID', + role_name varchar(30) not null comment '角色名称', + role_key varchar(100) not null comment '角色权限字符串', + role_sort int(4) not null comment '显示顺序', + data_scope char(1) default '1' comment '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)', + menu_check_strictly tinyint(1) default 1 comment '菜单树选择项是否关联显示', + dept_check_strictly tinyint(1) default 1 comment '部门树选择项是否关联显示', + status char(1) not null comment '角色状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (role_id) +) engine=innodb auto_increment=100 comment = '角色信息表'; + +-- ---------------------------- +-- 初始化-角色信息表数据 +-- ---------------------------- +insert into sys_role values('1', '超级管理员', 'admin', 1, 1, 1, 1, '0', '0', 'admin', sysdate(), '', null, '超级管理员'); +insert into sys_role values('2', '普通角色', 'common', 2, 2, 1, 1, '0', '0', 'admin', sysdate(), '', null, '普通角色'); + + +-- ---------------------------- +-- 5、菜单权限表 +-- ---------------------------- +drop table if exists sys_menu; +create table sys_menu ( + menu_id bigint(20) not null auto_increment comment '菜单ID', + menu_name varchar(50) not null comment '菜单名称', + parent_id bigint(20) default 0 comment '父菜单ID', + order_num int(4) default 0 comment '显示顺序', + path varchar(200) default '' comment '路由地址', + component varchar(255) default null comment '组件路径', + is_frame int(1) default 1 comment '是否为外链(0是 1否)', + is_cache int(1) default 0 comment '是否缓存(0缓存 1不缓存)', + menu_type char(1) default '' comment '菜单类型(M目录 C菜单 F按钮)', + visible char(1) default 0 comment '菜单状态(0显示 1隐藏)', + status char(1) default 0 comment '菜单状态(0正常 1停用)', + perms varchar(100) default null comment '权限标识', + icon varchar(100) default '#' comment '菜单图标', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default '' comment '备注', + primary key (menu_id) +) engine=innodb auto_increment=2000 comment = '菜单权限表'; + +-- ---------------------------- +-- 初始化-菜单信息表数据 +-- ---------------------------- +-- 一级菜单 +insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, 1, 0, 'M', '0', '0', '', 'system', 'admin', sysdate(), '', null, '系统管理目录'); +insert into sys_menu values('2', '系统监控', '0', '2', 'monitor', null, 1, 0, 'M', '0', '0', '', 'monitor', 'admin', sysdate(), '', null, '系统监控目录'); +insert into sys_menu values('3', '系统工具', '0', '3', 'tool', null, 1, 0, 'M', '0', '0', '', 'tool', 'admin', sysdate(), '', null, '系统工具目录'); +insert into sys_menu values('4', '若依官网', '0', '4', 'http://.vip', null , 0, 0, 'M', '0', '0', '', 'guide', 'admin', sysdate(), '', null, '若依官网地址'); +-- 二级菜单 +insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 'admin', sysdate(), '', null, '用户管理菜单'); +insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 'admin', sysdate(), '', null, '角色管理菜单'); +insert into sys_menu values('102', '菜单管理', '1', '3', 'menu', 'system/menu/index', 1, 0, 'C', '0', '0', 'system:menu:list', 'tree-table', 'admin', sysdate(), '', null, '菜单管理菜单'); +insert into sys_menu values('103', '部门管理', '1', '4', 'dept', 'system/dept/index', 1, 0, 'C', '0', '0', 'system:dept:list', 'tree', 'admin', sysdate(), '', null, '部门管理菜单'); +insert into sys_menu values('104', '岗位管理', '1', '5', 'post', 'system/post/index', 1, 0, 'C', '0', '0', 'system:post:list', 'post', 'admin', sysdate(), '', null, '岗位管理菜单'); +insert into sys_menu values('105', '字典管理', '1', '6', 'dict', 'system/dict/index', 1, 0, 'C', '0', '0', 'system:dict:list', 'dict', 'admin', sysdate(), '', null, '字典管理菜单'); +insert into sys_menu values('106', '参数设置', '1', '7', 'config', 'system/config/index', 1, 0, 'C', '0', '0', 'system:config:list', 'edit', 'admin', sysdate(), '', null, '参数设置菜单'); +insert into sys_menu values('107', '通知公告', '1', '8', 'notice', 'system/notice/index', 1, 0, 'C', '0', '0', 'system:notice:list', 'message', 'admin', sysdate(), '', null, '通知公告菜单'); +insert into sys_menu values('108', '日志管理', '1', '9', 'log', '', 1, 0, 'M', '0', '0', '', 'log', 'admin', sysdate(), '', null, '日志管理菜单'); +insert into sys_menu values('109', '在线用户', '2', '1', 'online', 'monitor/online/index', 1, 0, 'C', '0', '0', 'monitor:online:list', 'online', 'admin', sysdate(), '', null, '在线用户菜单'); +insert into sys_menu values('110', '定时任务', '2', '2', 'job', 'monitor/job/index', 1, 0, 'C', '0', '0', 'monitor:job:list', 'job', 'admin', sysdate(), '', null, '定时任务菜单'); +insert into sys_menu values('111', '数据监控', '2', '3', 'druid', 'monitor/druid/index', 1, 0, 'C', '0', '0', 'monitor:druid:list', 'druid', 'admin', sysdate(), '', null, '数据监控菜单'); +# insert into sys_menu values('112', '服务监控', '2', '4', 'server', 'monitor/server/index', 1, 0, 'C', '0', '0', 'monitor:server:list', 'server', 'admin', sysdate(), '', null, '服务监控菜单'); +insert into sys_menu values('113', '缓存监控', '2', '5', 'cache', 'monitor/cache/index', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis', 'admin', sysdate(), '', null, '缓存监控菜单'); +insert into sys_menu values('114', '表单构建', '3', '1', 'build', 'tool/build/index', 1, 0, 'C', '0', '0', 'tool:build:list', 'build', 'admin', sysdate(), '', null, '表单构建菜单'); +insert into sys_menu values('115', '代码生成', '3', '2', 'gen', 'tool/gen/index', 1, 0, 'C', '0', '0', 'tool:gen:list', 'code', 'admin', sysdate(), '', null, '代码生成菜单'); +insert into sys_menu values('116', '系统接口', '3', '3', 'swagger', 'tool/swagger/index', 1, 0, 'C', '0', '0', 'tool:swagger:list', 'swagger', 'admin', sysdate(), '', null, '系统接口菜单'); +-- springboot-admin监控 +insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', 1, 0, 'C', '0', '0', 'monitor:admin:list', 'dashboard', 'admin', sysdate(), '', null, 'Admin监控菜单'); + +-- 三级菜单 +insert into sys_menu values('500', '操作日志', '108', '1', 'operlog', 'monitor/operlog/index', 1, 0, 'C', '0', '0', 'monitor:operlog:list', 'form', 'admin', sysdate(), '', null, '操作日志菜单'); +insert into sys_menu values('501', '登录日志', '108', '2', 'logininfor', 'monitor/logininfor/index', 1, 0, 'C', '0', '0', 'monitor:logininfor:list', 'logininfor', 'admin', sysdate(), '', null, '登录日志菜单'); +-- 用户管理按钮 +insert into sys_menu values('1001', '用户查询', '100', '1', '', '', 1, 0, 'F', '0', '0', 'system:user:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1002', '用户新增', '100', '2', '', '', 1, 0, 'F', '0', '0', 'system:user:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1003', '用户修改', '100', '3', '', '', 1, 0, 'F', '0', '0', 'system:user:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1004', '用户删除', '100', '4', '', '', 1, 0, 'F', '0', '0', 'system:user:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1005', '用户导出', '100', '5', '', '', 1, 0, 'F', '0', '0', 'system:user:export', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1006', '用户导入', '100', '6', '', '', 1, 0, 'F', '0', '0', 'system:user:import', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1007', '重置密码', '100', '7', '', '', 1, 0, 'F', '0', '0', 'system:user:resetPwd', '#', 'admin', sysdate(), '', null, ''); +-- 角色管理按钮 +insert into sys_menu values('1008', '角色查询', '101', '1', '', '', 1, 0, 'F', '0', '0', 'system:role:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1009', '角色新增', '101', '2', '', '', 1, 0, 'F', '0', '0', 'system:role:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1010', '角色修改', '101', '3', '', '', 1, 0, 'F', '0', '0', 'system:role:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1011', '角色删除', '101', '4', '', '', 1, 0, 'F', '0', '0', 'system:role:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1012', '角色导出', '101', '5', '', '', 1, 0, 'F', '0', '0', 'system:role:export', '#', 'admin', sysdate(), '', null, ''); +-- 菜单管理按钮 +insert into sys_menu values('1013', '菜单查询', '102', '1', '', '', 1, 0, 'F', '0', '0', 'system:menu:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1014', '菜单新增', '102', '2', '', '', 1, 0, 'F', '0', '0', 'system:menu:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1015', '菜单修改', '102', '3', '', '', 1, 0, 'F', '0', '0', 'system:menu:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1016', '菜单删除', '102', '4', '', '', 1, 0, 'F', '0', '0', 'system:menu:remove', '#', 'admin', sysdate(), '', null, ''); +-- 部门管理按钮 +insert into sys_menu values('1017', '部门查询', '103', '1', '', '', 1, 0, 'F', '0', '0', 'system:dept:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1018', '部门新增', '103', '2', '', '', 1, 0, 'F', '0', '0', 'system:dept:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1019', '部门修改', '103', '3', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1020', '部门删除', '103', '4', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove', '#', 'admin', sysdate(), '', null, ''); +-- 岗位管理按钮 +insert into sys_menu values('1021', '岗位查询', '104', '1', '', '', 1, 0, 'F', '0', '0', 'system:post:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1022', '岗位新增', '104', '2', '', '', 1, 0, 'F', '0', '0', 'system:post:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1023', '岗位修改', '104', '3', '', '', 1, 0, 'F', '0', '0', 'system:post:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1024', '岗位删除', '104', '4', '', '', 1, 0, 'F', '0', '0', 'system:post:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1025', '岗位导出', '104', '5', '', '', 1, 0, 'F', '0', '0', 'system:post:export', '#', 'admin', sysdate(), '', null, ''); +-- 字典管理按钮 +insert into sys_menu values('1026', '字典查询', '105', '1', '#', '', 1, 0, 'F', '0', '0', 'system:dict:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1027', '字典新增', '105', '2', '#', '', 1, 0, 'F', '0', '0', 'system:dict:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1028', '字典修改', '105', '3', '#', '', 1, 0, 'F', '0', '0', 'system:dict:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1029', '字典删除', '105', '4', '#', '', 1, 0, 'F', '0', '0', 'system:dict:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1030', '字典导出', '105', '5', '#', '', 1, 0, 'F', '0', '0', 'system:dict:export', '#', 'admin', sysdate(), '', null, ''); +-- 参数设置按钮 +insert into sys_menu values('1031', '参数查询', '106', '1', '#', '', 1, 0, 'F', '0', '0', 'system:config:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1032', '参数新增', '106', '2', '#', '', 1, 0, 'F', '0', '0', 'system:config:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1033', '参数修改', '106', '3', '#', '', 1, 0, 'F', '0', '0', 'system:config:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1034', '参数删除', '106', '4', '#', '', 1, 0, 'F', '0', '0', 'system:config:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1035', '参数导出', '106', '5', '#', '', 1, 0, 'F', '0', '0', 'system:config:export', '#', 'admin', sysdate(), '', null, ''); +-- 通知公告按钮 +insert into sys_menu values('1036', '公告查询', '107', '1', '#', '', 1, 0, 'F', '0', '0', 'system:notice:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1037', '公告新增', '107', '2', '#', '', 1, 0, 'F', '0', '0', 'system:notice:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1038', '公告修改', '107', '3', '#', '', 1, 0, 'F', '0', '0', 'system:notice:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1039', '公告删除', '107', '4', '#', '', 1, 0, 'F', '0', '0', 'system:notice:remove', '#', 'admin', sysdate(), '', null, ''); +-- 操作日志按钮 +insert into sys_menu values('1040', '操作查询', '500', '1', '#', '', 1, 0, 'F', '0', '0', 'monitor:operlog:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1041', '操作删除', '500', '2', '#', '', 1, 0, 'F', '0', '0', 'monitor:operlog:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', 1, 0, 'F', '0', '0', 'monitor:operlog:export', '#', 'admin', sysdate(), '', null, ''); +-- 登录日志按钮 +insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export', '#', 'admin', sysdate(), '', null, ''); +-- 在线用户按钮 +insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', 1, 0, 'F', '0', '0', 'monitor:online:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1048', '单条强退', '109', '3', '#', '', 1, 0, 'F', '0', '0', 'monitor:online:forceLogout', '#', 'admin', sysdate(), '', null, ''); +-- 定时任务按钮 +insert into sys_menu values('1049', '任务查询', '110', '1', '#', '', 1, 0, 'F', '0', '0', 'monitor:job:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1050', '任务新增', '110', '2', '#', '', 1, 0, 'F', '0', '0', 'monitor:job:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1051', '任务修改', '110', '3', '#', '', 1, 0, 'F', '0', '0', 'monitor:job:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1052', '任务删除', '110', '4', '#', '', 1, 0, 'F', '0', '0', 'monitor:job:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1053', '状态修改', '110', '5', '#', '', 1, 0, 'F', '0', '0', 'monitor:job:changeStatus', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1054', '任务导出', '110', '7', '#', '', 1, 0, 'F', '0', '0', 'monitor:job:export', '#', 'admin', sysdate(), '', null, ''); +-- 代码生成按钮 +insert into sys_menu values('1055', '生成查询', '115', '1', '#', '', 1, 0, 'F', '0', '0', 'tool:gen:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1056', '生成修改', '115', '2', '#', '', 1, 0, 'F', '0', '0', 'tool:gen:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1057', '生成删除', '115', '3', '#', '', 1, 0, 'F', '0', '0', 'tool:gen:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1058', '导入代码', '115', '2', '#', '', 1, 0, 'F', '0', '0', 'tool:gen:import', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1059', '预览代码', '115', '4', '#', '', 1, 0, 'F', '0', '0', 'tool:gen:preview', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1060', '生成代码', '115', '5', '#', '', 1, 0, 'F', '0', '0', 'tool:gen:code', '#', 'admin', sysdate(), '', null, ''); + + +-- ---------------------------- +-- 6、用户和角色关联表 用户N-1角色 +-- ---------------------------- +drop table if exists sys_user_role; +create table sys_user_role ( + user_id bigint(20) not null comment '用户ID', + role_id bigint(20) not null comment '角色ID', + primary key(user_id, role_id) +) engine=innodb comment = '用户和角色关联表'; + +-- ---------------------------- +-- 初始化-用户和角色关联表数据 +-- ---------------------------- +insert into sys_user_role values ('1', '1'); +insert into sys_user_role values ('2', '2'); + + +-- ---------------------------- +-- 7、角色和菜单关联表 角色1-N菜单 +-- ---------------------------- +drop table if exists sys_role_menu; +create table sys_role_menu ( + role_id bigint(20) not null comment '角色ID', + menu_id bigint(20) not null comment '菜单ID', + primary key(role_id, menu_id) +) engine=innodb comment = '角色和菜单关联表'; + +-- ---------------------------- +-- 初始化-角色和菜单关联表数据 +-- ---------------------------- +insert into sys_role_menu values ('2', '1'); +insert into sys_role_menu values ('2', '2'); +insert into sys_role_menu values ('2', '3'); +insert into sys_role_menu values ('2', '4'); +insert into sys_role_menu values ('2', '100'); +insert into sys_role_menu values ('2', '101'); +insert into sys_role_menu values ('2', '102'); +insert into sys_role_menu values ('2', '103'); +insert into sys_role_menu values ('2', '104'); +insert into sys_role_menu values ('2', '105'); +insert into sys_role_menu values ('2', '106'); +insert into sys_role_menu values ('2', '107'); +insert into sys_role_menu values ('2', '108'); +insert into sys_role_menu values ('2', '109'); +insert into sys_role_menu values ('2', '110'); +insert into sys_role_menu values ('2', '111'); +insert into sys_role_menu values ('2', '112'); +insert into sys_role_menu values ('2', '113'); +insert into sys_role_menu values ('2', '114'); +insert into sys_role_menu values ('2', '115'); +insert into sys_role_menu values ('2', '116'); +insert into sys_role_menu values ('2', '500'); +insert into sys_role_menu values ('2', '501'); +insert into sys_role_menu values ('2', '1000'); +insert into sys_role_menu values ('2', '1001'); +insert into sys_role_menu values ('2', '1002'); +insert into sys_role_menu values ('2', '1003'); +insert into sys_role_menu values ('2', '1004'); +insert into sys_role_menu values ('2', '1005'); +insert into sys_role_menu values ('2', '1006'); +insert into sys_role_menu values ('2', '1007'); +insert into sys_role_menu values ('2', '1008'); +insert into sys_role_menu values ('2', '1009'); +insert into sys_role_menu values ('2', '1010'); +insert into sys_role_menu values ('2', '1011'); +insert into sys_role_menu values ('2', '1012'); +insert into sys_role_menu values ('2', '1013'); +insert into sys_role_menu values ('2', '1014'); +insert into sys_role_menu values ('2', '1015'); +insert into sys_role_menu values ('2', '1016'); +insert into sys_role_menu values ('2', '1017'); +insert into sys_role_menu values ('2', '1018'); +insert into sys_role_menu values ('2', '1019'); +insert into sys_role_menu values ('2', '1020'); +insert into sys_role_menu values ('2', '1021'); +insert into sys_role_menu values ('2', '1022'); +insert into sys_role_menu values ('2', '1023'); +insert into sys_role_menu values ('2', '1024'); +insert into sys_role_menu values ('2', '1025'); +insert into sys_role_menu values ('2', '1026'); +insert into sys_role_menu values ('2', '1027'); +insert into sys_role_menu values ('2', '1028'); +insert into sys_role_menu values ('2', '1029'); +insert into sys_role_menu values ('2', '1030'); +insert into sys_role_menu values ('2', '1031'); +insert into sys_role_menu values ('2', '1032'); +insert into sys_role_menu values ('2', '1033'); +insert into sys_role_menu values ('2', '1034'); +insert into sys_role_menu values ('2', '1035'); +insert into sys_role_menu values ('2', '1036'); +insert into sys_role_menu values ('2', '1037'); +insert into sys_role_menu values ('2', '1038'); +insert into sys_role_menu values ('2', '1039'); +insert into sys_role_menu values ('2', '1040'); +insert into sys_role_menu values ('2', '1041'); +insert into sys_role_menu values ('2', '1042'); +insert into sys_role_menu values ('2', '1043'); +insert into sys_role_menu values ('2', '1044'); +insert into sys_role_menu values ('2', '1045'); +insert into sys_role_menu values ('2', '1046'); +insert into sys_role_menu values ('2', '1047'); +insert into sys_role_menu values ('2', '1048'); +insert into sys_role_menu values ('2', '1049'); +insert into sys_role_menu values ('2', '1050'); +insert into sys_role_menu values ('2', '1051'); +insert into sys_role_menu values ('2', '1052'); +insert into sys_role_menu values ('2', '1053'); +insert into sys_role_menu values ('2', '1054'); +insert into sys_role_menu values ('2', '1055'); +insert into sys_role_menu values ('2', '1056'); +insert into sys_role_menu values ('2', '1057'); +insert into sys_role_menu values ('2', '1058'); +insert into sys_role_menu values ('2', '1059'); +insert into sys_role_menu values ('2', '1060'); + +-- ---------------------------- +-- 8、角色和部门关联表 角色1-N部门 +-- ---------------------------- +drop table if exists sys_role_dept; +create table sys_role_dept ( + role_id bigint(20) not null comment '角色ID', + dept_id bigint(20) not null comment '部门ID', + primary key(role_id, dept_id) +) engine=innodb comment = '角色和部门关联表'; + +-- ---------------------------- +-- 初始化-角色和部门关联表数据 +-- ---------------------------- +insert into sys_role_dept values ('2', '100'); +insert into sys_role_dept values ('2', '101'); +insert into sys_role_dept values ('2', '105'); + + +-- ---------------------------- +-- 9、用户与岗位关联表 用户1-N岗位 +-- ---------------------------- +drop table if exists sys_user_post; +create table sys_user_post +( + user_id bigint(20) not null comment '用户ID', + post_id bigint(20) not null comment '岗位ID', + primary key (user_id, post_id) +) engine=innodb comment = '用户与岗位关联表'; + +-- ---------------------------- +-- 初始化-用户与岗位关联表数据 +-- ---------------------------- +insert into sys_user_post values ('1', '1'); +insert into sys_user_post values ('2', '2'); + + +-- ---------------------------- +-- 10、操作日志记录 +-- ---------------------------- +drop table if exists sys_oper_log; +create table sys_oper_log ( + oper_id bigint(20) not null auto_increment comment '日志主键', + title varchar(50) default '' comment '模块标题', + business_type int(2) default 0 comment '业务类型(0其它 1新增 2修改 3删除)', + method varchar(100) default '' comment '方法名称', + request_method varchar(10) default '' comment '请求方式', + operator_type int(1) default 0 comment '操作类别(0其它 1后台用户 2手机端用户)', + oper_name varchar(50) default '' comment '操作人员', + dept_name varchar(50) default '' comment '部门名称', + oper_url varchar(255) default '' comment '请求URL', + oper_ip varchar(128) default '' comment '主机地址', + oper_location varchar(255) default '' comment '操作地点', + oper_param varchar(2000) default '' comment '请求参数', + json_result varchar(2000) default '' comment '返回参数', + status int(1) default 0 comment '操作状态(0正常 1异常)', + error_msg varchar(2000) default '' comment '错误消息', + oper_time datetime comment '操作时间', + primary key (oper_id) +) engine=innodb auto_increment=100 comment = '操作日志记录'; + + +-- ---------------------------- +-- 11、字典类型表 +-- ---------------------------- +drop table if exists sys_dict_type; +create table sys_dict_type +( + dict_id bigint(20) not null auto_increment comment '字典主键', + dict_name varchar(100) default '' comment '字典名称', + dict_type varchar(100) default '' comment '字典类型', + status char(1) default '0' comment '状态(0正常 1停用)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (dict_id), + unique (dict_type) +) engine=innodb auto_increment=100 comment = '字典类型表'; + +insert into sys_dict_type values(1, '用户性别', 'sys_user_sex', '0', 'admin', sysdate(), '', null, '用户性别列表'); +insert into sys_dict_type values(2, '菜单状态', 'sys_show_hide', '0', 'admin', sysdate(), '', null, '菜单状态列表'); +insert into sys_dict_type values(3, '系统开关', 'sys_normal_disable', '0', 'admin', sysdate(), '', null, '系统开关列表'); +insert into sys_dict_type values(4, '任务状态', 'sys_job_status', '0', 'admin', sysdate(), '', null, '任务状态列表'); +insert into sys_dict_type values(5, '任务分组', 'sys_job_group', '0', 'admin', sysdate(), '', null, '任务分组列表'); +insert into sys_dict_type values(6, '系统是否', 'sys_yes_no', '0', 'admin', sysdate(), '', null, '系统是否列表'); +insert into sys_dict_type values(7, '通知类型', 'sys_notice_type', '0', 'admin', sysdate(), '', null, '通知类型列表'); +insert into sys_dict_type values(8, '通知状态', 'sys_notice_status', '0', 'admin', sysdate(), '', null, '通知状态列表'); +insert into sys_dict_type values(9, '操作类型', 'sys_oper_type', '0', 'admin', sysdate(), '', null, '操作类型列表'); +insert into sys_dict_type values(10, '系统状态', 'sys_common_status', '0', 'admin', sysdate(), '', null, '登录状态列表'); + + +-- ---------------------------- +-- 12、字典数据表 +-- ---------------------------- +drop table if exists sys_dict_data; +create table sys_dict_data +( + dict_code bigint(20) not null auto_increment comment '字典编码', + dict_sort int(4) default 0 comment '字典排序', + dict_label varchar(100) default '' comment '字典标签', + dict_value varchar(100) default '' comment '字典键值', + dict_type varchar(100) default '' comment '字典类型', + css_class varchar(100) default null comment '样式属性(其他样式扩展)', + list_class varchar(100) default null comment '表格回显样式', + is_default char(1) default 'N' comment '是否默认(Y是 N否)', + status char(1) default '0' comment '状态(0正常 1停用)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (dict_code) +) engine=innodb auto_increment=100 comment = '字典数据表'; + +insert into sys_dict_data values(1, 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 'admin', sysdate(), '', null, '性别男'); +insert into sys_dict_data values(2, 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 'admin', sysdate(), '', null, '性别女'); +insert into sys_dict_data values(3, 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 'admin', sysdate(), '', null, '性别未知'); +insert into sys_dict_data values(4, 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '显示菜单'); +insert into sys_dict_data values(5, 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '隐藏菜单'); +insert into sys_dict_data values(6, 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态'); +insert into sys_dict_data values(7, 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态'); +insert into sys_dict_data values(8, 1, '正常', '0', 'sys_job_status', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态'); +insert into sys_dict_data values(9, 2, '暂停', '1', 'sys_job_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态'); +insert into sys_dict_data values(10, 1, '默认', 'DEFAULT', 'sys_job_group', '', '', 'Y', '0', 'admin', sysdate(), '', null, '默认分组'); +insert into sys_dict_data values(11, 2, '系统', 'SYSTEM', 'sys_job_group', '', '', 'N', '0', 'admin', sysdate(), '', null, '系统分组'); +insert into sys_dict_data values(12, 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '系统默认是'); +insert into sys_dict_data values(13, 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '系统默认否'); +insert into sys_dict_data values(14, 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 'admin', sysdate(), '', null, '通知'); +insert into sys_dict_data values(15, 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 'admin', sysdate(), '', null, '公告'); +insert into sys_dict_data values(16, 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态'); +insert into sys_dict_data values(17, 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '关闭状态'); +insert into sys_dict_data values(18, 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '新增操作'); +insert into sys_dict_data values(19, 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '修改操作'); +insert into sys_dict_data values(20, 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '删除操作'); +insert into sys_dict_data values(21, 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 'admin', sysdate(), '', null, '授权操作'); +insert into sys_dict_data values(22, 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '导出操作'); +insert into sys_dict_data values(23, 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '导入操作'); +insert into sys_dict_data values(24, 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '强退操作'); +insert into sys_dict_data values(25, 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '生成操作'); +insert into sys_dict_data values(26, 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '清空操作'); +insert into sys_dict_data values(27, 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 'admin', sysdate(), '', null, '正常状态'); +insert into sys_dict_data values(28, 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态'); + + +-- ---------------------------- +-- 13、参数配置表 +-- ---------------------------- +drop table if exists sys_config; +create table sys_config ( + config_id int(5) not null auto_increment comment '参数主键', + config_name varchar(100) default '' comment '参数名称', + config_key varchar(100) default '' comment '参数键名', + config_value varchar(500) default '' comment '参数键值', + config_type char(1) default 'N' comment '系统内置(Y是 N否)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (config_id) +) engine=innodb auto_increment=100 comment = '参数配置表'; + +insert into sys_config values(1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'admin', sysdate(), '', null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' ); +insert into sys_config values(2, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 'admin', sysdate(), '', null, '初始化密码 123456' ); +insert into sys_config values(3, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 'admin', sysdate(), '', null, '深色主题theme-dark,浅色主题theme-light' ); + + +-- ---------------------------- +-- 14、系统访问记录 +-- ---------------------------- +drop table if exists sys_logininfor; +create table sys_logininfor ( + info_id bigint(20) not null auto_increment comment '访问ID', + user_name varchar(50) default '' comment '用户账号', + ipaddr varchar(128) default '' comment '登录IP地址', + login_location varchar(255) default '' comment '登录地点', + browser varchar(50) default '' comment '浏览器类型', + os varchar(50) default '' comment '操作系统', + status char(1) default '0' comment '登录状态(0成功 1失败)', + msg varchar(255) default '' comment '提示消息', + login_time datetime comment '访问时间', + primary key (info_id) +) engine=innodb auto_increment=100 comment = '系统访问记录'; + + +-- ---------------------------- +-- 15、定时任务调度表 +-- ---------------------------- +drop table if exists sys_job; +create table sys_job ( + job_id bigint(20) not null auto_increment comment '任务ID', + job_name varchar(64) default '' comment '任务名称', + job_group varchar(64) default 'DEFAULT' comment '任务组名', + invoke_target varchar(500) not null comment '调用目标字符串', + cron_expression varchar(255) default '' comment 'cron执行表达式', + misfire_policy varchar(20) default '3' comment '计划执行错误策略(1立即执行 2执行一次 3放弃执行)', + concurrent char(1) default '1' comment '是否并发执行(0允许 1禁止)', + status char(1) default '0' comment '状态(0正常 1暂停)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default '' comment '备注信息', + primary key (job_id, job_name, job_group) +) engine=innodb auto_increment=100 comment = '定时任务调度表'; + +insert into sys_job values(1, '系统默认(无参)', 'DEFAULT', 'ryTask.ryNoParams', '0/10 * * * * ?', '3', '1', '1', 'admin', sysdate(), '', null, ''); +insert into sys_job values(2, '系统默认(有参)', 'DEFAULT', 'ryTask.ryParams(\'ry\')', '0/15 * * * * ?', '3', '1', '1', 'admin', sysdate(), '', null, ''); +insert into sys_job values(3, '系统默认(多参)', 'DEFAULT', 'ryTask.ryMultipleParams(\'ry\', true, 2000L, 316.50D, 100)', '0/20 * * * * ?', '3', '1', '1', 'admin', sysdate(), '', null, ''); + + +-- ---------------------------- +-- 16、定时任务调度日志表 +-- ---------------------------- +drop table if exists sys_job_log; +create table sys_job_log ( + job_log_id bigint(20) not null auto_increment comment '任务日志ID', + job_name varchar(64) not null comment '任务名称', + job_group varchar(64) not null comment '任务组名', + invoke_target varchar(500) not null comment '调用目标字符串', + job_message varchar(500) comment '日志信息', + status char(1) default '0' comment '执行状态(0正常 1失败)', + exception_info varchar(2000) default '' comment '异常信息', + create_time datetime comment '创建时间', + primary key (job_log_id) +) engine=innodb comment = '定时任务调度日志表'; + + +-- ---------------------------- +-- 17、通知公告表 +-- ---------------------------- +drop table if exists sys_notice; +create table sys_notice ( + notice_id int(4) not null auto_increment comment '公告ID', + notice_title varchar(50) not null comment '公告标题', + notice_type char(1) not null comment '公告类型(1通知 2公告)', + notice_content longblob default null comment '公告内容', + status char(1) default '0' comment '公告状态(0正常 1关闭)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(255) default null comment '备注', + primary key (notice_id) +) engine=innodb auto_increment=10 comment = '通知公告表'; + +-- ---------------------------- +-- 初始化-公告信息表数据 +-- ---------------------------- +insert into sys_notice values('1', '温馨提醒:2018-07-01 若依新版本发布啦', '2', '新版本内容', '0', 'admin', sysdate(), '', null, '管理员'); +insert into sys_notice values('2', '维护通知:2018-07-01 若依系统凌晨维护', '1', '维护内容', '0', 'admin', sysdate(), '', null, '管理员'); + + +-- ---------------------------- +-- 18、代码生成业务表 +-- ---------------------------- +drop table if exists gen_table; +create table gen_table ( + table_id bigint(20) not null auto_increment comment '编号', + table_name varchar(200) default '' comment '表名称', + table_comment varchar(500) default '' comment '表描述', + sub_table_name varchar(64) default null comment '关联子表的表名', + sub_table_fk_name varchar(64) default null comment '子表关联的外键名', + class_name varchar(100) default '' comment '实体类名称', + tpl_category varchar(200) default 'crud' comment '使用的模板(crud单表操作 tree树表操作)', + package_name varchar(100) comment '生成包路径', + module_name varchar(30) comment '生成模块名', + business_name varchar(30) comment '生成业务名', + function_name varchar(50) comment '生成功能名', + function_author varchar(50) comment '生成功能作者', + gen_type char(1) default '0' comment '生成代码方式(0zip压缩包 1自定义路径)', + gen_path varchar(200) default '/' comment '生成路径(不填默认项目路径)', + options varchar(1000) comment '其它生成选项', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (table_id) +) engine=innodb auto_increment=1 comment = '代码生成业务表'; + + +-- ---------------------------- +-- 19、代码生成业务表字段 +-- ---------------------------- +drop table if exists gen_table_column; +create table gen_table_column ( + column_id bigint(20) not null auto_increment comment '编号', + table_id varchar(64) comment '归属表编号', + column_name varchar(200) comment '列名称', + column_comment varchar(500) comment '列描述', + column_type varchar(100) comment '列类型', + java_type varchar(500) comment 'JAVA类型', + java_field varchar(200) comment 'JAVA字段名', + is_pk char(1) comment '是否主键(1是)', + is_increment char(1) comment '是否自增(1是)', + is_required char(1) comment '是否必填(1是)', + is_insert char(1) comment '是否为插入字段(1是)', + is_edit char(1) comment '是否编辑字段(1是)', + is_list char(1) comment '是否列表字段(1是)', + is_query char(1) comment '是否查询字段(1是)', + query_type varchar(200) default 'EQ' comment '查询方式(等于、不等于、大于、小于、范围)', + html_type varchar(200) comment '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)', + dict_type varchar(200) default '' comment '字典类型', + sort int comment '排序', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + primary key (column_id) +) engine=innodb auto_increment=1 comment = '代码生成业务表字段'; diff --git a/sql/test.sql b/sql/test.sql new file mode 100644 index 0000000..601360a --- /dev/null +++ b/sql/test.sql @@ -0,0 +1,171 @@ +DROP TABLE if EXISTS test_demo; +CREATE TABLE test_demo +( + id int(0) NOT NULL AUTO_INCREMENT COMMENT '主键', + dept_id int(0) NULL DEFAULT NULL COMMENT '部门id', + user_id int(0) NULL DEFAULT NULL COMMENT '用户id', + order_num int(0) NULL DEFAULT 0 COMMENT '排序号', + test_key varchar(255) NULL DEFAULT NULL COMMENT 'key键', + value varchar(255) NULL DEFAULT NULL COMMENT '值', + version int(0) NULL DEFAULT 0 COMMENT '版本', + create_time datetime(0) NULL DEFAULT NULL COMMENT '创建时间', + create_by varchar(64) NULL DEFAULT NULL COMMENT '创建人', + update_time datetime(0) NULL DEFAULT NULL COMMENT '更新时间', + update_by varchar(64) NULL DEFAULT NULL COMMENT '更新人', + del_flag int(0) NULL DEFAULT 0 COMMENT '删除标志', + PRIMARY KEY (id) USING BTREE +) ENGINE = InnoDB COMMENT = '测试单表'; + +DROP TABLE if EXISTS test_tree; +CREATE TABLE test_tree +( + id int(0) NOT NULL AUTO_INCREMENT COMMENT '主键', + parent_id int(0) NULL DEFAULT 0 COMMENT '父id', + dept_id int(0) NULL DEFAULT NULL COMMENT '部门id', + user_id int(0) NULL DEFAULT NULL COMMENT '用户id', + tree_name varchar(255) NULL DEFAULT NULL COMMENT '值', + version int(0) NULL DEFAULT 0 COMMENT '版本', + create_time datetime(0) NULL DEFAULT NULL COMMENT '创建时间', + create_by varchar(64) NULL DEFAULT NULL COMMENT '创建人', + update_time datetime(0) NULL DEFAULT NULL COMMENT '更新时间', + update_by varchar(64) NULL DEFAULT NULL COMMENT '更新人', + del_flag int(0) NULL DEFAULT 0 COMMENT '删除标志', + PRIMARY KEY (id) USING BTREE +) ENGINE = InnoDB COMMENT = '测试树表'; + +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (5, '测试菜单', 0, 5, 'demo', NULL, 1, 0, 'M', '0', '0', NULL, 'star', 'admin', '2021-05-30 00:34:26', NULL, NULL, ''); + +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1500, '测试单表', 5, 1, 'demo', 'demo/demo/index', 1, 0, 'C', '0', '0', 'demo:demo:list', '#', 'admin', '2021-05-30 00:39:23', '', NULL, '测试单表菜单'); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1501, '测试单表查询', 1500, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:query', '#', 'admin', '2021-05-30 00:39:23', '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1502, '测试单表新增', 1500, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:add', '#', 'admin', '2021-05-30 00:39:23', '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1503, '测试单表修改', 1500, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 'admin', '2021-05-30 00:39:23', '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1504, '测试单表删除', 1500, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 'admin', '2021-05-30 00:39:23', '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1505, '测试单表导出', 1500, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:export', '#', 'admin', '2021-05-30 00:39:23', '', NULL, ''); + +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1506, '测试树表', 5, 1, 'tree', 'demo/tree/index', 1, 0, 'C', '0', '0', 'demo:tree:list', '#', 'admin', '2021-05-30 00:39:30', '', NULL, '测试树表菜单'); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1507, '测试树表查询', 1506, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:query', '#', 'admin', '2021-05-30 00:39:30', '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1508, '测试树表新增', 1506, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:add', '#', 'admin', '2021-05-30 00:39:30', '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1509, '测试树表修改', 1506, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 'admin', '2021-05-30 00:39:30', '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1510, '测试树表删除', 1506, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 'admin', '2021-05-30 00:39:30', '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1511, '测试树表导出', 1506, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:export', '#', 'admin', '2021-05-30 00:39:30', '', NULL, ''); + +INSERT INTO sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) VALUES (3, '本部门及以下', 'test1', 3, '4', 1, 1, '0', '0', 'admin', '2021-05-08 22:31:37', 'admin', '2021-05-08 22:32:03', NULL); +INSERT INTO sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) VALUES (4, '仅本人', 'test2', 4, '5', 1, 1, '0', '0', 'admin', '2021-05-30 01:14:52', 'admin', '2021-05-30 01:18:38', NULL); + +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 5); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 100); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 101); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 102); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 103); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 104); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 105); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 106); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 107); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 108); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 500); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 501); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1001); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1002); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1003); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1004); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1005); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1006); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1007); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1008); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1009); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1010); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1011); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1012); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1013); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1014); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1015); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1016); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1017); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1018); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1019); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1020); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1021); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1022); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1023); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1024); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1025); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1026); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1027); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1028); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1029); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1030); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1031); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1032); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1033); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1034); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1035); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1036); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1037); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1038); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1039); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1040); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1041); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1042); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1043); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1044); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1045); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1500); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1501); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1502); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1503); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1504); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1505); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1506); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1507); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1508); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1509); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1510); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1511); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 5); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1500); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1501); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1502); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1503); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1504); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1505); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1506); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1507); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1508); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1509); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1510); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1511); + +INSERT INTO sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) VALUES (3, 108, 'test', '本部门及以下 密码666', '00', '', '', '0', '', '$2a$10$M6tZRpUZbWKq11O/z6YISePQc./Jhru8E18mmVJTr9aV8whzfjacC', '0', '0', '127.0.0.1', '2021-05-30 02:00:37', 'admin', '2021-04-22 09:50:41', 'test', '2021-05-30 02:00:37', NULL); +INSERT INTO sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) VALUES (4, 102, 'test1', '仅本人 密码666', '00', '', '', '0', '', '$2a$10$yBSXp5Ba1m402cxXTPSy4eXUO8CXCGvXfquNVP/XMWwZ8nf9GaoMy', '0', '0', '127.0.0.1', '2021-05-30 01:48:03', 'admin', '2021-05-30 01:16:02', 'test1', '2021-05-30 01:48:03', NULL); + +INSERT INTO sys_user_role(user_id, role_id) VALUES (3, 3); +INSERT INTO sys_user_role(user_id, role_id) VALUES (4, 4); + +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (1, 102, 4, 1, '测试数据权限', '测试', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (2, 102, 3, 2, '子节点1', '111', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (3, 102, 3, 3, '子节点2', '222', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (4, 108, 4, 4, '测试数据', 'demo', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (5, 108, 3, 13, '子节点11', '1111', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (6, 108, 3, 12, '子节点22', '2222', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (7, 108, 3, 11, '子节点33', '3333', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (8, 108, 3, 10, '子节点44', '4444', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (9, 108, 3, 9, '子节点55', '5555', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (10, 108, 3, 8, '子节点66', '6666', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (11, 108, 3, 7, '子节点77', '7777', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (12, 108, 3, 6, '子节点88', '8888', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (13, 108, 3, 5, '子节点99', '9999', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); + +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (1, 0, 102, 4, '测试数据权限', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (2, 1, 102, 3, '子节点1', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (3, 2, 102, 3, '子节点2', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (4, 0, 108, 4, '测试树1', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (5, 4, 108, 3, '子节点11', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (6, 4, 108, 3, '子节点22', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (7, 4, 108, 3, '子节点33', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (8, 5, 108, 3, '子节点44', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (9, 6, 108, 3, '子节点55', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (10, 7, 108, 3, '子节点66', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (11, 7, 108, 3, '子节点77', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (12, 10, 108, 3, '子节点88', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (13, 10, 108, 3, '子节点99', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0); diff --git a/up.sql b/up.sql new file mode 100644 index 0000000..3b1f3f2 --- /dev/null +++ b/up.sql @@ -0,0 +1,7 @@ +ALTER TABLE `dk_customer_info` + ADD COLUMN `income_wan` decimal(10, 2) NULL; +ALTER TABLE `dk_borrow` + ADD COLUMN `income_wan` decimal(10, 2) NULL; + +ALTER TABLE `dk_agreement_setting` + ADD COLUMN `contract_template` text NULL;