├── system ├── service │ ├── src │ │ ├── main │ │ │ ├── resources │ │ │ │ ├── META-INF │ │ │ │ │ └── spring │ │ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ │ ├── bootstrap.yaml │ │ │ │ └── application.yaml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── xh │ │ │ │ └── system │ │ │ │ ├── SystemApplication.java │ │ │ │ ├── controller │ │ │ │ ├── SysLogController.java │ │ │ │ ├── SysOrgController.java │ │ │ │ ├── SysMenuController.java │ │ │ │ ├── SysDictController.java │ │ │ │ ├── SysDataPermissionController.java │ │ │ │ ├── SysFileController.java │ │ │ │ └── SysRoleController.java │ │ │ │ └── service │ │ │ │ ├── SysDataPermissionService.java │ │ │ │ ├── SysOrgService.java │ │ │ │ ├── SysMenuService.java │ │ │ │ ├── SysLogService.java │ │ │ │ └── SysDictService.java │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── xh │ │ │ └── system │ │ │ ├── SystemApplicationTests.java │ │ │ └── CommonServiceTest.java │ └── pom.xml ├── client │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── spring │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── java │ │ │ └── com │ │ │ └── xh │ │ │ └── system │ │ │ └── client │ │ │ ├── entity │ │ │ ├── SysDataEntity.java │ │ │ ├── SysDictType.java │ │ │ ├── SysRoleMenu.java │ │ │ ├── SysDataPermission.java │ │ │ ├── SysUserPasswordLog.java │ │ │ ├── SysRoleDataPermission.java │ │ │ ├── SysOrg.java │ │ │ ├── SysUserGroupMember.java │ │ │ ├── SysUserGroup.java │ │ │ ├── SysRole.java │ │ │ ├── SysDictDetail.java │ │ │ ├── SysUserJob.java │ │ │ ├── SysFile.java │ │ │ ├── SysUser.java │ │ │ └── SysMenu.java │ │ │ ├── dto │ │ │ ├── ImageCaptchaDTO.java │ │ │ ├── SysRolePermissionDTO.java │ │ │ ├── SysUserJobDTO.java │ │ │ └── DownloadFileDTO.java │ │ │ ├── vo │ │ │ └── LoginUserInfoVO.java │ │ │ ├── configuration │ │ │ └── SystemWebClientConfig.java │ │ │ └── exchange │ │ │ └── SysFileClient.java │ └── pom.xml └── pom.xml ├── common ├── common-core │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ ├── ip2region.xdb │ │ │ └── META-INF │ │ │ │ └── spring │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── java │ │ │ └── com │ │ │ └── xh │ │ │ └── common │ │ │ └── core │ │ │ ├── service │ │ │ ├── BaseService.java │ │ │ └── BaseServiceImpl.java │ │ │ ├── dao │ │ │ ├── PersistenceType.java │ │ │ ├── BaseJdbcDao.java │ │ │ └── sql │ │ │ │ ├── MysqlExecutor.java │ │ │ │ ├── SqlExecutor.java │ │ │ │ └── PostgreSqlExecutor.java │ │ │ ├── dto │ │ │ ├── DataPermissionsCheckDTO.java │ │ │ ├── BaseDataPermissionDTO.java │ │ │ ├── SysLoginUserInfoDTO.java │ │ │ ├── BaseDTO.java │ │ │ ├── RolePermissionsDTO.java │ │ │ ├── SysUserDTO.java │ │ │ ├── SysOrgRoleDTO.java │ │ │ ├── WxUserInfoDTO.java │ │ │ ├── SysMenuDTO.java │ │ │ └── OnlineUserDTO.java │ │ │ ├── web │ │ │ ├── MyException.java │ │ │ ├── NoDataPermissionException.java │ │ │ ├── MyContext.java │ │ │ ├── PageResult.java │ │ │ └── RestResponse.java │ │ │ ├── Constant.java │ │ │ ├── entity │ │ │ ├── AutoSet.java │ │ │ ├── BaseDataPermissionEntity.java │ │ │ ├── BaseEntity.java │ │ │ ├── SysLog.java │ │ │ └── AutoSetFun.java │ │ │ ├── Banner.java │ │ │ ├── utils │ │ │ └── LoginUtil.java │ │ │ └── configuration │ │ │ ├── SaTokenConfigure.java │ │ │ ├── StpInterfaceImpl.java │ │ │ ├── MyControllerAdvice.java │ │ │ ├── WebConfig.java │ │ │ ├── MyFilter.java │ │ │ └── MyInterceptor.java │ └── pom.xml ├── common-nacos │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── spring │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── java │ │ │ └── com │ │ │ └── xh │ │ │ └── common │ │ │ └── nacos │ │ │ └── configuration │ │ │ └── WebClientConfig.java │ └── pom.xml ├── common-xxljob │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ ├── META-INF │ │ │ │ └── spring │ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── template.yml │ │ │ └── java │ │ │ └── com │ │ │ └── xh │ │ │ └── common │ │ │ └── xxljob │ │ │ └── configuration │ │ │ └── XxlJobConfig.java │ └── pom.xml ├── common-swagger │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ ├── META-INF │ │ │ │ └── spring │ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── template.yml │ │ │ └── java │ │ │ └── com │ │ │ └── xh │ │ │ └── common │ │ │ └── swagger │ │ │ └── configuration │ │ │ └── SpringDocConfig.java │ └── pom.xml ├── common-jdbc │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ ├── META-INF │ │ │ │ └── spring │ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── template.yml │ │ │ └── java │ │ │ └── com │ │ │ └── xh │ │ │ └── common │ │ │ └── jdbc │ │ │ └── configuration │ │ │ └── DataSourceConfiguration.java │ └── pom.xml └── pom.xml ├── generator ├── service │ ├── src │ │ ├── main │ │ │ ├── resources │ │ │ │ ├── templates │ │ │ │ │ ├── sql │ │ │ │ │ │ ├── createSysDataEntity.sql.ftl │ │ │ │ │ │ ├── createTable.sql.ftl │ │ │ │ │ │ └── createSysMenu.sql.ftl │ │ │ │ │ ├── backend │ │ │ │ │ │ ├── DTO.java.ftl │ │ │ │ │ │ ├── Entity.java.ftl │ │ │ │ │ │ ├── Controller.java.ftl │ │ │ │ │ │ └── Service.java.ftl │ │ │ │ │ └── frontend │ │ │ │ │ │ ├── api.ts.ftl │ │ │ │ │ │ ├── import.vue.ftl │ │ │ │ │ │ ├── form.vue.ftl │ │ │ │ │ │ └── index.vue.ftl │ │ │ │ ├── bootstrap.yaml │ │ │ │ └── application.yaml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── xh │ │ │ │ └── generator │ │ │ │ ├── GeneratorApplication.java │ │ │ │ └── controller │ │ │ │ └── CodeGenController.java │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── xh │ │ │ └── generator │ │ │ ├── GeneratorApplicationTests.java │ │ │ └── service │ │ │ └── CodeGenServiceTest.java │ └── pom.xml ├── client │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── com │ │ │ └── xh │ │ │ └── generator │ │ │ └── client │ │ │ ├── vo │ │ │ ├── TableMateDataVO.java │ │ │ ├── GenCodeResult.java │ │ │ ├── TableColumnMateDataVO.java │ │ │ └── GenTableVO.java │ │ │ ├── entity │ │ │ └── GenTable.java │ │ │ └── dto │ │ │ └── GenTableColumnDTO.java │ └── pom.xml └── pom.xml ├── .gitignore └── README.md /system/service/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /common/common-core/src/main/resources/ip2region.xdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alixhan/xh-admin-backend/HEAD/common/common-core/src/main/resources/ip2region.xdb -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/service/BaseService.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.service; 2 | 3 | public interface BaseService { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /common/common-nacos/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | com.xh.common.nacos.configuration.WebClientConfig 2 | -------------------------------------------------------------------------------- /common/common-xxljob/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | com.xh.common.xxljob.configuration.XxlJobConfig 2 | -------------------------------------------------------------------------------- /common/common-swagger/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | com.xh.common.swagger.configuration.SpringDocConfig 2 | -------------------------------------------------------------------------------- /system/client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | com.xh.system.client.configuration.SystemWebClientConfig 2 | -------------------------------------------------------------------------------- /common/common-jdbc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | com.xh.common.jdbc.configuration.DataSourceConfiguration 2 | -------------------------------------------------------------------------------- /generator/service/src/main/resources/templates/sql/createSysDataEntity.sql.ftl: -------------------------------------------------------------------------------- 1 | -- 创建系统数据实体 2 | INSERT INTO `sys_data_entity` (`id`, `name`) VALUES ('${tableName}', '${name}'); 3 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/dao/PersistenceType.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.dao; 2 | 3 | /** 4 | * 持久化类型 5 | * sunxh 2024/5/6 6 | */ 7 | public enum PersistenceType { 8 | INSERT, DELETE, UPDATE, FIND_BY_ID 9 | } 10 | -------------------------------------------------------------------------------- /generator/service/src/main/resources/templates/sql/createTable.sql.ftl: -------------------------------------------------------------------------------- 1 | CREATE TABLE `${tableName}` 2 | ( 3 | <#list columns as field> 4 | <#if !(field.isVirtual!false)> 5 | ${field.sqlColStr}<#if field_has_next>, 6 | 7 | 8 | ) COMMENT = '${tableComment}'; 9 | -------------------------------------------------------------------------------- /common/common-swagger/src/main/resources/template.yml: -------------------------------------------------------------------------------- 1 | #swagger配置文件模板 2 | 3 | # 官方文档地址 https://springdoc.org 4 | springdoc: 5 | api-docs: 6 | enabled: true 7 | path: /v3/api-docs 8 | swagger-ui: 9 | enabled: true 10 | # swagger首页地址 11 | path: /swagger-ui.html 12 | -------------------------------------------------------------------------------- /generator/service/src/test/java/com/xh/generator/GeneratorApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.xh.generator; 2 | 3 | import org.springframework.boot.test.context.SpringBootTest; 4 | import org.springframework.test.annotation.Rollback; 5 | 6 | @SpringBootTest 7 | @Rollback 8 | class GeneratorApplicationTests { 9 | } 10 | -------------------------------------------------------------------------------- /system/service/src/test/java/com/xh/system/SystemApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.xh.system; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SystemApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/dto/DataPermissionsCheckDTO.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.dto; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * 数据权限验证结果DTO 9 | */ 10 | @Data 11 | public class DataPermissionsCheckDTO implements Serializable { 12 | Boolean result; 13 | Serializable id; 14 | } 15 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/web/MyException.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.web; 2 | 3 | /** 4 | * 2021-09-26 sunxh 5 | * 业务异常,用于service抛出业务错误信息 6 | */ 7 | public class MyException extends RuntimeException { 8 | 9 | public MyException(String message) { 10 | super(message); 11 | } 12 | 13 | public MyException(Throwable e) { 14 | super(e); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /system/service/src/main/java/com/xh/system/SystemApplication.java: -------------------------------------------------------------------------------- 1 | package com.xh.system; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SystemApplication { 8 | public static void main(String[] args) { 9 | SpringApplication.run(SystemApplication.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /generator/service/src/main/java/com/xh/generator/GeneratorApplication.java: -------------------------------------------------------------------------------- 1 | package com.xh.generator; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class GeneratorApplication { 8 | public static void main(String[] args) { 9 | SpringApplication.run(GeneratorApplication.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /common/common-jdbc/src/main/resources/template.yml: -------------------------------------------------------------------------------- 1 | #多数据源配置示例 2 | spring: 3 | datasource: 4 | first: 5 | url: jdbc:mysql://localhost:3306/first 6 | username: dbuser 7 | password: sd 8 | configuration: 9 | maximum-pool-size: 20 10 | second: 11 | url: jdbc:mysql://localhost:3306/second 12 | username: root 13 | password: 143234 14 | configuration: 15 | maximum-pool-size: 20 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/entity/SysDataEntity.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.entity; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import jakarta.persistence.Id; 5 | import jakarta.persistence.Table; 6 | import lombok.Data; 7 | 8 | 9 | @Schema(title = "系统数据实体") 10 | @Table 11 | @Data 12 | public class SysDataEntity { 13 | 14 | @Schema(title = "主键ID") 15 | @Id 16 | private String id; 17 | 18 | @Schema(title = "数据实体名称") 19 | private String name; 20 | } 21 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/dto/ImageCaptchaDTO.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.dto; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * @author sunxh 2023/9/12 10 | */ 11 | @Schema(title = "存放图形验证码数据") 12 | @Data 13 | public class ImageCaptchaDTO implements Serializable { 14 | 15 | @Schema(title = "key") 16 | private String captchaKey; 17 | @Schema(title = "base64图形数据") 18 | private String imageBase64; 19 | } 20 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/Constant.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core; 2 | 3 | /** 4 | * 常量 5 | * 6 | * @author sunxh 2023/2/26 7 | */ 8 | public class Constant { 9 | public static final String AUTO_FEIGN_KEY = "xxxxxxffffgfsdgfsdafsgguyyerg"; 10 | 11 | /** 12 | * 验证码 redis 前缀 13 | */ 14 | public static final String CAPTCHA_KEY_PREFIX = "captcha:"; 15 | 16 | /** 17 | * 角色权限集合 redis 前缀 18 | */ 19 | public static final String ROLE_PERMISSIONS_PREFIX = "role:permissions:"; 20 | } 21 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/dto/SysRolePermissionDTO.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.dto; 2 | 3 | import com.xh.system.client.entity.SysRoleDataPermission; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import lombok.Data; 6 | 7 | import java.util.List; 8 | 9 | 10 | @Schema(title = "系统数据权限") 11 | @Data 12 | public class SysRolePermissionDTO { 13 | @Schema(title = "权限ID") 14 | private Integer sysRoleId; 15 | 16 | @Schema(title = "角色数据权限") 17 | List permissions; 18 | } 19 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/entity/AutoSet.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.entity; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 用于注释实体类字段 10 | * 实现持久化时自动注入字段值的逻辑 11 | * sunxh 2024/5/7 12 | */ 13 | @Target({ElementType.FIELD}) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface AutoSet { 16 | 17 | /** 18 | * 自动注入值执行方法 19 | */ 20 | AutoSetFun[] value(); 21 | } 22 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/dto/BaseDataPermissionDTO.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.dto; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | 7 | /** 8 | * 数据权限基础DTO 9 | * @author sunxh 2024/11/16 10 | */ 11 | @Data 12 | @EqualsAndHashCode(callSuper=false) 13 | public class BaseDataPermissionDTO extends BaseDTO { 14 | 15 | @Schema(title = "机构ID") 16 | protected Integer sysOrgId; 17 | 18 | @Schema(title = "角色ID") 19 | protected Integer sysRoleId; 20 | } 21 | -------------------------------------------------------------------------------- /common/common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | com.xh.common.core.configuration.WebConfig 2 | com.xh.common.core.configuration.MyControllerAdvice 3 | com.xh.common.core.configuration.MyInterceptor 4 | com.xh.common.core.configuration.MyFilter 5 | com.xh.common.core.configuration.SaTokenConfigure 6 | com.xh.common.core.configuration.StpInterfaceImpl 7 | com.xh.common.core.dao.BaseJdbcDaoImpl 8 | com.xh.common.core.service.BaseServiceImpl 9 | com.xh.common.core.service.CommonService 10 | com.xh.common.core.Banner 11 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/entity/SysDictType.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.entity; 2 | 3 | import com.xh.common.core.entity.BaseEntity; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import jakarta.persistence.Table; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | 9 | 10 | @Table 11 | @Data 12 | @Schema(title = "数据字典类型") 13 | @EqualsAndHashCode(callSuper = true) 14 | public class SysDictType extends BaseEntity { 15 | @Schema(title = "字典类型名称") 16 | private String name; 17 | @Schema(title = "是否可修改") 18 | private Boolean modifiable; 19 | } 20 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/entity/SysRoleMenu.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.entity; 2 | 3 | import com.xh.common.core.entity.BaseEntity; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import jakarta.persistence.Table; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | 9 | 10 | @Schema(title = "系统角色权限表") 11 | @Table 12 | @Data 13 | @EqualsAndHashCode(callSuper = true) 14 | public class SysRoleMenu extends BaseEntity { 15 | @Schema(title = "角色id") 16 | private Integer sysRoleId; 17 | @Schema(title = "菜单id") 18 | private Integer sysMenuId; 19 | } 20 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/dto/SysLoginUserInfoDTO.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.dto; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | import java.util.List; 8 | 9 | /** 10 | * 用于承载用户登录信息 11 | * 12 | * @author sunxh 2023/3/1 13 | */ 14 | @Schema(description = "用于承载用户登录信息,存入session中") 15 | @Data 16 | public class SysLoginUserInfoDTO implements Serializable { 17 | 18 | @Schema(title = "当前登录的用户信息") 19 | private SysUserDTO user; 20 | @Schema(title = "当前用户拥有的角色") 21 | private List roles; 22 | } 23 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/entity/SysDataPermission.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.entity; 2 | 3 | import com.xh.common.core.entity.BaseEntity; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import jakarta.persistence.Table; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | 9 | 10 | @Schema(title = "系统数据权限") 11 | @Table 12 | @Data 13 | @EqualsAndHashCode(callSuper = true) 14 | public class SysDataPermission extends BaseEntity { 15 | @Schema(title = "数据权限名称") 16 | private String name; 17 | 18 | @Schema(title = "权限表达式") 19 | private String expression; 20 | 21 | @Schema(title = "权限json数据") 22 | private String json; 23 | } 24 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/entity/SysUserPasswordLog.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.entity; 2 | 3 | import com.xh.common.core.entity.BaseEntity; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import jakarta.persistence.Table; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | 9 | 10 | @Schema(title = "系统用户密码修改日志") 11 | @Table 12 | @Data 13 | @EqualsAndHashCode(callSuper = true) 14 | public class SysUserPasswordLog extends BaseEntity { 15 | @Schema(title = "系统用户id") 16 | private Integer sysUserId; 17 | @Schema(title = "原密码") 18 | private String oldPassword; 19 | @Schema(title = "新密码") 20 | private String newPassword; 21 | } 22 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/dto/BaseDTO.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.dto; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | import java.time.LocalDateTime; 8 | 9 | /** 10 | * 基础DTO 11 | * @author sunxh 2023/3/1 12 | */ 13 | @Data 14 | public class BaseDTO implements Serializable { 15 | @Schema(title = "主键ID") 16 | protected I id; 17 | @Schema(title = "创建时间") 18 | private LocalDateTime createTime; 19 | @Schema(title = "修改时间") 20 | private LocalDateTime updateTime; 21 | @Schema(title = "创建人") 22 | private Integer createBy; 23 | @Schema(title = "修改人") 24 | private Integer updateBy; 25 | } 26 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/entity/BaseDataPermissionEntity.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.entity; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * 数据权限基础实体 11 | * 会自动写入当前机构ID和当前角色ID 12 | * sunxh 2024/8/24 13 | */ 14 | @Getter 15 | @Setter 16 | public class BaseDataPermissionEntity extends BaseEntity implements Serializable { 17 | 18 | @Schema(title = "机构ID") 19 | @AutoSet(AutoSetFun.CURRENT_ORG) 20 | protected Integer sysOrgId; 21 | 22 | @Schema(title = "角色ID") 23 | @AutoSet(AutoSetFun.CURRENT_ROLE) 24 | protected Integer sysRoleId; 25 | } 26 | -------------------------------------------------------------------------------- /system/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | pom 6 | 7 | com.xh 8 | xh-admin-backend 9 | 0.0.1-SNAPSHOT 10 | 11 | 12 | system 13 | 系统模块 14 | 15 | 16 | client 17 | service 18 | 19 | 20 | -------------------------------------------------------------------------------- /generator/client/src/main/java/com/xh/generator/client/vo/TableMateDataVO.java: -------------------------------------------------------------------------------- 1 | package com.xh.generator.client.vo; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 数据库表元数据 VO 10 | * sunxh 2024/11/22 11 | */ 12 | @Data 13 | @Schema(title = "数据库表元数据") 14 | public class TableMateDataVO { 15 | 16 | @Schema(title = "类型") 17 | private String tableType; 18 | 19 | @Schema(title = "数据库名") 20 | private String tableCat; 21 | 22 | @Schema(title = "表注释") 23 | private String remarks; 24 | 25 | @Schema(title = "表名") 26 | private String tableName; 27 | 28 | @Schema(title = "数据库表列明细") 29 | List columns; 30 | } 31 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/entity/SysRoleDataPermission.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.entity; 2 | 3 | import com.xh.common.core.entity.BaseEntity; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import jakarta.persistence.Table; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | 9 | 10 | @Schema(title = "系统角色数据权限") 11 | @Table 12 | @Data 13 | @EqualsAndHashCode(callSuper = true) 14 | public class SysRoleDataPermission extends BaseEntity { 15 | 16 | @Schema(title = "角色ID") 17 | private Integer sysRoleId; 18 | 19 | @Schema(title = "数据实体ID") 20 | private String sysDataEntityId; 21 | 22 | @Schema(title = "数据权限ID") 23 | private Integer sysDataPermissionId; 24 | } 25 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/dto/RolePermissionsDTO.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.dto; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | import java.time.LocalDateTime; 8 | import java.util.List; 9 | 10 | /** 11 | * 角色权限集合持久化对象 12 | * 13 | * @author sunxh 2023/3/1 14 | */ 15 | @Data 16 | public class RolePermissionsDTO implements Serializable { 17 | @Schema(title = "角色ID") 18 | protected Integer roleId; 19 | @Schema(title = "创建时间") 20 | private LocalDateTime createTime; 21 | @Schema(title = "修改时间") 22 | private LocalDateTime updateTime; 23 | @Schema(title = "角色拥有的权限") 24 | private List permissions; 25 | } 26 | -------------------------------------------------------------------------------- /generator/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | pom 6 | 7 | com.xh 8 | xh-admin-backend 9 | 0.0.1-SNAPSHOT 10 | 11 | 12 | generator 13 | 代码生成模块 14 | 15 | 16 | client 17 | service 18 | 19 | 20 | -------------------------------------------------------------------------------- /system/client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.xh 7 | system 8 | 0.0.1-SNAPSHOT 9 | 10 | 11 | system-client 12 | 13 | 14 | 15 | com.xh 16 | common-core 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /generator/client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.xh 7 | generator 8 | 0.0.1-SNAPSHOT 9 | 10 | 11 | generator-client 12 | 13 | 14 | 15 | com.xh 16 | common-core 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/dto/SysUserJobDTO.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.dto; 2 | 3 | import com.xh.system.client.entity.SysUserJob; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import lombok.Data; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * 存放用户岗位信息DTO 11 | * sunxh 2023/8/10 12 | */ 13 | 14 | @Schema(title = "用户或用户组岗位信息") 15 | @Data 16 | public class SysUserJobDTO { 17 | @Schema(title = "用户id或者用户组的id", requiredMode = Schema.RequiredMode.REQUIRED) 18 | private Integer userId; 19 | @Schema(title = "数据类型", allowableValues = {"1", "2"}, description = "1:用户,2:用户组", requiredMode = Schema.RequiredMode.REQUIRED) 20 | private Integer type; 21 | @Schema(title = "岗位信息") 22 | private List jobData; 23 | } 24 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/web/NoDataPermissionException.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.web; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | /** 7 | * 无数据权限异常,当访问越权数据时抛出 8 | * 9 | * @author sxh 10 | * @since 2025-09-10 11 | */ 12 | @Getter 13 | @Setter 14 | public class NoDataPermissionException extends RuntimeException { 15 | //数据实体名称 16 | String dataEntity; 17 | 18 | //验证sql 19 | String verificationSql; 20 | 21 | //sql参数 22 | Object sqlParam; 23 | 24 | public NoDataPermissionException(String dataEntity, String verificationSql, Object sqlParam) { 25 | super("数据权限验证失败,数据实体:%s \n验证sql: %s\nsql参数:%s".formatted(dataEntity, verificationSql, sqlParam)); 26 | this.dataEntity = dataEntity; 27 | this.verificationSql = verificationSql; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /generator/service/src/main/resources/templates/backend/DTO.java.ftl: -------------------------------------------------------------------------------- 1 | package ${dtoPackage}; 2 | <#if extend??> 3 | import com.xh.common.core.dto.${extend}DTO; 4 | 5 | import io.swagger.v3.oas.annotations.media.Schema; 6 | import lombok.Data; 7 | 8 | <#if hasBigDecimal> 9 | import java.math.BigDecimal; 10 | 11 | <#if hasLocalDate> 12 | import java.time.LocalDate; 13 | 14 | <#if hasLocalDateTime> 15 | import java.time.LocalDateTime; 16 | 17 | 18 | /** 19 | * ${name} DTO 20 | * 21 | * @author ${author} 22 | * @since ${date} 23 | */ 24 | @Schema(title = "${name}") 25 | @Data 26 | public class ${dtoName}${dtoExtendClassStr!''} { 27 | <#list columns as field> 28 | <#if !(field.isExtend!false)> 29 | 30 | @Schema(title ="${field.columnName}") 31 | private ${field.javaType} ${field.prop}; 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /common/common-xxljob/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.xh 7 | common 8 | 0.0.1-SNAPSHOT 9 | 10 | 11 | common-xxljob 12 | xxl-job自动程序 13 | 14 | 15 | 16 | 17 | com.xuxueli 18 | xxl-job-core 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/entity/SysOrg.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.entity; 2 | 3 | import com.xh.common.core.entity.BaseEntity; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import jakarta.persistence.Table; 6 | import jakarta.persistence.Transient; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | 10 | 11 | @Schema(title = "系统机构") 12 | @Table 13 | @Data 14 | @EqualsAndHashCode(callSuper = true) 15 | public class SysOrg extends BaseEntity { 16 | 17 | @Schema(title = "机构代码") 18 | private String code; 19 | @Schema(title = "机构名称") 20 | private String name; 21 | @Schema(title = "上级机构") 22 | private String parentId; 23 | @Schema(title = "启用状态") 24 | private Boolean enabled; 25 | @Schema(title = "上级机构名称") 26 | @Transient 27 | private String parentName; 28 | } 29 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/entity/SysUserGroupMember.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.entity; 2 | 3 | import com.xh.common.core.entity.BaseEntity; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import jakarta.persistence.Table; 6 | import jakarta.persistence.Transient; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | 10 | 11 | @Schema(title = "系统用户组成员") 12 | @Table 13 | @Data 14 | @EqualsAndHashCode(callSuper = true) 15 | public class SysUserGroupMember extends BaseEntity { 16 | 17 | @Schema(title = "系统用户组id") 18 | private Integer sysUserGroupId; 19 | @Schema(title = "系统用户id") 20 | private Integer sysUserId; 21 | 22 | @Schema(title = "用户账号") 23 | @Transient 24 | private String userCode; 25 | @Schema(title = "用户名称") 26 | @Transient 27 | private String userName; 28 | } 29 | -------------------------------------------------------------------------------- /system/service/src/main/resources/bootstrap.yaml: -------------------------------------------------------------------------------- 1 | nacos-server-addr: ${NACOS_SERVER_ADDR:122.51.118.42:8848} 2 | nacos-namespace: ${NACOS_NAMESPACE:dev} 3 | nacos-group: ${NACOS_GROUP:xh-admin} 4 | nacos-username: ${NACOS_USERNAME:nacos} 5 | nacos-password: ${NACOS_PASSWROD:nacos} 6 | 7 | server: 8 | port: ${SERVER_PORT:6002} 9 | 10 | spring: 11 | application: 12 | name: system 13 | cloud: 14 | nacos: 15 | server-addr: ${nacos-server-addr} 16 | username: ${nacos-username} 17 | password: ${nacos-password} 18 | discovery: 19 | namespace: ${nacos-namespace} 20 | group: ${nacos-group} 21 | config: 22 | namespace: ${nacos-namespace} 23 | group: ${nacos-group} 24 | config: 25 | import: 26 | - nacos:common.yaml?refreshEnabled=true 27 | - nacos:${spring.application.name}.yaml?refreshEnabled=true 28 | -------------------------------------------------------------------------------- /common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | pom 6 | 7 | com.xh 8 | xh-admin-backend 9 | 0.0.1-SNAPSHOT 10 | 11 | 12 | common 13 | 通用模块 14 | 15 | 16 | common-core 17 | common-jdbc 18 | common-swagger 19 | common-xxljob 20 | common-nacos 21 | 22 | 23 | -------------------------------------------------------------------------------- /common/common-swagger/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.xh 7 | common 8 | 0.0.1-SNAPSHOT 9 | 10 | 11 | common-swagger 12 | swagger-api文档 13 | 14 | 15 | 16 | 17 | org.springdoc 18 | springdoc-openapi-starter-webmvc-ui 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /generator/service/src/main/resources/bootstrap.yaml: -------------------------------------------------------------------------------- 1 | nacos-server-addr: ${NACOS_SERVER_ADDR:122.51.118.42:8848} 2 | nacos-namespace: ${NACOS_NAMESPACE:dev} 3 | nacos-group: ${NACOS_GROUP:xh-admin} 4 | nacos-username: ${NACOS_USERNAME:nacos} 5 | nacos-password: ${NACOS_PASSWROD:nacos} 6 | 7 | server: 8 | port: ${SERVER_PORT:6003} 9 | 10 | spring: 11 | application: 12 | name: generator 13 | cloud: 14 | nacos: 15 | server-addr: ${nacos-server-addr} 16 | username: ${nacos-username} 17 | password: ${nacos-password} 18 | discovery: 19 | namespace: ${nacos-namespace} 20 | group: ${nacos-group} 21 | config: 22 | namespace: ${nacos-namespace} 23 | group: ${nacos-group} 24 | config: 25 | import: 26 | - nacos:common.yaml?refreshEnabled=true 27 | - nacos:${spring.application.name}.yaml?refreshEnabled=true 28 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/web/MyContext.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.web; 2 | 3 | import com.xh.common.core.entity.SysLog; 4 | 5 | /** 6 | * 线程变量:记录请求参数,响应参数等日志信息 7 | * 最后保存日志 8 | * 9 | * @author sunxh 2024/5/8 10 | */ 11 | public class MyContext { 12 | public static final ThreadLocal sysLogContext = new ThreadLocal<>(); 13 | 14 | public static SysLog getSysLog() { 15 | return getSysLog(false); 16 | } 17 | 18 | public static SysLog getSysLog(Boolean newer) { 19 | SysLog sysLog = sysLogContext.get(); 20 | if (Boolean.TRUE.equals(newer) || sysLog == null) { 21 | sysLog = new SysLog(); 22 | sysLogContext.set(sysLog); 23 | } 24 | return sysLog; 25 | } 26 | 27 | public static void setSysLog(SysLog sysLog) { 28 | sysLogContext.set(sysLog); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /generator/service/src/test/java/com/xh/generator/service/CodeGenServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.xh.generator.service; 2 | 3 | import jakarta.annotation.Resource; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.annotation.Rollback; 7 | 8 | import java.sql.SQLException; 9 | import java.util.HashMap; 10 | 11 | /** 12 | * @author sunxh 2024/9/10 13 | */ 14 | @SpringBootTest 15 | @Rollback 16 | class CodeGenServiceTest { 17 | @Resource 18 | CodeGenService codeGenService; 19 | 20 | @Test 21 | void getTableDetail() throws SQLException { 22 | codeGenService.getTableDetail(new HashMap<>(){{ 23 | put("tableName", "sys_user"); 24 | }}); 25 | } 26 | 27 | @Test 28 | void testGetTableList() { 29 | codeGenService.getTableList(null); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/entity/SysUserGroup.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.entity; 2 | 3 | import com.xh.common.core.entity.BaseEntity; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import jakarta.persistence.Table; 6 | import jakarta.persistence.Transient; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | 10 | import java.util.List; 11 | 12 | 13 | @Schema(title = "系统用户组") 14 | @Table 15 | @Data 16 | @EqualsAndHashCode(callSuper = true) 17 | public class SysUserGroup extends BaseEntity { 18 | 19 | @Schema(title = "用户组名") 20 | private String name; 21 | @Schema(title = "是否启用") 22 | private Boolean enabled; 23 | 24 | @Schema(title = "岗位信息") 25 | @Transient 26 | private List jobData; 27 | @Schema(title = "用户组成员") 28 | @Transient 29 | private List memberData; 30 | } 31 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/vo/LoginUserInfoVO.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.vo; 2 | 3 | import com.xh.common.core.dto.SysMenuDTO; 4 | import com.xh.common.core.dto.SysOrgRoleDTO; 5 | import com.xh.common.core.dto.SysUserDTO; 6 | import io.swagger.v3.oas.annotations.media.Schema; 7 | import lombok.Data; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * 用于承载用户登录信息 13 | * 14 | * @author sunxh 2023/3/1 15 | */ 16 | @Schema(description = "用于承载用户登录信息") 17 | @Data 18 | public class LoginUserInfoVO { 19 | 20 | @Schema(title = "tokenName") 21 | private String tokenName; 22 | @Schema(title = "tokenValue") 23 | private String tokenValue; 24 | @Schema(title = "当前登录的用户信息") 25 | private SysUserDTO user; 26 | @Schema(title = "当前用户角色拥有的菜单权限") 27 | private List menus; 28 | @Schema(title = "当前用户角色拥有的角色") 29 | private List roles; 30 | } 31 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/service/BaseServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.service; 2 | 3 | import com.xh.common.core.dao.BaseJdbcDao; 4 | import jakarta.annotation.Resource; 5 | import org.springframework.data.redis.core.RedisTemplate; 6 | import org.springframework.jdbc.core.JdbcTemplate; 7 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; 8 | import org.springframework.stereotype.Repository; 9 | 10 | /** 11 | * 基础service实现类 12 | * sunxh 2023/4/16 13 | */ 14 | @Repository(value = "baseService") 15 | public class BaseServiceImpl implements BaseService { 16 | @Resource 17 | protected JdbcTemplate primaryJdbcTemplate; 18 | @Resource 19 | protected NamedParameterJdbcTemplate primaryNPJdbcTemplate; 20 | @Resource 21 | protected BaseJdbcDao baseJdbcDao; 22 | @Resource 23 | protected RedisTemplate redisTemplate; 24 | } 25 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/entity/SysRole.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.entity; 2 | 3 | import com.xh.common.core.entity.BaseEntity; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import jakarta.persistence.Table; 6 | import jakarta.persistence.Transient; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | 10 | import java.util.List; 11 | 12 | 13 | @Schema(title = "系统角色") 14 | @Table 15 | @Data 16 | @EqualsAndHashCode(callSuper = true) 17 | public class SysRole extends BaseEntity { 18 | 19 | @Schema(title = "角色名称") 20 | private String name; 21 | @Schema(title = "上级角色") 22 | private Integer parentId; 23 | @Schema(title = "启用状态") 24 | private Boolean enabled; 25 | @Transient 26 | @Schema(title = "上级角色名称") 27 | private String parentName; 28 | @Transient 29 | @Schema(title = "角色具有的权限") 30 | List roleMenus; 31 | } 32 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/Banner.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core; 2 | 3 | import org.springframework.boot.context.event.ApplicationReadyEvent; 4 | import org.springframework.context.event.EventListener; 5 | 6 | /** 7 | * @author sunxh 8 | * @since 2025/5/28 9 | */ 10 | public class Banner { 11 | 12 | @EventListener(ApplicationReadyEvent.class) 13 | public void init() { 14 | // 在这里调用你的方法 15 | System.out.println(""" 16 | __ ___ _ _ _ _ 17 | \\ \\/ / | | | __ _ _ __ / \\ __| |_ __ ___ (_)_ __ 18 | \\ /| |_| |/ _` | '_ \\ / _ \\ / _` | '_ ` _ \\| | '_ \\ 19 | / \\| _ | (_| | | | | / ___ \\ (_| | | | | | | | | | | 20 | /_/\\_\\_| |_|\\__,_|_| |_| /_/ \\_\\__,_|_| |_| |_|_|_| |_| 21 | https://www.xhansky.cn 22 | """); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/dto/SysUserDTO.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.dto; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | 7 | /** 8 | * 存放系统登录的用户信息DTO 9 | * 10 | * @author sunxh 2023/3/1 11 | */ 12 | @EqualsAndHashCode(callSuper = true) 13 | @Data 14 | public class SysUserDTO extends BaseDTO { 15 | 16 | @Schema(title = "用户代码") 17 | private String code; 18 | @Schema(title = "用户名") 19 | private String name; 20 | @Schema(title = "头像") 21 | private String avatar; 22 | @Schema(title = "手机号码") 23 | private String telephone; 24 | @Schema(title = "允许重复登录") 25 | private Boolean allowRepeat; 26 | @Schema(title = "自动续签,请求会自动延长token失效时间") 27 | private Boolean autoRenewal; 28 | @Schema(title = "是否启用") 29 | private Boolean enabled; 30 | @Schema(title = "是否演示账号") 31 | private Boolean isDemo; 32 | } 33 | -------------------------------------------------------------------------------- /generator/client/src/main/java/com/xh/generator/client/vo/GenCodeResult.java: -------------------------------------------------------------------------------- 1 | package com.xh.generator.client.vo; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | 7 | /** 8 | * 代码生成结果 9 | * sunxh 2024/11/16 10 | */ 11 | @Data 12 | @Schema(title = "代码生成结果表") 13 | public class GenCodeResult { 14 | 15 | @Schema(title = "源码类型") 16 | private String type; 17 | 18 | @Schema(title = "源码") 19 | private String code; 20 | 21 | @Schema(title = "模板路径") 22 | private String templatePath; 23 | 24 | @Schema(title = "文件名") 25 | private String fileName; 26 | 27 | @Schema(title = "zip路径") 28 | private String zipPath; 29 | 30 | @Schema(title = "绝对文件夹路径") 31 | private String absoluteDirPath; 32 | 33 | @Schema(title = "绝对路径") 34 | private String absolutePath; 35 | 36 | @Schema(title = "错误信息") 37 | private String errorMsg; 38 | 39 | @Schema(title = "是否执行") 40 | private Boolean execute = true; 41 | } 42 | -------------------------------------------------------------------------------- /common/common-xxljob/src/main/resources/template.yml: -------------------------------------------------------------------------------- 1 | #xxl-job配置文件模板 2 | 3 | #官方文档地址 https://www.xuxueli.com/xxl-job 4 | xxl: 5 | job: 6 | admin: 7 | ### 调度中心部署根地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册; 8 | addresses: http://127.0.0.1:8080/xxl-job-admin 9 | ### 执行器通讯TOKEN [选填]:非空时启用; 10 | accessToken: 11 | executor: 12 | ### 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册 13 | appname: xxl-job-executor-sample 14 | ### 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。 15 | address: 16 | ### 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务"; 17 | ip: 18 | ### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口; 19 | port: 9999 20 | ### 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径; 21 | logpath: /data/applogs/xxl-job/jobhandler 22 | ### 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能; 23 | logretentiondays: 30 24 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/dto/SysOrgRoleDTO.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.dto; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | 7 | /** 8 | * 岗位信息DTO 9 | * 10 | * @author sunxh 2023/3/1 11 | */ 12 | 13 | @EqualsAndHashCode(callSuper = true) 14 | @Data 15 | public class SysOrgRoleDTO extends BaseDTO { 16 | @Schema(title = "数据类型", allowableValues = {"1", "2"}, description = "1:用户,2:用户组") 17 | private Integer type; 18 | @Schema(title = "用户id或者用户组的id") 19 | private Integer userId; 20 | @Schema(title = "机构id") 21 | private Integer sysOrgId; 22 | @Schema(title = "角色id") 23 | private Integer sysRoleId; 24 | @Schema(title = "机构代码") 25 | private String orgCode; 26 | @Schema(title = "机构名称") 27 | private String orgName; 28 | @Schema(title = "角色名称") 29 | private String roleName; 30 | @Schema(title = "是否当前使用的角色") 31 | private Boolean active; 32 | } 33 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/web/PageResult.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.web; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import java.io.Serializable; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * 分页结果 14 | * sunxh 15 | * 2023-02-25 16 | */ 17 | @Setter 18 | @Getter 19 | @NoArgsConstructor 20 | @Schema(title = "分页结果") 21 | public class PageResult implements Serializable { 22 | @Schema(title = "列表数据") 23 | private List list = new ArrayList<>(); 24 | 25 | @Schema(title = "总数") 26 | private Integer total = 0; 27 | 28 | @Schema(title = "是否分页") 29 | private Boolean isPage; 30 | 31 | @Schema(title = "分页号") 32 | private Integer currentPage; 33 | 34 | @Schema(title = "分页大小") 35 | private Integer pageSize; 36 | 37 | public PageResult(List list, Integer total) { 38 | this.list = list; 39 | this.total = total; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/utils/LoginUtil.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.utils; 2 | 3 | 4 | import cn.dev33.satoken.stp.StpUtil; 5 | import com.xh.common.core.dto.OnlineUserDTO; 6 | import com.xh.common.core.dto.SysLoginUserInfoDTO; 7 | import com.xh.common.core.dto.WxUserInfoDTO; 8 | 9 | /** 10 | * 获取当前登录用户 11 | */ 12 | public class LoginUtil { 13 | public final static String SYS_USER_KEY = "sysUserInfo"; 14 | public final static String WX_USER_KEY = "wxUserInfo"; 15 | 16 | /** 17 | * 获取当前系统登录信息 18 | */ 19 | public static SysLoginUserInfoDTO getSysUserInfo() { 20 | return StpUtil.getSession().getModel(SYS_USER_KEY, SysLoginUserInfoDTO.class); 21 | } 22 | 23 | /** 24 | * 获取当前登录用户详情 25 | */ 26 | public static OnlineUserDTO getOnlineUserInfo() { 27 | return StpUtil.getTokenSession().getModel(SYS_USER_KEY, OnlineUserDTO.class); 28 | } 29 | 30 | /** 31 | * 获取当前微信登录用户信息 32 | */ 33 | public static WxUserInfoDTO getWxUserInfo() { 34 | return StpUtil.getTokenSession().getModel(WX_USER_KEY, WxUserInfoDTO.class); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/entity/SysDictDetail.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.entity; 2 | 3 | import com.xh.common.core.entity.BaseEntity; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import jakarta.persistence.Table; 6 | import jakarta.persistence.Transient; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | 10 | 11 | @Table 12 | @Data 13 | @Schema(title = "数据字典明细") 14 | @EqualsAndHashCode(callSuper = true) 15 | public class SysDictDetail extends BaseEntity { 16 | @Schema(title = "字典类型ID") 17 | private Integer sysDictTypeId; 18 | 19 | @Schema(title = "上级id") 20 | private Integer parentId; 21 | 22 | @Schema(title = "字典值key") 23 | private String value; 24 | 25 | @Schema(title = "字典名称") 26 | private String label; 27 | 28 | @Schema(title = "排序号") 29 | private Integer order; 30 | 31 | @Schema(title = "是否启用") 32 | private Boolean enabled; 33 | 34 | @Schema(title = "数据字典类型ID") 35 | @Transient 36 | private String dictTypeId; 37 | 38 | @Schema(title = "数据自定类型名称") 39 | @Transient 40 | private String dictTypeName; 41 | } 42 | -------------------------------------------------------------------------------- /generator/client/src/main/java/com/xh/generator/client/vo/TableColumnMateDataVO.java: -------------------------------------------------------------------------------- 1 | package com.xh.generator.client.vo; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | /** 7 | * 表格列元数据 VO 8 | * sunxh 2024/11/22 9 | */ 10 | @Data 11 | @Schema(title = "表格列元数据") 12 | public class TableColumnMateDataVO { 13 | 14 | @Schema(title = "数据库名") 15 | private String tableCat; 16 | 17 | @Schema(title = "类型名称") 18 | private String typeName; 19 | 20 | @Schema(title = "表名") 21 | private String tableName; 22 | 23 | @Schema(title = "数据大小") 24 | private Long bufferLength; 25 | 26 | @Schema(title = "是否自增") 27 | private String isAutoincrement; 28 | 29 | @Schema(title = "字段长度") 30 | private Integer columnSize; 31 | 32 | @Schema(title = "小数点") 33 | private Integer decimalDigits; 34 | 35 | @Schema(title = "可为空") 36 | private String isNullable; 37 | 38 | @Schema(title = "字段注释") 39 | private String remarks; 40 | 41 | @Schema(title = "是否主键") 42 | private Boolean primaryKey; 43 | 44 | @Schema(title = "字段名称") 45 | private String columnName; 46 | 47 | } 48 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/entity/SysUserJob.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.entity; 2 | 3 | import com.xh.common.core.entity.BaseEntity; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import jakarta.persistence.Table; 6 | import jakarta.persistence.Transient; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | 10 | 11 | @Schema(title = "系统用户岗位表") 12 | @Table 13 | @Data 14 | @EqualsAndHashCode(callSuper = true) 15 | public class SysUserJob extends BaseEntity { 16 | 17 | @Schema(title = "数据类型", allowableValues = {"1", "2"}, description = "1:用户,2:用户组") 18 | private Integer type; 19 | @Schema(title = "用户id或者用户组的id") 20 | private Integer userId; 21 | @Schema(title = "机构id") 22 | private Integer sysOrgId; 23 | @Schema(title = "角色id") 24 | private Integer sysRoleId; 25 | @Schema(title = "是否启用1:是,0:否") 26 | private Boolean enabled; 27 | 28 | @Schema(title = "机构代码") 29 | @Transient 30 | private String orgCode; 31 | @Schema(title = "机构名称") 32 | @Transient 33 | private String orgName; 34 | @Schema(title = "角色名称") 35 | @Transient 36 | private String roleName; 37 | } 38 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/entity/BaseEntity.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.entity; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import jakarta.persistence.GeneratedValue; 5 | import jakarta.persistence.GenerationType; 6 | import jakarta.persistence.Id; 7 | import lombok.Getter; 8 | import lombok.Setter; 9 | 10 | import java.io.Serializable; 11 | import java.time.LocalDateTime; 12 | 13 | @Getter 14 | @Setter 15 | public class BaseEntity implements Serializable { 16 | 17 | @Schema(title = "主键ID") 18 | @Id 19 | @GeneratedValue(strategy = GenerationType.AUTO) 20 | protected I id; 21 | 22 | @Schema(title = "创建时间") 23 | @AutoSet(AutoSetFun.INSERT_NOW) 24 | private LocalDateTime createTime; 25 | 26 | @Schema(title = "修改时间") 27 | @AutoSet(AutoSetFun.UPDATE_NOW) 28 | private LocalDateTime updateTime; 29 | 30 | @Schema(title = "创建人") 31 | @AutoSet(AutoSetFun.INSERT_BY) 32 | private Integer createBy; 33 | 34 | @Schema(title = "修改人") 35 | @AutoSet(AutoSetFun.UPDATE_BY) 36 | private Integer updateBy; 37 | 38 | @Schema(title = "是否已删除") 39 | @AutoSet(AutoSetFun.DEFAULT_FALSE) 40 | private Boolean deleted; 41 | } 42 | -------------------------------------------------------------------------------- /generator/service/src/main/resources/templates/backend/Entity.java.ftl: -------------------------------------------------------------------------------- 1 | package ${entityPackage}; 2 | 3 | <#if extend??> 4 | import ${extendEntityClassName}; 5 | 6 | import io.swagger.v3.oas.annotations.media.Schema; 7 | import jakarta.persistence.Column; 8 | <#if hasId> 9 | import jakarta.persistence.Id; 10 | 11 | import jakarta.persistence.Table; 12 | import lombok.Data; 13 | import lombok.EqualsAndHashCode; 14 | <#if hasBigDecimal> 15 | import java.math.BigDecimal; 16 | 17 | <#if hasLocalDate> 18 | import java.time.LocalDate; 19 | 20 | <#if hasLocalDateTime> 21 | import java.time.LocalDateTime; 22 | 23 | 24 | /** 25 | * ${name} Entity 26 | * 27 | * @author ${author} 28 | * @since ${date} 29 | */ 30 | @EqualsAndHashCode(callSuper = true) 31 | @Schema(title = "${name}") 32 | @Table(name = "${tableName}") 33 | @Data 34 | public class ${entityName}${entityExtendClassStr!''} { 35 | <#list columns as field> 36 | <#if !(field.isVirtual!false) && !(field.isExtend!false)> 37 | 38 | <#if field.primaryKey!false> 39 | @Id 40 | 41 | @Schema(title ="${field.remarks}") 42 | @Column(name = "${field.columnName}") 43 | private ${field.javaType} ${field.prop}; 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/dto/WxUserInfoDTO.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.dto; 2 | 3 | import cn.dev33.satoken.stp.SaTokenInfo; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import jakarta.persistence.Table; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | 9 | 10 | @Table 11 | @Data 12 | @Schema(title = "微信用户信息") 13 | @EqualsAndHashCode(callSuper = true) 14 | public class WxUserInfoDTO extends BaseDTO { 15 | 16 | @Schema(title = "sessionKey") 17 | private String sessionKey; 18 | @Schema(title = "appid") 19 | private String appid; 20 | @Schema(title = "openid") 21 | private String openid; 22 | @Schema(title = "unionid") 23 | private String unionid; 24 | @Schema(title = "微信昵称") 25 | private String nickName; 26 | @Schema(title = "性别") 27 | private String gender; 28 | @Schema(title = "语言") 29 | private String language; 30 | @Schema(title = "城市") 31 | private String city; 32 | @Schema(title = "省") 33 | private String province; 34 | @Schema(title = "国家") 35 | private String country; 36 | @Schema(title = "头像") 37 | private String avatarUrl; 38 | @Schema(title = "绑定手机号") 39 | private String phoneNumber; 40 | @Schema(title = "token信息") 41 | private SaTokenInfo tokenInfo; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /generator/service/src/main/resources/templates/frontend/api.ts.ftl: -------------------------------------------------------------------------------- 1 | import createAxios from '@/utils/request' 2 | 3 | const baseUrl = import.meta.env.VITE_${service?upper_case}_BASE_URL 4 | 5 | // ${name}列表查询 6 | export function ${queryFun}(params: PageQuery, option?: RequestOption) { 7 | return createAxios(option).post(`${r'${baseUrl}'}${mappingPath}/query`, params) 8 | } 9 | 10 | // 新增${name} 11 | export function ${insertFun}(params = {}, option?: RequestOption) { 12 | return createAxios(option).post(`${r'${baseUrl}'}${mappingPath}/insert`, params) 13 | } 14 | 15 | // 修改${name} 16 | export function ${updateFun}(params = {}, option?: RequestOption) { 17 | return createAxios(option).put(`${r'${baseUrl}'}${mappingPath}/update`, params) 18 | } 19 | 20 | // 获取${name}详情 21 | export function ${getFun}(id: number) { 22 | return createAxios().get(`${r'${baseUrl}'}${mappingPath}/get/${r'${id}'}`) 23 | } 24 | 25 | // 批量删除${name} 26 | export function ${delFun}(ids: string, option?: RequestOption) { 27 | return createAxios(option).delete(`${r'${baseUrl}'}${mappingPath}/del`, { params: { ids } }) 28 | } 29 | <#if hasImport> 30 | 31 | // ${name}导入 32 | export function ${importFun}(params: object[], option?: RequestOption) { 33 | return createAxios(option).post(`${r'${baseUrl}'}${mappingPath}/imports`, params) 34 | } 35 | 36 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/dto/DownloadFileDTO.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.dto; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | /** 7 | * 文件下载DTO 8 | * 9 | * @author sunxh 2023/5/13 10 | */ 11 | @Schema(description = "文件下载") 12 | @Data 13 | public class DownloadFileDTO { 14 | @Schema(title = "文件表ID") 15 | private Integer id; 16 | @Schema(title = "对象存储key") 17 | private String object; 18 | @Schema(title = "文件MIME类型") 19 | private String contentType; 20 | @Schema(title = "Content-Disposition属性", allowableValues = {"attachment", "inline"}, requiredMode = Schema.RequiredMode.REQUIRED, 21 | description = "告诉浏览器如何处理文件流,attachment为下载,inline浏览器在线预览,默认下载") 22 | private String disposition = "attachment"; 23 | @Schema(title = "文件名") 24 | private String fileName; 25 | @Schema(title = "缩略图显示", description = "是否压缩显示缩略图,仅图片文件有效") 26 | private Boolean isScale = false; 27 | @Schema(title = "缩略图长边大小", description = "仅图片文件有效,以最长边缩小到此大小等比例缩放,默认60像素") 28 | private Double scaleWidth = 60.0; 29 | @Schema(title = "视频帧图片预览", description = "仅视频文件有效,是可以传入需要预览视频第几帧图片,响应一个视频对应帧的图片流") 30 | private Integer videoFrameNum; 31 | @Schema(title = "禁用协议缓存", description = "设置为true时,禁用协议缓存") 32 | private Boolean noCache = false; 33 | } 34 | -------------------------------------------------------------------------------- /generator/service/src/main/resources/templates/sql/createSysMenu.sql.ftl: -------------------------------------------------------------------------------- 1 | -- 创建系统菜单 2 | INSERT INTO `sys_menu` (`id`, `parent_id`, `title`, `name`, `path`, `component`, `platform`, `type`, `handle_type`, `outer_url`, `icon`, `icon_type`, `order`, `cache`, `enabled`, `create_time`, `update_time`, `create_by`, `update_by`, `deleted`) 3 | VALUES 4 | (${menuId}, NULL, '${name}', '${permissionPrefix}', '${entityVarName}', '${indexViewPath}', 'web', 'menu', 'route', NULL, NULL, NULL, NULL, 1, 1, now(), now(), ${userId}, ${userId}, 0), 5 | <#if hasImport> 6 | (NULL, ${menuId}, '导入', '${permissionPrefix}:import', NULL, NULL, 'web', 'btn', NULL, NULL, NULL, NULL, NULL, NULL, 1, now(), now(), ${userId}, ${userId}, 0), 7 | 8 | (NULL, ${menuId}, '新增', '${permissionPrefix}:add', NULL, NULL, 'web', 'btn', NULL, NULL, NULL, NULL, NULL, NULL, 1, now(), now(), ${userId}, ${userId}, 0), 9 | (NULL, ${menuId}, '编辑', '${permissionPrefix}:edit', NULL, NULL, 'web', 'btn', NULL, NULL, NULL, NULL, NULL, NULL, 1, now(), now(), ${userId}, ${userId}, 0), 10 | (NULL, ${menuId}, '明细', '${permissionPrefix}:detail', NULL, NULL, 'web', 'btn', NULL, NULL, NULL, NULL, NULL, NULL, 1, now(), now(), ${userId}, ${userId}, 0), 11 | (NULL, ${menuId}, '删除', '${permissionPrefix}:del', NULL, NULL, 'web', 'btn', NULL, NULL, NULL, NULL, NULL, NULL, 1, now(), now(), ${userId}, ${userId}, 0); 12 | -------------------------------------------------------------------------------- /common/common-jdbc/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.xh 7 | common 8 | 0.0.1-SNAPSHOT 9 | 10 | 11 | common-jdbc 12 | 通用数据源配置 13 | 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-starter-jdbc 18 | 19 | 20 | com.mysql 21 | mysql-connector-j 22 | runtime 23 | 24 | 25 | org.postgresql 26 | postgresql 27 | runtime 28 | 29 | 30 | jakarta.persistence 31 | jakarta.persistence-api 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/entity/SysFile.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.entity; 2 | 3 | import com.xh.common.core.entity.BaseDataPermissionEntity; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import jakarta.persistence.Table; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | 9 | 10 | @Table 11 | @Data 12 | @Schema(title = "系统文件") 13 | @EqualsAndHashCode(callSuper = true) 14 | public class SysFile extends BaseDataPermissionEntity { 15 | 16 | @Schema(title = "对象存储key") 17 | private String object; 18 | 19 | @Schema(title = "文件名") 20 | private String name; 21 | 22 | @Schema(title = "文件类型") 23 | private String contentType; 24 | 25 | @Schema(title = "文件后缀扩展名") 26 | private String suffix; 27 | 28 | @Schema(title = "文件大小") 29 | private Long size; 30 | 31 | @Schema(title = "视频抽帧预览图,上传时会尝试抽第10帧图片作为视频文件的预览图") 32 | private Integer previewImageFileId; 33 | 34 | @Schema(title = "图片宽度") 35 | private Integer imgWidth; 36 | 37 | @Schema(title = "图片高度") 38 | private Integer imgHeight; 39 | 40 | @Schema(title = "图片宽高比") 41 | private Double imgRatio; 42 | 43 | @Schema(title = "文件状态", allowableValues = {"1", "2", "3", "4"}, description = "1:正常,2::禁用,3:标记删除,4:已删除") 44 | private Integer status; 45 | 46 | @Schema(title = "文件摘要sha1") 47 | private String sha1; 48 | } 49 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/entity/SysUser.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.entity; 2 | 3 | import com.xh.common.core.entity.BaseEntity; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import jakarta.persistence.Table; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | 9 | @Schema(title = "系统用户") 10 | @Table 11 | @Data 12 | @EqualsAndHashCode(callSuper = true) 13 | public class SysUser extends BaseEntity { 14 | 15 | @Schema(title = "用户代码") 16 | private String code; 17 | 18 | @Schema(title = "用户名") 19 | private String name; 20 | 21 | @Schema(title = "密码") 22 | private String password; 23 | 24 | @Schema(title = "头像") 25 | private String avatar; 26 | 27 | @Schema(title = "手机号码") 28 | private String telephone; 29 | 30 | @Schema(title = "状态(1:正常,2:锁定)") 31 | private Integer status; 32 | 33 | @Schema(title = "登录失败的次数") 34 | private Integer failuresNum; 35 | 36 | @Schema(title = "账号锁定的原因") 37 | private String lockMsg; 38 | 39 | @Schema(title = "允许重复登录") 40 | private Boolean allowRepeat; 41 | 42 | @Schema(title = "自动续签,请求会自动延长token失效时间") 43 | private Boolean autoRenewal; 44 | 45 | @Schema(title = "是否演示账号") 46 | private Boolean isDemo; 47 | 48 | @Schema(title = "角色排序") 49 | private String roleSorter; 50 | 51 | @Schema(title = "是否启用") 52 | private Boolean enabled; 53 | } 54 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/entity/SysMenu.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.entity; 2 | 3 | import com.xh.common.core.entity.BaseEntity; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import jakarta.persistence.Table; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | 9 | 10 | @Table 11 | @Data 12 | @Schema(title = "系统菜单") 13 | @EqualsAndHashCode(callSuper = true) 14 | public class SysMenu extends BaseEntity { 15 | @Schema(title = "上级id") 16 | private Integer parentId; 17 | @Schema(title = "菜单标题") 18 | private String title; 19 | @Schema(title = "路由名称") 20 | private String name; 21 | @Schema(title = "路由路径") 22 | private String path; 23 | @Schema(title = "组件全路径") 24 | private String component; 25 | @Schema(title = "平台", allowableValues = {"web", "app"}) 26 | private String platform; 27 | @Schema(title = "菜单类型", allowableValues = {"dir", "menu", "btn"}, description = "目录,menu:菜单,btn:按钮") 28 | private String type; 29 | @Schema(title = "处理类型", allowableValues = {"route", "iframe", "outer"}, description = "路由:route,iframe:iframe,outer:外链") 30 | private String handleType; 31 | @Schema(title = "外链地址") 32 | private String outerUrl; 33 | @Schema(title = "菜单图标") 34 | private String icon; 35 | @Schema(title = "排序号") 36 | private Integer order; 37 | @Schema(title = "是否缓存") 38 | private Boolean cache; 39 | @Schema(title = "是否启用") 40 | private Boolean enabled; 41 | } 42 | -------------------------------------------------------------------------------- /system/service/src/test/java/com/xh/system/CommonServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.xh.system; 2 | 3 | import cn.dev33.satoken.context.mock.SaTokenContextMockUtil; 4 | import cn.dev33.satoken.stp.StpUtil; 5 | import com.alibaba.fastjson2.JSONObject; 6 | import com.xh.common.core.service.CommonService; 7 | import com.xh.system.service.SysLoginService; 8 | import jakarta.annotation.Resource; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.junit.jupiter.api.Test; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.mock.web.MockHttpServletRequest; 13 | 14 | /** 15 | * @author sunxh 16 | * @since 2025/5/28 17 | */ 18 | @Slf4j 19 | @SpringBootTest 20 | public class CommonServiceTest { 21 | @Resource 22 | private CommonService commonService; 23 | @Resource 24 | private SysLoginService sysLoginService; 25 | 26 | @Test 27 | public void testGetPermissionSql() { 28 | SaTokenContextMockUtil.setMockContext(() -> { 29 | StpUtil.setTokenValueToStorage("test"); 30 | // 先模拟登录 31 | JSONObject params = new JSONObject(); 32 | params.put("username", "admin"); 33 | params.put("password", "admin123"); 34 | params.put("isDemo", "1"); 35 | sysLoginService.login(params); 36 | 37 | String permissionSql = commonService.getPermissionSql("sys_log", "create_by", "sys_role_id", "sys_org_id"); 38 | log.error("权限sql: {}", permissionSql); 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/configuration/SystemWebClientConfig.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.configuration; 2 | 3 | import com.xh.system.client.exchange.SysFileClient; 4 | import jakarta.annotation.Resource; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.web.reactive.function.client.WebClient; 8 | import org.springframework.web.reactive.function.client.support.WebClientAdapter; 9 | import org.springframework.web.service.invoker.HttpServiceProxyFactory; 10 | 11 | import java.time.Duration; 12 | 13 | /** 14 | * 系统服务 web client,跨服务调用配置 15 | * sunxh 2023/9/22 16 | */ 17 | public class SystemWebClientConfig { 18 | 19 | @Resource 20 | private WebClient.Builder myWebClientBuilder; 21 | 22 | //服务名或者ip地址调用对应服务 23 | @Value("${service.system:lb://system}") 24 | private String baseUrl; 25 | 26 | public HttpServiceProxyFactory httpServiceProxyFactory() { 27 | WebClientAdapter webClientAdapter = WebClientAdapter.create(myWebClientBuilder.baseUrl(baseUrl).build()); 28 | // 设置超时时间10s 29 | webClientAdapter.setBlockTimeout(Duration.ofSeconds(10)); 30 | return HttpServiceProxyFactory.builderFor(webClientAdapter).build(); 31 | } 32 | 33 | /** 34 | * SysFileServiceClient 35 | */ 36 | @Bean 37 | public SysFileClient getSysFileServiceClient() { 38 | return httpServiceProxyFactory().createClient(SysFileClient.class); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/dto/SysMenuDTO.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.dto; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | 7 | /** 8 | * 角色权限菜单DTO 9 | * 10 | * @author sunxh 2023/3/1 11 | */ 12 | @EqualsAndHashCode(callSuper = true) 13 | @Schema(description = "承载当前登录用户拥有的菜单权限") 14 | @Data 15 | public class SysMenuDTO extends BaseDTO { 16 | @Schema(title = "角色id") 17 | private Integer roleId; 18 | @Schema(title = "上级id") 19 | private Integer parentId; 20 | @Schema(title = "菜单标题") 21 | private String title; 22 | @Schema(title = "路由名称") 23 | private String name; 24 | @Schema(title = "路由路径") 25 | private String path; 26 | @Schema(title = "组件全路径") 27 | private String component; 28 | @Schema(title = "平台", allowableValues = {"web", "app"}) 29 | private String platform; 30 | @Schema(title = "菜单类型", allowableValues = {"dir", "menu", "btn"}, description = "目录,menu:菜单,btn:按钮") 31 | private String type; 32 | @Schema(title = "处理类型", allowableValues = {"route", "iframe", "outer"}, description = "路由:route,iframe:iframe,outer:外链") 33 | private String handleType; 34 | @Schema(title = "外链地址") 35 | private String outerUrl; 36 | @Schema(title = "菜单图标") 37 | private String icon; 38 | @Schema(title = "排序号") 39 | private Integer order; 40 | @Schema(title = "是否缓存") 41 | private Boolean cache; 42 | @Schema(title = "是否启用") 43 | private Boolean enabled; 44 | } 45 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/configuration/SaTokenConfigure.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.configuration; 2 | 3 | import cn.dev33.satoken.config.SaTokenConfig; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.context.annotation.Primary; 8 | 9 | /** 10 | * Sa-Token 配置类 11 | */ 12 | @Configuration 13 | public class SaTokenConfigure { 14 | @Value("${sys.auth.tokenHeaderName}") 15 | private String tokenHeaderName; 16 | 17 | // Sa-Token 参数配置,参考文档:https://sa-token.cc 18 | // 此配置会覆盖 application.yml 中的配置 19 | @Bean 20 | @Primary 21 | public SaTokenConfig getSaTokenConfigPrimary() { 22 | SaTokenConfig config = new SaTokenConfig(); 23 | config.setTokenName(tokenHeaderName); // token 名称(同时也是 cookie 名称) 24 | config.setTimeout(6 * 60 * 60); // token 有效期(单位:秒),默认30天,-1代表永不过期 25 | config.setActiveTimeout(-1); // token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结 26 | config.setIsConcurrent(true); // 是否允许同一账号多地同时登录(为 true 时允许一起登录,为 false 时新登录挤掉旧登录) 27 | config.setIsShare(false); // 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token,为 false 时每次登录新建一个 token) 28 | config.setTokenStyle("uuid"); // token 风格 29 | config.setIsLog(false); // 是否输出操作日志 30 | config.setMaxLoginCount(-1); 31 | return config; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /generator/service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.xh 7 | generator 8 | 0.0.1-SNAPSHOT 9 | 10 | 11 | generator-service 12 | 代码生成服务模块 13 | 14 | 15 | 16 | com.xh 17 | generator-client 18 | 19 | 20 | org.springframework.boot 21 | spring-boot-starter-test 22 | test 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-freemarker 27 | 28 | 29 | com.xh 30 | common-nacos 31 | 32 | 33 | 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-maven-plugin 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /common/common-nacos/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.xh 7 | common 8 | 0.0.1-SNAPSHOT 9 | 10 | 11 | common-nacos 12 | 通用nacos配置,以nacos作为配置中心和注册中心 13 | 14 | 15 | 16 | com.xh 17 | common-core 18 | 19 | 20 | 21 | org.springframework.cloud 22 | spring-cloud-starter-bootstrap 23 | 24 | 25 | 26 | com.alibaba.cloud 27 | spring-cloud-starter-alibaba-nacos-config 28 | 29 | 30 | 31 | com.alibaba.cloud 32 | spring-cloud-starter-alibaba-nacos-discovery 33 | 34 | 35 | 36 | 37 | org.springframework.cloud 38 | spring-cloud-loadbalancer 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /generator/service/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | sys: 2 | auth: 3 | tokenHeaderName: auth-token 4 | server: 5 | port: ${SERVER_PORT:6003} 6 | spring: 7 | application: 8 | name: generator 9 | web: 10 | allowedOriginPatterns: 11 | - http://localhost:** 12 | - http://demo.xhansky.cn/** 13 | - http://192.168.** 14 | datasource: 15 | first: 16 | url: jdbc:mysql://localhost:3306/xh_admin?characterEncoding=utf-8 17 | username: root 18 | password: 123456 19 | configuration: 20 | maximum-pool-size: 20 21 | # 第二数据源 22 | second: 23 | url: jdbc:mysql://localhost:3306/xh_admin?characterEncoding=utf-8 24 | username: root 25 | password: 123456 26 | configuration: 27 | maximum-pool-size: 20 28 | data: 29 | redis: 30 | host: localhost 31 | port: 6379 32 | database: 0 33 | timeout: 60000 34 | lettuce: 35 | cluster: 36 | refresh: 37 | adaptive: true 38 | period: 20 39 | servlet: 40 | multipart: 41 | max-file-size: 50MB 42 | max-request-size: 50MB 43 | devtools: 44 | livereload: 45 | enabled: true 46 | xxl: 47 | job: 48 | admin: 49 | ### 调度中心部署根地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册; 50 | addresses: http://122.51.118.42:8088/xxl-job-admin 51 | ### 执行器通讯TOKEN [选填]:非空时启用; 52 | accessToken: default_token 53 | 54 | minio: 55 | endpoint: http://localhost:9000 56 | access-key: QEGiz63wnKckxgmvTwre 57 | secret-key: CoWs7QBrFjsEonoFjkOx9eQhZGfE74n9VlmVNMOM 58 | bucket: bucket1 59 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/dto/OnlineUserDTO.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.dto; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | import java.time.LocalDateTime; 8 | 9 | /** 10 | * 在线用户DTO 11 | * sunxh 2023/8/19 12 | */ 13 | 14 | @Schema(title = "在线用户DTO") 15 | @Data 16 | public class OnlineUserDTO implements Serializable { 17 | @Schema(title = "token") 18 | private String token; 19 | @Schema(title = "设备类型") 20 | private String deviceType; 21 | @Schema(title = "登录用户ID") 22 | private Integer userId; 23 | @Schema(title = "登录账户") 24 | private String userCode; 25 | @Schema(title = "登录账户名称") 26 | private String userName; 27 | @Schema(title = "登录时间") 28 | private LocalDateTime loginTime; 29 | @Schema(title = "ip地址") 30 | private String loginIp; 31 | @Schema(title = "登录地点") 32 | private String loginAddress; 33 | @Schema(title = "登录浏览器") 34 | private String loginBrowser; 35 | @Schema(title = "浏览器版本") 36 | private String browserVersion; 37 | @Schema(title = "操作系统") 38 | private String loginOs; 39 | @Schema(title = "是否手机端") 40 | private Boolean isMobile; 41 | @Schema(title = "当前语言") 42 | private String locale; 43 | @Schema(title = "当前语言名称") 44 | private String localeLabel; 45 | @Schema(title = "当前机构ID") 46 | private Integer orgId; 47 | @Schema(title = "当前角色ID") 48 | private Integer roleId; 49 | @Schema(title = "当前机构") 50 | private String orgName; 51 | @Schema(title = "当前使用角色名称") 52 | private String roleName; 53 | } 54 | -------------------------------------------------------------------------------- /common/common-swagger/src/main/java/com/xh/common/swagger/configuration/SpringDocConfig.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.swagger.configuration; 2 | 3 | import io.swagger.v3.oas.models.ExternalDocumentation; 4 | import io.swagger.v3.oas.models.OpenAPI; 5 | import io.swagger.v3.oas.models.SpecVersion; 6 | import io.swagger.v3.oas.models.info.Info; 7 | import io.swagger.v3.oas.models.security.SecurityRequirement; 8 | import io.swagger.v3.oas.models.security.SecurityScheme; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.context.annotation.Bean; 11 | 12 | /** 13 | * 配置swagger 14 | * sunxh 2023/10/7 15 | */ 16 | public class SpringDocConfig { 17 | @Value("${sys.auth.tokenHeaderName}") 18 | private String tokenHeaderName; 19 | 20 | @Bean 21 | public OpenAPI myOpenAPI() { 22 | return new OpenAPI() 23 | .specVersion(SpecVersion.V31) 24 | .info(new Info().title("晓寒管理系统API").version("v1.0.0")) 25 | .externalDocs(new ExternalDocumentation() 26 | .description("晓寒管理系统文档") 27 | .url("https://www.xhansky.cn") 28 | ) 29 | //需要授权 30 | .addSecurityItem( 31 | new SecurityRequirement().addList(tokenHeaderName) 32 | ) 33 | .schemaRequirement(tokenHeaderName, 34 | new SecurityScheme() 35 | .name(tokenHeaderName) 36 | .type(SecurityScheme.Type.APIKEY) 37 | .in(SecurityScheme.In.HEADER)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /system/service/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | sys: 2 | auth: 3 | tokenHeaderName: auth-token 4 | server: 5 | port: ${SERVER_PORT:6002} 6 | spring: 7 | application: 8 | name: system 9 | web: 10 | allowedOriginPatterns: 11 | - http://localhost:** 12 | - http://demo.xhansky.cn/** 13 | - http://192.168.** 14 | datasource: 15 | first: 16 | url: jdbc:mysql://localhost:3306/xh_admin?characterEncoding=utf-8 17 | username: root 18 | password: 123456 19 | configuration: 20 | maximum-pool-size: 20 21 | # 第二数据源 22 | second: 23 | url: jdbc:mysql://localhost:3306/xh_admin?characterEncoding=utf-8 24 | username: root 25 | password: 123456 26 | configuration: 27 | maximum-pool-size: 20 28 | data: 29 | redis: 30 | host: localhost 31 | port: 6379 32 | database: 0 33 | timeout: 60000 34 | lettuce: 35 | cluster: 36 | refresh: 37 | adaptive: true 38 | period: 20 39 | servlet: 40 | multipart: 41 | max-file-size: 50MB 42 | max-request-size: 50MB 43 | devtools: 44 | livereload: 45 | enabled: true 46 | xxl: 47 | job: 48 | admin: 49 | ### 调度中心部署根地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册; 50 | addresses: http://122.51.118.42:8088/xxl-job-admin 51 | ### 执行器通讯TOKEN [选填]:非空时启用; 52 | accessToken: default_token 53 | 54 | minio: 55 | endpoint: http://localhost:9000 56 | access-key: QEGiz63wnKckxgmvTwre 57 | secret-key: CoWs7QBrFjsEonoFjkOx9eQhZGfE74n9VlmVNMOM 58 | bucket: bucket1 59 | 60 | # springboot 3.4开始,可直接输出结构化日志 61 | logging: 62 | structured: 63 | format: 64 | file: ecs 65 | -------------------------------------------------------------------------------- /system/service/src/main/java/com/xh/system/controller/SysLogController.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.controller; 2 | 3 | import cn.dev33.satoken.annotation.SaCheckPermission; 4 | import com.xh.common.core.entity.SysLog; 5 | import com.xh.common.core.web.PageQuery; 6 | import com.xh.common.core.web.PageResult; 7 | import com.xh.common.core.web.RestResponse; 8 | import com.xh.system.service.SysLogService; 9 | import io.swagger.v3.oas.annotations.Operation; 10 | import io.swagger.v3.oas.annotations.tags.Tag; 11 | import jakarta.annotation.Resource; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | @Tag(name = "系统日志") 18 | @RestController 19 | @RequestMapping("/api/system/log") 20 | public class SysLogController { 21 | 22 | @Resource 23 | private SysLogService sysLogService; 24 | 25 | @Operation(description = "日志列表查询") 26 | @PostMapping("/query") 27 | @SaCheckPermission("system:log") 28 | public RestResponse> query(@RequestBody PageQuery> pageQuery) { 29 | PageResult data = sysLogService.query(pageQuery); 30 | return RestResponse.success(data); 31 | } 32 | 33 | @SaCheckPermission("system:log:detail") 34 | @Operation(description = "获取日志详情") 35 | @GetMapping("/get/{id}") 36 | public RestResponse getById(@PathVariable Integer id) { 37 | return RestResponse.success(sysLogService.getById(id)); 38 | } 39 | 40 | @SaCheckPermission("system:log:del") 41 | @Operation(description = "日志批量删除") 42 | @DeleteMapping("/del") 43 | public RestResponse del(@RequestParam List ids) { 44 | sysLogService.del(ids); 45 | return RestResponse.success(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /generator/client/src/main/java/com/xh/generator/client/entity/GenTable.java: -------------------------------------------------------------------------------- 1 | package com.xh.generator.client.entity; 2 | 3 | import com.xh.common.core.entity.BaseEntity; 4 | import com.xh.generator.client.dto.GenTableColumnDTO; 5 | import io.swagger.v3.oas.annotations.media.Schema; 6 | import jakarta.persistence.Table; 7 | import jakarta.persistence.Transient; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | 11 | import java.util.List; 12 | 13 | 14 | /** 15 | * 代码生成表 16 | * sunxh 2024/11/17 17 | */ 18 | @Table 19 | @Data 20 | @Schema(title = "代码生成表") 21 | @EqualsAndHashCode(callSuper = true) 22 | public class GenTable extends BaseEntity { 23 | 24 | @Schema(title = "设计方式") 25 | private String designType; 26 | 27 | @Schema(title = "表名") 28 | private String tableName; 29 | 30 | @Schema(title = "表注释") 31 | private String tableComment; 32 | 33 | @Schema(title = "实体类名") 34 | private String entityName; 35 | 36 | @Schema(title = "功能名") 37 | private String name; 38 | 39 | @Schema(title = "所属服务") 40 | private String service; 41 | 42 | @Schema(title = "模块名") 43 | private String module; 44 | 45 | @Schema(title = "作者") 46 | private String author; 47 | 48 | @Schema(title = "继承类") 49 | private String extend; 50 | 51 | @Schema(title = "列json串") 52 | private String columnsJson; 53 | 54 | @Schema(title = "前端项目生成路径") 55 | private String frontendPath; 56 | 57 | @Schema(title = "后端项目生成路径") 58 | private String backendPath; 59 | 60 | @Schema(title = "是否创建系统菜单") 61 | private Boolean isCreateMenu; 62 | 63 | @Schema(title = "是否启用数据权限") 64 | private Boolean isDataPermission; 65 | 66 | @Schema(title = "列") 67 | @Transient 68 | private List columns; 69 | } 70 | -------------------------------------------------------------------------------- /common/common-xxljob/src/main/java/com/xh/common/xxljob/configuration/XxlJobConfig.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.xxljob.configuration; 2 | 3 | 4 | import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.context.annotation.Bean; 8 | 9 | /** 10 | * 配置xxl-job执行器 11 | * sunxh 2023/10/15 12 | */ 13 | @Slf4j 14 | public class XxlJobConfig { 15 | 16 | @Value("${xxl.job.admin.addresses}") 17 | private String adminAddresses; 18 | 19 | @Value("${xxl.job.accessToken}") 20 | private String accessToken; 21 | 22 | @Value("${xxl.job.executor.appname}") 23 | private String appname; 24 | 25 | @Value("${xxl.job.executor.address}") 26 | private String address; 27 | 28 | @Value("${xxl.job.executor.ip}") 29 | private String ip; 30 | 31 | @Value("${xxl.job.executor.port}") 32 | private int port; 33 | 34 | @Value("${xxl.job.executor.logpath}") 35 | private String logPath; 36 | 37 | @Value("${xxl.job.executor.logretentiondays}") 38 | private int logRetentionDays; 39 | 40 | 41 | @Bean 42 | public XxlJobSpringExecutor xxlJobExecutor() { 43 | log.info(">>>>>>>>>>> xxl-job config init."); 44 | XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); 45 | xxlJobSpringExecutor.setAdminAddresses(adminAddresses); 46 | xxlJobSpringExecutor.setAppname(appname); 47 | xxlJobSpringExecutor.setAddress(address); 48 | xxlJobSpringExecutor.setIp(ip); 49 | xxlJobSpringExecutor.setPort(port); 50 | xxlJobSpringExecutor.setAccessToken(accessToken); 51 | xxlJobSpringExecutor.setLogPath(logPath); 52 | xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); 53 | return xxlJobSpringExecutor; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | XHan Admin 3 |

XHan Admin

4 | XHan Admin(晓寒管理系统) 后端代码库 5 |
6 | 7 | ## ⚡ 介绍 8 | 9 | XHan Admin(晓寒开源管理系统)是一个开源免费(前后端分离)中后台管理系统基础解决方案, 无专业版收费,所有功能毫无保留的贡献给开源社区。 10 | 前端技术栈: Vue3, TypeScript, Element Plus, Pinia , Vite等 11 | 后端技术栈: jdk21, springboot3, spring-cloud, spring-cloud-alibaba, SaToken等 12 | 13 | 前端代码库: [xh-admin-frontend](https://github.com/Alixhan/xh-admin-frontend)
14 | 后端代码库: [xh-admin-backend](https://github.com/Alixhan/xh-admin-backend) 15 | 16 | gitee 码云代码仓库
17 | 前端代码库: [xh-admin-frontend](https://gitee.com/sun-xiaohan/xh-admin-frontend)
18 | 后端代码库: [xh-admin-backend](https://gitee.com/sun-xiaohan/xh-admin-backend) 19 | 20 | ## 📚 文档 21 | 22 | [官方文档](http://www.xhansky.cn) 23 | 24 | ## 📺 在线演示 25 | 26 | [演示站](http://demo.xhansky.cn) 27 | 28 | ## ❤️ Generate electricity with love 29 | 30 | - **完全免费**:觉得不错的话,点个star吧,感谢感谢,star是作者唯一的动力! 31 | - **最新技术栈**:vue3,vite5,typescript5,element-plus, jdk17, springboot3.2 32 | - **完全响应式布局**:各端均做了响应性布局适配,移动端也可以愉快的使用。 33 | - **通用组件**: 统一的组件封装,保证样式功能统一,开发更规范,效率更高,包括表格组件,表单组件,excel导入组件,图片统一上传管理,裁剪组件等 34 | - **纯净的代码**: 从零用最新技术栈逐行搭建,意味着无任何历史包袱,只包含核心纯净的代码。 35 | - **漂亮的样式**: 漂亮的样式设计,赶快试试吧。 36 | - **丰富的主题**: 内置各种颜色主题,支持暗黑模式。 37 | 38 | ## 内置功能模块 39 | - **菜单管理** 40 | - **用户组管理** 41 | - **用户管理** 42 | - **机构管理** 43 | - **角色管理** 44 | - **文件管理** 45 | - **数据字典** 46 | - **系统监控** 47 | - **多角色切换** 48 | - **数据权限** 49 | - **代码生成器** 50 | 51 | ## 💕 贡献者 52 | 53 | 非常感谢! 54 | 55 | 56 | 57 | 58 | 59 | ## ☕ 捐赠 60 | 61 | 提交PR,提交issue,帮我点点star,或者直接打赏我 62 | 63 | ## 📄 License 64 | 65 | [Apache License 2.0](./LICENSE) 66 | 67 | Copyright (c) 2023-present [sxh](https://github.com/Alixhan) 68 | -------------------------------------------------------------------------------- /generator/service/src/main/resources/templates/frontend/import.vue.ftl: -------------------------------------------------------------------------------- 1 | 15 | 73 | 74 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/configuration/StpInterfaceImpl.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.configuration; 2 | 3 | import cn.dev33.satoken.session.SaSession; 4 | import cn.dev33.satoken.stp.StpInterface; 5 | import cn.dev33.satoken.stp.StpUtil; 6 | import com.xh.common.core.dto.OnlineUserDTO; 7 | import com.xh.common.core.dto.SysLoginUserInfoDTO; 8 | import com.xh.common.core.dto.SysMenuDTO; 9 | import com.xh.common.core.dto.SysOrgRoleDTO; 10 | import com.xh.common.core.service.CommonService; 11 | import com.xh.common.core.utils.LoginUtil; 12 | import jakarta.annotation.Resource; 13 | import org.springframework.stereotype.Component; 14 | 15 | import java.util.List; 16 | 17 | /** 18 | * 自定义权限加载接口实现类 19 | */ 20 | @Component 21 | public class StpInterfaceImpl implements StpInterface { 22 | 23 | @Resource 24 | private CommonService commonService; 25 | 26 | /** 27 | * 返回一个账号所拥有的权限码集合 28 | */ 29 | @Override 30 | public List getPermissionList(Object loginId, String loginType) { 31 | SaSession tokenSession = StpUtil.getTokenSession(); 32 | OnlineUserDTO onlineUser = tokenSession.getModel(LoginUtil.SYS_USER_KEY, OnlineUserDTO.class); 33 | List rolePermissions = commonService.getRolePermissions(onlineUser.getRoleId(), false); 34 | return rolePermissions.stream().map(SysMenuDTO::getName).toList(); 35 | } 36 | 37 | /** 38 | * 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验) 39 | */ 40 | @Override 41 | public List getRoleList(Object loginId, String loginType) { 42 | SaSession session = StpUtil.getSession(); 43 | SysLoginUserInfoDTO loginUserInfoDTO = session.getModel(LoginUtil.SYS_USER_KEY, SysLoginUserInfoDTO.class); 44 | if (loginUserInfoDTO == null) return null; 45 | List roles = loginUserInfoDTO.getRoles(); 46 | return roles.stream().map(i -> i.getSysOrgId() + i.getRoleName()).toList(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /system/client/src/main/java/com/xh/system/client/exchange/SysFileClient.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.client.exchange; 2 | 3 | import com.xh.common.core.web.PageQuery; 4 | import com.xh.common.core.web.PageResult; 5 | import com.xh.common.core.web.RestResponse; 6 | import com.xh.system.client.dto.DownloadFileDTO; 7 | import com.xh.system.client.entity.SysFile; 8 | import io.swagger.v3.oas.annotations.Operation; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.web.bind.annotation.PathVariable; 11 | import org.springframework.web.bind.annotation.RequestBody; 12 | import org.springframework.web.bind.annotation.RequestHeader; 13 | import org.springframework.web.bind.annotation.RequestParam; 14 | import org.springframework.web.service.annotation.DeleteExchange; 15 | import org.springframework.web.service.annotation.GetExchange; 16 | import org.springframework.web.service.annotation.HttpExchange; 17 | import org.springframework.web.service.annotation.PostExchange; 18 | 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | /** 23 | * 文件操作跨服务调用客户端 24 | * sunxh 2023/9/22 25 | */ 26 | @HttpExchange(url = "/api/file/operation") 27 | public interface SysFileClient { 28 | 29 | @Operation(description = "文件下载(图片预览)") 30 | @GetExchange("/download") 31 | ResponseEntity downloadFile(DownloadFileDTO downloadFileDTO, @RequestHeader(value = "Range", defaultValue = "") String range); 32 | 33 | @Operation(description = "文件列表查询") 34 | @PostExchange("/query") 35 | RestResponse> query(@RequestBody PageQuery> pageQuery); 36 | 37 | @Operation(description = "文件保存") 38 | @PostExchange("/save") 39 | RestResponse save(@RequestBody SysFile sysMenu); 40 | 41 | @Operation(description = "获取系统文件详情") 42 | @GetExchange("/get/{id}") 43 | RestResponse getById(@PathVariable String id); 44 | 45 | @Operation(description = "批量删除文件") 46 | @DeleteExchange("/del") 47 | RestResponse del(@RequestParam List ids); 48 | } 49 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/entity/SysLog.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.entity; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import jakarta.persistence.Table; 5 | import jakarta.persistence.Transient; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | 9 | import java.time.LocalDateTime; 10 | 11 | 12 | @Schema(title = "系统日志") 13 | @Table 14 | @Data 15 | @EqualsAndHashCode(callSuper = true) 16 | public class SysLog extends BaseEntity { 17 | @Schema(title = "token") 18 | private String token; 19 | 20 | @Schema(title = "请求方法") 21 | private String method; 22 | 23 | @Schema(title = "请求路径") 24 | private String url; 25 | 26 | @Schema(title = "请求内容类型") 27 | private String contentType; 28 | 29 | @Schema(title = "请求参数") 30 | private String requestParameter; 31 | 32 | @Schema(title = "请求体") 33 | private String requestBody; 34 | 35 | @Schema(title = "响应体") 36 | private String responseBody; 37 | 38 | @Schema(title = "ip地址") 39 | private String ip; 40 | 41 | @Schema(title = "ip属地") 42 | private String ipAddress; 43 | 44 | @Schema(title = "模块") 45 | private String tag; 46 | 47 | @Schema(title = "操作") 48 | private String operation; 49 | 50 | @Schema(title = "开始时间") 51 | private LocalDateTime startTime; 52 | 53 | @Schema(title = "结束时间") 54 | private LocalDateTime endTime; 55 | 56 | @Schema(title = "耗时ms") 57 | private Long time; 58 | 59 | @Schema(title = "响应状态") 60 | private String status; 61 | 62 | @Schema(title = "异常堆栈信息") 63 | private String stackTrace; 64 | 65 | @Schema(title = "使用语言") 66 | private String locale; 67 | 68 | @Schema(title = "使用语言名称") 69 | private String localeLabel; 70 | 71 | @Schema(title = "使用机构ID") 72 | private Integer sysOrgId; 73 | 74 | @Schema(title = "使用机构名称") 75 | private String orgName; 76 | 77 | @Schema(title = "使用角色ID") 78 | private Integer sysRoleId; 79 | 80 | @Schema(title = "使用角色名称") 81 | private String roleName; 82 | 83 | @Transient 84 | @Schema(title = "用户名称") 85 | private String name; 86 | } 87 | -------------------------------------------------------------------------------- /common/common-nacos/src/main/java/com/xh/common/nacos/configuration/WebClientConfig.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.nacos.configuration; 2 | 3 | import cn.dev33.satoken.exception.NotWebContextException; 4 | import cn.dev33.satoken.stp.StpUtil; 5 | import com.xh.common.core.Constant; 6 | import com.xh.common.core.configuration.WebConfig; 7 | import org.springframework.cloud.client.loadbalancer.LoadBalanced; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.context.annotation.Scope; 11 | import org.springframework.http.codec.json.Jackson2JsonDecoder; 12 | import org.springframework.web.reactive.function.client.ClientRequest; 13 | import org.springframework.web.reactive.function.client.ExchangeStrategies; 14 | import org.springframework.web.reactive.function.client.WebClient; 15 | 16 | /** 17 | * WebClient跨服务配置 18 | * sunxh 2023/2/26 19 | */ 20 | @Configuration(proxyBeanMethods = false) 21 | public class WebClientConfig { 22 | /** 23 | * 通用跨服务用WebClientBuilder,携带鉴权token,设置序列化内容,默认启用负载均衡 24 | */ 25 | @Bean(name = "myWebClientBuilder") 26 | @Scope("prototype") 27 | @LoadBalanced 28 | WebClient.Builder getMyWebClientBuilder() { 29 | return WebClient.builder() 30 | //过滤器重构请求,携带鉴权token 31 | .filter((request, next) -> { 32 | final ClientRequest.Builder req = ClientRequest.from(request); 33 | try { 34 | req.header(StpUtil.getTokenName(), StpUtil.getTokenValue()); 35 | } catch (NotWebContextException e) { 36 | //非web环境则传输一个固定字符串,标识自动程序执行跨服务 37 | req.header(Constant.AUTO_FEIGN_KEY, "自动程序跨服务"); 38 | } 39 | return next.exchange(req.build()); 40 | }) 41 | //序列化配置 42 | .exchangeStrategies(ExchangeStrategies.builder() 43 | .codecs(configurer -> configurer.defaultCodecs().jackson2JsonDecoder( 44 | new Jackson2JsonDecoder(WebConfig.getDefaultObjectMapper()) 45 | )).build() 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/web/RestResponse.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.web; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | import org.springframework.http.HttpStatus; 6 | 7 | import java.io.Serializable; 8 | 9 | @Schema(title = "通用响应对象") 10 | @Data 11 | public class RestResponse implements Serializable { 12 | 13 | @Schema(title = "http状态码", description = "正确响应码为200") 14 | private Integer httpCode; 15 | @Schema(title = "响应消息状态", allowableValues = {"success", "error", "warning", "info"}) 16 | private String status; 17 | @Schema(title = "响应的消息内容") 18 | private String message; 19 | private T data; 20 | 21 | /** 22 | * 响应成功信息 23 | */ 24 | public static RestResponse success() { 25 | RestResponse restResponse = new RestResponse<>(); 26 | restResponse.httpCode = HttpStatus.OK.value(); 27 | restResponse.status = "success"; 28 | return restResponse; 29 | } 30 | 31 | /** 32 | * 响应成功信息,携带data 33 | */ 34 | public static RestResponse success(T data) { 35 | RestResponse restResponse = new RestResponse<>(); 36 | restResponse.httpCode = HttpStatus.OK.value(); 37 | restResponse.status = "success"; 38 | restResponse.data = data; 39 | return restResponse; 40 | } 41 | 42 | /** 43 | * 错误响应,携带data 44 | */ 45 | public static RestResponse errorData(T data) { 46 | RestResponse restResponse = new RestResponse<>(); 47 | restResponse.httpCode = HttpStatus.INTERNAL_SERVER_ERROR.value(); 48 | restResponse.status = "error"; 49 | restResponse.data = data; 50 | return restResponse; 51 | } 52 | 53 | /** 54 | * 错误响应 55 | */ 56 | public static RestResponse error() { 57 | RestResponse restResponse = new RestResponse<>(); 58 | restResponse.httpCode = HttpStatus.INTERNAL_SERVER_ERROR.value(); 59 | restResponse.status = "error"; 60 | return restResponse; 61 | } 62 | 63 | /** 64 | * 错误响应,携带错误message 65 | */ 66 | public static RestResponse error(String message) { 67 | RestResponse restResponse = error(); 68 | restResponse.message = message; 69 | return restResponse; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /common/common-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.xh 7 | common 8 | 0.0.1-SNAPSHOT 9 | 10 | 11 | common-core 12 | 通用配置 13 | 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-starter-data-redis 18 | 19 | 20 | com.google.guava 21 | guava 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-web 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-webflux 30 | 31 | 32 | 33 | cn.dev33 34 | sa-token-spring-boot3-starter 35 | 36 | 37 | 38 | cn.dev33 39 | sa-token-redis-jackson 40 | 41 | 42 | com.alibaba.fastjson2 43 | fastjson2 44 | 45 | 46 | 47 | org.lionsoul 48 | ip2region 49 | 50 | 51 | com.xh 52 | common-jdbc 53 | 54 | 55 | com.xh 56 | common-swagger 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /system/service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.xh 7 | system 8 | 0.0.1-SNAPSHOT 9 | 10 | 11 | system-service 12 | 系统模块服务,包含认证鉴权 13 | 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-starter-test 18 | 19 | 20 | 21 | cn.hutool 22 | hutool-http 23 | 24 | 25 | 26 | cn.hutool 27 | hutool-captcha 28 | 29 | 30 | 31 | 32 | io.minio 33 | minio 34 | 35 | 36 | 37 | 38 | org.sejda.imageio 39 | webp-imageio 40 | 0.1.6 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | com.xh 50 | system-client 51 | 52 | 53 | 54 | 55 | com.xh 56 | common-nacos 57 | 58 | 59 | 60 | 61 | 62 | 63 | org.springframework.boot 64 | spring-boot-maven-plugin 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /generator/client/src/main/java/com/xh/generator/client/dto/GenTableColumnDTO.java: -------------------------------------------------------------------------------- 1 | package com.xh.generator.client.dto; 2 | 3 | import com.xh.common.core.dto.BaseDTO; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import jakarta.persistence.Table; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * 代码生成列明细 13 | * sunxh 2024/11/17 14 | */ 15 | @Table 16 | @Data 17 | @Schema(title = "代码生成字段明细") 18 | @EqualsAndHashCode(callSuper = true) 19 | public class GenTableColumnDTO extends BaseDTO { 20 | 21 | @Schema(title = "表单类型") 22 | private String formType; 23 | 24 | @Schema(title = "数据字典类型") 25 | private Integer dictType; 26 | 27 | @Schema(title = "属性名") 28 | private String prop; 29 | 30 | @Schema(title = "名称") 31 | private String label; 32 | 33 | @Schema(title = "验证规则") 34 | private List rules; 35 | 36 | @Schema(title = "虚拟列") 37 | private Boolean isVirtual; 38 | 39 | @Schema(title = "主键") 40 | private Boolean primaryKey; 41 | 42 | @Schema(title = "主键类型") 43 | private String primaryKeyType; 44 | 45 | @Schema(title = "字段名") 46 | private String columnName; 47 | 48 | @Schema(title = "字段类型") 49 | private String colType; 50 | 51 | @Schema(title = "长度") 52 | private Integer columnSize; 53 | 54 | @Schema(title = "小数点") 55 | private Integer decimalDigits; 56 | 57 | @Schema(title = "注释") 58 | private String remarks; 59 | 60 | @Schema(title = "简单查询") 61 | private Boolean isQuery; 62 | 63 | @Schema(title = "表格列表") 64 | private Boolean isTable; 65 | 66 | @Schema(title = "表单维护") 67 | private Boolean isForm; 68 | 69 | @Schema(title = "导入") 70 | private Boolean isImport; 71 | 72 | @Schema(title = "导出") 73 | private Boolean isExport; 74 | 75 | @Schema(title = "是否继承而来的列") 76 | private Boolean isExtend; 77 | 78 | @Schema(title = "Java类型") 79 | private String javaType; 80 | 81 | @Schema(title = "简易查询sql") 82 | private String querySql; 83 | 84 | @Schema(title = "列表列") 85 | private String tableColStr; 86 | 87 | @Schema(title = "简单查询列") 88 | private String queryColStr; 89 | 90 | @Schema(title = "表单列") 91 | private String formColStr; 92 | 93 | @Schema(title = "导入列") 94 | private String importColStr; 95 | 96 | @Schema(title = "sql列") 97 | private String sqlColStr; 98 | } 99 | -------------------------------------------------------------------------------- /system/service/src/main/java/com/xh/system/controller/SysOrgController.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.controller; 2 | 3 | import cn.dev33.satoken.annotation.SaCheckPermission; 4 | import cn.dev33.satoken.annotation.SaMode; 5 | import com.xh.common.core.web.PageQuery; 6 | import com.xh.common.core.web.PageResult; 7 | import com.xh.common.core.web.RestResponse; 8 | import com.xh.system.client.entity.SysOrg; 9 | import com.xh.system.service.SysOrgService; 10 | import io.swagger.v3.oas.annotations.Operation; 11 | import io.swagger.v3.oas.annotations.tags.Tag; 12 | import jakarta.annotation.Resource; 13 | import org.springframework.web.bind.annotation.*; 14 | 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | @Tag(name = "系统机构") 19 | @RestController 20 | @RequestMapping("/api/system/org") 21 | @SaCheckPermission("system:org") 22 | public class SysOrgController { 23 | 24 | @Resource 25 | private SysOrgService sysOrgService; 26 | 27 | @Operation(description = "机构树查询") 28 | @GetMapping("/tree") 29 | public RestResponse> query(String name) { 30 | PageResult data = sysOrgService.queryOrgTree(name); 31 | return RestResponse.success(data); 32 | } 33 | 34 | @Operation(description = "机构列表查询") 35 | @PostMapping("/query") 36 | public RestResponse> query(@RequestBody PageQuery> pageQuery) { 37 | PageResult data = sysOrgService.query(pageQuery); 38 | return RestResponse.success(data); 39 | } 40 | 41 | @SaCheckPermission(value = {"system:org:add", "system:org:edit"}, mode = SaMode.OR) 42 | @Operation(description = "机构保存") 43 | @PostMapping("/save") 44 | public RestResponse save(@RequestBody SysOrg sysOrg) { 45 | return RestResponse.success(sysOrgService.save(sysOrg)); 46 | } 47 | 48 | @SaCheckPermission(value = {"system:org:edit", "system:org:detail"}, mode = SaMode.OR) 49 | @Operation(description = "获取机构详情") 50 | @GetMapping("/get/{id}") 51 | public RestResponse getById(@PathVariable Integer id) { 52 | return RestResponse.success(sysOrgService.getById(id)); 53 | } 54 | 55 | @SaCheckPermission("system:org:del") 56 | @Operation(description = "机构批量删除") 57 | @DeleteMapping("/del") 58 | public RestResponse del(@RequestParam List ids) { 59 | sysOrgService.del(ids); 60 | return RestResponse.success(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /system/service/src/main/java/com/xh/system/controller/SysMenuController.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.controller; 2 | 3 | import cn.dev33.satoken.annotation.SaCheckPermission; 4 | import cn.dev33.satoken.annotation.SaMode; 5 | import com.xh.common.core.web.PageQuery; 6 | import com.xh.common.core.web.PageResult; 7 | import com.xh.common.core.web.RestResponse; 8 | import com.xh.system.client.entity.SysMenu; 9 | import com.xh.system.service.SysMenuService; 10 | import io.swagger.v3.oas.annotations.Operation; 11 | import io.swagger.v3.oas.annotations.tags.Tag; 12 | import jakarta.annotation.Resource; 13 | import org.springframework.web.bind.annotation.*; 14 | 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | @Tag(name = "系统菜单") 19 | @RestController 20 | @RequestMapping("/api/system/menu") 21 | @SaCheckPermission("system:menu") 22 | public class SysMenuController { 23 | 24 | @Resource 25 | private SysMenuService sysMenuService; 26 | 27 | @Operation(description = "菜单列表查询") 28 | @PostMapping("/query") 29 | public RestResponse> query(@RequestBody PageQuery> pageQuery) { 30 | PageResult data = sysMenuService.query(pageQuery); 31 | return RestResponse.success(data); 32 | } 33 | 34 | @SaCheckPermission(value = {"system:menu:add", "system:menu:edit"}, mode = SaMode.OR) 35 | @Operation(description = "切换菜单字段值") 36 | @PostMapping("/switch_prop") 37 | public RestResponse> switchMenuProp(@RequestBody Map param) { 38 | sysMenuService.switchMenuProp(param); 39 | return RestResponse.success(); 40 | } 41 | 42 | @SaCheckPermission(value = {"system:menu:add", "system:menu:edit"}, mode = SaMode.OR) 43 | @Operation(description = "菜单保存") 44 | @PostMapping("/save") 45 | public RestResponse save(@RequestBody SysMenu sysMenu) { 46 | return RestResponse.success(sysMenuService.save(sysMenu)); 47 | } 48 | 49 | @SaCheckPermission(value = {"system:menu:edit", "system:menu:detail"}, mode = SaMode.OR) 50 | @Operation(description = "获取菜单详情") 51 | @GetMapping("/get/{id}") 52 | public RestResponse getById(@PathVariable Integer id) { 53 | return RestResponse.success(sysMenuService.getById(id)); 54 | } 55 | 56 | @SaCheckPermission("system:menu:del") 57 | @Operation(description = "菜单批量删除") 58 | @DeleteMapping("/del") 59 | public RestResponse del(@RequestParam List ids) { 60 | sysMenuService.del(ids); 61 | return RestResponse.success(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /system/service/src/main/java/com/xh/system/controller/SysDictController.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.controller; 2 | 3 | import cn.dev33.satoken.annotation.SaCheckPermission; 4 | import cn.dev33.satoken.annotation.SaMode; 5 | import com.xh.common.core.web.PageQuery; 6 | import com.xh.common.core.web.PageResult; 7 | import com.xh.common.core.web.RestResponse; 8 | import com.xh.system.client.entity.SysDictDetail; 9 | import com.xh.system.client.entity.SysDictType; 10 | import com.xh.system.service.SysDictService; 11 | import io.swagger.v3.oas.annotations.Operation; 12 | import io.swagger.v3.oas.annotations.tags.Tag; 13 | import jakarta.annotation.Resource; 14 | import org.springframework.web.bind.annotation.*; 15 | 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | @Tag(name = "数据字典") 20 | @RestController 21 | @RequestMapping("/api/system/dict") 22 | public class SysDictController { 23 | 24 | @Resource 25 | private SysDictService sysDictService; 26 | 27 | @Operation(description = "数据字典类型查询") 28 | @PostMapping("/type/query") 29 | @SaCheckPermission("system:dict") 30 | public RestResponse> queryTypes(@RequestBody PageQuery> pageQuery) { 31 | PageResult data = sysDictService.queryTypes(pageQuery); 32 | return RestResponse.success(data); 33 | } 34 | 35 | @Operation(description = "数据字典明细查询") 36 | @PostMapping("/detail/query") 37 | public RestResponse> queryDetails(@RequestBody PageQuery> pageQuery) { 38 | PageResult data = sysDictService.queryDetails(pageQuery); 39 | return RestResponse.success(data); 40 | } 41 | 42 | @SaCheckPermission(value = {"system:dict:edit", "system:dict:detail"}, mode = SaMode.OR) 43 | @Operation(description = "获取字典明细详情") 44 | @GetMapping("/detail/get/{id}") 45 | public RestResponse getById(@PathVariable Integer id) { 46 | return RestResponse.success(sysDictService.getDictDetailById(id)); 47 | } 48 | 49 | @SaCheckPermission(value = {"system:dict:add", "system:dict:edit"}, mode = SaMode.OR) 50 | @Operation(description = "数据字典明细保存") 51 | @PostMapping("/detail/save") 52 | public RestResponse saveDictDetail(@RequestBody SysDictDetail sysDictDetail) { 53 | return RestResponse.success(sysDictService.saveDictDetail(sysDictDetail)); 54 | } 55 | 56 | @SaCheckPermission("system:dict:del") 57 | @Operation(description = "批量数据字典删除") 58 | @DeleteMapping("/detail/del") 59 | public RestResponse del(@RequestParam List ids) { 60 | sysDictService.delDetail(ids); 61 | return RestResponse.success(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /system/service/src/main/java/com/xh/system/controller/SysDataPermissionController.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.controller; 2 | 3 | import cn.dev33.satoken.annotation.SaCheckPermission; 4 | import cn.dev33.satoken.annotation.SaMode; 5 | import com.xh.common.core.web.PageQuery; 6 | import com.xh.common.core.web.PageResult; 7 | import com.xh.common.core.web.RestResponse; 8 | import com.xh.system.client.entity.SysDataEntity; 9 | import com.xh.system.client.entity.SysDataPermission; 10 | import com.xh.system.service.SysDataPermissionService; 11 | import io.swagger.v3.oas.annotations.Operation; 12 | import io.swagger.v3.oas.annotations.tags.Tag; 13 | import jakarta.annotation.Resource; 14 | import org.springframework.web.bind.annotation.*; 15 | 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | @Tag(name = "系统数据权限") 20 | @RestController 21 | @RequestMapping("/api/system/dataPermission") 22 | @SaCheckPermission("system:dataPermission") 23 | public class SysDataPermissionController { 24 | 25 | @Resource 26 | private SysDataPermissionService sysDataPermissionService; 27 | 28 | @Operation(description = "数据实体列表查询") 29 | @PostMapping("/entity/query") 30 | public RestResponse> queryEntity() { 31 | List data = sysDataPermissionService.queryEntity(); 32 | return RestResponse.success(data); 33 | } 34 | 35 | @Operation(description = "数据权限列表查询") 36 | @PostMapping("/query") 37 | public RestResponse> query(@RequestBody PageQuery> pageQuery) { 38 | PageResult data = sysDataPermissionService.query(pageQuery); 39 | return RestResponse.success(data); 40 | } 41 | 42 | @SaCheckPermission(value = {"system:dataPermission:add", "system:dataPermission:edit"}, mode = SaMode.OR) 43 | @Operation(description = "数据权限保存") 44 | @PostMapping("/save") 45 | public RestResponse save(@RequestBody SysDataPermission sysDataPermission) { 46 | return RestResponse.success(sysDataPermissionService.save(sysDataPermission)); 47 | } 48 | 49 | @SaCheckPermission(value = {"system:dataPermission:edit", "system:dataPermission:detail"}, mode = SaMode.OR) 50 | @Operation(description = "获取数据权限详情") 51 | @GetMapping("/get/{id}") 52 | public RestResponse getById(@PathVariable Integer id) { 53 | return RestResponse.success(sysDataPermissionService.getById(id)); 54 | } 55 | 56 | @SaCheckPermission("system:dataPermission:del") 57 | @Operation(description = "数据权限批量删除") 58 | @DeleteMapping("/del") 59 | public RestResponse del(@RequestParam List ids) { 60 | sysDataPermissionService.del(ids); 61 | return RestResponse.success(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /common/common-jdbc/src/main/java/com/xh/common/jdbc/configuration/DataSourceConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.jdbc.configuration; 2 | 3 | import com.zaxxer.hikari.HikariDataSource; 4 | import org.springframework.beans.factory.annotation.Qualifier; 5 | import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.context.annotation.Primary; 10 | import org.springframework.jdbc.core.JdbcTemplate; 11 | 12 | import javax.sql.DataSource; 13 | 14 | /** 15 | * 数据源配置 16 | * 参考文档 ... 17 | * sunxh 2023/4/17 18 | */ 19 | @Configuration(proxyBeanMethods = false) 20 | public class DataSourceConfiguration { 21 | 22 | /** 23 | * 第一数据源配置信息 24 | */ 25 | @Bean("firstDataSourceProperties") 26 | @ConfigurationProperties("spring.datasource.first") 27 | public DataSourceProperties firstDataSourceProperties() { 28 | return new DataSourceProperties(); 29 | } 30 | 31 | /** 32 | * 第一数据源 33 | */ 34 | @Primary 35 | @Bean("firstDataSource") 36 | @ConfigurationProperties("spring.datasource.first.configuration") 37 | public DataSource firstDataSource(@Qualifier("firstDataSourceProperties") DataSourceProperties properties) { 38 | return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build(); 39 | } 40 | 41 | /** 42 | * 第一数据源JdbcTemplate 43 | */ 44 | @Primary 45 | @Bean("firstJdbcTemplate") 46 | public JdbcTemplate firstJdbcTemplate(@Qualifier("firstDataSource") DataSource dataSource) { 47 | return new JdbcTemplate(dataSource); 48 | } 49 | 50 | /** 51 | * 第二数据源配置信息 52 | */ 53 | @Bean("secondDataSourceProperties") 54 | @ConfigurationProperties("spring.datasource.second") 55 | public DataSourceProperties secondDataSourceProperties() { 56 | return new DataSourceProperties(); 57 | } 58 | 59 | /** 60 | * 第二数据源 61 | */ 62 | @Bean("secondDataSource") 63 | @ConfigurationProperties("spring.datasource.second.configuration") 64 | public DataSource secondDataSource(@Qualifier("secondDataSourceProperties") DataSourceProperties properties) { 65 | return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build(); 66 | } 67 | 68 | /** 69 | * 第二数据源JdbcTemplate 70 | */ 71 | @Bean("secondJdbcTemplate") 72 | public JdbcTemplate secondJdbcTemplate(@Qualifier("secondDataSource") DataSource dataSource) { 73 | return new JdbcTemplate(dataSource); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /system/service/src/main/java/com/xh/system/service/SysDataPermissionService.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.service; 2 | 3 | import com.xh.common.core.service.BaseServiceImpl; 4 | import com.xh.common.core.utils.CommonUtil; 5 | import com.xh.common.core.web.PageQuery; 6 | import com.xh.common.core.web.PageResult; 7 | import com.xh.system.client.entity.SysDataEntity; 8 | import com.xh.system.client.entity.SysDataPermission; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.transaction.annotation.Transactional; 12 | 13 | import java.io.Serializable; 14 | import java.util.HashMap; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | /** 19 | * 系统数据权限service 20 | * sunxh 2023/8/19 21 | */ 22 | @Service 23 | @Slf4j 24 | public class SysDataPermissionService extends BaseServiceImpl { 25 | /** 26 | * 系统数据权限查询 27 | */ 28 | @Transactional(readOnly = true) 29 | public List queryEntity() { 30 | String sql = "select id, name from sys_data_entity"; 31 | return baseJdbcDao.findList(SysDataEntity.class, sql); 32 | } 33 | 34 | /** 35 | * 系统数据权限查询 36 | */ 37 | @Transactional(readOnly = true) 38 | public PageResult query(PageQuery> pageQuery) { 39 | Map param = pageQuery.getParam(); 40 | String sql = "select id,name,expression,create_time,update_time,create_by,update_by from sys_data_permission where deleted is false "; 41 | if (CommonUtil.isNotEmpty(param.get("name"))) { 42 | sql += " and name like '%' ? '%'"; 43 | pageQuery.addArg(param.get("name")); 44 | } 45 | pageQuery.setBaseSql(sql); 46 | return baseJdbcDao.query(SysDataPermission.class, pageQuery); 47 | } 48 | 49 | 50 | @Transactional 51 | public SysDataPermission save(SysDataPermission sysDataPermission) { 52 | if (sysDataPermission.getId() == null) { 53 | baseJdbcDao.insert(sysDataPermission); 54 | } else { 55 | baseJdbcDao.update(sysDataPermission); 56 | } 57 | return sysDataPermission; 58 | } 59 | 60 | /** 61 | * id获取数据权限详情 62 | */ 63 | @Transactional(readOnly = true) 64 | public SysDataPermission getById(Serializable id) { 65 | return baseJdbcDao.findById(SysDataPermission.class, id); 66 | } 67 | 68 | /** 69 | * ids批量删除数据权限 70 | */ 71 | @Transactional 72 | public void del(List ids) { 73 | log.info("批量删除数据权限--"); 74 | String sql = "update sys_data_permission set deleted = 1 where id in (:ids)"; 75 | Map paramMap = new HashMap<>() {{ 76 | put("ids", ids); 77 | }}; 78 | primaryNPJdbcTemplate.update(sql, paramMap); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /generator/service/src/main/resources/templates/backend/Controller.java.ftl: -------------------------------------------------------------------------------- 1 | package ${controllerPackage}; 2 | 3 | import cn.dev33.satoken.annotation.SaCheckPermission; 4 | import cn.dev33.satoken.annotation.SaMode; 5 | import com.xh.common.core.web.PageQuery; 6 | import com.xh.common.core.web.PageResult; 7 | import com.xh.common.core.web.RestResponse; 8 | import ${entityPackage}.${entityName}; 9 | import ${dtoPackage}.${dtoName}; 10 | import ${servicePackage}.${serviceName}; 11 | import io.swagger.v3.oas.annotations.Operation; 12 | import io.swagger.v3.oas.annotations.tags.Tag; 13 | import jakarta.annotation.Resource; 14 | import org.springframework.web.bind.annotation.*; 15 | 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | /** 20 | * ${name} Controller 21 | * 22 | * @author ${author} 23 | * @since ${date} 24 | */ 25 | @Tag(name = "${name}") 26 | @RestController 27 | @RequestMapping("${mappingPath}") 28 | public class ${entityName}Controller { 29 | 30 | @Resource 31 | private ${serviceName} ${serviceVarName}; 32 | 33 | @SaCheckPermission("${permissionPrefix}") 34 | @Operation(description = "${name}查询") 35 | @PostMapping("/query") 36 | public RestResponse> query(@RequestBody PageQuery> pageQuery) { 37 | PageResult<${entityName}> data = ${serviceVarName}.query(pageQuery); 38 | return RestResponse.success(data); 39 | } 40 | 41 | @SaCheckPermission("${permissionPrefix}:add") 42 | @Operation(description = "${name}新增") 43 | @PostMapping("/insert") 44 | public RestResponse<${entityName}> insert(@RequestBody ${dtoName} ${dtoVarName}) { 45 | return RestResponse.success(${serviceVarName}.insert(${dtoVarName})); 46 | } 47 | 48 | @SaCheckPermission("${permissionPrefix}:edit") 49 | @Operation(description = "${name}修改") 50 | @PutMapping("/update") 51 | public RestResponse<${entityName}> update(@RequestBody ${dtoName} ${dtoVarName}) { 52 | return RestResponse.success(${serviceVarName}.update(${dtoVarName})); 53 | } 54 | 55 | @SaCheckPermission(value = {"${permissionPrefix}:edit", "${permissionPrefix}:detail"}, mode = SaMode.OR) 56 | @Operation(description = "${name}详情") 57 | @GetMapping("/get/{id}") 58 | public RestResponse<${entityName}> getById(@PathVariable ${idJavaType} id) { 59 | return RestResponse.success(${serviceVarName}.getById(id)); 60 | } 61 | 62 | @SaCheckPermission("${permissionPrefix}:del") 63 | @Operation(description = "${name}删除") 64 | @DeleteMapping("/del") 65 | public RestResponse del(@RequestParam List<${idJavaType}> ids) { 66 | ${serviceVarName}.del(ids); 67 | return RestResponse.success(); 68 | } 69 | <#if hasImport> 70 | 71 | @SaCheckPermission("${permissionPrefix}:import") 72 | @Operation(description = "${name}导入") 73 | @PostMapping("/imports") 74 | public RestResponse imports(@RequestBody List<${dtoName}> data) { 75 | ${serviceVarName}.imports(data); 76 | return RestResponse.success(); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /generator/service/src/main/resources/templates/frontend/form.vue.ftl: -------------------------------------------------------------------------------- 1 | 23 | 93 | 106 | -------------------------------------------------------------------------------- /system/service/src/main/java/com/xh/system/controller/SysFileController.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.controller; 2 | 3 | import cn.dev33.satoken.annotation.SaCheckPermission; 4 | import cn.dev33.satoken.annotation.SaMode; 5 | import com.xh.common.core.web.PageQuery; 6 | import com.xh.common.core.web.PageResult; 7 | import com.xh.common.core.web.RestResponse; 8 | import com.xh.system.client.dto.DownloadFileDTO; 9 | import com.xh.system.client.entity.SysFile; 10 | import com.xh.system.service.SysFileService; 11 | import io.swagger.v3.oas.annotations.Operation; 12 | import io.swagger.v3.oas.annotations.tags.Tag; 13 | import jakarta.annotation.Resource; 14 | import jakarta.servlet.http.HttpServletRequest; 15 | import jakarta.servlet.http.HttpServletResponse; 16 | import lombok.extern.slf4j.Slf4j; 17 | import org.springframework.http.MediaType; 18 | import org.springframework.web.bind.annotation.*; 19 | import org.springframework.web.multipart.MultipartFile; 20 | 21 | import java.util.List; 22 | import java.util.Map; 23 | 24 | @Tag(name = "文件操作") 25 | @RestController 26 | @RequestMapping("/api/file/operation") 27 | @Slf4j 28 | public class SysFileController { 29 | 30 | @Resource 31 | private SysFileService sysFileService; 32 | 33 | @Operation(description = "文件上传") 34 | @PostMapping(path = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) 35 | public RestResponse uploadFile(@RequestParam("file") MultipartFile file) { 36 | SysFile sysFile = sysFileService.uploadFile(file); 37 | return RestResponse.success(sysFile); 38 | } 39 | 40 | @Operation(description = "文件下载(图片预览)") 41 | @GetMapping("/download") 42 | public void downloadFile(DownloadFileDTO downloadFileDTO, @RequestHeader(value = "Range", defaultValue = "") String range, HttpServletRequest request, HttpServletResponse response) { 43 | sysFileService.downloadFile(downloadFileDTO, range, request, response); 44 | } 45 | 46 | @SaCheckPermission("system:file") 47 | @Operation(description = "文件列表查询") 48 | @PostMapping("/query") 49 | public RestResponse> query(@RequestBody PageQuery> pageQuery) { 50 | PageResult data = sysFileService.query(pageQuery); 51 | return RestResponse.success(data); 52 | } 53 | 54 | @SaCheckPermission("system:file:edit") 55 | @Operation(description = "文件保存") 56 | @PostMapping("/save") 57 | public RestResponse save(@RequestBody SysFile sysMenu) { 58 | return RestResponse.success(sysFileService.save(sysMenu)); 59 | } 60 | 61 | @SaCheckPermission(value = {"system:file:edit", "system:file:detail"}, mode = SaMode.OR) 62 | @Operation(description = "获取文件详情") 63 | @GetMapping("/get/{id}") 64 | public RestResponse getById(@PathVariable Integer id) { 65 | return RestResponse.success(sysFileService.getById(id)); 66 | } 67 | 68 | @Operation(description = "删除文件") 69 | @DeleteMapping("/del") 70 | public RestResponse del(@RequestParam List ids) { 71 | sysFileService.del(ids); 72 | return RestResponse.success(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /system/service/src/main/java/com/xh/system/controller/SysRoleController.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.controller; 2 | 3 | import cn.dev33.satoken.annotation.SaCheckPermission; 4 | import cn.dev33.satoken.annotation.SaMode; 5 | import com.xh.common.core.web.PageQuery; 6 | import com.xh.common.core.web.PageResult; 7 | import com.xh.common.core.web.RestResponse; 8 | import com.xh.system.client.dto.SysRolePermissionDTO; 9 | import com.xh.system.client.entity.SysMenu; 10 | import com.xh.system.client.entity.SysRole; 11 | import com.xh.system.client.entity.SysRoleDataPermission; 12 | import com.xh.system.service.SysRoleService; 13 | import io.swagger.v3.oas.annotations.Operation; 14 | import io.swagger.v3.oas.annotations.tags.Tag; 15 | import jakarta.annotation.Resource; 16 | import org.springframework.web.bind.annotation.*; 17 | 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | @Tag(name = "系统角色") 22 | @RestController 23 | @RequestMapping("/api/system/role") 24 | @SaCheckPermission("system:role") 25 | public class SysRoleController { 26 | 27 | @Resource 28 | private SysRoleService sysRoleService; 29 | 30 | @Operation(description = "角色列表查询") 31 | @PostMapping("/query") 32 | public RestResponse> query(@RequestBody PageQuery> pageQuery) { 33 | PageResult data = sysRoleService.query(pageQuery); 34 | return RestResponse.success(data); 35 | } 36 | 37 | @SaCheckPermission(value = {"system:role:add", "system:role:edit"}, mode = SaMode.OR) 38 | @Operation(description = "角色保存") 39 | @PostMapping("/save") 40 | public RestResponse save(@RequestBody SysRole sysRole) { 41 | return RestResponse.success(sysRoleService.save(sysRole)); 42 | } 43 | 44 | @SaCheckPermission(value = {"system:role:edit", "system:role:detail"}, mode = SaMode.OR) 45 | @Operation(description = "获取角色详情") 46 | @GetMapping("/get/{id}") 47 | public RestResponse getById(@PathVariable Integer id) { 48 | return RestResponse.success(sysRoleService.getById(id)); 49 | } 50 | 51 | @SaCheckPermission("system:role:del") 52 | @Operation(description = "角色批量删除") 53 | @DeleteMapping("/del") 54 | public RestResponse del(@RequestParam List ids) { 55 | sysRoleService.del(ids); 56 | return RestResponse.success(); 57 | } 58 | 59 | @Operation(description = "查询角色可配置的所有菜单权限") 60 | @GetMapping("/queryRoleMenu") 61 | public RestResponse> queryRoleMenu(@RequestParam Map param) { 62 | List roleMenus = sysRoleService.queryRoleMenu(param); 63 | return RestResponse.success(roleMenus); 64 | } 65 | 66 | @Operation(description = "查询角色的数据权限") 67 | @GetMapping("/queryRoleDataPermission") 68 | public RestResponse> queryRoleDataPermission(SysRolePermissionDTO sysRolePermissionDTO) { 69 | List roleDataPermission = sysRoleService.queryRoleDataPermission(sysRolePermissionDTO); 70 | return RestResponse.success(roleDataPermission); 71 | } 72 | 73 | @SaCheckPermission("system:role:dataPermission") 74 | @Operation(description = "保存角色数据权限") 75 | @PostMapping("/saveRoleDataPermission") 76 | public RestResponse saveRoleDataPermission(@RequestBody SysRolePermissionDTO sysRolePermissionDTO) { 77 | sysRoleService.saveRoleDataPermission(sysRolePermissionDTO); 78 | return RestResponse.success(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /system/service/src/main/java/com/xh/system/service/SysOrgService.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.service; 2 | 3 | import com.xh.common.core.service.BaseServiceImpl; 4 | import com.xh.common.core.utils.CommonUtil; 5 | import com.xh.common.core.web.PageQuery; 6 | import com.xh.common.core.web.PageResult; 7 | import com.xh.system.client.entity.SysOrg; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.stereotype.Service; 10 | import org.springframework.transaction.annotation.Transactional; 11 | 12 | import java.io.Serializable; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | /** 18 | * 系统机构service 19 | * sunxh 2023/6/3 20 | */ 21 | @Service 22 | @Slf4j 23 | public class SysOrgService extends BaseServiceImpl { 24 | 25 | /** 26 | * 系统机构查询 27 | */ 28 | @Transactional(readOnly = true) 29 | public PageResult queryOrgTree(String name) { 30 | if (CommonUtil.isEmpty(name)) { 31 | String sql = """ 32 | SELECT * from sys_org where deleted is false 33 | """; 34 | List list = baseJdbcDao.findList(SysOrg.class, sql); 35 | return new PageResult<>(list, list.size()); 36 | } 37 | //递归查询树 38 | String sql = """ 39 | WITH recursive tb as ( 40 | SELECT * from sys_org where deleted is false and name like '%' ? '%' 41 | UNION ALL 42 | SELECT b.* from tb inner join sys_org b on b.id = tb.parent_id 43 | ) 44 | select DISTINCT * from tb 45 | """; 46 | List list = baseJdbcDao.findList(SysOrg.class, sql, name); 47 | return new PageResult<>(list, list.size()); 48 | } 49 | 50 | /** 51 | * 系统机构查询 52 | */ 53 | @Transactional(readOnly = true) 54 | public PageResult query(PageQuery> pageQuery) { 55 | Map param = pageQuery.getParam(); 56 | String sql = """ 57 | select a.*, b.name parent_name from sys_org a 58 | left join sys_org b on a.parent_id = b.id where a.deleted is false 59 | """; 60 | if (CommonUtil.isNotEmpty(param.get("code"))) { 61 | sql += " and a.code like '%' ? '%'"; 62 | pageQuery.addArg(param.get("code")); 63 | } 64 | if (CommonUtil.isNotEmpty(param.get("name"))) { 65 | sql += " and a.name like '%' ? '%'"; 66 | pageQuery.addArg(param.get("name")); 67 | } 68 | if (CommonUtil.isNotEmpty(param.get("parentId"))) { 69 | sql += " and a.parent_id = ?"; 70 | pageQuery.addArg(param.get("parentId")); 71 | } 72 | if (CommonUtil.isNotEmpty(param.get("enabled"))) { 73 | sql += " and a.enabled = ?"; 74 | pageQuery.addArg(param.get("enabled")); 75 | } 76 | pageQuery.setBaseSql(sql); 77 | return baseJdbcDao.query(SysOrg.class, pageQuery); 78 | } 79 | 80 | 81 | @Transactional 82 | public SysOrg save(SysOrg sysOrg) { 83 | if (sysOrg.getId() == null) baseJdbcDao.insert(sysOrg); 84 | else baseJdbcDao.update(sysOrg); 85 | return sysOrg; 86 | } 87 | 88 | /** 89 | * id获取机构详情 90 | */ 91 | @Transactional(readOnly = true) 92 | public SysOrg getById(Serializable id) { 93 | return baseJdbcDao.findById(SysOrg.class, id); 94 | } 95 | 96 | /** 97 | * ids批量删除机构 98 | */ 99 | @Transactional 100 | public void del(List ids) { 101 | log.info("批量删除机构--"); 102 | String sql = "update sys_org set deleted = 1 where id in (:ids)"; 103 | Map paramMap = new HashMap<>() {{ 104 | put("ids", ids); 105 | }}; 106 | primaryNPJdbcTemplate.update(sql, paramMap); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/configuration/MyControllerAdvice.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.configuration; 2 | 3 | import cn.dev33.satoken.exception.NotLoginException; 4 | import cn.dev33.satoken.exception.NotPermissionException; 5 | import cn.dev33.satoken.exception.NotRoleException; 6 | import cn.dev33.satoken.exception.SaTokenException; 7 | import com.xh.common.core.utils.CommonUtil; 8 | import com.xh.common.core.web.MyContext; 9 | import com.xh.common.core.web.MyException; 10 | import com.xh.common.core.web.NoDataPermissionException; 11 | import com.xh.common.core.web.RestResponse; 12 | import jakarta.servlet.RequestDispatcher; 13 | import jakarta.servlet.http.HttpServletRequest; 14 | import lombok.extern.slf4j.Slf4j; 15 | import org.springframework.http.HttpStatus; 16 | import org.springframework.web.bind.annotation.ExceptionHandler; 17 | import org.springframework.web.bind.annotation.ResponseStatus; 18 | import org.springframework.web.bind.annotation.RestControllerAdvice; 19 | 20 | /** 21 | * 控制层异常统一处理 22 | * sunxh 2023/2/26 23 | */ 24 | @Slf4j 25 | @RestControllerAdvice 26 | public class MyControllerAdvice { 27 | 28 | @ResponseStatus(HttpStatus.OK) 29 | @ExceptionHandler(MyException.class) 30 | public RestResponse handleControllerException(MyException ex) { 31 | log.info("{} {}", ex.getStackTrace()[0].toString(), ex.getMessage()); 32 | RestResponse response = RestResponse.error(ex.getMessage()); 33 | response.setHttpCode(HttpStatus.OK.value()); 34 | return response; 35 | } 36 | 37 | @ResponseStatus(HttpStatus.UNAUTHORIZED) 38 | @ExceptionHandler(SaTokenException.class) 39 | public RestResponse handleNoDataPermissionException(SaTokenException e) { 40 | RestResponse res = new RestResponse<>(); 41 | res.setStatus("warning"); 42 | switch (e) { 43 | case NotLoginException ex -> { 44 | var message = switch (ex.getType()) { 45 | case "-3" -> "登录已超时!"; 46 | case "-4" -> "账号已在其他地方登录!"; 47 | case "-5" -> "当前会话已被管理员踢下线!"; 48 | case "-6" -> "token被冻结!"; 49 | case "-7" -> "非法格式token!"; 50 | default -> "会话未登录!"; 51 | }; 52 | res.setMessage(message); 53 | res.setHttpCode(HttpStatus.UNAUTHORIZED.value()); 54 | } 55 | case NotRoleException ignored -> { 56 | res.setHttpCode(HttpStatus.FORBIDDEN.value()); 57 | res.setMessage("角色无权操作!"); 58 | } 59 | case NotPermissionException ignored -> { 60 | res.setHttpCode(HttpStatus.FORBIDDEN.value()); 61 | res.setMessage("权限不足,无法操作!"); 62 | } 63 | default -> res.setMessage("服务器繁忙,请稍后重试..."); 64 | } 65 | return res; 66 | } 67 | 68 | @ResponseStatus(HttpStatus.UNAUTHORIZED) 69 | @ExceptionHandler(NoDataPermissionException.class) 70 | public RestResponse handleNoDataPermissionException(NoDataPermissionException e) { 71 | log.error("越权访问数据", e); 72 | MyContext.getSysLog().setStackTrace(CommonUtil.getThrowString(e)); 73 | return RestResponse.error("越权访问数据!"); 74 | } 75 | 76 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 77 | @ExceptionHandler(Exception.class) 78 | public RestResponse handleControllerException(HttpServletRequest request, Throwable e) { 79 | log.error("服务器运行异常", e); 80 | MyContext.getSysLog().setStackTrace(CommonUtil.getThrowString(e)); 81 | RestResponse response = RestResponse.error("服务器运行异常!"); 82 | HttpStatus status = getStatus(request); 83 | if (status != null) { 84 | response.setHttpCode(status.value()); 85 | } 86 | return response; 87 | } 88 | 89 | private HttpStatus getStatus(HttpServletRequest request) { 90 | Integer code = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE); 91 | if (code != null) return HttpStatus.resolve(code); 92 | return null; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /system/service/src/main/java/com/xh/system/service/SysMenuService.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.service; 2 | 3 | import com.xh.common.core.service.BaseServiceImpl; 4 | import com.xh.common.core.utils.CommonUtil; 5 | import com.xh.common.core.web.MyException; 6 | import com.xh.common.core.web.PageQuery; 7 | import com.xh.common.core.web.PageResult; 8 | import com.xh.system.client.entity.SysMenu; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.transaction.annotation.Transactional; 12 | 13 | import java.io.Serializable; 14 | import java.util.HashMap; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | /** 19 | * 系统菜单service 20 | * sunxh 2023/4/16 21 | */ 22 | @Service 23 | @Slf4j 24 | public class SysMenuService extends BaseServiceImpl { 25 | 26 | /** 27 | * 系统菜单查询 28 | */ 29 | @Transactional(readOnly = true) 30 | public PageResult query(PageQuery> pageQuery) { 31 | Map param = pageQuery.getParam(); 32 | if (param == null) param = new HashMap<>(); 33 | String flag = CommonUtil.getString(param.get("flag")); 34 | String sql = "select * from sys_menu where deleted is false "; 35 | if (CommonUtil.isNotEmpty(param.get("title"))) { 36 | sql += " and title like '%' ? '%'"; 37 | pageQuery.addArg(param.get("title")); 38 | } 39 | if (CommonUtil.isNotEmpty(param.get("enabled"))) { 40 | sql += " and enabled = ?"; 41 | pageQuery.addArg(param.get("enabled")); 42 | } 43 | if (CommonUtil.isNotEmpty(param.get("createTimeStart"))) { 44 | sql += " and create_time >= ?"; 45 | pageQuery.addArg(param.get("createTimeStart")); 46 | } 47 | if (CommonUtil.isNotEmpty(param.get("createTimeEnd"))) { 48 | sql += " and date_sub(create_time, interval 1 day) <= ?"; 49 | pageQuery.addArg(param.get("createTimeEnd")); 50 | } 51 | if (flag.equals("selectParentMenu")) { 52 | sql += " and type in ('dir', 'menu')"; 53 | } 54 | sql += " order by `order` asc"; 55 | pageQuery.setBaseSql(sql); 56 | return baseJdbcDao.query(SysMenu.class, pageQuery); 57 | } 58 | 59 | /** 60 | * 切换菜单字段值 61 | */ 62 | @Transactional 63 | public void switchMenuProp(Map param) { 64 | Object id = param.get("id"); 65 | Object prop = param.get("prop"); 66 | Object value = param.get("value"); 67 | SysMenu menu = baseJdbcDao.findById(SysMenu.class, (Serializable) id); 68 | if ("cache".equals(prop)) menu.setCache((Boolean) value); 69 | else if ("enabled".equals(prop)) menu.setEnabled((Boolean) value); 70 | else throw new MyException("参数异常,检查后重试!"); 71 | baseJdbcDao.update(menu); 72 | } 73 | 74 | 75 | @Transactional 76 | public SysMenu save(SysMenu sysMenu) { 77 | String sql = "select count(1) from sys_menu where deleted is false and name = ?"; 78 | if (sysMenu.getId() != null) { 79 | sql += " and id <> %s ".formatted(sysMenu.getId()); 80 | } 81 | Integer count = primaryJdbcTemplate.queryForObject(sql, Integer.class, sysMenu.getName()); 82 | if (count > 0) throw new MyException("菜单name:%s重复!".formatted(sysMenu.getName())); 83 | if (sysMenu.getId() == null) baseJdbcDao.insert(sysMenu); 84 | else baseJdbcDao.update(sysMenu); 85 | return sysMenu; 86 | } 87 | 88 | /** 89 | * id获取菜单详情 90 | */ 91 | @Transactional(readOnly = true) 92 | public SysMenu getById(Serializable id) { 93 | return baseJdbcDao.findById(SysMenu.class, id); 94 | } 95 | 96 | /** 97 | * ids批量删除菜单 98 | */ 99 | @Transactional 100 | public void del(List ids) { 101 | log.info("批量删除菜单--"); 102 | String sql = "update sys_menu set deleted = 1 where id in (:ids)"; 103 | Map paramMap = new HashMap<>(){{ 104 | put("ids", ids); 105 | }}; 106 | primaryNPJdbcTemplate.update(sql, paramMap); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /generator/service/src/main/resources/templates/frontend/index.vue.ftl: -------------------------------------------------------------------------------- 1 | 51 | 134 | 139 | -------------------------------------------------------------------------------- /generator/client/src/main/java/com/xh/generator/client/vo/GenTableVO.java: -------------------------------------------------------------------------------- 1 | package com.xh.generator.client.vo; 2 | 3 | import com.xh.generator.client.dto.GenTableColumnDTO; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import lombok.Data; 6 | 7 | import java.util.HashSet; 8 | import java.util.List; 9 | import java.util.Set; 10 | 11 | /** 12 | * 代码生成 VO 13 | * sunxh 2024/11/17 14 | */ 15 | @Data 16 | @Schema(title = "代码生成表") 17 | public class GenTableVO { 18 | 19 | @Schema(title = "设计方式") 20 | private String designType; 21 | 22 | @Schema(title = "表名") 23 | private String tableName; 24 | 25 | @Schema(title = "表注释") 26 | private String tableComment; 27 | 28 | @Schema(title = "实体类名") 29 | private String entityName; 30 | 31 | @Schema(title = "功能名") 32 | private String name; 33 | 34 | @Schema(title = "所属服务") 35 | private String service; 36 | 37 | @Schema(title = "模块名") 38 | private String module; 39 | 40 | @Schema(title = "作者") 41 | private String author; 42 | 43 | @Schema(title = "继承类") 44 | private String extend; 45 | 46 | @Schema(title = "生成日期") 47 | private String date; 48 | 49 | @Schema(title = "controller包名") 50 | private String controllerPackage; 51 | 52 | @Schema(title = "实体包名") 53 | private String entityPackage; 54 | 55 | @Schema(title = "实体字段名称") 56 | private String entityVarName; 57 | 58 | @Schema(title = "继承基础类名") 59 | private String extendEntityName; 60 | 61 | @Schema(title = "继承基础类名(包名+类名)") 62 | private String extendEntityClassName; 63 | 64 | @Schema(title = "实体继承基础类字符串") 65 | private String entityExtendClassStr; 66 | 67 | @Schema(title = "service包名") 68 | private String servicePackage; 69 | 70 | @Schema(title = "service类名") 71 | private String serviceName; 72 | 73 | @Schema(title = "service变量") 74 | private String serviceVarName; 75 | 76 | @Schema(title = "权限前缀") 77 | private String permissionPrefix; 78 | 79 | @Schema(title = "dto包名") 80 | private String dtoPackage; 81 | 82 | @Schema(title = "dto类名") 83 | private String dtoName; 84 | 85 | @Schema(title = "dto属性名") 86 | private String dtoVarName; 87 | 88 | @Schema(title = "dto继承类") 89 | private String dtoExtendClassStr; 90 | 91 | @Schema(title = "查询方法名") 92 | private String queryFun; 93 | 94 | @Schema(title = "新增方法名") 95 | private String insertFun; 96 | 97 | @Schema(title = "修改方法名") 98 | private String updateFun; 99 | 100 | @Schema(title = "详情方法名") 101 | private String getFun; 102 | 103 | @Schema(title = "删除方法名") 104 | private String delFun; 105 | 106 | @Schema(title = "导入方法名") 107 | private String importFun; 108 | 109 | @Schema(title = "api路径") 110 | private String apiPath; 111 | 112 | @Schema(title = "列表页全路径") 113 | private String indexViewPath; 114 | 115 | @Schema(title = "列表排序") 116 | private String orderBy; 117 | 118 | @Schema(title = "主键字段") 119 | private String idProp; 120 | 121 | @Schema(title = "主键字段java类型") 122 | private String idJavaType; 123 | 124 | @Schema(title = "主键get方法") 125 | private String primaryKeyGet; 126 | 127 | @Schema(title = "controller映射路径") 128 | private String mappingPath; 129 | 130 | @Schema(title = "是否有导入") 131 | private Boolean hasImport; 132 | 133 | @Schema(title = "是否有@Id注解") 134 | private Boolean hasId; 135 | 136 | @Schema(title = "是否有LocalDate") 137 | private Boolean hasLocalDate; 138 | 139 | @Schema(title = "是否有LocalDateTime") 140 | private Boolean hasLocalDateTime; 141 | 142 | @Schema(title = "是否有BigDecimal") 143 | private Boolean hasBigDecimal; 144 | 145 | @Schema(title = "是否包含数据字典") 146 | private Set hasDict = new HashSet<>(); 147 | 148 | @Schema(title = "前端项目路径") 149 | private String frontendPath; 150 | 151 | @Schema(title = "后端项目路径") 152 | private String backendPath; 153 | 154 | @Schema(title = "是否创建系统菜单") 155 | private Boolean isCreateMenu; 156 | 157 | @Schema(title = "是否启用数据权限") 158 | private Boolean isDataPermission; 159 | 160 | @Schema(title = "建表语句") 161 | private String createTableSql; 162 | 163 | @Schema(title = "菜单id") 164 | private Integer menuId; 165 | 166 | @Schema(title = "列") 167 | private List columns; 168 | 169 | @Schema(title = "当前登录用户ID") 170 | private Integer userId; 171 | } 172 | -------------------------------------------------------------------------------- /generator/service/src/main/java/com/xh/generator/controller/CodeGenController.java: -------------------------------------------------------------------------------- 1 | package com.xh.generator.controller; 2 | 3 | import cn.dev33.satoken.annotation.SaCheckPermission; 4 | import cn.dev33.satoken.annotation.SaMode; 5 | import com.xh.common.core.web.MyException; 6 | import com.xh.common.core.web.PageQuery; 7 | import com.xh.common.core.web.PageResult; 8 | import com.xh.common.core.web.RestResponse; 9 | import com.xh.generator.GeneratorApplication; 10 | import com.xh.generator.client.entity.GenTable; 11 | import com.xh.generator.client.vo.TableMateDataVO; 12 | import com.xh.generator.service.CodeGenService; 13 | import freemarker.template.TemplateException; 14 | import io.swagger.v3.oas.annotations.Operation; 15 | import io.swagger.v3.oas.annotations.tags.Tag; 16 | import jakarta.annotation.Resource; 17 | import jakarta.servlet.http.HttpServletResponse; 18 | import lombok.extern.slf4j.Slf4j; 19 | import org.springframework.beans.factory.annotation.Value; 20 | import org.springframework.boot.system.ApplicationHome; 21 | import org.springframework.web.bind.annotation.*; 22 | 23 | import java.io.IOException; 24 | import java.sql.SQLException; 25 | import java.util.List; 26 | import java.util.Map; 27 | 28 | @Tag(name = "代码生成") 29 | @RestController 30 | @RequestMapping("/api/generator") 31 | @Slf4j 32 | public class CodeGenController { 33 | 34 | @Resource 35 | private CodeGenService codeGenService; 36 | 37 | @Value("${env}") 38 | private String env; 39 | 40 | @SaCheckPermission("generator") 41 | @Operation(description = "代码生成列表查询") 42 | @PostMapping("/query") 43 | public RestResponse> query(@RequestBody PageQuery> pageQuery) { 44 | PageResult data = codeGenService.query(pageQuery); 45 | return RestResponse.success(data); 46 | } 47 | 48 | @SaCheckPermission("generator:edit") 49 | @Operation(description = "代码生成保存") 50 | @PostMapping("/save") 51 | public RestResponse save(@RequestBody GenTable genTable) { 52 | return RestResponse.success(codeGenService.save(genTable)); 53 | } 54 | 55 | @SaCheckPermission(value = {"generator:edit", "generator:detail"}, mode = SaMode.OR) 56 | @Operation(description = "获取代码生成详情") 57 | @GetMapping("/get/{id}") 58 | public RestResponse getById(@PathVariable Integer id) { 59 | return RestResponse.success(codeGenService.getById(id)); 60 | } 61 | 62 | @Operation(description = "删除代码生成") 63 | @DeleteMapping("/del") 64 | public RestResponse del(@RequestParam List ids) { 65 | codeGenService.del(ids); 66 | return RestResponse.success(); 67 | } 68 | 69 | @Operation(description = "生成代码") 70 | @PostMapping("/generate/{id}") 71 | public void generate(@PathVariable Integer id) throws IOException, TemplateException { 72 | if (!"development".equals(env)) { 73 | throw new MyException("仅开发环境可操作!"); 74 | } 75 | codeGenService.generate(id); 76 | } 77 | 78 | @Operation(description = "代码zip文件下载") 79 | @GetMapping("/getCodeZipFile/{id}") 80 | public void getCodeZipFile(@PathVariable Integer id, HttpServletResponse response) throws IOException, TemplateException { 81 | codeGenService.getCodeZipFile(id, response.getOutputStream()); 82 | } 83 | 84 | @Operation(description = "开发环境获取后端java项目的绝对路径") 85 | @GetMapping("/getBackendPath") 86 | public RestResponse getBackendPath() { 87 | var path = ""; 88 | if ("development".equals(env)) { 89 | ApplicationHome home = new ApplicationHome(GeneratorApplication.class); 90 | var absolutePath = home.getDir().getAbsolutePath().replaceAll("\\\\", "/"); 91 | path = absolutePath.split("/generator/service")[0]; 92 | } 93 | return RestResponse.success(path); 94 | } 95 | 96 | @SaCheckPermission(value = {"generator:edit", "generator:add"}, mode = SaMode.OR) 97 | @Operation(description = "获取数据库表列表") 98 | @GetMapping("/getTableList") 99 | public RestResponse> getTableList() { 100 | return RestResponse.success(codeGenService.getTableList(null)); 101 | } 102 | 103 | @SaCheckPermission(value = {"generator:edit", "generator:add"}, mode = SaMode.OR) 104 | @Operation(description = "获取表详情") 105 | @GetMapping("/getTableDetail") 106 | public RestResponse getTableDetail(@RequestParam Map param) throws SQLException { 107 | return RestResponse.success(codeGenService.getTableDetail(param)); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/dao/BaseJdbcDao.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.dao; 2 | 3 | import com.xh.common.core.dao.sql.SqlExecutor; 4 | import com.xh.common.core.web.PageQuery; 5 | import com.xh.common.core.web.PageResult; 6 | import org.springframework.jdbc.core.JdbcTemplate; 7 | 8 | import java.io.Serializable; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | public interface BaseJdbcDao { 13 | /** 14 | * 通过ID查询实体 15 | * 16 | * @param clazz 实体类Class类型 17 | * @param id 主键值 18 | * @return 返回实体对象 19 | */ 20 | K findById(Class clazz, Serializable id); 21 | 22 | /** 23 | * 通过ID查询实体,可指定数据源 24 | * 25 | * @param clazz clazz 实体类Class类型 26 | * @param jdbcTemplate 数据源 27 | * @param id 主键值 28 | * @return 返回实体对象 29 | */ 30 | K findById(Class clazz, JdbcTemplate jdbcTemplate, Serializable id); 31 | 32 | /** 33 | * 通过语句查询,返回第一行记录 34 | * 35 | * @param clazz 实体类Class类型 36 | * @param sql 查询语句 37 | * @param args 占位符变量 38 | * @return 第一行匹配的数据 39 | */ 40 | K findBySql(Class clazz, String sql, Object... args); 41 | 42 | /** 43 | * 通过语句查询,返回第一行记录,可指定数据源 44 | * 45 | * @param clazz 实体类Class类型 46 | * @param sql 查询语句 47 | * @param jdbcTemplate 数据源 48 | * @param args 占位符变量 49 | * @return 第一行匹配的数据 50 | */ 51 | K findBySql(Class clazz, String sql, JdbcTemplate jdbcTemplate, Object... args); 52 | 53 | /** 54 | * 获取列表数据 55 | * 56 | * @param clazz 接受对象类型 57 | * @param sql 查询语句 58 | * @param args 占位符变量 59 | * @return 返回匹配的记录 60 | */ 61 | List findList(Class clazz, String sql, Object... args); 62 | 63 | /** 64 | * 获取列表数据,可指定数据源 65 | * 66 | * @param clazz 接受对象类型 67 | * @param sql 查询语句 68 | * @param jdbcTemplate 查询语句 69 | * @param args 占位符变量 70 | * @return 返回匹配的记录 71 | */ 72 | List findList(Class clazz, String sql, JdbcTemplate jdbcTemplate, Object... args); 73 | 74 | /** 75 | * 分页查询,Map类型 76 | * 77 | * @param pageQuery 分页查询配置 78 | * @return Map类型的分页数据 79 | */ 80 | PageResult query(PageQuery pageQuery); 81 | 82 | /** 83 | * 分页查询,对象类型 84 | * 85 | * @param clazz 接受对象类型 86 | * @param pageQuery 分页查询配置 87 | * @return 对象类型的分页数据 88 | */ 89 | PageResult query(Class clazz, PageQuery pageQuery); 90 | 91 | /** 92 | * 分页查询,对象类型,可指定数据源 93 | * 94 | * @param clazz 接受对象类型 95 | * @param pageQuery 分页查询配置 96 | * @param jdbcTemplate 数据源 97 | * @return 对象类型的分页数据 98 | */ 99 | PageResult query(Class clazz, PageQuery pageQuery, JdbcTemplate jdbcTemplate); 100 | 101 | /** 102 | * 配量插入实体数据,可指定数据源 103 | * 104 | * @param jdbcTemplate 数据源 105 | * @param entities 实体数据数组 106 | */ 107 | void insert(JdbcTemplate jdbcTemplate, E[] entities); 108 | 109 | /** 110 | * 配量插入实体数据,默认主数据源 111 | * 112 | * @param entities 实体数据数组 113 | */ 114 | void insert(E[] entities); 115 | 116 | /** 117 | * 插入单条实体数据,默认主数据源 118 | * 119 | * @param entity 实体数据 120 | */ 121 | void insert(E entity); 122 | 123 | /** 124 | * 插入单条实体数据,可指定数据源 125 | * 126 | * @param jdbcTemplate 数据源 127 | * @param entity 实体数据 128 | */ 129 | void insert(JdbcTemplate jdbcTemplate, E entity); 130 | 131 | 132 | /** 133 | * 更新单条实体数据,默认主数据源 134 | * 135 | * @param entity 实体数据 136 | */ 137 | void update(E entity); 138 | 139 | /** 140 | * 更新单条实体数据,可指定数据源 141 | * 142 | * @param jdbcTemplate 数据源 143 | * @param entity 实体数据 144 | */ 145 | void update(JdbcTemplate jdbcTemplate, E entity); 146 | 147 | /** 148 | * 通过主键删除数据(物理删除),默认主数据源 149 | * 150 | * @param clazz 实体类Class类型 151 | * @param id 主键值 152 | */ 153 | void deleteById(Class clazz, Serializable id); 154 | 155 | /** 156 | * 通过主键删除数据(物理删除),可指定数据源 157 | * 158 | * @param clazz 实体类Class类型 159 | * @param jdbcTemplate 数据源 160 | * @param id 主键值 161 | */ 162 | void deleteById(Class clazz, JdbcTemplate jdbcTemplate, Serializable id); 163 | 164 | /** 165 | * 获取指定数据源 SqlExecutor 166 | * 167 | * @param jdbcTemplate 数据源 168 | */ 169 | SqlExecutor getSqlExecutor(JdbcTemplate jdbcTemplate); 170 | 171 | /** 172 | * 获取默认数据源 SqlExecutor 173 | */ 174 | SqlExecutor getSqlExecutor(); 175 | } 176 | -------------------------------------------------------------------------------- /system/service/src/main/java/com/xh/system/service/SysLogService.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.service; 2 | 3 | import com.xh.common.core.entity.SysLog; 4 | import com.xh.common.core.service.BaseServiceImpl; 5 | import com.xh.common.core.service.CommonService; 6 | import com.xh.common.core.utils.CommonUtil; 7 | import com.xh.common.core.web.PageQuery; 8 | import com.xh.common.core.web.PageResult; 9 | import com.xh.system.client.entity.SysUser; 10 | import jakarta.annotation.Resource; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.stereotype.Service; 13 | import org.springframework.transaction.annotation.Transactional; 14 | 15 | import java.io.Serializable; 16 | import java.util.HashMap; 17 | import java.util.List; 18 | import java.util.Map; 19 | 20 | @Service 21 | @Slf4j 22 | public class SysLogService extends BaseServiceImpl { 23 | 24 | @Resource 25 | private CommonService commonService; 26 | 27 | /** 28 | * 日志查询 29 | */ 30 | @Transactional(readOnly = true) 31 | public PageResult query(PageQuery> pageQuery) { 32 | Map param = pageQuery.getParam(); 33 | String sql = """ 34 | select a.id, a.token,a.method,a.url,a.ip,a.ip_address,a.tag,a.operation,a.start_time,a.end_time,a.time,a.status, 35 | a.locale, a.locale_label, a.sys_org_id, a.org_name, a.sys_role_id, a.role_name, a.create_by, a.create_time, b.name 36 | from sys_log a left join sys_user b on b.id = a.create_by where a.deleted is false 37 | """; 38 | if (CommonUtil.isNotEmpty(param.get("token"))) { 39 | sql += " and a.token = ? "; 40 | pageQuery.addArg(param.get("token")); 41 | } 42 | if (CommonUtil.isNotEmpty(param.get("method"))) { 43 | sql += " and a.method like '%' ? '%'"; 44 | pageQuery.addArg(param.get("method")); 45 | } 46 | if (CommonUtil.isNotEmpty(param.get("url"))) { 47 | sql += " and a.url like '%' ? '%'"; 48 | pageQuery.addArg(param.get("url")); 49 | } 50 | if (CommonUtil.isNotEmpty(param.get("ip"))) { 51 | sql += " and a.ip like '%' ? '%'"; 52 | pageQuery.addArg(param.get("ip")); 53 | } 54 | if (CommonUtil.isNotEmpty(param.get("ipAddress"))) { 55 | sql += " and a.ip_address like '%' ? '%'"; 56 | pageQuery.addArg(param.get("ipAddress")); 57 | } 58 | if (CommonUtil.isNotEmpty(param.get("tag"))) { 59 | sql += " and a.tag like '%' ? '%'"; 60 | pageQuery.addArg(param.get("tag")); 61 | } 62 | if (CommonUtil.isNotEmpty(param.get("operation"))) { 63 | sql += " and a.operation like '%' ? '%'"; 64 | pageQuery.addArg(param.get("operation")); 65 | } 66 | if (CommonUtil.isNotEmpty(param.get("status"))) { 67 | sql += " and a.status = ?"; 68 | pageQuery.addArg(param.get("status")); 69 | } 70 | if (CommonUtil.isNotEmpty(param.get("name"))) { 71 | sql += " and b.name like '%' ? '%'"; 72 | pageQuery.addArg(param.get("name")); 73 | } 74 | 75 | // 数据权限 76 | String permissionSql = commonService.getPermissionSql("sys_log", "a.create_by", "a.sys_role_id", "a.sys_org_id"); 77 | if (CommonUtil.isNotEmpty(permissionSql)) { 78 | sql += " and %s".formatted(permissionSql); 79 | } 80 | 81 | sql += " order by a.id desc"; 82 | pageQuery.setBaseSql(sql); 83 | return baseJdbcDao.query(SysLog.class, pageQuery); 84 | } 85 | 86 | /** 87 | * id获取日志详情 88 | */ 89 | @Transactional(readOnly = true) 90 | public SysLog getById(Serializable id) { 91 | // 校验数据权限是否满足 92 | commonService.checkDataPermissionByIds( 93 | "sys_log", "create_by", "sys_role_id", "sys_org_id", 94 | SysLog.class, id 95 | ); 96 | SysLog sysLog = baseJdbcDao.findById(SysLog.class, id); 97 | if (sysLog.getCreateBy() != null) { 98 | SysUser sysUser = baseJdbcDao.findById(SysUser.class, sysLog.getCreateBy()); 99 | sysLog.setName(sysUser.getName()); 100 | } 101 | return sysLog; 102 | } 103 | 104 | /** 105 | * ids批量删除日志 106 | */ 107 | @Transactional 108 | public void del(List ids) { 109 | log.info("批量删除日志--"); 110 | // 校验数据权限是否满足 111 | commonService.checkDataPermissionByIds( 112 | "sys_log", "create_by", "sys_role_id", "sys_org_id", 113 | SysLog.class, ids.toArray() 114 | ); 115 | String sql = "update sys_log set deleted = 1 where id in (:ids)"; 116 | Map paramMap = new HashMap<>() {{ 117 | put("ids", ids); 118 | }}; 119 | primaryNPJdbcTemplate.update(sql, paramMap); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/configuration/WebConfig.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.configuration; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; 5 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; 6 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; 7 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 8 | import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; 9 | import jakarta.annotation.Nonnull; 10 | import jakarta.annotation.Resource; 11 | import jakarta.servlet.Filter; 12 | import lombok.Setter; 13 | import org.springframework.boot.context.properties.ConfigurationProperties; 14 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 15 | import org.springframework.context.annotation.Bean; 16 | import org.springframework.context.annotation.Configuration; 17 | import org.springframework.http.converter.ByteArrayHttpMessageConverter; 18 | import org.springframework.http.converter.HttpMessageConverter; 19 | import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; 20 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 21 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 22 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 23 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 24 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 25 | 26 | import java.text.SimpleDateFormat; 27 | import java.time.format.DateTimeFormatter; 28 | import java.util.List; 29 | 30 | /** 31 | * web配置类 32 | * sunxh 2023/2/26 33 | */ 34 | @Configuration(proxyBeanMethods = false) 35 | @EnableWebMvc 36 | @ConfigurationProperties("spring.web") 37 | public class WebConfig implements WebMvcConfigurer { 38 | 39 | @Resource 40 | private MyFilter myFilter; 41 | @Resource 42 | private MyInterceptor myInterceptor; 43 | @Setter 44 | private String[] allowedOriginPatterns; 45 | 46 | 47 | /** 48 | * 资源跨域设置 49 | */ 50 | @Override 51 | public void addCorsMappings(CorsRegistry registry) { 52 | registry.addMapping("/api/**") 53 | .allowedOriginPatterns(allowedOriginPatterns) 54 | .allowedMethods("POST", "PUT", "GET", "OPTIONS", "DELETE") 55 | .maxAge(3600); 56 | } 57 | 58 | /** 59 | * 过滤器配置 60 | */ 61 | @Bean 62 | public FilterRegistrationBean filterRegistrationBean() { 63 | FilterRegistrationBean filterFilterRegistrationBean = new FilterRegistrationBean<>(); 64 | filterFilterRegistrationBean.setFilter(myFilter); 65 | filterFilterRegistrationBean.setOrder(1);//执行的顺序 66 | filterFilterRegistrationBean.addUrlPatterns("/*"); 67 | return filterFilterRegistrationBean; 68 | } 69 | 70 | /** 71 | * 请求拦截器 72 | */ 73 | @Override 74 | public void addInterceptors(@Nonnull InterceptorRegistry registry) { 75 | WebMvcConfigurer.super.addInterceptors(registry); 76 | registry.addInterceptor(myInterceptor) 77 | .addPathPatterns("/**") 78 | // 排除swagger文档 79 | .excludePathPatterns( 80 | "/swagger-ui.html", 81 | "/swagger-ui.html/**", 82 | "/swagger-ui/**", 83 | "/v3/**" 84 | ); 85 | } 86 | 87 | @Override 88 | public void configureMessageConverters(List> converters) { 89 | // 在头部添加,优先级别最高 90 | converters.addFirst(new MappingJackson2HttpMessageConverter(getDefaultObjectMapper())); 91 | converters.addFirst(new ByteArrayHttpMessageConverter()); 92 | } 93 | 94 | 95 | private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; 96 | private static final String DATE_FORMAT = "yyyy-MM-dd"; 97 | 98 | /** 99 | * 定义全局默认时间序列化 100 | */ 101 | public static ObjectMapper getDefaultObjectMapper() { 102 | Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() 103 | .indentOutput(true) 104 | .dateFormat(new SimpleDateFormat("yyyy-MM-dd")) 105 | .simpleDateFormat(DATE_TIME_FORMAT) 106 | .serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT))) 107 | .serializers(new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT))) 108 | .deserializers(new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT))) 109 | .deserializers(new LocalDateDeserializer(DateTimeFormatter.ofPattern(DATE_FORMAT))) 110 | .modulesToInstall(new ParameterNamesModule()); 111 | return builder.build(); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/dao/sql/MysqlExecutor.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.dao.sql; 2 | 3 | import jakarta.persistence.GeneratedValue; 4 | import jakarta.persistence.GenerationType; 5 | import jakarta.persistence.PersistenceException; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.jdbc.core.BeanPropertyRowMapper; 8 | import org.springframework.jdbc.core.JdbcTemplate; 9 | import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; 10 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; 11 | import org.springframework.jdbc.support.GeneratedKeyHolder; 12 | 13 | import java.math.BigInteger; 14 | import java.util.Arrays; 15 | import java.util.List; 16 | import java.util.Map; 17 | import java.util.stream.Collectors; 18 | 19 | /** 20 | * mysql orm 21 | * 22 | * @author sunxh 23 | * @since 2024/12/6 24 | */ 25 | @Slf4j 26 | public class MysqlExecutor implements SqlExecutor { 27 | 28 | @Override 29 | public void toInsert(JdbcTemplate jdbcTemplate, E[] entitys) { 30 | EntityStaff entityStaff = EntityStaff.init(entitys[0].getClass()); 31 | String columnStr = entityStaff.getColumns().stream() 32 | .map(EntityStaff.EntityColumnStaff::getColumnName) 33 | .collect(Collectors.joining("`,`", "`", "`")); 34 | String valueStr = entityStaff.getColumns().stream() 35 | .map(i -> { 36 | GeneratedValue generatedValue = i.getGeneratedValue(); 37 | // 如果是序列类型,则此主键由序列生成 38 | if (generatedValue != null && generatedValue.strategy() == GenerationType.SEQUENCE) { 39 | return generatedValue.generator(); 40 | } 41 | return ":" + i.getFieldName(); 42 | }) 43 | .collect(Collectors.joining(",")); 44 | String sql = "INSERT INTO `%s` (%s) VALUES (%s)".formatted(entityStaff.getTableName(), columnStr, valueStr); 45 | 46 | BeanPropertySqlParameterSource[] args = Arrays.stream(entitys).map(BeanPropertySqlParameterSource::new) 47 | .toArray(BeanPropertySqlParameterSource[]::new); 48 | 49 | GeneratedKeyHolder generatedKeyHolder = new GeneratedKeyHolder(); 50 | new NamedParameterJdbcTemplate(jdbcTemplate).batchUpdate(sql, args, generatedKeyHolder); 51 | 52 | List> keyList = generatedKeyHolder.getKeyList(); 53 | for (int i = 0; i < keyList.size(); i++) { 54 | for (EntityStaff.EntityColumnStaff idColumn : entityStaff.getIdColumns()) { 55 | Map keyMap = keyList.get(i); 56 | var val = (BigInteger) keyMap.get("GENERATED_KEY"); 57 | if (val == null) val = (BigInteger) keyMap.get(idColumn.getColumnName()); 58 | if (val != null) { 59 | try { 60 | idColumn.setFieldValue(entitys[i], val.intValue()); 61 | } catch (Exception e) { 62 | log.error("设置主键错误", e); 63 | } 64 | } 65 | } 66 | } 67 | } 68 | 69 | @Override 70 | public void toUpdate(JdbcTemplate jdbcTemplate, E entity) { 71 | EntityStaff entityStaff = EntityStaff.init(entity.getClass()); 72 | String columnStr = entityStaff.getColumns().stream() 73 | .map(i -> "`%s`=:%s".formatted(i.getColumnName(), i.getFieldName())) 74 | .collect(Collectors.joining(",")); 75 | String idWhereStr = entityStaff.getIdColumns().stream() 76 | .map(i -> "`%s`=:%s".formatted(i.getColumnName(), i.getFieldName())) 77 | .collect(Collectors.joining(",")); 78 | var sql = "UPDATE `%s` SET %s WHERE %s".formatted(entityStaff.getTableName(), columnStr, idWhereStr); 79 | 80 | BeanPropertySqlParameterSource arg = new BeanPropertySqlParameterSource(entity); 81 | 82 | new NamedParameterJdbcTemplate(jdbcTemplate).update(sql, arg); 83 | } 84 | 85 | 86 | @Override 87 | public E findById(JdbcTemplate jdbcTemplate, Class clazz, Object id) { 88 | EntityStaff entityStaff = EntityStaff.init(clazz); 89 | if (entityStaff.getIdColumns().isEmpty()) { 90 | throw new PersistenceException("实体没有主键"); 91 | } 92 | if (entityStaff.getIdColumns().size() > 1) { 93 | throw new PersistenceException("无法查询联合主键的数据"); 94 | } 95 | String columnStr = entityStaff.getColumns().stream() 96 | .map(i -> "`%s` as `%s`".formatted(i.getColumnName(), i.getFieldName())) 97 | .collect(Collectors.joining(",")); 98 | assert entityStaff.getIdColumns().peek() != null; 99 | String idWhere = "`" + entityStaff.getIdColumns().peek().getColumnName() + "` = ?"; 100 | var sql = "select %s FROM `%s` WHERE %s".formatted(columnStr, entityStaff.getTableName(), idWhere); 101 | return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(clazz), id); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /system/service/src/main/java/com/xh/system/service/SysDictService.java: -------------------------------------------------------------------------------- 1 | package com.xh.system.service; 2 | 3 | import com.xh.common.core.service.BaseServiceImpl; 4 | import com.xh.common.core.utils.CommonUtil; 5 | import com.xh.common.core.web.MyException; 6 | import com.xh.common.core.web.PageQuery; 7 | import com.xh.common.core.web.PageResult; 8 | import com.xh.system.client.entity.SysDictDetail; 9 | import com.xh.system.client.entity.SysDictType; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.stereotype.Service; 12 | import org.springframework.transaction.annotation.Transactional; 13 | 14 | import java.io.Serializable; 15 | import java.util.HashMap; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | /** 20 | * 系统数据字典service 21 | * sunxh 2023/4/16 22 | */ 23 | @Slf4j 24 | @Service 25 | public class SysDictService extends BaseServiceImpl { 26 | 27 | /** 28 | * 数据字典类型查询 29 | */ 30 | @Transactional(readOnly = true) 31 | public PageResult queryTypes(PageQuery> pageQuery) { 32 | Map param = pageQuery.getParam(); 33 | if (param == null) param = new HashMap<>(); 34 | String sql = "select * from sys_dict_type where deleted is false "; 35 | if (CommonUtil.isNotEmpty(param.get("name"))) { 36 | sql += " and name like '%' ? '%'"; 37 | pageQuery.addArg(param.get("name")); 38 | } 39 | if (CommonUtil.isNotEmpty(param.get("enabled"))) { 40 | sql += " and enabled = ?"; 41 | pageQuery.addArg(param.get("enabled")); 42 | } 43 | sql += " order by id asc"; 44 | pageQuery.setBaseSql(sql); 45 | return baseJdbcDao.query(SysDictType.class, pageQuery); 46 | } 47 | 48 | /** 49 | * 数据字典明细查询 50 | */ 51 | @Transactional(readOnly = true) 52 | public PageResult queryDetails(PageQuery> pageQuery) { 53 | Map param = pageQuery.getParam(); 54 | String sql = """ 55 | select a.*, b.id dict_type_id,b.name dict_type_name 56 | from sys_dict_detail a 57 | left join sys_dict_type b on a.sys_dict_type_id = b.id 58 | where a.deleted is false 59 | """; 60 | if (CommonUtil.isNotEmpty(param.get("dictTypeId"))) { 61 | sql += " and sys_dict_type_id = ?"; 62 | pageQuery.addArg(param.get("dictTypeId")); 63 | } 64 | if (CommonUtil.isNotEmpty(param.get("value"))) { 65 | sql += " and value like '%' ? '%'"; 66 | pageQuery.addArg(param.get("value")); 67 | } 68 | if (CommonUtil.isNotEmpty(param.get("label"))) { 69 | sql += " and label like '%' ? '%'"; 70 | pageQuery.addArg(param.get("label")); 71 | } 72 | if (CommonUtil.isNotEmpty(param.get("enabled"))) { 73 | sql += " and enabled = ?"; 74 | pageQuery.addArg(param.get("enabled")); 75 | } 76 | sql += " order by sys_dict_type_id asc,`order` asc"; 77 | pageQuery.setBaseSql(sql); 78 | return baseJdbcDao.query(SysDictDetail.class, pageQuery); 79 | } 80 | 81 | 82 | /** 83 | * id获取数据字典明细详情 84 | */ 85 | @Transactional(readOnly = true) 86 | public SysDictDetail getDictDetailById(Serializable id) { 87 | String sql = """ 88 | select a.*,b.id dict_type_id,b.name dict_type_name from sys_dict_detail a 89 | left join sys_dict_type b on a.sys_dict_type_id = b.id 90 | where a.id = ? 91 | """; 92 | return baseJdbcDao.findBySql(SysDictDetail.class, sql, id); 93 | } 94 | 95 | /** 96 | * 数据字典明细保存 97 | */ 98 | @Transactional 99 | public SysDictDetail saveDictDetail(SysDictDetail sysDictDetail) { 100 | String sql = """ 101 | select count(1) from sys_dict_detail where deleted is false 102 | and sys_dict_type_id = '%s' and value = '%s' 103 | """.formatted(sysDictDetail.getSysDictTypeId(), sysDictDetail.getValue()); 104 | if (sysDictDetail.getId() != null) { 105 | sql += " and id <> %s ".formatted(sysDictDetail.getId()); 106 | } 107 | Integer count = primaryJdbcTemplate.queryForObject(sql, Integer.class); 108 | if (count > 0) throw new MyException("字典key:%s重复!".formatted(sysDictDetail.getValue())); 109 | if (sysDictDetail.getId() == null) baseJdbcDao.insert(sysDictDetail); 110 | else baseJdbcDao.update(sysDictDetail); 111 | return sysDictDetail; 112 | } 113 | 114 | /** 115 | * ids批量删除数据字典明细 116 | */ 117 | @Transactional 118 | public void delDetail(List ids) { 119 | log.info("批量删除数据字典明细--"); 120 | String sql = "update sys_dict_detail set deleted = 1 where id in (:ids)"; 121 | Map paramMap = new HashMap<>() {{ 122 | put("ids", ids); 123 | }}; 124 | primaryNPJdbcTemplate.update(sql, paramMap); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/configuration/MyFilter.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.configuration; 2 | 3 | import cn.dev33.satoken.SaManager; 4 | import cn.dev33.satoken.context.SaTokenContext; 5 | import cn.dev33.satoken.router.SaRouter; 6 | import com.alibaba.fastjson2.JSONObject; 7 | import com.xh.common.core.entity.SysLog; 8 | import com.xh.common.core.service.CommonService; 9 | import com.xh.common.core.utils.CommonUtil; 10 | import com.xh.common.core.web.MyContext; 11 | import jakarta.annotation.Resource; 12 | import jakarta.servlet.FilterChain; 13 | import jakarta.servlet.ServletInputStream; 14 | import jakarta.servlet.http.HttpFilter; 15 | import jakarta.servlet.http.HttpServletRequest; 16 | import jakarta.servlet.http.HttpServletResponse; 17 | import lombok.extern.slf4j.Slf4j; 18 | import org.springframework.context.annotation.Configuration; 19 | import org.springframework.web.util.ContentCachingRequestWrapper; 20 | import org.springframework.web.util.ContentCachingResponseWrapper; 21 | import reactor.core.publisher.Mono; 22 | 23 | import java.io.IOException; 24 | import java.time.LocalDateTime; 25 | import java.util.Map; 26 | 27 | /** 28 | * web过滤器 29 | * 30 | * @author sunxh 2024/5/8 31 | */ 32 | @Configuration 33 | @Slf4j 34 | public class MyFilter extends HttpFilter { 35 | 36 | @Resource 37 | private CommonService commonService; 38 | 39 | protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException { 40 | preHandle(request); 41 | ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request); 42 | ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response); 43 | try { 44 | chain.doFilter(requestWrapper, responseWrapper); 45 | } catch (Exception e) { 46 | log.error("请求异常", e); 47 | MyContext.getSysLog().setStackTrace(CommonUtil.getThrowString(e)); 48 | } 49 | afterHandle(requestWrapper, responseWrapper); 50 | } 51 | 52 | /** 53 | * 前置处理 54 | */ 55 | private void preHandle(HttpServletRequest request) { 56 | SysLog sysLog = MyContext.getSysLog(true); 57 | sysLog.setStartTime(LocalDateTime.now()); 58 | sysLog.setMethod(request.getMethod()); 59 | sysLog.setUrl(request.getRequestURI()); 60 | sysLog.setContentType(request.getContentType()); 61 | String ip = request.getHeader("X-Real-IP"); 62 | if (CommonUtil.isEmpty(ip)) { 63 | ip = request.getRemoteAddr(); 64 | } 65 | sysLog.setIp(ip); 66 | String ipRegion2 = CommonUtil.getIpRegion2(ip); 67 | sysLog.setIpAddress(ipRegion2); 68 | 69 | Map parameterMap = request.getParameterMap(); 70 | if (!parameterMap.isEmpty()) { 71 | JSONObject parameter = JSONObject.from(parameterMap); 72 | sysLog.setRequestParameter(parameter.toString()); 73 | } 74 | } 75 | 76 | /** 77 | * 后置处理 78 | */ 79 | private void afterHandle(ContentCachingRequestWrapper request, ContentCachingResponseWrapper response) throws IOException { 80 | SysLog sysLog = MyContext.getSysLog(); 81 | ServletInputStream inputStream = request.getRequest().getInputStream(); 82 | //如果文件流已读取则从缓存中获取请求体 83 | if (inputStream.isFinished()) { 84 | sysLog.setRequestBody(request.getContentAsString()); 85 | } else { 86 | // 否则直接从request中获取请求体 87 | String requestBody = new String(inputStream.readAllBytes()); 88 | sysLog.setRequestBody(requestBody); 89 | } 90 | //存储响应体内容 91 | String contentType = response.getContentType(); 92 | if (contentType != null && contentType.startsWith("application/json")) { 93 | byte[] contentAsByteArray = response.getContentAsByteArray(); 94 | String responseContent = new String(contentAsByteArray); 95 | sysLog.setResponseBody(responseContent); 96 | } 97 | response.copyBodyToResponse(); 98 | 99 | boolean hit = SaRouter 100 | .notMatch(ignored -> request.getRequestURI().endsWith("/query")) 101 | .notMatch( 102 | "/api/system/log/get/**", 103 | "/api/system/user/queryOnlineUser", 104 | "/api/file/operation/download", 105 | "/api/system/user/queryUserGroupList" 106 | ) 107 | .notMatchMethod("OPTIONS") 108 | .isHit(); 109 | //设置指定匹配的或者出现报错的才记录日志 110 | if (hit || sysLog.getStackTrace() != null) { 111 | final SaTokenContext saTokenContext = SaManager.getSaTokenContext(); 112 | //异步存储请求的日志信息 113 | Mono.just(sysLog).subscribe(i -> { 114 | // 因为会开启新线程,所以把上SaToken下文对象传递进来 115 | if (saTokenContext != null) { 116 | SaManager.setSaTokenContext(saTokenContext); 117 | } 118 | commonService.saveSysLog(i); 119 | }); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/configuration/MyInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.configuration; 2 | 3 | import cn.dev33.satoken.config.SaTokenConfig; 4 | import cn.dev33.satoken.interceptor.SaInterceptor; 5 | import cn.dev33.satoken.router.SaRouter; 6 | import cn.dev33.satoken.stp.StpUtil; 7 | import com.xh.common.core.Constant; 8 | import com.xh.common.core.dto.SysLoginUserInfoDTO; 9 | import com.xh.common.core.dto.SysUserDTO; 10 | import com.xh.common.core.entity.SysLog; 11 | import com.xh.common.core.utils.CommonUtil; 12 | import com.xh.common.core.utils.LoginUtil; 13 | import com.xh.common.core.web.MyContext; 14 | import com.xh.common.core.web.MyException; 15 | import io.swagger.v3.oas.annotations.Operation; 16 | import io.swagger.v3.oas.annotations.tags.Tag; 17 | import jakarta.annotation.Nonnull; 18 | import jakarta.annotation.Resource; 19 | import jakarta.servlet.http.HttpServletRequest; 20 | import jakarta.servlet.http.HttpServletResponse; 21 | import lombok.extern.slf4j.Slf4j; 22 | import org.springframework.context.annotation.Configuration; 23 | import org.springframework.web.method.HandlerMethod; 24 | 25 | import java.lang.reflect.Method; 26 | 27 | /** 28 | * 拦截器,实现SaToken鉴权 29 | * 30 | * @author sunxh 2023/2/26 31 | */ 32 | @Configuration 33 | @Slf4j 34 | public class MyInterceptor extends SaInterceptor { 35 | 36 | @Resource 37 | private SaTokenConfig saTokenConfig; 38 | 39 | @Override 40 | public boolean preHandle(HttpServletRequest request, @Nonnull HttpServletResponse response, @Nonnull Object handler) throws Exception { 41 | //自动程序跨服务,无法获取登录用户,直接登录为自动程序用户 42 | String feignValue = request.getHeader(Constant.AUTO_FEIGN_KEY); 43 | if (CommonUtil.isNotEmpty(feignValue)) { 44 | log.info("自动程序跨服务:{}", feignValue); 45 | StpUtil.login(feignValue, "auto-job"); 46 | } 47 | 48 | //对于无法添加header但需要鉴权的请求可以将token放在参数中,手动从请求中获取token设置 49 | String tokenValue = request.getParameter(StpUtil.getTokenName()); 50 | if (CommonUtil.isNotEmpty(tokenValue)) { 51 | StpUtil.setTokenValue(tokenValue); 52 | } 53 | 54 | if (handler instanceof HandlerMethod handlerMethod) { 55 | final var requestURI = request.getRequestURI(); 56 | SysLog sysLog = MyContext.getSysLog(); 57 | Class controllerClass = handlerMethod.getBeanType(); 58 | Tag tag = controllerClass.getAnnotation(Tag.class); 59 | Method method = handlerMethod.getMethod(); 60 | Operation operation = method.getAnnotation(Operation.class); 61 | if (tag == null || CommonUtil.isEmpty(tag.name())) { 62 | throw new MyException("保持良好的开发规范,请补充:%s 类Tag注解name属性,描述controller用途".formatted(controllerClass.getName())); 63 | } 64 | if (operation == null || CommonUtil.isEmpty(operation.description())) { 65 | throw new MyException("保持良好的开发规范,请补充:%s 方法Operation注解description属性描述方法用途".formatted(method.getName())); 66 | } 67 | 68 | sysLog.setTag(tag.name()); 69 | sysLog.setOperation(operation.description()); 70 | 71 | //打印一下控制器相关日志 72 | log.info("{} {} {}--{}", controllerClass.getName(), method.getName(), tag.name(), operation.description()); 73 | 74 | this.auth = ignored -> { 75 | // SaToken鉴权 76 | StpUtil.checkLogin(); 77 | SysLoginUserInfoDTO userInfoDTO = LoginUtil.getSysUserInfo(); 78 | SysUserDTO user = userInfoDTO.getUser(); 79 | 80 | // 演示站的演示账号部分操作不允许 81 | if(Boolean.TRUE.equals(user.getIsDemo())) { 82 | boolean hit = SaRouter.notMatch( 83 | "/api/system/user/personalCenterSave", 84 | "/api/file/operation/upload", 85 | "/api/system/user/imports", 86 | "/api/system/user/resetPassword", 87 | "/api/system/user/saveUserJobs", 88 | "/api/system/user/saveUserGroup", 89 | "/api/system/user/delUserGroup" 90 | ) 91 | .notMatch(obj -> { 92 | boolean isDel = requestURI.endsWith("/del"); 93 | boolean isSave = requestURI.endsWith("/save"); 94 | boolean isSwitchProp = requestURI.endsWith("/switch_prop"); 95 | return isDel || isSave || isSwitchProp; 96 | }).isHit(); 97 | // 代码生成器不拦截 98 | boolean hitGenCode = SaRouter.match("/api/generator/**").isHit(); 99 | if(!hit && !hitGenCode) throw new MyException("演示账号不允许此操作"); 100 | } 101 | 102 | if (Boolean.TRUE.equals(user.getAutoRenewal())) { 103 | //续签token过期时间 104 | StpUtil.renewTimeout(saTokenConfig.getTimeout()); 105 | } 106 | }; 107 | return super.preHandle(request, response, handler); 108 | } 109 | return true; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/dao/sql/SqlExecutor.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.dao.sql; 2 | 3 | import jakarta.persistence.GeneratedValue; 4 | import jakarta.persistence.GenerationType; 5 | import jakarta.persistence.PersistenceException; 6 | import org.springframework.jdbc.core.BeanPropertyRowMapper; 7 | import org.springframework.jdbc.core.JdbcTemplate; 8 | import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; 9 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; 10 | import org.springframework.jdbc.support.GeneratedKeyHolder; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.stream.Collectors; 16 | 17 | /** 18 | * 利用jdbcTempLate 进行ORM映射 19 | * 20 | * @author sunxh 21 | * @since 2024/12/3 22 | */ 23 | public interface SqlExecutor { 24 | /** 25 | * 执行insert语句 26 | */ 27 | default void toInsert(JdbcTemplate jdbcTemplate, E[] entity) { 28 | EntityStaff entityStaff = EntityStaff.init(entity[0].getClass()); 29 | String columnStr = entityStaff.getColumns().stream() 30 | .map(EntityStaff.EntityColumnStaff::getColumnName) 31 | .collect(Collectors.joining(",")); 32 | String valueStr = entityStaff.getColumns().stream() 33 | .map(i -> { 34 | GeneratedValue generatedValue = i.getGeneratedValue(); 35 | // 如果是序列类型,则此主键由序列生成 36 | if (generatedValue != null && generatedValue.strategy() == GenerationType.SEQUENCE) { 37 | return generatedValue.generator(); 38 | } 39 | return ":" + i.getFieldName(); 40 | }) 41 | .collect(Collectors.joining(",")); 42 | var sql = "INSERT INTO %s (%s) VALUES (%s)".formatted(entityStaff.getTableName(), columnStr, valueStr); 43 | 44 | BeanPropertySqlParameterSource[] args = Arrays.stream(entity).map(BeanPropertySqlParameterSource::new) 45 | .toArray(BeanPropertySqlParameterSource[]::new); 46 | GeneratedKeyHolder generatedKeyHolder = new GeneratedKeyHolder(); 47 | 48 | new NamedParameterJdbcTemplate(jdbcTemplate).batchUpdate(sql, args, generatedKeyHolder); 49 | 50 | List> keyList = generatedKeyHolder.getKeyList(); 51 | for (int i = 0; i < keyList.size(); i++) { 52 | for (EntityStaff.EntityColumnStaff idColumn : entityStaff.getIdColumns()) { 53 | var val = keyList.get(i).get(idColumn.getColumnName()); 54 | idColumn.setFieldValue(entity[i], val); 55 | } 56 | } 57 | } 58 | 59 | /** 60 | * 执行update语句 61 | */ 62 | default void toUpdate(JdbcTemplate jdbcTemplate, E entity) { 63 | EntityStaff entityStaff = EntityStaff.init(entity.getClass()); 64 | String columnStr = entityStaff.getColumns().stream() 65 | .map(i -> "%s = :%s".formatted(i.getColumnName(), i.getFieldName())) 66 | .collect(Collectors.joining(",")); 67 | String idWhereStr = entityStaff.getIdColumns().stream() 68 | .map(i -> "%s = :%s".formatted(i.getColumnName(), i.getFieldName())) 69 | .collect(Collectors.joining(",")); 70 | var sql = "UPDATE %s SET %s WHERE %s".formatted(entityStaff.getTableName(), columnStr, idWhereStr); 71 | 72 | BeanPropertySqlParameterSource arg = new BeanPropertySqlParameterSource(entity); 73 | 74 | new NamedParameterJdbcTemplate(jdbcTemplate).update(sql, arg); 75 | 76 | } 77 | 78 | /** 79 | * 执行delete语句 80 | */ 81 | default void toDeleteById(JdbcTemplate jdbcTemplate, Class clazz, Object id) { 82 | EntityStaff entityStaff = EntityStaff.init(clazz); 83 | assert entityStaff.getIdColumns().peek() != null; 84 | String idWhereStr = entityStaff.getIdColumns().peek().getColumnName() + " = ?"; 85 | var sql = "DELETE FROM %s WHERE %s".formatted(entityStaff.getTableName(), idWhereStr); 86 | jdbcTemplate.update(sql, id); 87 | } 88 | 89 | /** 90 | * 执行findById语句 91 | */ 92 | default E findById(JdbcTemplate jdbcTemplate, Class clazz, Object id) { 93 | EntityStaff entityStaff = EntityStaff.init(clazz); 94 | if (entityStaff.getIdColumns().isEmpty()) { 95 | throw new PersistenceException("实体没有主键"); 96 | } 97 | if (entityStaff.getIdColumns().size() > 1) { 98 | throw new PersistenceException("无法查询联合主键的数据"); 99 | } 100 | String columnStr = entityStaff.getColumns().stream() 101 | .map(i -> "%s as %s".formatted(i.getColumnName(), i.getFieldName())) 102 | .collect(Collectors.joining(",")); 103 | assert entityStaff.getIdColumns().peek() != null; 104 | String idWhere = entityStaff.getIdColumns().peek().getColumnName() + " = ?"; 105 | var sql = "select %s FROM %s WHERE %s".formatted(columnStr, entityStaff.getTableName(), idWhere); 106 | return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(clazz), id); 107 | } 108 | 109 | /** 110 | * 获取分页语句 111 | */ 112 | default String getPageSql(String sql, int currentPage, int pageSize) { 113 | return "SELECT * FROM (%s) PAGE LIMIT %s,%s".formatted(sql, (currentPage - 1) * pageSize, pageSize); 114 | } 115 | 116 | /** 117 | * 转换一下sql包裹符 118 | */ 119 | default String convertSql(String sql) { 120 | return sql; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /generator/service/src/main/resources/templates/backend/Service.java.ftl: -------------------------------------------------------------------------------- 1 | package ${servicePackage}; 2 | 3 | import com.xh.common.core.service.BaseServiceImpl; 4 | <#if isDataPermission!false> 5 | import com.xh.common.core.service.CommonService; 6 | 7 | import com.xh.common.core.utils.CommonUtil; 8 | import com.xh.common.core.web.MyException; 9 | import com.xh.common.core.web.PageQuery; 10 | import com.xh.common.core.web.PageResult; 11 | import ${entityPackage}.${entityName}; 12 | import ${dtoPackage}.${dtoName}; 13 | <#if isDataPermission!false> 14 | import jakarta.annotation.Resource; 15 | 16 | import lombok.extern.slf4j.Slf4j; 17 | import org.springframework.beans.BeanUtils; 18 | import org.springframework.stereotype.Service; 19 | import org.springframework.transaction.annotation.Transactional; 20 | <#if hasImport> 21 | import org.springframework.transaction.interceptor.TransactionAspectSupport; 22 | 23 | 24 | <#if hasImport> 25 | import java.util.ArrayList; 26 | 27 | import java.util.HashMap; 28 | import java.util.List; 29 | import java.util.Map; 30 | 31 | /** 32 | * ${name} Service 33 | * 34 | * @author ${author} 35 | * @since ${date} 36 | */ 37 | @Slf4j 38 | @Service 39 | public class ${serviceName} extends BaseServiceImpl { 40 | <#if isDataPermission!false> 41 | @Resource 42 | private CommonService commonService; 43 | 44 | 45 | /** 46 | * ${name}查询 47 | */ 48 | @Transactional(readOnly = true) 49 | public PageResult<${entityName}> query(PageQuery> pageQuery) { 50 | Map param = pageQuery.getParam(); 51 | if (param == null) param = new HashMap<>(); 52 | 53 | String sql = "select a.* from ${tableName} a where 1=1<#if extend??> and a.deleted is false "; 54 | 55 | <#list columns as field> 56 | <#if field.isQuery!false> 57 | if (CommonUtil.isNotEmpty(param.get("${field.prop}"))) { 58 | sql += " ${field.querySql}"; 59 | pageQuery.addArg(param.get("${field.prop}")); 60 | } 61 | 62 | 63 | 64 | <#if isDataPermission!false> 65 | // 数据权限 66 | String permissionSql = commonService.getPermissionSql("${tableName}", "a.create_by", "a.sys_role_id", "a.sys_org_id"); 67 | if (CommonUtil.isNotEmpty(permissionSql)) { 68 | sql += " and %s".formatted(permissionSql); 69 | } 70 | 71 | 72 | <#if orderBy??> 73 | sql += " ${orderBy}"; 74 | 75 | 76 | pageQuery.setBaseSql(sql); 77 | return baseJdbcDao.query(${entityName}.class, pageQuery); 78 | } 79 | 80 | /** 81 | * ${name}新增 82 | */ 83 | @Transactional 84 | public ${entityName} insert(${dtoName} ${dtoVarName}) { 85 | ${entityName} ${entityVarName} = new ${entityName}(); 86 | BeanUtils.copyProperties(${dtoVarName}, ${entityVarName}); 87 | baseJdbcDao.insert(${entityVarName}); 88 | return ${entityVarName}; 89 | } 90 | 91 | /** 92 | * ${name}修改 93 | */ 94 | @Transactional 95 | public ${entityName} update(${dtoName} ${dtoVarName}) { 96 | ${entityName} ${entityVarName} = new ${entityName}(); 97 | BeanUtils.copyProperties(${dtoVarName}, ${entityVarName}); 98 | baseJdbcDao.update(${entityVarName}); 99 | return ${entityVarName}; 100 | } 101 | 102 | /** 103 | * id获取${name}详情 104 | */ 105 | @Transactional(readOnly = true) 106 | public ${entityName} getById(${idJavaType} id) { 107 | <#if isDataPermission!false> 108 | // 校验数据权限是否满足 109 | commonService.checkDataPermissionByIds( 110 | "sys_log", "create_by", "sys_role_id", "sys_org_id", 111 | ${entityName}.class, id 112 | ); 113 | 114 | return baseJdbcDao.findById(${entityName}.class, id); 115 | } 116 | 117 | /** 118 | * ids批量删除 119 | */ 120 | @Transactional 121 | public void del(List<${idJavaType}> ids) { 122 | log.info("批量删除${name}--"); 123 | <#if isDataPermission!false> 124 | // 校验数据权限是否满足 125 | commonService.checkDataPermissionByIds( 126 | "sys_log", "create_by", "sys_role_id", "sys_org_id", 127 | ${entityName}.class, ids.toArray() 128 | ); 129 | 130 | <#if extend??> 131 | String sql = "update ${tableName} set deleted = 1 where ${idProp} in (:ids)"; 132 | <#else> 133 | String sql = "delete from ${tableName} where ${idProp} in (:ids)"; 134 | 135 | Map paramMap = new HashMap<>(){{ 136 | put("ids", ids); 137 | }}; 138 | primaryNPJdbcTemplate.update(sql, paramMap); 139 | } 140 | <#if hasImport> 141 | 142 | /** 143 | * ${name}导入 144 | */ 145 | @Transactional 146 | public List> imports(List<${dtoName}> data) { 147 | List> res = new ArrayList<>(); 148 | for (int i = 0; i < data.size(); i++) { 149 | try { 150 | insert(data.get(i)); 151 | } catch (MyException e) { 152 | Map resMap = new HashMap<>(); 153 | resMap.put("rowIndex", i); 154 | resMap.put("error", e.getMessage()); 155 | res.add(resMap); 156 | } 157 | } 158 | //有错误信息直接手动回滚整个事务 159 | if (!res.isEmpty()) { 160 | TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 161 | return res; 162 | } 163 | return null; 164 | } 165 | 166 | } 167 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/dao/sql/PostgreSqlExecutor.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.dao.sql; 2 | 3 | import jakarta.persistence.GeneratedValue; 4 | import jakarta.persistence.GenerationType; 5 | import jakarta.persistence.PersistenceException; 6 | import org.springframework.jdbc.core.BeanPropertyRowMapper; 7 | import org.springframework.jdbc.core.JdbcTemplate; 8 | import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; 9 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; 10 | import org.springframework.jdbc.support.GeneratedKeyHolder; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.stream.Collectors; 16 | 17 | /** 18 | * postgresql orm 19 | * 20 | * @author sunxh 21 | * @since 2024/12/8 22 | */ 23 | public class PostgreSqlExecutor implements SqlExecutor { 24 | /** 25 | * 转化为insert语句 26 | */ 27 | public void toInsert(JdbcTemplate jdbcTemplate, E[] entity) { 28 | E first = entity[0]; 29 | EntityStaff entityStaff = EntityStaff.init(first.getClass()); 30 | List columns = entityStaff.getColumns().stream() 31 | .filter(i -> { 32 | Object fieldValue = i.getFieldValue(first); 33 | if (fieldValue != null) return true; 34 | // postgresql 自增类型的无需声明字段 35 | GeneratedValue generatedValue = i.getGeneratedValue(); 36 | return generatedValue == null || generatedValue.strategy() != GenerationType.AUTO; 37 | }) 38 | .toList(); 39 | String columnStr = columns.stream() 40 | .map(EntityStaff.EntityColumnStaff::getColumnName) 41 | .collect(Collectors.joining("\",\"", "\"", "\"")); 42 | String valueStr = columns.stream() 43 | .map(i -> { 44 | GeneratedValue generatedValue = i.getGeneratedValue(); 45 | // 如果是序列类型,则此主键由序列生成 46 | if (generatedValue != null && generatedValue.strategy() == GenerationType.SEQUENCE) { 47 | return generatedValue.generator(); 48 | } 49 | return ":" + i.getFieldName(); 50 | }) 51 | .collect(Collectors.joining(",")); 52 | var sql = "INSERT INTO \"%s\" (%s) VALUES (%s)".formatted(entityStaff.getTableName(), columnStr, valueStr); 53 | 54 | BeanPropertySqlParameterSource[] args = Arrays.stream(entity).map(BeanPropertySqlParameterSource::new) 55 | .toArray(BeanPropertySqlParameterSource[]::new); 56 | GeneratedKeyHolder generatedKeyHolder = new GeneratedKeyHolder(); 57 | 58 | new NamedParameterJdbcTemplate(jdbcTemplate).batchUpdate(sql, args, generatedKeyHolder); 59 | 60 | List> keyList = generatedKeyHolder.getKeyList(); 61 | for (int i = 0; i < keyList.size(); i++) { 62 | for (EntityStaff.EntityColumnStaff idColumn : entityStaff.getIdColumns()) { 63 | var val = keyList.get(i).get(idColumn.getColumnName()); 64 | idColumn.setFieldValue(entity[i], val); 65 | } 66 | } 67 | } 68 | 69 | /** 70 | * 转化为update语句 71 | */ 72 | public void toUpdate(JdbcTemplate jdbcTemplate, E entity) { 73 | EntityStaff entityStaff = EntityStaff.init(entity.getClass()); 74 | String columnStr = entityStaff.getColumns().stream() 75 | .map(i -> "\"%s\" = :%s".formatted(i.getColumnName(), i.getFieldName())) 76 | .collect(Collectors.joining(",")); 77 | String idWhereStr = entityStaff.getIdColumns().stream() 78 | .map(i -> "\"%s\" = :%s".formatted(i.getColumnName(), i.getFieldName())) 79 | .collect(Collectors.joining(",")); 80 | var sql = "UPDATE \"%s\" SET %s WHERE %s".formatted(entityStaff.getTableName(), columnStr, idWhereStr); 81 | 82 | BeanPropertySqlParameterSource arg = new BeanPropertySqlParameterSource(entity); 83 | 84 | new NamedParameterJdbcTemplate(jdbcTemplate).update(sql, arg); 85 | 86 | } 87 | 88 | /** 89 | * 转化为findById语句 90 | */ 91 | public E findById(JdbcTemplate jdbcTemplate, Class clazz, Object id) { 92 | EntityStaff entityStaff = EntityStaff.init(clazz); 93 | if (entityStaff.getIdColumns().isEmpty()) { 94 | throw new PersistenceException("实体没有主键"); 95 | } 96 | if (entityStaff.getIdColumns().size() > 1) { 97 | throw new PersistenceException("无法查询联合主键的数据"); 98 | } 99 | String columnStr = entityStaff.getColumns().stream() 100 | .map(i -> "\"%s\" as \"%s\"".formatted(i.getColumnName(), i.getFieldName())) 101 | .collect(Collectors.joining(",")); 102 | assert entityStaff.getIdColumns().peek() != null; 103 | String idWhere = "\"" + entityStaff.getIdColumns().peek().getColumnName() + "\" = ?"; 104 | var sql = "select %s FROM \"%s\" WHERE %s".formatted(columnStr, entityStaff.getTableName(), idWhere); 105 | return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(clazz), id); 106 | } 107 | 108 | 109 | /** 110 | * 获取分页语句 111 | */ 112 | public String getPageSql(String sql, int currentPage, int pageSize) { 113 | return "SELECT * FROM (%s) PAGE LIMIT %s OFFSET %s".formatted(sql, pageSize, (currentPage - 1) * pageSize); 114 | } 115 | 116 | /** 117 | * 转换一下sql包裹符 118 | */ 119 | @Override 120 | public String convertSql(String sql) { 121 | return sql.replaceAll("`", "\""); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /common/common-core/src/main/java/com/xh/common/core/entity/AutoSetFun.java: -------------------------------------------------------------------------------- 1 | package com.xh.common.core.entity; 2 | 3 | import cn.dev33.satoken.stp.StpUtil; 4 | import com.xh.common.core.dao.PersistenceType; 5 | import com.xh.common.core.dto.OnlineUserDTO; 6 | import com.xh.common.core.utils.LoginUtil; 7 | import lombok.extern.slf4j.Slf4j; 8 | 9 | import java.lang.reflect.Field; 10 | import java.time.LocalDate; 11 | import java.time.LocalDateTime; 12 | 13 | /** 14 | * 执行持久化操作时,自动注入字段值 15 | * sunxh 2024/5/6 16 | */ 17 | @Slf4j 18 | public enum AutoSetFun { 19 | /** 20 | * 新增时并且当字段为null时自动添时间为当前时间 21 | * 仅LocalDate或LocalDateTime类型生效 22 | */ 23 | INSERT_NOW((persistenceType, field, object) -> { 24 | if (persistenceType == PersistenceType.INSERT) { 25 | try { 26 | field.setAccessible(true); 27 | if (field.get(object) != null) return; 28 | Class type = field.getType(); 29 | if (type == LocalDate.class) { 30 | field.set(object, LocalDate.now()); 31 | } 32 | if (type == LocalDateTime.class) { 33 | field.set(object, LocalDateTime.now()); 34 | } 35 | } catch (IllegalAccessException e) { 36 | log.error("自动注入失败", e); 37 | } 38 | } 39 | }), 40 | 41 | /** 42 | * 修改时自动添时间为当前时间 43 | * 仅LocalDate或LocalDateTime类型生效 44 | */ 45 | UPDATE_NOW((persistenceType, field, object) -> { 46 | if (persistenceType == PersistenceType.UPDATE) { 47 | try { 48 | field.setAccessible(true); 49 | Class type = field.getType(); 50 | if (type == LocalDate.class) { 51 | field.set(object, LocalDate.now()); 52 | } 53 | if (type == LocalDateTime.class) { 54 | field.set(object, LocalDateTime.now()); 55 | } 56 | } catch (IllegalAccessException e) { 57 | log.error("自动注入失败", e); 58 | } 59 | } 60 | }), 61 | /** 62 | * 新增时并且字段值为null时,自动添加当前登录人员ID 63 | * 仅Integer类型有效 64 | */ 65 | INSERT_BY((persistenceType, field, object) -> { 66 | if (persistenceType == PersistenceType.INSERT) { 67 | try { 68 | field.setAccessible(true); 69 | Class type = field.getType(); 70 | if (field.get(object) == null && type == Integer.class) { 71 | Object id = StpUtil.getLoginIdDefaultNull(); 72 | if (id != null) { 73 | field.set(object, Integer.parseInt(id.toString())); 74 | } 75 | } 76 | } catch (IllegalAccessException e) { 77 | log.error("自动注入失败", e); 78 | } 79 | } 80 | }), 81 | /** 82 | * 修改时,自动更新为当前登录人ID 83 | * 仅LocalDate或LocalDateTime类型生效 84 | */ 85 | UPDATE_BY((persistenceType, field, object) -> { 86 | if (persistenceType == PersistenceType.UPDATE) { 87 | try { 88 | field.setAccessible(true); 89 | Class type = field.getType(); 90 | if (type == Integer.class) { 91 | Object id = StpUtil.getLoginIdDefaultNull(); 92 | if (id != null) { 93 | field.set(object, Integer.parseInt(id.toString())); 94 | } 95 | } 96 | } catch (IllegalAccessException e) { 97 | log.error("自动注入失败", e); 98 | } 99 | } 100 | }), 101 | /** 102 | * 实体保存时,如果字段为null,设置字段值为当前使用岗位机构id 103 | */ 104 | CURRENT_ORG((persistenceType, field, object) -> { 105 | try { 106 | field.setAccessible(true); 107 | Class type = field.getType(); 108 | if (field.get(object) == null && type == Integer.class) { 109 | OnlineUserDTO onlineUserInfo = LoginUtil.getOnlineUserInfo(); 110 | if (onlineUserInfo != null) { 111 | field.set(object, onlineUserInfo.getOrgId()); 112 | } 113 | } 114 | } catch (IllegalAccessException e) { 115 | log.error("自动注入失败", e); 116 | } 117 | }), 118 | /** 119 | * 实体保存时,如果字段为null,设置字段值为当前使用岗位角色id 120 | */ 121 | CURRENT_ROLE((persistenceType, field, object) -> { 122 | try { 123 | field.setAccessible(true); 124 | Class type = field.getType(); 125 | if (field.get(object) == null && type == Integer.class) { 126 | OnlineUserDTO onlineUserInfo = LoginUtil.getOnlineUserInfo(); 127 | if (onlineUserInfo != null) { 128 | field.set(object, onlineUserInfo.getRoleId()); 129 | } 130 | } 131 | } catch (IllegalAccessException e) { 132 | log.error("自动注入失败", e); 133 | } 134 | }), 135 | /** 136 | * 当字段值为null,默认值为false 137 | */ 138 | DEFAULT_FALSE((persistenceType, field, object) -> { 139 | try { 140 | field.setAccessible(true); 141 | Class type = field.getType(); 142 | if (field.get(object) == null && type == Boolean.class) { 143 | field.set(object, false); 144 | } 145 | } catch (IllegalAccessException e) { 146 | log.error("自动注入失败", e); 147 | } 148 | }); 149 | 150 | public final AutoSetFunction fun; 151 | 152 | AutoSetFun(AutoSetFunction fun) { 153 | this.fun = fun; 154 | } 155 | 156 | @FunctionalInterface 157 | public interface AutoSetFunction { 158 | /** 159 | * @param persistenceType 操作类型 160 | * @param field 当前field 161 | * @param object 实体对象 162 | */ 163 | void exec(T1 persistenceType, T2 field, T3 object); 164 | } 165 | } 166 | --------------------------------------------------------------------------------