From 58431f905304fc1b948f0969a11a04110ea0431e Mon Sep 17 00:00:00 2001 From: shizhendong Date: Wed, 26 Jun 2024 10:29:24 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20ASW-2=20=E6=96=B0=E5=A2=9E=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E5=9F=BA=E7=A1=80=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 系统认证接口 2. 用户相关接口 3. i18n 国际化接口 --- .../geedge/asw/common/config/I18nConfig.java | 122 ++++++++++++++++ .../asw/common/config/LocaleConfig.java | 66 +++++++++ .../asw/common/config/SaTokenConfigure.java | 2 +- .../config/exception/ASWExceptionHandler.java | 36 +++-- .../geedge/asw/common/util/ASWException.java | 2 + .../net/geedge/asw/common/util/Constants.java | 6 +- .../net/geedge/asw/common/util/RCode.java | 24 +--- .../sys/controller/SysAuthController.java | 15 +- .../sys/controller/SysI18nController.java | 136 ++++++++++++++++++ .../sys/controller/SysRoleController.java | 24 ++-- .../sys/controller/SysUserController.java | 59 ++++---- .../geedge/asw/module/sys/dao/SysI18nDao.java | 10 ++ .../asw/module/sys/entity/SysI18nEntity.java | 53 +++++++ .../asw/module/sys/entity/SysUserEntity.java | 2 +- .../module/sys/service/ISysI18nService.java | 23 +++ .../sys/service/impl/SysAuthServiceImpl.java | 16 +-- .../sys/service/impl/SysI18nServiceImpl.java | 69 +++++++++ .../sys/service/impl/SysUserServiceImpl.java | 17 +-- src/main/resources/application-magic-api.yml | 8 +- src/main/resources/config/logback-spring.xml | 10 +- .../resources/db/migration/R__AZ_sys_i18n.sql | 44 ++++++ .../db/migration/V1.0.01__INIT_TABLES.sql | 3 +- 22 files changed, 627 insertions(+), 120 deletions(-) create mode 100644 src/main/java/net/geedge/asw/common/config/I18nConfig.java create mode 100644 src/main/java/net/geedge/asw/common/config/LocaleConfig.java create mode 100644 src/main/java/net/geedge/asw/module/sys/controller/SysI18nController.java create mode 100644 src/main/java/net/geedge/asw/module/sys/dao/SysI18nDao.java create mode 100644 src/main/java/net/geedge/asw/module/sys/entity/SysI18nEntity.java create mode 100644 src/main/java/net/geedge/asw/module/sys/service/ISysI18nService.java create mode 100644 src/main/java/net/geedge/asw/module/sys/service/impl/SysI18nServiceImpl.java create mode 100644 src/main/resources/db/migration/R__AZ_sys_i18n.sql diff --git a/src/main/java/net/geedge/asw/common/config/I18nConfig.java b/src/main/java/net/geedge/asw/common/config/I18nConfig.java new file mode 100644 index 0000000..b9621c1 --- /dev/null +++ b/src/main/java/net/geedge/asw/common/config/I18nConfig.java @@ -0,0 +1,122 @@ +package net.geedge.asw.common.config; + +import cn.hutool.log.Log; +import jakarta.annotation.PostConstruct; +import net.geedge.asw.common.util.Constants; +import net.geedge.asw.common.util.T; +import net.geedge.asw.module.sys.entity.SysI18nEntity; +import net.geedge.asw.module.sys.service.ISysI18nService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ResourceLoaderAware; +import org.springframework.context.support.AbstractMessageSource; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.ResourceLoader; +import org.springframework.stereotype.Component; + +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.stream.Collectors; + +@Component +public class I18nConfig extends AbstractMessageSource implements ResourceLoaderAware { + + private static final Log log = Log.get(); + + @Autowired + private ISysI18nService sysI18nService; + + /** + * 国际化缓存 + */ + public static final Map> I18N_CACHE = new HashMap<>(); + + @PostConstruct + public void init() { + this.reload(); + } + + /** + * 重新将数据库中的国际化配置加载 + */ + public void reload() { + I18N_CACHE.clear(); + } + + + protected ResourceLoader resourceLoader; + + @Override + public void setResourceLoader(ResourceLoader resourceLoader) { + this.resourceLoader = (resourceLoader == null ? new DefaultResourceLoader() : resourceLoader); + } + + @Override + protected MessageFormat resolveCode(String code, Locale locale) { + String msg = this.getSourceFromCache(code, locale); + MessageFormat messageFormat = new MessageFormat(msg, locale); + return messageFormat; + } + + @Override + protected String resolveCodeWithoutArguments(String code, Locale locale) { + return this.getSourceFromCache(code, locale); + } + + /** + * 从缓存中取出国际化配置对应的数据 或者从父级获取 + * + * @param code + * @param locale + * @param param + * @return + */ + public String getSourceFromCache(String code, Locale locale, Object... param) { + String language = locale.getLanguage(); + if (T.ObjectUtil.isEmpty(I18N_CACHE)) { + this.loadAllMessageResourcesFromDB(); + } + Map props = I18N_CACHE.get(language); + if (null != props && props.containsKey(code)) { + String msg = props.get(code); + if (T.ObjectUtil.isEmpty(param)) { + return msg; + } + return MessageFormat.format(msg, param); + } else { + try { + if (null != this.getParentMessageSource()) { + return this.getParentMessageSource().getMessage(code, param, locale); + } + } catch (Exception e) { + log.error(e); + } + return code; + } + } + + /** + * 从数据库中获取所有国际化配置 + */ + public void loadAllMessageResourcesFromDB() { + List list = sysI18nService.list(); + if (T.CollUtil.isNotEmpty(list)) { + try { + for (String lang : Constants.LANG_LIST) { + Map langMap = I18N_CACHE.get(lang); + langMap = T.ObjectUtil.defaultIfNull(langMap, new HashMap<>()); + I18N_CACHE.put(lang, langMap); + + List dataList = list.stream().filter(pojo -> T.StrUtil.equals(lang, pojo.getLang())).collect(Collectors.toList()); + for (SysI18nEntity entity : dataList) { + langMap.put(entity.getCode(), entity.getValue()); + } + } + } catch (Exception e) { + log.error(e); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/net/geedge/asw/common/config/LocaleConfig.java b/src/main/java/net/geedge/asw/common/config/LocaleConfig.java new file mode 100644 index 0000000..9908dee --- /dev/null +++ b/src/main/java/net/geedge/asw/common/config/LocaleConfig.java @@ -0,0 +1,66 @@ +package net.geedge.asw.common.config; + +import cn.hutool.log.Log; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import net.geedge.asw.common.util.T; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; +import org.springframework.web.servlet.i18n.SessionLocaleResolver; + +import java.util.Locale; + +@Configuration +@EnableAutoConfiguration +@ComponentScan +public class LocaleConfig implements WebMvcConfigurer { + + @Bean + public LocaleResolver localeResolver() { + SessionLocaleResolver slr = new SessionLocaleResolver(); + // 默认语言 + slr.setDefaultLocale(Locale.of("en")); + return slr; + } + + @Bean + public LocaleChangeInterceptor localeChangeInterceptor() { + MyI18nInterceptor lci = new MyI18nInterceptor(); + // 参数名 + lci.setParamName("Language"); + return lci; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(localeChangeInterceptor()); + } + +} + +class MyI18nInterceptor extends LocaleChangeInterceptor { + + private static final Log log = Log.get(); + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + try { + String language = request.getHeader(getParamName()); + if (T.ObjectUtil.isNotEmpty(language)) { + Locale locale = parseLocaleValue(language); + LocaleContextHolder.setLocale(locale); + } + } catch (Exception e) { + log.error(e, "[preHandle] [error]"); + } + return true; + } + +} \ No newline at end of file diff --git a/src/main/java/net/geedge/asw/common/config/SaTokenConfigure.java b/src/main/java/net/geedge/asw/common/config/SaTokenConfigure.java index 958c36c..3ebbd07 100644 --- a/src/main/java/net/geedge/asw/common/config/SaTokenConfigure.java +++ b/src/main/java/net/geedge/asw/common/config/SaTokenConfigure.java @@ -47,7 +47,7 @@ public class SaTokenConfigure implements WebMvcConfigurer { // 注册 Sa-Token 拦截器,打开注解式鉴权功能 registry.addInterceptor(new SaInterceptor(handler -> { SaRouter.match("/file/**").notMatch("/file/content/*").check(r -> StpUtil.checkLogin()); - SaRouter.match("/sys/**").notMatch("/sys/login").check(r -> StpUtil.checkLogin()); + SaRouter.match("/api/v1/**").notMatch("/api/v1/login").check(r -> StpUtil.checkLogin()); })).addPathPatterns("/**"); } } diff --git a/src/main/java/net/geedge/asw/common/config/exception/ASWExceptionHandler.java b/src/main/java/net/geedge/asw/common/config/exception/ASWExceptionHandler.java index c91c9db..c61c312 100644 --- a/src/main/java/net/geedge/asw/common/config/exception/ASWExceptionHandler.java +++ b/src/main/java/net/geedge/asw/common/config/exception/ASWExceptionHandler.java @@ -1,18 +1,20 @@ package net.geedge.asw.common.config.exception; -import org.apache.catalina.connector.ClientAbortException; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestControllerAdvice; - import cn.dev33.satoken.exception.NotLoginException; import cn.hutool.log.Log; import jakarta.servlet.http.HttpServletRequest; import net.geedge.asw.common.util.ASWException; import net.geedge.asw.common.util.R; import net.geedge.asw.common.util.RCode; +import net.geedge.asw.common.util.T; +import net.geedge.asw.module.sys.service.ISysI18nService; +import org.apache.catalina.connector.ClientAbortException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; /** * 异常处理器 @@ -22,6 +24,9 @@ public class ASWExceptionHandler { private static final Log log = Log.get(); + @Autowired + private ISysI18nService sysI18nService; + /** * 处理自定义异常 */ @@ -29,7 +34,19 @@ public class ASWExceptionHandler { @ResponseStatus(value = HttpStatus.BAD_REQUEST) public R handleDHException(ASWException e, HttpServletRequest request) { log.warn(e, "Request uri: {}", request.getRequestURI()); - return R.error(e.getCode(), e.getMsg()); + R r = new R(); + r.put("code", e.getCode()); + String msg = ""; + // by code + if (T.ObjectUtil.isNotEmpty(e.getCode())) { + msg = sysI18nService.queryValueByName(T.StrUtil.toString(e.getCode()), e.getParam()); + } + if (T.StrUtil.isEmpty(msg) && (T.ObjectUtil.isEmpty(e.getRCode()) || T.ObjectUtil.equals(msg, e.getRCode().toString()))) { + r.put("msg", e.getMsg()); + } else { + r.put("msg", msg); + } + return r; } /** @@ -49,7 +66,8 @@ public class ASWExceptionHandler { @ResponseStatus(value = HttpStatus.BAD_REQUEST) public R handleDuplicateKeyException(DuplicateKeyException e, HttpServletRequest request) { log.error(e, "Request uri: {}", request.getRequestURI()); - return R.error(RCode.SYS_DUPLICATE_RECORD); + String msg = sysI18nService.queryValueByName(RCode.SYS_DUPLICATE_RECORD.getCode().toString()); + return R.error(RCode.SYS_DUPLICATE_RECORD.getCode(), msg); } @ExceptionHandler(Exception.class) diff --git a/src/main/java/net/geedge/asw/common/util/ASWException.java b/src/main/java/net/geedge/asw/common/util/ASWException.java index 47d56d9..eb47acc 100644 --- a/src/main/java/net/geedge/asw/common/util/ASWException.java +++ b/src/main/java/net/geedge/asw/common/util/ASWException.java @@ -12,12 +12,14 @@ public class ASWException extends RuntimeException { private String msg = RCode.ERROR.getMsg(); private int code = RCode.ERROR.getCode(); private Object[] param = new Object[] {}; + private RCode rCode; public ASWException(RCode rCode) { super(rCode.getMsg()); this.code = rCode.getCode(); this.msg = rCode.getMsg(); this.param = rCode.getParam(); + this.rCode = rCode; } public ASWException(String msg) { diff --git a/src/main/java/net/geedge/asw/common/util/Constants.java b/src/main/java/net/geedge/asw/common/util/Constants.java index d2e343f..b2f6fd6 100644 --- a/src/main/java/net/geedge/asw/common/util/Constants.java +++ b/src/main/java/net/geedge/asw/common/util/Constants.java @@ -1,6 +1,7 @@ package net.geedge.asw.common.util; import java.io.File; +import java.util.List; public class Constants { @@ -14,6 +15,9 @@ public class Constants { */ public static final String TEMP_PATH = System.getProperty("user.dir") + File.separator + "tmp"; + /** + * 国际化语言列表 + */ + public static final List LANG_LIST = T.ListUtil.of("en", "zh"); - } diff --git a/src/main/java/net/geedge/asw/common/util/RCode.java b/src/main/java/net/geedge/asw/common/util/RCode.java index f242b15..6308e8a 100644 --- a/src/main/java/net/geedge/asw/common/util/RCode.java +++ b/src/main/java/net/geedge/asw/common/util/RCode.java @@ -5,7 +5,7 @@ import java.text.MessageFormat; public enum RCode { /** - * 10**** : 系统认证 或 通用错误提示 20**** : screen module + * 10**** : 系统认证 或 通用错误提示 20**** : sys module */ ERROR(999, "error"), // 通用错误/未知错误 @@ -19,28 +19,6 @@ public enum RCode { USER_NO_LOGIN(100007, "user not login"), // 用户未登录 SYS_RECORD_NOT_FOUND(100008, "record not found"),// 未找到记录 - - SCREEN_ID_CANNOT_EMPTY(200001, "id cannot be empty"), - - SCREE_DATASOURCE_DEFAULT_CANNOT_BE_DELETE(300001,"The default data source cannot be deleted."), - SCREE_DATASOURCE_REPEAT(300002,"Screen datasource name duplicate"), - - /** - * import - */ - EXCELFILE_TYPE_ERROR(400001, "The type can only be xlsx, json, csv"), - EXCELFILE_PARSE_ERROR(400002, "Import file resolution failed"), - EXCELFILE_HEADER_TEMPLATE_ERROR(400003,"The header row of the import template is inconsistent with the system template"), - EXCELFILE_HEADER_LANGUAGE_ERROR(400004, "Language must be en, zh or ru"), - EXCELFILE_IMPORT_FILE_ISNULL(400005, "Import file is null"), - EXCELFILE_HEADER_LANGUAGE_ISNULL(400006, "Language can not be empty"), - EXCELFILE_IMPORT_ERROR(400007, "File import error"), - EXCELFILE_SCHEDULE_TASK_IS_NULL(400008, "Schedule task can not be empty"), - EXCELFILE_SCHEDULE_CRON_IS_NULL(400009, "Schedule cron can not be empty"), - EXCELFILE_SCHEDULE_ENABLE_IS_NULL(400010, "Schedule enable can not be empty"), - EXCELFILE_SCHEDULE_SCRIPT_IS_NULL(400011, "Schedule script can not be empty"), - - SUCCESS(200, "success"); // 成功 private RCode(Integer code, String msg) { diff --git a/src/main/java/net/geedge/asw/module/sys/controller/SysAuthController.java b/src/main/java/net/geedge/asw/module/sys/controller/SysAuthController.java index 0fb8979..0a1ff37 100644 --- a/src/main/java/net/geedge/asw/module/sys/controller/SysAuthController.java +++ b/src/main/java/net/geedge/asw/module/sys/controller/SysAuthController.java @@ -1,14 +1,5 @@ package net.geedge.asw.module.sys.controller; -import java.util.Map; - -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.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - import cn.dev33.satoken.stp.SaTokenInfo; import cn.dev33.satoken.stp.StpUtil; import net.geedge.asw.common.util.R; @@ -16,9 +7,13 @@ import net.geedge.asw.common.util.RCode; import net.geedge.asw.common.util.T; import net.geedge.asw.module.sys.entity.SysUserEntity; import net.geedge.asw.module.sys.service.ISysAuthService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; @RestController -@RequestMapping("/sys") +@RequestMapping("/api/v1") public class SysAuthController { @Autowired diff --git a/src/main/java/net/geedge/asw/module/sys/controller/SysI18nController.java b/src/main/java/net/geedge/asw/module/sys/controller/SysI18nController.java new file mode 100644 index 0000000..88f4887 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/controller/SysI18nController.java @@ -0,0 +1,136 @@ +package net.geedge.asw.module.sys.controller; + +import cn.dev33.satoken.stp.StpUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import net.geedge.asw.common.util.ASWException; +import net.geedge.asw.common.util.R; +import net.geedge.asw.common.util.RCode; +import net.geedge.asw.common.util.T; +import net.geedge.asw.module.sys.entity.SysI18nEntity; +import net.geedge.asw.module.sys.entity.SysUserEntity; +import net.geedge.asw.module.sys.service.ISysI18nService; +import net.geedge.asw.module.sys.service.ISysUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/api/v1/i18n") +public class SysI18nController { + + @Autowired + private ISysUserService userService; + + @Autowired + private ISysI18nService sysI18nService; + + @GetMapping("/{id}") + public R detail(@PathVariable("id") String id) { + SysI18nEntity entity = sysI18nService.getById(id); + if (T.ObjectUtil.isNotNull(entity)) { + SysUserEntity user = userService.getById(entity.getUid()); + user.setPwd(null); + entity.setUpdateUser(user); + } + return R.ok().putData("record", entity); + } + + @GetMapping + public R list(String ids, String q, String lang, String code, String value, + @RequestParam(defaultValue = "1") Integer current, + @RequestParam(defaultValue = "20") Integer size, + @RequestParam(defaultValue = "name") String orderBy) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.in(T.StrUtil.isNotEmpty(ids), "id", T.StrUtil.split(ids, ',')); + if (T.StrUtil.isNotBlank(q)) { + queryWrapper.and(wrapper -> wrapper.like("lang", q) + .or().like("code", q) + .or().like("value", q) + .or().like("remark", q) + ); + } + queryWrapper.eq(T.StrUtil.isNotEmpty(lang), "lang", lang); + queryWrapper.like(T.StrUtil.isNotEmpty(code), "code", code); + queryWrapper.like(T.StrUtil.isNotEmpty(value), "value", value); + Page page = Page.of(current, size); + page.addOrder(T.PageUtil.decodeOrderByStr(orderBy)); + page = sysI18nService.page(page, queryWrapper); + return R.ok(page); + } + + + @PostMapping + public R add(@RequestBody SysI18nEntity entity) { + T.VerifyUtil.is(entity).notNull() + .and(entity.getCode()).notEmpty() + .and(entity.getValue()).notEmpty() + .and(entity.getLang()).notEmpty(); + SysI18nEntity one = sysI18nService.getOne(new LambdaQueryWrapper() + .eq(SysI18nEntity::getCode, entity.getCode()) + .eq(SysI18nEntity::getLang, entity.getLang())); + if (T.ObjectUtil.isNotNull(one)) { + throw ASWException.builder().rcode(RCode.SYS_DUPLICATE_RECORD).build(); + } + if (T.ObjectUtil.isEmpty(entity.getName())) { + entity.setName(entity.getCode()); + } + entity.setUts(System.currentTimeMillis()); + entity.setUid(StpUtil.getLoginIdAsString()); + sysI18nService.save(entity); + return R.ok().putData("id", entity.getId()); + } + + @PutMapping + public R update(@RequestBody SysI18nEntity entity) { + T.VerifyUtil.is(entity).notNull() + .and(entity.getId()).notEmpty(RCode.ID_CANNOT_EMPTY) + .and(entity.getCode()).notEmpty() + .and(entity.getValue()).notEmpty() + .and(entity.getLang()).notEmpty(); + SysI18nEntity one = sysI18nService.getOne(new LambdaQueryWrapper() + .eq(SysI18nEntity::getCode, entity.getCode()) + .eq(SysI18nEntity::getLang, entity.getLang()) + .ne(SysI18nEntity::getId, entity.getId())); + if (T.ObjectUtil.isNotNull(one)) { + throw ASWException.builder().rcode(RCode.SYS_DUPLICATE_RECORD).build(); + } + if (T.ObjectUtil.isEmpty(entity.getName())) { + entity.setName(entity.getCode()); + } + entity.setUts(System.currentTimeMillis()); + entity.setUid(StpUtil.getLoginIdAsString()); + sysI18nService.updateById(entity); + return R.ok().putData("id", entity.getId()); + } + + @DeleteMapping + public R delete(String[] ids) { + T.VerifyUtil.is(ids).notEmpty(); + sysI18nService.removeBatchByIds(T.ListUtil.of(ids)); + return R.ok(); + } + + @GetMapping("/lang") + public R lang(@RequestParam(required = false, defaultValue = "en,zh") String l) { + List langList = T.StrUtil.split(l, ','); + Map result = T.MapUtil.builder().build(); + for (String lang : langList) { + List dataList = sysI18nService.list(new LambdaQueryWrapper().eq(SysI18nEntity::getLang, lang)); + Map collect = dataList.stream().collect(Collectors.toMap(SysI18nEntity::getCode, SysI18nEntity::getValue)); + result.put(lang, collect); + } + return R.ok(result); + } + + @PutMapping("/clearCache") + public R clearCache() { + sysI18nService.clearCache(); + return R.ok(); + } + +} diff --git a/src/main/java/net/geedge/asw/module/sys/controller/SysRoleController.java b/src/main/java/net/geedge/asw/module/sys/controller/SysRoleController.java index 80c00e5..5afe814 100644 --- a/src/main/java/net/geedge/asw/module/sys/controller/SysRoleController.java +++ b/src/main/java/net/geedge/asw/module/sys/controller/SysRoleController.java @@ -1,29 +1,19 @@ package net.geedge.asw.module.sys.controller; -import org.springframework.beans.factory.annotation.Autowired; -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.RequestParam; -import org.springframework.web.bind.annotation.RestController; - +import cn.hutool.log.Log; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; - -import cn.hutool.log.Log; import net.geedge.asw.common.util.ASWException; import net.geedge.asw.common.util.R; import net.geedge.asw.common.util.RCode; import net.geedge.asw.common.util.T; import net.geedge.asw.module.sys.entity.SysRoleEntity; import net.geedge.asw.module.sys.service.ISysRoleService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; @RestController -@RequestMapping("/sys/role") +@RequestMapping("/api/v1/role") public class SysRoleController { private static final Log log = Log.get(); @@ -37,8 +27,10 @@ public class SysRoleController { } @GetMapping - public R list(String ids, String name, @RequestParam(defaultValue = "1") Integer current, - @RequestParam(defaultValue = "20") Integer size, @RequestParam(defaultValue = "name") String orderBy) { + public R list(String ids, String name, + @RequestParam(defaultValue = "1") Integer current, + @RequestParam(defaultValue = "20") Integer size, + @RequestParam(defaultValue = "name") String orderBy) { QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.like(T.StrUtil.isNotBlank(name), "name", name).in(T.StrUtil.isNotBlank(ids), "id", ids.split(",")); Page page = Page.of(current, size); diff --git a/src/main/java/net/geedge/asw/module/sys/controller/SysUserController.java b/src/main/java/net/geedge/asw/module/sys/controller/SysUserController.java index 328b8a5..f97314c 100644 --- a/src/main/java/net/geedge/asw/module/sys/controller/SysUserController.java +++ b/src/main/java/net/geedge/asw/module/sys/controller/SysUserController.java @@ -1,34 +1,24 @@ package net.geedge.asw.module.sys.controller; -import java.util.List; - -import org.springframework.beans.factory.annotation.Autowired; -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.RequestParam; -import org.springframework.web.bind.annotation.RestController; - +import cn.hutool.log.Log; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; - -import cn.hutool.log.Log; -import net.geedge.asw.common.util.ASWException; -import net.geedge.asw.common.util.Constants; -import net.geedge.asw.common.util.R; -import net.geedge.asw.common.util.RCode; -import net.geedge.asw.common.util.T; +import net.geedge.asw.common.util.*; import net.geedge.asw.module.sys.entity.SysRoleEntity; import net.geedge.asw.module.sys.entity.SysUserEntity; +import net.geedge.asw.module.sys.entity.SysUserRoleEntity; import net.geedge.asw.module.sys.service.ISysRoleService; +import net.geedge.asw.module.sys.service.ISysUserRoleService; import net.geedge.asw.module.sys.service.ISysUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.Collectors; @RestController -@RequestMapping("/sys/user") +@RequestMapping("/api/v1/user") public class SysUserController { private static final Log log = Log.get(); @@ -36,24 +26,35 @@ public class SysUserController { private ISysUserService userService; @Autowired private ISysRoleService roleService; + @Autowired + private ISysUserRoleService uerRoleService; @GetMapping("/{id}") public R detail(@PathVariable("id") String id) { SysUserEntity entity = userService.getById(id); - entity.setPwd(null); - List roleList = roleService.listByIds(T.ListUtil.of(entity.getRoleIds())); - entity.setRoles(roleList); + if (T.ObjectUtil.isNotNull(entity)) { + entity.setPwd(null); + List userRoleList = uerRoleService.list(new LambdaQueryWrapper().eq(SysUserRoleEntity::getUserId, entity.getId())); + if (T.CollUtil.isNotEmpty(userRoleList)) { + List roleIds = userRoleList.stream().map(SysUserRoleEntity::getRoleId).collect(Collectors.toList()); + List roleList = roleService.listByIds(roleIds); + entity.setRoles(roleList); + } + } return R.ok().putData("record", entity); } @GetMapping - public R list(String name, @RequestParam(defaultValue = "1") Integer current, - @RequestParam(defaultValue = "20") Integer size, @RequestParam(defaultValue = "name") String orderBy) { - QueryWrapper queryWrapper = new QueryWrapper(); + public R list(String ids, String q, + @RequestParam(defaultValue = "1") Integer current, + @RequestParam(defaultValue = "20") Integer size, + @RequestParam(defaultValue = "name") String orderBy) { + QueryWrapper queryWrapper = new QueryWrapper<>(); // 不查询 pwd 列 queryWrapper.select(SysUserEntity.class, entity -> !entity.getColumn().equals("pwd")); - if (T.StrUtil.isNotBlank(name)) { - queryWrapper.and(wrapper -> wrapper.like("name", name).or().like("user_name", name)); + queryWrapper.in(T.StrUtil.isNotEmpty(ids), "id", T.StrUtil.split(ids, ',')); + if (T.StrUtil.isNotBlank(q)) { + queryWrapper.and(wrapper -> wrapper.like("name", q).or().like("user_name", q)); } Page page = Page.of(current, size); page.addOrder(T.PageUtil.decodeOrderByStr(orderBy)); diff --git a/src/main/java/net/geedge/asw/module/sys/dao/SysI18nDao.java b/src/main/java/net/geedge/asw/module/sys/dao/SysI18nDao.java new file mode 100644 index 0000000..8db1c11 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/dao/SysI18nDao.java @@ -0,0 +1,10 @@ +package net.geedge.asw.module.sys.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import net.geedge.asw.module.sys.entity.SysI18nEntity; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface SysI18nDao extends BaseMapper { + +} diff --git a/src/main/java/net/geedge/asw/module/sys/entity/SysI18nEntity.java b/src/main/java/net/geedge/asw/module/sys/entity/SysI18nEntity.java new file mode 100644 index 0000000..a99ef00 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/entity/SysI18nEntity.java @@ -0,0 +1,53 @@ +package net.geedge.asw.module.sys.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 lombok.Data; + +import java.io.Serializable; + +@Data +@TableName("sys_i18n") +public class SysI18nEntity implements Serializable { + + private static final long serialVersionUID = 1L; + /** + * 主键 + */ + @TableId(type = IdType.AUTO) + private Long id; + /** + * 名称 + */ + private String name; + /** + * i18n code + */ + private String code; + /** + * 语言 + */ + private String lang; + /** + * 翻译值 + */ + private String value; + /** + * 备注信息 + */ + private String remark; + /** + * 操作人 + */ + private String uid; + /** + * 操作时间 + */ + private Long uts; + + @TableField(exist = false) + private SysUserEntity updateUser; + +} diff --git a/src/main/java/net/geedge/asw/module/sys/entity/SysUserEntity.java b/src/main/java/net/geedge/asw/module/sys/entity/SysUserEntity.java index d9ad678..803aa6c 100644 --- a/src/main/java/net/geedge/asw/module/sys/entity/SysUserEntity.java +++ b/src/main/java/net/geedge/asw/module/sys/entity/SysUserEntity.java @@ -20,7 +20,7 @@ public class SysUserEntity { private String userName; private String pwd; @TableField(exist = false) - private String[] roleIds; + private String roleIds; @TableField(exist = false) private List roles; private Long createTimestamp; diff --git a/src/main/java/net/geedge/asw/module/sys/service/ISysI18nService.java b/src/main/java/net/geedge/asw/module/sys/service/ISysI18nService.java new file mode 100644 index 0000000..7eb7e92 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/service/ISysI18nService.java @@ -0,0 +1,23 @@ +package net.geedge.asw.module.sys.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import net.geedge.asw.module.sys.entity.SysI18nEntity; + +import java.util.Locale; + +public interface ISysI18nService extends IService { + + String queryValue(String code); + + String queryValue(String code, Object... param); + + String queryValue(String code, String lang); + + String queryValue(String code, Locale locale, Object... param); + + String queryValueByName(String name, Object... param); + + void clearCache(); + +} + diff --git a/src/main/java/net/geedge/asw/module/sys/service/impl/SysAuthServiceImpl.java b/src/main/java/net/geedge/asw/module/sys/service/impl/SysAuthServiceImpl.java index 5de1c45..ec39cf5 100644 --- a/src/main/java/net/geedge/asw/module/sys/service/impl/SysAuthServiceImpl.java +++ b/src/main/java/net/geedge/asw/module/sys/service/impl/SysAuthServiceImpl.java @@ -1,16 +1,8 @@ package net.geedge.asw.module.sys.service.impl; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; - import cn.dev33.satoken.stp.StpUtil; import cn.hutool.log.Log; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import net.geedge.asw.common.util.ASWException; import net.geedge.asw.common.util.Constants; import net.geedge.asw.common.util.RCode; @@ -21,6 +13,12 @@ import net.geedge.asw.module.sys.entity.SysMenuEntity; import net.geedge.asw.module.sys.entity.SysRoleEntity; import net.geedge.asw.module.sys.entity.SysUserEntity; import net.geedge.asw.module.sys.service.ISysAuthService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; @Service public class SysAuthServiceImpl implements ISysAuthService { diff --git a/src/main/java/net/geedge/asw/module/sys/service/impl/SysI18nServiceImpl.java b/src/main/java/net/geedge/asw/module/sys/service/impl/SysI18nServiceImpl.java new file mode 100644 index 0000000..5cf1c5c --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/service/impl/SysI18nServiceImpl.java @@ -0,0 +1,69 @@ +package net.geedge.asw.module.sys.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 net.geedge.asw.common.config.I18nConfig; +import net.geedge.asw.common.util.T; +import net.geedge.asw.module.sys.dao.SysI18nDao; +import net.geedge.asw.module.sys.entity.SysI18nEntity; +import net.geedge.asw.module.sys.service.ISysI18nService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Locale; + +@Service +public class SysI18nServiceImpl extends ServiceImpl implements ISysI18nService { + + @Autowired + private I18nConfig i18nConfig; + + @Override + public String queryValue(String code) { + return this.queryValue(code, this.getLocale()); + } + + @Override + public String queryValue(String code, Object... param) { + return this.queryValue(code, this.getLocale(), param); + } + + @Override + public String queryValue(String code, String lang) { + return this.queryValue(code, Locale.of(lang)); + } + + @Override + public String queryValue(String code, Locale locale, Object... param) { + return i18nConfig.getSourceFromCache(code, locale, param); + } + + @Override + public String queryValueByName(String name, Object... param) { + String language = this.getLocale().getLanguage(); + List list = this.list(new LambdaQueryWrapper().eq(SysI18nEntity::getName, name).eq(SysI18nEntity::getLang, language)); + if (T.CollUtil.isEmpty(list)) { + return StrUtil.EMPTY; + } + SysI18nEntity sysI18nEntity = list.stream().findFirst().get(); + return this.queryValue(sysI18nEntity.getCode(), this.getLocale(), param); + } + + @Override + public void clearCache() { + i18nConfig.reload(); + } + + /** + * getLocale + * + * @return + */ + private Locale getLocale() { + return LocaleContextHolder.getLocale(); + } + +} diff --git a/src/main/java/net/geedge/asw/module/sys/service/impl/SysUserServiceImpl.java b/src/main/java/net/geedge/asw/module/sys/service/impl/SysUserServiceImpl.java index a89aa44..4eeb300 100644 --- a/src/main/java/net/geedge/asw/module/sys/service/impl/SysUserServiceImpl.java +++ b/src/main/java/net/geedge/asw/module/sys/service/impl/SysUserServiceImpl.java @@ -1,20 +1,17 @@ package net.geedge.asw.module.sys.service.impl; -import java.util.List; -import java.util.stream.Stream; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; - import net.geedge.asw.common.util.T; import net.geedge.asw.module.sys.dao.SysUserDao; import net.geedge.asw.module.sys.entity.SysUserEntity; import net.geedge.asw.module.sys.entity.SysUserRoleEntity; import net.geedge.asw.module.sys.service.ISysUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; @Service public class SysUserServiceImpl extends ServiceImpl implements ISysUserService { @@ -32,9 +29,9 @@ public class SysUserServiceImpl extends ServiceImpl i this.saveOrUpdate(entity); // 保存 user role关系 List urList = T.ListUtil.list(false); - Stream.of(entity.getRoleIds()).forEach(roleId -> { + for (String roleId : T.StrUtil.split(entity.getRoleIds(), ",")) { urList.add(SysUserRoleEntity.builder().roleId(roleId).userId(entity.getId()).build()); - }); + } userRoleService.saveBatch(urList); } diff --git a/src/main/resources/application-magic-api.yml b/src/main/resources/application-magic-api.yml index 64266ce..038d2d1 100644 --- a/src/main/resources/application-magic-api.yml +++ b/src/main/resources/application-magic-api.yml @@ -62,13 +62,13 @@ magic-api: swagger: version: 1.0 description: Digital Horizon API 接口信息 - title: DH API Swagger Docs - name: DH API 接口 - location: /v2/api-docs/dh-api/swagger2.json + title: ASW Controller Swagger Docs + name: ASW Controller 接口 + location: /v2/api-docs/asw-controller/swagger2.json debug: timeout: 60 # 断点超时时间,默认60s task: - thread-name-prefix: dh-task- #线程池名字前缀 + thread-name-prefix: asw-controller-task- #线程池名字前缀 log: true # 打印日志 pool: size: 8 #线程池大小,默认值为CPU核心数 diff --git a/src/main/resources/config/logback-spring.xml b/src/main/resources/config/logback-spring.xml index 1dbdc8a..e107ee2 100644 --- a/src/main/resources/config/logback-spring.xml +++ b/src/main/resources/config/logback-spring.xml @@ -7,7 +7,7 @@ - + @@ -20,7 +20,7 @@ - ${log.path}/dh-web.log + ${log.path}/asw-controller.log ${out.pattern} @@ -28,7 +28,7 @@ - ${log.path}/dh-web-%d{yyyy-MM-dd}.%i.log + ${log.path}/asw-controller-%d{yyyy-MM-dd}.%i.log ${max.file.size} ${max.history} ${total.size.cap} @@ -38,7 +38,7 @@ - ${log.path}/dh-web-error.log + ${log.path}/asw-controller-error.log ${out.pattern} @@ -46,7 +46,7 @@ - ${log.path}/dh-web-error-%d{yyyy-MM-dd}.%i.log + ${log.path}/asw-controller-error-%d{yyyy-MM-dd}.%i.log ${max.file.size} ${max.history} ${total.size.cap} diff --git a/src/main/resources/db/migration/R__AZ_sys_i18n.sql b/src/main/resources/db/migration/R__AZ_sys_i18n.sql new file mode 100644 index 0000000..dd5d3b0 --- /dev/null +++ b/src/main/resources/db/migration/R__AZ_sys_i18n.sql @@ -0,0 +1,44 @@ +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for sys_i18n +-- ---------------------------- +DROP TABLE IF EXISTS `sys_i18n`; +CREATE TABLE `sys_i18n` ( + `id` int(10) NOT NULL AUTO_INCREMENT, + `name` varchar(128) NOT NULL COMMENT '名称', + `code` varchar(128) NOT NULL COMMENT 'code码', + `value` longtext NOT NULL COMMENT '翻译值', + `lang` varchar(64) NOT NULL COMMENT '语言', + `remark` varchar(512) NOT NULL DEFAULT '' COMMENT '备注', + `uid` varchar(64) NOT NULL COMMENT '更新人', + `uts` bigint(20) NOT NULL COMMENT '更新时间', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_lang_code`(`lang`, `code`) USING BTREE +) ENGINE=InnoDB ROW_FORMAT=Dynamic DEFAULT CHARSET=utf8mb4; + + +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (1, '999', 'ERROR', 'error', 'en', '', 'admin', 1719280800000); +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (3, '999', 'ERROR', '错误', 'zh', '', 'admin', 1719280800000); +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (5, '100001', 'SYS_USER_PWD_ERROR', 'username or password error', 'en', '', 'admin', 1719280800000); +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (7, '100001', 'SYS_USER_PWD_ERROR', '用户名或密码错误', 'zh', '', 'admin', 1719280800000); +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (9, '100002', 'SYS_DUPLICATE_RECORD', 'duplicate record', 'en', '', 'admin', 1719280800000); +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (11, '100002', 'SYS_DUPLICATE_RECORD', '重复记录', 'zh', '', 'admin', 1719280800000); +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (13, '100003', 'SYS_NO_AUTH', 'Permission denied', 'en', '', 'admin', 1719280800000); +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (15, '100003', 'SYS_NO_AUTH', '没有权限', 'zh', '', 'admin', 1719280800000); +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (17, '100004', 'ID_CANNOT_EMPTY', 'id cannot be empty', 'en', '', 'admin', 1719280800000); +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (19, '100004', 'ID_CANNOT_EMPTY', 'id不能为空', 'zh', '', 'admin', 1719280800000); +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (21, '100005', 'NAME_CANNOT_EMPTY', 'name cannot be empty', 'en', '', 'admin', 1719280800000); +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (23, '100005', 'NAME_CANNOT_EMPTY', '名称不能为空', 'zh', '', 'admin', 1719280800000); +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (25, '100006', 'PARAM_CANNOT_EMPTY', 'parameter cannot be empty', 'en', '', 'admin', 1719280800000); +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (27, '100006', 'PARAM_CANNOT_EMPTY', '参数不能为空', 'zh', '', 'admin', 1719280800000); +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (29, '100007', 'USER_NO_LOGIN', 'user not login', 'en', '', 'admin', 1719280800000); +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (31, '100007', 'USER_NO_LOGIN', '用户未登录', 'zh', '', 'admin', 1719280800000); +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (33, '100008', 'SYS_RECORD_NOT_FOUND', 'record not found', 'en', '', 'admin', 1719280800000); +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (35, '100008', 'SYS_RECORD_NOT_FOUND', '找不到记录', 'zh', '', 'admin', 1719280800000); +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (37, '200', 'SUCCESS', 'success', 'en', '', 'admin', 1719280800000); +INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `uid`, `uts`) VALUES (39, '200', 'SUCCESS', '成功', 'zh', '', 'admin', 1719280800000); + + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/src/main/resources/db/migration/V1.0.01__INIT_TABLES.sql b/src/main/resources/db/migration/V1.0.01__INIT_TABLES.sql index c46f34d..fb2a4c6 100644 --- a/src/main/resources/db/migration/V1.0.01__INIT_TABLES.sql +++ b/src/main/resources/db/migration/V1.0.01__INIT_TABLES.sql @@ -56,8 +56,7 @@ CREATE TABLE `sys_menu` ( DROP TABLE IF EXISTS `sys_user_role`; CREATE TABLE `sys_user_role` ( `user_id` varchar(64) NOT NULL, - `role_id` varchar(64) NOT NULL, - PRIMARY KEY (`user_id`) USING BTREE + `role_id` varchar(64) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; DROP TABLE IF EXISTS `sys_role_menu`;