feat(tenant): 实现多租户数据隔离功能

- 新增租户管理模块(SysTenant CRUD)
  - 实现 TenantContext 租户上下文(基于 TransmittableThreadLocal)
  - 新增 TenantInterceptor HTTP拦截器,自动识别当前租户
  - 新增 TenantSqlInterceptor MyBatis 拦截器,自动注入 tenant_id 过滤条件
  - 支持超级管理员全局模式,可查看所有租户数据
  - 扩展 LoginUser/SysUser 支持租户字段
  - 新增前端租户管理页面
pull/1126/head
9264yf 2025-12-19 21:21:39 +08:00
parent a50dc8af8a
commit 9a8ffdbb8f
28 changed files with 2949 additions and 32 deletions

17
pom.xml
View File

@ -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>

View File

@ -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));
}
}

View File

@ -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;
}

View File

@ -119,6 +119,12 @@
<artifactId>javax.servlet-api</artifactId>
</dependency>
<!-- 多租户支持 - TransmittableThreadLocal -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -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);
}
}

View File

@ -0,0 +1,98 @@
package com.ruoyi.common.core.context;
import com.alibaba.ttl.TransmittableThreadLocal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* - ThreadLocalID
*
* 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;
}
}

View File

@ -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;
}
}

View File

@ -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)

View File

@ -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()
{

View File

@ -59,6 +59,12 @@
<artifactId>ruoyi-system</artifactId>
</dependency>
<!-- SQL解析器 - 用于MyBatis租户拦截器 -->
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -23,6 +23,7 @@ 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*
@ -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();
}
}

View File

@ -14,6 +14,7 @@ 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;
/**
*
@ -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("/**");
}

View File

@ -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;
/**
* - LoginUsertenant_idTenantContext
*
* Reason: HTTPThreadLocal
*
*
* 1. preHandle()
* 2. SecurityContextLoginUser
* 3.
* -
* - tenant_idTenantContext
* 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已清除");
}
}

View File

@ -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;
/**
* MyBatisSQL - SQL tenant_id
*
* Reason: DataScopeSQL
*
*
* 1. MyBatisStatementHandler.prepare()
* 2. SQL
* 3. SELECT/UPDATE/DELETE +
* 4. 使JSQLParserSQL
* 5. WHERE tenant_id = ?
* 6. SQLBoundSql
*/
@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) {
// 可以从配置文件读取租户表配置
}
}

View File

@ -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;
@ -36,6 +39,7 @@ import com.ruoyi.system.service.ISysUserService;
@Component
public class SysLoginService
{
private static final Logger log = LoggerFactory.getLogger(SysLoginService.class);
@Autowired
private TokenService tokenService;
@ -62,6 +66,14 @@ public class SysLoginService
*/
public String login(String username, String password, String code, String uuid)
{
// 【多租户】登录阶段:临时忽略租户过滤
// Reason: 登录时需要查询用户表获取tenant_id形成先有鸡还是先有蛋的问题
// 因此登录阶段临时设置全局模式,允许查询和更新用户表
try
{
TenantContext.setIgnore(true);
log.debug("【多租户】登录阶段:临时设置全局模式");
// 验证码校验
validateCaptcha(username, code, uuid);
// 登录前置校验
@ -95,9 +107,34 @@ public class SysLoginService
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
{
log.warn("【多租户】超级管理员 {} 登录无需租户ID", username);
}
// 生成token
return tokenService.createToken(loginUser);
}
finally
{
// 【多租户】登录完成,清除全局模式标记
TenantContext.clear();
log.debug("【多租户】登录流程结束,清除全局模式");
}
}
/**
*

View File

@ -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)
@ -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);
}

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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>

View File

@ -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}

View File

@ -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'
})
}

View File

@ -134,3 +134,6 @@ export function deptTreeSelect() {
method: 'get'
})
}
// 导出租户列表查询方法
export { listTenant } from '@/api/system/tenant'

View File

@ -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>

View File

@ -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

View File

@ -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. 开始后端代码开发
-- ============================

View File

@ -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 '租户IDNULL表示全局配置',
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拦截器)
-- ============================