diff --git a/logs/gateway-server.log.2025-05-22.0.gz b/logs/gateway-server.log.2025-05-22.0.gz
new file mode 100644
index 0000000..4e92a6e
Binary files /dev/null and b/logs/gateway-server.log.2025-05-22.0.gz differ
diff --git a/logs/gateway-server.log.2025-05-23.0.gz b/logs/gateway-server.log.2025-05-23.0.gz
new file mode 100644
index 0000000..da74149
Binary files /dev/null and b/logs/gateway-server.log.2025-05-23.0.gz differ
diff --git a/logs/infra-server.log.2025-05-22.0.gz b/logs/infra-server.log.2025-05-22.0.gz
new file mode 100644
index 0000000..67228b4
Binary files /dev/null and b/logs/infra-server.log.2025-05-22.0.gz differ
diff --git a/logs/infra-server.log.2025-05-23.0.gz b/logs/infra-server.log.2025-05-23.0.gz
new file mode 100644
index 0000000..0064c6c
Binary files /dev/null and b/logs/infra-server.log.2025-05-23.0.gz differ
diff --git a/logs/system-server.log.2025-05-22.0.gz b/logs/system-server.log.2025-05-22.0.gz
new file mode 100644
index 0000000..63e381e
Binary files /dev/null and b/logs/system-server.log.2025-05-22.0.gz differ
diff --git a/logs/system-server.log.2025-05-23.0.gz b/logs/system-server.log.2025-05-23.0.gz
new file mode 100644
index 0000000..7015e55
Binary files /dev/null and b/logs/system-server.log.2025-05-23.0.gz differ
diff --git a/tashow-module/tashow-module-sso/pom.xml b/tashow-module/tashow-module-sso/pom.xml
new file mode 100644
index 0000000..b694075
--- /dev/null
+++ b/tashow-module/tashow-module-sso/pom.xml
@@ -0,0 +1,24 @@
+
+
+
+ com.tashow.cloud
+ tashow-module
+ ${revision}
+
+ 4.0.0
+
+ tashow-module-sso-api
+ tashow-module-sso-biz
+
+ tashow-module-sso
+ pom
+
+ ${project.artifactId}
+
+ system 模块下,我们放通用业务,支撑上层的核心业务。
+ 例如说:用户、部门、权限、数据字典等等
+
+
+
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-api/pom.xml b/tashow-module/tashow-module-sso/tashow-module-sso-api/pom.xml
new file mode 100644
index 0000000..44a314d
--- /dev/null
+++ b/tashow-module/tashow-module-sso/tashow-module-sso-api/pom.xml
@@ -0,0 +1,48 @@
+
+
+
+ com.tashow.cloud
+ tashow-module-sso
+ ${revision}
+
+ 4.0.0
+ tashow-module-sso-api
+ jar
+
+ ${project.artifactId}
+
+ system 模块 API,暴露给其它模块调用
+
+
+
+
+ com.tashow.cloud
+ tashow-common
+
+
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-api
+ provided
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+ true
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+ true
+
+
+
+
+
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-api/src/main/java/com/tashow/cloud/TashowModuleSsoApiApplication.java b/tashow-module/tashow-module-sso/tashow-module-sso-api/src/main/java/com/tashow/cloud/TashowModuleSsoApiApplication.java
new file mode 100644
index 0000000..28627af
--- /dev/null
+++ b/tashow-module/tashow-module-sso/tashow-module-sso-api/src/main/java/com/tashow/cloud/TashowModuleSsoApiApplication.java
@@ -0,0 +1,13 @@
+package com.tashow.cloud;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class TashowModuleSsoApiApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(TashowModuleSsoApiApplication.class, args);
+ }
+
+}
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/pom.xml b/tashow-module/tashow-module-sso/tashow-module-sso-biz/pom.xml
new file mode 100644
index 0000000..1e96a1e
--- /dev/null
+++ b/tashow-module/tashow-module-sso/tashow-module-sso-biz/pom.xml
@@ -0,0 +1,208 @@
+
+
+
+ com.tashow.cloud
+ tashow-module-sso
+ ${revision}
+
+ 4.0.0
+ tashow-module-sso-biz
+ jar
+
+ ${project.artifactId}
+
+ system 模块下,我们放通用业务,支撑上层的核心业务。
+ 例如说:用户、部门、权限、数据字典等等
+
+
+
+
+
+ com.tashow.cloud
+ tashow-framework-env
+
+
+
+
+
+
+
+ com.tashow.cloud
+ tashow-data-permission
+
+
+ com.tashow.cloud
+ tashow-framework-tenant
+
+
+
+
+ com.tashow.cloud
+ tashow-framework-security
+
+
+
+
+ com.tashow.cloud
+ tashow-data-mybatis
+
+
+
+ com.tashow.cloud
+ tashow-data-redis
+
+
+
+
+ com.tashow.cloud
+ tashow-framework-rpc
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-config
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-mail
+
+
+
+
+ com.tashow.cloud
+ tashow-framework-monitor
+
+
+
+
+ com.xingyuv
+ spring-boot-starter-justauth
+
+
+
+ com.xingyuv
+ spring-boot-starter-captcha-plus
+
+
+
+ org.dromara.hutool
+ hutool-extra
+
+
+
+
+
+ cn.dev33
+ sa-token-reactor-spring-boot3-starter
+ 1.42.0
+
+
+
+ cn.dev33
+ sa-token-spring-boot3-starter
+ 1.42.0
+
+
+
+
+ cn.dev33
+ sa-token-sso
+ 1.42.0
+
+
+
+
+ cn.dev33
+ sa-token-oauth2
+ 1.42.0
+
+
+
+
+ cn.dev33
+ sa-token-redis-jackson
+ 1.42.0
+
+
+
+ com.dtflys.forest
+ forest-spring-boot-starter
+ 1.5.26
+
+
+
+ org.apache.commons
+ commons-pool2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+
+
+
+ ${project.artifactId}
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.boot.version}
+
+
+
+ repackage
+
+
+
+
+
+
+
+
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/TashowModuleSsoBizApplication.java b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/TashowModuleSsoBizApplication.java
new file mode 100644
index 0000000..7a3db24
--- /dev/null
+++ b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/TashowModuleSsoBizApplication.java
@@ -0,0 +1,16 @@
+package com.tashow.cloud;
+
+import cn.dev33.satoken.oauth2.SaOAuth2Manager;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class TashowModuleSsoBizApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(TashowModuleSsoBizApplication.class, args);
+ System.out.println("\nSa-Token-OAuth2 Server端启动成功,配置如下:");
+ System.out.println(SaOAuth2Manager.getServerConfig());
+ }
+
+}
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/config/MybatisPlusConfig.java b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/config/MybatisPlusConfig.java
new file mode 100644
index 0000000..ad1e19d
--- /dev/null
+++ b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/config/MybatisPlusConfig.java
@@ -0,0 +1,27 @@
+package com.tashow.cloud.config;
+
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * MyBatis Plus配置类
+ */
+@Configuration
+@MapperScan("com.tashow.cloud.mapper")
+public class MybatisPlusConfig {
+
+ /**
+ * 配置分页插件
+ */
+ @Bean
+ public MybatisPlusInterceptor mybatisPlusInterceptor() {
+ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+ // 添加分页插件
+ interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
+ return interceptor;
+ }
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/config/SsoSecurityConfig.java b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/config/SsoSecurityConfig.java
new file mode 100644
index 0000000..e35d8fd
--- /dev/null
+++ b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/config/SsoSecurityConfig.java
@@ -0,0 +1,21 @@
+package com.tashow.cloud.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+/**
+ * SSO模块安全配置类
+ */
+@Configuration
+public class SsoSecurityConfig {
+
+ /**
+ * 配置密码编码器
+ * 使用BCrypt强哈希算法
+ */
+ @Bean
+ public BCryptPasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/controller/SaOAuth2ServerController.java b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/controller/SaOAuth2ServerController.java
new file mode 100644
index 0000000..04339ee
--- /dev/null
+++ b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/controller/SaOAuth2ServerController.java
@@ -0,0 +1,378 @@
+package com.tashow.cloud.controller;
+
+import cn.dev33.satoken.context.SaHolder;
+import cn.dev33.satoken.oauth2.config.SaOAuth2ServerConfig;
+import cn.dev33.satoken.oauth2.consts.GrantType;
+import cn.dev33.satoken.oauth2.data.model.loader.SaClientModel;
+import cn.dev33.satoken.oauth2.processor.SaOAuth2ServerProcessor;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.dev33.satoken.util.SaFoxUtil;
+import cn.dev33.satoken.util.SaResult;
+import com.tashow.cloud.model.SystemUser;
+import com.tashow.cloud.service.SystemUserService;
+import jakarta.annotation.security.PermitAll;
+import jakarta.servlet.http.HttpServletRequest;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Sa-Token OAuth2 Server端 控制器
+ */
+@RestController
+public class SaOAuth2ServerController {
+
+ @Autowired
+ private SystemUserService userService;
+
+ // OAuth2-Server 端:处理所有 OAuth2 相关请求
+ @RequestMapping("/oauth2/*")
+ @PermitAll
+ public Object request() {
+ System.out.println("------- 进入请求: " + SaHolder.getRequest().getUrl());
+ return SaOAuth2ServerProcessor.instance.dister();
+ }
+
+ // Sa-Token OAuth2 定制化配置
+ @Autowired
+ @PermitAll
+ public void configOAuth2Server(SaOAuth2ServerConfig oauth2Server) {
+
+ // 添加 client 信息
+ oauth2Server.addClient(
+ new SaClientModel()
+ .setClientId("1001") // client id
+ .setClientSecret("aaaa-bbbb-cccc-dddd-eeee") // client 秘钥
+ .addAllowRedirectUris("*") // 所有允许授权的 url
+ .addContractScopes("openid", "userid", "userinfo") // 所有签约的权限
+ .addAllowGrantTypes( // 所有允许的授权模式
+ GrantType.authorization_code, // 授权码式
+ GrantType.implicit, // 隐式式
+ GrantType.refresh_token, // 刷新令牌
+ GrantType.password, // 密码式
+ GrantType.client_credentials // 客户端模式
+ )
+ );
+
+ // 可以添加更多 client 信息,只要保持 clientId 唯一就行了
+ // oauth2Server.addClient(...)
+
+ // 配置:未登录时返回的View
+ oauth2Server.notLoginView = () -> {
+ String msg = "当前会话在OAuth-Server端尚未登录,请先访问"
+ + "登录页面"
+ + "或 注册用户"
+ + ",进行登录之后,刷新页面开始授权";
+ return msg;
+ };
+
+ // 配置:登录处理函数
+ oauth2Server.doLoginHandle = (name, pwd) -> {
+ // 从数据库查询用户
+ SystemUser user = userService.getUserByUsername(name);
+
+ // 验证用户是否存在且密码是否正确
+ if(user != null && userService.validatePassword(pwd, user.getPassword())) {
+ // 用户状态检查
+ if(user.getStatus() != 0) {
+ return SaResult.error("账号已被禁用");
+ }
+
+ // 登录成功,记录用户IP
+ StpUtil.login(user.getId());
+
+ // 获取当前的HTTP请求对象
+ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+ String ip = "unknown";
+ if (attributes != null) {
+ HttpServletRequest request = attributes.getRequest();
+ ip = getClientIp(request);
+ }
+
+ userService.updateLoginInfo(user.getId(), ip);
+
+ return SaResult.ok();
+ }
+ return SaResult.error("账号名或密码错误");
+ };
+
+ // 配置:确认授权时返回的 view
+ oauth2Server.confirmView = (clientId, scopes) -> {
+ String scopeStr = SaFoxUtil.convertListToString(scopes);
+ String yesCode =
+ "fetch('/oauth2/doConfirm?client_id=" + clientId + "&scope=" + scopeStr + "', {method: 'POST'})" +
+ ".then(res => res.json())" +
+ ".then(res => location.reload())";
+ String res = "
应用 " + clientId + " 请求授权:" + scopeStr + ",是否同意?
"
+ + "" +
+ " " +
+ " " +
+ "
";
+ return res;
+ };
+ }
+
+ /**
+ * 获取客户端真实IP地址
+ */
+ private String getClientIp(HttpServletRequest request) {
+ String ip = request.getHeader("X-Forwarded-For");
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("X-Real-IP");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("Proxy-Client-IP");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("WL-Proxy-Client-IP");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getRemoteAddr();
+ }
+ // 多个代理的情况,第一个IP为客户端真实IP
+ if (ip != null && ip.indexOf(",") > 0) {
+ ip = ip.substring(0, ip.indexOf(","));
+ }
+ return ip;
+ }
+
+ /**
+ * 登录页面接口 - 返回登录页面HTML
+ */
+ @GetMapping("/oauth2/login-page")
+ @PermitAll
+ public Object loginPage() {
+ String html = "\n" +
+ "\n" +
+ "\n" +
+ " \n" +
+ " \n" +
+ " OAuth2登录\n" +
+ " \n" +
+ "\n" +
+ "\n" +
+ " \n" +
+ "
用户登录
\n" +
+ "
\n" +
+ "
\n" +
+ "
\n" +
+ " \n" +
+ "\n" +
+ "";
+ return html;
+ }
+
+ /**
+ * 登录接口 - 处理表单提交
+ */
+ @PostMapping("/oauth2/login")
+ @PermitAll
+ public Object login(
+ @RequestParam("username") String username,
+ @RequestParam("password") String password,
+ HttpServletRequest request) {
+
+ // 查询用户
+ SystemUser user = userService.getUserByUsername(username);
+
+ // 验证用户
+ if (user == null || !userService.validatePassword(password, user.getPassword())) {
+ return SaResult.error("账号或密码错误");
+ }
+
+ // 检查用户状态
+ if (user.getStatus() != 0) {
+ return SaResult.error("账号已被禁用");
+ }
+
+ // 登录
+ StpUtil.login(user.getId());
+
+ // 更新登录信息
+ String ip = getClientIp(request);
+ userService.updateLoginInfo(user.getId(), ip);
+
+ // 返回结果
+ Map result = new HashMap<>();
+ result.put("tokenValue", StpUtil.getTokenValue());
+ result.put("tokenName", StpUtil.getTokenName());
+
+ return SaResult.data(result);
+ }
+
+ /**
+ * 登录成功页面 - 当未指定回调地址时的默认页面
+ */
+ @GetMapping("/oauth2/login-success")
+ @PermitAll
+ public Object loginSuccess() {
+ String html = "\n" +
+ "\n" +
+ "\n" +
+ " \n" +
+ " \n" +
+ " 授权成功\n" +
+ " \n" +
+ "\n" +
+ "\n" +
+ " \n" +
+ "
OAuth2授权成功
\n" +
+ "
您已成功登录并授权给应用
\n" +
+ "
\n" +
+ "
当前登录状态:已登录
\n" +
+ "
令牌信息已生成
\n" +
+ "
\n" +
+ "
关闭窗口\n" +
+ "
\n" +
+ "\n" +
+ "";
+ return html;
+ }
+}
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/controller/SsoServerController.java b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/controller/SsoServerController.java
new file mode 100644
index 0000000..12453e5
--- /dev/null
+++ b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/controller/SsoServerController.java
@@ -0,0 +1,654 @@
+package com.tashow.cloud.controller;
+import cn.dev33.satoken.context.SaHolder;
+import cn.dev33.satoken.sso.config.SaSsoServerConfig;
+import cn.dev33.satoken.sso.processor.SaSsoServerProcessor;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.dev33.satoken.util.SaResult;
+import com.dtflys.forest.Forest;
+import com.tashow.cloud.model.SystemUser;
+import com.tashow.cloud.service.SystemUserService;
+import com.tashow.cloud.service.impl.SystemUserServiceImpl;
+import jakarta.annotation.security.PermitAll;
+import jakarta.servlet.http.HttpServletRequest;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Sa-Token-SSO Server端 Controller
+ */
+@RestController
+public class SsoServerController {
+
+ @Autowired
+ private SystemUserService userService;
+
+ @Autowired
+ private SystemUserServiceImpl userServiceImpl;
+
+ /**
+ * SSO-Server端:处理所有SSO相关请求 (下面的章节我们会详细列出开放的接口)
+ */
+ @RequestMapping("/sso/*")
+ @PermitAll
+ public Object ssoRequest() {
+ return SaSsoServerProcessor.instance.dister();
+ }
+
+ /**
+ * 配置SSO相关参数
+ */
+ @Autowired
+ @PermitAll
+ private void configSso(SaSsoServerConfig ssoServer) {
+ // 配置:未登录时返回的View
+ ssoServer.notLoginView = () -> {
+ String msg = "当前会话在SSO-Server端尚未登录,请访问"
+ + "登录页面"
+ + "或 注册用户"
+ + ",登录成功后刷新页面开始授权";
+ return msg;
+ };
+
+ // 配置:登录处理函数
+ ssoServer.doLoginHandle = (name, pwd) -> {
+ // 从数据库查询用户
+ SystemUser user = userService.getUserByUsername(name);
+
+ // 验证用户是否存在且密码是否正确
+ if(user != null && userService.validatePassword(pwd, user.getPassword())) {
+ // 用户状态检查
+ if(user.getStatus() != 0) {
+ return SaResult.error("账号已被禁用");
+ }
+
+ // 登录成功,记录用户IP
+ StpUtil.login(user.getId());
+
+ // 获取当前的HTTP请求对象
+ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+ String ip = "unknown";
+ if (attributes != null) {
+ HttpServletRequest request = attributes.getRequest();
+ ip = getClientIp(request);
+ }
+
+ userService.updateLoginInfo(user.getId(), ip);
+
+ return SaResult.ok("登录成功!").setData(StpUtil.getTokenValue());
+ }
+
+ return SaResult.error("账号或密码错误");
+ };
+
+ // 配置 Http 请求处理器 (在模式三的单点注销功能下用到,如不需要可以注释掉)
+ ssoServer.sendHttp = url -> {
+ try {
+ System.out.println("------ 发起请求:" + url);
+ String resStr = Forest.get(url).executeAsString();
+ System.out.println("------ 请求结果:" + resStr);
+ return resStr;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ };
+ }
+
+ /**
+ * 获取客户端真实IP地址
+ */
+ private String getClientIp(HttpServletRequest request) {
+ String ip = request.getHeader("X-Forwarded-For");
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("X-Real-IP");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("Proxy-Client-IP");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("WL-Proxy-Client-IP");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getRemoteAddr();
+ }
+ // 多个代理的情况,第一个IP为客户端真实IP
+ if (ip != null && ip.indexOf(",") > 0) {
+ ip = ip.substring(0, ip.indexOf(","));
+ }
+ return ip;
+ }
+
+ /**
+ * 单点登录检查 - 自定义接口,方便前端调用
+ */
+ @GetMapping("/sso/checkLogin")
+ @PermitAll
+ public Object ssoCheckLogin() {
+ Map result = new HashMap<>();
+ result.put("isLogin", StpUtil.isLogin());
+ if (StpUtil.isLogin()) {
+ result.put("loginId", StpUtil.getLoginId());
+ result.put("tokenValue", StpUtil.getTokenValue());
+ }
+ return result;
+ }
+
+ /**
+ * 登录页面接口 - 返回登录页面HTML
+ */
+ @GetMapping("/sso/login-page")
+ @PermitAll
+ public Object loginPage() {
+ String html = "\n" +
+ "\n" +
+ "\n" +
+ " \n" +
+ " \n" +
+ " 单点登录\n" +
+ " \n" +
+ "\n" +
+ "\n" +
+ " \n" +
+ "
用户登录
\n" +
+ "
\n" +
+ "
\n" +
+ "
没有账号?点击注册\n" +
+ "
\n" +
+ " \n" +
+ "\n" +
+ "";
+ return html;
+ }
+
+ /**
+ * 登录接口 - 前端表单登录请求
+ */
+ @PostMapping("/sso/login")
+ @PermitAll
+ public Object login(
+ @RequestParam("username") String username,
+ @RequestParam("password") String password,
+ HttpServletRequest request) {
+
+ // 查询用户
+ SystemUser user = userService.getUserByUsername(username);
+
+ // 验证用户
+ if (user == null || !userService.validatePassword(password, user.getPassword())) {
+ return SaResult.error("账号或密码错误");
+ }
+
+ // 检查用户状态
+ if (user.getStatus() != 0) {
+ return SaResult.error("账号已被禁用");
+ }
+
+ // 登录
+ StpUtil.login(user.getId());
+
+ // 更新登录信息
+ String ip = getClientIp(request);
+ userService.updateLoginInfo(user.getId(), ip);
+
+ // 返回结果
+ Map result = new HashMap<>();
+ result.put("tokenValue", StpUtil.getTokenValue());
+ result.put("tokenName", StpUtil.getTokenName());
+
+ return SaResult.data(result);
+ }
+
+ /**
+ * 注销登录
+ */
+ @GetMapping("/sso/logout")
+ @PermitAll
+ public Object logout() {
+ if (StpUtil.isLogin()) {
+ StpUtil.logout();
+ return SaResult.ok("注销成功");
+ }
+ return SaResult.error("当前会话未登录");
+ }
+
+ /**
+ * 查询用户信息 - 自定义接口,用于获取当前登录用户信息
+ */
+ @GetMapping("/sso/userinfo")
+ @PermitAll
+ public Object ssoUserInfo() {
+ if (!StpUtil.isLogin()) {
+ return SaResult.error("用户未登录");
+ }
+
+ Long userId = Long.valueOf(StpUtil.getLoginId().toString());
+ SystemUser user = userService.getUserById(userId);
+
+ if (user == null) {
+ return SaResult.error("用户不存在");
+ }
+
+ // 获取用户角色信息
+ List roleCodes = userServiceImpl.getUserRoleCodes(userId);
+ List roleNames = userServiceImpl.getUserRoleNames(userId);
+
+ Map userInfo = new HashMap<>();
+ userInfo.put("id", user.getId());
+ userInfo.put("username", user.getUsername());
+ userInfo.put("nickname", user.getNickname());
+ userInfo.put("email", user.getEmail());
+ userInfo.put("mobile", user.getMobile());
+ userInfo.put("avatar", user.getAvatar());
+ userInfo.put("roleCodes", roleCodes);
+ userInfo.put("roleNames", roleNames);
+
+ return SaResult.data(userInfo);
+ }
+
+ /**
+ * 登录成功页面 - 当未指定回调地址时的默认页面
+ */
+ @GetMapping("/sso/login-success")
+ @PermitAll
+ public Object loginSuccess() {
+ String html = "\n" +
+ "\n" +
+ "\n" +
+ " \n" +
+ " \n" +
+ " 登录成功\n" +
+ " \n" +
+ "\n" +
+ "\n" +
+ " \n" +
+ "
登录成功
\n" +
+ "
您已成功登录到单点登录系统
\n" +
+ "
\n" +
+ "
正在加载用户信息...
\n" +
+ "
\n" +
+ "
退出登录\n" +
+ "
\n" +
+ " \n" +
+ "\n" +
+ "";
+ return html;
+ }
+
+ /**
+ * 注册页面 - 返回注册页面HTML
+ */
+ @GetMapping("/sso/register-page")
+ @PermitAll
+ public Object registerPage() {
+ String html = "\n" +
+ "\n" +
+ "\n" +
+ " \n" +
+ " \n" +
+ " 用户注册\n" +
+ " \n" +
+ "\n" +
+ "\n" +
+ " \n" +
+ "
用户注册
\n" +
+ "
\n" +
+ "
\n" +
+ "
\n" +
+ "
已有账号?点击登录\n" +
+ "
\n" +
+ " \n" +
+ "\n" +
+ "";
+ return html;
+ }
+}
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/framework/core/RedisCaptchaServiceImpl.java b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/framework/core/RedisCaptchaServiceImpl.java
new file mode 100644
index 0000000..982721a
--- /dev/null
+++ b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/framework/core/RedisCaptchaServiceImpl.java
@@ -0,0 +1,49 @@
+package com.tashow.cloud.framework.core;
+
+import com.xingyuv.captcha.service.CaptchaCacheService;
+import lombok.Setter;
+import org.springframework.data.redis.core.StringRedisTemplate;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 基于 Redis 实现验证码的存储
+ *
+ * @author 星语
+ */
+@Setter
+public class RedisCaptchaServiceImpl implements CaptchaCacheService {
+
+ private StringRedisTemplate stringRedisTemplate;
+
+ @Override
+ public String type() {
+ return "redis";
+ }
+
+ @Override
+ public void set(String key, String value, long expiresInSeconds) {
+ stringRedisTemplate.opsForValue().set(key, value, expiresInSeconds, TimeUnit.SECONDS);
+ }
+
+ @Override
+ public boolean exists(String key) {
+ return Boolean.TRUE.equals(stringRedisTemplate.hasKey(key));
+ }
+
+ @Override
+ public void delete(String key) {
+ stringRedisTemplate.delete(key);
+ }
+
+ @Override
+ public String get(String key) {
+ return stringRedisTemplate.opsForValue().get(key);
+ }
+
+ @Override
+ public Long increment(String key, long val) {
+ return stringRedisTemplate.opsForValue().increment(key,val);
+ }
+
+}
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/mapper/SystemUserMapper.java b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/mapper/SystemUserMapper.java
new file mode 100644
index 0000000..deff44f
--- /dev/null
+++ b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/mapper/SystemUserMapper.java
@@ -0,0 +1,46 @@
+package com.tashow.cloud.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.tashow.cloud.model.SystemUser;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import java.util.List;
+
+/**
+ * 系统用户Mapper接口
+ */
+@Mapper
+public interface SystemUserMapper extends BaseMapper {
+
+ /**
+ * 获取用户角色ID列表
+ *
+ * @param userId 用户ID
+ * @return 角色ID列表
+ */
+ @Select("SELECT role_id FROM system_user_role WHERE user_id = #{userId} AND deleted = 0")
+ List selectUserRoleIds(@Param("userId") Long userId);
+
+ /**
+ * 获取用户角色名称列表
+ *
+ * @param userId 用户ID
+ * @return 角色名称列表
+ */
+ @Select("SELECT r.name FROM system_role r " +
+ "JOIN system_user_role ur ON r.id = ur.role_id " +
+ "WHERE ur.user_id = #{userId} AND r.deleted = 0 AND ur.deleted = 0 AND r.status = 0")
+ List selectUserRoleNames(@Param("userId") Long userId);
+
+ /**
+ * 获取用户角色编码列表
+ *
+ * @param userId 用户ID
+ * @return 角色编码列表
+ */
+ @Select("SELECT r.code FROM system_role r " +
+ "JOIN system_user_role ur ON r.id = ur.role_id " +
+ "WHERE ur.user_id = #{userId} AND r.deleted = 0 AND ur.deleted = 0 AND r.status = 0")
+ List selectUserRoleCodes(@Param("userId") Long userId);
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/model/SystemUser.java b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/model/SystemUser.java
new file mode 100644
index 0000000..a3cbcac
--- /dev/null
+++ b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/model/SystemUser.java
@@ -0,0 +1,110 @@
+package com.tashow.cloud.model;
+
+import lombok.Data;
+import java.time.LocalDateTime;
+
+/**
+ * 系统用户实体类
+ */
+@Data
+public class SystemUser {
+ /**
+ * 用户ID
+ */
+ private Long id;
+
+ /**
+ * 用户账号
+ */
+ private String username;
+
+ /**
+ * 密码
+ */
+ private String password;
+
+ /**
+ * 用户昵称
+ */
+ private String nickname;
+
+ /**
+ * 备注
+ */
+ private String remark;
+
+ /**
+ * 部门ID
+ */
+ private Long deptId;
+
+ /**
+ * 岗位编号数组
+ */
+ private String postIds;
+
+ /**
+ * 用户邮箱
+ */
+ private String email;
+
+ /**
+ * 手机号码
+ */
+ private String mobile;
+
+ /**
+ * 用户性别
+ */
+ private Integer sex;
+
+ /**
+ * 头像地址
+ */
+ private String avatar;
+
+ /**
+ * 帐号状态(0正常 1停用)
+ */
+ private Integer status;
+
+ /**
+ * 最后登录IP
+ */
+ private String loginIp;
+
+ /**
+ * 最后登录时间
+ */
+ private LocalDateTime loginDate;
+
+ /**
+ * 创建者
+ */
+ private String creator;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+
+ /**
+ * 更新者
+ */
+ private String updater;
+
+ /**
+ * a更新时间
+ */
+ private LocalDateTime updateTime;
+
+ /**
+ * 是否删除
+ */
+ private Boolean deleted;
+
+ /**
+ * 租户编号
+ */
+ private Long tenantId;
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/security/config/MyFilter.java b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/security/config/MyFilter.java
new file mode 100644
index 0000000..6e174fd
--- /dev/null
+++ b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/security/config/MyFilter.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2020-2099 sa-token.cc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.tashow.cloud.security.config;
+
+import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
+import cn.dev33.satoken.stp.StpUtil;
+import org.springframework.stereotype.Component;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+import reactor.core.publisher.Mono;
+
+/**
+ * 自定义过滤器
+ */
+@Component
+public class MyFilter implements WebFilter {
+
+ @Override
+ public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
+ System.out.println("进入自定义过滤器");
+
+ try {
+ // 先 set 上下文,再调用 Sa-Token 同步 API,并在 finally 里清除上下文
+ SaReactorSyncHolder.setContext(exchange);
+ System.out.println(StpUtil.isLogin());
+ }
+ finally {
+ SaReactorSyncHolder.clearContext();
+ }
+
+ return chain.filter(exchange);
+ }
+
+}
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/security/config/SecurityConfiguration.java b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/security/config/SecurityConfiguration.java
new file mode 100644
index 0000000..44f58a2
--- /dev/null
+++ b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/security/config/SecurityConfiguration.java
@@ -0,0 +1,41 @@
+package com.tashow.cloud.security.config;
+
+import com.tashow.cloud.security.security.config.AuthorizeRequestsCustomizer;
+import com.tashow.cloud.systemapi.enums.ApiConstants;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
+
+/**
+ * System 模块的 Security 配置
+ */
+@Configuration(proxyBeanMethods = false, value = "systemSecurityConfiguration")
+public class SecurityConfiguration {
+
+ @Bean("systemAuthorizeRequestsCustomizer")
+ public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {
+ return new AuthorizeRequestsCustomizer() {
+
+ @Override
+ public void customize(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry registry) {
+ // TODO 芋艿:这个每个项目都需要重复配置,得捉摸有没通用的方案
+ // Swagger 接口文档
+ registry.requestMatchers("/v3/api-docs/**").permitAll()
+ .requestMatchers("/webjars/**").permitAll()
+ .requestMatchers("/swagger-ui").permitAll()
+ .requestMatchers("/swagger-ui/**").permitAll()
+ ;
+ // Druid 监控
+ registry.requestMatchers("/druid/**").permitAll();
+ // Spring Boot Actuator 的安全配置
+ registry.requestMatchers("/actuator").permitAll()
+ .requestMatchers("/actuator/**").permitAll();
+ // RPC 服务的安全配置
+ registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll();
+ }
+
+ };
+ }
+
+}
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/service/SystemUserService.java b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/service/SystemUserService.java
new file mode 100644
index 0000000..e48778b
--- /dev/null
+++ b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/service/SystemUserService.java
@@ -0,0 +1,58 @@
+package com.tashow.cloud.service;
+
+import com.tashow.cloud.model.SystemUser;
+
+/**
+ * 系统用户服务接口
+ */
+public interface SystemUserService {
+
+ /**
+ * 根据用户名查询用户
+ *
+ * @param username 用户名
+ * @return 用户信息
+ */
+ SystemUser getUserByUsername(String username);
+
+ /**
+ * 验证用户密码
+ *
+ * @param rawPassword 原始密码
+ * @param encodedPassword 编码后的密码
+ * @return 是否匹配
+ */
+ boolean validatePassword(String rawPassword, String encodedPassword);
+
+ /**
+ * 根据用户ID获取用户信息
+ *
+ * @param userId 用户ID
+ * @return 用户信息
+ */
+ SystemUser getUserById(Long userId);
+
+ /**
+ * 更新用户登录信息
+ *
+ * @param userId 用户ID
+ * @param ip 登录IP
+ */
+ void updateLoginInfo(Long userId, String ip);
+
+ /**
+ * 注册新用户
+ *
+ * @param user 用户信息
+ * @return 是否注册成功
+ */
+ boolean registerUser(SystemUser user);
+
+ /**
+ * 加密密码
+ *
+ * @param rawPassword 原始密码
+ * @return 加密后的密码
+ */
+ String encodePassword(String rawPassword);
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/service/impl/SystemUserServiceImpl.java b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/service/impl/SystemUserServiceImpl.java
new file mode 100644
index 0000000..8305f75
--- /dev/null
+++ b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/java/com/tashow/cloud/service/impl/SystemUserServiceImpl.java
@@ -0,0 +1,104 @@
+package com.tashow.cloud.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.tashow.cloud.mapper.SystemUserMapper;
+import com.tashow.cloud.model.SystemUser;
+import com.tashow.cloud.service.SystemUserService;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.stereotype.Service;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 系统用户服务实现类
+ */
+@Service
+public class SystemUserServiceImpl extends ServiceImpl implements SystemUserService {
+
+ private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+
+ @Override
+ public SystemUser getUserByUsername(String username) {
+ return getOne(new LambdaQueryWrapper()
+ .eq(SystemUser::getUsername, username)
+ .eq(SystemUser::getStatus, 0)
+ .eq(SystemUser::getDeleted, false));
+ }
+
+ @Override
+ public boolean validatePassword(String rawPassword, String encodedPassword) {
+ return passwordEncoder.matches(rawPassword, encodedPassword);
+ }
+
+ @Override
+ public SystemUser getUserById(Long userId) {
+ return getById(userId);
+ }
+
+ @Override
+ public void updateLoginInfo(Long userId, String ip) {
+ SystemUser user = new SystemUser();
+ user.setId(userId);
+ user.setLoginIp(ip);
+ LocalDateTime now = LocalDateTime.now();
+ user.setLoginDate(now);
+ user.setUpdateTime(now);
+ updateById(user);
+ }
+
+ @Override
+ public boolean registerUser(SystemUser user) {
+ // 设置默认值
+ LocalDateTime now = LocalDateTime.now();
+ user.setCreateTime(now);
+ user.setUpdateTime(now);
+ user.setLoginDate(now);
+ user.setCreator("system");
+ user.setUpdater("system");
+
+ if (user.getAvatar() == null || user.getAvatar().isEmpty()) {
+ user.setAvatar("https://sa-token.cc/logo.png"); // 设置默认头像
+ }
+
+ user.setTenantId(0L); // 默认租户ID
+ user.setDeleted(false); // 设置为未删除
+
+ return save(user);
+ }
+
+ @Override
+ public String encodePassword(String rawPassword) {
+ return passwordEncoder.encode(rawPassword);
+ }
+
+ /**
+ * 获取用户角色ID列表
+ *
+ * @param userId 用户ID
+ * @return 角色ID列表
+ */
+ public List getUserRoleIds(Long userId) {
+ return baseMapper.selectUserRoleIds(userId);
+ }
+
+ /**
+ * 获取用户角色名称列表
+ *
+ * @param userId 用户ID
+ * @return 角色名称列表
+ */
+ public List getUserRoleNames(Long userId) {
+ return baseMapper.selectUserRoleNames(userId);
+ }
+
+ /**
+ * 获取用户角色编码列表
+ *
+ * @param userId 用户ID
+ * @return 角色编码列表
+ */
+ public List getUserRoleCodes(Long userId) {
+ return baseMapper.selectUserRoleCodes(userId);
+ }
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/META-INF/services/com.xingyuv.captcha.service.CaptchaCacheService b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/META-INF/services/com.xingyuv.captcha.service.CaptchaCacheService
new file mode 100644
index 0000000..93db95c
--- /dev/null
+++ b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/META-INF/services/com.xingyuv.captcha.service.CaptchaCacheService
@@ -0,0 +1 @@
+com.tashow.cloud.framework.core.RedisCaptchaServiceImpl
\ No newline at end of file
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/application-local.yaml b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/application-local.yaml
new file mode 100644
index 0000000..ee65c20
--- /dev/null
+++ b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/application-local.yaml
@@ -0,0 +1,66 @@
+--- #################### 注册中心 + 配置中心相关配置 ####################
+
+spring:
+ cloud:
+ nacos:
+ server-addr: 43.139.42.137:8848 # Nacos 服务器地址
+ username: nacos # Nacos 账号
+ password: nacos # Nacos 密码
+ discovery: # 【配置中心】配置项
+ namespace: 5c8b8fe6-9a89-4ae3-975e-ef3bf560ff82 # 命名空间。这里使用 dev 开发环境
+ group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
+ metadata:
+ version: 1.0.0 # 服务实例的版本号,可用于灰度发布
+ config: # 【注册中心】配置项
+ namespace: 5c8b8fe6-9a89-4ae3-975e-ef3bf560ff82 # 命名空间。这里使用 dev 开发环境
+ group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
+
+
+
+ # 数据源配置项
+ autoconfigure:
+ exclude:
+ - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置
+ datasource:
+ druid: # Druid 【监控】相关的全局配置
+ web-stat-filter:
+ enabled: true
+ stat-view-servlet:
+ enabled: true
+ allow: # 设置白名单,不填则允许所有访问
+ url-pattern: /druid/*
+ login-username: # 控制台管理用户名和密码
+ login-password:
+ filter:
+ stat:
+ enabled: true
+ log-slow-sql: true # 慢 SQL 记录
+ slow-sql-millis: 100
+ merge-sql: true
+ wall:
+ config:
+ multi-statement-allow: true
+ dynamic: # 多数据源配置
+ druid: # Druid 【连接池】相关的全局配置
+ initial-size: 1 # 初始连接数
+ min-idle: 1 # 最小连接池数量
+ max-active: 20 # 最大连接池数量
+ max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
+ time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
+ min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
+ max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
+ validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
+ test-while-idle: true
+ test-on-borrow: false
+ test-on-return: false
+ primary: master
+ datasource:
+ master:
+ url: jdbc:mysql://43.139.42.137:8406/tashow-platform?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
+ username: tashow-platform
+ password: tashow123,
+# slave: # 模拟从库,可根据自己需要修改
+# lazy: true # 开启懒加载,保证启动速度
+# url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true
+# username: root
+# password: 123456
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/application.yaml b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/application.yaml
new file mode 100644
index 0000000..77a4ec5
--- /dev/null
+++ b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/application.yaml
@@ -0,0 +1,236 @@
+server:
+ port: 48082
+spring:
+ application:
+ name: sso-server
+ profiles:
+ active: local
+ main:
+ allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。
+ allow-bean-definition-overriding: true # 允许 Bean 覆盖,例如说 Feign 等会存在重复定义的服务
+ config:
+ import:
+ - optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置
+ - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置
+ # Servlet 配置
+ servlet:
+ # 文件上传相关配置项
+ multipart:
+ max-file-size: 16MB # 单个文件大小
+ max-request-size: 32MB # 设置总上传的文件大小
+ # Jackson 配置项
+ jackson:
+ serialization:
+ write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳
+ write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401
+ write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳
+ fail-on-empty-beans: false # 允许序列化无属性的 Bean
+ # Cache 配置项
+ cache:
+ type: REDIS
+ redis:
+ time-to-live: 1h # 设置过期时间为 1 小时
+
+
+logging:
+ file:
+ name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径
+
+--- #################### 接口文档配置 ####################
+
+springdoc:
+ api-docs:
+ enabled: true # 1. 是否开启 Swagger 接文档的元数据
+ path: /v3/api-docs
+ swagger-ui:
+ enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面
+ path: /swagger-ui
+ default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档
+
+knife4j:
+ enable: true # 2.2 是否开启 Swagger 文档的 Knife4j UI 界面
+ setting:
+ language: zh_cn
+
+# MyBatis Plus 的配置项
+mybatis-plus:
+ configuration:
+ map-underscore-to-camel-case: true #虽然默认为 true ,但是还是显示去指定下。
+ global-config:
+ db-config:
+ id-type: NONE # "智能"模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。
+ logic-delete-value: 1 # 逻辑已删除值(默认为 1)
+ logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
+ banner: false # 关闭控制台的 Banner 打印
+ type-aliases-package: ${tashow.info.base-package}.dal.dataobject
+ encryptor:
+ password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成
+
+mybatis-plus-join:
+ banner: false # 关闭控制台的 Banner 打印
+
+# Spring Data Redis 配置
+spring:
+ data:
+ redis:
+ repositories:
+ enabled: false # 项目未使用到 Spring Data Redis 的 Repository,所以直接禁用,保证启动速度
+
+
+
+
+# VO 转换(数据翻译)相关
+easy-trans:
+ is-enable-global: true # 启用全局翻译(拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置,或通过 @IgnoreTrans 忽略某个接口
+
+--- #################### RPC 远程调用相关配置 ####################
+
+--- #################### 消息队列相关 ####################
+spring:
+ # Kafka 配置项,对应 KafkaProperties 配置类
+ kafka:
+ # Kafka Producer 配置项
+ producer:
+ acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。
+ retries: 3 # 发送失败时,重试发送的次数
+ value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化
+ # Kafka Consumer 配置项
+ consumer:
+ auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解
+ value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
+ properties:
+ spring.json.trusted.packages: '*'
+ # Kafka Consumer Listener 监听器配置
+ listener:
+ missing-topics-fatal: false # 消费监听接口监听的主题不存在时,默认会报错。所以通过设置为 false ,解决报错
+
+--- #################### 定时任务相关配置 ####################
+
+xxl:
+ job:
+ executor:
+ appname: ${spring.application.name} # 执行器 AppName
+ logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径
+ accessToken: default_token # 执行器通讯TOKEN
+
+--- #################### 验证码相关配置 ####################
+
+aj:
+ captcha:
+ jigsaw: classpath:images/jigsaw # 滑动验证,底图路径,不配置将使用默认图片;以 classpath: 开头,取 resource 目录下路径
+ pic-click: classpath:images/pic-click # 滑动验证,底图路径,不配置将使用默认图片;以 classpath: 开头,取 resource 目录下路径
+ cache-type: redis # 缓存 local/redis...
+ cache-number: 1000 # local 缓存的阈值,达到这个值,清除缓存
+ timing-clear: 180 # local定时清除过期缓存(单位秒),设置为0代表不执行
+ type: blockPuzzle # 验证码类型 default两种都实例化。 blockPuzzle 滑块拼图 clickWord 文字点选
+ water-mark: 他秀 # 右下角水印文字(我的水印),可使用 https://tool.chinaz.com/tools/unicode.aspx 中文转 Unicode,Linux 可能需要转 unicode
+ interference-options: 0 # 滑动干扰项(0/1/2)
+ req-frequency-limit-enable: false # 接口请求次数一分钟限制是否开启 true|false
+ req-get-lock-limit: 5 # 验证失败5次,get接口锁定
+ req-get-lock-seconds: 10 # 验证失败后,锁定时间间隔
+ req-get-minute-limit: 30 # get 接口一分钟内请求数限制
+ req-check-minute-limit: 60 # check 接口一分钟内请求数限制
+ req-verify-minute-limit: 60 # verify 接口一分钟内请求数限制
+
+--- #################### 芋道相关配置 ####################
+
+tashow:
+ info:
+ version: 1.0.0
+ base-package: com.tashow.cloud.system
+ web:
+ admin-ui:
+ url: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址
+ xss:
+ enable: false
+ exclude-urls: # 如下 url,仅仅是为了演示,去掉配置也没关系
+ - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
+ swagger:
+ title: 管理后台
+ description: 提供管理员管理的所有功能
+ version: ${tashow.info.version}
+ tenant: # 多租户相关配置项
+ enable: true
+ ignore-urls:
+ - /admin-api/system/tenant/get-id-by-name # 基于名字获取租户,不许带租户编号
+ - /admin-api/system/tenant/get-by-website # 基于域名获取租户,不许带租户编号
+ - /admin-api/system/captcha/get-image # 获取图片验证码,和租户无关
+ - /admin-api/system/captcha/get # 获取图片验证码,和租户无关
+ - /admin-api/system/captcha/check # 校验图片验证码,和租户无关
+ - /admin-api/system/sms/callback/* # 短信回调接口,无法带上租户编号
+ - /rpc-api/system/tenant/valid # 防止递归。避免调用 /rpc-api/system/tenant/valid 接口时,又去触发 /rpc-api/system/tenant/valid 去校验
+ - /rpc-api/system/tenant/id-list # 获得租户列表的时候,无需传递租户编号
+ - /rpc-api/system/oauth2/token/check # 访问令牌校验时,无需传递租户编号;主要解决上传文件的场景,前端不会传递 tenant-id!
+ ignore-tables:
+ - system_tenant
+ - system_tenant_package
+ - system_dict_data
+ - system_dict_type
+ - system_error_code
+ - system_menu
+ - system_sms_channel
+ - system_sms_template
+ - system_sms_log
+ - system_sensitive_word
+ - system_oauth2_client
+ - system_mail_account
+ - system_mail_template
+ - system_mail_log
+ - system_notify_template
+ ignore-caches:
+ - user_role_ids
+ - permission_menu_ids
+ - oauth_client
+ - notify_template
+ - mail_account
+ - mail_template
+ - sms_template
+ sms-code: # 短信验证码相关的配置项
+ expire-times: 10m
+ send-frequency: 1m
+ send-maximum-quantity-per-day: 10
+ begin-code: 9999 # 这里配置 9999 的原因是,测试方便。
+ end-code: 9999 # 这里配置 9999 的原因是,测试方便。
+
+debug: false
+
+
+sa-token:
+ # ------- SSO-模式一相关配置 (非模式一不需要配置)
+ # cookie:
+ # 配置 Cookie 作用域
+ # domain: stp.com
+
+ # ------- SSO-模式二相关配置
+ sso-server:
+ # Ticket有效期 (单位: 秒),默认五分钟
+ ticket-timeout: 300
+ # 所有允许的授权回调地址
+ allow-url: "*"
+ home-route: "/sso/login-success"
+ # ------- SSO-模式三相关配置 (下面的配置在使用SSO模式三时打开)
+ # 是否打开模式三
+ is-http: true
+
+ # OAuth2相关配置
+ oauth2:
+ is-code: true
+ is-implicit: true
+ is-password: true
+ is-client: true
+ is-refresh: true
+ # 授权码有效期 (单位: 秒)
+ code-timeout: 300
+ # Access-Token有效期 (单位: 秒)
+ access-token-timeout: 7200
+ # Refresh-Token有效期 (单位: 秒)
+ refresh-token-timeout: 604800
+ # 客户端模式 Access-Token 有效期 (单位: 秒)
+ client-token-timeout: 7200
+ # 登录后的默认跳转页面
+ home-route: "/oauth2/login-success"
+ sign:
+ # API 接口调用秘钥
+ secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
+ # ---- 除了以上配置项,你还需要为 Sa-Token 配置http请求处理器(文档有步骤说明)
+
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg1.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg1.png
new file mode 100644
index 0000000..c481457
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg1.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg2.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg2.png
new file mode 100644
index 0000000..bf8fb38
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg2.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg3.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg3.png
new file mode 100644
index 0000000..f871d3d
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg3.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg4.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg4.png
new file mode 100644
index 0000000..2e3d871
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg4.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg5.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg5.png
new file mode 100644
index 0000000..fe383b7
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg5.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg6.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg6.png
new file mode 100644
index 0000000..5024ceb
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg6.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg7.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg7.png
new file mode 100644
index 0000000..efe76f8
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg7.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg8.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg8.png
new file mode 100644
index 0000000..2727aa3
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg8.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg9.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg9.png
new file mode 100644
index 0000000..4463aa2
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/original/bg9.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/1.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/1.png
new file mode 100644
index 0000000..ef11324
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/1.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/10.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/10.png
new file mode 100644
index 0000000..297e44c
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/10.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/11.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/11.png
new file mode 100644
index 0000000..d9b1da8
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/11.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/12.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/12.png
new file mode 100644
index 0000000..07e7313
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/12.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/13.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/13.png
new file mode 100644
index 0000000..82c3dd9
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/13.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/14.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/14.png
new file mode 100644
index 0000000..0b9a866
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/14.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/15.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/15.png
new file mode 100644
index 0000000..86b0d1c
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/15.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/16.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/16.png
new file mode 100644
index 0000000..e90a6e2
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/16.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/17.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/17.png
new file mode 100644
index 0000000..a82cbc7
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/17.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/18.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/18.png
new file mode 100644
index 0000000..d3f3cfd
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/18.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/19.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/19.png
new file mode 100644
index 0000000..eb2855b
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/19.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/8.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/8.png
new file mode 100644
index 0000000..3cb5ce1
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/8.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/9.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/9.png
new file mode 100644
index 0000000..384d354
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/11/9.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/2.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/2.png
new file mode 100644
index 0000000..baf3f06
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/2.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/3.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/3.png
new file mode 100644
index 0000000..ccaf617
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/3.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/4.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/4.png
new file mode 100644
index 0000000..7dab162
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/jigsaw/slidingBlock/4.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg1.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg1.png
new file mode 100644
index 0000000..14e7345
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg1.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg10.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg10.png
new file mode 100644
index 0000000..1ea1d6d
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg10.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg2.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg2.png
new file mode 100644
index 0000000..0edb329
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg2.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg3.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg3.png
new file mode 100644
index 0000000..9167996
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg3.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg4.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg4.png
new file mode 100644
index 0000000..e8e8e6c
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg4.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg5.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg5.png
new file mode 100644
index 0000000..66a3181
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg5.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg6.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg6.png
new file mode 100644
index 0000000..9b0f5d8
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg6.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg7.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg7.png
new file mode 100644
index 0000000..db41c74
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg7.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg8.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg8.png
new file mode 100644
index 0000000..3496813
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg8.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg9.png b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg9.png
new file mode 100644
index 0000000..4e7b477
Binary files /dev/null and b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/images/pic-click/bg9.png differ
diff --git a/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/logback-spring.xml b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000..16f0c0f
--- /dev/null
+++ b/tashow-module/tashow-module-sso/tashow-module-sso-biz/src/main/resources/logback-spring.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ ${PATTERN_DEFAULT}
+
+
+
+
+
+
+
+
+
+ ${PATTERN_DEFAULT}
+
+
+
+ ${LOG_FILE}
+
+
+ ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}
+
+ ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false}
+
+ ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB}
+
+ ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0}
+
+ ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30}
+
+
+
+
+
+ 0
+
+ 256
+
+
+
+
+
+
+
+ ${PATTERN_DEFAULT}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tashow-module/tashow-module-user/pom.xml b/tashow-module/tashow-module-user/pom.xml
new file mode 100644
index 0000000..cdfe634
--- /dev/null
+++ b/tashow-module/tashow-module-user/pom.xml
@@ -0,0 +1,24 @@
+
+
+
+ com.tashow.cloud
+ tashow-module
+ ${revision}
+
+ 4.0.0
+
+ tashow-module-user-api
+ tashow-module-user-biz
+
+ tashow-module-user
+ pom
+
+ ${project.artifactId}
+
+ system 模块下,我们放通用业务,支撑上层的核心业务。
+ 例如说:用户、部门、权限、数据字典等等
+
+
+
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/HELP.md b/tashow-module/tashow-module-user/tashow-module-user-api/HELP.md
new file mode 100644
index 0000000..b7509c2
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/HELP.md
@@ -0,0 +1,10 @@
+# Getting Started
+
+### Reference Documentation
+
+For further reference, please consider the following sections:
+
+* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html)
+* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.6.13/maven-plugin/reference/html/)
+* [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.6.13/maven-plugin/reference/html/#build-image)
+
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/pom.xml b/tashow-module/tashow-module-user/tashow-module-user-api/pom.xml
new file mode 100644
index 0000000..d8e519d
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/pom.xml
@@ -0,0 +1,54 @@
+
+
+
+ com.tashow.cloud
+ tashow-module-user
+ ${revision}
+
+ 4.0.0
+ tashow-module-user-api
+ jar
+
+ ${project.artifactId}
+
+ system 模块 API,暴露给其它模块调用
+
+
+
+
+ com.tashow.cloud
+ tashow-common
+
+
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-api
+ provided
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+ true
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+ true
+
+
+ com.baomidou
+ mybatis-plus-annotation
+ 3.5.9
+ compile
+
+
+
+
+
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/convert/SystemRoleConvert.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/convert/SystemRoleConvert.java
new file mode 100644
index 0000000..1755f20
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/convert/SystemRoleConvert.java
@@ -0,0 +1,63 @@
+package com.tashow.cloud.convert;
+
+import com.tashow.cloud.model.SystemRole;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 系统角色对象转换器
+ */
+@Mapper
+public interface SystemRoleConvert {
+
+ SystemRoleConvert INSTANCE = Mappers.getMapper(SystemRoleConvert.class);
+
+ /**
+ * 将角色列表转换为角色ID列表
+ *
+ * @param roles 角色列表
+ * @return 角色ID列表
+ */
+ default List convertToRoleIds(List roles) {
+ if (roles == null || roles.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return roles.stream()
+ .map(SystemRole::getId)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * 将角色列表转换为角色名称列表
+ *
+ * @param roles 角色列表
+ * @return 角色名称列表
+ */
+ default List convertToRoleNames(List roles) {
+ if (roles == null || roles.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return roles.stream()
+ .map(SystemRole::getName)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * 将角色列表转换为角色编码列表
+ *
+ * @param roles 角色列表
+ * @return 角色编码列表
+ */
+ default List convertToRoleCodes(List roles) {
+ if (roles == null || roles.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return roles.stream()
+ .map(SystemRole::getCode)
+ .collect(Collectors.toList());
+ }
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/model/SystemRole.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/model/SystemRole.java
new file mode 100644
index 0000000..7031edf
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/model/SystemRole.java
@@ -0,0 +1,92 @@
+package com.tashow.cloud.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import java.time.LocalDateTime;
+
+/**
+ * 系统角色实体类
+ */
+@Data
+@TableName("system_role")
+public class SystemRole {
+ /**
+ * 角色ID
+ */
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 角色名称
+ */
+ private String name;
+
+ /**
+ * 角色权限字符串
+ */
+ private String code;
+
+ /**
+ * 显示顺序
+ */
+ private Integer sort;
+
+ /**
+ * 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)
+ */
+ private Integer dataScope;
+
+ /**
+ * 数据范围(指定部门数组)
+ */
+ private String dataScopeDeptIds;
+
+ /**
+ * 角色状态(0正常 1停用)
+ */
+ private Integer status;
+
+ /**
+ * 角色类型
+ */
+ private Integer type;
+
+ /**
+ * 备注
+ */
+ private String remark;
+
+ /**
+ * 创建者
+ */
+ private String creator;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+
+ /**
+ * 更新者
+ */
+ private String updater;
+
+ /**
+ * 更新时间
+ */
+ private LocalDateTime updateTime;
+
+ /**
+ * 是否删除
+ */
+ @TableLogic
+ private Boolean deleted;
+
+ /**
+ * 租户编号
+ */
+ private Long tenantId;
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/model/SystemRoleMenu.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/model/SystemRoleMenu.java
new file mode 100644
index 0000000..cabed2d
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/model/SystemRoleMenu.java
@@ -0,0 +1,62 @@
+package com.tashow.cloud.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import java.time.LocalDateTime;
+
+/**
+ * 角色菜单关联实体类
+ */
+@Data
+@TableName("system_role_menu")
+public class SystemRoleMenu {
+ /**
+ * 自增编号
+ */
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 角色ID
+ */
+ private Long roleId;
+
+ /**
+ * 菜单ID
+ */
+ private Long menuId;
+
+ /**
+ * 创建者
+ */
+ private String creator;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+
+ /**
+ * 更新者
+ */
+ private String updater;
+
+ /**
+ * 更新时间
+ */
+ private LocalDateTime updateTime;
+
+ /**
+ * 是否删除
+ */
+ @TableLogic
+ private Boolean deleted;
+
+ /**
+ * 租户编号
+ */
+ private Long tenantId;
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/model/SystemUser.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/model/SystemUser.java
new file mode 100644
index 0000000..5e0914d
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/model/SystemUser.java
@@ -0,0 +1,118 @@
+package com.tashow.cloud.model;
+
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import java.time.LocalDateTime;
+
+/**
+ * 系统用户实体类
+ */
+@Data
+@TableName("system_users")
+public class SystemUser {
+ /**
+ * 用户ID
+ */
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 用户账号
+ */
+ private String username;
+
+ /**
+ * 密码
+ */
+ private String password;
+
+ /**
+ * 用户昵称
+ */
+ private String nickname;
+
+ /**
+ * 备注
+ */
+ private String remark;
+
+ /**
+ * 部门ID
+ */
+ private Long deptId;
+
+ /**
+ * 岗位编号数组
+ */
+ private String postIds;
+
+ /**
+ * 用户邮箱
+ */
+ private String email;
+
+ /**
+ * 手机号码
+ */
+ private String mobile;
+
+ /**
+ * 用户性别
+ */
+ private Integer sex;
+
+ /**
+ * 头像地址
+ */
+ private String avatar;
+
+ /**
+ * 帐号状态(0正常 1停用)
+ */
+ private Integer status;
+
+ /**
+ * 最后登录IP
+ */
+ private String loginIp;
+
+ /**
+ * 最后登录时间
+ */
+ private LocalDateTime loginDate;
+
+ /**
+ * 创建者
+ */
+ private String creator;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+
+ /**
+ * 更新者
+ */
+ private String updater;
+
+ /**
+ * 更新时间
+ */
+ private LocalDateTime updateTime;
+
+ /**
+ * 是否删除
+ */
+ @TableLogic
+ private Boolean deleted;
+
+ /**
+ * 租户编号
+ */
+ private Long tenantId;
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/model/SystemUserPost.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/model/SystemUserPost.java
new file mode 100644
index 0000000..68076af
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/model/SystemUserPost.java
@@ -0,0 +1,62 @@
+package com.tashow.cloud.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import java.time.LocalDateTime;
+
+/**
+ * 用户岗位关联实体类
+ */
+@Data
+@TableName("system_user_post")
+public class SystemUserPost {
+ /**
+ * 自增编号
+ */
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 用户ID
+ */
+ private Long userId;
+
+ /**
+ * 岗位ID
+ */
+ private Long postId;
+
+ /**
+ * 创建者
+ */
+ private String creator;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+
+ /**
+ * 更新者
+ */
+ private String updater;
+
+ /**
+ * 更新时间
+ */
+ private LocalDateTime updateTime;
+
+ /**
+ * 是否删除
+ */
+ @TableLogic
+ private Boolean deleted;
+
+ /**
+ * 租户编号
+ */
+ private Long tenantId;
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/model/SystemUserRole.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/model/SystemUserRole.java
new file mode 100644
index 0000000..f68d8df
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/model/SystemUserRole.java
@@ -0,0 +1,62 @@
+package com.tashow.cloud.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import java.time.LocalDateTime;
+
+/**
+ * 用户角色关联实体类
+ */
+@Data
+@TableName("system_user_role")
+public class SystemUserRole {
+ /**
+ * 自增编号
+ */
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 用户ID
+ */
+ private Long userId;
+
+ /**
+ * 角色ID
+ */
+ private Long roleId;
+
+ /**
+ * 创建者
+ */
+ private String creator;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+
+ /**
+ * 更新者
+ */
+ private String updater;
+
+ /**
+ * 更新时间
+ */
+ private LocalDateTime updateTime;
+
+ /**
+ * 是否删除
+ */
+ @TableLogic
+ private Boolean deleted;
+
+ /**
+ * 租户编号
+ */
+ private Long tenantId;
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/model/dto/UserRegisterReqDTO.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/model/dto/UserRegisterReqDTO.java
new file mode 100644
index 0000000..8b1076a
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/model/dto/UserRegisterReqDTO.java
@@ -0,0 +1,59 @@
+package com.tashow.cloud.model.dto;
+
+import lombok.Data;
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
+import java.io.Serializable;
+
+/**
+ * 用户注册请求DTO
+ */
+@Data
+public class UserRegisterReqDTO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 用户账号
+ */
+ @NotEmpty(message = "用户账号不能为空")
+ @Size(min = 4, max = 16, message = "账号长度为 4-16 位")
+ private String username;
+
+ /**
+ * 密码
+ */
+ @NotEmpty(message = "密码不能为空")
+ @Size(min = 4, max = 16, message = "密码长度为 4-16 位")
+ private String password;
+
+ /**
+ * 用户昵称
+ */
+ @Size(max = 30, message = "用户昵称长度不能超过30个字符")
+ private String nickname;
+
+ /**
+ * 用户邮箱
+ */
+ @Email(message = "邮箱格式不正确")
+ @Size(max = 50, message = "邮箱长度不能超过50个字符")
+ private String email;
+
+ /**
+ * 手机号码
+ */
+ @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号码格式不正确")
+ private String mobile;
+
+ /**
+ * 用户性别
+ */
+ private Integer sex;
+
+ /**
+ * 登录IP
+ */
+ private String loginIp;
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/service/SystemRoleService.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/service/SystemRoleService.java
new file mode 100644
index 0000000..29257d8
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/service/SystemRoleService.java
@@ -0,0 +1,92 @@
+package com.tashow.cloud.service;
+
+import com.tashow.cloud.model.SystemRole;
+import java.util.List;
+
+/**
+ * 系统角色服务接口
+ */
+public interface SystemRoleService {
+
+ /**
+ * 创建角色
+ *
+ * @param role 角色信息
+ * @param menuIds 菜单ID列表
+ * @return 是否创建成功
+ */
+ boolean createRole(SystemRole role, List menuIds);
+
+ /**
+ * 更新角色
+ *
+ * @param role 角色信息
+ * @param menuIds 菜单ID列表
+ * @return 是否更新成功
+ */
+ boolean updateRole(SystemRole role, List menuIds);
+
+ /**
+ * 删除角色
+ *
+ * @param roleId 角色ID
+ * @return 是否删除成功
+ */
+ boolean deleteRole(Long roleId);
+
+ /**
+ * 获取角色信息
+ *
+ * @param roleId 角色ID
+ * @return 角色信息
+ */
+ SystemRole getRoleById(Long roleId);
+
+ /**
+ * 根据角色编码获取角色
+ *
+ * @param code 角色编码
+ * @return 角色信息
+ */
+ SystemRole getRoleByCode(String code);
+
+ /**
+ * 获取所有角色列表
+ *
+ * @return 角色列表
+ */
+ List getAllRoles();
+
+ /**
+ * 获取指定状态的角色列表
+ *
+ * @param status 状态
+ * @return 角色列表
+ */
+ List getRolesByStatus(Integer status);
+
+ /**
+ * 获取角色的菜单ID列表
+ *
+ * @param roleId 角色ID
+ * @return 菜单ID列表
+ */
+ List getRoleMenuIds(Long roleId);
+
+ /**
+ * 根据用户ID获取角色列表
+ *
+ * @param userId 用户ID
+ * @return 角色列表
+ */
+ List getRolesByUserId(Long userId);
+
+ /**
+ * 更新角色状态
+ *
+ * @param roleId 角色ID
+ * @param status 新状态
+ * @return 是否更新成功
+ */
+ boolean updateRoleStatus(Long roleId, Integer status);
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/service/SystemUserService.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/service/SystemUserService.java
new file mode 100644
index 0000000..f157b5a
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/service/SystemUserService.java
@@ -0,0 +1,137 @@
+package com.tashow.cloud.service;
+
+import com.tashow.cloud.model.SystemUser;
+import java.util.List;
+
+/**
+ * 系统用户服务接口
+ */
+public interface SystemUserService {
+
+ /**
+ * 根据用户名查询用户
+ *
+ * @param username 用户名
+ * @return 用户信息
+ */
+ SystemUser getUserByUsername(String username);
+
+ /**
+ * 验证用户密码
+ *
+ * @param rawPassword 原始密码
+ * @param encodedPassword 编码后的密码
+ * @return 是否匹配
+ */
+ boolean validatePassword(String rawPassword, String encodedPassword);
+
+ /**
+ * 根据用户ID获取用户信息
+ *
+ * @param userId 用户ID
+ * @return 用户信息
+ */
+ SystemUser getUserById(Long userId);
+
+ /**
+ * 更新用户登录信息
+ *
+ * @param userId 用户ID
+ * @param ip 登录IP
+ */
+ void updateLoginInfo(Long userId, String ip);
+
+ /**
+ * 注册新用户
+ *
+ * @param user 用户信息
+ * @return 是否注册成功
+ */
+ boolean registerUser(SystemUser user);
+
+ /**
+ * 加密密码
+ *
+ * @param rawPassword 原始密码
+ * @return 加密后的密码
+ */
+ String encodePassword(String rawPassword);
+
+ /**
+ * 创建用户
+ *
+ * @param user 用户信息
+ * @param roleIds 角色ID列表
+ * @param postIds 岗位ID列表
+ * @return 是否创建成功
+ */
+ boolean createUser(SystemUser user, List roleIds, List postIds);
+
+ /**
+ * 更新用户
+ *
+ * @param user 用户信息
+ * @param roleIds 角色ID列表
+ * @param postIds 岗位ID列表
+ * @return 是否更新成功
+ */
+ boolean updateUser(SystemUser user, List roleIds, List postIds);
+
+ /**
+ * 删除用户
+ *
+ * @param userId 用户ID
+ * @return 是否删除成功
+ */
+ boolean deleteUser(Long userId);
+
+ /**
+ * 重置用户密码
+ *
+ * @param userId 用户ID
+ * @param newPassword 新密码
+ * @return 是否重置成功
+ */
+ boolean resetUserPassword(Long userId, String newPassword);
+
+ /**
+ * 更新用户状态
+ *
+ * @param userId 用户ID
+ * @param status 新状态
+ * @return 是否更新成功
+ */
+ boolean updateUserStatus(Long userId, Integer status);
+
+ /**
+ * 获取用户角色ID列表
+ *
+ * @param userId 用户ID
+ * @return 角色ID列表
+ */
+ List getUserRoleIds(Long userId);
+
+ /**
+ * 获取用户角色名称列表
+ *
+ * @param userId 用户ID
+ * @return 角色名称列表
+ */
+ List getUserRoleNames(Long userId);
+
+ /**
+ * 获取用户角色编码列表
+ *
+ * @param userId 用户ID
+ * @return 角色编码列表
+ */
+ List getUserRoleCodes(Long userId);
+
+ /**
+ * 获取用户岗位ID列表
+ *
+ * @param userId 用户ID
+ * @return 岗位ID列表
+ */
+ List getUserPostIds(Long userId);
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/dto/UserCreateReqDTO.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/dto/UserCreateReqDTO.java
new file mode 100644
index 0000000..2d4b464
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/dto/UserCreateReqDTO.java
@@ -0,0 +1,66 @@
+package com.tashow.cloud.user.dto;
+
+import lombok.Data;
+import java.util.List;
+
+/**
+ * 用户创建请求DTO
+ */
+@Data
+public class UserCreateReqDTO {
+
+ /**
+ * 用户账号
+ */
+ private String username;
+
+ /**
+ * 用户密码
+ */
+ private String password;
+
+ /**
+ * 用户昵称
+ */
+ private String nickname;
+
+ /**
+ * 备注
+ */
+ private String remark;
+
+ /**
+ * 部门ID
+ */
+ private Long deptId;
+
+ /**
+ * 用户邮箱
+ */
+ private String email;
+
+ /**
+ * 手机号码
+ */
+ private String mobile;
+
+ /**
+ * 用户性别
+ */
+ private Integer sex;
+
+ /**
+ * 头像地址
+ */
+ private String avatar;
+
+ /**
+ * 角色ID列表
+ */
+ private List roleIds;
+
+ /**
+ * 岗位ID列表
+ */
+ private List postIds;
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/dto/UserPageReqDTO.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/dto/UserPageReqDTO.java
new file mode 100644
index 0000000..6d89806
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/dto/UserPageReqDTO.java
@@ -0,0 +1,55 @@
+package com.tashow.cloud.user.dto;
+
+import lombok.Data;
+
+/**
+ * 用户分页查询请求DTO
+ */
+@Data
+public class UserPageReqDTO {
+
+ /**
+ * 当前页码
+ */
+ private Integer pageNum = 1;
+
+ /**
+ * 每页条数
+ */
+ private Integer pageSize = 10;
+
+ /**
+ * 用户账号
+ */
+ private String username;
+
+ /**
+ * 用户昵称
+ */
+ private String nickname;
+
+ /**
+ * 手机号码
+ */
+ private String mobile;
+
+ /**
+ * 帐号状态(0正常 1停用)
+ */
+ private Integer status;
+
+ /**
+ * 部门ID
+ */
+ private Long deptId;
+
+ /**
+ * 开始创建时间
+ */
+ private String beginCreateTime;
+
+ /**
+ * 结束创建时间
+ */
+ private String endCreateTime;
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/dto/UserRegisterReqDTO.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/dto/UserRegisterReqDTO.java
new file mode 100644
index 0000000..61a229d
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/dto/UserRegisterReqDTO.java
@@ -0,0 +1,35 @@
+package com.tashow.cloud.user.dto;
+
+import lombok.Data;
+
+/**
+ * 用户注册请求DTO
+ */
+@Data
+public class UserRegisterReqDTO {
+
+ /**
+ * 用户账号
+ */
+ private String username;
+
+ /**
+ * 用户密码
+ */
+ private String password;
+
+ /**
+ * 用户昵称
+ */
+ private String nickname;
+
+ /**
+ * 用户邮箱
+ */
+ private String email;
+
+ /**
+ * 手机号码
+ */
+ private String mobile;
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/dto/UserRespDTO.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/dto/UserRespDTO.java
new file mode 100644
index 0000000..7c81443
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/dto/UserRespDTO.java
@@ -0,0 +1,117 @@
+package com.tashow.cloud.user.dto;
+
+import lombok.Data;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 用户响应DTO
+ */
+@Data
+public class UserRespDTO {
+
+ /**
+ * 用户ID
+ */
+ private Long id;
+
+ /**
+ * 用户账号
+ */
+ private String username;
+
+ /**
+ * 用户昵称
+ */
+ private String nickname;
+
+ /**
+ * 备注
+ */
+ private String remark;
+
+ /**
+ * 部门ID
+ */
+ private Long deptId;
+
+ /**
+ * 部门名称
+ */
+ private String deptName;
+
+ /**
+ * 用户邮箱
+ */
+ private String email;
+
+ /**
+ * 手机号码
+ */
+ private String mobile;
+
+ /**
+ * 用户性别
+ */
+ private Integer sex;
+
+ /**
+ * 性别名称
+ */
+ private String sexName;
+
+ /**
+ * 头像地址
+ */
+ private String avatar;
+
+ /**
+ * 帐号状态(0正常 1停用)
+ */
+ private Integer status;
+
+ /**
+ * 状态名称
+ */
+ private String statusName;
+
+ /**
+ * 最后登录IP
+ */
+ private String loginIp;
+
+ /**
+ * 最后登录时间
+ */
+ private LocalDateTime loginDate;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+
+ /**
+ * 角色ID列表
+ */
+ private List roleIds;
+
+ /**
+ * 角色名称列表
+ */
+ private List roleNames;
+
+ /**
+ * 角色编码列表
+ */
+ private List roleCodes;
+
+ /**
+ * 岗位ID列表
+ */
+ private List postIds;
+
+ /**
+ * 岗位名称列表
+ */
+ private List postNames;
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/dto/UserUpdateReqDTO.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/dto/UserUpdateReqDTO.java
new file mode 100644
index 0000000..d26f937
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/dto/UserUpdateReqDTO.java
@@ -0,0 +1,66 @@
+package com.tashow.cloud.user.dto;
+
+import lombok.Data;
+import java.util.List;
+
+/**
+ * 用户更新请求DTO
+ */
+@Data
+public class UserUpdateReqDTO {
+
+ /**
+ * 用户ID
+ */
+ private Long id;
+
+ /**
+ * 用户昵称
+ */
+ private String nickname;
+
+ /**
+ * 备注
+ */
+ private String remark;
+
+ /**
+ * 部门ID
+ */
+ private Long deptId;
+
+ /**
+ * 用户邮箱
+ */
+ private String email;
+
+ /**
+ * 手机号码
+ */
+ private String mobile;
+
+ /**
+ * 用户性别
+ */
+ private Integer sex;
+
+ /**
+ * 头像地址
+ */
+ private String avatar;
+
+ /**
+ * 帐号状态(0正常 1停用)
+ */
+ private Integer status;
+
+ /**
+ * 角色ID列表
+ */
+ private List roleIds;
+
+ /**
+ * 岗位ID列表
+ */
+ private List postIds;
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/dto/package-info.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/dto/package-info.java
new file mode 100644
index 0000000..0519ecb
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/dto/package-info.java
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/model/BaseEntity.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/model/BaseEntity.java
new file mode 100644
index 0000000..a8fc1a2
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/model/BaseEntity.java
@@ -0,0 +1,34 @@
+package com.tashow.cloud.user.model;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import lombok.Data;
+
+/**
+ * 基础实体类,包含所有实体共有的字段
+ */
+@Data
+public class BaseEntity implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 创建者
+ */
+ private String creator;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+
+ /**
+ * 更新者
+ */
+ private String updater;
+
+ /**
+ * 更新时间
+ */
+ private LocalDateTime updateTime;
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/model/Role.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/model/Role.java
new file mode 100644
index 0000000..8359041
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/model/Role.java
@@ -0,0 +1,78 @@
+package com.tashow.cloud.user.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import lombok.experimental.Accessors;
+
+/**
+ * 角色信息实体
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Accessors(chain = true)
+@TableName("system_role")
+public class Role extends BaseEntity {
+
+ /**
+ * 角色ID
+ */
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 角色名称
+ */
+ private String name;
+
+ /**
+ * 角色权限字符串
+ */
+ private String code;
+
+ /**
+ * 显示顺序
+ */
+ private Integer sort;
+
+ /**
+ * 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)
+ */
+ private Integer dataScope;
+
+ /**
+ * 数据范围(指定部门数组)
+ */
+ private String dataScopeDeptIds;
+
+ /**
+ * 角色状态(0正常 1停用)
+ */
+ private Integer status;
+
+ /**
+ * 角色类型
+ */
+ private Integer type;
+
+ /**
+ * 备注
+ */
+ private String remark;
+
+ /**
+ * 是否删除
+ */
+ @TableLogic
+ private Boolean deleted;
+
+ /**
+ * 租户编号
+ */
+ private Long tenantId;
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/model/RoleMenu.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/model/RoleMenu.java
new file mode 100644
index 0000000..014caef
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/model/RoleMenu.java
@@ -0,0 +1,48 @@
+package com.tashow.cloud.user.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import lombok.experimental.Accessors;
+
+/**
+ * 角色菜单关联实体
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Accessors(chain = true)
+@TableName("system_role_menu")
+public class RoleMenu extends BaseEntity {
+
+ /**
+ * 自增编号
+ */
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 角色ID
+ */
+ private Long roleId;
+
+ /**
+ * 菜单ID
+ */
+ private Long menuId;
+
+ /**
+ * 是否删除
+ */
+ @TableLogic
+ private Boolean deleted;
+
+ /**
+ * 租户编号
+ */
+ private Long tenantId;
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/model/User.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/model/User.java
new file mode 100644
index 0000000..716fe61
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/model/User.java
@@ -0,0 +1,115 @@
+package com.tashow.cloud.user.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import lombok.experimental.Accessors;
+import java.util.List;
+
+/**
+ * 用户信息实体
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Accessors(chain = true)
+@TableName("system_users")
+public class User extends BaseEntity {
+ /**
+ * 用户ID
+ */
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 用户账号
+ */
+ private String username;
+
+ /**
+ * 密码
+ */
+ private String password;
+
+ /**
+ * 用户昵称
+ */
+ private String nickname;
+
+ /**
+ * 备注
+ */
+ private String remark;
+
+ /**
+ * 部门ID
+ */
+ private Long deptId;
+
+ /**
+ * 岗位编号数组
+ */
+ private String postIds;
+
+ /**
+ * 用户邮箱
+ */
+ private String email;
+
+ /**
+ * 手机号码
+ */
+ private String mobile;
+
+ /**
+ * 用户性别
+ * 0: 未知
+ * 1: 男
+ * 2: 女
+ */
+ private Integer sex;
+
+ /**
+ * 头像地址
+ */
+ private String avatar;
+
+ /**
+ * 帐号状态
+ * 0: 正常
+ * 1: 停用
+ */
+ private Integer status;
+
+ /**
+ * 最后登录IP
+ */
+ private String loginIp;
+
+ /**
+ * 最后登录时间
+ */
+ private java.time.LocalDateTime loginDate;
+
+ /**
+ * 是否删除
+ */
+ @TableLogic
+ private Boolean deleted;
+
+ /**
+ * 租户编号
+ */
+ private Long tenantId;
+
+ /**
+ * 角色列表 - 非数据库字段
+ */
+ @TableField(exist = false)
+ private List roles;
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/model/UserPost.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/model/UserPost.java
new file mode 100644
index 0000000..ea00e89
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/model/UserPost.java
@@ -0,0 +1,48 @@
+package com.tashow.cloud.user.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import lombok.experimental.Accessors;
+
+/**
+ * 用户岗位关联实体
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Accessors(chain = true)
+@TableName("system_user_post")
+public class UserPost extends BaseEntity {
+
+ /**
+ * 自增编号
+ */
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 用户ID
+ */
+ private Long userId;
+
+ /**
+ * 岗位ID
+ */
+ private Long postId;
+
+ /**
+ * 是否删除
+ */
+ @TableLogic
+ private Boolean deleted;
+
+ /**
+ * 租户编号
+ */
+ private Long tenantId;
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/model/UserRole.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/model/UserRole.java
new file mode 100644
index 0000000..d1fbd88
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/model/UserRole.java
@@ -0,0 +1,48 @@
+package com.tashow.cloud.user.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import lombok.experimental.Accessors;
+
+/**
+ * 用户角色关联实体
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Accessors(chain = true)
+@TableName("system_user_role")
+public class UserRole extends BaseEntity {
+
+ /**
+ * 自增编号
+ */
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 用户ID
+ */
+ private Long userId;
+
+ /**
+ * 角色ID
+ */
+ private Long roleId;
+
+ /**
+ * 是否删除
+ */
+ @TableLogic
+ private Boolean deleted;
+
+ /**
+ * 租户编号
+ */
+ private Long tenantId;
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/service/RoleService.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/service/RoleService.java
new file mode 100644
index 0000000..62d01ef
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/service/RoleService.java
@@ -0,0 +1,89 @@
+package com.tashow.cloud.user.service;
+
+import com.tashow.cloud.user.model.Role;
+import java.util.List;
+
+/**
+ * 角色服务接口
+ */
+public interface RoleService {
+
+ /**
+ * 创建角色
+ *
+ * @param role 角色信息
+ * @param menuIds 菜单ID列表
+ * @return 角色ID
+ */
+ Long createRole(Role role, List menuIds);
+
+ /**
+ * 更新角色
+ *
+ * @param role 角色信息
+ * @param menuIds 菜单ID列表
+ */
+ void updateRole(Role role, List menuIds);
+
+ /**
+ * 删除角色
+ *
+ * @param roleId 角色ID
+ */
+ void deleteRole(Long roleId);
+
+ /**
+ * 获取角色信息
+ *
+ * @param roleId 角色ID
+ * @return 角色信息
+ */
+ Role getRoleById(Long roleId);
+
+ /**
+ * 根据角色编码获取角色
+ *
+ * @param code 角色编码
+ * @return 角色信息
+ */
+ Role getRoleByCode(String code);
+
+ /**
+ * 获取所有角色列表
+ *
+ * @return 角色列表
+ */
+ List getAllRoles();
+
+ /**
+ * 获取指定状态的角色列表
+ *
+ * @param status 状态
+ * @return 角色列表
+ */
+ List getRolesByStatus(Integer status);
+
+ /**
+ * 获取角色的菜单ID列表
+ *
+ * @param roleId 角色ID
+ * @return 菜单ID列表
+ */
+ List getRoleMenuIds(Long roleId);
+
+ /**
+ * 根据用户ID获取角色列表
+ *
+ * @param userId 用户ID
+ * @return 角色列表
+ */
+ List getRolesByUserId(Long userId);
+
+ /**
+ * 更新角色状态
+ *
+ * @param roleId 角色ID
+ * @param status 新状态
+ */
+ void updateRoleStatus(Long roleId, Integer status);
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/service/UserService.java b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/service/UserService.java
new file mode 100644
index 0000000..84530c5
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-api/src/main/java/com/tashow/cloud/user/service/UserService.java
@@ -0,0 +1,142 @@
+package com.tashow.cloud.user.service;
+
+import com.tashow.cloud.user.dto.UserCreateReqDTO;
+import com.tashow.cloud.user.dto.UserPageReqDTO;
+import com.tashow.cloud.user.dto.UserRegisterReqDTO;
+import com.tashow.cloud.user.dto.UserRespDTO;
+import com.tashow.cloud.user.dto.UserUpdateReqDTO;
+import com.tashow.cloud.user.model.User;
+
+import java.util.List;
+
+/**
+ * 用户服务接口
+ */
+public interface UserService {
+
+ /**
+ * 创建用户
+ *
+ * @param reqDTO 用户创建信息
+ * @return 用户ID
+ */
+ Long createUser(UserCreateReqDTO reqDTO);
+
+ /**
+ * 更新用户
+ *
+ * @param reqDTO 用户更新信息
+ */
+ void updateUser(UserUpdateReqDTO reqDTO);
+
+ /**
+ * 删除用户
+ *
+ * @param id 用户ID
+ */
+ void deleteUser(Long id);
+
+ /**
+ * 获取用户详情
+ *
+ * @param id 用户ID
+ * @return 用户详情
+ */
+ UserRespDTO getUser(Long id);
+
+ /**
+ * 获取用户详情
+ *
+ * @param username 用户名
+ * @return 用户详情
+ */
+ UserRespDTO getUserByUsername(String username);
+
+ /**
+ * 获取用户分页列表
+ *
+ * @param reqDTO 查询条件
+ * @return 用户分页列表
+ */
+ List getUserPage(UserPageReqDTO reqDTO);
+
+ /**
+ * 重置用户密码
+ *
+ * @param id 用户ID
+ * @param password 新密码
+ */
+ void resetUserPassword(Long id, String password);
+
+ /**
+ * 更新用户状态
+ *
+ * @param id 用户ID
+ * @param status 新状态
+ */
+ void updateUserStatus(Long id, Integer status);
+
+ /**
+ * 获取用户的角色ID列表
+ *
+ * @param userId 用户ID
+ * @return 角色ID列表
+ */
+ List getUserRoleIds(Long userId);
+
+ /**
+ * 获取用户的角色名称列表
+ *
+ * @param userId 用户ID
+ * @return 角色名称列表
+ */
+ List getUserRoleNames(Long userId);
+
+ /**
+ * 获取用户的角色编码列表
+ *
+ * @param userId 用户ID
+ * @return 角色编码列表
+ */
+ List getUserRoleCodes(Long userId);
+
+ /**
+ * 注册用户
+ *
+ * @param reqDTO 注册信息
+ * @return 用户ID
+ */
+ Long registerUser(UserRegisterReqDTO reqDTO);
+
+ /**
+ * 获取当前登录用户信息
+ *
+ * @return 用户详情
+ */
+ UserRespDTO getCurrentUser();
+
+ /**
+ * 验证用户密码
+ *
+ * @param rawPassword 原始密码
+ * @param encodedPassword 编码后的密码
+ * @return 是否匹配
+ */
+ boolean validatePassword(String rawPassword, String encodedPassword);
+
+ /**
+ * 加密密码
+ *
+ * @param rawPassword 原始密码
+ * @return 加密后的密码
+ */
+ String encodePassword(String rawPassword);
+
+ /**
+ * 更新用户登录信息
+ *
+ * @param userId 用户ID
+ * @param ip 登录IP
+ */
+ void updateLoginInfo(Long userId, String ip);
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-biz/Dockerfile b/tashow-module/tashow-module-user/tashow-module-user-biz/Dockerfile
new file mode 100644
index 0000000..7c18a64
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-biz/Dockerfile
@@ -0,0 +1,19 @@
+## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性
+## 感谢复旦核博士的建议!灰子哥,牛皮!
+FROM eclipse-temurin:21-jre
+
+## 创建目录,并使用它作为工作目录
+RUN mkdir -p /yudao-module-user-biz
+WORKDIR /yudao-module-user-biz
+## 将后端项目的 Jar 文件,复制到镜像中
+COPY ./target/yudao-module-user-biz.jar app.jar
+
+## 设置 TZ 时区
+## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖
+ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx512m"
+
+## 暴露后端项目的 48080 端口
+EXPOSE 48081
+
+## 启动后端项目
+CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar
diff --git a/tashow-module/tashow-module-user/tashow-module-user-biz/HELP.md b/tashow-module/tashow-module-user/tashow-module-user-biz/HELP.md
new file mode 100644
index 0000000..b7509c2
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-biz/HELP.md
@@ -0,0 +1,10 @@
+# Getting Started
+
+### Reference Documentation
+
+For further reference, please consider the following sections:
+
+* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html)
+* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.6.13/maven-plugin/reference/html/)
+* [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.6.13/maven-plugin/reference/html/#build-image)
+
diff --git a/tashow-module/tashow-module-user/tashow-module-user-biz/pom.xml b/tashow-module/tashow-module-user/tashow-module-user-biz/pom.xml
new file mode 100644
index 0000000..f10391a
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-biz/pom.xml
@@ -0,0 +1,212 @@
+
+
+
+ com.tashow.cloud
+ tashow-module-user
+ ${revision}
+
+ 4.0.0
+ tashow-module-user-biz
+ jar
+
+ ${project.artifactId}
+
+ system 模块下,我们放通用业务,支撑上层的核心业务。
+ 例如说:用户、部门、权限、数据字典等等
+
+
+
+
+
+ com.tashow.cloud
+ tashow-framework-env
+
+
+
+
+
+
+
+ com.tashow.cloud
+ tashow-data-permission
+
+
+ com.tashow.cloud
+ tashow-framework-tenant
+
+
+
+
+ com.tashow.cloud
+ tashow-framework-security
+
+
+
+
+ com.tashow.cloud
+ tashow-data-mybatis
+
+
+
+ com.tashow.cloud
+ tashow-data-redis
+
+
+
+
+ com.tashow.cloud
+ tashow-framework-rpc
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-config
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-mail
+
+
+
+
+ com.tashow.cloud
+ tashow-framework-monitor
+
+
+
+
+ com.xingyuv
+ spring-boot-starter-justauth
+
+
+
+ com.xingyuv
+ spring-boot-starter-captcha-plus
+
+
+
+ org.dromara.hutool
+ hutool-extra
+
+
+
+
+
+ cn.dev33
+ sa-token-reactor-spring-boot3-starter
+ 1.42.0
+
+
+
+ cn.dev33
+ sa-token-spring-boot3-starter
+ 1.42.0
+
+
+
+
+ cn.dev33
+ sa-token-sso
+ 1.42.0
+
+
+
+
+ cn.dev33
+ sa-token-oauth2
+ 1.42.0
+
+
+
+
+ cn.dev33
+ sa-token-redis-jackson
+ 1.42.0
+
+
+
+ com.dtflys.forest
+ forest-spring-boot-starter
+ 1.5.26
+
+
+
+ org.apache.commons
+ commons-pool2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+ com.tashow.cloud
+ tashow-module-user-api
+
+
+
+
+
+ ${project.artifactId}
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.boot.version}
+
+
+
+ repackage
+
+
+
+
+
+
+
+
diff --git a/tashow-module/tashow-module-user/tashow-module-user-biz/src/main/java/com/tashow/cloud/tashowmoduleuserbiz/TashowModuleUserBizApplication.java b/tashow-module/tashow-module-user/tashow-module-user-biz/src/main/java/com/tashow/cloud/tashowmoduleuserbiz/TashowModuleUserBizApplication.java
new file mode 100644
index 0000000..e809bc6
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-biz/src/main/java/com/tashow/cloud/tashowmoduleuserbiz/TashowModuleUserBizApplication.java
@@ -0,0 +1,18 @@
+package com.tashow.cloud.tashowmoduleuserbiz;
+
+import cn.dev33.satoken.sso.SaSsoManager;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class TashowModuleUserBizApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(TashowModuleUserBizApplication.class, args);
+ System.out.println();
+ System.out.println("---------------------- Sa-Token SSO 统一认证中心启动成功 ----------------------");
+ System.out.println("配置信息:" + SaSsoManager.getServerConfig());
+ System.out.println();
+ }
+
+}
diff --git a/tashow-module/tashow-module-user/tashow-module-user-biz/src/main/java/com/tashow/cloud/tashowmoduleuserbiz/config/MybatisPlusConfig.java b/tashow-module/tashow-module-user/tashow-module-user-biz/src/main/java/com/tashow/cloud/tashowmoduleuserbiz/config/MybatisPlusConfig.java
new file mode 100644
index 0000000..6a6598b
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-biz/src/main/java/com/tashow/cloud/tashowmoduleuserbiz/config/MybatisPlusConfig.java
@@ -0,0 +1,27 @@
+package com.tashow.cloud.tashowmoduleuserbiz.config;
+
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * MyBatis Plus配置类
+ */
+@Configuration
+@MapperScan("com.tashow.cloud.tashowmoduleuserbiz.mapper")
+public class MybatisPlusConfig {
+
+ /**
+ * 配置分页插件
+ */
+ @Bean
+ public MybatisPlusInterceptor mybatisPlusInterceptor() {
+ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+ // 添加分页插件
+ interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
+ return interceptor;
+ }
+}
\ No newline at end of file
diff --git a/tashow-module/tashow-module-user/tashow-module-user-biz/src/main/java/com/tashow/cloud/tashowmoduleuserbiz/controller/RoleController.java b/tashow-module/tashow-module-user/tashow-module-user-biz/src/main/java/com/tashow/cloud/tashowmoduleuserbiz/controller/RoleController.java
new file mode 100644
index 0000000..4e24d9b
--- /dev/null
+++ b/tashow-module/tashow-module-user/tashow-module-user-biz/src/main/java/com/tashow/cloud/tashowmoduleuserbiz/controller/RoleController.java
@@ -0,0 +1,203 @@
+package com.tashow.cloud.tashowmoduleuserbiz.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.dev33.satoken.annotation.SaIgnore;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.dev33.satoken.util.SaResult;
+import com.tashow.cloud.model.SystemRole;
+import com.tashow.cloud.service.SystemRoleService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.stream.Collectors;
+
+/**
+ * 角色管理控制器
+ */
+@RestController
+@RequestMapping("/role")
+public class RoleController {
+
+ @Autowired
+ private SystemRoleService roleService;
+
+ /**
+ * 创建角色
+ */
+ @PostMapping
+ @SaCheckPermission("system:role:create")
+ public SaResult createRole(@RequestBody Map params) {
+ try {
+ SystemRole role = new SystemRole();
+ role.setName((String) params.get("name"));
+ role.setCode((String) params.get("code"));
+ role.setSort((Integer) params.get("sort"));
+ role.setStatus((Integer) params.get("status"));
+ role.setType((Integer) params.get("type"));
+ role.setRemark((String) params.get("remark"));
+ role.setDataScope((Integer) params.get("dataScope"));
+ role.setDataScopeDeptIds((String) params.get("dataScopeDeptIds"));
+
+ // 设置创建者
+ String username = StpUtil.getLoginIdAsString();
+ role.setCreator(username);
+ role.setUpdater(username);
+
+ @SuppressWarnings("unchecked")
+ List menuIds = (List) params.get("menuIds");
+
+ boolean success = roleService.createRole(role, menuIds);
+
+ if (success) {
+ return SaResult.ok("角色创建成功").setData(role);
+ } else {
+ return SaResult.error("角色创建失败");
+ }
+ } catch (Exception e) {
+ return SaResult.error("角色创建异常: " + e.getMessage());
+ }
+ }
+
+ /**
+ * 更新角色
+ */
+ @PutMapping("/{id}")
+ @SaCheckPermission("system:role:update")
+ public SaResult updateRole(@PathVariable("id") Long id, @RequestBody Map params) {
+ try {
+ SystemRole role = roleService.getRoleById(id);
+ if (role == null) {
+ return SaResult.error("角色不存在");
+ }
+
+ role.setName((String) params.get("name"));
+ role.setCode((String) params.get("code"));
+ role.setSort((Integer) params.get("sort"));
+ role.setStatus((Integer) params.get("status"));
+ role.setType((Integer) params.get("type"));
+ role.setRemark((String) params.get("remark"));
+ role.setDataScope((Integer) params.get("dataScope"));
+ role.setDataScopeDeptIds((String) params.get("dataScopeDeptIds"));
+
+ // 设置更新者
+ String username = StpUtil.getLoginIdAsString();
+ role.setUpdater(username);
+
+ @SuppressWarnings("unchecked")
+ List menuIds = (List) params.get("menuIds");
+
+ boolean success = roleService.updateRole(role, menuIds);
+
+ if (success) {
+ return SaResult.ok("角色更新成功");
+ } else {
+ return SaResult.error("角色更新失败");
+ }
+ } catch (Exception e) {
+ return SaResult.error("角色更新异常: " + e.getMessage());
+ }
+ }
+
+ /**
+ * 删除角色
+ */
+ @DeleteMapping("/{id}")
+ @SaCheckPermission("system:role:delete")
+ public SaResult deleteRole(@PathVariable("id") Long id) {
+ try {
+ boolean success = roleService.deleteRole(id);
+
+ if (success) {
+ return SaResult.ok("角色删除成功");
+ } else {
+ return SaResult.error("角色删除失败");
+ }
+ } catch (Exception e) {
+ return SaResult.error("角色删除异常: " + e.getMessage());
+ }
+ }
+
+ /**
+ * 获取角色详情
+ */
+ @GetMapping("/{id}")
+ @SaCheckPermission("system:role:query")
+ public SaResult getRoleDetail(@PathVariable("id") Long id) {
+ try {
+ SystemRole role = roleService.getRoleById(id);
+ if (role == null) {
+ return SaResult.error("角色不存在");
+ }
+
+ Map result = new HashMap<>();
+ result.put("role", role);
+
+ // 获取角色关联的菜单ID列表
+ List menuIds = roleService.getRoleMenuIds(id);
+ result.put("menuIds", menuIds);
+
+ return SaResult.ok().setData(result);
+ } catch (Exception e) {
+ return SaResult.error("获取角色详情异常: " + e.getMessage());
+ }
+ }
+
+ /**
+ * 获取角色列表
+ */
+ @GetMapping("/list")
+ @SaCheckPermission("system:role:query")
+ public SaResult getRoleList() {
+ try {
+ List roles = roleService.getAllRoles();
+ return SaResult.ok().setData(roles);
+ } catch (Exception e) {
+ return SaResult.error("获取角色列表异常: " + e.getMessage());
+ }
+ }
+
+ /**
+ * 获取启用状态的角色列表(可用于下拉选择)
+ */
+ @GetMapping("/options")
+ @SaIgnore
+ public SaResult getRoleOptions() {
+ try {
+ List roles = roleService.getRolesByStatus(0); // 0表示正常状态
+ List