feat(tenant): 实现多租户数据隔离功能
- 新增租户管理模块(SysTenant CRUD) - 实现 TenantContext 租户上下文(基于 TransmittableThreadLocal) - 新增 TenantInterceptor HTTP拦截器,自动识别当前租户 - 新增 TenantSqlInterceptor MyBatis 拦截器,自动注入 tenant_id 过滤条件 - 支持超级管理员全局模式,可查看所有租户数据 - 扩展 LoginUser/SysUser 支持租户字段 - 新增前端租户管理页面pull/1126/head
parent
a50dc8af8a
commit
9a8ffdbb8f
17
pom.xml
17
pom.xml
|
|
@ -30,6 +30,9 @@
|
|||
<poi.version>4.1.2</poi.version>
|
||||
<velocity.version>2.3</velocity.version>
|
||||
<jwt.version>0.9.1</jwt.version>
|
||||
<!-- 多租户相关依赖版本 -->
|
||||
<transmittable-thread-local.version>2.14.3</transmittable-thread-local.version>
|
||||
<jsqlparser.version>4.6</jsqlparser.version>
|
||||
<!-- override dependency version -->
|
||||
<tomcat.version>9.0.108</tomcat.version>
|
||||
<logback.version>1.2.13</logback.version>
|
||||
|
|
@ -218,6 +221,20 @@
|
|||
<version>${ruoyi.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 多租户支持 - TransmittableThreadLocal -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>transmittable-thread-local</artifactId>
|
||||
<version>${transmittable-thread-local.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SQL解析器 - JSqlParser -->
|
||||
<dependency>
|
||||
<groupId>com.github.jsqlparser</groupId>
|
||||
<artifactId>jsqlparser</artifactId>
|
||||
<version>${jsqlparser.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
package com.ruoyi.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.ruoyi.system.domain.SysTenant;
|
||||
import com.ruoyi.system.service.ISysTenantService;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
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.PutMapping;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
* 租户信息Controller
|
||||
*
|
||||
* @author W-yf
|
||||
* @date 2025-12-19
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/link/tenant")
|
||||
public class SysTenantController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private ISysTenantService sysTenantService;
|
||||
|
||||
/**
|
||||
* 查询租户信息列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('link:tenant:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(
|
||||
SysTenant sysTenant)
|
||||
{
|
||||
startPage();
|
||||
List<SysTenant> list = sysTenantService.selectSysTenantList(sysTenant);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出租户信息列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('link:tenant:export')")
|
||||
@Log(title = "租户信息", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, SysTenant sysTenant)
|
||||
{
|
||||
List<SysTenant> list = sysTenantService.selectSysTenantList(sysTenant);
|
||||
ExcelUtil<SysTenant> util = new ExcelUtil<SysTenant>(SysTenant.class);
|
||||
util.exportExcel(response, list, "租户信息数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取租户信息详细信息
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('link:tenant:query')")
|
||||
@GetMapping(value = "/{tenantId}")
|
||||
public AjaxResult getInfo(@PathVariable("tenantId") Long tenantId)
|
||||
{
|
||||
return success(sysTenantService.selectSysTenantByTenantId(tenantId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增租户信息
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('link:tenant:add')")
|
||||
@Log(title = "租户信息", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody SysTenant sysTenant)
|
||||
{
|
||||
return toAjax(sysTenantService.insertSysTenant(sysTenant));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改租户信息
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('link:tenant:edit')")
|
||||
@Log(title = "租户信息", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody SysTenant sysTenant)
|
||||
{
|
||||
return toAjax(sysTenantService.updateSysTenant(sysTenant));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除租户信息
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('link:tenant:remove')")
|
||||
@Log(title = "租户信息", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{tenantIds}")
|
||||
public AjaxResult remove(@PathVariable Long[] tenantIds)
|
||||
{
|
||||
return toAjax(sysTenantService.deleteSysTenantByTenantIds(tenantIds));
|
||||
}
|
||||
}
|
||||
|
|
@ -27,9 +27,11 @@ import com.ruoyi.common.enums.BusinessType;
|
|||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import com.ruoyi.system.domain.SysTenant;
|
||||
import com.ruoyi.system.service.ISysDeptService;
|
||||
import com.ruoyi.system.service.ISysPostService;
|
||||
import com.ruoyi.system.service.ISysRoleService;
|
||||
import com.ruoyi.system.service.ISysTenantService;
|
||||
import com.ruoyi.system.service.ISysUserService;
|
||||
|
||||
/**
|
||||
|
|
@ -53,6 +55,9 @@ public class SysUserController extends BaseController
|
|||
@Autowired
|
||||
private ISysPostService postService;
|
||||
|
||||
@Autowired
|
||||
private ISysTenantService tenantService;
|
||||
|
||||
/**
|
||||
* 获取用户列表
|
||||
*/
|
||||
|
|
@ -113,6 +118,13 @@ public class SysUserController extends BaseController
|
|||
List<SysRole> 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 (SysUser.isAdmin(SecurityUtils.getUserId()))
|
||||
{
|
||||
SysTenant query = new SysTenant();
|
||||
query.setStatus("0");
|
||||
ajax.put("tenants", tenantService.selectSysTenantList(query));
|
||||
}
|
||||
return ajax;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -119,6 +119,12 @@
|
|||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 多租户支持 - TransmittableThreadLocal -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>transmittable-thread-local</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
package com.ruoyi.common.config;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 租户配置类
|
||||
*
|
||||
* Reason: 集中管理租户相关配置,包括超级管理员列表
|
||||
*/
|
||||
public class TenantConfig {
|
||||
|
||||
/**
|
||||
* 超级管理员用户ID列表
|
||||
* Reason: 使用固定用户ID判断,简单可靠,避免角色权限体系的复杂度
|
||||
*
|
||||
* 超级管理员拥有以下特权:
|
||||
* 1. 登录后自动进入全局模式
|
||||
* 2. 可以查看所有租户的数据
|
||||
* 3. 所有操作都会记录审计日志
|
||||
*/
|
||||
public static final List<Long> SUPER_ADMIN_USER_IDS = Arrays.asList(
|
||||
1L // admin 用户
|
||||
);
|
||||
|
||||
/**
|
||||
* 忽略租户过滤的URL路径(不需要租户隔离的接口)
|
||||
* Reason: 登录、验证码等公共接口不需要租户上下文
|
||||
*/
|
||||
public static final List<String> IGNORE_URLS = Arrays.asList(
|
||||
"/login",
|
||||
"/captchaImage",
|
||||
"/logout",
|
||||
"/register"
|
||||
);
|
||||
|
||||
/**
|
||||
* 需要进行租户隔离的表名列表
|
||||
* Reason: 明确定义需要拦截的表,避免误拦截
|
||||
*/
|
||||
public static final List<String> TENANT_TABLES = Arrays.asList(
|
||||
// 若依框架表
|
||||
"sys_user",
|
||||
"sys_dept",
|
||||
"sys_role",
|
||||
"sys_post",
|
||||
"sys_notice",
|
||||
// HairLink业务表
|
||||
"hl_customer",
|
||||
"hl_member_card",
|
||||
"hl_service",
|
||||
"hl_staff",
|
||||
"hl_order"
|
||||
);
|
||||
|
||||
/**
|
||||
* 不需要租户隔离的表(白名单)
|
||||
* Reason: 这些表是全局共享的,所有租户共用
|
||||
*/
|
||||
public static final List<String> IGNORE_TABLES = Arrays.asList(
|
||||
"sys_menu",
|
||||
"sys_dict_type",
|
||||
"sys_dict_data",
|
||||
"sys_config",
|
||||
"sys_user_role",
|
||||
"sys_role_menu",
|
||||
"sys_role_dept",
|
||||
"sys_tenant",
|
||||
"sys_logininfor",
|
||||
"sys_oper_log",
|
||||
"sys_job",
|
||||
"sys_job_log"
|
||||
);
|
||||
|
||||
/**
|
||||
* 判断是否是超级管理员
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return true-超级管理员,false-普通用户
|
||||
*/
|
||||
public static boolean isSuperAdmin(Long userId) {
|
||||
if (userId == null) {
|
||||
return false;
|
||||
}
|
||||
return SUPER_ADMIN_USER_IDS.contains(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断URL是否需要忽略租户过滤
|
||||
*
|
||||
* @param url 请求URL
|
||||
* @return true-忽略,false-需要过滤
|
||||
*/
|
||||
public static boolean isIgnoreUrl(String url) {
|
||||
if (url == null) {
|
||||
return false;
|
||||
}
|
||||
return IGNORE_URLS.stream().anyMatch(url::contains);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断表是否需要租户隔离
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @return true-需要隔离,false-不需要
|
||||
*/
|
||||
public static boolean needTenantFilter(String tableName) {
|
||||
if (tableName == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String lowerTableName = tableName.toLowerCase();
|
||||
|
||||
// 白名单表不需要过滤
|
||||
if (IGNORE_TABLES.stream().anyMatch(lowerTableName::equals)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 租户表需要过滤
|
||||
return TENANT_TABLES.stream().anyMatch(lowerTableName::equals);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
package com.ruoyi.common.core.context;
|
||||
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 租户上下文 - 基于ThreadLocal实现租户ID的传递
|
||||
*
|
||||
* Reason: 使用TransmittableThreadLocal支持线程池场景下的上下文传递
|
||||
*/
|
||||
public class TenantContext {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(TenantContext.class);
|
||||
|
||||
/**
|
||||
* 支持父子线程传递的ThreadLocal
|
||||
* Reason: 若依框架使用了异步任务和线程池,普通ThreadLocal会丢失上下文
|
||||
*/
|
||||
private static final TransmittableThreadLocal<Long> TENANT_ID_HOLDER = new TransmittableThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 忽略租户过滤的标志(用于超级管理员或系统任务)
|
||||
*/
|
||||
private static final TransmittableThreadLocal<Boolean> IGNORE_TENANT = new TransmittableThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 设置当前租户ID
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
*/
|
||||
public static void setTenantId(Long tenantId) {
|
||||
if (tenantId == null) {
|
||||
log.warn("尝试设置NULL租户ID,已拒绝");
|
||||
return;
|
||||
}
|
||||
TENANT_ID_HOLDER.set(tenantId);
|
||||
log.debug("TenantContext设置租户ID: {}", tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前租户ID
|
||||
*
|
||||
* @return 租户ID
|
||||
*/
|
||||
public static Long getTenantId() {
|
||||
return TENANT_ID_HOLDER.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除租户上下文
|
||||
* Reason: 请求结束后必须清除,避免线程池复用时的上下文污染
|
||||
*/
|
||||
public static void clear() {
|
||||
TENANT_ID_HOLDER.remove();
|
||||
IGNORE_TENANT.remove();
|
||||
log.trace("TenantContext已清除");
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置忽略租户过滤(超级管理员专用)
|
||||
*
|
||||
* @param ignore 是否忽略租户过滤
|
||||
*/
|
||||
public static void setIgnore(boolean ignore) {
|
||||
IGNORE_TENANT.set(ignore);
|
||||
if (ignore) {
|
||||
log.warn("租户过滤已禁用,当前处于全局模式!请谨慎操作");
|
||||
} else {
|
||||
log.info("租户过滤已启用");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否忽略租户过滤
|
||||
*
|
||||
* @return true-忽略(全局模式),false-正常过滤
|
||||
*/
|
||||
public static boolean isIgnore() {
|
||||
Boolean ignore = IGNORE_TENANT.get();
|
||||
return ignore != null && ignore;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前租户ID(如果不存在则抛出异常)
|
||||
*
|
||||
* @return 租户ID
|
||||
* @throws com.ruoyi.common.exception.ServiceException 租户ID不存在时抛出
|
||||
*/
|
||||
public static Long getRequiredTenantId() {
|
||||
Long tenantId = getTenantId();
|
||||
if (tenantId == null && !isIgnore()) {
|
||||
log.error("【安全警告】租户上下文丢失,当前操作被拦截");
|
||||
throw new RuntimeException("租户上下文丢失,请重新登录");
|
||||
}
|
||||
return tenantId;
|
||||
}
|
||||
}
|
||||
|
|
@ -38,6 +38,10 @@ public class BaseEntity implements Serializable
|
|||
/** 备注 */
|
||||
private String remark;
|
||||
|
||||
/** 租户ID */
|
||||
@JsonIgnore
|
||||
private Long tenantId;
|
||||
|
||||
/** 请求参数 */
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private Map<String, Object> params;
|
||||
|
|
@ -115,4 +119,14 @@ public class BaseEntity implements Serializable
|
|||
{
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
public Long getTenantId()
|
||||
{
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(Long tenantId)
|
||||
{
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,6 +92,9 @@ public class SysUser extends BaseEntity
|
|||
/** 角色ID */
|
||||
private Long roleId;
|
||||
|
||||
/** 租户ID */
|
||||
private Long tenantId;
|
||||
|
||||
public SysUser()
|
||||
{
|
||||
|
||||
|
|
@ -310,6 +313,16 @@ public class SysUser extends BaseEntity
|
|||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
public Long getTenantId()
|
||||
{
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(Long tenantId)
|
||||
{
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,11 @@ public class LoginUser implements UserDetails
|
|||
*/
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
private Long tenantId;
|
||||
|
||||
/**
|
||||
* 用户唯一标识
|
||||
*/
|
||||
|
|
@ -258,6 +263,16 @@ public class LoginUser implements UserDetails
|
|||
this.user = user;
|
||||
}
|
||||
|
||||
public Long getTenantId()
|
||||
{
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(Long tenantId)
|
||||
{
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -59,6 +59,12 @@
|
|||
<artifactId>ruoyi-system</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SQL解析器 - 用于MyBatis租户拦截器 -->
|
||||
<dependency>
|
||||
<groupId>com.github.jsqlparser</groupId>
|
||||
<artifactId>jsqlparser</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
@ -23,10 +23,11 @@ import org.springframework.core.type.classreading.MetadataReader;
|
|||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.framework.interceptor.TenantSqlInterceptor;
|
||||
|
||||
/**
|
||||
* Mybatis支持*匹配扫描包
|
||||
*
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Configuration
|
||||
|
|
@ -35,6 +36,9 @@ public class MyBatisConfig
|
|||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
@Autowired
|
||||
private TenantSqlInterceptor tenantSqlInterceptor;
|
||||
|
||||
static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
|
||||
|
||||
public static String setTypeAliasesPackage(String typeAliasesPackage)
|
||||
|
|
@ -127,6 +131,10 @@ public class MyBatisConfig
|
|||
sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
|
||||
sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
|
||||
sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
|
||||
|
||||
// 【多租户】注册MyBatis租户SQL拦截器
|
||||
sessionFactory.setPlugins(tenantSqlInterceptor);
|
||||
|
||||
return sessionFactory.getObject();
|
||||
}
|
||||
}
|
||||
|
|
@ -14,10 +14,11 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|||
import com.ruoyi.common.config.RuoYiConfig;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
|
||||
import com.ruoyi.framework.interceptor.TenantInterceptor;
|
||||
|
||||
/**
|
||||
* 通用配置
|
||||
*
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Configuration
|
||||
|
|
@ -26,6 +27,9 @@ public class ResourcesConfig implements WebMvcConfigurer
|
|||
@Autowired
|
||||
private RepeatSubmitInterceptor repeatSubmitInterceptor;
|
||||
|
||||
@Autowired
|
||||
private TenantInterceptor tenantInterceptor;
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry)
|
||||
{
|
||||
|
|
@ -45,6 +49,10 @@ public class ResourcesConfig implements WebMvcConfigurer
|
|||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry)
|
||||
{
|
||||
// 【多租户】租户拦截器 - 必须在第一位,优先设置租户上下文
|
||||
registry.addInterceptor(tenantInterceptor).addPathPatterns("/**");
|
||||
|
||||
// 防重复提交拦截器
|
||||
registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
package com.ruoyi.framework.interceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import com.ruoyi.common.config.TenantConfig;
|
||||
import com.ruoyi.common.core.context.TenantContext;
|
||||
import com.ruoyi.common.core.domain.model.LoginUser;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
|
||||
/**
|
||||
* 租户拦截器 - 从LoginUser中提取tenant_id并设置到TenantContext
|
||||
*
|
||||
* Reason: 每个HTTP请求都需要识别当前租户,并将租户信息存入ThreadLocal
|
||||
*
|
||||
* 工作流程:
|
||||
* 1. 请求进入 → preHandle()
|
||||
* 2. 从SecurityContext获取LoginUser
|
||||
* 3. 判断是否是超级管理员
|
||||
* - 是:设置忽略租户过滤(全局模式)
|
||||
* - 否:提取tenant_id并设置到TenantContext
|
||||
* 4. 请求结束 → afterCompletion() → 清除TenantContext
|
||||
*/
|
||||
@Component
|
||||
public class TenantInterceptor implements HandlerInterceptor {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(TenantInterceptor.class);
|
||||
|
||||
/**
|
||||
* 请求处理前的拦截
|
||||
*/
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws Exception {
|
||||
|
||||
String requestUri = request.getRequestURI();
|
||||
|
||||
// 判断是否是忽略URL(如登录、验证码接口)
|
||||
if (TenantConfig.isIgnoreUrl(requestUri)) {
|
||||
log.trace("忽略URL租户过滤: {}", requestUri);
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
// 从SecurityContext获取当前登录用户
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
|
||||
if (loginUser != null) {
|
||||
Long userId = loginUser.getUserId();
|
||||
|
||||
// 判断是否是超级管理员
|
||||
if (TenantConfig.isSuperAdmin(userId)) {
|
||||
// 超级管理员:忽略租户过滤(全局模式)
|
||||
TenantContext.setIgnore(true);
|
||||
log.warn("【超级管理员】用户 {} (ID:{}) 进入全局模式,可查看所有租户数据",
|
||||
loginUser.getUsername(), userId);
|
||||
}
|
||||
else if (loginUser.getTenantId() != null) {
|
||||
// 普通用户:设置租户ID
|
||||
TenantContext.setTenantId(loginUser.getTenantId());
|
||||
log.debug("【租户用户】用户 {} (ID:{}) 进入租户 {} 模式",
|
||||
loginUser.getUsername(), userId, loginUser.getTenantId());
|
||||
}
|
||||
else {
|
||||
// 未分配租户的普通用户(不允许访问)
|
||||
log.error("【安全警告】用户 {} (ID:{}) 未分配租户且非超级管理员,拒绝访问",
|
||||
loginUser.getUsername(), userId);
|
||||
throw new ServiceException("用户未分配租户,请联系管理员");
|
||||
}
|
||||
} else {
|
||||
// 未登录状态(可能是匿名访问或登录接口)
|
||||
log.trace("未获取到LoginUser,跳过租户拦截: {}", requestUri);
|
||||
}
|
||||
|
||||
} catch (ServiceException e) {
|
||||
// 业务异常直接抛出
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
// 其他异常(可能是登录接口或匿名访问)
|
||||
log.trace("获取租户上下文失败: {}, URI: {}", e.getMessage(), requestUri);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求完成后的清理
|
||||
* Reason: 必须清除ThreadLocal,避免线程池复用时的上下文污染
|
||||
*/
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler, Exception ex) throws Exception {
|
||||
TenantContext.clear();
|
||||
log.trace("请求完成,TenantContext已清除");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,319 @@
|
|||
package com.ruoyi.framework.interceptor;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import net.sf.jsqlparser.expression.LongValue;
|
||||
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
|
||||
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
|
||||
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
|
||||
import net.sf.jsqlparser.schema.Column;
|
||||
import net.sf.jsqlparser.schema.Table;
|
||||
import net.sf.jsqlparser.statement.Statement;
|
||||
import net.sf.jsqlparser.statement.delete.Delete;
|
||||
import net.sf.jsqlparser.statement.select.FromItem;
|
||||
import net.sf.jsqlparser.statement.select.PlainSelect;
|
||||
import net.sf.jsqlparser.statement.select.Select;
|
||||
import net.sf.jsqlparser.statement.update.Update;
|
||||
import org.apache.ibatis.executor.statement.StatementHandler;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.SqlCommandType;
|
||||
import org.apache.ibatis.plugin.Interceptor;
|
||||
import org.apache.ibatis.plugin.Intercepts;
|
||||
import org.apache.ibatis.plugin.Invocation;
|
||||
import org.apache.ibatis.plugin.Plugin;
|
||||
import org.apache.ibatis.plugin.Signature;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.apache.ibatis.reflection.SystemMetaObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.ruoyi.common.config.TenantConfig;
|
||||
import com.ruoyi.common.core.context.TenantContext;
|
||||
|
||||
/**
|
||||
* MyBatis租户SQL拦截器 - 自动在SQL中添加 tenant_id 过滤条件
|
||||
*
|
||||
* Reason: 借鉴若依DataScope模式,通过SQL解析实现租户隔离
|
||||
*
|
||||
* 工作原理:
|
||||
* 1. 拦截MyBatis的StatementHandler.prepare()方法
|
||||
* 2. 获取原始SQL和命令类型
|
||||
* 3. 判断是否需要拦截(SELECT/UPDATE/DELETE + 租户表)
|
||||
* 4. 使用JSQLParser解析SQL
|
||||
* 5. 在WHERE子句中添加 tenant_id = ? 条件
|
||||
* 6. 将改写后的SQL设置回BoundSql
|
||||
*/
|
||||
@Component
|
||||
@Intercepts({
|
||||
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
|
||||
})
|
||||
public class TenantSqlInterceptor implements Interceptor {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(TenantSqlInterceptor.class);
|
||||
|
||||
@Override
|
||||
public Object intercept(Invocation invocation) throws Throwable {
|
||||
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
|
||||
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
|
||||
|
||||
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
|
||||
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
|
||||
|
||||
BoundSql boundSql = statementHandler.getBoundSql();
|
||||
String originalSql = boundSql.getSql();
|
||||
|
||||
// 判断是否需要拦截
|
||||
if (!shouldIntercept(sqlCommandType, originalSql)) {
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
// 超级管理员忽略租户过滤
|
||||
if (TenantContext.isIgnore()) {
|
||||
log.debug("【全局模式】跳过租户过滤: {}", originalSql.substring(0, Math.min(100, originalSql.length())));
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
// 获取租户ID
|
||||
Long tenantId = TenantContext.getTenantId();
|
||||
if (tenantId == null) {
|
||||
log.error("【安全警告】租户ID为空且非全局模式,SQL被拦截: {}",
|
||||
originalSql.substring(0, Math.min(100, originalSql.length())));
|
||||
throw new ServiceException("租户上下文丢失,请重新登录");
|
||||
}
|
||||
|
||||
// 解析并修改SQL
|
||||
try {
|
||||
String modifiedSql = processSql(originalSql, tenantId, sqlCommandType);
|
||||
if (!modifiedSql.equals(originalSql)) {
|
||||
metaObject.setValue("delegate.boundSql.sql", modifiedSql);
|
||||
log.debug("【租户SQL拦截】租户ID: {}, 原SQL: {}", tenantId,
|
||||
originalSql.substring(0, Math.min(100, originalSql.length())));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("租户SQL解析失败: {}, 原SQL: {}", e.getMessage(),
|
||||
originalSql.substring(0, Math.min(100, originalSql.length())));
|
||||
// SQL解析失败不影响执行,记录日志后继续
|
||||
}
|
||||
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否需要拦截
|
||||
*
|
||||
* 核心逻辑:通过SQL解析获取主表名,只有主表是租户表时才拦截
|
||||
* 这样可以避免:
|
||||
* 1. 字符串子串匹配导致的误判(如 sys_role_menu 误匹配 sys_role)
|
||||
* 2. JOIN表被错误过滤(只对主表添加租户条件)
|
||||
*
|
||||
* @param sqlCommandType SQL命令类型
|
||||
* @param sql 原始SQL
|
||||
* @return true-需要拦截,false-不需要
|
||||
*/
|
||||
private boolean shouldIntercept(SqlCommandType sqlCommandType, String sql) {
|
||||
// 只拦截 SELECT、UPDATE、DELETE
|
||||
if (sqlCommandType != SqlCommandType.SELECT &&
|
||||
sqlCommandType != SqlCommandType.UPDATE &&
|
||||
sqlCommandType != SqlCommandType.DELETE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// 解析SQL获取主表名
|
||||
Statement statement = CCJSqlParserUtil.parse(sql);
|
||||
String mainTableName = getMainTableName(statement);
|
||||
|
||||
// 只有主表是租户表时才拦截
|
||||
return mainTableName != null && isTenantTable(mainTableName);
|
||||
} catch (Exception e) {
|
||||
// SQL解析失败,不拦截(避免影响正常业务)
|
||||
log.debug("SQL解析失败,跳过租户拦截: {}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从SQL语句中获取主表名
|
||||
*
|
||||
* @param statement SQL语句
|
||||
* @return 主表名,解析失败返回null
|
||||
*/
|
||||
private String getMainTableName(Statement statement) {
|
||||
if (statement instanceof Select) {
|
||||
PlainSelect plainSelect = (PlainSelect) ((Select) statement).getSelectBody();
|
||||
FromItem fromItem = plainSelect.getFromItem();
|
||||
if (fromItem instanceof Table) {
|
||||
return ((Table) fromItem).getName();
|
||||
}
|
||||
} else if (statement instanceof Update) {
|
||||
return ((Update) statement).getTable().getName();
|
||||
} else if (statement instanceof Delete) {
|
||||
return ((Delete) statement).getTable().getName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断表名是否是租户表
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @return true-是租户表,false-不是
|
||||
*/
|
||||
private boolean isTenantTable(String tableName) {
|
||||
return TenantConfig.TENANT_TABLES.stream()
|
||||
.anyMatch(t -> t.equalsIgnoreCase(tableName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理SQL - 添加租户过滤条件
|
||||
*
|
||||
* @param sql 原始SQL
|
||||
* @param tenantId 租户ID
|
||||
* @param sqlCommandType SQL类型
|
||||
* @return 改写后的SQL
|
||||
*/
|
||||
private String processSql(String sql, Long tenantId, SqlCommandType sqlCommandType) throws Exception {
|
||||
Statement statement = CCJSqlParserUtil.parse(sql);
|
||||
|
||||
if (statement instanceof Select) {
|
||||
// 处理 SELECT 语句
|
||||
Select selectStatement = (Select) statement;
|
||||
PlainSelect plainSelect = (PlainSelect) selectStatement.getSelectBody();
|
||||
|
||||
// 获取主表别名,避免多表JOIN时tenant_id歧义
|
||||
String tableAlias = getMainTableAlias(plainSelect);
|
||||
|
||||
// 构造 tenant_id = ? 条件(带表别名)
|
||||
EqualsTo tenantCondition = buildTenantCondition(tenantId, tableAlias);
|
||||
|
||||
// 合并WHERE条件
|
||||
if (plainSelect.getWhere() != null) {
|
||||
AndExpression andExpression = new AndExpression(tenantCondition, plainSelect.getWhere());
|
||||
plainSelect.setWhere(andExpression);
|
||||
} else {
|
||||
plainSelect.setWhere(tenantCondition);
|
||||
}
|
||||
|
||||
return selectStatement.toString();
|
||||
}
|
||||
else if (statement instanceof Update) {
|
||||
// 处理 UPDATE 语句
|
||||
Update updateStatement = (Update) statement;
|
||||
|
||||
// 获取表别名
|
||||
String tableAlias = getUpdateTableAlias(updateStatement);
|
||||
EqualsTo tenantCondition = buildTenantCondition(tenantId, tableAlias);
|
||||
|
||||
if (updateStatement.getWhere() != null) {
|
||||
AndExpression andExpression = new AndExpression(tenantCondition, updateStatement.getWhere());
|
||||
updateStatement.setWhere(andExpression);
|
||||
} else {
|
||||
updateStatement.setWhere(tenantCondition);
|
||||
}
|
||||
|
||||
return updateStatement.toString();
|
||||
}
|
||||
else if (statement instanceof Delete) {
|
||||
// 处理 DELETE 语句
|
||||
Delete deleteStatement = (Delete) statement;
|
||||
|
||||
// 获取表别名
|
||||
String tableAlias = getDeleteTableAlias(deleteStatement);
|
||||
EqualsTo tenantCondition = buildTenantCondition(tenantId, tableAlias);
|
||||
|
||||
if (deleteStatement.getWhere() != null) {
|
||||
AndExpression andExpression = new AndExpression(tenantCondition, deleteStatement.getWhere());
|
||||
deleteStatement.setWhere(andExpression);
|
||||
} else {
|
||||
deleteStatement.setWhere(tenantCondition);
|
||||
}
|
||||
|
||||
return deleteStatement.toString();
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取SELECT语句主表的别名或表名
|
||||
*
|
||||
* @param plainSelect SELECT语句
|
||||
* @return 表别名或表名,用于构造 tenant_id 条件
|
||||
*/
|
||||
private String getMainTableAlias(PlainSelect plainSelect) {
|
||||
FromItem fromItem = plainSelect.getFromItem();
|
||||
if (fromItem instanceof Table) {
|
||||
Table table = (Table) fromItem;
|
||||
if (table.getAlias() != null) {
|
||||
return table.getAlias().getName();
|
||||
}
|
||||
return table.getName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取UPDATE语句的表别名或表名
|
||||
*
|
||||
* @param updateStatement UPDATE语句
|
||||
* @return 表别名或表名
|
||||
*/
|
||||
private String getUpdateTableAlias(Update updateStatement) {
|
||||
Table table = updateStatement.getTable();
|
||||
if (table != null) {
|
||||
if (table.getAlias() != null) {
|
||||
return table.getAlias().getName();
|
||||
}
|
||||
return table.getName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取DELETE语句的表别名或表名
|
||||
*
|
||||
* @param deleteStatement DELETE语句
|
||||
* @return 表别名或表名
|
||||
*/
|
||||
private String getDeleteTableAlias(Delete deleteStatement) {
|
||||
Table table = deleteStatement.getTable();
|
||||
if (table != null) {
|
||||
if (table.getAlias() != null) {
|
||||
return table.getAlias().getName();
|
||||
}
|
||||
return table.getName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造租户过滤条件:[表别名.]tenant_id = ?
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @param tableAlias 表别名(可为null)
|
||||
* @return EqualsTo 表达式
|
||||
*/
|
||||
private EqualsTo buildTenantCondition(Long tenantId, String tableAlias) {
|
||||
EqualsTo equalsTo = new EqualsTo();
|
||||
if (tableAlias != null) {
|
||||
equalsTo.setLeftExpression(new Column(new Table(tableAlias), "tenant_id"));
|
||||
} else {
|
||||
equalsTo.setLeftExpression(new Column("tenant_id"));
|
||||
}
|
||||
equalsTo.setRightExpression(new LongValue(tenantId));
|
||||
return equalsTo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object plugin(Object target) {
|
||||
return Plugin.wrap(target, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperties(Properties properties) {
|
||||
// 可以从配置文件读取租户表配置
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.ruoyi.framework.web.service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
|
|
@ -22,6 +24,7 @@ import com.ruoyi.common.utils.DateUtils;
|
|||
import com.ruoyi.common.utils.MessageUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.ip.IpUtils;
|
||||
import com.ruoyi.common.core.context.TenantContext;
|
||||
import com.ruoyi.framework.manager.AsyncManager;
|
||||
import com.ruoyi.framework.manager.factory.AsyncFactory;
|
||||
import com.ruoyi.framework.security.context.AuthenticationContextHolder;
|
||||
|
|
@ -30,12 +33,13 @@ import com.ruoyi.system.service.ISysUserService;
|
|||
|
||||
/**
|
||||
* 登录校验方法
|
||||
*
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Component
|
||||
public class SysLoginService
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(SysLoginService.class);
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
|
||||
|
|
@ -62,41 +66,74 @@ public class SysLoginService
|
|||
*/
|
||||
public String login(String username, String password, String code, String uuid)
|
||||
{
|
||||
// 验证码校验
|
||||
validateCaptcha(username, code, uuid);
|
||||
// 登录前置校验
|
||||
loginPreCheck(username, password);
|
||||
// 用户验证
|
||||
Authentication authentication = null;
|
||||
// 【多租户】登录阶段:临时忽略租户过滤
|
||||
// Reason: 登录时需要查询用户表获取tenant_id,形成先有鸡还是先有蛋的问题
|
||||
// 因此登录阶段临时设置全局模式,允许查询和更新用户表
|
||||
try
|
||||
{
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
|
||||
AuthenticationContextHolder.setContext(authenticationToken);
|
||||
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
|
||||
authentication = authenticationManager.authenticate(authenticationToken);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e instanceof BadCredentialsException)
|
||||
TenantContext.setIgnore(true);
|
||||
log.debug("【多租户】登录阶段:临时设置全局模式");
|
||||
|
||||
// 验证码校验
|
||||
validateCaptcha(username, code, uuid);
|
||||
// 登录前置校验
|
||||
loginPreCheck(username, password);
|
||||
// 用户验证
|
||||
Authentication authentication = null;
|
||||
try
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
|
||||
throw new UserPasswordNotMatchException();
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
|
||||
AuthenticationContextHolder.setContext(authenticationToken);
|
||||
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
|
||||
authentication = authenticationManager.authenticate(authenticationToken);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e instanceof BadCredentialsException)
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
|
||||
throw new UserPasswordNotMatchException();
|
||||
}
|
||||
else
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
|
||||
throw new ServiceException(e.getMessage());
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
AuthenticationContextHolder.clearContext();
|
||||
}
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
|
||||
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
|
||||
recordLoginInfo(loginUser.getUserId());
|
||||
|
||||
// 【多租户】从SysUser获取tenant_id并设置到LoginUser
|
||||
if (loginUser.getUser() != null && loginUser.getUser().getTenantId() != null)
|
||||
{
|
||||
loginUser.setTenantId(loginUser.getUser().getTenantId());
|
||||
log.info("【多租户】用户 {} 登录成功,租户ID: {}", username, loginUser.getTenantId());
|
||||
}
|
||||
else if (!com.ruoyi.common.config.TenantConfig.isSuperAdmin(loginUser.getUserId()))
|
||||
{
|
||||
// 非超级管理员必须有租户ID
|
||||
log.error("用户 {} 未分配租户且非超级管理员,拒绝登录", username);
|
||||
throw new ServiceException("用户未分配租户,请联系管理员");
|
||||
}
|
||||
else
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
|
||||
throw new ServiceException(e.getMessage());
|
||||
log.warn("【多租户】超级管理员 {} 登录,无需租户ID", username);
|
||||
}
|
||||
|
||||
// 生成token
|
||||
return tokenService.createToken(loginUser);
|
||||
}
|
||||
finally
|
||||
{
|
||||
AuthenticationContextHolder.clearContext();
|
||||
// 【多租户】登录完成,清除全局模式标记
|
||||
TenantContext.clear();
|
||||
log.debug("【多租户】登录流程结束,清除全局模式");
|
||||
}
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
|
||||
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
|
||||
recordLoginInfo(loginUser.getUserId());
|
||||
// 生成token
|
||||
return tokenService.createToken(loginUser);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ public class TokenService
|
|||
|
||||
/**
|
||||
* 获取用户身份信息
|
||||
*
|
||||
*
|
||||
* @return 用户信息
|
||||
*/
|
||||
public LoginUser getLoginUser(HttpServletRequest request)
|
||||
|
|
@ -72,6 +72,15 @@ public class TokenService
|
|||
String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
|
||||
String userKey = getTokenKey(uuid);
|
||||
LoginUser user = redisCache.getCacheObject(userKey);
|
||||
|
||||
// 【多租户】从JWT中提取tenant_id并设置到LoginUser
|
||||
if (user != null && claims.get("tenant_id") != null)
|
||||
{
|
||||
Long tenantId = claims.get("tenant_id", Long.class);
|
||||
user.setTenantId(tenantId);
|
||||
log.debug("从JWT中提取租户ID: {}", tenantId);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
@ -107,7 +116,7 @@ public class TokenService
|
|||
|
||||
/**
|
||||
* 创建令牌
|
||||
*
|
||||
*
|
||||
* @param loginUser 用户信息
|
||||
* @return 令牌
|
||||
*/
|
||||
|
|
@ -121,6 +130,14 @@ public class TokenService
|
|||
Map<String, Object> claims = new HashMap<>();
|
||||
claims.put(Constants.LOGIN_USER_KEY, token);
|
||||
claims.put(Constants.JWT_USERNAME, loginUser.getUsername());
|
||||
|
||||
// 【多租户】将tenant_id存入JWT
|
||||
if (loginUser.getTenantId() != null)
|
||||
{
|
||||
claims.put("tenant_id", loginUser.getTenantId());
|
||||
log.debug("JWT中存储租户ID: {}", loginUser.getTenantId());
|
||||
}
|
||||
|
||||
return createToken(claims);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,163 @@
|
|||
package com.ruoyi.system.domain;
|
||||
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
|
||||
/**
|
||||
* 租户信息对象 sys_tenant
|
||||
*
|
||||
* @author W-yf
|
||||
* @date 2025-12-19
|
||||
*/
|
||||
public class SysTenant extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 租户ID */
|
||||
private Long tenantId;
|
||||
|
||||
/** 租户名称 */
|
||||
@Excel(name = "租户名称")
|
||||
private String tenantName;
|
||||
|
||||
/** 租户编码(唯一标识) */
|
||||
@Excel(name = "租户编码", readConverterExp = "唯=一标识")
|
||||
private String tenantCode;
|
||||
|
||||
/** 联系人 */
|
||||
@Excel(name = "联系人")
|
||||
private String contactName;
|
||||
|
||||
/** 联系电话 */
|
||||
@Excel(name = "联系电话")
|
||||
private String contactPhone;
|
||||
|
||||
/** 过期时间(NULL表示永久) */
|
||||
@Excel(name = "过期时间", readConverterExp = "N=ULL表示永久")
|
||||
private Date expireTime;
|
||||
|
||||
/** 套餐ID(预留) */
|
||||
@Excel(name = "套餐ID", readConverterExp = "预=留")
|
||||
private Long packageId;
|
||||
|
||||
/** 状态(0正常 1停用) */
|
||||
@Excel(name = "状态", readConverterExp = "0=正常,1=停用")
|
||||
private String status;
|
||||
|
||||
/** 删除标志(0存在 2删除) */
|
||||
private String delFlag;
|
||||
|
||||
public void setTenantId(Long tenantId)
|
||||
{
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
public Long getTenantId()
|
||||
{
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantName(String tenantName)
|
||||
{
|
||||
this.tenantName = tenantName;
|
||||
}
|
||||
|
||||
public String getTenantName()
|
||||
{
|
||||
return tenantName;
|
||||
}
|
||||
|
||||
public void setTenantCode(String tenantCode)
|
||||
{
|
||||
this.tenantCode = tenantCode;
|
||||
}
|
||||
|
||||
public String getTenantCode()
|
||||
{
|
||||
return tenantCode;
|
||||
}
|
||||
|
||||
public void setContactName(String contactName)
|
||||
{
|
||||
this.contactName = contactName;
|
||||
}
|
||||
|
||||
public String getContactName()
|
||||
{
|
||||
return contactName;
|
||||
}
|
||||
|
||||
public void setContactPhone(String contactPhone)
|
||||
{
|
||||
this.contactPhone = contactPhone;
|
||||
}
|
||||
|
||||
public String getContactPhone()
|
||||
{
|
||||
return contactPhone;
|
||||
}
|
||||
|
||||
public void setExpireTime(Date expireTime)
|
||||
{
|
||||
this.expireTime = expireTime;
|
||||
}
|
||||
|
||||
public Date getExpireTime()
|
||||
{
|
||||
return expireTime;
|
||||
}
|
||||
|
||||
public void setPackageId(Long packageId)
|
||||
{
|
||||
this.packageId = packageId;
|
||||
}
|
||||
|
||||
public Long getPackageId()
|
||||
{
|
||||
return packageId;
|
||||
}
|
||||
|
||||
public void setStatus(String status)
|
||||
{
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getStatus()
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setDelFlag(String delFlag)
|
||||
{
|
||||
this.delFlag = delFlag;
|
||||
}
|
||||
|
||||
public String getDelFlag()
|
||||
{
|
||||
return delFlag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("tenantId", getTenantId())
|
||||
.append("tenantName", getTenantName())
|
||||
.append("tenantCode", getTenantCode())
|
||||
.append("contactName", getContactName())
|
||||
.append("contactPhone", getContactPhone())
|
||||
.append("expireTime", getExpireTime())
|
||||
.append("packageId", getPackageId())
|
||||
.append("status", getStatus())
|
||||
.append("delFlag", getDelFlag())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
.append("updateTime", getUpdateTime())
|
||||
.append("remark", getRemark())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package com.ruoyi.system.mapper;
|
||||
|
||||
import com.ruoyi.system.domain.SysTenant;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 租户信息Mapper接口
|
||||
*
|
||||
* @author W-yf
|
||||
* @date 2025-12-19
|
||||
*/
|
||||
public interface SysTenantMapper
|
||||
{
|
||||
/**
|
||||
* 查询租户信息
|
||||
*
|
||||
* @param tenantId 租户信息主键
|
||||
* @return 租户信息
|
||||
*/
|
||||
public SysTenant selectSysTenantByTenantId(Long tenantId);
|
||||
|
||||
/**
|
||||
* 查询租户信息列表
|
||||
*
|
||||
* @param sysTenant 租户信息
|
||||
* @return 租户信息集合
|
||||
*/
|
||||
public List<SysTenant> selectSysTenantList(SysTenant sysTenant);
|
||||
|
||||
/**
|
||||
* 新增租户信息
|
||||
*
|
||||
* @param sysTenant 租户信息
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertSysTenant(SysTenant sysTenant);
|
||||
|
||||
/**
|
||||
* 修改租户信息
|
||||
*
|
||||
* @param sysTenant 租户信息
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateSysTenant(SysTenant sysTenant);
|
||||
|
||||
/**
|
||||
* 删除租户信息
|
||||
*
|
||||
* @param tenantId 租户信息主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysTenantByTenantId(Long tenantId);
|
||||
|
||||
/**
|
||||
* 批量删除租户信息
|
||||
*
|
||||
* @param tenantIds 需要删除的数据主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysTenantByTenantIds(Long[] tenantIds);
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package com.ruoyi.system.service;
|
||||
|
||||
import com.ruoyi.system.domain.SysTenant;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 租户信息Service接口
|
||||
*
|
||||
* @author W-yf
|
||||
* @date 2025-12-19
|
||||
*/
|
||||
public interface ISysTenantService
|
||||
{
|
||||
/**
|
||||
* 查询租户信息
|
||||
*
|
||||
* @param tenantId 租户信息主键
|
||||
* @return 租户信息
|
||||
*/
|
||||
public SysTenant selectSysTenantByTenantId(Long tenantId);
|
||||
|
||||
/**
|
||||
* 查询租户信息列表
|
||||
*
|
||||
* @param sysTenant 租户信息
|
||||
* @return 租户信息集合
|
||||
*/
|
||||
public List<SysTenant> selectSysTenantList(SysTenant sysTenant);
|
||||
|
||||
/**
|
||||
* 新增租户信息
|
||||
*
|
||||
* @param sysTenant 租户信息
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertSysTenant(SysTenant sysTenant);
|
||||
|
||||
/**
|
||||
* 修改租户信息
|
||||
*
|
||||
* @param sysTenant 租户信息
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateSysTenant(SysTenant sysTenant);
|
||||
|
||||
/**
|
||||
* 批量删除租户信息
|
||||
*
|
||||
* @param tenantIds 需要删除的租户信息主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysTenantByTenantIds(Long[] tenantIds);
|
||||
|
||||
/**
|
||||
* 删除租户信息信息
|
||||
*
|
||||
* @param tenantId 租户信息主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysTenantByTenantId(Long tenantId);
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
package com.ruoyi.system.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.system.domain.SysTenant;
|
||||
import com.ruoyi.system.mapper.SysTenantMapper;
|
||||
import com.ruoyi.system.service.ISysTenantService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 租户信息Service业务层处理
|
||||
*
|
||||
* @author W-yf
|
||||
* @date 2025-12-19
|
||||
*/
|
||||
@Service
|
||||
public class SysTenantServiceImpl implements ISysTenantService
|
||||
{
|
||||
@Autowired
|
||||
private SysTenantMapper sysTenantMapper;
|
||||
|
||||
/**
|
||||
* 查询租户信息
|
||||
*
|
||||
* @param tenantId 租户信息主键
|
||||
* @return 租户信息
|
||||
*/
|
||||
@Override
|
||||
public SysTenant selectSysTenantByTenantId(Long tenantId)
|
||||
{
|
||||
return sysTenantMapper.selectSysTenantByTenantId(tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询租户信息列表
|
||||
*
|
||||
* @param sysTenant 租户信息
|
||||
* @return 租户信息
|
||||
*/
|
||||
@Override
|
||||
public List<SysTenant> selectSysTenantList(SysTenant sysTenant)
|
||||
{
|
||||
return sysTenantMapper.selectSysTenantList(sysTenant);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增租户信息
|
||||
*
|
||||
* @param sysTenant 租户信息
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int insertSysTenant(SysTenant sysTenant)
|
||||
{
|
||||
sysTenant.setCreateTime(DateUtils.getNowDate());
|
||||
return sysTenantMapper.insertSysTenant(sysTenant);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改租户信息
|
||||
*
|
||||
* @param sysTenant 租户信息
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int updateSysTenant(SysTenant sysTenant)
|
||||
{
|
||||
sysTenant.setUpdateTime(DateUtils.getNowDate());
|
||||
return sysTenantMapper.updateSysTenant(sysTenant);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除租户信息
|
||||
*
|
||||
* @param tenantIds 需要删除的租户信息主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteSysTenantByTenantIds(Long[] tenantIds)
|
||||
{
|
||||
return sysTenantMapper.deleteSysTenantByTenantIds(tenantIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除租户信息信息
|
||||
*
|
||||
* @param tenantId 租户信息主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteSysTenantByTenantId(Long tenantId)
|
||||
{
|
||||
return sysTenantMapper.deleteSysTenantByTenantId(tenantId);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.system.mapper.SysTenantMapper">
|
||||
|
||||
<resultMap type="SysTenant" id="SysTenantResult">
|
||||
<result property="tenantId" column="tenant_id" />
|
||||
<result property="tenantName" column="tenant_name" />
|
||||
<result property="tenantCode" column="tenant_code" />
|
||||
<result property="contactName" column="contact_name" />
|
||||
<result property="contactPhone" column="contact_phone" />
|
||||
<result property="expireTime" column="expire_time" />
|
||||
<result property="packageId" column="package_id" />
|
||||
<result property="status" column="status" />
|
||||
<result property="delFlag" column="del_flag" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="remark" column="remark" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectSysTenantVo">
|
||||
select tenant_id, tenant_name, tenant_code, contact_name, contact_phone, expire_time, package_id, status, del_flag, create_by, create_time, update_by, update_time, remark from sys_tenant
|
||||
</sql>
|
||||
|
||||
<select id="selectSysTenantList" parameterType="SysTenant" resultMap="SysTenantResult">
|
||||
<include refid="selectSysTenantVo"/>
|
||||
<where>
|
||||
<if test="tenantName != null and tenantName != ''"> and tenant_name like concat('%', #{tenantName}, '%')</if>
|
||||
<if test="tenantCode != null and tenantCode != ''"> and tenant_code = #{tenantCode}</if>
|
||||
<if test="contactName != null and contactName != ''"> and contact_name like concat('%', #{contactName}, '%')</if>
|
||||
<if test="contactPhone != null and contactPhone != ''"> and contact_phone = #{contactPhone}</if>
|
||||
<if test="expireTime != null "> and expire_time = #{expireTime}</if>
|
||||
<if test="packageId != null "> and package_id = #{packageId}</if>
|
||||
<if test="status != null and status != ''"> and status = #{status}</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="selectSysTenantByTenantId" parameterType="Long" resultMap="SysTenantResult">
|
||||
<include refid="selectSysTenantVo"/>
|
||||
where tenant_id = #{tenantId}
|
||||
</select>
|
||||
|
||||
<insert id="insertSysTenant" parameterType="SysTenant" useGeneratedKeys="true" keyProperty="tenantId">
|
||||
insert into sys_tenant
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="tenantName != null and tenantName != ''">tenant_name,</if>
|
||||
<if test="tenantCode != null and tenantCode != ''">tenant_code,</if>
|
||||
<if test="contactName != null">contact_name,</if>
|
||||
<if test="contactPhone != null">contact_phone,</if>
|
||||
<if test="expireTime != null">expire_time,</if>
|
||||
<if test="packageId != null">package_id,</if>
|
||||
<if test="status != null">status,</if>
|
||||
<if test="delFlag != null">del_flag,</if>
|
||||
<if test="createBy != null">create_by,</if>
|
||||
<if test="createTime != null">create_time,</if>
|
||||
<if test="updateBy != null">update_by,</if>
|
||||
<if test="updateTime != null">update_time,</if>
|
||||
<if test="remark != null">remark,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="tenantName != null and tenantName != ''">#{tenantName},</if>
|
||||
<if test="tenantCode != null and tenantCode != ''">#{tenantCode},</if>
|
||||
<if test="contactName != null">#{contactName},</if>
|
||||
<if test="contactPhone != null">#{contactPhone},</if>
|
||||
<if test="expireTime != null">#{expireTime},</if>
|
||||
<if test="packageId != null">#{packageId},</if>
|
||||
<if test="status != null">#{status},</if>
|
||||
<if test="delFlag != null">#{delFlag},</if>
|
||||
<if test="createBy != null">#{createBy},</if>
|
||||
<if test="createTime != null">#{createTime},</if>
|
||||
<if test="updateBy != null">#{updateBy},</if>
|
||||
<if test="updateTime != null">#{updateTime},</if>
|
||||
<if test="remark != null">#{remark},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<update id="updateSysTenant" parameterType="SysTenant">
|
||||
update sys_tenant
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="tenantName != null and tenantName != ''">tenant_name = #{tenantName},</if>
|
||||
<if test="tenantCode != null and tenantCode != ''">tenant_code = #{tenantCode},</if>
|
||||
<if test="contactName != null">contact_name = #{contactName},</if>
|
||||
<if test="contactPhone != null">contact_phone = #{contactPhone},</if>
|
||||
<if test="expireTime != null">expire_time = #{expireTime},</if>
|
||||
<if test="packageId != null">package_id = #{packageId},</if>
|
||||
<if test="status != null">status = #{status},</if>
|
||||
<if test="delFlag != null">del_flag = #{delFlag},</if>
|
||||
<if test="createBy != null">create_by = #{createBy},</if>
|
||||
<if test="createTime != null">create_time = #{createTime},</if>
|
||||
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||
<if test="updateTime != null">update_time = #{updateTime},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
</trim>
|
||||
where tenant_id = #{tenantId}
|
||||
</update>
|
||||
|
||||
<delete id="deleteSysTenantByTenantId" parameterType="Long">
|
||||
delete from sys_tenant where tenant_id = #{tenantId}
|
||||
</delete>
|
||||
|
||||
<delete id="deleteSysTenantByTenantIds" parameterType="String">
|
||||
delete from sys_tenant where tenant_id in
|
||||
<foreach item="tenantId" collection="array" open="(" separator="," close=")">
|
||||
#{tenantId}
|
||||
</foreach>
|
||||
</delete>
|
||||
</mapper>
|
||||
|
|
@ -7,6 +7,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<resultMap type="SysUser" id="SysUserResult">
|
||||
<id property="userId" column="user_id" />
|
||||
<result property="deptId" column="dept_id" />
|
||||
<result property="tenantId" column="tenant_id" />
|
||||
<result property="userName" column="user_name" />
|
||||
<result property="nickName" column="nick_name" />
|
||||
<result property="email" column="email" />
|
||||
|
|
@ -48,7 +49,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
</resultMap>
|
||||
|
||||
<sql id="selectUserVo">
|
||||
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.pwd_update_date, u.create_by, u.create_time, u.remark,
|
||||
select u.user_id, u.dept_id, u.tenant_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.pwd_update_date, u.create_by, u.create_time, u.remark,
|
||||
d.dept_id, d.parent_id, d.ancestors, 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
|
||||
|
|
@ -58,7 +59,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
</sql>
|
||||
|
||||
<select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">
|
||||
select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader from sys_user u
|
||||
select u.user_id, u.dept_id, u.tenant_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader from sys_user u
|
||||
left join sys_dept d on u.dept_id = d.dept_id
|
||||
where u.del_flag = '0'
|
||||
<if test="userId != null and userId != 0">
|
||||
|
|
@ -158,6 +159,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<if test="pwdUpdateDate != null">pwd_update_date,</if>
|
||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
||||
<if test="remark != null and remark != ''">remark,</if>
|
||||
<if test="tenantId != null">tenant_id,</if>
|
||||
create_time
|
||||
)values(
|
||||
<if test="userId != null and userId != ''">#{userId},</if>
|
||||
|
|
@ -173,6 +175,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<if test="pwdUpdateDate != null">#{pwdUpdateDate},</if>
|
||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||
<if test="remark != null and remark != ''">#{remark},</if>
|
||||
<if test="tenantId != null">#{tenantId},</if>
|
||||
sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
|
@ -192,6 +195,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<if test="loginDate != null">login_date = #{loginDate},</if>
|
||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
<if test="tenantId != null">tenant_id = #{tenantId},</if>
|
||||
update_time = sysdate()
|
||||
</set>
|
||||
where user_id = #{userId}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询租户信息列表
|
||||
export function listTenant(query) {
|
||||
return request({
|
||||
url: '/link/tenant/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询租户信息详细
|
||||
export function getTenant(tenantId) {
|
||||
return request({
|
||||
url: '/link/tenant/' + tenantId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增租户信息
|
||||
export function addTenant(data) {
|
||||
return request({
|
||||
url: '/link/tenant',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改租户信息
|
||||
export function updateTenant(data) {
|
||||
return request({
|
||||
url: '/link/tenant',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除租户信息
|
||||
export function delTenant(tenantId) {
|
||||
return request({
|
||||
url: '/link/tenant/' + tenantId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
|
@ -134,3 +134,6 @@ export function deptTreeSelect() {
|
|||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出租户列表查询方法
|
||||
export { listTenant } from '@/api/system/tenant'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,346 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="租户名称" prop="tenantName">
|
||||
<el-input
|
||||
v-model="queryParams.tenantName"
|
||||
placeholder="请输入租户名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="租户编码" prop="tenantCode">
|
||||
<el-input
|
||||
v-model="queryParams.tenantCode"
|
||||
placeholder="请输入租户编码"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="联系人" prop="contactName">
|
||||
<el-input
|
||||
v-model="queryParams.contactName"
|
||||
placeholder="请输入联系人"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="联系电话" prop="contactPhone">
|
||||
<el-input
|
||||
v-model="queryParams.contactPhone"
|
||||
placeholder="请输入联系电话"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="过期时间" prop="expireTime">
|
||||
<el-date-picker clearable
|
||||
v-model="queryParams.expireTime"
|
||||
type="date"
|
||||
value-format="yyyy-MM-dd"
|
||||
placeholder="请选择过期时间">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="套餐ID" prop="packageId">
|
||||
<el-input
|
||||
v-model="queryParams.packageId"
|
||||
placeholder="请输入套餐ID"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleAdd"
|
||||
v-hasPermi="['link:tenant:add']"
|
||||
>新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="el-icon-edit"
|
||||
size="mini"
|
||||
:disabled="single"
|
||||
@click="handleUpdate"
|
||||
v-hasPermi="['link:tenant:edit']"
|
||||
>修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="el-icon-delete"
|
||||
size="mini"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
v-hasPermi="['link:tenant:remove']"
|
||||
>删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="el-icon-download"
|
||||
size="mini"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['link:tenant:export']"
|
||||
>导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="tenantList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="租户ID" align="center" prop="tenantId" />
|
||||
<el-table-column label="租户名称" align="center" prop="tenantName" />
|
||||
<el-table-column label="租户编码" align="center" prop="tenantCode" />
|
||||
<el-table-column label="联系人" align="center" prop="contactName" />
|
||||
<el-table-column label="联系电话" align="center" prop="contactPhone" />
|
||||
<el-table-column label="过期时间" align="center" prop="expireTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.expireTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="套餐ID" align="center" prop="packageId" />
|
||||
<el-table-column label="状态" align="center" prop="status" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['link:tenant:edit']"
|
||||
>修改</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['link:tenant:remove']"
|
||||
>删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 添加或修改租户信息对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="租户名称" prop="tenantName">
|
||||
<el-input v-model="form.tenantName" placeholder="请输入租户名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="租户编码" prop="tenantCode">
|
||||
<el-input v-model="form.tenantCode" placeholder="请输入租户编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="联系人" prop="contactName">
|
||||
<el-input v-model="form.contactName" placeholder="请输入联系人" />
|
||||
</el-form-item>
|
||||
<el-form-item label="联系电话" prop="contactPhone">
|
||||
<el-input v-model="form.contactPhone" placeholder="请输入联系电话" />
|
||||
</el-form-item>
|
||||
<el-form-item label="过期时间" prop="expireTime">
|
||||
<el-date-picker clearable
|
||||
v-model="form.expireTime"
|
||||
type="date"
|
||||
value-format="yyyy-MM-dd"
|
||||
placeholder="请选择过期时间">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="套餐ID" prop="packageId">
|
||||
<el-input v-model="form.packageId" placeholder="请输入套餐ID" />
|
||||
</el-form-item>
|
||||
<el-form-item label="删除标志" prop="delFlag">
|
||||
<el-input v-model="form.delFlag" placeholder="请输入删除标志" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listTenant, getTenant, delTenant, addTenant, updateTenant } from "@/api/system/tenant"
|
||||
|
||||
export default {
|
||||
name: "Tenant",
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 租户信息表格数据
|
||||
tenantList: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
tenantName: null,
|
||||
tenantCode: null,
|
||||
contactName: null,
|
||||
contactPhone: null,
|
||||
expireTime: null,
|
||||
packageId: null,
|
||||
status: null,
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
tenantName: [
|
||||
{ required: true, message: "租户名称不能为空", trigger: "blur" }
|
||||
],
|
||||
tenantCode: [
|
||||
{ required: true, message: "租户编码不能为空", trigger: "blur" }
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
/** 查询租户信息列表 */
|
||||
getList() {
|
||||
this.loading = true
|
||||
listTenant(this.queryParams).then(response => {
|
||||
this.tenantList = response.rows
|
||||
this.total = response.total
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 取消按钮
|
||||
cancel() {
|
||||
this.open = false
|
||||
this.reset()
|
||||
},
|
||||
// 表单重置
|
||||
reset() {
|
||||
this.form = {
|
||||
tenantId: null,
|
||||
tenantName: null,
|
||||
tenantCode: null,
|
||||
contactName: null,
|
||||
contactPhone: null,
|
||||
expireTime: null,
|
||||
packageId: null,
|
||||
status: null,
|
||||
delFlag: null,
|
||||
createBy: null,
|
||||
createTime: null,
|
||||
updateBy: null,
|
||||
updateTime: null,
|
||||
remark: null
|
||||
}
|
||||
this.resetForm("form")
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm")
|
||||
this.handleQuery()
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map(item => item.tenantId)
|
||||
this.single = selection.length!==1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset()
|
||||
this.open = true
|
||||
this.title = "添加租户信息"
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset()
|
||||
const tenantId = row.tenantId || this.ids
|
||||
getTenant(tenantId).then(response => {
|
||||
this.form = response.data
|
||||
this.open = true
|
||||
this.title = "修改租户信息"
|
||||
})
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (valid) {
|
||||
if (this.form.tenantId != null) {
|
||||
updateTenant(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功")
|
||||
this.open = false
|
||||
this.getList()
|
||||
})
|
||||
} else {
|
||||
addTenant(this.form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功")
|
||||
this.open = false
|
||||
this.getList()
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const tenantIds = row.tenantId || this.ids
|
||||
this.$modal.confirm('是否确认删除租户信息编号为"' + tenantIds + '"的数据项?').then(function() {
|
||||
return delTenant(tenantIds)
|
||||
}).then(() => {
|
||||
this.getList()
|
||||
this.$modal.msgSuccess("删除成功")
|
||||
}).catch(() => {})
|
||||
},
|
||||
/** 导出按钮操作 */
|
||||
handleExport() {
|
||||
this.download('link/tenant/export', {
|
||||
...this.queryParams
|
||||
}, `tenant_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -165,6 +165,15 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="isSuperAdmin">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所属租户" prop="tenantId">
|
||||
<el-select v-model="form.tenantId" placeholder="请选择租户" clearable>
|
||||
<el-option v-for="item in tenantOptions" :key="item.tenantId" :label="item.tenantName" :value="item.tenantId" :disabled="item.status == '1'"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注">
|
||||
|
|
@ -246,6 +255,8 @@ export default {
|
|||
postOptions: [],
|
||||
// 角色选项
|
||||
roleOptions: [],
|
||||
// 租户选项
|
||||
tenantOptions: [],
|
||||
// 表单参数
|
||||
form: {},
|
||||
defaultProps: {
|
||||
|
|
@ -317,6 +328,12 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 判断当前用户是否为超级管理员
|
||||
isSuperAdmin() {
|
||||
return this.$store.getters.id === 1
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 根据名称筛选部门树
|
||||
deptName(val) {
|
||||
|
|
@ -400,7 +417,8 @@ export default {
|
|||
status: "0",
|
||||
remark: undefined,
|
||||
postIds: [],
|
||||
roleIds: []
|
||||
roleIds: [],
|
||||
tenantId: undefined
|
||||
}
|
||||
this.resetForm("form")
|
||||
},
|
||||
|
|
@ -442,6 +460,9 @@ export default {
|
|||
getUser().then(response => {
|
||||
this.postOptions = response.posts
|
||||
this.roleOptions = response.roles
|
||||
if (response.tenants) {
|
||||
this.tenantOptions = response.tenants
|
||||
}
|
||||
this.open = true
|
||||
this.title = "添加用户"
|
||||
this.form.password = this.initPassword
|
||||
|
|
@ -455,6 +476,9 @@ export default {
|
|||
this.form = response.data
|
||||
this.postOptions = response.posts
|
||||
this.roleOptions = response.roles
|
||||
if (response.tenants) {
|
||||
this.tenantOptions = response.tenants
|
||||
}
|
||||
this.$set(this.form, "postIds", response.postIds)
|
||||
this.$set(this.form, "roleIds", response.roleIds)
|
||||
this.open = true
|
||||
|
|
|
|||
|
|
@ -0,0 +1,312 @@
|
|||
-- ============================
|
||||
-- HairLink Phase 1 数据库初始化脚本
|
||||
-- 版本:1.0.0
|
||||
-- 创建日期:2025-12-19
|
||||
-- 说明:包含P1阶段核心业务表(顾客管理、订单管理、基础配置)
|
||||
-- 表数量:6个核心业务表
|
||||
-- 依赖:需先执行 ry_20250522.sql(若依框架基础表)
|
||||
-- ============================
|
||||
|
||||
-- ----------------------------
|
||||
-- 1、顾客档案表
|
||||
-- ----------------------------
|
||||
drop table if exists hl_customer;
|
||||
create table hl_customer (
|
||||
customer_id bigint(20) not null auto_increment comment '顾客ID',
|
||||
tenant_id bigint(20) default null comment '租户ID',
|
||||
dept_id bigint(20) default null comment '归属门店',
|
||||
|
||||
-- [P1] 基础信息
|
||||
customer_name varchar(50) default '散客' comment '姓名',
|
||||
phone varchar(11) default null comment '手机号(兼容散客为空)',
|
||||
gender char(1) default '2' comment '性别 0男 1女 2未知',
|
||||
|
||||
-- [P2] 资产快照(预留)
|
||||
member_level char(1) default '0' comment '会员等级 0普通 1银卡 2金卡',
|
||||
balance decimal(10,2) default 0.00 comment '储值余额',
|
||||
|
||||
-- [P3] C端连接(预留)
|
||||
wx_openid varchar(64) default null comment '小程序OpenID',
|
||||
points int(11) default 0 comment '积分',
|
||||
|
||||
-- 标准字段
|
||||
create_by varchar(64) default '',
|
||||
create_time datetime default null,
|
||||
update_by varchar(64) default '',
|
||||
update_time datetime default null,
|
||||
remark varchar(500) default null,
|
||||
|
||||
primary key (customer_id),
|
||||
index idx_tenant (tenant_id),
|
||||
index idx_phone (phone),
|
||||
index idx_openid (wx_openid)
|
||||
) engine=innodb auto_increment=100 comment = '顾客档案表';
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 2、会员权益卡表
|
||||
-- ----------------------------
|
||||
drop table if exists hl_member_card;
|
||||
create table hl_member_card (
|
||||
card_id bigint(20) not null auto_increment comment '卡ID',
|
||||
tenant_id bigint(20) default null comment '租户ID',
|
||||
customer_id bigint(20) not null comment '顾客ID',
|
||||
|
||||
-- [P1] 计次卡逻辑
|
||||
card_name varchar(100) default '' comment '卡名称',
|
||||
total_count int(11) default 0 comment '总次数',
|
||||
remain_count int(11) default 0 comment '剩余次数',
|
||||
|
||||
-- [P2] 储值卡/期限卡逻辑(预留)
|
||||
card_type char(1) default '0' comment '卡类型 0计次 1储值 2期限',
|
||||
total_amount decimal(10,2) default 0.00 comment '总充值金额',
|
||||
remain_amount decimal(10,2) default 0.00 comment '剩余金额',
|
||||
gift_amount decimal(10,2) default 0.00 comment '赠送金额',
|
||||
|
||||
expire_time datetime default null comment '过期时间',
|
||||
status char(1) default '0' comment '状态 0正常 1停用 2耗尽',
|
||||
|
||||
create_by varchar(64) default '',
|
||||
create_time datetime default null,
|
||||
update_by varchar(64) default '',
|
||||
update_time datetime default null,
|
||||
|
||||
primary key (card_id),
|
||||
index idx_tenant (tenant_id),
|
||||
index idx_customer (customer_id)
|
||||
) engine=innodb auto_increment=100 comment = '会员权益卡表';
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 3、服务项目表
|
||||
-- ----------------------------
|
||||
drop table if exists hl_service;
|
||||
create table hl_service (
|
||||
service_id bigint(20) not null auto_increment comment '服务ID',
|
||||
tenant_id bigint(20) default null comment '租户ID',
|
||||
|
||||
service_name varchar(100) not null comment '服务名称',
|
||||
service_code varchar(50) default null comment '服务编码',
|
||||
price decimal(10,2) default 0.00 comment '标准价格',
|
||||
duration int(11) default 30 comment '服务时长(分钟)',
|
||||
category varchar(50) default null comment '服务分类',
|
||||
status char(1) default '0' comment '状态 0正常 1停用',
|
||||
sort_order int(11) default 0 comment '排序',
|
||||
|
||||
create_by varchar(64) default '',
|
||||
create_time datetime default null,
|
||||
update_by varchar(64) default '',
|
||||
update_time datetime default null,
|
||||
remark varchar(500) default null,
|
||||
|
||||
primary key (service_id),
|
||||
index idx_tenant (tenant_id),
|
||||
unique key uk_code (service_code)
|
||||
) engine=innodb auto_increment=100 comment = '服务项目表';
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 4、员工扩展表
|
||||
-- ----------------------------
|
||||
drop table if exists hl_staff;
|
||||
create table hl_staff (
|
||||
staff_id bigint(20) not null auto_increment comment '员工ID',
|
||||
user_id bigint(20) not null comment '关联sys_user用户ID',
|
||||
tenant_id bigint(20) default null comment '租户ID',
|
||||
dept_id bigint(20) default null comment '所属门店',
|
||||
|
||||
staff_name varchar(50) not null comment '员工姓名',
|
||||
phone varchar(11) default null comment '手机号',
|
||||
position varchar(50) default '理发师' comment '职位',
|
||||
level char(1) default '1' comment '级别 1初级 2中级 3高级',
|
||||
|
||||
-- [P2] 业绩相关(预留)
|
||||
commission_rate decimal(5,2) default 30.00 comment '提成比例',
|
||||
base_salary decimal(10,2) default 0.00 comment '底薪',
|
||||
|
||||
status char(1) default '0' comment '状态 0在职 1离职',
|
||||
entry_date date default null comment '入职日期',
|
||||
|
||||
create_by varchar(64) default '',
|
||||
create_time datetime default null,
|
||||
update_by varchar(64) default '',
|
||||
update_time datetime default null,
|
||||
|
||||
primary key (staff_id),
|
||||
index idx_user (user_id),
|
||||
index idx_dept (dept_id)
|
||||
) engine=innodb auto_increment=100 comment = '员工扩展表';
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 5、业务订单表
|
||||
-- ----------------------------
|
||||
drop table if exists hl_order;
|
||||
create table hl_order (
|
||||
order_id bigint(20) not null auto_increment comment '订单ID',
|
||||
order_no varchar(32) not null comment '订单编号',
|
||||
tenant_id bigint(20) default null comment '租户ID',
|
||||
dept_id bigint(20) default null comment '门店ID',
|
||||
customer_id bigint(20) not null comment '顾客ID',
|
||||
|
||||
-- [P1] 基础交易
|
||||
pay_amount decimal(10,2) default 0.00 comment '实收/实扣金额',
|
||||
pay_method char(1) default '0' comment '支付方式 0现金 1微信 2卡扣次',
|
||||
pay_status char(1) default '1' comment '支付状态 1已付',
|
||||
|
||||
-- [P2] 复杂交易(预留)
|
||||
order_type char(1) default '0' comment '订单类型 0服务 1办卡 2零售',
|
||||
total_amount decimal(10,2) default 0.00 comment '原价总额',
|
||||
discount_amount decimal(10,2) default 0.00 comment '优惠金额',
|
||||
|
||||
-- [P3] 在线支付(预留)
|
||||
transaction_id varchar(64) default null comment '微信支付流水号',
|
||||
|
||||
create_by varchar(64) default '',
|
||||
create_time datetime default null,
|
||||
|
||||
primary key (order_id),
|
||||
unique key uk_order_no (order_no),
|
||||
index idx_tenant (tenant_id),
|
||||
index idx_customer (customer_id),
|
||||
index idx_create_time (create_time)
|
||||
) engine=innodb auto_increment=1000 comment = '业务订单表';
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 6、订单明细表
|
||||
-- ----------------------------
|
||||
drop table if exists hl_order_detail;
|
||||
create table hl_order_detail (
|
||||
detail_id bigint(20) not null auto_increment comment '明细ID',
|
||||
order_id bigint(20) not null comment '订单ID',
|
||||
|
||||
-- [P1] 核心内容
|
||||
item_id bigint(20) not null comment '项目ID',
|
||||
item_name varchar(100) not null comment '项目名称',
|
||||
price decimal(10,2) default 0.00 comment '单价',
|
||||
quantity int(11) default 1 comment '数量',
|
||||
staff_id bigint(20) default null comment '主理发师ID',
|
||||
deduct_card_id bigint(20) default null comment '关联扣次卡ID',
|
||||
|
||||
-- [P2] 类型扩展(预留)
|
||||
item_type char(1) default '0' comment '项目类型 0服务 1产品',
|
||||
is_stock_deducted char(1) default '0' comment '是否已扣库 0否 1是',
|
||||
|
||||
primary key (detail_id),
|
||||
index idx_order (order_id)
|
||||
) engine=innodb auto_increment=1000 comment = '订单明细表';
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 外键约束
|
||||
-- ----------------------------
|
||||
|
||||
-- 会员卡 -> 顾客
|
||||
ALTER TABLE hl_member_card
|
||||
ADD CONSTRAINT fk_card_customer
|
||||
FOREIGN KEY (customer_id) REFERENCES hl_customer(customer_id);
|
||||
|
||||
-- 订单 -> 顾客
|
||||
ALTER TABLE hl_order
|
||||
ADD CONSTRAINT fk_order_customer
|
||||
FOREIGN KEY (customer_id) REFERENCES hl_customer(customer_id);
|
||||
|
||||
-- 订单明细 -> 订单(级联删除)
|
||||
ALTER TABLE hl_order_detail
|
||||
ADD CONSTRAINT fk_detail_order
|
||||
FOREIGN KEY (order_id) REFERENCES hl_order(order_id) ON DELETE CASCADE;
|
||||
|
||||
-- 订单明细 -> 会员卡(可选)
|
||||
ALTER TABLE hl_order_detail
|
||||
ADD CONSTRAINT fk_detail_card
|
||||
FOREIGN KEY (deduct_card_id) REFERENCES hl_member_card(card_id);
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 初始化-服务项目表数据
|
||||
-- ----------------------------
|
||||
INSERT INTO hl_service (service_name, service_code, price, duration, category, status, sort_order, create_by, create_time) VALUES
|
||||
('男士剪发', 'MAN_CUT', 38.00, 30, '基础服务', '0', 1, 'admin', sysdate()),
|
||||
('女士剪发', 'WOMAN_CUT', 58.00, 45, '基础服务', '0', 2, 'admin', sysdate()),
|
||||
('烫发', 'PERM', 288.00, 120, '造型服务', '0', 3, 'admin', sysdate()),
|
||||
('染发', 'DYE', 268.00, 120, '造型服务', '0', 4, 'admin', sysdate()),
|
||||
('洗剪吹', 'WASH_CUT_BLOW', 68.00, 60, '套餐服务', '0', 5, 'admin', sysdate());
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 测试数据-顾客档案
|
||||
-- ----------------------------
|
||||
INSERT INTO hl_customer (customer_name, phone, gender, member_level, balance, create_by, create_time, remark) VALUES
|
||||
('张三', '13800138001', '0', '0', 0.00, 'admin', sysdate(), '测试会员顾客1'),
|
||||
('李四', '13800138002', '1', '0', 0.00, 'admin', sysdate(), '测试会员顾客2'),
|
||||
('散客', NULL, '2', '0', 0.00, 'admin', sysdate(), '测试散客');
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 测试数据-员工扩展表
|
||||
-- ----------------------------
|
||||
INSERT INTO hl_staff (user_id, staff_name, phone, position, level, commission_rate, status, entry_date, create_by, create_time) VALUES
|
||||
(1, '王师傅', '13900139001', '高级理发师', '3', 30.00, '0', '2024-01-01', 'admin', sysdate()),
|
||||
(2, '赵师傅', '13900139002', '理发师', '2', 30.00, '0', '2024-06-01', 'admin', sysdate());
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 测试数据-会员卡
|
||||
-- ----------------------------
|
||||
-- 为张三创建一张10次理发卡
|
||||
INSERT INTO hl_member_card (customer_id, card_name, card_type, total_count, remain_count, status, create_by, create_time) VALUES
|
||||
(100, '理发计次卡', '0', 10, 10, '0', 'admin', sysdate());
|
||||
|
||||
-- 为李四创建一张20次理发卡
|
||||
INSERT INTO hl_member_card (customer_id, card_name, card_type, total_count, remain_count, status, create_by, create_time) VALUES
|
||||
(101, '理发计次卡', '0', 20, 19, '0', 'admin', sysdate());
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 测试数据-订单及明细
|
||||
-- ----------------------------
|
||||
-- 订单1:散客现金支付剪发
|
||||
INSERT INTO hl_order (order_no, customer_id, pay_amount, pay_method, pay_status, order_type, total_amount, create_by, create_time) VALUES
|
||||
('20251219000001', 102, 38.00, '0', '1', '0', 38.00, 'admin', sysdate());
|
||||
|
||||
INSERT INTO hl_order_detail (order_id, item_id, item_name, price, quantity, staff_id, item_type) VALUES
|
||||
(1000, 100, '男士剪发', 38.00, 1, 100, '0');
|
||||
|
||||
|
||||
-- 订单2:李四会员卡扣次烫发
|
||||
INSERT INTO hl_order (order_no, customer_id, pay_amount, pay_method, pay_status, order_type, total_amount, create_by, create_time) VALUES
|
||||
('20251219000002', 101, 288.00, '2', '1', '0', 288.00, 'admin', sysdate());
|
||||
|
||||
INSERT INTO hl_order_detail (order_id, item_id, item_name, price, quantity, staff_id, deduct_card_id, item_type) VALUES
|
||||
(1001, 102, '烫发', 288.00, 1, 100, 101, '0');
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 初始化完成提示
|
||||
-- ----------------------------
|
||||
-- ============================
|
||||
-- HairLink Phase 1 数据库初始化完成!
|
||||
--
|
||||
-- 已创建表:
|
||||
-- 1. hl_customer (顾客档案表)
|
||||
-- 2. hl_member_card (会员权益卡表)
|
||||
-- 3. hl_service (服务项目表)
|
||||
-- 4. hl_staff (员工扩展表)
|
||||
-- 5. hl_order (业务订单表)
|
||||
-- 6. hl_order_detail (订单明细表)
|
||||
--
|
||||
-- 已添加外键约束:4个
|
||||
--
|
||||
-- 已初始化数据:
|
||||
-- - 服务项目:5条
|
||||
-- - 测试顾客:3条
|
||||
-- - 测试员工:2条
|
||||
-- - 测试会员卡:2条
|
||||
-- - 测试订单:2条(含订单明细)
|
||||
--
|
||||
-- 下一步:
|
||||
-- 1. 在MySQL中执行本脚本
|
||||
-- 2. 验证表结构和数据
|
||||
-- 3. 开始后端代码开发
|
||||
-- ============================
|
||||
|
|
@ -0,0 +1,792 @@
|
|||
-- ============================
|
||||
-- 若依框架基础表 + 多租户支持
|
||||
-- 版本:1.0.0_with_tenant
|
||||
-- 创建日期:2025-12-19
|
||||
-- 说明:在若依框架基础表创建时就包含多租户支持,无需后续ALTER TABLE
|
||||
-- 改动说明:
|
||||
-- 1. 新增 sys_tenant 租户管理表
|
||||
-- 2. 为 sys_user, sys_dept, sys_role, sys_post, sys_notice, sys_config 添加 tenant_id 字段
|
||||
-- 3. 为 tenant_id 字段创建索引
|
||||
-- 4. 初始化数据设置默认租户ID
|
||||
-- ============================
|
||||
|
||||
-- ----------------------------
|
||||
-- 0、租户信息表 (新增)
|
||||
-- ----------------------------
|
||||
drop table if exists sys_tenant;
|
||||
create table sys_tenant (
|
||||
tenant_id bigint(20) not null auto_increment comment '租户ID',
|
||||
tenant_name varchar(50) not null comment '租户名称',
|
||||
tenant_code varchar(50) not null comment '租户编码(唯一标识)',
|
||||
contact_name varchar(50) default null comment '联系人',
|
||||
contact_phone varchar(20) default null comment '联系电话',
|
||||
expire_time datetime default null comment '过期时间(NULL表示永久)',
|
||||
package_id bigint(20) default null comment '套餐ID(预留)',
|
||||
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 default null comment '创建时间',
|
||||
update_by varchar(64) default '' comment '更新者',
|
||||
update_time datetime default null comment '更新时间',
|
||||
remark varchar(500) default null comment '备注',
|
||||
primary key (tenant_id),
|
||||
unique key uk_tenant_code (tenant_code)
|
||||
) engine=innodb auto_increment=1000 comment = '租户信息表';
|
||||
|
||||
-- ----------------------------
|
||||
-- 初始化-租户信息表数据
|
||||
-- ----------------------------
|
||||
insert into sys_tenant (tenant_name, tenant_code, contact_name, contact_phone, status, create_by, create_time, remark) values
|
||||
('默认租户', 'DEFAULT', '系统管理员', '13800138000', '0', 'admin', sysdate(), '系统默认租户,现有数据归属此租户'),
|
||||
('测试租户A', 'TENANT_A', '张三', '13800138001', '0', 'admin', sysdate(), '测试租户A,用于多租户隔离测试'),
|
||||
('测试租户B', 'TENANT_B', '李四', '13800138002', '0', 'admin', sysdate(), '测试租户B,用于跨租户访问测试');
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 1、部门表 (已添加 tenant_id)
|
||||
-- ----------------------------
|
||||
drop table if exists sys_dept;
|
||||
create table sys_dept (
|
||||
dept_id bigint(20) not null auto_increment comment '部门id',
|
||||
tenant_id bigint(20) default null 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),
|
||||
index idx_tenant_id (tenant_id)
|
||||
) engine=innodb auto_increment=200 comment = '部门表';
|
||||
|
||||
-- ----------------------------
|
||||
-- 初始化-部门表数据
|
||||
-- ----------------------------
|
||||
set @default_tenant_id = (select tenant_id from sys_tenant where tenant_code = 'DEFAULT');
|
||||
insert into sys_dept values(100, @default_tenant_id, 0, '0', '若依科技', 0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||
insert into sys_dept values(101, @default_tenant_id, 100, '0,100', '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||
insert into sys_dept values(102, @default_tenant_id, 100, '0,100', '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||
insert into sys_dept values(103, @default_tenant_id, 101, '0,100,101', '研发部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||
insert into sys_dept values(104, @default_tenant_id, 101, '0,100,101', '市场部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||
insert into sys_dept values(105, @default_tenant_id, 101, '0,100,101', '测试部门', 3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||
insert into sys_dept values(106, @default_tenant_id, 101, '0,100,101', '财务部门', 4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||
insert into sys_dept values(107, @default_tenant_id, 101, '0,100,101', '运维部门', 5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||
insert into sys_dept values(108, @default_tenant_id, 102, '0,100,102', '市场部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||
insert into sys_dept values(109, @default_tenant_id, 102, '0,100,102', '财务部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 2、用户信息表 (已添加 tenant_id)
|
||||
-- ----------------------------
|
||||
drop table if exists sys_user;
|
||||
create table sys_user (
|
||||
user_id bigint(20) not null auto_increment comment '用户ID',
|
||||
tenant_id bigint(20) default null 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 '最后登录时间',
|
||||
pwd_update_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),
|
||||
index idx_tenant_id (tenant_id),
|
||||
index idx_tenant_user (tenant_id, user_name)
|
||||
) engine=innodb auto_increment=100 comment = '用户信息表';
|
||||
|
||||
-- ----------------------------
|
||||
-- 初始化-用户信息表数据
|
||||
-- ----------------------------
|
||||
insert into sys_user values(1, @default_tenant_id, 103, 'admin', '若依', '00', 'ry@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), sysdate(), 'admin', sysdate(), '', null, '管理员');
|
||||
insert into sys_user values(2, @default_tenant_id, 105, 'ry', '若依', '00', 'ry@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), sysdate(), 'admin', sysdate(), '', null, '测试员');
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 3、岗位信息表 (已添加 tenant_id)
|
||||
-- ----------------------------
|
||||
drop table if exists sys_post;
|
||||
create table sys_post
|
||||
(
|
||||
post_id bigint(20) not null auto_increment comment '岗位ID',
|
||||
tenant_id bigint(20) default null 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),
|
||||
index idx_tenant_id (tenant_id)
|
||||
) engine=innodb comment = '岗位信息表';
|
||||
|
||||
-- ----------------------------
|
||||
-- 初始化-岗位信息表数据
|
||||
-- ----------------------------
|
||||
insert into sys_post values(1, @default_tenant_id, 'ceo', '董事长', 1, '0', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_post values(2, @default_tenant_id, 'se', '项目经理', 2, '0', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_post values(3, @default_tenant_id, 'hr', '人力资源', 3, '0', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_post values(4, @default_tenant_id, 'user', '普通员工', 4, '0', 'admin', sysdate(), '', null, '');
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 4、角色信息表 (已添加 tenant_id)
|
||||
-- ----------------------------
|
||||
drop table if exists sys_role;
|
||||
create table sys_role (
|
||||
role_id bigint(20) not null auto_increment comment '角色ID',
|
||||
tenant_id bigint(20) default null 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),
|
||||
index idx_tenant_id (tenant_id)
|
||||
) engine=innodb auto_increment=100 comment = '角色信息表';
|
||||
|
||||
-- ----------------------------
|
||||
-- 初始化-角色信息表数据
|
||||
-- ----------------------------
|
||||
insert into sys_role values('1', @default_tenant_id, '超级管理员', 'admin', 1, 1, 1, 1, '0', '0', 'admin', sysdate(), '', null, '超级管理员');
|
||||
insert into sys_role values('2', @default_tenant_id, '普通角色', '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 '组件路径',
|
||||
query varchar(255) default null comment '路由参数',
|
||||
route_name varchar(50) default '' 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://ruoyi.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', '缓存列表', '2', '6', 'cacheList', 'monitor/cache/list', '', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis-list', 'admin', sysdate(), '', null, '缓存列表菜单');
|
||||
insert into sys_menu values('115', '表单构建', '3', '1', 'build', 'tool/build/index', '', '', 1, 0, 'C', '0', '0', 'tool:build:list', 'build', 'admin', sysdate(), '', null, '表单构建菜单');
|
||||
insert into sys_menu values('116', '代码生成', '3', '2', 'gen', 'tool/gen/index', '', '', 1, 0, 'C', '0', '0', 'tool:gen:list', 'code', 'admin', sysdate(), '', null, '代码生成菜单');
|
||||
insert into sys_menu values('117', '系统接口', '3', '3', 'swagger', 'tool/swagger/index', '', '', 1, 0, 'C', '0', '0', 'tool:swagger:list', 'swagger', 'admin', sysdate(), '', null, '系统接口菜单');
|
||||
-- 三级菜单
|
||||
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('1000', '用户查询', '100', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:query', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1001', '用户新增', '100', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:add', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1002', '用户修改', '100', '3', '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:edit', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1003', '用户删除', '100', '4', '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:remove', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1004', '用户导出', '100', '5', '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:export', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1005', '用户导入', '100', '6', '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:import', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1006', '重置密码', '100', '7', '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:resetPwd', '#', 'admin', sysdate(), '', null, '');
|
||||
-- 角色管理按钮
|
||||
insert into sys_menu values('1007', '角色查询', '101', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'system:role:query', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1008', '角色新增', '101', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'system:role:add', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1009', '角色修改', '101', '3', '', '', '', '', 1, 0, 'F', '0', '0', 'system:role:edit', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1010', '角色删除', '101', '4', '', '', '', '', 1, 0, 'F', '0', '0', 'system:role:remove', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1011', '角色导出', '101', '5', '', '', '', '', 1, 0, 'F', '0', '0', 'system:role:export', '#', 'admin', sysdate(), '', null, '');
|
||||
-- 菜单管理按钮
|
||||
insert into sys_menu values('1012', '菜单查询', '102', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:query', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1013', '菜单新增', '102', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:add', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1014', '菜单修改', '102', '3', '', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:edit', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1015', '菜单删除', '102', '4', '', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:remove', '#', 'admin', sysdate(), '', null, '');
|
||||
-- 部门管理按钮
|
||||
insert into sys_menu values('1016', '部门查询', '103', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:query', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1017', '部门新增', '103', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:add', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1018', '部门修改', '103', '3', '', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1019', '部门删除', '103', '4', '', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove', '#', 'admin', sysdate(), '', null, '');
|
||||
-- 岗位管理按钮
|
||||
insert into sys_menu values('1020', '岗位查询', '104', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'system:post:query', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1021', '岗位新增', '104', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'system:post:add', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1022', '岗位修改', '104', '3', '', '', '', '', 1, 0, 'F', '0', '0', 'system:post:edit', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1023', '岗位删除', '104', '4', '', '', '', '', 1, 0, 'F', '0', '0', 'system:post:remove', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1024', '岗位导出', '104', '5', '', '', '', '', 1, 0, 'F', '0', '0', 'system:post:export', '#', 'admin', sysdate(), '', null, '');
|
||||
-- 字典管理按钮
|
||||
insert into sys_menu values('1025', '字典查询', '105', '1', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:dict:query', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1026', '字典新增', '105', '2', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:dict:add', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1027', '字典修改', '105', '3', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:dict:edit', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1028', '字典删除', '105', '4', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:dict:remove', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1029', '字典导出', '105', '5', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:dict:export', '#', 'admin', sysdate(), '', null, '');
|
||||
-- 参数设置按钮
|
||||
insert into sys_menu values('1030', '参数查询', '106', '1', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:config:query', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1031', '参数新增', '106', '2', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:config:add', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1032', '参数修改', '106', '3', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:config:edit', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1033', '参数删除', '106', '4', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:config:remove', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1034', '参数导出', '106', '5', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:config:export', '#', 'admin', sysdate(), '', null, '');
|
||||
-- 通知公告按钮
|
||||
insert into sys_menu values('1035', '公告查询', '107', '1', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:notice:query', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1036', '公告新增', '107', '2', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:notice:add', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1037', '公告修改', '107', '3', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:notice:edit', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1038', '公告删除', '107', '4', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:notice:remove', '#', 'admin', sysdate(), '', null, '');
|
||||
-- 操作日志按钮
|
||||
insert into sys_menu values('1039', '操作查询', '500', '1', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:query', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1040', '操作删除', '500', '2', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:remove', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1041', '日志导出', '500', '3', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:export', '#', 'admin', sysdate(), '', null, '');
|
||||
-- 登录日志按钮
|
||||
insert into sys_menu values('1042', '登录查询', '501', '1', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1043', '登录删除', '501', '2', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1044', '日志导出', '501', '3', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1045', '账户解锁', '501', '4', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', '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', '6', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:job:export', '#', 'admin', sysdate(), '', null, '');
|
||||
-- 代码生成按钮
|
||||
insert into sys_menu values('1055', '生成查询', '116', '1', '#', '', '', '', 1, 0, 'F', '0', '0', 'tool:gen:query', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1056', '生成修改', '116', '2', '#', '', '', '', 1, 0, 'F', '0', '0', 'tool:gen:edit', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1057', '生成删除', '116', '3', '#', '', '', '', 1, 0, 'F', '0', '0', 'tool:gen:remove', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1058', '导入代码', '116', '4', '#', '', '', '', 1, 0, 'F', '0', '0', 'tool:gen:import', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1059', '预览代码', '116', '5', '#', '', '', '', 1, 0, 'F', '0', '0', 'tool:gen:preview', '#', 'admin', sysdate(), '', null, '');
|
||||
insert into sys_menu values('1060', '生成代码', '116', '6', '#', '', '', '', 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', '117');
|
||||
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(200) 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 '操作时间',
|
||||
cost_time bigint(20) default 0 comment '消耗时间',
|
||||
primary key (oper_id),
|
||||
key idx_sys_oper_log_bt (business_type),
|
||||
key idx_sys_oper_log_s (status),
|
||||
key idx_sys_oper_log_ot (oper_time)
|
||||
) 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, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '其他操作');
|
||||
insert into sys_dict_data values(19, 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '新增操作');
|
||||
insert into sys_dict_data values(20, 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '修改操作');
|
||||
insert into sys_dict_data values(21, 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '删除操作');
|
||||
insert into sys_dict_data values(22, 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 'admin', sysdate(), '', null, '授权操作');
|
||||
insert into sys_dict_data values(23, 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '导出操作');
|
||||
insert into sys_dict_data values(24, 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '导入操作');
|
||||
insert into sys_dict_data values(25, 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '强退操作');
|
||||
insert into sys_dict_data values(26, 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '生成操作');
|
||||
insert into sys_dict_data values(27, 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '清空操作');
|
||||
insert into sys_dict_data values(28, 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 'admin', sysdate(), '', null, '正常状态');
|
||||
insert into sys_dict_data values(29, 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态');
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 13、参数配置表 (已添加 tenant_id)
|
||||
-- ----------------------------
|
||||
drop table if exists sys_config;
|
||||
create table sys_config (
|
||||
config_id int(5) not null auto_increment comment '参数主键',
|
||||
tenant_id bigint(20) default null comment '租户ID(NULL表示全局配置)',
|
||||
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),
|
||||
index idx_tenant_id (tenant_id)
|
||||
) engine=innodb auto_increment=100 comment = '参数配置表';
|
||||
|
||||
-- 注意:sys_config 的初始化数据 tenant_id 保持 NULL,表示全局配置
|
||||
insert into sys_config values(1, NULL, '主框架页-默认皮肤样式名称', '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, NULL, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 'admin', sysdate(), '', null, '初始化密码 123456' );
|
||||
insert into sys_config values(3, NULL, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 'admin', sysdate(), '', null, '深色主题theme-dark,浅色主题theme-light' );
|
||||
insert into sys_config values(4, NULL, '账号自助-验证码开关', 'sys.account.captchaEnabled', 'true', 'Y', 'admin', sysdate(), '', null, '是否开启验证码功能(true开启,false关闭)');
|
||||
insert into sys_config values(5, NULL, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'admin', sysdate(), '', null, '是否开启注册用户功能(true开启,false关闭)');
|
||||
insert into sys_config values(6, NULL, '用户登录-黑名单列表', 'sys.login.blackIPList', '', 'Y', 'admin', sysdate(), '', null, '设置登录IP黑名单限制,多个匹配项以;分隔,支持匹配(*通配、网段)');
|
||||
insert into sys_config values(7, NULL, '用户管理-初始密码修改策略', 'sys.account.initPasswordModify', '1', 'Y', 'admin', sysdate(), '', null, '0:初始密码修改策略关闭,没有任何提示,1:提醒用户,如果未修改初始密码,则在登录时就会提醒修改密码对话框');
|
||||
insert into sys_config values(8, NULL, '用户管理-账号密码更新周期', 'sys.account.passwordValidateDays', '0', 'Y', 'admin', sysdate(), '', null, '密码更新周期(填写数字,数据初始化值为0不限制,若修改必须为大于0小于365的正整数),如果超过这个周期登录系统时,则在登录时就会提醒修改密码对话框');
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 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),
|
||||
key idx_sys_logininfor_s (status),
|
||||
key idx_sys_logininfor_lt (login_time)
|
||||
) 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、通知公告表 (已添加 tenant_id)
|
||||
-- ----------------------------
|
||||
drop table if exists sys_notice;
|
||||
create table sys_notice (
|
||||
notice_id int(4) not null auto_increment comment '公告ID',
|
||||
tenant_id bigint(20) default null 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),
|
||||
index idx_tenant_id (tenant_id)
|
||||
) engine=innodb auto_increment=10 comment = '通知公告表';
|
||||
|
||||
-- ----------------------------
|
||||
-- 初始化-公告信息表数据
|
||||
-- ----------------------------
|
||||
insert into sys_notice values('1', @default_tenant_id, '温馨提醒:2018-07-01 若依新版本发布啦', '2', '新版本内容', '0', 'admin', sysdate(), '', null, '管理员');
|
||||
insert into sys_notice values('2', @default_tenant_id, '维护通知: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树表操作)',
|
||||
tpl_web_type varchar(30) default '' comment '前端模板类型(element-ui模版 element-plus模版)',
|
||||
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 bigint(20) 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 = '代码生成业务表字段';
|
||||
|
||||
|
||||
-- ============================
|
||||
-- 初始化完成提示
|
||||
-- ============================
|
||||
-- ============================
|
||||
-- 若依框架 + 多租户支持 初始化完成!
|
||||
--
|
||||
-- 已完成改造:
|
||||
-- 1. 新增 sys_tenant 租户管理表
|
||||
-- 2. 为 6 张表添加 tenant_id 字段:
|
||||
-- - sys_user (用户表)
|
||||
-- - sys_dept (部门表)
|
||||
-- - sys_role (角色表)
|
||||
-- - sys_post (岗位表)
|
||||
-- - sys_notice (通知公告表)
|
||||
-- - sys_config (参数配置表,NULL表示全局配置)
|
||||
-- 3. 创建必要的索引:
|
||||
-- - sys_user: idx_tenant_id, idx_tenant_user(tenant_id, user_name)
|
||||
-- - 其他表: idx_tenant_id
|
||||
-- 4. 初始化数据已设置默认租户ID
|
||||
--
|
||||
-- 下一步:
|
||||
-- 1. 执行 hairlink_phase1.sql (业务表)
|
||||
-- 2. 开发后端租户隔离逻辑:
|
||||
-- - TenantContext (租户上下文)
|
||||
-- - TenantInterceptor (HTTP拦截器)
|
||||
-- - TenantSqlInterceptor (MyBatis拦截器)
|
||||
-- ============================
|
||||
Loading…
Reference in New Issue